[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] OSX Keychain support

From: Ken Allan <k.allan_at_au.darkbluesea.com>
Date: 2005-12-01 06:26:01 CET

Hi,

As a consequence of our company wishing to adopt SVN, it underwent a
security review, and the plain-text storage of passwords when using OSX
was considered a major hurdle to the adoption of Subversion as a source
management tool by our security people.

I have developed some very rudimentary patches to the source that allows
the SVN client to use the OSX keychain manager to store the passwords.

I am posting these code patches as a courtesy to the SVN project in case
anyone else wants to use this functionality, or the SVN developers want
to integrate them into the project.

NOTE: These patches contain no checking for platform, and may not comply
with the coding standards of subversion. They are very "hacky" and will
need recoding if they are to be used. That said, I have tried as best I
can to implement them as proper "providers" and the general intent of
the code is to operate as the subversion client framework intends it to.

I am not available to do further work on this code, however if you need
code release forms to be signed, I can arrange to get this done. I am
also not subscribed to the list, so be sure to cc me in any replies.

Thanks for a great product.

-- 
Ken Allan,
Senior Programmer,
Dark Blue Sea
Telephone:    +617 30070000
Direct line:    +617 30070008
Fax:    +617 30070001
Email:    k.allan@au.darkbluesea.com
The information contained in this email is confidential.
If you are not the intended recipient, you may not disclose or use the 
information in this email in any way.
DarkBlueSea does not guarantee the integrity of any emails or attached 
files.
The views or opinions expressed are the author's own and may not reflect 
the views or opinions of DarkBlueSea.
DarkBlueSea does not warrant that any attachments are free from viruses 
or other defects.
You assume all liability for any loss, damage or other consequences 
which may arise from opening or using the attachments.

diff -raud ../subversion-1.2.3/subversion/clients/cmdline/main.c ./subversion/clients/cmdline/main.c
--- ../subversion-1.2.3/subversion/clients/cmdline/main.c 2005-07-29 08:08:40.000000000 +1000
+++ ./subversion/clients/cmdline/main.c 2005-12-01 12:33:46.000000000 +1000
@@ -1341,6 +1341,11 @@
     svn_client_get_windows_simple_provider (&provider, pool);
     APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;
 #endif
+ svn_client_get_osx_simple_provider (&provider, pool);
+ APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;
+ svn_client_get_osx_ssl_client_cert_pw_file_provider (&provider, pool);
+ APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;
+
     svn_client_get_simple_provider (&provider, pool);
     APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;
     svn_client_get_username_provider (&provider, pool);
diff -raud ../subversion-1.2.3/subversion/include/svn_client.h ./subversion/include/svn_client.h
--- ../subversion-1.2.3/subversion/include/svn_client.h 2005-05-07 21:00:28.000000000 +1000
+++ ./subversion/include/svn_client.h 2005-12-01 11:29:48.000000000 +1000
@@ -41,6 +41,9 @@
 #include "svn_opt.h"
 #include "svn_version.h"
 
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#include <CoreServices/CoreServices.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -2039,6 +2042,9 @@
                            svn_client_ctx_t *ctx,
                            apr_pool_t *pool);
 
+SecKeychainItemRef OSX_Keychain_GetPassword(const char *serviceName, const char *accountName, char *password, int length);
+void OSX_Keychain_SetPassword(const char *serviceName, const char *accountName, const char *password);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff -raud ../subversion-1.2.3/subversion/libsvn_client/simple_providers.c ./subversion/libsvn_client/simple_providers.c
--- ../subversion-1.2.3/subversion/libsvn_client/simple_providers.c 2005-05-07 06:20:28.000000000 +1000
+++ ./subversion/libsvn_client/simple_providers.c 2005-12-01 13:22:46.000000000 +1000
@@ -29,6 +29,44 @@
 #include "svn_utf.h"
 #include "svn_config.h"
 
