macos_keychain.c revision 251886
1/* 2 * macos_keychain.c: Mac OS keychain providers for SVN_AUTH_* 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/*** Includes. ***/ 27 28#include <apr_pools.h> 29#include "svn_auth.h" 30#include "svn_error.h" 31#include "svn_utf.h" 32#include "svn_config.h" 33#include "svn_user.h" 34 35#include "private/svn_auth_private.h" 36 37#include "svn_private_config.h" 38 39#ifdef SVN_HAVE_KEYCHAIN_SERVICES 40 41#include <Security/Security.h> 42 43/*-----------------------------------------------------------------------*/ 44/* keychain simple provider, puts passwords in the KeyChain */ 45/*-----------------------------------------------------------------------*/ 46 47/* 48 * XXX (2005-12-07): If no GUI is available (e.g. over a SSH session), 49 * you won't be prompted for credentials with which to unlock your 50 * keychain. Apple recognizes lack of TTY prompting as a known 51 * problem. 52 * 53 * 54 * XXX (2005-12-07): SecKeychainSetUserInteractionAllowed(FALSE) does 55 * not appear to actually prevent all user interaction. Specifically, 56 * if the executable changes (for example, if it is rebuilt), the 57 * system prompts the user to okay the use of the new executable. 58 * 59 * Worse than that, the interactivity setting is global per app (not 60 * process/thread), meaning that there is a race condition in the 61 * implementation below between calls to 62 * SecKeychainSetUserInteractionAllowed() when multiple instances of 63 * the same Subversion auth provider-based app run concurrently. 64 */ 65 66/* Implementation of svn_auth__password_set_t that stores 67 the password in the OS X KeyChain. */ 68static svn_error_t * 69keychain_password_set(svn_boolean_t *done, 70 apr_hash_t *creds, 71 const char *realmstring, 72 const char *username, 73 const char *password, 74 apr_hash_t *parameters, 75 svn_boolean_t non_interactive, 76 apr_pool_t *pool) 77{ 78 OSStatus status; 79 SecKeychainItemRef item; 80 81 if (non_interactive) 82 SecKeychainSetUserInteractionAllowed(FALSE); 83 84 status = SecKeychainFindGenericPassword(NULL, (int) strlen(realmstring), 85 realmstring, username == NULL 86 ? 0 87 : (int) strlen(username), 88 username, 0, NULL, &item); 89 if (status) 90 { 91 if (status == errSecItemNotFound) 92 status = SecKeychainAddGenericPassword(NULL, (int) strlen(realmstring), 93 realmstring, username == NULL 94 ? 0 95 : (int) strlen(username), 96 username, (int) strlen(password), 97 password, NULL); 98 } 99 else 100 { 101 status = SecKeychainItemModifyAttributesAndData(item, NULL, 102 (int) strlen(password), 103 password); 104 CFRelease(item); 105 } 106 107 if (non_interactive) 108 SecKeychainSetUserInteractionAllowed(TRUE); 109 110 *done = (status == 0); 111 112 return SVN_NO_ERROR; 113} 114 115/* Implementation of svn_auth__password_get_t that retrieves 116 the password from the OS X KeyChain. */ 117static svn_error_t * 118keychain_password_get(svn_boolean_t *done, 119 const char **password, 120 apr_hash_t *creds, 121 const char *realmstring, 122 const char *username, 123 apr_hash_t *parameters, 124 svn_boolean_t non_interactive, 125 apr_pool_t *pool) 126{ 127 OSStatus status; 128 UInt32 length; 129 void *data; 130 131 *done = FALSE; 132 133 if (non_interactive) 134 SecKeychainSetUserInteractionAllowed(FALSE); 135 136 status = SecKeychainFindGenericPassword(NULL, (int) strlen(realmstring), 137 realmstring, username == NULL 138 ? 0 139 : (int) strlen(username), 140 username, &length, &data, NULL); 141 142 if (non_interactive) 143 SecKeychainSetUserInteractionAllowed(TRUE); 144 145 if (status != 0) 146 return SVN_NO_ERROR; 147 148 *password = apr_pstrmemdup(pool, data, length); 149 SecKeychainItemFreeContent(NULL, data); 150 *done = TRUE; 151 return SVN_NO_ERROR; 152} 153 154/* Get cached encrypted credentials from the simple provider's cache. */ 155static svn_error_t * 156keychain_simple_first_creds(void **credentials, 157 void **iter_baton, 158 void *provider_baton, 159 apr_hash_t *parameters, 160 const char *realmstring, 161 apr_pool_t *pool) 162{ 163 return svn_auth__simple_creds_cache_get(credentials, 164 iter_baton, 165 provider_baton, 166 parameters, 167 realmstring, 168 keychain_password_get, 169 SVN_AUTH__KEYCHAIN_PASSWORD_TYPE, 170 pool); 171} 172 173/* Save encrypted credentials to the simple provider's cache. */ 174static svn_error_t * 175keychain_simple_save_creds(svn_boolean_t *saved, 176 void *credentials, 177 void *provider_baton, 178 apr_hash_t *parameters, 179 const char *realmstring, 180 apr_pool_t *pool) 181{ 182 return svn_auth__simple_creds_cache_set(saved, credentials, 183 provider_baton, 184 parameters, 185 realmstring, 186 keychain_password_set, 187 SVN_AUTH__KEYCHAIN_PASSWORD_TYPE, 188 pool); 189} 190 191static const svn_auth_provider_t keychain_simple_provider = { 192 SVN_AUTH_CRED_SIMPLE, 193 keychain_simple_first_creds, 194 NULL, 195 keychain_simple_save_creds 196}; 197 198/* Get cached encrypted credentials from the ssl client cert password 199 provider's cache. */ 200static svn_error_t * 201keychain_ssl_client_cert_pw_first_creds(void **credentials, 202 void **iter_baton, 203 void *provider_baton, 204 apr_hash_t *parameters, 205 const char *realmstring, 206 apr_pool_t *pool) 207{ 208 return svn_auth__ssl_client_cert_pw_cache_get(credentials, 209 iter_baton, provider_baton, 210 parameters, realmstring, 211 keychain_password_get, 212 SVN_AUTH__KEYCHAIN_PASSWORD_TYPE, 213 pool); 214} 215 216/* Save encrypted credentials to the ssl client cert password provider's 217 cache. */ 218static svn_error_t * 219keychain_ssl_client_cert_pw_save_creds(svn_boolean_t *saved, 220 void *credentials, 221 void *provider_baton, 222 apr_hash_t *parameters, 223 const char *realmstring, 224 apr_pool_t *pool) 225{ 226 return svn_auth__ssl_client_cert_pw_cache_set(saved, credentials, 227 provider_baton, parameters, 228 realmstring, 229 keychain_password_set, 230 SVN_AUTH__KEYCHAIN_PASSWORD_TYPE, 231 pool); 232} 233 234static const svn_auth_provider_t keychain_ssl_client_cert_pw_provider = { 235 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 236 keychain_ssl_client_cert_pw_first_creds, 237 NULL, 238 keychain_ssl_client_cert_pw_save_creds 239}; 240 241 242/* Public API */ 243void 244svn_auth_get_keychain_simple_provider(svn_auth_provider_object_t **provider, 245 apr_pool_t *pool) 246{ 247 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 248 249 po->vtable = &keychain_simple_provider; 250 *provider = po; 251} 252 253void 254svn_auth_get_keychain_ssl_client_cert_pw_provider 255 (svn_auth_provider_object_t **provider, 256 apr_pool_t *pool) 257{ 258 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 259 260 po->vtable = &keychain_ssl_client_cert_pw_provider; 261 *provider = po; 262} 263#endif /* SVN_HAVE_KEYCHAIN_SERVICES */ 264