All pastes #2009618 Raw Edit

OS X keychain patch

public diff v1 · immutable
#2009618 ·published 2010-12-03 14:51 UTC
rendered paste body
From 150846dbe71b86eff27da05b01df5a49b1018857 Mon Sep 17 00:00:00 2001From: Brian Raderman <brian@irregularexpression.org>Date: Wed, 28 Apr 2010 00:26:00 -0400Subject: [PATCH] Implement Mac OSX Keychain certificate supportAdding functionality that allows the use of Mac OSX Keychain certificatesto authenticate with the server so that the certificate and the privatekey don't need to be stored in the config file. This also supports hardwaretokens that conform to Apple's tokend specification and can be accessedlike any other Keychain certificate. The changes are based on the Crypto APIfunctionality for Windows that already exists in the cryptoapi.c/.h files.Signed-off-by: Brian Raderman <brian@irregularexpression.org>--- Makefile.am      |    9 + configure.ac     |   19 ++ openvpn.8        |   66 ++++++ options.c        |   40 +++- options.h        |    3 + osx/cert_data.c  |  641 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ osx/cert_data.h  |   46 ++++ osx/common_osx.c |   99 +++++++++ osx/common_osx.h |   36 +++ osx/crypto_osx.c |   76 +++++++ osx/crypto_osx.h |   34 +++ osx/keychain.c   |  290 ++++++++++++++++++++++++ osx/keychain.h   |    7 + ssl.c            |   10 + 14 files changed, 1373 insertions(+), 3 deletions(-) create mode 100644 osx/cert_data.c create mode 100644 osx/cert_data.h create mode 100644 osx/common_osx.c create mode 100644 osx/common_osx.h create mode 100644 osx/crypto_osx.c create mode 100644 osx/crypto_osx.h create mode 100644 osx/keychain.c create mode 100644 osx/keychain.hdiff --git a/Makefile.am b/Makefile.amindex 430d3ab..ce8806b 100644--- a/Makefile.am+++ b/Makefile.am@@ -143,6 +143,15 @@ openvpn_SOURCES = \ 	win32.h win32.c \ 	cryptoapi.h cryptoapi.c +if KEYCHAINCERT+openvpn_SOURCES += \+	osx/cert_data.h osx/cert_data.c \+	osx/common_osx.h osx/common_osx.c \+	osx/crypto_osx.h osx/crypto_osx.c \+	osx/keychain.h osx/keychain.c+endif++ nodist_openvpn_SOURCES = configure.h options.$(OBJEXT): configure.h diff --git a/configure.ac b/configure.acindex 795c650..caee488 100644--- a/configure.ac+++ b/configure.ac@@ -223,6 +223,12 @@ AC_ARG_ENABLE(selinux,    [SELINUX="yes"] ) +AC_ARG_ENABLE(keychaincert,+   [  --disable-keychaincert  Disable support for Mac OSX Keychain certificate services (Mac OSX only)],+   [KEYCHAINCERT="$enableval"],+   [KEYCHAINCERT="yes"]+)+ AC_ARG_WITH(ssl-headers,    [  --with-ssl-headers=DIR  Crypto/SSL Include files location],    [CS_HDR_DIR="$withval"]@@ -294,6 +300,7 @@ dnl fix search path, to allow compilers to find syshead.h CPPFLAGS="$CPPFLAGS -I${srcdir}"  dnl check host OS+MACOSX="no" openvpn_host=$host if test $host_alias; then   openvpn_host=$host_alias@@ -323,6 +330,7 @@ case "$host" in 	dnl some Mac OS X tendering (we use vararg macros...) 	AC_DEFINE(TARGET_DARWIN, 1, [Are we running on Mac OS X?]) 	CPPFLAGS="$CPPFLAGS -no-cpp-precomp"+	MACOSX="yes" 	;; *-mingw*) 	AC_DEFINE(TARGET_WIN32, 1, [Are we running WIN32?])@@ -920,6 +928,17 @@ if test "$SELINUX" = "yes"; then    ) fi +dnl enable Mac OSX Keychain certificate support+if test "$MACOSX" = "yes" -a "$KEYCHAINCERT" = "yes"; then+	OPENVPN_ADD_LIBS(-framework CoreFoundation)+	OPENVPN_ADD_LIBS(-framework Security)+	AC_DEFINE(KEYCHAINCERT, 1, [Enable support for MAc OSX Keychain certificate services])+else+	KEYCHAINCERT="no"+fi++AM_CONDITIONAL(KEYCHAINCERT, test "$KEYCHAINCERT" = "yes")+ TAP_ID="PRODUCT_TAP_ID" TAP_WIN32_MIN_MAJOR="PRODUCT_TAP_WIN32_MIN_MAJOR" TAP_WIN32_MIN_MINOR="PRODUCT_TAP_WIN32_MIN_MINOR"diff --git a/openvpn.8 b/openvpn.8index 1303b52..113afbe 100644--- a/openvpn.8+++ b/openvpn.8@@ -3961,6 +3961,72 @@ Certificate Store GUI.  .\"********************************************************* .TP+.B \-\-keychaincert select-string+Load the certificate and private key from the+Mac OSX Keychain (Mac OSX Only).++Use this option instead of+.B \-\-cert+and+.B \-\-key.++This makes it possible to use any smart card supported by Mac OSX using the+tokend interface, but also anykind of certificate, residing in the Keychain,+where you have access to the private key.  This option has been tested on+the client side with an Aladdin eToken on Mac OSX Leopard and with software+certificates stored in the Keychain on Mac OSX Leopard and Mac OSX Snow Leopard.+As of this writing (4/27/10) Aladdin has not yet released Snow Leopard drivers.++To select a certificate based on a string search in the+certificate's subject and/or issuer:++.B keychaincert+"SUBJECT:c=US/o=Apple Inc./ou=me.com/cn=username ISSUER:c=US/o=Apple Computer,+Inc./ou=Apple Computer Certificate Authority/cn=Apple .Mac Certificate Authority"++.I "Distinguished Name Component Abbreviations:"+.br+o = organization+.br+ou = organizational unit+.br+c = country+.br+l = locality+.br+st = state+.br+cn = common name+.br+e = email+.br++All of the distinguished name components are optional, although you do need to+specify at least one of them.  You can add spaces around the '/' and '='+characters, e.g. "SUBJECT: c = US / o = Apple Inc.".  You do not need to specify+both the subject and the issuer, one or the other will work fine.  The identity+searching algorithm will return the first certificate it finds that matches all+of the criteria you have specified.  You can also include the MD5 and/or SHA1+thumbprints along with the subject and issuer (see example of thumbprint search+criteria below).++To select a certificate based on certificate's MD5 or SHA1 thumbprint:++.B keychaincert+"SHA1: 30 F7 3A 7A B7 73 2A 98 54 33 4A A7 00 6F 6E AC EC D1 EF 02+MD5: D5 F5 11 F1 38 EB 5F 4D CF 23 B6 94 E8 33 D8 B5"++Again, you can include both the SHA1 and the MD5 thumbprints, but you can also+use just one of them.  The thumbprint hex strings can easily be copy-and-pasted+from the OSX Keychain Access GUI in the Applications/Utilities folder.  The hex+string comparison is not case sensitive.++Note that this option cannot be used with the --daemon option.  Mac OSX needs+to present the user with an authentication GUI when the Keychain is accessed+by openvpn and the --daemon option will prevent the GUI from being shown and+therefore cause the authentication to silently fail.+.\"*********************************************************+.TP .B \-\-key-method m Use data channel key negotiation method .B m.diff --git a/options.c b/options.cindex 61da085..fe72a8f 100644--- a/options.c+++ b/options.c@@ -542,6 +542,10 @@ static const char usage_message[] =   "--cryptoapicert select-string : Load the certificate and private key from the\n"   "                  Windows Certificate System Store.\n" #endif+#ifdef KEYCHAINCERT+  "--keychaincert  select-string : Load the certificate and private key from the\n"+  "                  Mac OSX Keychain.\n"+#endif   "--tls-cipher l  : A list l of allowable TLS ciphers separated by : (optional).\n"   "                : Use --show-tls to see a list of supported TLS ciphers.\n"   "--tls-timeout n : Packet retransmit timeout on TLS control channel\n"@@ -1480,6 +1484,9 @@ show_settings (const struct options *o) #ifdef WIN32   SHOW_STR (cryptoapi_cert); #endif+#ifdef KEYCHAINCERT+	SHOW_STR (keychain_cert);+#endif   SHOW_STR (cipher_list);   SHOW_STR (tls_verify);   SHOW_STR (tls_export_cert);@@ -2198,11 +2205,15 @@ options_postprocess_verify_ce (const struct options *options, const struct conne 	if (options->cryptoapi_cert) 	  msg(M_USAGE, "Parameter --cryptoapicert cannot be used when --pkcs11-provider is also specified."); #endif+#ifdef KEYCHAINCERT+	if (options->keychain_cert)+	   msg(M_USAGE, "Parameter --keychaincert cannot be used when --pkcs11-provider is also specified.");+#endif        }       else #endif #ifdef WIN32-      if (options->cryptoapi_cert)+	if (options->cryptoapi_cert) 	{ 	  if ((!(options->ca_file)) && (!(options->ca_path))) 	    msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath)");@@ -2213,9 +2224,25 @@ options_postprocess_verify_ce (const struct options *options, const struct conne           if (options->pkcs12_file) 	    msg(M_USAGE, "Parameter --pkcs12 cannot be used when --cryptoapicert is also specified."); 	}-      else+	else+#endif+#ifdef KEYCHAINCERT+	if (options->keychain_cert)+	{+		if ((!(options->ca_file)) && (!(options->ca_path)))+			msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath)");+		if (options->cert_file)+			msg(M_USAGE, "Parameter --cert cannot be used when --keychaincert is also specified.");+		if (options->priv_key_file)+			msg(M_USAGE, "Parameter --key cannot be used when --keychaincert is also specified.");+		if (options->pkcs12_file)+			msg(M_USAGE, "Parameter --pkcs12 cannot be used when --keychaincert is also specified.");+        if (options->daemon)+            msg(M_USAGE, "Parameter --daemon cannot be used when --keychaincert is also specified.");+	}+	else #endif-      if (options->pkcs12_file)+       if (options->pkcs12_file)         {           if (options->ca_path) 	    msg(M_USAGE, "Parameter --capath cannot be used when --pkcs12 is also specified.");@@ -6057,6 +6084,13 @@ add_option (struct options *options,       options->cryptoapi_cert = p[1];     } #endif+#ifdef KEYCHAINCERT+  else if (streq (p[0], "keychaincert") && p[1])+  {+      VERIFY_PERMISSION (OPT_P_GENERAL);+      options->keychain_cert = p[1];+  }+#endif   else if (streq (p[0], "key") && p[1])     {       VERIFY_PERMISSION (OPT_P_GENERAL);diff --git a/options.h b/options.hindex 1ef96ca..e41f79e 100644--- a/options.h+++ b/options.h@@ -513,6 +513,9 @@ struct options #ifdef WIN32   const char *cryptoapi_cert; #endif+#ifdef KEYCHAINCERT+  const char *keychain_cert;+#endif    /* data channel key exchange method */   int key_method;diff --git a/osx/cert_data.c b/osx/cert_data.cnew file mode 100644index 0000000..c90b0b1--- /dev/null+++ b/osx/cert_data.c@@ -0,0 +1,641 @@+/*+ *  OpenVPN -- An application to securely tunnel IP networks+ *             over a single UDP port, with support for SSL/TLS-based+ *             session authentication and key exchange,+ *             packet encryption, packet authentication, and+ *             packet compression.+ *+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>+ *+ *  This program is free software; you can redistribute it and/or modify+ *  it under the terms of the GNU General Public License version 2+ *  as published by the Free Software Foundation.+ *+ *  This program is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ *  GNU General Public License for more details.+ *+ *  You should have received a copy of the GNU General Public License+ *  along with this program (see the file COPYING included with this+ *  distribution); if not, write to the Free Software Foundation, Inc.,+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ */++#include "../syshead.h"+#include "../error.h"+#include "../buffer.h"+#include <CommonCrypto/CommonDigest.h>+#include "cert_data.h"+#include "common_osx.h"++CFStringRef kCertDataSubjectName = CFSTR("subject"), kCertDataIssuerName = CFSTR("issuer"), kCertDataSha1Name = CFSTR("SHA1"), kCertDataMd5Name = CFSTR("MD5"), kCertNameFwdSlash = CFSTR("/"), kCertNameEquals = CFSTR("=");+CFStringRef kCertNameOrganization = CFSTR("o"), kCertNameOrganizationalUnit = CFSTR("ou"), kCertNameCountry = CFSTR("c"), kCertNameLocality = CFSTR("l"), kCertNameState = CFSTR("st"), kCertNameCommonName = CFSTR("cn"), kCertNameEmail = CFSTR("e"), kCertNameDomainComponent = CFSTR("dc");+CFStringRef kStringSpace = CFSTR(" "), kStringEmpty = CFSTR("");++typedef struct _CertName+{+	CFArrayRef countryName, organization, organizationalUnit, commonName, description, emailAddress, +	stateName, localityName, distinguishedName, domainComponent;+} CertName, *CertNameRef;		++typedef struct _DescData+{+	CFStringRef name, value;+} DescData, *DescDataRef;++static struct gc_arena g_gc;++void initCertData()+{+	g_gc = gc_new();+}++void freeCertData()+{+	gc_free(&g_gc);+}++CertNameRef createCertName()+{+	CertNameRef pCertName = (CertNameRef)gc_malloc(sizeof(CertName), false, &g_gc);+	pCertName->countryName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);+	pCertName->organization =  CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);+	pCertName->organizationalUnit = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);+	pCertName->commonName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);+	pCertName->description = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);	+	pCertName->emailAddress = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);+	pCertName->stateName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);+	pCertName->localityName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);+	pCertName->distinguishedName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);+	pCertName->domainComponent = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);+	return pCertName;+}++void destroyCertName(CertNameRef pCertName)+{	+	if (!pCertName)+		return;+	+	CFRelease(pCertName->countryName);+	CFRelease(pCertName->organization);+	CFRelease(pCertName->organizationalUnit);+	CFRelease(pCertName->commonName);+	CFRelease(pCertName->description);+	CFRelease(pCertName->emailAddress);+	CFRelease(pCertName->stateName);+	CFRelease(pCertName->localityName);+	CFRelease(pCertName->distinguishedName);+	CFRelease(pCertName->domainComponent);+}+++CertNameRef dataToName(CSSM_DATA_PTR pData)+{+    CSSM_X509_NAME_PTR pName = (CSSM_X509_NAME_PTR)pData->Data;+	CertNameRef pCertName = createCertName();+    int i, j;+	    +    for(i = 0;i < pName->numberOfRDNs;i++)+    {+        CSSM_X509_RDN rdn = pName->RelativeDistinguishedName[i];+        +        for(j = 0;j < rdn.numberOfPairs;j++)+        {+            CSSM_X509_TYPE_VALUE_PAIR nvp = rdn.AttributeTypeAndValue[j];+			CFStringRef str = CFStringCreateWithBytes (NULL, nvp.value.Data, nvp.value.Length, kCFStringEncodingUTF8, false);+            +			if (memcmp(nvp.type.Data, CSSMOID_CountryName.Data, CSSMOID_CountryName.Length) == 0)+				CFArrayAppendValue((CFMutableArrayRef)pCertName->countryName, str);+			else if (memcmp(nvp.type.Data, CSSMOID_OrganizationName.Data, CSSMOID_OrganizationName.Length) == 0)+				CFArrayAppendValue((CFMutableArrayRef)pCertName->organization, str);+			else if (memcmp(nvp.type.Data, CSSMOID_OrganizationalUnitName.Data, CSSMOID_OrganizationalUnitName.Length) == 0)+				CFArrayAppendValue((CFMutableArrayRef)pCertName->organizationalUnit, str);+			else if (memcmp(nvp.type.Data, CSSMOID_CommonName.Data, CSSMOID_CommonName.Length) == 0)+				CFArrayAppendValue((CFMutableArrayRef)pCertName->commonName, str);+			else if (memcmp(nvp.type.Data, CSSMOID_Description.Data, CSSMOID_Description.Length) == 0)+				CFArrayAppendValue((CFMutableArrayRef)pCertName->description, str);+			else if (memcmp(nvp.type.Data, CSSMOID_EmailAddress.Data, CSSMOID_EmailAddress.Length) == 0)+				CFArrayAppendValue((CFMutableArrayRef)pCertName->emailAddress, str);+			else if (memcmp(nvp.type.Data, CSSMOID_StateProvinceName.Data, CSSMOID_StateProvinceName.Length) == 0)+				CFArrayAppendValue((CFMutableArrayRef)pCertName->stateName, str);+			else if (memcmp(nvp.type.Data, CSSMOID_LocalityName.Data, CSSMOID_LocalityName.Length) == 0)+				CFArrayAppendValue((CFMutableArrayRef)pCertName->localityName, str);+			else if (memcmp(nvp.type.Data, CSSMOID_DistinguishedName.Data, CSSMOID_DistinguishedName.Length) == 0)+				CFArrayAppendValue((CFMutableArrayRef)pCertName->distinguishedName, str);+			+			CFRelease(str);+        }+    }+    +    return pCertName;+}++CFArrayRef GetFieldsFromCssmCertData(CSSM_CL_HANDLE hCL, CSSM_DATA_PTR pCertData, CSSM_OID oid)+{+	CSSM_DATA_PTR pData = NULL;+	CFMutableArrayRef fields = CFArrayCreateMutable(NULL, 0, NULL);+	uint32 numFields, i;+	+	CSSM_HANDLE ResultsHandle = (CSSM_HANDLE)NULL;+	numFields = 0;+	+	CSSM_CL_CertGetFirstFieldValue(hCL, pCertData, &oid, &ResultsHandle, &numFields, &pData);+	+	if (!pData)+	{+		CSSM_CL_CertAbortQuery(hCL, ResultsHandle);+		return NULL;+	}+	+	for (i = 0; i < numFields; i++) +	{		+		CertNameRef pName = dataToName(pData);+		CFArrayAppendValue(fields, pName);+		CSSM_CL_FreeFieldValue (hCL, &oid, pData);		+		if (i < (numFields - 1))+			CSSM_CL_CertGetNextFieldValue(hCL, ResultsHandle, &pData);+	}++	CSSM_CL_CertAbortQuery(hCL, ResultsHandle);+	return fields;+}++CFArrayRef GetFieldsFromCertificate(SecCertificateRef certificate, CSSM_OID oid)+{+	CSSM_CL_HANDLE hCL;+	CSSM_DATA certData;+	CFArrayRef fieldValues;+	+	SecCertificateGetCLHandle(certificate, &hCL);+	SecCertificateGetData(certificate, &certData);+	+	fieldValues = GetFieldsFromCssmCertData(hCL, &certData, oid);+	+	if (fieldValues == NULL) 		+	{+		return NULL; +	}+	else if (CFArrayGetCount(fieldValues) == 0)+	{+		CFRelease(fieldValues);+		return NULL;+	}+	+	return fieldValues;+}++CertDataRef createCertDataFromCertificate(SecCertificateRef certificate)+{+	+	CertDataRef pCertData = (CertDataRef)gc_malloc(sizeof(CertData), false, &g_gc);+	pCertData->subject = GetFieldsFromCertificate(certificate, CSSMOID_X509V1SubjectNameCStruct);+	pCertData->issuer = GetFieldsFromCertificate(certificate, CSSMOID_X509V1IssuerNameCStruct);++	CSSM_DATA cssmCertData;+	SecCertificateGetData (certificate, &cssmCertData);++	unsigned char sha1[CC_SHA1_DIGEST_LENGTH]; +	CC_SHA1(cssmCertData.Data, cssmCertData.Length, sha1); +	pCertData->sha1 = createHexString(sha1, CC_SHA1_DIGEST_LENGTH);+	+	unsigned char md5[CC_MD5_DIGEST_LENGTH]; +	CC_MD5(cssmCertData.Data, cssmCertData.Length, md5); +	pCertData->md5 = createHexString((unsigned char*)md5, CC_MD5_DIGEST_LENGTH);+	+	return pCertData;+}++CFStringRef stringFromRange(const char *cstring, CFRange range)+{+	CFStringRef str = CFStringCreateWithBytes (NULL, (uint8*)&cstring[range.location], range.length, kCFStringEncodingUTF8, false);+	CFMutableStringRef mutableStr = CFStringCreateMutableCopy(NULL, 0, str);+	CFStringTrimWhitespace(mutableStr);+	CFRelease(str);+	return mutableStr;+}++DescDataRef createDescData(const char *description, CFRange nameRange, CFRange valueRange)+{+	DescDataRef pRetVal = (DescDataRef)gc_malloc(sizeof(DescData), false, &g_gc);+	+	memset(pRetVal, 0, sizeof(DescData));+	+	if (nameRange.length > 0)+		pRetVal->name = stringFromRange(description, nameRange);+	+	if (valueRange.length > 0)+		pRetVal->value = stringFromRange(description, valueRange);+	+	return pRetVal;+}++void destroyDescData(DescDataRef pData)+{+	if (pData->name)+		CFRelease(pData->name);+	+	if (pData->value)+		CFRelease(pData->value);+}++CFArrayRef createDescDataPairs(const char *description)+{+	int numChars = strlen(description);+	CFRange nameRange, valueRange;+	DescDataRef pData;+	CFMutableArrayRef retVal = CFArrayCreateMutable(NULL, 0, NULL);+	+	int i = 0;+	+	nameRange = CFRangeMake(0, 0);+	valueRange = CFRangeMake(0, 0);+	bool bInValue = false;+	+	while(i < numChars)+	{+		if (!bInValue && (description[i] != ':'))+		{+			nameRange.length++;+		}+		else if (bInValue && (description[i] != ':'))+		{+			valueRange.length++;+		}+		else if(!bInValue)+		{+			bInValue = true;+			valueRange.location = i + 1;+			valueRange.length = 0;+		}+		else //(bInValue)+		{+			bInValue = false;+			while(description[i] != ' ')+			{+				valueRange.length--;+				i--;+			}+			+			pData = createDescData(description, nameRange, valueRange);+			CFArrayAppendValue(retVal, pData);+			+			nameRange.location = i + 1;+			nameRange.length = 0;+		}+		+		i++;+	}+	+	pData = createDescData(description, nameRange, valueRange);+	CFArrayAppendValue(retVal, pData);+	return retVal;+}++void arrayDestroyDescData(const void *val, void *context)+{+	DescDataRef pData = (DescDataRef) val;+	destroyDescData(pData);+}+++void parseNameComponent(CFStringRef dn, CFStringRef *pName, CFStringRef *pValue)+{+	CFArrayRef nameStrings = CFStringCreateArrayBySeparatingStrings(NULL, dn, kCertNameEquals);+	+	*pName = *pValue = NULL;+	+	if (CFArrayGetCount(nameStrings) != 2)+		return;+	+	CFMutableStringRef str;+	+	str = CFStringCreateMutableCopy(NULL, 0, CFArrayGetValueAtIndex(nameStrings, 0));+	CFStringTrimWhitespace(str);+	*pName = str;+	+	str = CFStringCreateMutableCopy(NULL, 0, CFArrayGetValueAtIndex(nameStrings, 1));+	CFStringTrimWhitespace(str);+	*pValue = str;+	+	CFRelease(nameStrings);+}++void parseCertName(CFStringRef nameDesc, CFMutableArrayRef names)+{+	CFArrayRef nameStrings = CFStringCreateArrayBySeparatingStrings(NULL, nameDesc, kCertNameFwdSlash);+	int count = CFArrayGetCount(nameStrings);+    int i;+	+	CertNameRef pCertName = createCertName();+	+	for(i = 0;i < count;i++)+	{+		CFMutableStringRef dn = CFStringCreateMutableCopy(NULL, 0, CFArrayGetValueAtIndex(nameStrings, i));+		CFStringTrimWhitespace(dn);+		+		CFStringRef name, value;+		+		parseNameComponent(dn, &name, &value);+		+		if (!name || !value)+		{+			if (name)+				CFRelease(name);+			+			if (value)+				CFRelease(value);+			+			CFRelease(dn);+			continue;+		}+		+		if (CFStringCompareWithOptions(name, kCertNameOrganization, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+			CFArrayAppendValue((CFMutableArrayRef)pCertName->organization, value);+		else if (CFStringCompareWithOptions(name, kCertNameOrganizationalUnit, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+			CFArrayAppendValue((CFMutableArrayRef)pCertName->organizationalUnit, value);+		else if (CFStringCompareWithOptions(name, kCertNameCountry, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+			CFArrayAppendValue((CFMutableArrayRef)pCertName->countryName, value);+		else if (CFStringCompareWithOptions(name, kCertNameLocality, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+			CFArrayAppendValue((CFMutableArrayRef)pCertName->localityName, value);+		else if (CFStringCompareWithOptions(name, kCertNameState, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+			CFArrayAppendValue((CFMutableArrayRef)pCertName->stateName, value);+		else if (CFStringCompareWithOptions(name, kCertNameCommonName, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+			CFArrayAppendValue((CFMutableArrayRef)pCertName->commonName, value);+		else if (CFStringCompareWithOptions(name, kCertNameEmail, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+			CFArrayAppendValue((CFMutableArrayRef)pCertName->emailAddress, value);+		else if (CFStringCompareWithOptions(name, kCertNameDomainComponent, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+			CFArrayAppendValue((CFMutableArrayRef)pCertName->domainComponent, value);+		+		CFRelease(name);+		CFRelease(value);+		CFRelease(dn);+	}+	+	CFArrayAppendValue(names, pCertName);+	CFRelease(nameStrings);+}++void arrayParseDescDataPair(const void *val, void *context)+{+	DescDataRef pDescData = (DescDataRef)val;+	CertDataRef pCertData = (CertDataRef)context;+	+	if (!pDescData->name || !pDescData->value)+		return;+	+	if (CFStringCompareWithOptions(pDescData->name, kCertDataSubjectName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+		parseCertName(pDescData->value, (CFMutableArrayRef)pCertData->subject);+	else if (CFStringCompareWithOptions(pDescData->name, kCertDataIssuerName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+		parseCertName(pDescData->value, (CFMutableArrayRef)pCertData->issuer);+	else if (CFStringCompareWithOptions(pDescData->name, kCertDataSha1Name, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+		pCertData->sha1 = CFRetain(pDescData->value);+	else if (CFStringCompareWithOptions(pDescData->name, kCertDataMd5Name, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)+		pCertData->md5 = CFRetain(pDescData->value);+}++CertDataRef createCertDataFromString(const char *description)+{+	CertDataRef pCertData = (CertDataRef)gc_malloc(sizeof(CertData), false, &g_gc);+	pCertData->subject = CFArrayCreateMutable(NULL, 0, NULL);+	pCertData->issuer = CFArrayCreateMutable(NULL, 0, NULL);+	pCertData->sha1 = NULL;+	pCertData->md5 = NULL;+	+	CFArrayRef pairs = createDescDataPairs(description);+	CFArrayApplyFunction(pairs, CFRangeMake(0, CFArrayGetCount(pairs)), arrayParseDescDataPair, pCertData);+	CFArrayApplyFunction(pairs, CFRangeMake(0, CFArrayGetCount(pairs)), arrayDestroyDescData, NULL);+	CFRelease(pairs);+	return pCertData;+}++void arrayDestroyCertName(const void *val, void *context)+{+	CertNameRef pCertName = (CertNameRef)val;+	destroyCertName(pCertName);+}++void destroyCertData(CertDataRef pCertData)+{+	if (pCertData->subject)+	{+		CFArrayApplyFunction(pCertData->subject, CFRangeMake(0, CFArrayGetCount(pCertData->subject)), arrayDestroyCertName, NULL);+		CFRelease(pCertData->subject);+	}+	+	if (pCertData->issuer)+	{+		CFArrayApplyFunction(pCertData->issuer, CFRangeMake(0, CFArrayGetCount(pCertData->issuer)), arrayDestroyCertName, NULL);+		CFRelease(pCertData->issuer);+	}+	+	if (pCertData->sha1)+		CFRelease(pCertData->sha1);+	+	if (pCertData->md5)+		CFRelease(pCertData->md5);+}++bool stringArrayMatchesTemplate(CFArrayRef strings, CFArrayRef templateArray)+{+	int templateCount, stringCount, i;+	+	templateCount = CFArrayGetCount(templateArray);+	+	if (templateCount > 0)+	{+		stringCount = CFArrayGetCount(strings);+		if (stringCount != templateCount)+			return false;+		+		for(i = 0;i < stringCount;i++)+		{+			CFStringRef str, template;+			+			template = (CFStringRef)CFArrayGetValueAtIndex(templateArray, i);+			str = (CFStringRef)CFArrayGetValueAtIndex(strings, i);+			+			if (CFStringCompareWithOptions(template, str, CFRangeMake(0, CFStringGetLength(template)), kCFCompareCaseInsensitive) != kCFCompareEqualTo)+				return false;+		}+	}+	+	return true;+	+}++bool certNameMatchesTemplate(CertNameRef pCertName, CertNameRef pTemplate)+{+	if (!stringArrayMatchesTemplate(pCertName->countryName, pTemplate->countryName))+		return false;+	else if (!stringArrayMatchesTemplate(pCertName->organization, pTemplate->organization))+		return false;+	else if (!stringArrayMatchesTemplate(pCertName->organizationalUnit, pTemplate->organizationalUnit))+		return false;+	else if (!stringArrayMatchesTemplate(pCertName->commonName, pTemplate->commonName))+		return false;+	else if (!stringArrayMatchesTemplate(pCertName->emailAddress, pTemplate->emailAddress))+		return false;+	else if (!stringArrayMatchesTemplate(pCertName->stateName, pTemplate->stateName))+		return false;+	else if (!stringArrayMatchesTemplate(pCertName->localityName, pTemplate->localityName))+		return false;+	else+		return true;+}++bool certNameArrayMatchesTemplate(CFArrayRef certNameArray, CFArrayRef templateArray)+{+	int templateCount, certCount, i;+	+	templateCount = CFArrayGetCount(templateArray);+	+	if (templateCount > 0)+	{+		certCount = CFArrayGetCount(certNameArray);+		if (certCount != templateCount)+			return false;+		+		for(i = 0;i < certCount;i++)+		{+			CertNameRef pName, pTemplateName;+			+			pTemplateName = (CertNameRef)CFArrayGetValueAtIndex(templateArray, i);+			pName = (CertNameRef)CFArrayGetValueAtIndex(certNameArray, i);+			+			if (!certNameMatchesTemplate(pName, pTemplateName))+				return false;+		}+	}+	+	return true;+}++bool hexStringMatchesTemplate(CFStringRef str, CFStringRef template)+{+	if (template)+	{+		if (!str)+			return false;+		+		CFMutableStringRef strMutable, templateMutable;+		+		strMutable = CFStringCreateMutableCopy(NULL, 0, str);+		templateMutable = CFStringCreateMutableCopy(NULL, 0, template);+		+		CFStringFindAndReplace(strMutable, kStringSpace, kStringEmpty, CFRangeMake(0, CFStringGetLength(strMutable)), 0);+		CFStringFindAndReplace(templateMutable, kStringSpace, kStringEmpty, CFRangeMake(0, CFStringGetLength(templateMutable)), 0);++        CFComparisonResult result = CFStringCompareWithOptions(templateMutable, strMutable, CFRangeMake(0, CFStringGetLength(templateMutable)), kCFCompareCaseInsensitive);+		+		CFRelease(strMutable);+		CFRelease(templateMutable);+				+		if (result != kCFCompareEqualTo)+			return false;+	}+	+	return true;+}++bool certDataMatchesTemplate(CertDataRef pCertData, CertDataRef pTemplate)+{+	if (!certNameArrayMatchesTemplate(pCertData->subject, pTemplate->subject))+		return false;+	+	if (!certNameArrayMatchesTemplate(pCertData->issuer, pTemplate->issuer))+		return false;+	+	if (!hexStringMatchesTemplate(pCertData->sha1, pTemplate->sha1))+		return false;+	+	if (!hexStringMatchesTemplate(pCertData->md5, pTemplate->md5))+		return false;++	return true;+}++void printString(const void *val, void *context) +{+    CFStringRef str = (CFStringRef)val;+	const char *label = (const char *)context;+	msg(M_DEBUG, "%s = ", label);+	printCFString(str, &g_gc);+	msg(M_DEBUG, "\n");+}++void printCertName(CertNameRef pCertName) +{+	CFArrayApplyFunction(pCertName->countryName, CFRangeMake(0, CFArrayGetCount(pCertName->countryName)), printString, "Country Name");+	CFArrayApplyFunction(pCertName->organization, CFRangeMake(0, CFArrayGetCount(pCertName->organization)), printString, "Organization");+	CFArrayApplyFunction(pCertName->organizationalUnit, CFRangeMake(0, CFArrayGetCount(pCertName->organizationalUnit)), printString, "Organizational Unit");+	CFArrayApplyFunction(pCertName->commonName, CFRangeMake(0, CFArrayGetCount(pCertName->commonName)), printString, "Common Name");+	CFArrayApplyFunction(pCertName->description, CFRangeMake(0, CFArrayGetCount(pCertName->description)), printString, "Description");+	CFArrayApplyFunction(pCertName->emailAddress, CFRangeMake(0, CFArrayGetCount(pCertName->emailAddress)), printString, "Email Address");+	CFArrayApplyFunction(pCertName->stateName, CFRangeMake(0, CFArrayGetCount(pCertName->stateName)), printString, "State Name");+	CFArrayApplyFunction(pCertName->localityName, CFRangeMake(0, CFArrayGetCount(pCertName->localityName)), printString, "Locality Name");+	CFArrayApplyFunction(pCertName->distinguishedName, CFRangeMake(0, CFArrayGetCount(pCertName->distinguishedName)), printString, "Distinguished Name");	+}++void arrayPrintCertName(const void *val, void *context)+{+	CertNameRef pCertName = (CertNameRef) val;+	printCertName(pCertName);+}++void printCertData(CertDataRef pCertData)+{+	msg(M_DEBUG, "*** Subject ***\n");+	if (pCertData->subject)+		CFArrayApplyFunction(pCertData->subject, CFRangeMake(0, CFArrayGetCount(pCertData->subject)), arrayPrintCertName, NULL);++	msg(M_DEBUG, "*** Issuer ***\n");+	if (pCertData->issuer)+		CFArrayApplyFunction(pCertData->issuer, CFRangeMake(0, CFArrayGetCount(pCertData->issuer)), arrayPrintCertName, NULL);+	+	msg(M_DEBUG, "*** Fingerprints ***\n");+	+	msg(M_DEBUG, "SHA1 = ");+	if (pCertData->sha1)+		printCFString(pCertData->sha1, &g_gc);+	+	msg(M_DEBUG, "\n");+		+	msg(M_DEBUG, "MD5 = ");+	if (pCertData->md5)+		printCFString(pCertData->md5, &g_gc);+	+	msg(M_DEBUG, "\n");	+}++SecIdentityRef findIdentity(CertDataRef pCertDataTemplate)+{+	SecIdentitySearchRef search;+	SecIdentitySearchCreate(NULL, 0, &search);+	+	SecIdentityRef identity;+	while(SecIdentitySearchCopyNext(search, &identity) != errSecItemNotFound)+	{+		SecCertificateRef certificate;+		SecIdentityCopyCertificate (identity, &certificate);+		+		CertDataRef pCertData = createCertDataFromCertificate(certificate);		+		bool bMatches = certDataMatchesTemplate(pCertData, pCertDataTemplate);++		destroyCertData(pCertData);+		CFRelease(certificate);++		if (bMatches)+		{+			break;+		}+		else+		{			+			CFRelease(identity);+			identity = NULL;+		}+	}+	+	CFRelease(search);+	return identity;+}diff --git a/osx/cert_data.h b/osx/cert_data.hnew file mode 100644index 0000000..639678e--- /dev/null+++ b/osx/cert_data.h@@ -0,0 +1,46 @@+/*+ *  OpenVPN -- An application to securely tunnel IP networks+ *             over a single UDP port, with support for SSL/TLS-based+ *             session authentication and key exchange,+ *             packet encryption, packet authentication, and+ *             packet compression.+ *+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>+ *+ *  This program is free software; you can redistribute it and/or modify+ *  it under the terms of the GNU General Public License version 2+ *  as published by the Free Software Foundation.+ *+ *  This program is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ *  GNU General Public License for more details.+ *+ *  You should have received a copy of the GNU General Public License+ *  along with this program (see the file COPYING included with this+ *  distribution); if not, write to the Free Software Foundation, Inc.,+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ */+#ifndef __cert_data_h__+#define __cert_data_h__++#include <CoreFoundation/CoreFoundation.h>+#include <Security/Security.h>++typedef struct _CertData+{+	CFArrayRef subject;+	CFArrayRef issuer;+	CFStringRef md5, sha1;+} CertData, *CertDataRef;++CertDataRef createCertDataFromCertificate(SecCertificateRef certificate);+CertDataRef createCertDataFromString(const char *description);+void destroyCertData(CertDataRef pCertData);+bool certDataMatchesTemplate(CertDataRef pCertData, CertDataRef pTemplate);+void printCertData(CertDataRef pCertData);+SecIdentityRef findIdentity(CertDataRef pCertDataTemplate);+void initCertData();+void freeCertData();++#endif\ No newline at end of filediff --git a/osx/common_osx.c b/osx/common_osx.cnew file mode 100644index 0000000..4909acd--- /dev/null+++ b/osx/common_osx.c@@ -0,0 +1,99 @@+/*+ *  OpenVPN -- An application to securely tunnel IP networks+ *             over a single UDP port, with support for SSL/TLS-based+ *             session authentication and key exchange,+ *             packet encryption, packet authentication, and+ *             packet compression.+ *+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>+ *+ *  This program is free software; you can redistribute it and/or modify+ *  it under the terms of the GNU General Public License version 2+ *  as published by the Free Software Foundation.+ *+ *  This program is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ *  GNU General Public License for more details.+ *+ *  You should have received a copy of the GNU General Public License+ *  along with this program (see the file COPYING included with this+ *  distribution); if not, write to the Free Software Foundation, Inc.,+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ */+#include "../syshead.h"+#include "../error.h"+#include "common_osx.h"++void printCFString(CFStringRef str, struct gc_arena *pGc)+{+	CFIndex bufferLength = CFStringGetLength(str) + 1;+	char *pBuffer = (char*)gc_malloc(sizeof(char) * bufferLength, false, pGc);+	CFStringGetCString(str, pBuffer, bufferLength, kCFStringEncodingUTF8);+	msg(M_DEBUG, "%s", pBuffer);+}++char* cfstringToCstr(CFStringRef str, struct gc_arena *pGc)+{+	CFIndex bufferLength = CFStringGetLength(str) + 1;+	char *pBuffer = (char*)gc_malloc(sizeof(char) * bufferLength, false, pGc);+	CFStringGetCString(str, pBuffer, bufferLength, kCFStringEncodingUTF8);+	return pBuffer;+}++void appendHexChar(CFMutableStringRef str, unsigned char halfByte)+{+	if (halfByte < 10)+		CFStringAppendFormat (str, NULL, CFSTR("%d"), halfByte);+	+	switch(halfByte)+	{+		case 10:+			CFStringAppendCString(str, "A", kCFStringEncodingUTF8);+			break;+		case 11:+			CFStringAppendCString(str, "B", kCFStringEncodingUTF8);+			break;+		case 12:+			CFStringAppendCString(str, "C", kCFStringEncodingUTF8);+			break;+		case 13:+			CFStringAppendCString(str, "D", kCFStringEncodingUTF8);+			break;+		case 14:+			CFStringAppendCString(str, "E", kCFStringEncodingUTF8);+			break;+		case 15:+			CFStringAppendCString(str, "F", kCFStringEncodingUTF8);+			break;+	}+}++CFStringRef createHexString(unsigned char *pData, int length)+{+	unsigned char byte, low, high;+	int i;+	CFMutableStringRef str = CFStringCreateMutable(NULL, 0);+	+	for(i = 0;i < length;i++)+	{+		byte = pData[i];+		low = byte & 0x0F;+		high = (byte >> 4);+		+		appendHexChar(str, high);+		appendHexChar(str, low);+		+		if (i != (length - 1))+			CFStringAppendCString(str, " ", kCFStringEncodingUTF8);+	}+	+	return str;+}++void printHex(unsigned char *pData, int length, struct gc_arena *pGc)+{+	CFStringRef hexStr = createHexString(pData, length);+	printCFString(hexStr, pGc);+	CFRelease(hexStr);+}\ No newline at end of filediff --git a/osx/common_osx.h b/osx/common_osx.hnew file mode 100644index 0000000..88b58c1--- /dev/null+++ b/osx/common_osx.h@@ -0,0 +1,36 @@+/*+ *  OpenVPN -- An application to securely tunnel IP networks+ *             over a single UDP port, with support for SSL/TLS-based+ *             session authentication and key exchange,+ *             packet encryption, packet authentication, and+ *             packet compression.+ *+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>+ *+ *  This program is free software; you can redistribute it and/or modify+ *  it under the terms of the GNU General Public License version 2+ *  as published by the Free Software Foundation.+ *+ *  This program is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ *  GNU General Public License for more details.+ *+ *  You should have received a copy of the GNU General Public License+ *  along with this program (see the file COPYING included with this+ *  distribution); if not, write to the Free Software Foundation, Inc.,+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ */++#ifndef __common_osx_h__+#define __common_osx_h__++#include "../buffer.h"+#include <CoreFoundation/CoreFoundation.h>++void printCFString(CFStringRef str, struct gc_arena *pGc);+char* cfstringToCstr(CFStringRef str, struct gc_arena *pGc);+CFStringRef createHexString(unsigned char *pData, int length);+void printHex(unsigned char *pData, int length, struct gc_arena *pGc);++#endif //__Common_osx_h__\ No newline at end of filediff --git a/osx/crypto_osx.c b/osx/crypto_osx.cnew file mode 100644index 0000000..a0b83bc--- /dev/null+++ b/osx/crypto_osx.c@@ -0,0 +1,76 @@+/*+ *  OpenVPN -- An application to securely tunnel IP networks+ *             over a single UDP port, with support for SSL/TLS-based+ *             session authentication and key exchange,+ *             packet encryption, packet authentication, and+ *             packet compression.+ *+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>+ *+ *  This program is free software; you can redistribute it and/or modify+ *  it under the terms of the GNU General Public License version 2+ *  as published by the Free Software Foundation.+ *+ *  This program is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ *  GNU General Public License for more details.+ *+ *  You should have received a copy of the GNU General Public License+ *  along with this program (see the file COPYING included with this+ *  distribution); if not, write to the Free Software Foundation, Inc.,+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ */++#include "crypto_osx.h"+#include <CommonCrypto/CommonDigest.h>++CSSM_DATA signData(SecIdentityRef identity, CSSM_DATA dataBuf)+{+    SecKeyRef privateKey;+    +    SecIdentityCopyPrivateKey(identity,  &privateKey); +    const CSSM_ACCESS_CREDENTIALS *pCredentials;+    SecKeyGetCredentials(privateKey, CSSM_ACL_AUTHORIZATION_SIGN, kSecCredentialTypeDefault, &pCredentials); +    +    CSSM_CSP_HANDLE cspHandle;+    SecKeyGetCSPHandle(privateKey, &cspHandle);+    +    const CSSM_KEY *pCssmKey;+    SecKeyGetCSSMKey (privateKey, &pCssmKey); +    +    CSSM_DATA signBuf;+    signBuf.Data = NULL;+    signBuf.Length = 0;+    +    if (!(pCssmKey->KeyHeader.KeyUsage & CSSM_KEYUSE_SIGN))+	{+		CFRelease(privateKey);+		return signBuf;+	}+    +    CSSM_CC_HANDLE cryptoContextHandle;+    CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA, pCredentials, pCssmKey, &cryptoContextHandle);+	+    CSSM_SignData(cryptoContextHandle, &dataBuf, 1, CSSM_ALGID_NONE, &signBuf);+    +    CSSM_DeleteContext(cryptoContextHandle);+    CFRelease(privateKey);+    return signBuf;+}++void freeSignature(SecIdentityRef identity, CSSM_DATA sigBuf)+{+    SecKeyRef privateKey;+    +    SecIdentityCopyPrivateKey(identity,  &privateKey); +    +    CSSM_CSP_HANDLE cspHandle;+    SecKeyGetCSPHandle(privateKey, &cspHandle);+	+	CSSM_API_MEMORY_FUNCS memFuncs;+	CSSM_GetAPIMemoryFunctions(cspHandle, &memFuncs);	+	+	memFuncs.free_func(sigBuf.Data, memFuncs.AllocRef);+}+diff --git a/osx/crypto_osx.h b/osx/crypto_osx.hnew file mode 100644index 0000000..b4fc8ee--- /dev/null+++ b/osx/crypto_osx.h@@ -0,0 +1,34 @@+/*+ *  OpenVPN -- An application to securely tunnel IP networks+ *             over a single UDP port, with support for SSL/TLS-based+ *             session authentication and key exchange,+ *             packet encryption, packet authentication, and+ *             packet compression.+ *+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>+ *+ *  This program is free software; you can redistribute it and/or modify+ *  it under the terms of the GNU General Public License version 2+ *  as published by the Free Software Foundation.+ *+ *  This program is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ *  GNU General Public License for more details.+ *+ *  You should have received a copy of the GNU General Public License+ *  along with this program (see the file COPYING included with this+ *  distribution); if not, write to the Free Software Foundation, Inc.,+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ */++#ifndef __crypto_osx_h__+#define __crypto_osx_h__++#include <CoreFoundation/CoreFoundation.h>+#include <Security/Security.h>++CSSM_DATA signData(SecIdentityRef identity, CSSM_DATA dataBuf);+void freeSignature(SecIdentityRef identity, CSSM_DATA sigBuf);++#endif //__crypto_osx_h__diff --git a/osx/keychain.c b/osx/keychain.cnew file mode 100644index 0000000..238dc9a--- /dev/null+++ b/osx/keychain.c@@ -0,0 +1,290 @@+/*+ * File: keyhchain.c.  Original was named cryptoapi.c.+ *+ * Copyright (c) 2004 Peter 'Luna' Runestig <peter@runestig.com>+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without modifi-+ * cation, are permitted provided that the following conditions are met:+ *+ *   o  Redistributions of source code must retain the above copyright notice,+ *      this list of conditions and the following disclaimer.+ *+ *   o  Redistributions in binary form must reproduce the above copyright no-+ *      tice, this list of conditions and the following disclaimer in the do-+ *      cumentation and/or other materials provided with the distribution.+ *+ *   o  The names of the contributors may not be used to endorse or promote+ *      products derived from this software without specific prior written+ *      permission.+ *+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LI-+ * ABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUEN-+ * TIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEV-+ * ER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABI-+ * LITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * This code has been modified from the original version by Brian Raderman + * <brian@irregularexpression.org>.  It was changed to work with the Mac OSX+ * Keychain services instead of the Microsoft Crypto API, which was its original+ * intent.+ */++#include "syshead.h"++#if defined(USE_CRYPTO) && defined(USE_SSL)++#include <openssl/ssl.h>+#include <openssl/err.h>+#include <stdio.h>+#include <ctype.h>+#include <assert.h>+#include <CoreFoundation/CoreFoundation.h>+#include <Security/Security.h>+#include "cert_data.h"+#include "common_osx.h"+#include "crypto_osx.h"++/* Size of an SSL signature: MD5+SHA1 */+#define SSL_SIG_LENGTH	36++/* try to funnel any Keychain/CSSM error messages to OpenSSL ERR_... */+#define ERR_LIB_KEYCHAIN (ERR_LIB_USER + 70)+#define KeychainErr(f)   err_put_apple_error((f), __FILE__, __LINE__)+#define KEYCHAIN_F_FIND_IDENTITY	    101+#define KEYCHAIN_F_CREATE_CERT_DATA_FROM_STRING   102+#define KEYCHAIN_F_SIGN_DATA			    103++static ERR_STRING_DATA KEYCHAIN_str_functs[] =	{+    { ERR_PACK(ERR_LIB_KEYCHAIN, 0, 0),				    "Mac OSX Keychain"},+    { ERR_PACK(0, KEYCHAIN_F_FIND_IDENTITY, 0),		    "findIdentity" },+    { ERR_PACK(0, KEYCHAIN_F_CREATE_CERT_DATA_FROM_STRING, 0),	    "createCertDataFromString" },+    { ERR_PACK(0, KEYCHAIN_F_SIGN_DATA, 0),    "signData" },+    { 0, NULL }+};++typedef struct _AppData+{+	SecIdentityRef identity;+	struct gc_arena arena;+} AppData;++static void err_put_apple_error(int func, const char *file, int line)+{+    static int init = 0;++    if (!init) {+		ERR_load_strings(ERR_LIB_KEYCHAIN, KEYCHAIN_str_functs);++	    ERR_STRING_DATA *esd = calloc(4, sizeof(ERR_STRING_DATA));+	    if (esd)+		{+			esd[0].error = 101;+			esd[0].string = "Unable to find identity in keychain (certificate + private key)";++			esd[1].error = 102;+			esd[1].string = "Unable to parse certificate description string";++			esd[2].error = 103;+			esd[2].string = "Unable to sign data with private key";++			ERR_load_strings(ERR_LIB_KEYCHAIN, esd);+		}+		+		init++;+    }+	+	ERR_PUT_error(ERR_LIB_KEYCHAIN, func, 0, file, line);+}++/* encrypt */+static int rsa_pub_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)+{+    /* I haven't been able to trigger this one, but I want to know if it happens... */+    assert(0);+    return 0;+}++/* verify arbitrary data */+static int rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)+{+    /* I haven't been able to trigger this one, but I want to know if it happens... */+    assert(0);+    return 0;+}++/* sign arbitrary data */+static int rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)+{+	AppData *pData = (AppData*) rsa->meth->app_data;+	int length;+	+    if (!pData->identity) +	{+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER);+		return 0;+    }+	+    if (padding != RSA_PKCS1_PADDING) +	{+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);+		return 0;+    }+	+	CSSM_DATA fromData;+	fromData.Data = (uint8*)from;+	fromData.Length = flen;+	CSSM_DATA sigBuf = signData(pData->identity, fromData);+	length = sigBuf.Length;+	memcpy(to, sigBuf.Data, sigBuf.Length);+	freeSignature(pData->identity, sigBuf);+    return length;+}++/* decrypt */+static int rsa_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)+{+    /* I haven't been able to trigger this one, but I want to know if it happens... */+    assert(0);+    return 0;+}++/* called at RSA_new */+static int init(RSA *rsa)+{+    return 0;+}++/* called at RSA_free */+static int finish(RSA *rsa)+{+	AppData *pData = (AppData*) rsa->meth->app_data;++    if (!pData->identity)+		return 0;+	+	CFRelease(pData->identity);+	gc_free(&(pData->arena));+    rsa->meth = NULL;+    return 1;+}++int SSL_CTX_use_Keychain_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)+{+    X509 *cert = NULL;+    RSA *rsa = NULL, *pub_rsa;+    RSA_METHOD *my_rsa_method; +	SecIdentityRef identity = NULL;+	struct gc_arena arena = gc_new();+	+	initCertData();+	+	my_rsa_method = (RSA_METHOD*)gc_malloc(sizeof(RSA_METHOD), true, &arena);+	+    if (my_rsa_method == NULL) +	{+		SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);+		goto err;+    }++	AppData *pData = (AppData*)gc_malloc(sizeof(AppData), false, &arena);+	pData->arena = arena;+	+	CertDataRef pCertDataTemplate = createCertDataFromString(cert_prop);+	identity = findIdentity(pCertDataTemplate);+	destroyCertData(pCertDataTemplate);+	freeCertData();+	+ 	if (!identity) +	{+	    KeychainErr(KEYCHAIN_F_FIND_IDENTITY);+	    goto err;+	}+	+	pData->identity = identity;+	+    CSSM_DATA cssmCertData;+	SecCertificateRef certificate;++    SecIdentityCopyCertificate(identity, &certificate);+	SecCertificateGetData (certificate, &cssmCertData);+	CFRelease(certificate);++    /* cert_context->pbCertEncoded is the cert X509 DER encoded. */+    cert = d2i_X509(NULL, (unsigned char **) &cssmCertData.Data, cssmCertData.Length);+    if (cert == NULL) +	{+		SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB);+		goto err;+    }++    my_rsa_method->name = "Mac OSX Keychain RSA Method";+    my_rsa_method->rsa_pub_enc = rsa_pub_enc;+    my_rsa_method->rsa_pub_dec = rsa_pub_dec;+    my_rsa_method->rsa_priv_enc = rsa_priv_enc;+    my_rsa_method->rsa_priv_dec = rsa_priv_dec;+    /* my_rsa_method->init = init; */+    my_rsa_method->finish = finish;+    my_rsa_method->flags = RSA_METHOD_FLAG_NO_CHECK;+    my_rsa_method->app_data = (char *) pData;++    rsa = RSA_new();+    if (rsa == NULL)+	{+		SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);+		goto err;+    }++    /* cert->cert_info->key->pkey is NULL until we call SSL_CTX_use_certificate(),+     * so we do it here then...  */+    if (!SSL_CTX_use_certificate(ssl_ctx, cert))+		goto err;+	+    /* the public key */+    pub_rsa = cert->cert_info->key->pkey->pkey.rsa;+    /* SSL_CTX_use_certificate() increased the reference count in 'cert', so+     * we decrease it here with X509_free(), or it will never be cleaned up. */+    X509_free(cert);+    cert = NULL;++    /* I'm not sure about what we have to fill in in the RSA, trying out stuff... */+    /* rsa->n indicates the key size */+    rsa->n = BN_dup(pub_rsa->n);+    rsa->flags |= RSA_FLAG_EXT_PKEY;+    if (!RSA_set_method(rsa, my_rsa_method))+		goto err;++    if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa))+		goto err;+	+    /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so+     * we decrease it here with RSA_free(), or it will never be cleaned up. */+    RSA_free(rsa);+    return 1;++  err:	+    if (cert)+		X509_free(cert);+	+    if (rsa)+	{+		RSA_free(rsa);+	}+    else +	{		+		if (identity) +			CFRelease(identity);+    }+	+	freeCertData();+	gc_free(&arena);++    return 0;+}++#endif				/*  defined(USE_CRYPTO) && defined(USE_SSL) */diff --git a/osx/keychain.h b/osx/keychain.hnew file mode 100644index 0000000..ba6e1b1--- /dev/null+++ b/osx/keychain.h@@ -0,0 +1,7 @@+#ifndef _OSXKEYCHAIN_H_+#define _OSXKEYCHAIN_H_++int SSL_CTX_use_Keychain_certificate(SSL_CTX *ssl_ctx, const char *cert_prop);+++#endif /* !_OSXKEYCHAIN_H_ */diff --git a/ssl.c b/ssl.cindex a5a3014..a27a9d1 100644--- a/ssl.c+++ b/ssl.c@@ -1749,6 +1749,16 @@ init_ssl (const struct options *options) 	}       else #endif+#ifdef KEYCHAINCERT+	if (options->keychain_cert)+	{+	  /* Load Certificate and Private Key */+	  if (!SSL_CTX_use_Keychain_certificate (ctx, options->keychain_cert))+		  msg (M_SSLERR, "Cannot load certificate \"%s\" from Mac OSX Keychain",+			   options->keychain_cert);+	}+	else+#endif 	{ 	  /* Load Certificate */ 	  if (options->cert_file)-- 1.7.2.3