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/* The key that will be stored on disk. Serves the same role as similar 44 constants in other providers. */ 45#define AUTHN_USERNAME_KEY "username" 46 47 48 49/*** Username-only Provider ***/ 50static svn_error_t * 51username_first_creds(void **credentials, 52 void **iter_baton, 53 void *provider_baton, 54 apr_hash_t *parameters, 55 const char *realmstring, 56 apr_pool_t *pool) 57{ 58 const char *config_dir = svn_hash_gets(parameters, 59 SVN_AUTH_PARAM_CONFIG_DIR); 60 const char *username = svn_hash_gets(parameters, 61 SVN_AUTH_PARAM_DEFAULT_USERNAME); 62 svn_boolean_t may_save = !! username; 63 svn_error_t *err; 64 65 /* If we don't have a usename yet, try the auth cache */ 66 if (! username) 67 { 68 apr_hash_t *creds_hash = NULL; 69 70 /* Try to load credentials from a file on disk, based on the 71 realmstring. Don't throw an error, though: if something went 72 wrong reading the file, no big deal. What really matters is that 73 we failed to get the creds, so allow the auth system to try the 74 next provider. */ 75 err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_USERNAME, 76 realmstring, config_dir, pool); 77 svn_error_clear(err); 78 if (! err && creds_hash) 79 { 80 svn_string_t *str = svn_hash_gets(creds_hash, AUTHN_USERNAME_KEY); 81 if (str && str->data) 82 username = str->data; 83 } 84 } 85 86 /* If that failed, ask the OS for the username */ 87 if (! username) 88 username = svn_user_get_name(pool); 89 90 if (username) 91 { 92 svn_auth_cred_simple_t *creds = apr_pcalloc(pool, sizeof(*creds)); 93 creds->username = username; 94 creds->may_save = may_save; 95 *credentials = creds; 96 } 97 else 98 *credentials = NULL; 99 100 *iter_baton = NULL; 101 102 return SVN_NO_ERROR; 103} 104 105 106static svn_error_t * 107username_save_creds(svn_boolean_t *saved, 108 void *credentials, 109 void *provider_baton, 110 apr_hash_t *parameters, 111 const char *realmstring, 112 apr_pool_t *pool) 113{ 114 svn_auth_cred_simple_t *creds = credentials; 115 apr_hash_t *creds_hash = NULL; 116 const char *config_dir; 117 svn_error_t *err; 118 119 *saved = FALSE; 120 121 if (! creds->may_save) 122 return SVN_NO_ERROR; 123 124 config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR); 125 126 /* Put the credentials in a hash and save it to disk */ 127 creds_hash = apr_hash_make(pool); 128 svn_hash_sets(creds_hash, AUTHN_USERNAME_KEY, 129 svn_string_create(creds->username, pool)); 130 err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_USERNAME, 131 realmstring, config_dir, pool); 132 svn_error_clear(err); 133 *saved = ! err; 134 135 return SVN_NO_ERROR; 136} 137 138 139static const svn_auth_provider_t username_provider = { 140 SVN_AUTH_CRED_USERNAME, 141 username_first_creds, 142 NULL, 143 username_save_creds 144}; 145 146 147/* Public API */ 148void 149svn_auth_get_username_provider(svn_auth_provider_object_t **provider, 150 apr_pool_t *pool) 151{ 152 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 153 154 po->vtable = &username_provider; 155 *provider = po; 156} 157 158 159/*-----------------------------------------------------------------------*/ 160/* Prompt provider */ 161/*-----------------------------------------------------------------------*/ 162 163/* Baton type for username-only prompting. */ 164typedef struct username_prompt_provider_baton_t 165{ 166 svn_auth_username_prompt_func_t prompt_func; 167 void *prompt_baton; 168 169 /* how many times to re-prompt after the first one fails */ 170 int retry_limit; 171} username_prompt_provider_baton_t; 172 173 174/* Iteration baton type for username-only prompting. */ 175typedef struct username_prompt_iter_baton_t 176{ 177 /* how many times we've reprompted */ 178 int retries; 179 180} username_prompt_iter_baton_t; 181 182 183/*** Helper Functions ***/ 184static svn_error_t * 185prompt_for_username_creds(svn_auth_cred_username_t **cred_p, 186 username_prompt_provider_baton_t *pb, 187 apr_hash_t *parameters, 188 const char *realmstring, 189 svn_boolean_t first_time, 190 svn_boolean_t may_save, 191 apr_pool_t *pool) 192{ 193 const char *def_username = NULL; 194 195 *cred_p = NULL; 196 197 /* If we're allowed to check for default usernames, do so. */ 198 if (first_time) 199 def_username = svn_hash_gets(parameters, SVN_AUTH_PARAM_DEFAULT_USERNAME); 200 201 /* If we have defaults, just build the cred here and return it. 202 * 203 * ### I do wonder why this is here instead of in a separate 204 * ### 'defaults' provider that would run before the prompt 205 * ### provider... Hmmm. 206 */ 207 if (def_username) 208 { 209 *cred_p = apr_palloc(pool, sizeof(**cred_p)); 210 (*cred_p)->username = apr_pstrdup(pool, def_username); 211 (*cred_p)->may_save = TRUE; 212 } 213 else 214 { 215 SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring, 216 may_save, pool)); 217 } 218 219 return SVN_NO_ERROR; 220} 221 222 223/* Our first attempt will use any default username passed 224 in, and prompt for the remaining stuff. */ 225static svn_error_t * 226username_prompt_first_creds(void **credentials_p, 227 void **iter_baton, 228 void *provider_baton, 229 apr_hash_t *parameters, 230 const char *realmstring, 231 apr_pool_t *pool) 232{ 233 username_prompt_provider_baton_t *pb = provider_baton; 234 username_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton)); 235 const char *no_auth_cache = svn_hash_gets(parameters, 236 SVN_AUTH_PARAM_NO_AUTH_CACHE); 237 238 SVN_ERR(prompt_for_username_creds 239 ((svn_auth_cred_username_t **) credentials_p, pb, 240 parameters, realmstring, TRUE, ! no_auth_cache, pool)); 241 242 ibaton->retries = 0; 243 *iter_baton = ibaton; 244 245 return SVN_NO_ERROR; 246} 247 248 249/* Subsequent attempts to fetch will ignore the default username 250 value, and simply re-prompt for the username, up to a maximum of 251 ib->pb->retry_limit. */ 252static svn_error_t * 253username_prompt_next_creds(void **credentials_p, 254 void *iter_baton, 255 void *provider_baton, 256 apr_hash_t *parameters, 257 const char *realmstring, 258 apr_pool_t *pool) 259{ 260 username_prompt_iter_baton_t *ib = iter_baton; 261 username_prompt_provider_baton_t *pb = provider_baton; 262 const char *no_auth_cache = svn_hash_gets(parameters, 263 SVN_AUTH_PARAM_NO_AUTH_CACHE); 264 265 if ((pb->retry_limit >= 0) && (ib->retries >= pb->retry_limit)) 266 { 267 /* give up, go on to next provider. */ 268 *credentials_p = NULL; 269 return SVN_NO_ERROR; 270 } 271 ib->retries++; 272 273 return prompt_for_username_creds 274 ((svn_auth_cred_username_t **) credentials_p, pb, 275 parameters, realmstring, FALSE, ! no_auth_cache, pool); 276} 277 278 279static const svn_auth_provider_t username_prompt_provider = { 280 SVN_AUTH_CRED_USERNAME, 281 username_prompt_first_creds, 282 username_prompt_next_creds, 283 NULL, 284}; 285 286 287/* Public API */ 288void 289svn_auth_get_username_prompt_provider 290 (svn_auth_provider_object_t **provider, 291 svn_auth_username_prompt_func_t prompt_func, 292 void *prompt_baton, 293 int retry_limit, 294 apr_pool_t *pool) 295{ 296 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 297 username_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb)); 298 299 pb->prompt_func = prompt_func; 300 pb->prompt_baton = prompt_baton; 301 pb->retry_limit = retry_limit; 302 303 po->vtable = &username_prompt_provider; 304 po->provider_baton = pb; 305 *provider = po; 306} 307