Created
October 23, 2011 17:17
-
-
Save rbsgn/1307595 to your computer and use it in GitHub Desktop.
Openvpn patch that allows loading of VPN certificate from Mac OS X keychain. Originally created by Brian Raderman (<brian@irregularexpression.org>).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From a53d19a34be1301c4f4c37e9767ca698885b84fb Mon Sep 17 00:00:00 2001 | |
From: Roman Busyghin <nskboy@gmail.com> | |
Date: Sun, 23 Oct 2011 20:37:52 +0400 | |
Subject: [PATCH] Ability to lookup OS X keychain to load VPN certificate | |
This patch is created by Brian Raderman <brian@irregularexpression.org> and modified a bit in order to be compiled with make instead of Xcode | |
--- | |
Makefile.am | 9 + | |
cert_data.c | 631 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
cert_data.h | 44 ++++ | |
common_osx.c | 98 +++++++++ | |
common_osx.h | 35 ++++ | |
configure.ac | 7 + | |
crypto_osx.c | 75 +++++++ | |
crypto_osx.h | 34 +++ | |
keychain.c | 274 +++++++++++++++++++++++++ | |
keychain.h | 6 + | |
openvpn.8 | 57 ++++++ | |
options.c | 31 +++ | |
options.h | 2 + | |
ssl.c | 4 + | |
14 files changed, 1307 insertions(+), 0 deletions(-) | |
create mode 100644 cert_data.c | |
create mode 100644 cert_data.h | |
create mode 100644 common_osx.c | |
create mode 100644 common_osx.h | |
create mode 100644 crypto_osx.c | |
create mode 100644 crypto_osx.h | |
create mode 100644 keychain.c | |
create mode 100644 keychain.h | |
diff --git a/Makefile.am b/Makefile.am | |
index 3228798..0e1443b 100644 | |
--- a/Makefile.am | |
+++ b/Makefile.am | |
@@ -145,6 +145,15 @@ openvpn_SOURCES = \ | |
tun.c tun.h \ | |
win32.h win32.c \ | |
cryptoapi.h cryptoapi.c | |
+ | |
+if APPLE | |
+openvpn_SOURCES += \ | |
+ cert_data.c cert_data.h \ | |
+ common_osx.c common_osx.h \ | |
+ crypto_osx.c crypto_osx.h \ | |
+ keychain.c keychain.h | |
+ | |
+endif | |
nodist_openvpn_SOURCES = configure.h | |
options.$(OBJEXT): configure.h | |
diff --git a/cert_data.c b/cert_data.c | |
new file mode 100644 | |
index 0000000..c357640 | |
--- /dev/null | |
+++ b/cert_data.c | |
@@ -0,0 +1,631 @@ | |
+/* | |
+ * 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 <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; | |
+ | |
+ | |
+CertNameRef createCertName() | |
+{ | |
+ CertNameRef pCertName = (CertNameRef)malloc(sizeof(CertName)); | |
+ 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); | |
+ free(pCertName); | |
+} | |
+ | |
+ | |
+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)malloc(sizeof(CertData)); | |
+ 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)malloc(sizeof(DescData)); | |
+ | |
+ 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); | |
+ | |
+ free(pData); | |
+} | |
+ | |
+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)malloc(sizeof(CertData)); | |
+ 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); | |
+ | |
+ free(pCertData); | |
+} | |
+ | |
+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; | |
+ printf("%s = ", label); | |
+ printCFString(str); | |
+ printf("\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) | |
+{ | |
+ printf("*** Subject ***\n"); | |
+ if (pCertData->subject) | |
+ CFArrayApplyFunction(pCertData->subject, CFRangeMake(0, CFArrayGetCount(pCertData->subject)), arrayPrintCertName, NULL); | |
+ | |
+ printf("*** Issuer ***\n"); | |
+ if (pCertData->issuer) | |
+ CFArrayApplyFunction(pCertData->issuer, CFRangeMake(0, CFArrayGetCount(pCertData->issuer)), arrayPrintCertName, NULL); | |
+ | |
+ printf("*** Fingerprints ***\n"); | |
+ | |
+ printf("SHA1 = "); | |
+ if (pCertData->sha1) | |
+ printCFString(pCertData->sha1); | |
+ | |
+ printf("\n"); | |
+ | |
+ printf("MD5 = "); | |
+ if (pCertData->md5) | |
+ printCFString(pCertData->md5); | |
+ | |
+ printf("\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; | |
+} | |
\ No newline at end of file | |
diff --git a/cert_data.h b/cert_data.h | |
new file mode 100644 | |
index 0000000..b3092de | |
--- /dev/null | |
+++ b/cert_data.h | |
@@ -0,0 +1,44 @@ | |
+/* | |
+ * 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); | |
+ | |
+#endif | |
\ No newline at end of file | |
diff --git a/common_osx.c b/common_osx.c | |
new file mode 100644 | |
index 0000000..fd39f50 | |
--- /dev/null | |
+++ b/common_osx.c | |
@@ -0,0 +1,98 @@ | |
+/* | |
+ * 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 "common_osx.h" | |
+ | |
+void printCFString(CFStringRef str) | |
+{ | |
+ CFIndex bufferLength = CFStringGetLength(str) + 1; | |
+ char *pBuffer = (char*)malloc(sizeof(char) * bufferLength); | |
+ CFStringGetCString(str, pBuffer, bufferLength, kCFStringEncodingUTF8); | |
+ printf("%s", pBuffer); | |
+ free(pBuffer); | |
+} | |
+ | |
+char* cfstringToCstr(CFStringRef str) | |
+{ | |
+ CFIndex bufferLength = CFStringGetLength(str) + 1; | |
+ char *pBuffer = (char*)malloc(sizeof(char) * bufferLength); | |
+ 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) | |
+{ | |
+ CFStringRef hexStr = createHexString(pData, length); | |
+ printCFString(hexStr); | |
+ CFRelease(hexStr); | |
+} | |
\ No newline at end of file | |
diff --git a/common_osx.h b/common_osx.h | |
new file mode 100644 | |
index 0000000..7cf6582 | |
--- /dev/null | |
+++ b/common_osx.h | |
@@ -0,0 +1,35 @@ | |
+/* | |
+ * 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 <CoreFoundation/CoreFoundation.h> | |
+ | |
+void printCFString(CFStringRef str); | |
+char* cfstringToCstr(CFStringRef str); | |
+CFStringRef createHexString(unsigned char *pData, int length); | |
+void printHex(unsigned char *pData, int length); | |
+ | |
+#endif //__Common_osx_h__ | |
\ No newline at end of file | |
diff --git a/configure.ac b/configure.ac | |
index a6b3019..114b6f2 100644 | |
--- a/configure.ac | |
+++ b/configure.ac | |
@@ -42,7 +42,11 @@ AC_ARG_WITH(cygwin-native, | |
WIN32="no" | |
CYGWIN="no" | |
+APPLE="no" | |
case "${host}" in | |
+ *darwin*) | |
+ APPLE="yes" | |
+ ;; | |
*-mingw*) | |
WIN32="yes" | |
cross_compiling="yes" | |
@@ -335,6 +339,8 @@ 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" | |
+ CFLAGS="$CFLAGS -D__APPLE__" | |
+ LDFLAGS="$LDFLAGS -framework CoreFoundation -framework Security" | |
;; | |
*-mingw*) | |
AC_DEFINE(TARGET_WIN32, 1, [Are we running WIN32?]) | |
@@ -962,6 +968,7 @@ AC_SUBST(TAP_WIN32_MIN_MINOR) | |
win32datadir="\${datadir}/${PACKAGE}-win32" | |
AC_SUBST(win32datadir) | |
AM_CONDITIONAL(WIN32, test "${WIN32}" = "yes") | |
+AM_CONDITIONAL(APPLE, test "${APPLE}" = "yes") | |
# workaround for <autoconf-2.60 | |
if test -z "${docdir}"; then | |
diff --git a/crypto_osx.c b/crypto_osx.c | |
new file mode 100644 | |
index 0000000..3d137e4 | |
--- /dev/null | |
+++ b/crypto_osx.c | |
@@ -0,0 +1,75 @@ | |
+/* | |
+ * 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/crypto_osx.h b/crypto_osx.h | |
new file mode 100644 | |
index 0000000..858e130 | |
--- /dev/null | |
+++ b/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__ | |
\ No newline at end of file | |
diff --git a/keychain.c b/keychain.c | |
new file mode 100644 | |
index 0000000..d7c4d52 | |
--- /dev/null | |
+++ b/keychain.c | |
@@ -0,0 +1,274 @@ | |
+/* | |
+ * 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(__APPLE__) && 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 OS X 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 } | |
+}; | |
+ | |
+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) | |
+{ | |
+ SecIdentityRef identity = (SecIdentityRef) rsa->meth->app_data; | |
+ int length; | |
+ | |
+ if (!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(identity, fromData); | |
+ length = sigBuf.Length; | |
+ memcpy(to, sigBuf.Data, sigBuf.Length); | |
+ freeSignature(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) | |
+{ | |
+ SecIdentityRef identity = (SecIdentityRef) rsa->meth->app_data; | |
+ | |
+ if (!identity) | |
+ return 0; | |
+ | |
+ CFRelease(identity); | |
+ free((char *) rsa->meth); | |
+ 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 = calloc(1, sizeof(RSA_METHOD)); | |
+ SecIdentityRef identity = NULL; | |
+ | |
+ if (my_rsa_method == NULL) | |
+ { | |
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); | |
+ goto err; | |
+ } | |
+ | |
+ CertDataRef pCertDataTemplate = createCertDataFromString(cert_prop); | |
+ identity = findIdentity(pCertDataTemplate); | |
+ destroyCertData(pCertDataTemplate); | |
+ | |
+ if (!identity) | |
+ { | |
+ KeychainErr(KEYCHAIN_F_FIND_IDENTITY); | |
+ goto err; | |
+ } | |
+ | |
+ 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 OS X 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 *) identity; | |
+ | |
+ 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 (my_rsa_method) | |
+ free(my_rsa_method); | |
+ | |
+ if (identity) | |
+ CFRelease(identity); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+#else | |
+static void dummy1 (void) {} | |
+#endif /* __APPLE__ */ | |
\ No newline at end of file | |
diff --git a/keychain.h b/keychain.h | |
new file mode 100644 | |
index 0000000..84a1936 | |
--- /dev/null | |
+++ b/keychain.h | |
@@ -0,0 +1,6 @@ | |
+#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/openvpn.8 b/openvpn.8 | |
index b8594e1..6fed3ff 100644 | |
--- a/openvpn.8 | |
+++ b/openvpn.8 | |
@@ -4116,6 +4116,63 @@ Mode is encoded as hex number, and can be a mask one of the following: | |
.br | |
.\"********************************************************* | |
.TP | |
+.B --keychaincert select-string | |
+Load the certificate and private key from the | |
+Mac OS X Keychain (Mac OS X 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 any | |
+kind 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 OS X 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 \-\-cryptoapicert select-string | |
Load the certificate and private key from the | |
Windows Certificate System Store (Windows/OpenSSL Only). | |
diff --git a/options.c b/options.c | |
index 39e7a57..0bcf1bd 100644 | |
--- a/options.c | |
+++ b/options.c | |
@@ -570,6 +570,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 __APPLE__ | |
+ "--keychaincert select-string : Load the certificate and private key from the\n" | |
+ " Mac OS X 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" | |
@@ -1579,6 +1583,9 @@ show_settings (const struct options *o) | |
#ifdef ENABLE_CRYPTOAPI | |
SHOW_STR (cryptoapi_cert); | |
#endif | |
+#ifdef __APPLE__ | |
+ SHOW_STR (keychain_cert); | |
+#endif | |
SHOW_STR (cipher_list); | |
SHOW_STR (tls_verify); | |
SHOW_STR (tls_export_cert); | |
@@ -2259,6 +2266,9 @@ options_postprocess_verify_ce (const struct options *options, const struct conne | |
#ifdef ENABLE_CRYPTOAPI | |
if (options->cryptoapi_cert) | |
msg(M_USAGE, "Parameter --cryptoapicert cannot be used when --pkcs11-provider is also specified."); | |
+#elif defined(__APPLE__) | |
+ if (options->keychain_cert) | |
+ msg(M_USAGE, "Parameter --keychaincert cannot be used when --pkcs11-provider is also specified."); | |
#endif | |
} | |
else | |
@@ -2276,6 +2286,21 @@ options_postprocess_verify_ce (const struct options *options, const struct conne | |
msg(M_USAGE, "Parameter --pkcs12 cannot be used when --cryptoapicert is also specified."); | |
} | |
else | |
+#elif defined(__APPLE__) | |
+ 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) | |
{ | |
@@ -6250,6 +6275,12 @@ add_option (struct options *options, | |
VERIFY_PERMISSION (OPT_P_GENERAL); | |
options->cryptoapi_cert = p[1]; | |
} | |
+#elif defined(__APPLE__) | |
+ 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]) | |
{ | |
diff --git a/options.h b/options.h | |
index abdddba..2d2181b 100644 | |
--- a/options.h | |
+++ b/options.h | |
@@ -557,6 +557,8 @@ struct options | |
#ifdef ENABLE_CRYPTOAPI | |
const char *cryptoapi_cert; | |
+#elif defined(__APPLE__) | |
+ const char *keychain_cert; | |
#endif | |
/* data channel key exchange method */ | |
diff --git a/ssl.c b/ssl.c | |
index 30f6eb2..98d72de 100644 | |
--- a/ssl.c | |
+++ b/ssl.c | |
@@ -66,6 +66,10 @@ | |
#include "cryptoapi.h" | |
#endif | |
+#ifdef __APPLE__ | |
+#include "keychain.h" | |
+#endif | |
+ | |
#include "memdbg.h" | |
#ifndef ENABLE_OCC | |
-- | |
1.7.6 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Where is the original code version this DIFF is compared with ? thank you