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