diff options
| -rw-r--r-- | main/openvpn/.gitignore | 1 | ||||
| -rw-r--r-- | main/openvpn/contrib/keychain-mcd/cert_data.c | 733 | ||||
| -rw-r--r-- | main/openvpn/contrib/keychain-mcd/cert_data.h | 46 | ||||
| -rw-r--r-- | main/openvpn/contrib/keychain-mcd/common_osx.c | 94 | ||||
| -rw-r--r-- | main/openvpn/contrib/keychain-mcd/common_osx.h | 36 | ||||
| -rw-r--r-- | main/openvpn/contrib/keychain-mcd/crypto_osx.c | 75 | ||||
| -rw-r--r-- | main/openvpn/contrib/keychain-mcd/crypto_osx.h | 44 | ||||
| -rw-r--r-- | main/openvpn/contrib/keychain-mcd/keychain-mcd.8 | 149 | ||||
| -rw-r--r-- | main/openvpn/contrib/keychain-mcd/main.c | 244 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/buffer.c | 13 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/buffer.h | 1 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/error.c | 2 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/init.c | 24 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/manage.c | 145 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/manage.h | 5 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/mtu.h | 6 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/mudp.c | 9 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/options.c | 19 | ||||
| -rw-r--r-- | main/openvpn/src/openvpn/ssl.c | 26 | 
19 files changed, 1628 insertions, 44 deletions
| diff --git a/main/openvpn/.gitignore b/main/openvpn/.gitignore index 06ff7c66..1d5191f3 100644 --- a/main/openvpn/.gitignore +++ b/main/openvpn/.gitignore @@ -19,7 +19,6 @@ Debug  Win32-Output  .deps  .libs -Makefile  Makefile.in  aclocal.m4  autodefs.h diff --git a/main/openvpn/contrib/keychain-mcd/cert_data.c b/main/openvpn/contrib/keychain-mcd/cert_data.c new file mode 100644 index 00000000..f2b33edc --- /dev/null +++ b/main/openvpn/contrib/keychain-mcd/cert_data.c @@ -0,0 +1,733 @@ +/* + *  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> + *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com> + * + *  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 "cert_data.h" +#include <CommonCrypto/CommonDigest.h> +#include <openssl/ssl.h> + +#include "common_osx.h" +#include "crypto_osx.h" +#include <err.h> + +CFStringRef kCertDataSubjectName = CFSTR("subject"), +            kCertDataIssuerName = CFSTR("issuer"), +            kCertDataSha1Name = CFSTR("SHA1"), +            kCertDataMd5Name = CFSTR("MD5"), +            kCertDataSerialName = CFSTR("serial"), +            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"); +CFStringRef kStringSpace = CFSTR(" "), +            kStringEmpty = CFSTR(""); + +typedef struct _CertName +{ +  CFArrayRef countryName, organization, organizationalUnit, commonName, description, emailAddress, +             stateName, localityName; +} CertName, *CertNameRef; + +typedef struct _DescData +{ +  CFStringRef name, value; +} DescData, *DescDataRef; + +void destroyDescData(DescDataRef pData); + +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); +  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); +  free(pCertName); +} + +bool CFStringRefCmpCString(CFStringRef cfstr, const char *str) +{ +  CFStringRef tmp = CFStringCreateWithCStringNoCopy(NULL, str, kCFStringEncodingUTF8, kCFAllocatorNull); +  CFComparisonResult cresult = CFStringCompare(cfstr, tmp, 0); +  bool result = cresult == kCFCompareEqualTo; +  CFRelease(tmp); +  return result; +} + +CFDateRef GetDateFieldFromCertificate(SecCertificateRef certificate, CFTypeRef oid) +{ +  const void *keys[] = { oid }; +  CFDictionaryRef dict = NULL; +  CFErrorRef error; +  CFDateRef date = NULL; + +  CFArrayRef keySelection = CFArrayCreate(NULL, keys , sizeof(keys)/sizeof(keys[0]), &kCFTypeArrayCallBacks); +  dict = SecCertificateCopyValues(certificate, keySelection, &error); +  if (dict == NULL) +    { +      printErrorMsg("GetDateFieldFromCertificate: SecCertificateCopyValues", error); +      goto release_ks; +    } +  CFDictionaryRef vals = dict ? CFDictionaryGetValue(dict, oid) : NULL; +  CFNumberRef vals2 = vals ? CFDictionaryGetValue(vals, kSecPropertyKeyValue) : NULL; +  if (vals2 == NULL) +    goto release_dict; + +  CFAbsoluteTime validityNotBefore; +  if (CFNumberGetValue(vals2, kCFNumberDoubleType, &validityNotBefore)) +    date = CFDateCreate(kCFAllocatorDefault,validityNotBefore); + +release_dict: +  CFRelease(dict); +release_ks: +  CFRelease(keySelection); +  return date; +} + +CFArrayRef GetFieldsFromCertificate(SecCertificateRef certificate, CFTypeRef oid) +{ +  CFMutableArrayRef fields = CFArrayCreateMutable(NULL, 0, NULL); +  CertNameRef pCertName = createCertName(); +  const void* keys[] = { oid, }; +  CFDictionaryRef dict; +  CFErrorRef error; + +  CFArrayRef keySelection = CFArrayCreate(NULL, keys , 1, NULL); + +  dict = SecCertificateCopyValues(certificate, keySelection, &error); +  if (dict == NULL) { +      printErrorMsg("GetFieldsFromCertificate: SecCertificateCopyValues", error); +      CFRelease(keySelection); +      CFRelease(fields); +      return NULL; +  } +  CFDictionaryRef vals = CFDictionaryGetValue(dict, oid); +  CFArrayRef vals2 = vals ? CFDictionaryGetValue(vals, kSecPropertyKeyValue) : NULL; +  if (vals2) +    { +      for(int i = 0; i < CFArrayGetCount(vals2); i++) { +        CFDictionaryRef subDict = CFArrayGetValueAtIndex(vals2, i); +        CFStringRef label = CFDictionaryGetValue(subDict, kSecPropertyKeyLabel); +        CFStringRef value = CFDictionaryGetValue(subDict, kSecPropertyKeyValue); + +        if (CFStringCompare(label, kSecOIDEmailAddress, 0) == kCFCompareEqualTo) +          CFArrayAppendValue((CFMutableArrayRef)pCertName->emailAddress, value); +        else if (CFStringCompare(label, kSecOIDCountryName, 0) == kCFCompareEqualTo) +          CFArrayAppendValue((CFMutableArrayRef)pCertName->countryName, value); +        else if (CFStringCompare(label, kSecOIDOrganizationName, 0) == kCFCompareEqualTo) +          CFArrayAppendValue((CFMutableArrayRef)pCertName->organization, value); +        else if (CFStringCompare(label, kSecOIDOrganizationalUnitName, 0) == kCFCompareEqualTo) +          CFArrayAppendValue((CFMutableArrayRef)pCertName->organizationalUnit, value); +        else if (CFStringCompare(label, kSecOIDCommonName, 0) == kCFCompareEqualTo) +          CFArrayAppendValue((CFMutableArrayRef)pCertName->commonName, value); +        else if (CFStringCompare(label, kSecOIDDescription, 0) == kCFCompareEqualTo) +          CFArrayAppendValue((CFMutableArrayRef)pCertName->description, value); +        else if (CFStringCompare(label, kSecOIDStateProvinceName, 0) == kCFCompareEqualTo) +          CFArrayAppendValue((CFMutableArrayRef)pCertName->stateName, value); +        else if (CFStringCompare(label, kSecOIDLocalityName, 0) == kCFCompareEqualTo) +          CFArrayAppendValue((CFMutableArrayRef)pCertName->localityName, value); +      } +      CFArrayAppendValue(fields, pCertName); +    } + +  CFRelease(dict); +  CFRelease(keySelection); +  return fields; +} + +CertDataRef createCertDataFromCertificate(SecCertificateRef certificate) +{ +  CertDataRef pCertData = (CertDataRef)malloc(sizeof(CertData)); +  pCertData->subject = GetFieldsFromCertificate(certificate, kSecOIDX509V1SubjectName); +  pCertData->issuer = GetFieldsFromCertificate(certificate, kSecOIDX509V1IssuerName); + +  CFDataRef data = SecCertificateCopyData(certificate); +  if (data == NULL) +    { +      warnx("SecCertificateCopyData() returned NULL"); +      destroyCertData(pCertData); +      return NULL; +    } + +  unsigned char sha1[CC_SHA1_DIGEST_LENGTH]; +  CC_SHA1(CFDataGetBytePtr(data), CFDataGetLength(data), sha1); +  pCertData->sha1 = createHexString(sha1, CC_SHA1_DIGEST_LENGTH); + +  unsigned char md5[CC_MD5_DIGEST_LENGTH]; +  CC_MD5(CFDataGetBytePtr(data), CFDataGetLength(data), md5); +  pCertData->md5 = createHexString((unsigned char*)md5, CC_MD5_DIGEST_LENGTH); + +  CFDataRef serial = SecCertificateCopySerialNumber(certificate, NULL); +  pCertData->serial = createHexString((unsigned char *)CFDataGetBytePtr(serial), CFDataGetLength(serial)); +  CFRelease(serial); + +  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); + +#if 0 +  fprintf(stderr, "name = '%s', value = '%s'\n", +      CFStringGetCStringPtr(pRetVal->name, kCFStringEncodingUTF8), +      CFStringGetCStringPtr(pRetVal->value, kCFStringEncodingUTF8)); +#endif +  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); +} + + +int parseNameComponent(CFStringRef dn, CFStringRef *pName, CFStringRef *pValue) +{ +  CFArrayRef nameStrings = CFStringCreateArrayBySeparatingStrings(NULL, dn, kCertNameEquals); + +  *pName = *pValue = NULL; + +  if (CFArrayGetCount(nameStrings) != 2) +    return 0; + +  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); +  return 1; +} + +int tryAppendSingleCertField(CertNameRef pCertName, CFArrayRef where, CFStringRef key, +    CFStringRef name, CFStringRef value) +{ +  if (CFStringCompareWithOptions(name, key, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive) +      == kCFCompareEqualTo) { +    CFArrayAppendValue((CFMutableArrayRef)where, value); +    return 1; +  } +  return 0; +} + +int appendCertField(CertNameRef pCert, CFStringRef name, CFStringRef value) +{ +  struct { +    CFArrayRef field; +    CFStringRef key; +  } fields[] = { +    { pCert->organization, kCertNameOrganization}, +    { pCert->organizationalUnit, kCertNameOrganizationalUnit}, +    { pCert->countryName, kCertNameCountry}, +    { pCert->localityName, kCertNameLocality}, +    { pCert->stateName, kCertNameState}, +    { pCert->commonName, kCertNameCommonName}, +    { pCert->emailAddress, kCertNameEmail}, +  }; +  int i; +  int ret = 0; + +  for (i=0; i<sizeof(fields)/sizeof(fields[0]); i++) +    ret += tryAppendSingleCertField(pCert, fields[i].field, fields[i].key, name, value); +  return ret; +} + +int parseCertName(CFStringRef nameDesc, CFMutableArrayRef names) +{ +  CFArrayRef nameStrings = CFStringCreateArrayBySeparatingStrings(NULL, nameDesc, kCertNameFwdSlash); +  int count = CFArrayGetCount(nameStrings); +  int i; +  int ret = 1; + +  CertNameRef pCertName = createCertName(); + +  for(i = 0;i < count;i++) +    { +      CFMutableStringRef dn = CFStringCreateMutableCopy(NULL, 0, CFArrayGetValueAtIndex(nameStrings, i)); +      CFStringTrimWhitespace(dn); + +      CFStringRef name, value; + +      if (!parseNameComponent(dn, &name, &value)) +        ret = 0; + +      if (!name || !value) +        { +          if (name) +            CFRelease(name); + +          if (value) +            CFRelease(value); +          if (name && !value) +            ret = 0; + +          CFRelease(dn); +          continue; +        } + +      if (!appendCertField(pCertName, name, value)) +        ret = 0; +      CFRelease(name); +      CFRelease(value); +      CFRelease(dn); +    } + +  CFArrayAppendValue(names, pCertName); +  CFRelease(nameStrings); +  return ret; +} + +int arrayParseDescDataPair(const void *val, void *context) +{ +  DescDataRef pDescData = (DescDataRef)val; +  CertDataRef pCertData = (CertDataRef)context; +  int ret = 1; + +  if (!pDescData->name || !pDescData->value) +    return 0; + +  if (CFStringCompareWithOptions(pDescData->name, kCertDataSubjectName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo) +    ret = parseCertName(pDescData->value, (CFMutableArrayRef)pCertData->subject); +  else if (CFStringCompareWithOptions(pDescData->name, kCertDataIssuerName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo) +    ret = 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); +  else if (CFStringCompareWithOptions(pDescData->name, kCertDataSerialName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo) +    pCertData->serial = CFRetain(pDescData->value); +  else +    return 0; + +  return ret; +} + +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; +  pCertData->serial = NULL; + +  CFArrayRef pairs = createDescDataPairs(description); +  for (int i=0; i<CFArrayGetCount(pairs); i++) +    if (!arrayParseDescDataPair(CFArrayGetValueAtIndex(pairs, i), pCertData)) { +      arrayDestroyDescData(pCertData, NULL); +      CFArrayApplyFunction(pairs, CFRangeMake(0, CFArrayGetCount(pairs)), arrayDestroyDescData, NULL); +      CFRelease(pairs); +      return 0; +    } + +  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); + +  if (pCertData->serial) +    CFRelease(pCertData->serial); + +  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; + +  if (!hexStringMatchesTemplate(pCertData->serial, pTemplate->serial)) +    return false; + +  return true; +} + +bool certExpired(SecCertificateRef certificate) +{ +  bool result; +  CFDateRef notAfter = GetDateFieldFromCertificate(certificate, kSecOIDX509V1ValidityNotAfter); +  CFDateRef notBefore = GetDateFieldFromCertificate(certificate, kSecOIDX509V1ValidityNotBefore); +  CFDateRef now = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()); + +  if (!notAfter || !notBefore || !now) +    { +      warnx("GetDateFieldFromCertificate() returned NULL"); +      result = true; +    } +  else +    { +      if (CFDateCompare(notBefore, now, NULL) != kCFCompareLessThan || +          CFDateCompare(now, notAfter, NULL) != kCFCompareLessThan) +        result = true; +      else +        result = false; +    } + +  CFRelease(notAfter); +  CFRelease(notBefore); +  CFRelease(now); +  return result; +} + +SecIdentityRef findIdentity(CertDataRef pCertDataTemplate) +{ +  const void *keys[] = { +    kSecClass, +    kSecReturnRef, +    kSecMatchLimit +  }; +  const void *values[] = { +    kSecClassIdentity, +    kCFBooleanTrue, +    kSecMatchLimitAll +  }; +  CFArrayRef result = NULL; + +  CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, +      sizeof(keys) / sizeof(*keys), +      &kCFTypeDictionaryKeyCallBacks, +      &kCFTypeDictionaryValueCallBacks); +  OSStatus status = SecItemCopyMatching(query, (CFTypeRef*)&result); +  CFRelease(query); +  if (status != noErr) +    { +      warnx ("No identities in keychain found"); +      return NULL; +    } + +  SecIdentityRef bestIdentity = NULL; +  CFDateRef bestNotBeforeDate = NULL; + +  for (int i=0; i<CFArrayGetCount(result); i++) +    { +      SecIdentityRef identity = (SecIdentityRef)CFArrayGetValueAtIndex(result, i); +      if (identity == NULL) +        { +          warnx ("identity == NULL"); +          continue; +        } + +      SecCertificateRef certificate = NULL; +      SecIdentityCopyCertificate (identity, &certificate); +      if (certificate == NULL) +        { +          warnx ("SecIdentityCopyCertificate() returned NULL"); +          continue; +        } + +      CertDataRef pCertData2 = createCertDataFromCertificate(certificate); +      if (pCertData2 == NULL) +        { +          warnx ("createCertDataFromCertificate() returned NULL"); +          goto release_cert; +        } +      bool bMatches = certDataMatchesTemplate(pCertData2, pCertDataTemplate); +      bool bExpired = certExpired(certificate); +      destroyCertData(pCertData2); + +      if (bMatches && !bExpired) +        { +          CFDateRef notBeforeDate = GetDateFieldFromCertificate(certificate, kSecOIDX509V1ValidityNotBefore); +          if (!notBeforeDate) +            { +              warnx ("GetDateFieldFromCertificate() returned NULL"); +              goto release_cert; +            } +          if (bestIdentity == NULL) +            { +              CFRetain(identity); +              bestIdentity = identity; + +              bestNotBeforeDate = notBeforeDate; +              CFRetain(notBeforeDate); +            } +          else if (CFDateCompare(bestNotBeforeDate, notBeforeDate, NULL) == kCFCompareLessThan) +            { +              CFRelease(bestIdentity); +              CFRetain(identity); +              bestIdentity = identity; + +              bestNotBeforeDate = notBeforeDate; +              CFRetain(notBeforeDate); +            } +          CFRelease(notBeforeDate); +        } +    release_cert: +      CFRelease(certificate); +    } +  CFRelease(result); + +  return bestIdentity; +} diff --git a/main/openvpn/contrib/keychain-mcd/cert_data.h b/main/openvpn/contrib/keychain-mcd/cert_data.h new file mode 100644 index 00000000..407cca1c --- /dev/null +++ b/main/openvpn/contrib/keychain-mcd/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> + *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com> + * + *  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 serial; +	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 diff --git a/main/openvpn/contrib/keychain-mcd/common_osx.c b/main/openvpn/contrib/keychain-mcd/common_osx.c new file mode 100644 index 00000000..3effa8b0 --- /dev/null +++ b/main/openvpn/contrib/keychain-mcd/common_osx.c @@ -0,0 +1,94 @@ +/* + *  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> + *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com> + * + *  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 "config.h" +#include "syshead.h" +#include "common.h" +#include "buffer.h" +#include "error.h" +*/ + +#include "common_osx.h" +#include <err.h> + +void printCFString(CFStringRef str) +{ +  CFIndex bufferLength = CFStringGetLength(str) + 1; +  char *pBuffer = (char*)malloc(sizeof(char) * bufferLength); +  CFStringGetCString(str, pBuffer, bufferLength, kCFStringEncodingUTF8); +  warnx("%s\n", 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); +    } +  else +    { +      char tmp[2] = {'A'+halfByte-10, 0}; +      CFStringAppendCString(str, tmp, kCFStringEncodingUTF8); +    } +} + +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); +} diff --git a/main/openvpn/contrib/keychain-mcd/common_osx.h b/main/openvpn/contrib/keychain-mcd/common_osx.h new file mode 100644 index 00000000..42735486 --- /dev/null +++ b/main/openvpn/contrib/keychain-mcd/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> + *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com> + * + *  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__ diff --git a/main/openvpn/contrib/keychain-mcd/crypto_osx.c b/main/openvpn/contrib/keychain-mcd/crypto_osx.c new file mode 100644 index 00000000..87ba09ba --- /dev/null +++ b/main/openvpn/contrib/keychain-mcd/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> + *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com> + * + *  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 <Security/SecKey.h> +#include <Security/Security.h> + +#include "crypto_osx.h" +#include <err.h> + +void printErrorMsg(const char *func, CFErrorRef error) +{ +  CFStringRef desc = CFErrorCopyDescription(error); +  warnx("%s failed: %s", func, CFStringGetCStringPtr(desc, kCFStringEncodingUTF8)); +  CFRelease(desc); +} + +void printErrorStatusMsg(const char *func, OSStatus status) +{ +  CFStringRef error; +  error = SecCopyErrorMessageString(status, NULL); +  if (error) +    { +      warnx("%s failed: %s", func, CFStringGetCStringPtr(error, kCFStringEncodingUTF8)); +      CFRelease(error); +    } +  else +    warnx("%s failed: %X", func, (int)status); +} + +void signData(SecIdentityRef identity, const uint8_t *from, int flen, uint8_t *to, size_t *tlen) +{ +  SecKeyRef privateKey = NULL; +  OSStatus status; + +  status = SecIdentityCopyPrivateKey(identity,  &privateKey); +  if (status != noErr) +    { +      printErrorStatusMsg("signData: SecIdentityCopyPrivateKey", status); +      *tlen = 0; +      return; +    } + +  status = SecKeyRawSign(privateKey, kSecPaddingPKCS1, from, flen, to, tlen); +  CFRelease(privateKey); +  if (status != noErr) +    { +      printErrorStatusMsg("signData: SecKeyRawSign", status); +      *tlen = 0; +      return; +    } +} diff --git a/main/openvpn/contrib/keychain-mcd/crypto_osx.h b/main/openvpn/contrib/keychain-mcd/crypto_osx.h new file mode 100644 index 00000000..0da58b60 --- /dev/null +++ b/main/openvpn/contrib/keychain-mcd/crypto_osx.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> + *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com> + * + *  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> + +extern OSStatus SecKeyRawSign ( +   SecKeyRef key, +   SecPadding padding, +   const uint8_t *dataToSign, +   size_t dataToSignLen, +   uint8_t *sig, +   size_t *sigLen +); + +void signData(SecIdentityRef identity, const uint8_t *from, int flen, uint8_t *to, size_t *tlen); +void printErrorMsg(const char *func, CFErrorRef error); + +#endif //__crypto_osx_h__ diff --git a/main/openvpn/contrib/keychain-mcd/keychain-mcd.8 b/main/openvpn/contrib/keychain-mcd/keychain-mcd.8 new file mode 100644 index 00000000..c4581e7b --- /dev/null +++ b/main/openvpn/contrib/keychain-mcd/keychain-mcd.8 @@ -0,0 +1,149 @@ +.TH keychain-mcd 8 +.SH NAME + +keychain-mcd \- Mac OS X Keychain management daemon for OpenVPN + +.SH SYNOPSIS + +.B keychain-mcd +.I identity-template management-server-ip management-server-port +[ +.I password-file +] + +.SH DESCRIPTION + +.B keychain-mcd +is Mac OS X Keychain management daemon for OpenVPN. +It loads the certificate and private key from the Mac OSX Keychain (Mac OSX Only). +.B keychain-mcd +connects to OpenVPN via management interface and handles +certificate and private key commands (namely +.B NEED-CERTIFICATE +and +.B RSA-SIGN +commands). + +.B keychain-mcd +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 OS X. + +Note that Mac OS X might need to present the user with an authentication GUI when the Keychain +is accessed by keychain-mcd. + +Use +.B keychain-mcd +along with +.B --management-external-key +and/or +.B --management-external-cert +passed to +.B openvpn. + +.SH OPTIONS + +.TP +.BR identity-template + +A select string which is used to choose a keychain identity from +Mac OS X Keychain. + +\fBSubject\fR, \fBIssuer\fR, \fBSerial\fR, \fBSHA1\fR, \fBMD5\fR selectors can be used. + +To select a certificate based on a string search in the +certificate's subject and/or issuer: + +.nf + +"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" + +.fi + +.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 +certificate it finds that matches all of the criteria you have specified. +If there are several certificates matching all of the criteria then the youngest certificate is returned +(i.e. with the greater "not before" validity field). +You can also include the MD5 and/or SHA1 thumbprints and/or serial number +along with the subject and issuer. + +To select a certificate based on certificate's MD5 or SHA1 thumbprint: + +.nf +"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" +.fi + +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. + +To select a certificate based on certificate's serial number: + +"Serial: 3E 9B 6F 02 00 00 00 01 1F 20" + +.TP +.BR management-server-ip +OpenVPN management IP to connect to. +Both IPv4 and IPv6 addresses can be used. + +.TP +.BR management-server-port +OpenVPN management port to connect to. +Use +.B unix +for +.I management-server-port +and socket path for +.I management-server-ip +to connect to a local unix socket. + +.TP +.BR password-file + +Password file containing the management password on first line. +The password will be used to connect to +.B openvpn +management interface. + +Pass +.I password-file +to +.B keychain-mcd +if +.I pw-file +was specified in +.B --management +option to +.B openvpn. + + +.SH AUTHOR + +Vasily Kulikov <segoon@openwall.com> + +.SH "SEE ALSO" + +.BR openvpn (8) diff --git a/main/openvpn/contrib/keychain-mcd/main.c b/main/openvpn/contrib/keychain-mcd/main.c new file mode 100644 index 00000000..5f59403c --- /dev/null +++ b/main/openvpn/contrib/keychain-mcd/main.c @@ -0,0 +1,244 @@ +/* + *  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) 2015 Vasily Kulikov <segoon@openwall.com> + * + *  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 <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/un.h> +#include <err.h> +#include <netdb.h> + +#include <Security/Security.h> +#include <CoreServices/CoreServices.h> + +#include "cert_data.h" +#include "crypto_osx.h" +#include "../../src/openvpn/base64.h" + + +SecIdentityRef template_to_identity(const char *template) +{ +    SecIdentityRef identity; +    CertDataRef pCertDataTemplate = createCertDataFromString(template); +    if (pCertDataTemplate == NULL) +        errx(1, "Bad certificate template"); +    identity = findIdentity(pCertDataTemplate); +    if (identity == NULL) +        errx(1, "No such identify"); +    fprintf(stderr, "Identity found\n"); +    destroyCertData(pCertDataTemplate); +    return identity; +} + +int connect_to_management_server(const char *ip, const char *port) +{ +    int fd; +    struct sockaddr_un addr_un; +    struct sockaddr *addr; +    size_t addr_len; + +    if (strcmp(port, "unix") == 0) { +        addr = (struct sockaddr*)&addr_un; +        addr_len = sizeof(addr_un); + +        addr_un.sun_family = AF_UNIX; +        strncpy(addr_un.sun_path, ip, sizeof(addr_un.sun_path)); +        fd = socket(AF_UNIX, SOCK_STREAM, 0); +    } +    else { +        int rv; +        struct addrinfo *result; +        struct addrinfo hints; + +        memset(&hints, 0, sizeof(hints)); +        hints.ai_family = AF_UNSPEC; +        hints.ai_socktype = SOCK_STREAM; + +        rv = getaddrinfo(ip, port, &hints, &result); +        if (rv < 0) +            errx(1, "getaddrinfo: %s", gai_strerror(rv)); +        if (result == NULL) +            errx(1, "getaddrinfo returned 0 addressed"); + +        /* Use the first found address */ +        fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol); +        addr = result->ai_addr; +        addr_len = result->ai_addrlen; +    } +    if (fd < 0) +        err(1, "socket"); + +    if (connect(fd, addr, addr_len) < 0) +        err(1, "connect"); + +    return fd; +} + +int is_prefix(const char *s, const char *prefix) +{ +    return strncmp(s, prefix, strlen(prefix)) == 0; +} + +void handle_rsasign(FILE *man_file, SecIdentityRef identity, const char *input) +{ +    const char *input_b64 = strchr(input, ':') + 1; +    char *input_binary; +    int input_len; +    char *output_binary; +    size_t output_len; +    char *output_b64; + +    input_len = strlen(input_b64)*8/6 + 4; +    input_binary = malloc(input_len); +    input_len = openvpn_base64_decode(input_b64, input_binary, input_len); +    if (input_len < 0) +        errx(1, "openvpn_base64_decode: overflow"); + +    output_len = 1024; +    output_binary = malloc(output_len); +    signData(identity, (const uint8_t *)input_binary, input_len, (uint8_t *)output_binary, &output_len); +    if (output_len == 0) +        errx(1, "handle_rsasign: failed to sign data"); + +    openvpn_base64_encode(output_binary, output_len, &output_b64); +    fprintf(man_file, "rsa-sig\n%s\nEND\n", output_b64); +    free(output_b64); +    free(input_binary); +    free(output_binary); + +    fprintf(stderr, "Handled RSA_SIGN command\n"); +} + +void handle_needcertificate(FILE *man_file, SecIdentityRef identity) +{ +    OSStatus status; +    SecCertificateRef certificate = NULL; +    CFDataRef data; +    const unsigned char *cert; +    size_t cert_len; +    char *result_b64, *tmp_b64; + +    status = SecIdentityCopyCertificate(identity, &certificate); +    if (status != noErr) { +        const char *msg = GetMacOSStatusErrorString(status); +        err(1, "SecIdentityCopyCertificate() failed: %s", msg); +    } + +    data = SecCertificateCopyData(certificate); +    if (data == NULL) +        err(1, "SecCertificateCopyData() returned NULL"); + +    cert = CFDataGetBytePtr(data); +    cert_len = CFDataGetLength(data); + +    openvpn_base64_encode(cert, cert_len, &result_b64); +#if 0 +    fprintf(stderr, "certificate %s\n", result_b64); +#endif + +    fprintf(man_file, "certificate\n"); +    fprintf(man_file, "-----BEGIN CERTIFICATE-----\n"); +    tmp_b64 = result_b64; +    while (strlen(tmp_b64) > 64) { +      fprintf(man_file, "%.64s\n", tmp_b64); +      tmp_b64 += 64; +    } +    if (*tmp_b64) +      fprintf(man_file, "%s\n", tmp_b64); +    fprintf(man_file, "-----END CERTIFICATE-----\n"); +    fprintf(man_file, "END\n"); + +    free(result_b64); +    CFRelease(data); +    CFRelease(certificate); + +    fprintf(stderr, "Handled NEED 'cert' command\n"); +} + +void management_loop(SecIdentityRef identity, int man_fd, const char *password) +{ +    char *buffer = NULL; +    size_t buffer_len = 0; +    FILE *man = fdopen(man_fd, "w+"); +    if (man == 0) +        err(1, "fdopen"); + +    if (password) +        fprintf(man, "%s\n", password); + +    while (1) { +        if (getline(&buffer, &buffer_len, man) < 0) +            err(1, "getline"); +#if 0 +        fprintf(stderr, "M: %s", buffer); +#endif + +        if (is_prefix(buffer, ">RSA_SIGN:")) +            handle_rsasign(man, identity, buffer); +        if (is_prefix(buffer, ">NEED-CERTIFICATE")) +            handle_needcertificate(man, identity); +        if (is_prefix(buffer, ">FATAL")) +            fprintf(stderr, "Fatal message from OpenVPN: %s\n", buffer+7); +        if (is_prefix(buffer, ">INFO")) +            fprintf(stderr, "INFO message from OpenVPN: %s\n", buffer+6); +    } +} + +char *read_password(const char *fname) +{ +    char *password = NULL; +    FILE *pwf = fopen(fname, "r"); +    size_t n = 0; + +    if (pwf == NULL) +        errx(1, "fopen(%s) failed", fname); +    if (getline(&password, &n, pwf) < 0) +        err(1, "getline"); +    fclose(pwf); +    return password; +} + +int main(int argc, char* argv[]) +{ +    if (argc < 4) +        err(1, "usage: %s <identity_template> <management_ip> <management_port> [<pw-file>]", argv[0]); + +    char *cert_prop = argv[1]; +    char *s_ip = argv[2]; +    char *s_port = argv[3]; +    char *password = NULL; +    int man_fd; + +    if (argc > 4) { +        char *s_pw_file = argv[4]; +        password = read_password(s_pw_file); +    } + +    SecIdentityRef identity = template_to_identity(cert_prop); +    man_fd = connect_to_management_server(s_ip, s_port); +    fprintf(stderr, "Successfully connected to openvpn\n"); + +    management_loop(identity, man_fd, password); +} diff --git a/main/openvpn/src/openvpn/buffer.c b/main/openvpn/src/openvpn/buffer.c index 46f874b2..421d60e9 100644 --- a/main/openvpn/src/openvpn/buffer.c +++ b/main/openvpn/src/openvpn/buffer.c @@ -1066,8 +1066,10 @@ buffer_list_peek (struct buffer_list *ol)  }  void -buffer_list_aggregate (struct buffer_list *bl, const size_t max) +buffer_list_aggregate_separator (struct buffer_list *bl, const size_t max, const char *sep)  { +  int sep_len = strlen(sep); +    if (bl->head)      {        struct buffer_entry *more = bl->head; @@ -1075,7 +1077,7 @@ buffer_list_aggregate (struct buffer_list *bl, const size_t max)        int count = 0;        for (count = 0; more && size <= max; ++count)  	{ -	  size += BLEN(&more->buf); +	  size += BLEN(&more->buf) + sep_len;  	  more = more->next;  	} @@ -1092,6 +1094,7 @@ buffer_list_aggregate (struct buffer_list *bl, const size_t max)  	    {  	      struct buffer_entry *next = e->next;  	      buf_copy (&f->buf, &e->buf); +	      buf_write(&f->buf, sep, sep_len);  	      free_buf (&e->buf);  	      free (e);  	      e = next; @@ -1105,6 +1108,12 @@ buffer_list_aggregate (struct buffer_list *bl, const size_t max)  }  void +buffer_list_aggregate (struct buffer_list *bl, const size_t max) +{ +  buffer_list_aggregate_separator(bl, max, ""); +} + +void  buffer_list_pop (struct buffer_list *ol)  {    if (ol && ol->head) diff --git a/main/openvpn/src/openvpn/buffer.h b/main/openvpn/src/openvpn/buffer.h index 7469da63..5695f64f 100644 --- a/main/openvpn/src/openvpn/buffer.h +++ b/main/openvpn/src/openvpn/buffer.h @@ -931,6 +931,7 @@ void buffer_list_advance (struct buffer_list *ol, int n);  void buffer_list_pop (struct buffer_list *ol);  void buffer_list_aggregate (struct buffer_list *bl, const size_t max); +void buffer_list_aggregate_separator (struct buffer_list *bl, const size_t max, const char *sep);  struct buffer_list *buffer_list_file (const char *fn, int max_line_len);  #endif /* BUFFER_H */ diff --git a/main/openvpn/src/openvpn/error.c b/main/openvpn/src/openvpn/error.c index 72ebfab6..08503238 100644 --- a/main/openvpn/src/openvpn/error.c +++ b/main/openvpn/src/openvpn/error.c @@ -323,7 +323,7 @@ void x_msg_va (const unsigned int flags, const char *format, va_list arglist)  	      fprintf (fp, "%lu.%06lu %x %s%s%s%s",  		       tv.tv_sec, -		       tv.tv_usec, +		       (unsigned long)tv.tv_usec,  		       flags,  		       prefix,  		       prefix_sep, diff --git a/main/openvpn/src/openvpn/init.c b/main/openvpn/src/openvpn/init.c index 77827a57..077b130e 100644 --- a/main/openvpn/src/openvpn/init.c +++ b/main/openvpn/src/openvpn/init.c @@ -1794,6 +1794,19 @@ do_deferred_options (struct context *c, const unsigned int found)        msg (D_PUSH, "OPTIONS IMPORT: peer-id set");        c->c2.tls_multi->use_peer_id = true;        c->c2.tls_multi->peer_id = c->options.peer_id; +      frame_add_to_extra_frame(&c->c2.frame, +3);	/* peer-id overhead */ +      if ( !c->options.ce.link_mtu_defined ) +	{ +	  frame_add_to_link_mtu(&c->c2.frame, +3); +	  msg (D_PUSH, "OPTIONS IMPORT: adjusting link_mtu to %d", +				EXPANDED_SIZE(&c->c2.frame)); +	} +      else +	{ +	  msg (M_WARN, "OPTIONS IMPORT: WARNING: peer-id set, but link-mtu" +                       " fixed by config - reducing tun-mtu to %d, expect" +                       " MTU problems", TUN_MTU_SIZE(&c->c2.frame) ); +	}      }  #endif  } @@ -2403,6 +2416,17 @@ do_init_frame (struct context *c)  #endif  #endif /* USE_COMP */ +  /* packets with peer-id (P_DATA_V2) need 3 extra bytes in frame (on client) +   * and need link_mtu+3 bytes on socket reception (on server). +   * +   * accomodate receive path in f->extra_link, which has the side effect of +   * also increasing send buffers (BUF_SIZE() macro), which need to be +   * allocated big enough before receiving peer-id option from server. +   * +   * f->extra_frame is adjusted when peer-id option is push-received +   */ +  frame_add_to_extra_link(&c->c2.frame, 3); +  #ifdef ENABLE_FRAGMENT    /*     * Set frame parameter for fragment code.  This is necessary because diff --git a/main/openvpn/src/openvpn/manage.c b/main/openvpn/src/openvpn/manage.c index 27164f18..6617ccd4 100644 --- a/main/openvpn/src/openvpn/manage.c +++ b/main/openvpn/src/openvpn/manage.c @@ -113,6 +113,8 @@ man_help ()  #ifdef MANAGMENT_EXTERNAL_KEY    msg (M_CLIENT, "rsa-sig                : Enter an RSA signature in response to >RSA_SIGN challenge");    msg (M_CLIENT, "                         Enter signature base64 on subsequent lines followed by END"); +  msg (M_CLIENT, "certificate            : Enter a client certificate in response to >NEED-CERT challenge"); +  msg (M_CLIENT, "                         Enter certificate base64 on subsequent lines followed by END");  #endif    msg (M_CLIENT, "signal s               : Send signal s to daemon,");    msg (M_CLIENT, "                         s = SIGHUP|SIGTERM|SIGUSR1|SIGUSR2."); @@ -868,6 +870,12 @@ in_extra_dispatch (struct management *man)        man->connection.ext_key_input = man->connection.in_extra;        man->connection.in_extra = NULL;        return; +    case IEC_CERTIFICATE: +      man->connection.ext_cert_state = EKS_READY; +      buffer_list_free (man->connection.ext_cert_input); +      man->connection.ext_cert_input = man->connection.in_extra; +      man->connection.in_extra = NULL; +      return;  #endif      }     in_extra_reset (&man->connection, IER_RESET); @@ -1030,6 +1038,20 @@ man_rsa_sig (struct management *man)      msg (M_CLIENT, "ERROR: The rsa-sig command is not currently available");  } +static void +man_certificate (struct management *man) +{ +  struct man_connection *mc = &man->connection; +  if (mc->ext_cert_state == EKS_SOLICIT) +    { +      mc->ext_cert_state = EKS_INPUT; +      mc->in_extra_cmd = IEC_CERTIFICATE; +      in_extra_reset (mc, IER_NEW); +    } +  else +    msg (M_CLIENT, "ERROR: The certificate command is not currently available"); +} +  #endif  static void @@ -1332,6 +1354,10 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch      {        man_rsa_sig (man);      } +  else if (streq (p[0], "certificate")) +    { +      man_certificate (man); +    }  #endif  #ifdef ENABLE_PKCS11    else if (streq (p[0], "pkcs11-id-count")) @@ -3118,15 +3144,14 @@ management_query_user_pass (struct management *man,  #ifdef MANAGMENT_EXTERNAL_KEY -char * /* returns allocated base64 signature */ -management_query_rsa_sig (struct management *man, -			  const char *b64_data) +int +management_query_multiline (struct management *man, +			  const char *b64_data, const char *prompt, const char *cmd, int *state, struct buffer_list **input)  {    struct gc_arena gc = gc_new (); -  char *ret = NULL; +  int ret = 0;    volatile int signal_received = 0;    struct buffer alert_msg = clear_buf(); -  struct buffer *buf;    const bool standalone_disabled_save = man->persist.standalone_disabled;    struct man_connection *mc = &man->connection; @@ -3135,10 +3160,15 @@ management_query_rsa_sig (struct management *man,        man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */        man->persist.special_state_msg = NULL; -      mc->ext_key_state = EKS_SOLICIT; +      *state = EKS_SOLICIT; -      alert_msg = alloc_buf_gc (strlen(b64_data)+64, &gc); -      buf_printf (&alert_msg, ">RSA_SIGN:%s", b64_data); +      if (b64_data) { +        alert_msg = alloc_buf_gc (strlen(b64_data)+strlen(prompt)+3, &gc); +        buf_printf (&alert_msg, ">%s:%s", prompt, b64_data); +      } else { +        alert_msg = alloc_buf_gc (strlen(prompt)+3, &gc); +        buf_printf (&alert_msg, ">%s", prompt); +      }        man_wait_for_client_connection (man, &signal_received, 0, MWCC_OTHER_WAIT); @@ -3156,40 +3186,99 @@ management_query_rsa_sig (struct management *man,  	    man_check_for_signals (&signal_received);  	  if (signal_received)  	    goto done; -	} while (mc->ext_key_state != EKS_READY); +	} while (*state != EKS_READY); -      if (buffer_list_defined(mc->ext_key_input)) -	{ -	  buffer_list_aggregate (mc->ext_key_input, 2048); -	  buf = buffer_list_peek (mc->ext_key_input); -	  if (buf && BLEN(buf) > 0) -	    { -	      ret = (char *) malloc(BLEN(buf)+1); -	      check_malloc_return(ret); -	      memcpy(ret, buf->data, BLEN(buf)); -	      ret[BLEN(buf)] = '\0'; -	    } -	} +      ret = 1;      }   done: -  if (mc->ext_key_state == EKS_READY && ret) -    msg (M_CLIENT, "SUCCESS: rsa-sig command succeeded"); -  else if (mc->ext_key_state == EKS_INPUT || mc->ext_key_state == EKS_READY) -    msg (M_CLIENT, "ERROR: rsa-sig command failed"); +  if (*state == EKS_READY && ret) +    msg (M_CLIENT, "SUCCESS: %s command succeeded", cmd); +  else if (*state == EKS_INPUT || *state == EKS_READY) +    msg (M_CLIENT, "ERROR: %s command failed", cmd);    /* revert state */    man->persist.standalone_disabled = standalone_disabled_save;    man->persist.special_state_msg = NULL;    in_extra_reset (mc, IER_RESET); -  mc->ext_key_state = EKS_UNDEF; -  buffer_list_free (mc->ext_key_input); -  mc->ext_key_input = NULL; +  *state = EKS_UNDEF;    gc_free (&gc);    return ret;  } +char * /* returns allocated base64 signature */ +management_query_multiline_flatten_newline (struct management *man, +    const char *b64_data, const char *prompt, const char *cmd, int *state, struct buffer_list **input) +{ +  int ok; +  char *result = NULL; +  struct buffer *buf; + +  ok = management_query_multiline(man, b64_data, prompt, cmd, state, input); +  if (ok && buffer_list_defined(*input)) +  { +    buffer_list_aggregate_separator (*input, 10000, "\n"); +    buf = buffer_list_peek (*input); +    if (buf && BLEN(buf) > 0) +    { +      result = (char *) malloc(BLEN(buf)+1); +      check_malloc_return(result); +      memcpy(result, buf->data, BLEN(buf)); +      result[BLEN(buf)] = '\0'; +    } +  } + +  buffer_list_free (*input); +  *input = NULL; + +  return result; +} + +char * /* returns allocated base64 signature */ +management_query_multiline_flatten (struct management *man, +    const char *b64_data, const char *prompt, const char *cmd, int *state, struct buffer_list **input) +{ +  int ok; +  char *result = NULL; +  struct buffer *buf; + +  ok = management_query_multiline(man, b64_data, prompt, cmd, state, input); +  if (ok && buffer_list_defined(*input)) +  { +    buffer_list_aggregate (*input, 2048); +    buf = buffer_list_peek (*input); +    if (buf && BLEN(buf) > 0) +    { +      result = (char *) malloc(BLEN(buf)+1); +      check_malloc_return(result); +      memcpy(result, buf->data, BLEN(buf)); +      result[BLEN(buf)] = '\0'; +    } +  } + +  buffer_list_free (*input); +  *input = NULL; + +  return result; +} + +char * /* returns allocated base64 signature */ +management_query_rsa_sig (struct management *man, +			  const char *b64_data) +{ +  return management_query_multiline_flatten(man, b64_data, "RSA_SIGN", "rsa-sign", +      &man->connection.ext_key_state, &man->connection.ext_key_input); +} + + +char* management_query_cert (struct management *man) +{ +  return management_query_multiline_flatten_newline(management, +      NULL, "NEED-CERTIFICATE", "certificate", +      &man->connection.ext_cert_state, &man->connection.ext_cert_input); +} +  #endif  /* diff --git a/main/openvpn/src/openvpn/manage.h b/main/openvpn/src/openvpn/manage.h index c48875c7..d9da0063 100644 --- a/main/openvpn/src/openvpn/manage.h +++ b/main/openvpn/src/openvpn/manage.h @@ -271,6 +271,7 @@ struct man_connection {  # define IEC_CLIENT_AUTH 1  # define IEC_CLIENT_PF   2  # define IEC_RSA_SIGN    3 +# define IEC_CERTIFICATE 4    int in_extra_cmd;    struct buffer_list *in_extra;  #ifdef MANAGEMENT_DEF_AUTH @@ -284,6 +285,8 @@ struct man_connection {  # define EKS_READY   3    int ext_key_state;    struct buffer_list *ext_key_input; +  int ext_cert_state; +  struct buffer_list *ext_cert_input;  #endif  #endif    struct event_set *es; @@ -341,6 +344,7 @@ struct management *management_init (void);  #define MF_UP_DOWN          (1<<10)  #define MF_QUERY_REMOTE     (1<<11)  #define MF_QUERY_PROXY      (1<<12) +#define MF_EXTERNAL_CERT    (1<<13)  bool management_open (struct management *man,  		      const char *addr, @@ -423,6 +427,7 @@ void management_learn_addr (struct management *management,  #ifdef MANAGMENT_EXTERNAL_KEY  char *management_query_rsa_sig (struct management *man, const char *b64_data); +char* management_query_cert (struct management *man);  #endif diff --git a/main/openvpn/src/openvpn/mtu.h b/main/openvpn/src/openvpn/mtu.h index 29ec21fd..bccd6810 100644 --- a/main/openvpn/src/openvpn/mtu.h +++ b/main/openvpn/src/openvpn/mtu.h @@ -258,6 +258,12 @@ frame_headroom (const struct frame *f, const unsigned int flag_mask)   */  static inline void +frame_add_to_link_mtu (struct frame *frame, const int increment) +{ +  frame->link_mtu += increment; +} + +static inline void  frame_add_to_extra_frame (struct frame *frame, const int increment)  {    frame->extra_frame += increment; diff --git a/main/openvpn/src/openvpn/mudp.c b/main/openvpn/src/openvpn/mudp.c index 3e3f7508..57118f87 100644 --- a/main/openvpn/src/openvpn/mudp.c +++ b/main/openvpn/src/openvpn/mudp.c @@ -52,20 +52,19 @@ multi_get_create_instance_udp (struct multi_context *m, bool *floated)    struct multi_instance *mi = NULL;    struct hash *hash = m->hash; -  if (mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true)) +  if (mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true) && +      m->top.c2.buf.len > 0)      {        struct hash_element *he;        const uint32_t hv = hash_value (hash, &real);        struct hash_bucket *bucket = hash_bucket (hash, hv);        uint8_t* ptr = BPTR(&m->top.c2.buf);        uint8_t op = ptr[0] >> P_OPCODE_SHIFT; -      uint32_t peer_id; -      int i;        /* make sure buffer has enough length to read opcode (1 byte) and peer-id (3 bytes) */        if (op == P_DATA_V2 && m->top.c2.buf.len >= (1 + 3))  	{ -	  peer_id = ntohl(*(uint32_t*)ptr) & 0xFFFFFF; +	  uint32_t peer_id = ntohl(*(uint32_t*)ptr) & 0xFFFFFF;  	  if ((peer_id < m->max_clients) && (m->instances[peer_id]))  	    {  	      mi = m->instances[peer_id]; @@ -99,6 +98,8 @@ multi_get_create_instance_udp (struct multi_context *m, bool *floated)  		  mi = multi_create_instance (m, &real);  		  if (mi)  		    { +		      int i; +  		      hash_add_fast (hash, bucket, &mi->real, hv, mi);  		      mi->did_real_hash = true; diff --git a/main/openvpn/src/openvpn/options.c b/main/openvpn/src/openvpn/options.c index e2897984..c2a377b3 100644 --- a/main/openvpn/src/openvpn/options.c +++ b/main/openvpn/src/openvpn/options.c @@ -1568,6 +1568,11 @@ show_settings (const struct options *o)    SHOW_STR (ca_file);    SHOW_STR (ca_path);    SHOW_STR (dh_file); +#ifdef MANAGMENT_EXTERNAL_KEY +  if((o->management_flags & MF_EXTERNAL_CERT)) +	SHOW_PARM ("cert_file","EXTERNAL_CERT","%s"); +  else +#endif    SHOW_STR (cert_file);  #ifdef MANAGMENT_EXTERNAL_KEY @@ -2144,6 +2149,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne  #ifdef MANAGMENT_EXTERNAL_KEY  	if (options->management_flags & MF_EXTERNAL_KEY)  	  msg(M_USAGE, "Parameter --management-external-key cannot be used when --pkcs11-provider is also specified."); +	if (options->management_flags & MF_EXTERNAL_CERT) +	  msg(M_USAGE, "Parameter --management-external-cert cannot be used when --pkcs11-provider is also specified.");  #endif  	if (options->pkcs12_file)  	  msg(M_USAGE, "Parameter --pkcs12 cannot be used when --pkcs11-provider is also specified."); @@ -2175,6 +2182,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne  #ifdef MANAGMENT_EXTERNAL_KEY            if (options->management_flags & MF_EXTERNAL_KEY)  	    msg(M_USAGE, "Parameter --management-external-key cannot be used when --cryptoapicert is also specified."); +          if (options->management_flags & MF_EXTERNAL_CERT) +	    msg(M_USAGE, "Parameter --management-external-cert cannot be used when --cryptoapicert is also specified.");  #endif  	}        else @@ -2193,6 +2202,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne  #ifdef MANAGMENT_EXTERNAL_KEY            if (options->management_flags & MF_EXTERNAL_KEY)  	    msg(M_USAGE, "Parameter --external-management-key cannot be used when --pkcs12 is also specified."); +          if (options->management_flags & MF_EXTERNAL_CERT) +	    msg(M_USAGE, "Parameter --external-management-cert cannot be used when --pkcs12 is also specified.");  #endif  #endif          } @@ -2234,6 +2245,9 @@ options_postprocess_verify_ce (const struct options *options, const struct conne  	    }  	  else  	    { +#ifdef MANAGMENT_EXTERNAL_KEY +          if (!(options->management_flags & MF_EXTERNAL_CERT)) +#endif  	      notnull (options->cert_file, "certificate file (--cert) or PKCS#12 file (--pkcs12)");  #ifdef MANAGMENT_EXTERNAL_KEY            if (!(options->management_flags & MF_EXTERNAL_KEY)) @@ -4232,6 +4246,11 @@ add_option (struct options *options,        VERIFY_PERMISSION (OPT_P_GENERAL);        options->management_flags |= MF_EXTERNAL_KEY;      } +  else if (streq (p[0], "management-external-cert")) +    { +      VERIFY_PERMISSION (OPT_P_GENERAL); +      options->management_flags |= MF_EXTERNAL_CERT; +    }  #endif  #ifdef MANAGEMENT_DEF_AUTH    else if (streq (p[0], "management-client-auth")) diff --git a/main/openvpn/src/openvpn/ssl.c b/main/openvpn/src/openvpn/ssl.c index 80293efd..59bb2a12 100644 --- a/main/openvpn/src/openvpn/ssl.c +++ b/main/openvpn/src/openvpn/ssl.c @@ -264,14 +264,16 @@ tls_get_cipher_name_pair (const char * cipher_name, size_t len) {    return NULL;  } -/** - * Max number of bytes we will add for data structures common to both data and - * control channel packets (1 byte opcode + 3 bytes peer-id). +/* + * Max number of bytes we will add + * for data structures common to both + * data and control channel packets. + * (opcode only).   */  void  tls_adjust_frame_parameters(struct frame *frame)  { -  frame_add_to_extra_frame (frame, 1 + 3); /* space for opcode + peer-id */ +  frame_add_to_extra_frame (frame, 1); /* space for opcode */  }  /* @@ -518,10 +520,18 @@ init_ssl (const struct options *options, struct tls_root_ctx *new_ctx)      }  #endif  #ifdef MANAGMENT_EXTERNAL_KEY -  else if ((options->management_flags & MF_EXTERNAL_KEY) && options->cert_file) -    { -      tls_ctx_use_external_private_key(new_ctx, options->cert_file, -	  options->cert_file_inline); +  else if ((options->management_flags & MF_EXTERNAL_KEY) && +           (options->cert_file || options->management_flags & MF_EXTERNAL_CERT)) +    { +      if (options->cert_file) { +        tls_ctx_use_external_private_key(new_ctx, options->cert_file, +          options->cert_file_inline); +      } else { +        char *external_certificate = management_query_cert(management); +        tls_ctx_use_external_private_key(new_ctx, INLINE_FILE_TAG, +            external_certificate); +        free(external_certificate); +      }      }  #endif    else | 
