1/* 2 * username_providers.c: providers for SVN_AUTH_CRED_USERNAME 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* ==================================================================== */ 25 26 27 28/*** Includes. ***/ 29 30#include <apr_pools.h> 31#include "svn_hash.h" 32#include "svn_auth.h" 33#include "svn_error.h" 34#include "svn_utf.h" 35#include "svn_config.h" 36#include "svn_user.h" 37 38 39/*-----------------------------------------------------------------------*/ 40/* File provider */ 41/*-----------------------------------------------------------------------*/ 42 43/*** Username-only Provider ***/ 44static svn_error_t * 45username_first_creds(void **credentials, 46 void **iter_baton, 47 void *provider_baton, 48 apr_hash_t *parameters, 49 const char *realmstring, 50 apr_pool_t *pool) 51{ 52 const char *config_dir = svn_hash_gets(parameters, 53 SVN_AUTH_PARAM_CONFIG_DIR); 54 const char *username = svn_hash_gets(parameters, 55 SVN_AUTH_PARAM_DEFAULT_USERNAME); 56 svn_boolean_t may_save = !! username; 57 svn_error_t *err; 58 59 /* If we don't have a usename yet, try the auth cache */ 60 if (! username) 61 { 62 apr_hash_t *creds_hash = NULL; 63 64 /* Try to load credentials from a file on disk, based on the 65 realmstring. Don't throw an error, though: if something went 66 wrong reading the file, no big deal. What really matters is that 67 we failed to get the creds, so allow the auth system to try the 68 next provider. */ 69 err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_USERNAME, 70 realmstring, config_dir, pool); 71 svn_error_clear(err); 72 if (! err && creds_hash) 73 { 74 svn_string_t *str = svn_hash_gets(creds_hash, 75 SVN_CONFIG_AUTHN_USERNAME_KEY); 76 if (str && str->data) 77 username = str->data; 78 } 79 } 80 81 /* If that failed, ask the OS for the username */ 82 if (! username) 83 username = svn_user_get_name(pool); 84 85 if (username) 86 { 87 svn_auth_cred_simple_t *creds = apr_pcalloc(pool, sizeof(*creds)); 88 creds->username = username; 89 creds->may_save = may_save; 90 *credentials = creds; 91 } 92 else 93 *credentials = NULL; 94 95 *iter_baton = NULL; 96 97 return SVN_NO_ERROR; 98} 99 100 101static svn_error_t * 102username_save_creds(svn_boolean_t *saved, 103 void *credentials, 104 void *provider_baton, 105 apr_hash_t *parameters, 106 const char *realmstring, 107 apr_pool_t *pool) 108{ 109 svn_auth_cred_simple_t *creds = credentials; 110 apr_hash_t *creds_hash = NULL; 111 const char *config_dir; 112 svn_error_t *err; 113 114 *saved = FALSE; 115 116 if (! creds->may_save) 117 return SVN_NO_ERROR; 118 119 config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR); 120 121 /* Put the credentials in a hash and save it to disk */ 122 creds_hash = apr_hash_make(pool); 123 svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_USERNAME_KEY, 124 svn_string_create(creds->username, pool)); 125 err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_USERNAME, 126 realmstring, config_dir, pool); 127 svn_error_clear(err); 128 *saved = ! err; 129 130 return SVN_NO_ERROR; 131} 132 133 134static const svn_auth_provider_t username_provider = { 135 SVN_AUTH_CRED_USERNAME, 136 username_first_creds, 137 NULL, 138 username_save_creds 139}; 140 141 142/* Public API */ 143void 144svn_auth_get_username_provider(svn_auth_provider_object_t **provider, 145 apr_pool_t *pool) 146{ 147 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 148 149 po->vtable = &username_provider; 150 *provider = po; 151} 152 153 154/*-----------------------------------------------------------------------*/ 155/* Prompt provider */ 156/*-----------------------------------------------------------------------*/ 157 158/* Baton type for username-only prompting. */ 159typedef struct username_prompt_provider_baton_t 160{ 161 svn_auth_username_prompt_func_t prompt_func; 162 void *prompt_baton; 163 164 /* how many times to re-prompt after the first one fails */ 165 int retry_limit; 166} username_prompt_provider_baton_t; 167 168 169/* Iteration baton type for username-only prompting. */ 170typedef struct username_prompt_iter_baton_t 171{ 172 /* how many times we've reprompted */ 173 int retries; 174 175} username_prompt_iter_baton_t; 176 177 178/*** Helper Functions ***/ 179static svn_error_t * 180prompt_for_username_creds(svn_auth_cred_username_t **cred_p, 181 username_prompt_provider_baton_t *pb, 182 apr_hash_t *parameters, 183 const char *realmstring, 184 svn_boolean_t first_time, 185 svn_boolean_t may_save, 186 apr_pool_t *pool) 187{ 188 const char *def_username = NULL; 189 190 *cred_p = NULL; 191 192 /* If we're allowed to check for default usernames, do so. */ 193 if (first_time) 194 def_username = svn_hash_gets(parameters, SVN_AUTH_PARAM_DEFAULT_USERNAME); 195 196 /* If we have defaults, just build the cred here and return it. 197 * 198 * ### I do wonder why this is here instead of in a separate 199 * ### 'defaults' provider that would run before the prompt 200 * ### provider... Hmmm. 201 */ 202 if (def_username) 203 { 204 *cred_p = apr_palloc(pool, sizeof(**cred_p)); 205 (*cred_p)->username = apr_pstrdup(pool, def_username); 206 (*cred_p)->may_save = TRUE; 207 } 208 else 209 { 210 SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring, 211 may_save, pool)); 212 } 213 214 return SVN_NO_ERROR; 215} 216 217 218/* Our first attempt will use any default username passed 219 in, and prompt for the remaining stuff. */ 220static svn_error_t * 221username_prompt_first_creds(void **credentials_p, 222 void **iter_baton, 223 void *provider_baton, 224 apr_hash_t *parameters, 225 const char *realmstring, 226 apr_pool_t *pool) 227{ 228 username_prompt_provider_baton_t *pb = provider_baton; 229 username_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton)); 230 const char *no_auth_cache = svn_hash_gets(parameters, 231 SVN_AUTH_PARAM_NO_AUTH_CACHE); 232 233 SVN_ERR(prompt_for_username_creds 234 ((svn_auth_cred_username_t **) credentials_p, pb, 235 parameters, realmstring, TRUE, ! no_auth_cache, pool)); 236 237 ibaton->retries = 0; 238 *iter_baton = ibaton; 239 240 return SVN_NO_ERROR; 241} 242 243 244/* Subsequent attempts to fetch will ignore the default username 245 value, and simply re-prompt for the username, up to a maximum of 246 ib->pb->retry_limit. */ 247static svn_error_t * 248username_prompt_next_creds(void **credentials_p, 249 void *iter_baton, 250 void *provider_baton, 251 apr_hash_t *parameters, 252 const char *realmstring, 253 apr_pool_t *pool) 254{ 255 username_prompt_iter_baton_t *ib = iter_baton; 256 username_prompt_provider_baton_t *pb = provider_baton; 257 const char *no_auth_cache = svn_hash_gets(parameters, 258 SVN_AUTH_PARAM_NO_AUTH_CACHE); 259 260 if ((pb->retry_limit >= 0) && (ib->retries >= pb->retry_limit)) 261 { 262 /* give up, go on to next provider. */ 263 *credentials_p = NULL; 264 return SVN_NO_ERROR; 265 } 266 ib->retries++; 267 268 return prompt_for_username_creds 269 ((svn_auth_cred_username_t **) credentials_p, pb, 270 parameters, realmstring, FALSE, ! no_auth_cache, pool); 271} 272 273 274static const svn_auth_provider_t username_prompt_provider = { 275 SVN_AUTH_CRED_USERNAME, 276 username_prompt_first_creds, 277 username_prompt_next_creds, 278 NULL, 279}; 280 281 282/* Public API */ 283void 284svn_auth_get_username_prompt_provider 285 (svn_auth_provider_object_t **provider, 286 svn_auth_username_prompt_func_t prompt_func, 287 void *prompt_baton, 288 int retry_limit, 289 apr_pool_t *pool) 290{ 291 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 292 username_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb)); 293 294 pb->prompt_func = prompt_func; 295 pb->prompt_baton = prompt_baton; 296 pb->retry_limit = retry_limit; 297 298 po->vtable = &username_prompt_provider; 299 po->provider_baton = pb; 300 *provider = po; 301} 302