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