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 /* The keys that will be stored on disk. These serve the same role as
51 similar constants in other providers. */
52 #define AUTHN_USERNAME_KEY "username"
53 #define AUTHN_PASSWORD_KEY "password"
54 #define AUTHN_PASSTYPE_KEY "passtype"
56 /* Baton type for the simple provider. */
57 typedef struct simple_provider_baton_t
59 svn_auth_plaintext_prompt_func_t plaintext_prompt_func;
61 /* We cache the user's answer to the plaintext prompt, keyed
62 * by realm, in case we'll be called multiple times for the
64 apr_hash_t *plaintext_answers;
65 } simple_provider_baton_t;
68 /* Implementation of svn_auth__password_get_t that retrieves
69 the plaintext password from CREDS. */
71 svn_auth__simple_password_get(svn_boolean_t *done,
72 const char **password,
74 const char *realmstring,
76 apr_hash_t *parameters,
77 svn_boolean_t non_interactive,
84 str = svn_hash_gets(creds, AUTHN_USERNAME_KEY);
85 if (str && username && strcmp(str->data, username) == 0)
87 str = svn_hash_gets(creds, AUTHN_PASSWORD_KEY);
90 *password = str->data;
98 /* Implementation of svn_auth__password_set_t that stores
99 the plaintext password in CREDS. */
101 svn_auth__simple_password_set(svn_boolean_t *done,
103 const char *realmstring,
104 const char *username,
105 const char *password,
106 apr_hash_t *parameters,
107 svn_boolean_t non_interactive,
110 svn_hash_sets(creds, AUTHN_PASSWORD_KEY, svn_string_create(password, pool));
116 /* Set **USERNAME to the username retrieved from CREDS; ignore
117 other parameters. *USERNAME will have the same lifetime as CREDS. */
119 simple_username_get(const char **username,
121 const char *realmstring,
122 svn_boolean_t non_interactive)
125 str = svn_hash_gets(creds, AUTHN_USERNAME_KEY);
126 if (str && str->data)
128 *username = str->data;
136 svn_auth__simple_creds_cache_get(void **credentials,
138 void *provider_baton,
139 apr_hash_t *parameters,
140 const char *realmstring,
141 svn_auth__password_get_t password_get,
142 const char *passtype,
145 const char *config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
146 svn_config_t *cfg = svn_hash_gets(parameters,
147 SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS);
148 const char *server_group = svn_hash_gets(parameters,
149 SVN_AUTH_PARAM_SERVER_GROUP);
150 const char *username = svn_hash_gets(parameters,
151 SVN_AUTH_PARAM_DEFAULT_USERNAME);
152 const char *password = svn_hash_gets(parameters,
153 SVN_AUTH_PARAM_DEFAULT_PASSWORD);
154 svn_boolean_t non_interactive = svn_hash_gets(parameters,
155 SVN_AUTH_PARAM_NON_INTERACTIVE)
157 const char *default_username = NULL; /* Default username from cache. */
158 const char *default_password = NULL; /* Default password from cache. */
160 /* This checks if we should save the CREDS, iff saving the credentials is
161 allowed by the run-time configuration. */
162 svn_boolean_t need_to_save = FALSE;
163 apr_hash_t *creds_hash = NULL;
167 /* Try to load credentials from a file on disk, based on the
168 realmstring. Don't throw an error, though: if something went
169 wrong reading the file, no big deal. What really matters is that
170 we failed to get the creds, so allow the auth system to try the
172 err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
173 realmstring, config_dir, pool);
176 svn_error_clear(err);
181 /* We have something in the auth cache for this realm. */
182 svn_boolean_t have_passtype = FALSE;
184 /* The password type in the auth data must match the
185 mangler's type, otherwise the password must be
186 interpreted by another provider. */
187 str = svn_hash_gets(creds_hash, AUTHN_PASSTYPE_KEY);
188 if (str && str->data)
189 if (passtype && (0 == strcmp(str->data, passtype)))
190 have_passtype = TRUE;
192 /* See if we need to save this username if it is not present in
196 if (!simple_username_get(&default_username, creds_hash, realmstring,
203 if (strcmp(default_username, username) != 0)
208 /* See if we need to save this password if it is not present in
216 SVN_ERR(password_get(&done, &default_password, creds_hash,
217 realmstring, username, parameters,
218 non_interactive, pool));
225 if (strcmp(default_password, password) != 0)
231 /* If we don't have a username and a password yet, we try the
233 if (! (username && password))
236 if (!simple_username_get(&username, creds_hash, realmstring,
240 if (username && ! password)
248 SVN_ERR(password_get(&done, &password, creds_hash,
249 realmstring, username, parameters,
250 non_interactive, pool));
254 /* If the auth data didn't contain a password type,
255 force a write to upgrade the format of the auth
257 if (password && ! have_passtype)
265 /* Nothing was present in the auth cache, so indicate that these
266 credentials should be saved. */
270 /* If we don't have a username yet, check the 'servers' file */
273 username = svn_config_get_server_setting(cfg, server_group,
274 SVN_CONFIG_OPTION_USERNAME,
278 /* Ask the OS for the username if we have a password but no
280 if (password && ! username)
281 username = svn_user_get_name(pool);
283 if (username && password)
285 svn_auth_cred_simple_t *creds = apr_pcalloc(pool, sizeof(*creds));
286 creds->username = username;
287 creds->password = password;
288 creds->may_save = need_to_save;
289 *credentials = creds;
301 svn_auth__simple_creds_cache_set(svn_boolean_t *saved,
303 void *provider_baton,
304 apr_hash_t *parameters,
305 const char *realmstring,
306 svn_auth__password_set_t password_set,
307 const char *passtype,
310 svn_auth_cred_simple_t *creds = credentials;
311 apr_hash_t *creds_hash = NULL;
312 const char *config_dir;
314 svn_boolean_t dont_store_passwords =
315 svn_hash_gets(parameters, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL;
316 svn_boolean_t non_interactive = svn_hash_gets(parameters,
317 SVN_AUTH_PARAM_NON_INTERACTIVE)
319 svn_boolean_t no_auth_cache =
320 (! creds->may_save) || (svn_hash_gets(parameters,
321 SVN_AUTH_PARAM_NO_AUTH_CACHE)
324 /* Make sure we've been passed a passtype. */
325 SVN_ERR_ASSERT(passtype != NULL);
332 config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
334 /* Put the username into the credentials hash. */
335 creds_hash = apr_hash_make(pool);
336 svn_hash_sets(creds_hash, AUTHN_USERNAME_KEY,
337 svn_string_create(creds->username, pool));
339 /* Don't store passwords in any form if the user has told
340 * us not to do so. */
341 if (! dont_store_passwords)
343 svn_boolean_t may_save_password = FALSE;
345 /* If the password is going to be stored encrypted, go right
346 * ahead and store it to disk. Else determine whether saving
347 * in plaintext is OK. */
349 (strcmp(passtype, SVN_AUTH__WINCRYPT_PASSWORD_TYPE) == 0
350 || strcmp(passtype, SVN_AUTH__KEYCHAIN_PASSWORD_TYPE) == 0
351 || strcmp(passtype, SVN_AUTH__KWALLET_PASSWORD_TYPE) == 0
352 || strcmp(passtype, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE) == 0
353 || strcmp(passtype, SVN_AUTH__GPG_AGENT_PASSWORD_TYPE) == 0))
355 may_save_password = TRUE;
359 #ifdef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
360 may_save_password = FALSE;
362 const char *store_plaintext_passwords =
363 svn_hash_gets(parameters, SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS);
364 simple_provider_baton_t *b =
365 (simple_provider_baton_t *)provider_baton;
367 if (store_plaintext_passwords
368 && svn_cstring_casecmp(store_plaintext_passwords,
369 SVN_CONFIG_ASK) == 0)
372 /* In non-interactive mode, the default behaviour is
373 * to not store the password, because it is usually
374 * passed on the command line. */
375 may_save_password = FALSE;
376 else if (b->plaintext_prompt_func)
378 /* We're interactive, and the client provided a
379 * prompt callback. So we can ask the user.
381 * Check for a cached answer before prompting. */
382 svn_boolean_t *cached_answer;
383 cached_answer = svn_hash_gets(b->plaintext_answers,
385 if (cached_answer != NULL)
386 may_save_password = *cached_answer;
389 apr_pool_t *cached_answer_pool;
391 /* Nothing cached for this realm, prompt the user. */
392 SVN_ERR((*b->plaintext_prompt_func)(&may_save_password,
397 /* Cache the user's answer in case we're called again
398 * for the same realm.
400 * We allocate the answer cache in the hash table's pool
401 * to make sure that is has the same life time as the
402 * hash table itself. This means that the answer will
403 * survive across RA sessions -- which is important,
404 * because otherwise we'd prompt users once per RA session.
406 cached_answer_pool = apr_hash_pool_get(b->plaintext_answers);
407 cached_answer = apr_palloc(cached_answer_pool,
408 sizeof(svn_boolean_t));
409 *cached_answer = may_save_password;
410 svn_hash_sets(b->plaintext_answers, realmstring,
416 /* TODO: We might want to default to not storing if the
417 * prompt callback is NULL, i.e. have may_save_password
418 * default to FALSE here, in order to force clients to
419 * implement the callback.
421 * This would change the semantics of old API though.
423 * So for now, clients that don't implement the callback
424 * and provide no explicit value for
425 * SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS
426 * cause unencrypted passwords to be stored by default.
427 * Needless to say, our own client is sane, but who knows
428 * what other clients are doing.
430 may_save_password = TRUE;
433 else if (store_plaintext_passwords
434 && svn_cstring_casecmp(store_plaintext_passwords,
435 SVN_CONFIG_FALSE) == 0)
437 may_save_password = FALSE;
439 else if (!store_plaintext_passwords
440 || svn_cstring_casecmp(store_plaintext_passwords,
441 SVN_CONFIG_TRUE) == 0)
443 may_save_password = TRUE;
447 return svn_error_createf
448 (SVN_ERR_BAD_CONFIG_VALUE, NULL,
449 _("Config error: invalid value '%s' for option '%s'"),
450 store_plaintext_passwords,
451 SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS);
456 if (may_save_password)
458 SVN_ERR(password_set(saved, creds_hash, realmstring,
459 creds->username, creds->password,
460 parameters, non_interactive, pool));
461 if (*saved && passtype)
462 /* Store the password type with the auth data, so that we
463 know which provider owns the password. */
464 svn_hash_sets(creds_hash, AUTHN_PASSTYPE_KEY,
465 svn_string_create(passtype, pool));
469 /* Save credentials to disk. */
470 err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_SIMPLE,
471 realmstring, config_dir, pool);
475 /* ### return error? */
476 svn_error_clear(err);
481 /* Get cached (unencrypted) credentials from the simple provider's cache. */
483 simple_first_creds(void **credentials,
485 void *provider_baton,
486 apr_hash_t *parameters,
487 const char *realmstring,
490 return svn_auth__simple_creds_cache_get(credentials, iter_baton,
491 provider_baton, parameters,
493 svn_auth__simple_password_get,
494 SVN_AUTH__SIMPLE_PASSWORD_TYPE,
498 /* Save (unencrypted) credentials to the simple provider's cache. */
500 simple_save_creds(svn_boolean_t *saved,
502 void *provider_baton,
503 apr_hash_t *parameters,
504 const char *realmstring,
507 return svn_auth__simple_creds_cache_set(saved, credentials, provider_baton,
508 parameters, realmstring,
509 svn_auth__simple_password_set,
510 SVN_AUTH__SIMPLE_PASSWORD_TYPE,
514 static const svn_auth_provider_t simple_provider = {
515 SVN_AUTH_CRED_SIMPLE,
524 svn_auth_get_simple_provider2
525 (svn_auth_provider_object_t **provider,
526 svn_auth_plaintext_prompt_func_t plaintext_prompt_func,
530 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
531 simple_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
533 pb->plaintext_prompt_func = plaintext_prompt_func;
534 pb->prompt_baton = prompt_baton;
535 pb->plaintext_answers = apr_hash_make(pool);
537 po->vtable = &simple_provider;
538 po->provider_baton = pb;
543 /*-----------------------------------------------------------------------*/
544 /* Prompt provider */
545 /*-----------------------------------------------------------------------*/
547 /* Baton type for username/password prompting. */
548 typedef struct simple_prompt_provider_baton_t
550 svn_auth_simple_prompt_func_t prompt_func;
553 /* how many times to re-prompt after the first one fails */
555 } simple_prompt_provider_baton_t;
558 /* Iteration baton type for username/password prompting. */
559 typedef struct simple_prompt_iter_baton_t
561 /* how many times we've reprompted */
563 } simple_prompt_iter_baton_t;
567 /*** Helper Functions ***/
569 prompt_for_simple_creds(svn_auth_cred_simple_t **cred_p,
570 simple_prompt_provider_baton_t *pb,
571 apr_hash_t *parameters,
572 const char *realmstring,
573 svn_boolean_t first_time,
574 svn_boolean_t may_save,
577 const char *default_username = NULL;
578 const char *default_password = NULL;
582 /* If we're allowed to check for default usernames and passwords, do
586 default_username = svn_hash_gets(parameters,
587 SVN_AUTH_PARAM_DEFAULT_USERNAME);
589 /* No default username? Try the auth cache. */
590 if (! default_username)
592 const char *config_dir = svn_hash_gets(parameters,
593 SVN_AUTH_PARAM_CONFIG_DIR);
594 apr_hash_t *creds_hash = NULL;
598 err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
599 realmstring, config_dir, pool);
600 svn_error_clear(err);
601 if (! err && creds_hash)
603 str = svn_hash_gets(creds_hash, AUTHN_USERNAME_KEY);
604 if (str && str->data)
605 default_username = str->data;
609 /* Still no default username? Try the 'servers' file. */
610 if (! default_username)
612 svn_config_t *cfg = svn_hash_gets(parameters,
613 SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS);
614 const char *server_group = svn_hash_gets(parameters,
615 SVN_AUTH_PARAM_SERVER_GROUP);
617 svn_config_get_server_setting(cfg, server_group,
618 SVN_CONFIG_OPTION_USERNAME,
622 /* Still no default username? Try the UID. */
623 if (! default_username)
624 default_username = svn_user_get_name(pool);
626 default_password = svn_hash_gets(parameters,
627 SVN_AUTH_PARAM_DEFAULT_PASSWORD);
630 /* If we have defaults, just build the cred here and return it.
632 * ### I do wonder why this is here instead of in a separate
633 * ### 'defaults' provider that would run before the prompt
634 * ### provider... Hmmm.
636 if (default_username && default_password)
638 *cred_p = apr_palloc(pool, sizeof(**cred_p));
639 (*cred_p)->username = apr_pstrdup(pool, default_username);
640 (*cred_p)->password = apr_pstrdup(pool, default_password);
641 (*cred_p)->may_save = TRUE;
645 SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring,
646 default_username, may_save, pool));
653 /* Our first attempt will use any default username/password passed
654 in, and prompt for the remaining stuff. */
656 simple_prompt_first_creds(void **credentials_p,
658 void *provider_baton,
659 apr_hash_t *parameters,
660 const char *realmstring,
663 simple_prompt_provider_baton_t *pb = provider_baton;
664 simple_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton));
665 const char *no_auth_cache = svn_hash_gets(parameters,
666 SVN_AUTH_PARAM_NO_AUTH_CACHE);
668 SVN_ERR(prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
669 pb, parameters, realmstring, TRUE,
670 ! no_auth_cache, pool));
673 *iter_baton = ibaton;
679 /* Subsequent attempts to fetch will ignore the default values, and
680 simply re-prompt for both, up to a maximum of ib->pb->retry_limit. */
682 simple_prompt_next_creds(void **credentials_p,
684 void *provider_baton,
685 apr_hash_t *parameters,
686 const char *realmstring,
689 simple_prompt_iter_baton_t *ib = iter_baton;
690 simple_prompt_provider_baton_t *pb = provider_baton;
691 const char *no_auth_cache = svn_hash_gets(parameters,
692 SVN_AUTH_PARAM_NO_AUTH_CACHE);
694 if ((pb->retry_limit >= 0) && (ib->retries >= pb->retry_limit))
696 /* give up, go on to next provider. */
697 *credentials_p = NULL;
702 return prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
703 pb, parameters, realmstring, FALSE,
704 ! no_auth_cache, pool);
707 static const svn_auth_provider_t simple_prompt_provider = {
708 SVN_AUTH_CRED_SIMPLE,
709 simple_prompt_first_creds,
710 simple_prompt_next_creds,
717 svn_auth_get_simple_prompt_provider
718 (svn_auth_provider_object_t **provider,
719 svn_auth_simple_prompt_func_t prompt_func,
724 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
725 simple_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
727 pb->prompt_func = prompt_func;
728 pb->prompt_baton = prompt_baton;
729 pb->retry_limit = retry_limit;
731 po->vtable = &simple_prompt_provider;
732 po->provider_baton = pb;