2 * simple_providers.c: providers for SVN_AUTH_CRED_SIMPLE
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
24 /* ==================================================================== */
30 #include <apr_pools.h>
32 #include "svn_dirent_uri.h"
34 #include "svn_pools.h"
35 #include "svn_error.h"
37 #include "svn_config.h"
40 #include "private/svn_auth_private.h"
42 #include "svn_private_config.h"
46 /*-----------------------------------------------------------------------*/
48 /*-----------------------------------------------------------------------*/
50 /* Baton type for the simple provider. */
51 typedef struct simple_provider_baton_t
53 svn_auth_plaintext_prompt_func_t plaintext_prompt_func;
55 /* We cache the user's answer to the plaintext prompt, keyed
56 * by realm, in case we'll be called multiple times for the
58 apr_hash_t *plaintext_answers;
59 } simple_provider_baton_t;
62 /* Implementation of svn_auth__password_get_t that retrieves
63 the plaintext password from CREDS. */
65 svn_auth__simple_password_get(svn_boolean_t *done,
66 const char **password,
68 const char *realmstring,
70 apr_hash_t *parameters,
71 svn_boolean_t non_interactive,
78 str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_USERNAME_KEY);
79 if (str && username && strcmp(str->data, username) == 0)
81 str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_PASSWORD_KEY);
84 *password = str->data;
92 /* Implementation of svn_auth__password_set_t that stores
93 the plaintext password in CREDS. */
95 svn_auth__simple_password_set(svn_boolean_t *done,
97 const char *realmstring,
100 apr_hash_t *parameters,
101 svn_boolean_t non_interactive,
104 svn_hash_sets(creds, SVN_CONFIG_AUTHN_PASSWORD_KEY,
105 svn_string_create(password, pool));
111 /* Set **USERNAME to the username retrieved from CREDS; ignore
112 other parameters. *USERNAME will have the same lifetime as CREDS. */
114 simple_username_get(const char **username,
116 const char *realmstring,
117 svn_boolean_t non_interactive)
120 str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_USERNAME_KEY);
121 if (str && str->data)
123 *username = str->data;
131 svn_auth__simple_creds_cache_get(void **credentials,
133 void *provider_baton,
134 apr_hash_t *parameters,
135 const char *realmstring,
136 svn_auth__password_get_t password_get,
137 const char *passtype,
140 const char *config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
141 svn_config_t *cfg = svn_hash_gets(parameters,
142 SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS);
143 const char *server_group = svn_hash_gets(parameters,
144 SVN_AUTH_PARAM_SERVER_GROUP);
145 const char *username = svn_hash_gets(parameters,
146 SVN_AUTH_PARAM_DEFAULT_USERNAME);
147 const char *password = svn_hash_gets(parameters,
148 SVN_AUTH_PARAM_DEFAULT_PASSWORD);
149 svn_boolean_t non_interactive = svn_hash_gets(parameters,
150 SVN_AUTH_PARAM_NON_INTERACTIVE)
152 const char *default_username = NULL; /* Default username from cache. */
153 const char *default_password = NULL; /* Default password from cache. */
155 /* This checks if we should save the CREDS, iff saving the credentials is
156 allowed by the run-time configuration. */
157 svn_boolean_t need_to_save = FALSE;
158 apr_hash_t *creds_hash = NULL;
162 /* Try to load credentials from a file on disk, based on the
163 realmstring. Don't throw an error, though: if something went
164 wrong reading the file, no big deal. What really matters is that
165 we failed to get the creds, so allow the auth system to try the
167 err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
168 realmstring, config_dir, pool);
171 svn_error_clear(err);
176 /* We have something in the auth cache for this realm. */
177 svn_boolean_t have_passtype = FALSE;
179 /* The password type in the auth data must match the
180 mangler's type, otherwise the password must be
181 interpreted by another provider. */
182 str = svn_hash_gets(creds_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY);
183 if (str && str->data)
184 if (passtype && (0 == strcmp(str->data, passtype)))
185 have_passtype = TRUE;
187 /* See if we need to save this username if it is not present in
191 if (!simple_username_get(&default_username, creds_hash, realmstring,
198 if (strcmp(default_username, username) != 0)
203 /* See if we need to save this password if it is not present in
211 SVN_ERR(password_get(&done, &default_password, creds_hash,
212 realmstring, username, parameters,
213 non_interactive, pool));
220 if (strcmp(default_password, password) != 0)
226 /* If we don't have a username and a password yet, we try the
228 if (! (username && password))
231 if (!simple_username_get(&username, creds_hash, realmstring,
235 if (username && ! password)
243 SVN_ERR(password_get(&done, &password, creds_hash,
244 realmstring, username, parameters,
245 non_interactive, pool));
249 /* If the auth data didn't contain a password type,
250 force a write to upgrade the format of the auth
252 if (password && ! have_passtype)
260 /* Nothing was present in the auth cache, so indicate that these
261 credentials should be saved. */
265 /* If we don't have a username yet, check the 'servers' file */
268 username = svn_config_get_server_setting(cfg, server_group,
269 SVN_CONFIG_OPTION_USERNAME,
273 /* Ask the OS for the username if we have a password but no
275 if (password && ! username)
276 username = svn_user_get_name(pool);
278 if (username && password)
280 svn_auth_cred_simple_t *creds = apr_pcalloc(pool, sizeof(*creds));
281 creds->username = username;
282 creds->password = password;
283 creds->may_save = need_to_save;
284 *credentials = creds;
296 svn_auth__simple_creds_cache_set(svn_boolean_t *saved,
298 void *provider_baton,
299 apr_hash_t *parameters,
300 const char *realmstring,
301 svn_auth__password_set_t password_set,
302 const char *passtype,
305 svn_auth_cred_simple_t *creds = credentials;
306 apr_hash_t *creds_hash = NULL;
307 const char *config_dir;
309 svn_boolean_t dont_store_passwords =
310 svn_hash_gets(parameters, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL;
311 svn_boolean_t non_interactive = svn_hash_gets(parameters,
312 SVN_AUTH_PARAM_NON_INTERACTIVE)
314 svn_boolean_t no_auth_cache =
315 (! creds->may_save) || (svn_hash_gets(parameters,
316 SVN_AUTH_PARAM_NO_AUTH_CACHE)
319 /* Make sure we've been passed a passtype. */
320 SVN_ERR_ASSERT(passtype != NULL);
327 config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
329 /* Put the username into the credentials hash. */
330 creds_hash = apr_hash_make(pool);
331 svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_USERNAME_KEY,
332 svn_string_create(creds->username, pool));
334 /* Don't store passwords in any form if the user has told
335 * us not to do so. */
336 if (! dont_store_passwords)
338 svn_boolean_t may_save_password = FALSE;
340 /* If the password is going to be stored encrypted, go right
341 * ahead and store it to disk. Else determine whether saving
342 * in plaintext is OK. */
344 (strcmp(passtype, SVN_AUTH__WINCRYPT_PASSWORD_TYPE) == 0
345 || strcmp(passtype, SVN_AUTH__KEYCHAIN_PASSWORD_TYPE) == 0
346 || strcmp(passtype, SVN_AUTH__KWALLET_PASSWORD_TYPE) == 0
347 || strcmp(passtype, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE) == 0
348 || strcmp(passtype, SVN_AUTH__GPG_AGENT_PASSWORD_TYPE) == 0))
350 may_save_password = TRUE;
354 #ifdef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
355 may_save_password = FALSE;
357 const char *store_plaintext_passwords =
358 svn_hash_gets(parameters, SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS);
359 simple_provider_baton_t *b =
360 (simple_provider_baton_t *)provider_baton;
362 if (store_plaintext_passwords
363 && svn_cstring_casecmp(store_plaintext_passwords,
364 SVN_CONFIG_ASK) == 0)
367 /* In non-interactive mode, the default behaviour is
368 * to not store the password, because it is usually
369 * passed on the command line. */
370 may_save_password = FALSE;
371 else if (b->plaintext_prompt_func)
373 /* We're interactive, and the client provided a
374 * prompt callback. So we can ask the user.
376 * Check for a cached answer before prompting. */
377 svn_boolean_t *cached_answer;
378 cached_answer = svn_hash_gets(b->plaintext_answers,
380 if (cached_answer != NULL)
381 may_save_password = *cached_answer;
384 apr_pool_t *cached_answer_pool;
386 /* Nothing cached for this realm, prompt the user. */
387 SVN_ERR((*b->plaintext_prompt_func)(&may_save_password,
392 /* Cache the user's answer in case we're called again
393 * for the same realm.
395 * We allocate the answer cache in the hash table's pool
396 * to make sure that is has the same life time as the
397 * hash table itself. This means that the answer will
398 * survive across RA sessions -- which is important,
399 * because otherwise we'd prompt users once per RA session.
401 cached_answer_pool = apr_hash_pool_get(b->plaintext_answers);
402 cached_answer = apr_palloc(cached_answer_pool,
403 sizeof(svn_boolean_t));
404 *cached_answer = may_save_password;
405 svn_hash_sets(b->plaintext_answers, realmstring,
411 /* TODO: We might want to default to not storing if the
412 * prompt callback is NULL, i.e. have may_save_password
413 * default to FALSE here, in order to force clients to
414 * implement the callback.
416 * This would change the semantics of old API though.
418 * So for now, clients that don't implement the callback
419 * and provide no explicit value for
420 * SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS
421 * cause unencrypted passwords to be stored by default.
422 * Needless to say, our own client is sane, but who knows
423 * what other clients are doing.
425 may_save_password = TRUE;
428 else if (store_plaintext_passwords
429 && svn_cstring_casecmp(store_plaintext_passwords,
430 SVN_CONFIG_FALSE) == 0)
432 may_save_password = FALSE;
434 else if (!store_plaintext_passwords
435 || svn_cstring_casecmp(store_plaintext_passwords,
436 SVN_CONFIG_TRUE) == 0)
438 may_save_password = TRUE;
442 return svn_error_createf
443 (SVN_ERR_BAD_CONFIG_VALUE, NULL,
444 _("Config error: invalid value '%s' for option '%s'"),
445 store_plaintext_passwords,
446 SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS);
451 if (may_save_password)
453 SVN_ERR(password_set(saved, creds_hash, realmstring,
454 creds->username, creds->password,
455 parameters, non_interactive, pool));
456 if (*saved && passtype)
457 /* Store the password type with the auth data, so that we
458 know which provider owns the password. */
459 svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY,
460 svn_string_create(passtype, pool));
464 /* Save credentials to disk. */
465 err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_SIMPLE,
466 realmstring, config_dir, pool);
470 /* ### return error? */
471 svn_error_clear(err);
476 /* Get cached (unencrypted) credentials from the simple provider's cache. */
478 simple_first_creds(void **credentials,
480 void *provider_baton,
481 apr_hash_t *parameters,
482 const char *realmstring,
485 return svn_auth__simple_creds_cache_get(credentials, iter_baton,
486 provider_baton, parameters,
488 svn_auth__simple_password_get,
489 SVN_AUTH__SIMPLE_PASSWORD_TYPE,
493 /* Save (unencrypted) credentials to the simple provider's cache. */
495 simple_save_creds(svn_boolean_t *saved,
497 void *provider_baton,
498 apr_hash_t *parameters,
499 const char *realmstring,
502 return svn_auth__simple_creds_cache_set(saved, credentials, provider_baton,
503 parameters, realmstring,
504 svn_auth__simple_password_set,
505 SVN_AUTH__SIMPLE_PASSWORD_TYPE,
509 static const svn_auth_provider_t simple_provider = {
510 SVN_AUTH_CRED_SIMPLE,
519 svn_auth_get_simple_provider2
520 (svn_auth_provider_object_t **provider,
521 svn_auth_plaintext_prompt_func_t plaintext_prompt_func,
525 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
526 simple_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
528 pb->plaintext_prompt_func = plaintext_prompt_func;
529 pb->prompt_baton = prompt_baton;
530 pb->plaintext_answers = apr_hash_make(pool);
532 po->vtable = &simple_provider;
533 po->provider_baton = pb;
538 /*-----------------------------------------------------------------------*/
539 /* Prompt provider */
540 /*-----------------------------------------------------------------------*/
542 /* Baton type for username/password prompting. */
543 typedef struct simple_prompt_provider_baton_t
545 svn_auth_simple_prompt_func_t prompt_func;
548 /* how many times to re-prompt after the first one fails */
550 } simple_prompt_provider_baton_t;
553 /* Iteration baton type for username/password prompting. */
554 typedef struct simple_prompt_iter_baton_t
556 /* how many times we've reprompted */
558 } simple_prompt_iter_baton_t;
562 /*** Helper Functions ***/
564 prompt_for_simple_creds(svn_auth_cred_simple_t **cred_p,
565 simple_prompt_provider_baton_t *pb,
566 apr_hash_t *parameters,
567 const char *realmstring,
568 svn_boolean_t first_time,
569 svn_boolean_t may_save,
572 const char *default_username = NULL;
573 const char *default_password = NULL;
577 /* If we're allowed to check for default usernames and passwords, do
581 default_username = svn_hash_gets(parameters,
582 SVN_AUTH_PARAM_DEFAULT_USERNAME);
584 /* No default username? Try the auth cache. */
585 if (! default_username)
587 const char *config_dir = svn_hash_gets(parameters,
588 SVN_AUTH_PARAM_CONFIG_DIR);
589 apr_hash_t *creds_hash = NULL;
593 err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
594 realmstring, config_dir, pool);
595 svn_error_clear(err);
596 if (! err && creds_hash)
598 str = svn_hash_gets(creds_hash, SVN_CONFIG_AUTHN_USERNAME_KEY);
599 if (str && str->data)
600 default_username = str->data;
604 /* Still no default username? Try the 'servers' file. */
605 if (! default_username)
607 svn_config_t *cfg = svn_hash_gets(parameters,
608 SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS);
609 const char *server_group = svn_hash_gets(parameters,
610 SVN_AUTH_PARAM_SERVER_GROUP);
612 svn_config_get_server_setting(cfg, server_group,
613 SVN_CONFIG_OPTION_USERNAME,
617 /* Still no default username? Try the UID. */
618 if (! default_username)
619 default_username = svn_user_get_name(pool);
621 default_password = svn_hash_gets(parameters,
622 SVN_AUTH_PARAM_DEFAULT_PASSWORD);
625 /* If we have defaults, just build the cred here and return it.
627 * ### I do wonder why this is here instead of in a separate
628 * ### 'defaults' provider that would run before the prompt
629 * ### provider... Hmmm.
631 if (default_username && default_password)
633 *cred_p = apr_palloc(pool, sizeof(**cred_p));
634 (*cred_p)->username = apr_pstrdup(pool, default_username);
635 (*cred_p)->password = apr_pstrdup(pool, default_password);
636 (*cred_p)->may_save = TRUE;
640 SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring,
641 default_username, may_save, pool));
648 /* Our first attempt will use any default username/password passed
649 in, and prompt for the remaining stuff. */
651 simple_prompt_first_creds(void **credentials_p,
653 void *provider_baton,
654 apr_hash_t *parameters,
655 const char *realmstring,
658 simple_prompt_provider_baton_t *pb = provider_baton;
659 simple_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton));
660 const char *no_auth_cache = svn_hash_gets(parameters,
661 SVN_AUTH_PARAM_NO_AUTH_CACHE);
663 SVN_ERR(prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
664 pb, parameters, realmstring, TRUE,
665 ! no_auth_cache, pool));
668 *iter_baton = ibaton;
674 /* Subsequent attempts to fetch will ignore the default values, and
675 simply re-prompt for both, up to a maximum of ib->pb->retry_limit. */
677 simple_prompt_next_creds(void **credentials_p,
679 void *provider_baton,
680 apr_hash_t *parameters,
681 const char *realmstring,
684 simple_prompt_iter_baton_t *ib = iter_baton;
685 simple_prompt_provider_baton_t *pb = provider_baton;
686 const char *no_auth_cache = svn_hash_gets(parameters,
687 SVN_AUTH_PARAM_NO_AUTH_CACHE);
689 if ((pb->retry_limit >= 0) && (ib->retries >= pb->retry_limit))
691 /* give up, go on to next provider. */
692 *credentials_p = NULL;
697 return prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
698 pb, parameters, realmstring, FALSE,
699 ! no_auth_cache, pool);
702 static const svn_auth_provider_t simple_prompt_provider = {
703 SVN_AUTH_CRED_SIMPLE,
704 simple_prompt_first_creds,
705 simple_prompt_next_creds,
712 svn_auth_get_simple_prompt_provider
713 (svn_auth_provider_object_t **provider,
714 svn_auth_simple_prompt_func_t prompt_func,
719 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
720 simple_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
722 pb->prompt_func = prompt_func;
723 pb->prompt_baton = prompt_baton;
724 pb->retry_limit = retry_limit;
726 po->vtable = &simple_prompt_provider;
727 po->provider_baton = pb;