Change the format of the revprops block sent in svnserve for
[svn.git] / subversion / libsvn_subr / simple_providers.c
blobd485d1122605e4a9559c0f1b4395c1a50894e9ec
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 const char *dont_store_passwords =
238 apr_hash_get(parameters,
239 SVN_AUTH_PARAM_DONT_STORE_PASSWORDS,
240 APR_HASH_KEY_STRING);
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 password_stored = TRUE;
246 *saved = FALSE;
248 if (! creds->may_save)
249 return SVN_NO_ERROR;
251 config_dir = apr_hash_get(parameters,
252 SVN_AUTH_PARAM_CONFIG_DIR,
253 APR_HASH_KEY_STRING);
255 /* Put the credentials in a hash and save it to disk */
256 creds_hash = apr_hash_make(pool);
257 apr_hash_set(creds_hash, SVN_AUTH__AUTHFILE_USERNAME_KEY,
258 APR_HASH_KEY_STRING,
259 svn_string_create(creds->username, pool));
260 if (! dont_store_passwords)
262 password_stored = password_set(creds_hash, realmstring, creds->username,
263 creds->password, non_interactive, pool);
264 if (password_stored)
266 /* Store the password type with the auth data, so that we
267 know which provider owns the password. */
268 if (passtype)
270 apr_hash_set(creds_hash, SVN_AUTH__AUTHFILE_PASSTYPE_KEY,
271 APR_HASH_KEY_STRING,
272 svn_string_create(passtype, pool));
275 else
276 *saved = FALSE;
279 if (password_stored)
281 err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_SIMPLE,
282 realmstring, config_dir, pool);
283 svn_error_clear(err);
284 *saved = ! err;
287 return SVN_NO_ERROR;
290 /* Get cached (unencrypted) credentials from the simple provider's cache. */
291 static svn_error_t *
292 simple_first_creds(void **credentials,
293 void **iter_baton,
294 void *provider_baton,
295 apr_hash_t *parameters,
296 const char *realmstring,
297 apr_pool_t *pool)
299 return simple_first_creds_helper(credentials,
300 iter_baton, provider_baton,
301 parameters, realmstring,
302 simple_password_get,
303 SVN_AUTH__SIMPLE_PASSWORD_TYPE,
304 pool);
307 /* Save (unencrypted) credentials to the simple provider's cache. */
308 static svn_error_t *
309 simple_save_creds(svn_boolean_t *saved,
310 void *credentials,
311 void *provider_baton,
312 apr_hash_t *parameters,
313 const char *realmstring,
314 apr_pool_t *pool)
316 return simple_save_creds_helper(saved, credentials, provider_baton,
317 parameters, realmstring,
318 simple_password_set,
319 SVN_AUTH__SIMPLE_PASSWORD_TYPE,
320 pool);
323 static const svn_auth_provider_t simple_provider = {
324 SVN_AUTH_CRED_SIMPLE,
325 simple_first_creds,
326 NULL,
327 simple_save_creds
331 /* Public API */
332 void
333 svn_auth_get_simple_provider(svn_auth_provider_object_t **provider,
334 apr_pool_t *pool)
336 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
338 po->vtable = &simple_provider;
339 *provider = po;
343 /*-----------------------------------------------------------------------*/
344 /* Prompt provider */
345 /*-----------------------------------------------------------------------*/
347 /* Baton type for username/password prompting. */
348 typedef struct
350 svn_auth_simple_prompt_func_t prompt_func;
351 void *prompt_baton;
353 /* how many times to re-prompt after the first one fails */
354 int retry_limit;
355 } simple_prompt_provider_baton_t;
358 /* Iteration baton type for username/password prompting. */
359 typedef struct
361 /* how many times we've reprompted */
362 int retries;
363 } simple_prompt_iter_baton_t;
367 /*** Helper Functions ***/
368 static svn_error_t *
369 prompt_for_simple_creds(svn_auth_cred_simple_t **cred_p,
370 simple_prompt_provider_baton_t *pb,
371 apr_hash_t *parameters,
372 const char *realmstring,
373 svn_boolean_t first_time,
374 svn_boolean_t may_save,
375 apr_pool_t *pool)
377 const char *def_username = NULL, *def_password = NULL;
379 *cred_p = NULL;
381 /* If we're allowed to check for default usernames and passwords, do
382 so. */
383 if (first_time)
385 def_username = apr_hash_get(parameters,
386 SVN_AUTH_PARAM_DEFAULT_USERNAME,
387 APR_HASH_KEY_STRING);
389 /* No default username? Try the auth cache. */
390 if (! def_username)
392 const char *config_dir = apr_hash_get(parameters,
393 SVN_AUTH_PARAM_CONFIG_DIR,
394 APR_HASH_KEY_STRING);
395 apr_hash_t *creds_hash = NULL;
396 svn_string_t *str;
397 svn_error_t *err;
399 err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
400 realmstring, config_dir, pool);
401 svn_error_clear(err);
402 if (! err && creds_hash)
404 str = apr_hash_get(creds_hash,
405 SVN_AUTH__AUTHFILE_USERNAME_KEY,
406 APR_HASH_KEY_STRING);
407 if (str && str->data)
408 def_username = str->data;
412 /* Still no default username? Try the UID. */
413 if (! def_username)
414 def_username = svn_user_get_name(pool);
416 def_password = apr_hash_get(parameters,
417 SVN_AUTH_PARAM_DEFAULT_PASSWORD,
418 APR_HASH_KEY_STRING);
421 /* If we have defaults, just build the cred here and return it.
423 * ### I do wonder why this is here instead of in a separate
424 * ### 'defaults' provider that would run before the prompt
425 * ### provider... Hmmm.
427 if (def_username && def_password)
429 *cred_p = apr_palloc(pool, sizeof(**cred_p));
430 (*cred_p)->username = apr_pstrdup(pool, def_username);
431 (*cred_p)->password = apr_pstrdup(pool, def_password);
432 (*cred_p)->may_save = TRUE;
434 else
436 SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring,
437 def_username, may_save, pool));
440 return SVN_NO_ERROR;
444 /* Our first attempt will use any default username/password passed
445 in, and prompt for the remaining stuff. */
446 static svn_error_t *
447 simple_prompt_first_creds(void **credentials_p,
448 void **iter_baton,
449 void *provider_baton,
450 apr_hash_t *parameters,
451 const char *realmstring,
452 apr_pool_t *pool)
454 simple_prompt_provider_baton_t *pb = provider_baton;
455 simple_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton));
456 const char *no_auth_cache = apr_hash_get(parameters,
457 SVN_AUTH_PARAM_NO_AUTH_CACHE,
458 APR_HASH_KEY_STRING);
460 SVN_ERR(prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
461 pb, parameters, realmstring, TRUE,
462 ! no_auth_cache, pool));
464 ibaton->retries = 0;
465 *iter_baton = ibaton;
467 return SVN_NO_ERROR;
471 /* Subsequent attempts to fetch will ignore the default values, and
472 simply re-prompt for both, up to a maximum of ib->pb->retry_limit. */
473 static svn_error_t *
474 simple_prompt_next_creds(void **credentials_p,
475 void *iter_baton,
476 void *provider_baton,
477 apr_hash_t *parameters,
478 const char *realmstring,
479 apr_pool_t *pool)
481 simple_prompt_iter_baton_t *ib = iter_baton;
482 simple_prompt_provider_baton_t *pb = provider_baton;
483 const char *no_auth_cache = apr_hash_get(parameters,
484 SVN_AUTH_PARAM_NO_AUTH_CACHE,
485 APR_HASH_KEY_STRING);
487 if (ib->retries >= pb->retry_limit)
489 /* give up, go on to next provider. */
490 *credentials_p = NULL;
491 return SVN_NO_ERROR;
493 ib->retries++;
495 SVN_ERR(prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
496 pb, parameters, realmstring, FALSE,
497 ! no_auth_cache, pool));
499 return SVN_NO_ERROR;
503 static const svn_auth_provider_t simple_prompt_provider = {
504 SVN_AUTH_CRED_SIMPLE,
505 simple_prompt_first_creds,
506 simple_prompt_next_creds,
507 NULL,
511 /* Public API */
512 void
513 svn_auth_get_simple_prompt_provider
514 (svn_auth_provider_object_t **provider,
515 svn_auth_simple_prompt_func_t prompt_func,
516 void *prompt_baton,
517 int retry_limit,
518 apr_pool_t *pool)
520 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
521 simple_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
523 pb->prompt_func = prompt_func;
524 pb->prompt_baton = prompt_baton;
525 pb->retry_limit = retry_limit;
527 po->vtable = &simple_prompt_provider;
528 po->provider_baton = pb;
529 *provider = po;
534 /*-----------------------------------------------------------------------*/
535 /* Windows simple provider, encrypts the password on Win2k and later. */
536 /*-----------------------------------------------------------------------*/
538 #if defined(WIN32) && !defined(__MINGW32__)
539 #include <wincrypt.h>
540 #include <apr_base64.h>
542 /* The description string that's combined with unencrypted data by the
543 Windows CryptoAPI. Used during decryption to verify that the
544 encrypted data were valid. */
545 static const WCHAR description[] = L"auth_svn.simple.wincrypt";
547 /* Dynamically load the address of function NAME in PDLL into
548 PFN. Return TRUE if the function name was found, otherwise
549 FALSE. Equivalent to dlsym(). */
550 static svn_boolean_t
551 get_crypto_function(const char *name, HINSTANCE *pdll, FARPROC *pfn)
553 /* In case anyone wonders why we use LoadLibraryA here: This will
554 always work on Win9x/Me, whilst LoadLibraryW may not. */
555 HINSTANCE dll = LoadLibraryA("Crypt32.dll");
556 if (dll)
558 FARPROC fn = GetProcAddress(dll, name);
559 if (fn)
561 *pdll = dll;
562 *pfn = fn;
563 return TRUE;
565 FreeLibrary(dll);
567 return FALSE;
570 /* Implementation of password_set_t that encrypts the incoming
571 password using the Windows CryptoAPI. */
572 static svn_boolean_t
573 windows_password_encrypter(apr_hash_t *creds,
574 const char *realmstring,
575 const char *username,
576 const char *in,
577 svn_boolean_t non_interactive,
578 apr_pool_t *pool)
580 typedef BOOL (CALLBACK *encrypt_fn_t)
581 (DATA_BLOB *, /* pDataIn */
582 LPCWSTR, /* szDataDescr */
583 DATA_BLOB *, /* pOptionalEntropy */
584 PVOID, /* pvReserved */
585 CRYPTPROTECT_PROMPTSTRUCT*, /* pPromptStruct */
586 DWORD, /* dwFlags */
587 DATA_BLOB*); /* pDataOut */
589 HINSTANCE dll;
590 FARPROC fn;
591 encrypt_fn_t encrypt;
592 DATA_BLOB blobin;
593 DATA_BLOB blobout;
594 svn_boolean_t crypted;
596 if (!get_crypto_function("CryptProtectData", &dll, &fn))
597 return FALSE;
598 encrypt = (encrypt_fn_t) fn;
600 blobin.cbData = strlen(in);
601 blobin.pbData = (BYTE*) in;
602 crypted = encrypt(&blobin, description, NULL, NULL, NULL,
603 CRYPTPROTECT_UI_FORBIDDEN, &blobout);
604 if (crypted)
606 char *coded = apr_palloc(pool, apr_base64_encode_len(blobout.cbData));
607 apr_base64_encode(coded, blobout.pbData, blobout.cbData);
608 crypted = simple_password_set(creds, realmstring, username, coded,
609 non_interactive, pool);
610 LocalFree(blobout.pbData);
613 FreeLibrary(dll);
614 return crypted;
617 /* Implementation of password_get_t that decrypts the incoming
618 password using the Windows CryptoAPI and verifies its validity. */
619 static svn_boolean_t
620 windows_password_decrypter(const char **out,
621 apr_hash_t *creds,
622 const char *realmstring,
623 const char *username,
624 svn_boolean_t non_interactive,
625 apr_pool_t *pool)
627 typedef BOOL (CALLBACK * decrypt_fn_t)
628 (DATA_BLOB *, /* pDataIn */
629 LPWSTR *, /* ppszDataDescr */
630 DATA_BLOB *, /* pOptionalEntropy */
631 PVOID, /* pvReserved */
632 CRYPTPROTECT_PROMPTSTRUCT*, /* pPromptStruct */
633 DWORD, /* dwFlags */
634 DATA_BLOB*); /* pDataOut */
636 HINSTANCE dll;
637 FARPROC fn;
638 DATA_BLOB blobin;
639 DATA_BLOB blobout;
640 LPWSTR descr;
641 decrypt_fn_t decrypt;
642 svn_boolean_t decrypted;
643 char *in;
645 if (!simple_password_get(&in, creds, realmstring, username,
646 non_interactive, pool))
647 return FALSE;
649 if (!get_crypto_function("CryptUnprotectData", &dll, &fn))
650 return FALSE;
651 decrypt = (decrypt_fn_t) fn;
653 blobin.cbData = strlen(in);
654 blobin.pbData = apr_palloc(pool, apr_base64_decode_len(in));
655 apr_base64_decode(blobin.pbData, in);
656 decrypted = decrypt(&blobin, &descr, NULL, NULL, NULL,
657 CRYPTPROTECT_UI_FORBIDDEN, &blobout);
658 if (decrypted)
660 if (0 == lstrcmpW(descr, description))
661 *out = apr_pstrndup(pool, blobout.pbData, blobout.cbData);
662 else
663 decrypted = FALSE;
664 LocalFree(blobout.pbData);
667 FreeLibrary(dll);
668 return decrypted;
671 /* Get cached encrypted credentials from the simple provider's cache. */
672 static svn_error_t *
673 windows_simple_first_creds(void **credentials,
674 void **iter_baton,
675 void *provider_baton,
676 apr_hash_t *parameters,
677 const char *realmstring,
678 apr_pool_t *pool)
680 return simple_first_creds_helper(credentials,
681 iter_baton, provider_baton,
682 parameters, realmstring,
683 windows_password_decrypter,
684 SVN_AUTH__WINCRYPT_PASSWORD_TYPE,
685 pool);
688 /* Save encrypted credentials to the simple provider's cache. */
689 static svn_error_t *
690 windows_simple_save_creds(svn_boolean_t *saved,
691 void *credentials,
692 void *provider_baton,
693 apr_hash_t *parameters,
694 const char *realmstring,
695 apr_pool_t *pool)
697 return simple_save_creds_helper(saved, credentials, provider_baton,
698 parameters, realmstring,
699 windows_password_encrypter,
700 SVN_AUTH__WINCRYPT_PASSWORD_TYPE,
701 pool);
704 static const svn_auth_provider_t windows_simple_provider = {
705 SVN_AUTH_CRED_SIMPLE,
706 windows_simple_first_creds,
707 NULL,
708 windows_simple_save_creds
712 /* Public API */
713 void
714 svn_auth_get_windows_simple_provider(svn_auth_provider_object_t **provider,
715 apr_pool_t *pool)
717 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
719 po->vtable = &windows_simple_provider;
720 *provider = po;
723 #endif /* WIN32 */
725 /*-----------------------------------------------------------------------*/
726 /* keychain simple provider, puts passwords in the KeyChain */
727 /*-----------------------------------------------------------------------*/
729 #ifdef SVN_HAVE_KEYCHAIN_SERVICES
730 #include <Security/Security.h>
733 * XXX (2005-12-07): If no GUI is available (e.g. over a SSH session),
734 * you won't be prompted for credentials with which to unlock your
735 * keychain. Apple recognizes lack of TTY prompting as a known
736 * problem.
739 * XXX (2005-12-07): SecKeychainSetUserInteractionAllowed(FALSE) does
740 * not appear to actually prevent all user interaction. Specifically,
741 * if the executable changes (for example, if it is rebuilt), the
742 * system prompts the user to okay the use of the new executable.
744 * Worse than that, the interactivity setting is global per app (not
745 * process/thread), meaning that there is a race condition in the
746 * implementation below between calls to
747 * SecKeychainSetUserInteractionAllowed() when multiple instances of
748 * the same Subversion auth provider-based app run concurrently.
751 /* Implementation of password_set_t that stores the password
752 in the OS X KeyChain. */
753 static svn_boolean_t
754 keychain_password_set(apr_hash_t *creds,
755 const char *realmstring,
756 const char *username,
757 const char *password,
758 svn_boolean_t non_interactive,
759 apr_pool_t *pool)
761 OSStatus status;
762 SecKeychainItemRef item;
764 if (non_interactive)
765 SecKeychainSetUserInteractionAllowed(FALSE);
767 status = SecKeychainFindGenericPassword(NULL, strlen(realmstring),
768 realmstring, strlen(username),
769 username, 0, NULL, &item);
770 if (status)
772 if (status == errSecItemNotFound)
773 status = SecKeychainAddGenericPassword(NULL, strlen(realmstring),
774 realmstring, strlen(username),
775 username, strlen(password),
776 password, NULL);
778 else
780 status = SecKeychainItemModifyAttributesAndData(item, NULL,
781 strlen(password),
782 password);
783 CFRelease(item);
786 if (non_interactive)
787 SecKeychainSetUserInteractionAllowed(TRUE);
789 return status == 0;
792 /* Implementation of password_get_t that retrieves the password
793 from the OS X KeyChain. */
794 static svn_boolean_t
795 keychain_password_get(const char **password,
796 apr_hash_t *creds,
797 const char *realmstring,
798 const char *username,
799 svn_boolean_t non_interactive,
800 apr_pool_t *pool)
802 OSStatus status;
803 UInt32 length;
804 void *data;
806 if (non_interactive)
807 SecKeychainSetUserInteractionAllowed(FALSE);
809 status = SecKeychainFindGenericPassword(NULL, strlen(realmstring),
810 realmstring, strlen(username),
811 username, &length, &data, NULL);
813 if (non_interactive)
814 SecKeychainSetUserInteractionAllowed(TRUE);
816 if (status != 0)
817 return FALSE;
819 *password = apr_pstrmemdup(pool, data, length);
820 SecKeychainItemFreeContent(NULL, data);
821 return TRUE;
824 /* Get cached encrypted credentials from the simple provider's cache. */
825 static svn_error_t *
826 keychain_simple_first_creds(void **credentials,
827 void **iter_baton,
828 void *provider_baton,
829 apr_hash_t *parameters,
830 const char *realmstring,
831 apr_pool_t *pool)
833 return simple_first_creds_helper(credentials,
834 iter_baton, provider_baton,
835 parameters, realmstring,
836 keychain_password_get,
837 SVN_AUTH__KEYCHAIN_PASSWORD_TYPE,
838 pool);
841 /* Save encrypted credentials to the simple provider's cache. */
842 static svn_error_t *
843 keychain_simple_save_creds(svn_boolean_t *saved,
844 void *credentials,
845 void *provider_baton,
846 apr_hash_t *parameters,
847 const char *realmstring,
848 apr_pool_t *pool)
850 return simple_save_creds_helper(saved, credentials, provider_baton,
851 parameters, realmstring,
852 keychain_password_set,
853 SVN_AUTH__KEYCHAIN_PASSWORD_TYPE,
854 pool);
857 static const svn_auth_provider_t keychain_simple_provider = {
858 SVN_AUTH_CRED_SIMPLE,
859 keychain_simple_first_creds,
860 NULL,
861 keychain_simple_save_creds
864 /* Public API */
865 void
866 svn_auth_get_keychain_simple_provider(svn_auth_provider_object_t **provider,
867 apr_pool_t *pool)
869 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
871 po->vtable = &keychain_simple_provider;
872 *provider = po;
875 #endif /* SVN_HAVE_KEYCHAIN_SERVICES */