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