summaryrefslogtreecommitdiff
path: root/subversion/libsvn_subr/macos_keychain.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_subr/macos_keychain.c')
-rw-r--r--subversion/libsvn_subr/macos_keychain.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/macos_keychain.c b/subversion/libsvn_subr/macos_keychain.c
new file mode 100644
index 0000000000000..f15324e504005
--- /dev/null
+++ b/subversion/libsvn_subr/macos_keychain.c
@@ -0,0 +1,263 @@
+/*
+ * macos_keychain.c: Mac OS keychain providers for SVN_AUTH_*
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+/*** Includes. ***/
+
+#include <apr_pools.h>
+#include "svn_auth.h"
+#include "svn_error.h"
+#include "svn_utf.h"
+#include "svn_config.h"
+#include "svn_user.h"
+
+#include "private/svn_auth_private.h"
+
+#include "svn_private_config.h"
+
+#ifdef SVN_HAVE_KEYCHAIN_SERVICES
+
+#include <Security/Security.h>
+
+/*-----------------------------------------------------------------------*/
+/* keychain simple provider, puts passwords in the KeyChain */
+/*-----------------------------------------------------------------------*/
+
+/*
+ * XXX (2005-12-07): If no GUI is available (e.g. over a SSH session),
+ * you won't be prompted for credentials with which to unlock your
+ * keychain. Apple recognizes lack of TTY prompting as a known
+ * problem.
+ *
+ *
+ * XXX (2005-12-07): SecKeychainSetUserInteractionAllowed(FALSE) does
+ * not appear to actually prevent all user interaction. Specifically,
+ * if the executable changes (for example, if it is rebuilt), the
+ * system prompts the user to okay the use of the new executable.
+ *
+ * Worse than that, the interactivity setting is global per app (not
+ * process/thread), meaning that there is a race condition in the
+ * implementation below between calls to
+ * SecKeychainSetUserInteractionAllowed() when multiple instances of
+ * the same Subversion auth provider-based app run concurrently.
+ */
+
+/* Implementation of svn_auth__password_set_t that stores
+ the password in the OS X KeyChain. */
+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)
+{
+ OSStatus status;
+ SecKeychainItemRef item;
+
+ if (non_interactive)
+ SecKeychainSetUserInteractionAllowed(FALSE);
+
+ status = SecKeychainFindGenericPassword(NULL, (int) strlen(realmstring),
+ realmstring, username == NULL
+ ? 0
+ : (int) strlen(username),
+ username, 0, NULL, &item);
+ if (status)
+ {
+ if (status == errSecItemNotFound)
+ status = SecKeychainAddGenericPassword(NULL, (int) strlen(realmstring),
+ realmstring, username == NULL
+ ? 0
+ : (int) strlen(username),
+ username, (int) strlen(password),
+ password, NULL);
+ }
+ else
+ {
+ status = SecKeychainItemModifyAttributesAndData(item, NULL,
+ (int) strlen(password),
+ password);
+ CFRelease(item);
+ }
+
+ if (non_interactive)
+ SecKeychainSetUserInteractionAllowed(TRUE);
+
+ *done = (status == 0);
+
+ return SVN_NO_ERROR;
+}
+
+/* Implementation of svn_auth__password_get_t that retrieves
+ the password from the OS X KeyChain. */
+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)
+{
+ OSStatus status;
+ UInt32 length;
+ void *data;
+
+ *done = FALSE;
+
+ if (non_interactive)
+ SecKeychainSetUserInteractionAllowed(FALSE);
+
+ status = SecKeychainFindGenericPassword(NULL, (int) strlen(realmstring),
+ realmstring, username == NULL
+ ? 0
+ : (int) strlen(username),
+ username, &length, &data, NULL);
+
+ if (non_interactive)
+ SecKeychainSetUserInteractionAllowed(TRUE);
+
+ if (status != 0)
+ return SVN_NO_ERROR;
+
+ *password = apr_pstrmemdup(pool, data, length);
+ SecKeychainItemFreeContent(NULL, data);
+ *done = TRUE;
+ return SVN_NO_ERROR;
+}
+
+/* Get cached encrypted credentials from the simple provider's cache. */
+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)
+{
+ return svn_auth__simple_creds_cache_get(credentials,
+ iter_baton,
+ provider_baton,
+ parameters,
+ realmstring,
+ keychain_password_get,
+ SVN_AUTH__KEYCHAIN_PASSWORD_TYPE,
+ pool);
+}
+
+/* Save encrypted credentials to the simple provider's cache. */
+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)
+{
+ return svn_auth__simple_creds_cache_set(saved, credentials,
+ provider_baton,
+ parameters,
+ realmstring,
+ keychain_password_set,
+ SVN_AUTH__KEYCHAIN_PASSWORD_TYPE,
+ pool);
+}
+
+static const svn_auth_provider_t keychain_simple_provider = {
+ SVN_AUTH_CRED_SIMPLE,
+ keychain_simple_first_creds,
+ NULL,
+ keychain_simple_save_creds
+};
+
+/* Get cached encrypted credentials from the ssl client cert password
+ provider's cache. */
+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)
+{
+ return svn_auth__ssl_client_cert_pw_cache_get(credentials,
+ iter_baton, provider_baton,
+ parameters, realmstring,
+ keychain_password_get,
+ SVN_AUTH__KEYCHAIN_PASSWORD_TYPE,
+ pool);
+}
+
+/* Save encrypted credentials to the ssl client cert password provider's
+ cache. */
+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)
+{
+ return svn_auth__ssl_client_cert_pw_cache_set(saved, credentials,
+ provider_baton, parameters,
+ realmstring,
+ keychain_password_set,
+ SVN_AUTH__KEYCHAIN_PASSWORD_TYPE,
+ pool);
+}
+
+static const svn_auth_provider_t keychain_ssl_client_cert_pw_provider = {
+ SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
+ keychain_ssl_client_cert_pw_first_creds,
+ NULL,
+ keychain_ssl_client_cert_pw_save_creds
+};
+
+
+/* Public API */
+void
+svn_auth_get_keychain_simple_provider(svn_auth_provider_object_t **provider,
+ apr_pool_t *pool)
+{
+ svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
+
+ po->vtable = &keychain_simple_provider;
+ *provider = po;
+}
+
+void
+svn_auth_get_keychain_ssl_client_cert_pw_provider
+ (svn_auth_provider_object_t **provider,
+ apr_pool_t *pool)
+{
+ svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
+
+ po->vtable = &keychain_ssl_client_cert_pw_provider;
+ *provider = po;
+}
+#endif /* SVN_HAVE_KEYCHAIN_SERVICES */