+// Supply an account name (cert file name) and a buffer, we'll fill it with the password (if known)
+SecKeychainItemRef OSX_Keychain_GetPassword(const char *serviceName, const char *accountName, char *password, int length) {
+ OSStatus status;
+
+ SecKeychainItemRef itemRef = NULL;
+ UInt32 passwordLength = 0;
+ void *passwordData;
+ status = SecKeychainFindGenericPassword(NULL, strlen(serviceName), serviceName, strlen(accountName), accountName, &passwordLength, &passwordData, &itemRef);
+ if (status == noErr) {
+ if (password != NULL) {
+ strncpy(password, (char *)passwordData, (passwordLength<length?passwordLength:length));
+ password[passwordLength] = '\0';
+ }
+ SecKeychainItemFreeContent(NULL, passwordData);
+
+ return itemRef;
+ }
+ return NULL;
+}
+
+void OSX_Keychain_SetPassword(const char *serviceName, const char *accountName, const char *password) {
+ OSStatus status;
+
+ SecKeychainItemRef itemRef = OSX_Keychain_GetPassword(serviceName, accountName, NULL, 0);
+ if (itemRef) {
+ // This item exists
+ SecKeychainAttribute attrs[] = {
+ { kSecAccountItemAttr, strlen(accountName), (void*)accountName },
+ { kSecServiceItemAttr, strlen(serviceName), (void*)serviceName } };
+ const SecKeychainAttributeList attributes = {2, attrs};
+ status = SecKeychainItemModifyAttributesAndData(itemRef, &attributes, strlen(password), password);
+ }
+ else {
+ status = SecKeychainAddGenericPassword(NULL, strlen(serviceName), serviceName, strlen(accountName), accountName, strlen(password), password, NULL);
+ }
+}
+
+
 
 /*-----------------------------------------------------------------------*/
 /* File provider */
@@ -689,3 +727,89 @@
 }
 
 #endif /* WIN32 */
+
+
+
+/* Get cached encrypted credentials from the simple provider's cache. */
+static svn_error_t *
+osx_simple_first_creds (void **credentials,
+ void **iter_baton,
+ void *provider_baton,
+ apr_hash_t *parameters,
+ const char *realmstring,
+ apr_pool_t *pool)
+{
+// printf("Running OSX simple first creds\n");
+ char *creduser = NULL;
+ char *credpass = NULL;
+
+ char kcusername[64];
+ kcusername[0] = '\0';
+ OSX_Keychain_GetPassword("Subversion - Username", realmstring, kcusername, 64);
+ if (kcusername[0] != '\0') {
+ creduser = apr_palloc(pool, strlen(kcusername)+1);
+ strncpy(creduser, kcusername, strlen(kcusername)+1);
+// printf("Using your Keychain username: %s\n",creduser);
+
+ }
+
+ char kcpassword[64];
+ kcpassword[0] = '\0';
+ OSX_Keychain_GetPassword("Subversion - Password", realmstring, kcpassword, 64);
+ if (kcpassword[0] != '\0') {
+ credpass = apr_palloc(pool, strlen(kcpassword)+1);
+ strncpy(credpass, kcpassword, strlen(kcpassword)+1);
+// printf("Using your Keychain password: %s\n",credpass);
+ }
+
+ if ((creduser) || (credpass)) {
+ svn_auth_cred_simple_t *cred
+ = apr_palloc (pool, sizeof (*cred));
+ cred->may_save = FALSE;
+ if (creduser) cred->username = creduser;
+ if (credpass) cred->password = credpass;
+ *credentials = cred;
+ }
+ return SVN_NO_ERROR;
+}
+
+/* Save encrypted credentials to the simple provider's cache. */
+static svn_error_t *
+osx_simple_save_creds (svn_boolean_t *saved,
+ void *credentials,
+ void *provider_baton,
+ apr_hash_t *parameters,
+ const char *realmstring,
+ apr_pool_t *pool)
+{
+ svn_auth_cred_simple_t *cred = credentials;
+// printf("Saving your OSX Keychain username and password\n");
+
+ OSX_Keychain_SetPassword("Subversion - Username", realmstring, cred->username);
+ OSX_Keychain_SetPassword("Subversion - Password", realmstring, cred->password);
+ *saved = TRUE;
+
+ return SVN_NO_ERROR;
+}
+
+static const svn_auth_provider_t osx_simple_provider = {
+ SVN_AUTH_CRED_SIMPLE,
+ osx_simple_first_creds,
+ NULL,
+ osx_simple_save_creds
+};
+
+
+/* Public API */
+void
+svn_client_get_osx_simple_provider (svn_auth_provider_object_t **provider,
+ apr_pool_t *pool)
+{
+// printf("Getting OSX simple provider\n");
+ svn_auth_provider_object_t *po = apr_pcalloc (pool, sizeof(*po));
+
+ po->vtable = &osx_simple_provider;
+ *provider = po;
+}
+
+
diff -raud ../subversion-1.2.3/subversion/libsvn_client/ssl_client_cert_pw_providers.c ./subversion/libsvn_client/ssl_client_cert_pw_providers.c
--- ../subversion-1.2.3/subversion/libsvn_client/ssl_client_cert_pw_providers.c 2004-01-23 06:45:49.000000000 +1000
+++ ./subversion/libsvn_client/ssl_client_cert_pw_providers.c 2005-12-01 13:24:36.000000000 +1000
@@ -201,3 +201,75 @@
   po->provider_baton = pb;
   *provider = po;
 }
