* subversion/libsvn_subr/validate.c
[svn.git] / subversion / libsvn_subr / simple_providers.c
blobf9cf829fe98d83cfd2e635e809f06fed2468c867
1 /*
2 * simple_providers.c: providers for SVN_AUTH_CRED_SIMPLE
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
23 /*** Includes. ***/
25 #include <apr_pools.h>
26 #include "svn_auth.h"
27 #include "svn_error.h"
28 #include "svn_utf.h"
29 #include "svn_config.h"
30 #include "svn_user.h"
32 #include "svn_private_config.h"
34 /*-----------------------------------------------------------------------*/
35 /* File provider */
36 /*-----------------------------------------------------------------------*/
38 /* The keys that will be stored on disk */
39 #define SVN_AUTH__AUTHFILE_USERNAME_KEY "username"
40 #define SVN_AUTH__AUTHFILE_PASSWORD_KEY "password"
41 #define SVN_AUTH__AUTHFILE_PASSTYPE_KEY "passtype"
43 #define SVN_AUTH__SIMPLE_PASSWORD_TYPE "simple"
44 #define SVN_AUTH__WINCRYPT_PASSWORD_TYPE "wincrypt"
45 #define SVN_AUTH__KEYCHAIN_PASSWORD_TYPE "keychain"
48 /* A function that stores PASSWORD (or some encrypted version thereof)
49 either directly in CREDS, or externally using REALMSTRING and USERNAME
50 as keys into the external store. If NON_INTERACTIVE is set, the user
51 must not be involved in the storage process. POOL is used for any
52 necessary allocation. */
53 typedef svn_boolean_t (*password_set_t)(apr_hash_t *creds,
54 const char *realmstring,
55 const char *username,
56 const char *password,
57 svn_boolean_t non_interactive,
58 apr_pool_t *pool);
60 /* A function that stores in *PASSWORD (potentially after decrypting it)
61 the user's password. It might be obtained directly from CREDS, or
62 from an external store, using REALMSTRING and USERNAME as keys.
63 If NON_INTERACTIVE is set, the user must not be involved in the
64 retrieval process. POOL is used for any necessary allocation. */
65 typedef svn_boolean_t (*password_get_t)(const char **password,
66 apr_hash_t *creds,
67 const char *realmstring,
68 const char *username,
69 svn_boolean_t non_interactive,
70 apr_pool_t *pool);
74 /* Implementation of password_get_t that retrieves the plaintext password
75 from CREDS. */
76 static svn_boolean_t
77 simple_password_get(const char **password,
78 apr_hash_t *creds,
79 const char *realmstring,
80 const char *username,
81 svn_boolean_t non_interactive,
82 apr_pool_t *pool)
84 svn_string_t *str;
85 str = apr_hash_get(creds, SVN_AUTH__AUTHFILE_PASSWORD_KEY,
86 APR_HASH_KEY_STRING);
87 if (str && str->data)
89 *password = str->data;
90 return TRUE;
92 return FALSE;
95 /* Implementation of password_set_t that store the plaintext password
96 in CREDS. */
97 static svn_boolean_t
98 simple_password_set(apr_hash_t *creds,
99 const char *realmstring,
100 const char *username,
101 const char *password,
102 svn_boolean_t non_interactive,
103 apr_pool_t *pool)
105 apr_hash_set(creds, SVN_AUTH__AUTHFILE_PASSWORD_KEY, APR_HASH_KEY_STRING,
106 svn_string_create(password, pool));
107 return TRUE;
110 /* Common implementation for simple_first_creds and
111 windows_simple_first_creds. Uses PARAMETERS, REALMSTRING and the
112 simple auth provider's username and password cache to fill a set of
113 CREDENTIALS. PASSWORD_GET is used to obtain the password value.
114 PASSTYPE identifies the type of the cached password. CREDENTIALS are
115 allocated from POOL. */
116 static svn_error_t *
117 simple_first_creds_helper(void **credentials,
118 void **iter_baton,
119 void *provider_baton,
120 apr_hash_t *parameters,
121 const char *realmstring,
122 password_get_t password_get,
123 const char *passtype,
124 apr_pool_t *pool)
126 const char *config_dir = apr_hash_get(parameters,
127 SVN_AUTH_PARAM_CONFIG_DIR,
128 APR_HASH_KEY_STRING);
129 const char *username = apr_hash_get(parameters,
130 SVN_AUTH_PARAM_DEFAULT_USERNAME,
131 APR_HASH_KEY_STRING);
132 const char *password = apr_hash_get(parameters,
133 SVN_AUTH_PARAM_DEFAULT_PASSWORD,
134 APR_HASH_KEY_STRING);
135 svn_boolean_t non_interactive = apr_hash_get(parameters,
136 SVN_AUTH_PARAM_NON_INTERACTIVE,
137 APR_HASH_KEY_STRING) != NULL;
139 svn_boolean_t may_save = username || password;
140 svn_error_t *err;
142 /* If we don't have a usename and a password yet, we try the auth cache */
143 if (! (username && password))
145 apr_hash_t *creds_hash = NULL;
147 /* Try to load credentials from a file on disk, based on the
148 realmstring. Don't throw an error, though: if something went
149 wrong reading the file, no big deal. What really matters is that
150 we failed to get the creds, so allow the auth system to try the
151 next provider. */
152 err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
153 realmstring, config_dir, pool);
154 svn_error_clear(err);
155 if (! err && creds_hash)
157 svn_string_t *str;
158 if (! username)
160 str = apr_hash_get(creds_hash,
161 SVN_AUTH__AUTHFILE_USERNAME_KEY,
162 APR_HASH_KEY_STRING);
163 if (str && str->data)
164 username = str->data;
167 if (! password)
169 svn_boolean_t have_passtype;
170 /* The password type in the auth data must match the
171 mangler's type, otherwise the password must be
172 interpreted by another provider. */
173 str = apr_hash_get(creds_hash,
174 SVN_AUTH__AUTHFILE_PASSTYPE_KEY,
175 APR_HASH_KEY_STRING);
176 have_passtype = (str && str->data);
177 if (have_passtype && passtype
178 && 0 != strcmp(str->data, passtype))
179 password = NULL;
180 else
182 if (!password_get(&password, creds_hash, realmstring,
183 username, non_interactive, pool))
184 password = NULL;
186 /* If the auth data didn't contain a password type,
187 force a write to upgrade the format of the auth
188 data file. */
189 if (password && passtype && !have_passtype)
190 may_save = TRUE;
196 /* Ask the OS for the username if we have a password but no
197 username. */
198 if (password && ! username)
199 username = svn_user_get_name(pool);
201 if (username && password)
203 svn_auth_cred_simple_t *creds = apr_pcalloc(pool, sizeof(*creds));
204 creds->username = username;
205 creds->password = password;
206 creds->may_save = may_save;
207 *credentials = creds;
209 else
210 *credentials = NULL;
212 *iter_baton = NULL;
214 return SVN_NO_ERROR;
218 /* Common implementation for simple_save_creds and
219 windows_simple_save_creds. Uses PARAMETERS and REALMSTRING to save
220 a set of CREDENTIALS to the simple auth provider's username and
221 password cache. PASSWORD_SET is used to store the password.
222 PASSTYPE identifies the type of the cached password. Allocates from POOL. */
223 static svn_error_t *
224 simple_save_creds_helper(svn_boolean_t *saved,
225 void *credentials,
226 void *provider_baton,
227 apr_hash_t *parameters,
228 const char *realmstring,
229 password_set_t password_set,
230 const char *passtype,
231 apr_pool_t *pool)
233 svn_auth_cred_simple_t *creds = credentials;
234 apr_hash_t *creds_hash = NULL;
235 const char *config_dir;
236 svn_error_t *err;
237 svn_boolean_t dont_store_passwords =
238 apr_hash_get(parameters,
239 SVN_AUTH_PARAM_DONT_STORE_PASSWORDS,
240 APR_HASH_KEY_STRING) != NULL;
241 svn_boolean_t non_interactive = apr_hash_get(parameters,
242 SVN_AUTH_PARAM_NON_INTERACTIVE,
243 APR_HASH_KEY_STRING) != NULL;
244 svn_boolean_t no_auth_cache =
245 (! creds->may_save) || (apr_hash_get(parameters,
246 SVN_AUTH_PARAM_NO_AUTH_CACHE,
247 APR_HASH_KEY_STRING) != NULL);
249 svn_boolean_t username_stored = FALSE;
250 svn_boolean_t password_stored = TRUE;
252 *saved = FALSE;
254 if (no_auth_cache)
255 return SVN_NO_ERROR;
257 config_dir = apr_hash_get(parameters,
258 SVN_AUTH_PARAM_CONFIG_DIR,
259 APR_HASH_KEY_STRING);
261 /* Put the credentials in a hash and save it to disk */
262 creds_hash = apr_hash_make(pool);
264 /* Maybe cache the username. */
265 if (! no_auth_cache)
267 apr_hash_set(creds_hash, SVN_AUTH__AUTHFILE_USERNAME_KEY,
268 APR_HASH_KEY_STRING,
269 svn_string_create(creds->username, pool));
270 username_stored = TRUE;
273 /* Maybe cache the password. */
274 if (! dont_store_passwords)
276 password_stored = password_set(creds_hash, realmstring, creds->username,
277 creds->password, non_interactive, pool);
278 if (password_stored)
280 /* Store the password type with the auth data, so that we
281 know which provider owns the password. */
282 if (passtype)
284 apr_hash_set(creds_hash, SVN_AUTH__AUTHFILE_PASSTYPE_KEY,
285 APR_HASH_KEY_STRING,
286 svn_string_create(passtype, pool));
289 else
290 *saved = FALSE;
293 /* If we cached anything, write it to disk. */
294 if (username_stored || password_stored)
296 err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_SIMPLE,
297 realmstring, config_dir, pool);
298 svn_error_clear(err);
299 *saved = ! err;
302 return SVN_NO_ERROR;
305 /* Get cached (unencrypted) credentials from the simple provider's cache. */
306 static svn_error_t *
307 simple_first_creds(void **credentials,
308 void **iter_baton,
309 void *provider_baton,
310 apr_hash_t *parameters,
311 const char *realmstring,
312 apr_pool_t *pool)
314 return simple_first_creds_helper(credentials,
315 iter_baton, provider_baton,
316 parameters, realmstring,
317 simple_password_get,
318 SVN_AUTH__SIMPLE_PASSWORD_TYPE,
319 pool);
322 /* Save (unencrypted) credentials to the simple provider's cache. */
323 static svn_error_t *
324 simple_save_creds(svn_boolean_t *saved,
325 void *credentials,
326 void *provider_baton,
327 apr_hash_t *parameters,
328 const char *realmstring,
329 apr_pool_t *pool)
331 return simple_save_creds_helper(saved, credentials, provider_baton,
332 parameters, realmstring,
333 simple_password_set,
334 SVN_AUTH__SIMPLE_PASSWORD_TYPE,
335 pool);
338 static const svn_auth_provider_t simple_provider = {
339 SVN_AUTH_CRED_SIMPLE,
340 simple_first_creds,
341 NULL,
342 simple_save_creds
346 /* Public API */
347 void
348 svn_auth_get_simple_provider(svn_auth_provider_object_t **provider,
349 apr_pool_t *pool)
351 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
353 po->vtable = &simple_provider;
354 *provider = po;
358 /*-----------------------------------------------------------------------*/
359 /* Prompt provider */
360 /*-----------------------------------------------------------------------*/
362 /* Baton type for username/password prompting. */
363 typedef struct
365 svn_auth_simple_prompt_func_t prompt_func;
366 void *prompt_baton;
368 /* how many times to re-prompt after the first one fails */
369 int retry_limit;
370 } simple_prompt_provider_baton_t;
373 /* Iteration baton type for username/password prompting. */
374 typedef struct
376 /* how many times we've reprompted */
377 int retries;
378 } simple_prompt_iter_baton_t;
382 /*** Helper Functions ***/
383 static svn_error_t *
384 prompt_for_simple_creds(svn_auth_cred_simple_t **cred_p,
385 simple_prompt_provider_baton_t *pb,
386 apr_hash_t *parameters,
387 const char *realmstring,
388 svn_boolean_t first_time,
389 svn_boolean_t may_save,
390 apr_pool_t *pool)
392 const char *def_username = NULL, *def_password = NULL;
394 *cred_p = NULL;
396 /* If we're allowed to check for default usernames and passwords, do
397 so. */
398 if (first_time)
400 def_username = apr_hash_get(parameters,
401 SVN_AUTH_PARAM_DEFAULT_USERNAME,
402 APR_HASH_KEY_STRING);
404 /* No default username? Try the auth cache. */
405 if (! def_username)
407 const char *config_dir = apr_hash_get(parameters,
408 SVN_AUTH_PARAM_CONFIG_DIR,
409 APR_HASH_KEY_STRING);
410 apr_hash_t *creds_hash = NULL;
411 svn_string_t *str;
412 svn_error_t *err;
414 err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
415 realmstring, config_dir, pool);
416 svn_error_clear(err);
417 if (! err && creds_hash)
419 str = apr_hash_get(creds_hash,
420 SVN_AUTH__AUTHFILE_USERNAME_KEY,
421 APR_HASH_KEY_STRING);
422 if (str && str->data)
423 def_username = str->data;
427 /* Still no default username? Try the UID. */
428 if (! def_username)
429 def_username = svn_user_get_name(pool);
431 def_password = apr_hash_get(parameters,
432 SVN_AUTH_PARAM_DEFAULT_PASSWORD,
433 APR_HASH_KEY_STRING);
436 /* If we have defaults, just build the cred here and return it.
438 * ### I do wonder why this is here instead of in a separate
439 * ### 'defaults' provider that would run before the prompt
440 * ### provider... Hmmm.
442 if (def_username && def_password)
444 *cred_p = apr_palloc(pool, sizeof(**cred_p));
445 (*cred_p)->username = apr_pstrdup(pool, def_username);
446 (*cred_p)->password = apr_pstrdup(pool, def_password);
447 (*cred_p)->may_save = TRUE;
449 else
451 SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring,
452 def_username, may_save, pool));
455 return SVN_NO_ERROR;
459 /* Our first attempt will use any default username/password passed
460 in, and prompt for the remaining stuff. */
461 static svn_error_t *
462 simple_prompt_first_creds(void **credentials_p,
463 void **iter_baton,
464 void *provider_baton,
465 apr_hash_t *parameters,
466 const char *realmstring,
467 apr_pool_t *pool)
469 simple_prompt_provider_baton_t *pb = provider_baton;
470 simple_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton));
471 const char *no_auth_cache = apr_hash_get(parameters,
472 SVN_AUTH_PARAM_NO_AUTH_CACHE,
473 APR_HASH_KEY_STRING);
475 SVN_ERR(prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
476 pb, parameters, realmstring, TRUE,
477 ! no_auth_cache, pool));
479 ibaton->retries = 0;
480 *iter_baton = ibaton;
482 return SVN_NO_ERROR;
486 /* Subsequent attempts to fetch will ignore the default values, and
487 simply re-prompt for both, up to a maximum of ib->pb->retry_limit. */
488 static svn_error_t *
489 simple_prompt_next_creds(void **credentials_p,
490 void *iter_baton,
491 void *provider_baton,
492 apr_hash_t *parameters,
493 const char *realmstring,
494 apr_pool_t *pool)
496 simple_prompt_iter_baton_t *ib = iter_baton;
497 simple_prompt_provider_baton_t *pb = provider_baton;
498 const char *no_auth_cache = apr_hash_get(parameters,
499 SVN_AUTH_PARAM_NO_AUTH_CACHE,
500 APR_HASH_KEY_STRING);
502 if (ib->retries >= pb->retry_limit)
504 /* give up, go on to next provider. */
505 *credentials_p = NULL;
506 return SVN_NO_ERROR;
508 ib->retries++;
510 SVN_ERR(prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
511 pb, parameters, realmstring, FALSE,
512 ! no_auth_cache, pool));
514 return SVN_NO_ERROR;
518 static const svn_auth_provider_t simple_prompt_provider = {
519 SVN_AUTH_CRED_SIMPLE,
520 simple_prompt_first_creds,
521 simple_prompt_next_creds,
522 NULL,
526 /* Public API */
527 void
528 svn_auth_get_simple_prompt_provider
529 (svn_auth_provider_object_t **provider,
530 svn_auth_simple_prompt_func_t prompt_func,
531 void *prompt_baton,
532 int retry_limit,
533 apr_pool_t *pool)
535 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
536 simple_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
538 pb->prompt_func = prompt_func;
539 pb->prompt_baton = prompt_baton;
540 pb->retry_limit = retry_limit;
542 po->vtable = &simple_prompt_provider;
543 po->provider_baton = pb;
544 *provider = po;
549 /*-----------------------------------------------------------------------*/
550 /* Windows simple provider, encrypts the password on Win2k and later. */
551 /*-----------------------------------------------------------------------*/
553 #if defined(WIN32) && !defined(__MINGW32__)
554 #include <wincrypt.h>
555 #include <apr_base64.h>
557 /* The description string that's combined with unencrypted data by the
558 Windows CryptoAPI. Used during decryption to verify that the
559 encrypted data were valid. */
560 static const WCHAR description[] = L"auth_svn.simple.wincrypt";
562 /* Dynamically load the address of function NAME in PDLL into
563 PFN. Return TRUE if the function name was found, otherwise
564 FALSE. Equivalent to dlsym(). */
565 static svn_boolean_t
566 get_crypto_function(const char *name, HINSTANCE *pdll, FARPROC *pfn)
568 /* In case anyone wonders why we use LoadLibraryA here: This will
569 always work on Win9x/Me, whilst LoadLibraryW may not. */
570 HINSTANCE dll = LoadLibraryA("Crypt32.dll");
571 if (dll)
573 FARPROC fn = GetProcAddress(dll, name);
574 if (fn)
576 *pdll = dll;
577 *pfn = fn;
578 return TRUE;
580 FreeLibrary(dll);
582 return FALSE;
585 /* Implementation of password_set_t that encrypts the incoming
586 password using the Windows CryptoAPI. */
587 static svn_boolean_t
588 windows_password_encrypter(apr_hash_t *creds,
589 const char *realmstring,
590 const char *username,
591 const char *in,
592 svn_boolean_t non_interactive,
593 apr_pool_t *pool)
595 typedef BOOL (CALLBACK *encrypt_fn_t)
596 (DATA_BLOB *, /* pDataIn */
597 LPCWSTR, /* szDataDescr */
598 DATA_BLOB *, /* pOptionalEntropy */
599 PVOID, /* pvReserved */
600 CRYPTPROTECT_PROMPTSTRUCT*, /* pPromptStruct */
601 DWORD, /* dwFlags */
602 DATA_BLOB*); /* pDataOut */
604 HINSTANCE dll;
605 FARPROC fn;
606 encrypt_fn_t encrypt;
607 DATA_BLOB blobin;
608 DATA_BLOB blobout;
609 svn_boolean_t crypted;
611 if (!get_crypto_function("CryptProtectData", &dll, &fn))
612 return FALSE;
613 encrypt = (encrypt_fn_t) fn;
615 blobin.cbData = strlen(in);
616 blobin.pbData = (BYTE*) in;
617 crypted = encrypt(&blobin, description, NULL, NULL, NULL,
618 CRYPTPROTECT_UI_FORBIDDEN, &blobout);
619 if (crypted)
621 char *coded = apr_palloc(pool, apr_base64_encode_len(blobout.cbData));
622 apr_base64_encode(coded, blobout.pbData, blobout.cbData);
623 crypted = simple_password_set(creds, realmstring, username, coded,
624 non_interactive, pool);
625 LocalFree(blobout.pbData);
628 FreeLibrary(dll);
629 return crypted;
632 /* Implementation of password_get_t that decrypts the incoming
633 password using the Windows CryptoAPI and verifies its validity. */
634 static svn_boolean_t
635 windows_password_decrypter(const char **out,
636 apr_hash_t *creds,
637 const char *realmstring,
638 const char *username,
639 svn_boolean_t non_interactive,
640 apr_pool_t *pool)
642 typedef BOOL (CALLBACK * decrypt_fn_t)
643 (DATA_BLOB *, /* pDataIn */
644 LPWSTR *, /* ppszDataDescr */
645 DATA_BLOB *, /* pOptionalEntropy */
646 PVOID, /* pvReserved */
647 CRYPTPROTECT_PROMPTSTRUCT*, /* pPromptStruct */
648 DWORD, /* dwFlags */
649 DATA_BLOB*); /* pDataOut */
651 HINSTANCE dll;
652 FARPROC fn;
653 DATA_BLOB blobin;
654 DATA_BLOB blobout;
655 LPWSTR descr;
656 decrypt_fn_t decrypt;
657 svn_boolean_t decrypted;
658 char *in;
660 if (!simple_password_get(&in, creds, realmstring, username,
661 non_interactive, pool))
662 return FALSE;
664 if (!get_crypto_function("CryptUnprotectData", &dll, &fn))
665 return FALSE;
666 decrypt = (decrypt_fn_t) fn;
668 blobin.cbData = strlen(in);
669 blobin.pbData = apr_palloc(pool, apr_base64_decode_len(in));
670 apr_base64_decode(blobin.pbData, in);
671 decrypted = decrypt(&blobin, &descr, NULL, NULL, NULL,
672 CRYPTPROTECT_UI_FORBIDDEN, &blobout);
673 if (decrypted)
675 if (0 == lstrcmpW(descr, description))
676 *out = apr_pstrndup(pool, blobout.pbData, blobout.cbData);
677 else
678 decrypted = FALSE;
679 LocalFree(blobout.pbData);
682 FreeLibrary(dll);
683 return decrypted;
686 /* Get cached encrypted credentials from the simple provider's cache. */
687 static svn_error_t *
688 windows_simple_first_creds(void **credentials,
689 void **iter_baton,
690 void *provider_baton,
691 apr_hash_t *parameters,
692 const char *realmstring,
693 apr_pool_t *pool)
695 return simple_first_creds_helper(credentials,
696 iter_baton, provider_baton,
697 parameters, realmstring,
698 windows_password_decrypter,
699 SVN_AUTH__WINCRYPT_PASSWORD_TYPE,
700 pool);
703 /* Save encrypted credentials to the simple provider's cache. */
704 static svn_error_t *
705 windows_simple_save_creds(svn_boolean_t *saved,
706 void *credentials,
707 void *provider_baton,
708 apr_hash_t *parameters,
709 const char *realmstring,
710 apr_pool_t *pool)
712 return simple_save_creds_helper(saved, credentials, provider_baton,
713 parameters, realmstring,
714 windows_password_encrypter,
715 SVN_AUTH__WINCRYPT_PASSWORD_TYPE,
716 pool);
719 static const svn_auth_provider_t windows_simple_provider = {
720 SVN_AUTH_CRED_SIMPLE,
721 windows_simple_first_creds,
722 NULL,
723 windows_simple_save_creds
727 /* Public API */
728 void
729 svn_auth_get_windows_simple_provider(svn_auth_provider_object_t **provider,
730 apr_pool_t *pool)
732 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
734 po->vtable = &windows_simple_provider;
735 *provider = po;
738 #endif /* WIN32 */
740 /*-----------------------------------------------------------------------*/
741 /* keychain simple provider, puts passwords in the KeyChain */
742 /*-----------------------------------------------------------------------*/
744 #ifdef SVN_HAVE_KEYCHAIN_SERVICES
745 #include <Security/Security.h>
748 * XXX (2005-12-07): If no GUI is available (e.g. over a SSH session),
749 * you won't be prompted for credentials with which to unlock your
750 * keychain. Apple recognizes lack of TTY prompting as a known
751 * problem.
754 * XXX (2005-12-07): SecKeychainSetUserInteractionAllowed(FALSE) does
755 * not appear to actually prevent all user interaction. Specifically,
756 * if the executable changes (for example, if it is rebuilt), the
757 * system prompts the user to okay the use of the new executable.
759 * Worse than that, the interactivity setting is global per app (not
760 * process/thread), meaning that there is a race condition in the
761 * implementation below between calls to
762 * SecKeychainSetUserInteractionAllowed() when multiple instances of
763 * the same Subversion auth provider-based app run concurrently.
766 /* Implementation of password_set_t that stores the password
767 in the OS X KeyChain. */
768 static svn_boolean_t
769 keychain_password_set(apr_hash_t *creds,
770 const char *realmstring,
771 const char *username,
772 const char *password,
773 svn_boolean_t non_interactive,
774 apr_pool_t *pool)
776 OSStatus status;
777 SecKeychainItemRef item;
779 if (non_interactive)
780 SecKeychainSetUserInteractionAllowed(FALSE);
782 status = SecKeychainFindGenericPassword(NULL, strlen(realmstring),
783 realmstring, strlen(username),
784 username, 0, NULL, &item);
785 if (status)
787 if (status == errSecItemNotFound)
788 status = SecKeychainAddGenericPassword(NULL, strlen(realmstring),
789 realmstring, strlen(username),
790 username, strlen(password),
791 password, NULL);
793 else
795 status = SecKeychainItemModifyAttributesAndData(item, NULL,
796 strlen(password),
797 password);
798 CFRelease(item);
801 if (non_interactive)
802 SecKeychainSetUserInteractionAllowed(TRUE);
804 return status == 0;
807 /* Implementation of password_get_t that retrieves the password
808 from the OS X KeyChain. */
809 static svn_boolean_t
810 keychain_password_get(const char **password,
811 apr_hash_t *creds,
812 const char *realmstring,
813 const char *username,
814 svn_boolean_t non_interactive,
815 apr_pool_t *pool)
817 OSStatus status;
818 UInt32 length;
819 void *data;
821 if (non_interactive)
822 SecKeychainSetUserInteractionAllowed(FALSE);
824 status = SecKeychainFindGenericPassword(NULL, strlen(realmstring),
825 realmstring, strlen(username),
826 username, &length, &data, NULL);
828 if (non_interactive)
829 SecKeychainSetUserInteractionAllowed(TRUE);
831 if (status != 0)
832 return FALSE;
834 *password = apr_pstrmemdup(pool, data, length);
835 SecKeychainItemFreeContent(NULL, data);
836 return TRUE;
839 /* Get cached encrypted credentials from the simple provider's cache. */
840 static svn_error_t *
841 keychain_simple_first_creds(void **credentials,
842 void **iter_baton,
843 void *provider_baton,
844 apr_hash_t *parameters,
845 const char *realmstring,
846 apr_pool_t *pool)
848 return simple_first_creds_helper(credentials,
849 iter_baton, provider_baton,
850 parameters, realmstring,
851 keychain_password_get,
852 SVN_AUTH__KEYCHAIN_PASSWORD_TYPE,
853 pool);
856 /* Save encrypted credentials to the simple provider's cache. */
857 static svn_error_t *
858 keychain_simple_save_creds(svn_boolean_t *saved,
859 void *credentials,
860 void *provider_baton,
861 apr_hash_t *parameters,
862 const char *realmstring,
863 apr_pool_t *pool)
865 return simple_save_creds_helper(saved, credentials, provider_baton,
866 parameters, realmstring,
867 keychain_password_set,
868 SVN_AUTH__KEYCHAIN_PASSWORD_TYPE,
869 pool);
872 static const svn_auth_provider_t keychain_simple_provider = {
873 SVN_AUTH_CRED_SIMPLE,
874 keychain_simple_first_creds,
875 NULL,
876 keychain_simple_save_creds
879 /* Public API */
880 void
881 svn_auth_get_keychain_simple_provider(svn_auth_provider_object_t **provider,
882 apr_pool_t *pool)
884 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
886 po->vtable = &keychain_simple_provider;
887 *provider = po;
890 #endif /* SVN_HAVE_KEYCHAIN_SERVICES */