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. */
69 static svn_error_t *
keychain_password_set(svn_boolean_t * done,apr_hash_t * creds,const char * realmstring,const char * username,const char * password,apr_hash_t * parameters,svn_boolean_t non_interactive,apr_pool_t * pool)70 keychain_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. */
118 static svn_error_t *
keychain_password_get(svn_boolean_t * done,const char ** password,apr_hash_t * creds,const char * realmstring,const char * username,apr_hash_t * parameters,svn_boolean_t non_interactive,apr_pool_t * pool)119 keychain_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. */
156 static svn_error_t *
keychain_simple_first_creds(void ** credentials,void ** iter_baton,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)157 keychain_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. */
175 static svn_error_t *
keychain_simple_save_creds(svn_boolean_t * saved,void * credentials,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)176 keychain_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
192 static 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. */
201 static svn_error_t *
keychain_ssl_client_cert_pw_first_creds(void ** credentials,void ** iter_baton,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)202 keychain_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. */
219 static svn_error_t *
keychain_ssl_client_cert_pw_save_creds(svn_boolean_t * saved,void * credentials,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)220 keychain_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
235 static 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 */
244 void
svn_auth__get_keychain_simple_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)245 svn_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
254 void
svn_auth__get_keychain_ssl_client_cert_pw_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)255 svn_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