+
+
+/* Get cached encrypted credentials from the ssl_client_cert_pw provider's cache. */
+static svn_error_t *
+osx_ssl_client_cert_pw_file_first_creds (void **credentials,
+ void **iter_baton,
+ void *provider_baton,
+ apr_hash_t *parameters,
+ const char *realmstring,
+ apr_pool_t *pool)
+{
+ //printf("Running OSX SSL ClientCert PW file first creds\n");
+ char *credpass = NULL;
+
+ char kcpassword[64];
+ kcpassword[0] = '\0';
+ OSX_Keychain_GetPassword("Subversion - SSL ClientCert Password", realmstring, kcpassword, 64);
+ if (kcpassword[0] != '\0') {
+ credpass = apr_palloc(pool, strlen(kcpassword)+1);
+ strncpy(credpass, kcpassword, strlen(kcpassword)+1);
+ //printf("Using your Keychain password: %s\n",credpass);
+ }
+
+ if (credpass) {
+ svn_auth_cred_ssl_client_cert_pw_t *cred
+ = apr_palloc (pool, sizeof (*cred));
+ cred->may_save = FALSE;
+ cred->password = credpass;
+ *credentials = cred;
+ }
+ return SVN_NO_ERROR;
+}
+
+/* Save encrypted credentials to the ssl_client_cert_pw provider's cache. */
+static svn_error_t *
+osx_ssl_client_cert_pw_file_save_creds (svn_boolean_t *saved,
+ void *credentials,
+ void *provider_baton,
+ apr_hash_t *parameters,
+ const char *realmstring,
+ apr_pool_t *pool)
+{
+ svn_auth_cred_ssl_client_cert_pw_t *cred = credentials;
+ //printf("Saving your OSX Keychain SSL ClientCert password: \"%s\"\n", cred->password);
+
+ OSX_Keychain_SetPassword("Subversion - SSL ClientCert Password", realmstring, cred->password);
+ *saved = TRUE;
+
+ return SVN_NO_ERROR;
+}
+
+static const svn_auth_provider_t osx_ssl_client_cert_pw_file_provider = {
+ SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
+ osx_ssl_client_cert_pw_file_first_creds,
+ NULL,
+ osx_ssl_client_cert_pw_file_save_creds
+};
+
+
+/* Public API */
+void
+svn_client_get_osx_ssl_client_cert_pw_file_provider (svn_auth_provider_object_t **provider,
+ apr_pool_t *pool)
+{
+// printf("Getting OSX ssl_client_cert_pw provider\n");
+ svn_auth_provider_object_t *po = apr_pcalloc (pool, sizeof(*po));
+
+ po->vtable = &osx_ssl_client_cert_pw_file_provider;
+ *provider = po;
+}
+
+
diff -raud ../subversion-1.2.3/subversion/libsvn_ra_dav/session.c ./subversion/libsvn_ra_dav/session.c
--- ../subversion-1.2.3/subversion/libsvn_ra_dav/session.c 2005-07-02 02:02:13.000000000 +1000
+++ ./subversion/libsvn_ra_dav/session.c 2005-12-01 13:14:07.000000000 +1000
@@ -270,6 +270,8 @@
         }
       else
         {
+ // Now we save
+ svn_auth_save_credentials(state, pool);
           svn_auth_cred_ssl_client_cert_pw_t *pw_creds = creds;
 
           if (ne_ssl_clicert_decrypt(clicert, pw_creds->password) == 0)
diff -raud ../subversion-1.2.3/Makefile.in ./Makefile.in
--- ../subversion-1.2.3/Makefile.in 2005-05-09 15:56:09.000000000 +1000
+++ ./Makefile.in 2005-12-01 14:52:00.000000000 +1000
@@ -42,7 +42,7 @@
 SVN_APRUTIL_LIBS = @SVN_APRUTIL_LIBS@
 SVN_DB_LIBS =
 
-LIBS = @LIBS@
+LIBS = @LIBS@ -framework Security -framework CoreFoundation -framework CoreServices
 
 prefix = @prefix@
 exec_prefix = @exec_prefix@

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Thu Dec 1 10:13:26 2005

This is an archived mail posted to the Subversion Dev mailing list.