Browse code

Mac OS X Keychain management client

This patch adds support for using certificates stored in the Mac OSX
Keychain to authenticate with the OpenVPN server. This works with
certificates stored on the computer as well as certificates on hardware
tokens that support Apple's tokend interface. The patch is based on
the Windows Crypto API certificate functionality that currently exists
in OpenVPN.

This patch version implements management client which handles RSA-SIGN
command for RSA offloading. Also it handles new 'NEED-CERTIFICATE'
request to pass a certificate from the keychain to OpenVPN.

OpenVPN itself gets new 'NEED-CERTIFICATE" command which is called when
--management-external-cert is used. It is implemented as a multiline
command very similar to an existing 'RSA-SIGN' command.

The patch is against commit 3341a98c2852d1d0c1eafdc70a3bdb218ec29049.

v4:
- added '--management-external-cert' argument
- keychain-mcd now parses NEED-CERTIFICATE argument if 'auto' is passed
as cmdline's identity template
- fixed typo in help output option name
- added '--management-external-cert' info in openvpn(8) manpage
- added 'certificate' command documentation into doc/management-notes.txt

v3:
- used new 'NEED-CERTIFICATE' command for certificate data request
instead of 'NEED-OK'
- improved option checking
- improved invalid certificate selection string handling
- added man page for keychain-mcd
- handle INFO, FATAL commands from openvpn and show them to user
* ACK from Arne Schwabe for OpenVPN part
* ACK from James based on Arne's testing

v2 (http://sourceforge.net/p/openvpn/mailman/message/33225603/):
- used management interface to communicate with OpenVPN process

v1 (http://sourceforge.net/p/openvpn/mailman/message/33125844/):
- used RSA_METHOD to extend openvpn itself

Signed-off-by: Vasily Kulikov <segoon@openwall.com>
--
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <20150225160718.GA6306@cachalot>
URL: http://article.gmane.org/gmane.network.openvpn.devel/9486
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Vasily Kulikov authored on 2015/02/26 01:07:18
Showing 18 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+CFILES = cert_data.c common_osx.c crypto_osx.c main.c
1
+OFILES = $(CFILES:.c=.o) ../../src/openvpn/base64.o
2
+prog = keychain-mcd
3
+
4
+CC = gcc
5
+CFLAGS = -Wall
6
+LDFLAGS =  -framework CoreFoundation -framework Security -framework CoreServices
7
+
8
+$(prog): $(OFILES)
9
+	$(CC) $(LDFLAGS) $(OFILES) -o $(prog)
10
+
11
+%.o: %.c
12
+	$(CC) $(CFLAGS) -c $< -o $@
0 13
new file mode 100644
... ...
@@ -0,0 +1,733 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>
8
+ *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com>
9
+ *
10
+ *  This program is free software; you can redistribute it and/or modify
11
+ *  it under the terms of the GNU General Public License version 2
12
+ *  as published by the Free Software Foundation.
13
+ *
14
+ *  This program is distributed in the hope that it will be useful,
15
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ *  GNU General Public License for more details.
18
+ *
19
+ *  You should have received a copy of the GNU General Public License
20
+ *  along with this program (see the file COPYING included with this
21
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
22
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
+ */
24
+
25
+
26
+#include "cert_data.h"
27
+#include <CommonCrypto/CommonDigest.h>
28
+#include <openssl/ssl.h>
29
+
30
+#include "common_osx.h"
31
+#include "crypto_osx.h"
32
+#include <err.h>
33
+
34
+CFStringRef kCertDataSubjectName = CFSTR("subject"),
35
+            kCertDataIssuerName = CFSTR("issuer"),
36
+            kCertDataSha1Name = CFSTR("SHA1"),
37
+            kCertDataMd5Name = CFSTR("MD5"),
38
+            kCertDataSerialName = CFSTR("serial"),
39
+            kCertNameFwdSlash = CFSTR("/"),
40
+            kCertNameEquals = CFSTR("=");
41
+CFStringRef kCertNameOrganization = CFSTR("o"),
42
+            kCertNameOrganizationalUnit = CFSTR("ou"),
43
+            kCertNameCountry = CFSTR("c"),
44
+            kCertNameLocality = CFSTR("l"),
45
+            kCertNameState = CFSTR("st"),
46
+            kCertNameCommonName = CFSTR("cn"),
47
+            kCertNameEmail = CFSTR("e");
48
+CFStringRef kStringSpace = CFSTR(" "),
49
+            kStringEmpty = CFSTR("");
50
+
51
+typedef struct _CertName
52
+{
53
+  CFArrayRef countryName, organization, organizationalUnit, commonName, description, emailAddress,
54
+             stateName, localityName;
55
+} CertName, *CertNameRef;
56
+
57
+typedef struct _DescData
58
+{
59
+  CFStringRef name, value;
60
+} DescData, *DescDataRef;
61
+
62
+void destroyDescData(DescDataRef pData);
63
+
64
+CertNameRef createCertName()
65
+{
66
+  CertNameRef pCertName = (CertNameRef)malloc(sizeof(CertName));
67
+  pCertName->countryName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
68
+  pCertName->organization =  CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
69
+  pCertName->organizationalUnit = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
70
+  pCertName->commonName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
71
+  pCertName->description = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
72
+  pCertName->emailAddress = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
73
+  pCertName->stateName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
74
+  pCertName->localityName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
75
+  return pCertName;
76
+}
77
+
78
+void destroyCertName(CertNameRef pCertName)
79
+{
80
+  if (!pCertName)
81
+    return;
82
+
83
+  CFRelease(pCertName->countryName);
84
+  CFRelease(pCertName->organization);
85
+  CFRelease(pCertName->organizationalUnit);
86
+  CFRelease(pCertName->commonName);
87
+  CFRelease(pCertName->description);
88
+  CFRelease(pCertName->emailAddress);
89
+  CFRelease(pCertName->stateName);
90
+  CFRelease(pCertName->localityName);
91
+  free(pCertName);
92
+}
93
+
94
+bool CFStringRefCmpCString(CFStringRef cfstr, const char *str)
95
+{
96
+  CFStringRef tmp = CFStringCreateWithCStringNoCopy(NULL, str, kCFStringEncodingUTF8, kCFAllocatorNull);
97
+  CFComparisonResult cresult = CFStringCompare(cfstr, tmp, 0);
98
+  bool result = cresult == kCFCompareEqualTo;
99
+  CFRelease(tmp);
100
+  return result;
101
+}
102
+
103
+CFDateRef GetDateFieldFromCertificate(SecCertificateRef certificate, CFTypeRef oid)
104
+{
105
+  const void *keys[] = { oid };
106
+  CFDictionaryRef dict = NULL;
107
+  CFErrorRef error;
108
+  CFDateRef date = NULL;
109
+
110
+  CFArrayRef keySelection = CFArrayCreate(NULL, keys , sizeof(keys)/sizeof(keys[0]), &kCFTypeArrayCallBacks);
111
+  dict = SecCertificateCopyValues(certificate, keySelection, &error);
112
+  if (dict == NULL)
113
+    {
114
+      printErrorMsg("GetDateFieldFromCertificate: SecCertificateCopyValues", error);
115
+      goto release_ks;
116
+    }
117
+  CFDictionaryRef vals = dict ? CFDictionaryGetValue(dict, oid) : NULL;
118
+  CFNumberRef vals2 = vals ? CFDictionaryGetValue(vals, kSecPropertyKeyValue) : NULL;
119
+  if (vals2 == NULL)
120
+    goto release_dict;
121
+
122
+  CFAbsoluteTime validityNotBefore;
123
+  if (CFNumberGetValue(vals2, kCFNumberDoubleType, &validityNotBefore))
124
+    date = CFDateCreate(kCFAllocatorDefault,validityNotBefore);
125
+
126
+release_dict:
127
+  CFRelease(dict);
128
+release_ks:
129
+  CFRelease(keySelection);
130
+  return date;
131
+}
132
+
133
+CFArrayRef GetFieldsFromCertificate(SecCertificateRef certificate, CFTypeRef oid)
134
+{
135
+  CFMutableArrayRef fields = CFArrayCreateMutable(NULL, 0, NULL);
136
+  CertNameRef pCertName = createCertName();
137
+  const void* keys[] = { oid, };
138
+  CFDictionaryRef dict;
139
+  CFErrorRef error;
140
+
141
+  CFArrayRef keySelection = CFArrayCreate(NULL, keys , 1, NULL);
142
+
143
+  dict = SecCertificateCopyValues(certificate, keySelection, &error);
144
+  if (dict == NULL) {
145
+      printErrorMsg("GetFieldsFromCertificate: SecCertificateCopyValues", error);
146
+      CFRelease(keySelection);
147
+      CFRelease(fields);
148
+      return NULL;
149
+  }
150
+  CFDictionaryRef vals = CFDictionaryGetValue(dict, oid);
151
+  CFArrayRef vals2 = vals ? CFDictionaryGetValue(vals, kSecPropertyKeyValue) : NULL;
152
+  if (vals2)
153
+    {
154
+      for(int i = 0; i < CFArrayGetCount(vals2); i++) {
155
+        CFDictionaryRef subDict = CFArrayGetValueAtIndex(vals2, i);
156
+        CFStringRef label = CFDictionaryGetValue(subDict, kSecPropertyKeyLabel);
157
+        CFStringRef value = CFDictionaryGetValue(subDict, kSecPropertyKeyValue);
158
+
159
+        if (CFStringCompare(label, kSecOIDEmailAddress, 0) == kCFCompareEqualTo)
160
+          CFArrayAppendValue((CFMutableArrayRef)pCertName->emailAddress, value);
161
+        else if (CFStringCompare(label, kSecOIDCountryName, 0) == kCFCompareEqualTo)
162
+          CFArrayAppendValue((CFMutableArrayRef)pCertName->countryName, value);
163
+        else if (CFStringCompare(label, kSecOIDOrganizationName, 0) == kCFCompareEqualTo)
164
+          CFArrayAppendValue((CFMutableArrayRef)pCertName->organization, value);
165
+        else if (CFStringCompare(label, kSecOIDOrganizationalUnitName, 0) == kCFCompareEqualTo)
166
+          CFArrayAppendValue((CFMutableArrayRef)pCertName->organizationalUnit, value);
167
+        else if (CFStringCompare(label, kSecOIDCommonName, 0) == kCFCompareEqualTo)
168
+          CFArrayAppendValue((CFMutableArrayRef)pCertName->commonName, value);
169
+        else if (CFStringCompare(label, kSecOIDDescription, 0) == kCFCompareEqualTo)
170
+          CFArrayAppendValue((CFMutableArrayRef)pCertName->description, value);
171
+        else if (CFStringCompare(label, kSecOIDStateProvinceName, 0) == kCFCompareEqualTo)
172
+          CFArrayAppendValue((CFMutableArrayRef)pCertName->stateName, value);
173
+        else if (CFStringCompare(label, kSecOIDLocalityName, 0) == kCFCompareEqualTo)
174
+          CFArrayAppendValue((CFMutableArrayRef)pCertName->localityName, value);
175
+      }
176
+      CFArrayAppendValue(fields, pCertName);
177
+    }
178
+
179
+  CFRelease(dict);
180
+  CFRelease(keySelection);
181
+  return fields;
182
+}
183
+
184
+CertDataRef createCertDataFromCertificate(SecCertificateRef certificate)
185
+{
186
+  CertDataRef pCertData = (CertDataRef)malloc(sizeof(CertData));
187
+  pCertData->subject = GetFieldsFromCertificate(certificate, kSecOIDX509V1SubjectName);
188
+  pCertData->issuer = GetFieldsFromCertificate(certificate, kSecOIDX509V1IssuerName);
189
+
190
+  CFDataRef data = SecCertificateCopyData(certificate);
191
+  if (data == NULL)
192
+    {
193
+      warnx("SecCertificateCopyData() returned NULL");
194
+      destroyCertData(pCertData);
195
+      return NULL;
196
+    }
197
+
198
+  unsigned char sha1[CC_SHA1_DIGEST_LENGTH];
199
+  CC_SHA1(CFDataGetBytePtr(data), CFDataGetLength(data), sha1);
200
+  pCertData->sha1 = createHexString(sha1, CC_SHA1_DIGEST_LENGTH);
201
+
202
+  unsigned char md5[CC_MD5_DIGEST_LENGTH];
203
+  CC_MD5(CFDataGetBytePtr(data), CFDataGetLength(data), md5);
204
+  pCertData->md5 = createHexString((unsigned char*)md5, CC_MD5_DIGEST_LENGTH);
205
+
206
+  CFDataRef serial = SecCertificateCopySerialNumber(certificate, NULL);
207
+  pCertData->serial = createHexString((unsigned char *)CFDataGetBytePtr(serial), CFDataGetLength(serial));
208
+  CFRelease(serial);
209
+
210
+  return pCertData;
211
+}
212
+
213
+CFStringRef stringFromRange(const char *cstring, CFRange range)
214
+{
215
+  CFStringRef str = CFStringCreateWithBytes (NULL, (uint8*)&cstring[range.location], range.length, kCFStringEncodingUTF8, false);
216
+  CFMutableStringRef mutableStr = CFStringCreateMutableCopy(NULL, 0, str);
217
+  CFStringTrimWhitespace(mutableStr);
218
+  CFRelease(str);
219
+  return mutableStr;
220
+}
221
+
222
+DescDataRef createDescData(const char *description, CFRange nameRange, CFRange valueRange)
223
+{
224
+  DescDataRef pRetVal = (DescDataRef)malloc(sizeof(DescData));
225
+
226
+  memset(pRetVal, 0, sizeof(DescData));
227
+
228
+  if (nameRange.length > 0)
229
+    pRetVal->name = stringFromRange(description, nameRange);
230
+
231
+  if (valueRange.length > 0)
232
+    pRetVal->value = stringFromRange(description, valueRange);
233
+
234
+#if 0
235
+  fprintf(stderr, "name = '%s', value = '%s'\n",
236
+      CFStringGetCStringPtr(pRetVal->name, kCFStringEncodingUTF8),
237
+      CFStringGetCStringPtr(pRetVal->value, kCFStringEncodingUTF8));
238
+#endif
239
+  return pRetVal;
240
+}
241
+
242
+void destroyDescData(DescDataRef pData)
243
+{
244
+  if (pData->name)
245
+    CFRelease(pData->name);
246
+
247
+  if (pData->value)
248
+    CFRelease(pData->value);
249
+
250
+  free(pData);
251
+}
252
+
253
+CFArrayRef createDescDataPairs(const char *description)
254
+{
255
+  int numChars = strlen(description);
256
+  CFRange nameRange, valueRange;
257
+  DescDataRef pData;
258
+  CFMutableArrayRef retVal = CFArrayCreateMutable(NULL, 0, NULL);
259
+
260
+  int i = 0;
261
+
262
+  nameRange = CFRangeMake(0, 0);
263
+  valueRange = CFRangeMake(0, 0);
264
+  bool bInValue = false;
265
+
266
+  while(i < numChars)
267
+    {
268
+      if (!bInValue && (description[i] != ':'))
269
+        {
270
+          nameRange.length++;
271
+        }
272
+      else if (bInValue && (description[i] != ':'))
273
+        {
274
+          valueRange.length++;
275
+        }
276
+      else if(!bInValue)
277
+        {
278
+          bInValue = true;
279
+          valueRange.location = i + 1;
280
+          valueRange.length = 0;
281
+        }
282
+      else //(bInValue)
283
+        {
284
+          bInValue = false;
285
+          while(description[i] != ' ')
286
+            {
287
+              valueRange.length--;
288
+              i--;
289
+            }
290
+
291
+          pData = createDescData(description, nameRange, valueRange);
292
+          CFArrayAppendValue(retVal, pData);
293
+
294
+          nameRange.location = i + 1;
295
+          nameRange.length = 0;
296
+        }
297
+
298
+      i++;
299
+    }
300
+
301
+  pData = createDescData(description, nameRange, valueRange);
302
+  CFArrayAppendValue(retVal, pData);
303
+  return retVal;
304
+}
305
+
306
+void arrayDestroyDescData(const void *val, void *context)
307
+{
308
+  DescDataRef pData = (DescDataRef) val;
309
+  destroyDescData(pData);
310
+}
311
+
312
+
313
+int parseNameComponent(CFStringRef dn, CFStringRef *pName, CFStringRef *pValue)
314
+{
315
+  CFArrayRef nameStrings = CFStringCreateArrayBySeparatingStrings(NULL, dn, kCertNameEquals);
316
+
317
+  *pName = *pValue = NULL;
318
+
319
+  if (CFArrayGetCount(nameStrings) != 2)
320
+    return 0;
321
+
322
+  CFMutableStringRef str;
323
+
324
+  str = CFStringCreateMutableCopy(NULL, 0, CFArrayGetValueAtIndex(nameStrings, 0));
325
+  CFStringTrimWhitespace(str);
326
+  *pName = str;
327
+
328
+  str = CFStringCreateMutableCopy(NULL, 0, CFArrayGetValueAtIndex(nameStrings, 1));
329
+  CFStringTrimWhitespace(str);
330
+  *pValue = str;
331
+
332
+  CFRelease(nameStrings);
333
+  return 1;
334
+}
335
+
336
+int tryAppendSingleCertField(CertNameRef pCertName, CFArrayRef where, CFStringRef key,
337
+    CFStringRef name, CFStringRef value)
338
+{
339
+  if (CFStringCompareWithOptions(name, key, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive)
340
+      == kCFCompareEqualTo) {
341
+    CFArrayAppendValue((CFMutableArrayRef)where, value);
342
+    return 1;
343
+  }
344
+  return 0;
345
+}
346
+
347
+int appendCertField(CertNameRef pCert, CFStringRef name, CFStringRef value)
348
+{
349
+  struct {
350
+    CFArrayRef field;
351
+    CFStringRef key;
352
+  } fields[] = {
353
+    { pCert->organization, kCertNameOrganization},
354
+    { pCert->organizationalUnit, kCertNameOrganizationalUnit},
355
+    { pCert->countryName, kCertNameCountry},
356
+    { pCert->localityName, kCertNameLocality},
357
+    { pCert->stateName, kCertNameState},
358
+    { pCert->commonName, kCertNameCommonName},
359
+    { pCert->emailAddress, kCertNameEmail},
360
+  };
361
+  int i;
362
+  int ret = 0;
363
+
364
+  for (i=0; i<sizeof(fields)/sizeof(fields[0]); i++)
365
+    ret += tryAppendSingleCertField(pCert, fields[i].field, fields[i].key, name, value);
366
+  return ret;
367
+}
368
+
369
+int parseCertName(CFStringRef nameDesc, CFMutableArrayRef names)
370
+{
371
+  CFArrayRef nameStrings = CFStringCreateArrayBySeparatingStrings(NULL, nameDesc, kCertNameFwdSlash);
372
+  int count = CFArrayGetCount(nameStrings);
373
+  int i;
374
+  int ret = 1;
375
+
376
+  CertNameRef pCertName = createCertName();
377
+
378
+  for(i = 0;i < count;i++)
379
+    {
380
+      CFMutableStringRef dn = CFStringCreateMutableCopy(NULL, 0, CFArrayGetValueAtIndex(nameStrings, i));
381
+      CFStringTrimWhitespace(dn);
382
+
383
+      CFStringRef name, value;
384
+
385
+      if (!parseNameComponent(dn, &name, &value))
386
+        ret = 0;
387
+
388
+      if (!name || !value)
389
+        {
390
+          if (name)
391
+            CFRelease(name);
392
+
393
+          if (value)
394
+            CFRelease(value);
395
+          if (name && !value)
396
+            ret = 0;
397
+
398
+          CFRelease(dn);
399
+          continue;
400
+        }
401
+
402
+      if (!appendCertField(pCertName, name, value))
403
+        ret = 0;
404
+      CFRelease(name);
405
+      CFRelease(value);
406
+      CFRelease(dn);
407
+    }
408
+
409
+  CFArrayAppendValue(names, pCertName);
410
+  CFRelease(nameStrings);
411
+  return ret;
412
+}
413
+
414
+int arrayParseDescDataPair(const void *val, void *context)
415
+{
416
+  DescDataRef pDescData = (DescDataRef)val;
417
+  CertDataRef pCertData = (CertDataRef)context;
418
+  int ret = 1;
419
+
420
+  if (!pDescData->name || !pDescData->value)
421
+    return 0;
422
+
423
+  if (CFStringCompareWithOptions(pDescData->name, kCertDataSubjectName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
424
+    ret = parseCertName(pDescData->value, (CFMutableArrayRef)pCertData->subject);
425
+  else if (CFStringCompareWithOptions(pDescData->name, kCertDataIssuerName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
426
+    ret = parseCertName(pDescData->value, (CFMutableArrayRef)pCertData->issuer);
427
+  else if (CFStringCompareWithOptions(pDescData->name, kCertDataSha1Name, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
428
+    pCertData->sha1 = CFRetain(pDescData->value);
429
+  else if (CFStringCompareWithOptions(pDescData->name, kCertDataMd5Name, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
430
+    pCertData->md5 = CFRetain(pDescData->value);
431
+  else if (CFStringCompareWithOptions(pDescData->name, kCertDataSerialName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
432
+    pCertData->serial = CFRetain(pDescData->value);
433
+  else
434
+    return 0;
435
+
436
+  return ret;
437
+}
438
+
439
+CertDataRef createCertDataFromString(const char *description)
440
+{
441
+  CertDataRef pCertData = (CertDataRef)malloc(sizeof(CertData));
442
+  pCertData->subject = CFArrayCreateMutable(NULL, 0, NULL);
443
+  pCertData->issuer = CFArrayCreateMutable(NULL, 0, NULL);
444
+  pCertData->sha1 = NULL;
445
+  pCertData->md5 = NULL;
446
+  pCertData->serial = NULL;
447
+
448
+  CFArrayRef pairs = createDescDataPairs(description);
449
+  for (int i=0; i<CFArrayGetCount(pairs); i++)
450
+    if (!arrayParseDescDataPair(CFArrayGetValueAtIndex(pairs, i), pCertData)) {
451
+      arrayDestroyDescData(pCertData, NULL);
452
+      CFArrayApplyFunction(pairs, CFRangeMake(0, CFArrayGetCount(pairs)), arrayDestroyDescData, NULL);
453
+      CFRelease(pairs);
454
+      return 0;
455
+    }
456
+
457
+  CFArrayApplyFunction(pairs, CFRangeMake(0, CFArrayGetCount(pairs)), arrayDestroyDescData, NULL);
458
+  CFRelease(pairs);
459
+  return pCertData;
460
+}
461
+
462
+void arrayDestroyCertName(const void *val, void *context)
463
+{
464
+  CertNameRef pCertName = (CertNameRef)val;
465
+  destroyCertName(pCertName);
466
+}
467
+
468
+void destroyCertData(CertDataRef pCertData)
469
+{
470
+  if (pCertData->subject)
471
+    {
472
+      CFArrayApplyFunction(pCertData->subject, CFRangeMake(0, CFArrayGetCount(pCertData->subject)), arrayDestroyCertName, NULL);
473
+      CFRelease(pCertData->subject);
474
+    }
475
+
476
+  if (pCertData->issuer)
477
+    {
478
+      CFArrayApplyFunction(pCertData->issuer, CFRangeMake(0, CFArrayGetCount(pCertData->issuer)), arrayDestroyCertName, NULL);
479
+      CFRelease(pCertData->issuer);
480
+    }
481
+
482
+  if (pCertData->sha1)
483
+    CFRelease(pCertData->sha1);
484
+
485
+  if (pCertData->md5)
486
+    CFRelease(pCertData->md5);
487
+
488
+  if (pCertData->serial)
489
+    CFRelease(pCertData->serial);
490
+
491
+  free(pCertData);
492
+}
493
+
494
+bool stringArrayMatchesTemplate(CFArrayRef strings, CFArrayRef templateArray)
495
+{
496
+  int templateCount, stringCount, i;
497
+
498
+  templateCount = CFArrayGetCount(templateArray);
499
+
500
+  if (templateCount > 0)
501
+    {
502
+      stringCount = CFArrayGetCount(strings);
503
+      if (stringCount != templateCount)
504
+        return false;
505
+
506
+      for(i = 0;i < stringCount;i++)
507
+        {
508
+          CFStringRef str, template;
509
+
510
+          template = (CFStringRef)CFArrayGetValueAtIndex(templateArray, i);
511
+          str = (CFStringRef)CFArrayGetValueAtIndex(strings, i);
512
+
513
+          if (CFStringCompareWithOptions(template, str, CFRangeMake(0, CFStringGetLength(template)), kCFCompareCaseInsensitive) != kCFCompareEqualTo)
514
+            return false;
515
+        }
516
+    }
517
+
518
+  return true;
519
+
520
+}
521
+
522
+bool certNameMatchesTemplate(CertNameRef pCertName, CertNameRef pTemplate)
523
+{
524
+  if (!stringArrayMatchesTemplate(pCertName->countryName, pTemplate->countryName))
525
+    return false;
526
+  else if (!stringArrayMatchesTemplate(pCertName->organization, pTemplate->organization))
527
+    return false;
528
+  else if (!stringArrayMatchesTemplate(pCertName->organizationalUnit, pTemplate->organizationalUnit))
529
+    return false;
530
+  else if (!stringArrayMatchesTemplate(pCertName->commonName, pTemplate->commonName))
531
+    return false;
532
+  else if (!stringArrayMatchesTemplate(pCertName->emailAddress, pTemplate->emailAddress))
533
+    return false;
534
+  else if (!stringArrayMatchesTemplate(pCertName->stateName, pTemplate->stateName))
535
+    return false;
536
+  else if (!stringArrayMatchesTemplate(pCertName->localityName, pTemplate->localityName))
537
+    return false;
538
+  else
539
+    return true;
540
+}
541
+
542
+bool certNameArrayMatchesTemplate(CFArrayRef certNameArray, CFArrayRef templateArray)
543
+{
544
+  int templateCount, certCount, i;
545
+
546
+  templateCount = CFArrayGetCount(templateArray);
547
+
548
+  if (templateCount > 0)
549
+    {
550
+      certCount = CFArrayGetCount(certNameArray);
551
+      if (certCount != templateCount)
552
+        return false;
553
+
554
+      for(i = 0;i < certCount;i++)
555
+        {
556
+          CertNameRef pName, pTemplateName;
557
+
558
+          pTemplateName = (CertNameRef)CFArrayGetValueAtIndex(templateArray, i);
559
+          pName = (CertNameRef)CFArrayGetValueAtIndex(certNameArray, i);
560
+
561
+          if (!certNameMatchesTemplate(pName, pTemplateName))
562
+            return false;
563
+        }
564
+    }
565
+
566
+  return true;
567
+}
568
+
569
+bool hexStringMatchesTemplate(CFStringRef str, CFStringRef template)
570
+{
571
+  if (template)
572
+    {
573
+      if (!str)
574
+        return false;
575
+
576
+      CFMutableStringRef strMutable, templateMutable;
577
+
578
+      strMutable = CFStringCreateMutableCopy(NULL, 0, str);
579
+      templateMutable = CFStringCreateMutableCopy(NULL, 0, template);
580
+
581
+      CFStringFindAndReplace(strMutable, kStringSpace, kStringEmpty, CFRangeMake(0, CFStringGetLength(strMutable)), 0);
582
+      CFStringFindAndReplace(templateMutable, kStringSpace, kStringEmpty, CFRangeMake(0, CFStringGetLength(templateMutable)), 0);
583
+
584
+      CFComparisonResult result = CFStringCompareWithOptions(templateMutable, strMutable, CFRangeMake(0, CFStringGetLength(templateMutable)), kCFCompareCaseInsensitive);
585
+
586
+      CFRelease(strMutable);
587
+      CFRelease(templateMutable);
588
+
589
+      if (result != kCFCompareEqualTo)
590
+        return false;
591
+    }
592
+
593
+  return true;
594
+}
595
+
596
+bool certDataMatchesTemplate(CertDataRef pCertData, CertDataRef pTemplate)
597
+{
598
+  if (!certNameArrayMatchesTemplate(pCertData->subject, pTemplate->subject))
599
+    return false;
600
+
601
+  if (!certNameArrayMatchesTemplate(pCertData->issuer, pTemplate->issuer))
602
+    return false;
603
+
604
+  if (!hexStringMatchesTemplate(pCertData->sha1, pTemplate->sha1))
605
+    return false;
606
+
607
+  if (!hexStringMatchesTemplate(pCertData->md5, pTemplate->md5))
608
+    return false;
609
+
610
+  if (!hexStringMatchesTemplate(pCertData->serial, pTemplate->serial))
611
+    return false;
612
+
613
+  return true;
614
+}
615
+
616
+bool certExpired(SecCertificateRef certificate)
617
+{
618
+  bool result;
619
+  CFDateRef notAfter = GetDateFieldFromCertificate(certificate, kSecOIDX509V1ValidityNotAfter);
620
+  CFDateRef notBefore = GetDateFieldFromCertificate(certificate, kSecOIDX509V1ValidityNotBefore);
621
+  CFDateRef now = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent());
622
+
623
+  if (!notAfter || !notBefore || !now)
624
+    {
625
+      warnx("GetDateFieldFromCertificate() returned NULL");
626
+      result = true;
627
+    }
628
+  else
629
+    {
630
+      if (CFDateCompare(notBefore, now, NULL) != kCFCompareLessThan ||
631
+          CFDateCompare(now, notAfter, NULL) != kCFCompareLessThan)
632
+        result = true;
633
+      else
634
+        result = false;
635
+    }
636
+
637
+  CFRelease(notAfter);
638
+  CFRelease(notBefore);
639
+  CFRelease(now);
640
+  return result;
641
+}
642
+
643
+SecIdentityRef findIdentity(CertDataRef pCertDataTemplate)
644
+{
645
+  const void *keys[] = {
646
+    kSecClass,
647
+    kSecReturnRef,
648
+    kSecMatchLimit
649
+  };
650
+  const void *values[] = {
651
+    kSecClassIdentity,
652
+    kCFBooleanTrue,
653
+    kSecMatchLimitAll
654
+  };
655
+  CFArrayRef result = NULL;
656
+
657
+  CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values,
658
+      sizeof(keys) / sizeof(*keys),
659
+      &kCFTypeDictionaryKeyCallBacks,
660
+      &kCFTypeDictionaryValueCallBacks);
661
+  OSStatus status = SecItemCopyMatching(query, (CFTypeRef*)&result);
662
+  CFRelease(query);
663
+  if (status != noErr)
664
+    {
665
+      warnx ("No identities in keychain found");
666
+      return NULL;
667
+    }
668
+
669
+  SecIdentityRef bestIdentity = NULL;
670
+  CFDateRef bestNotBeforeDate = NULL;
671
+
672
+  for (int i=0; i<CFArrayGetCount(result); i++)
673
+    {
674
+      SecIdentityRef identity = (SecIdentityRef)CFArrayGetValueAtIndex(result, i);
675
+      if (identity == NULL)
676
+        {
677
+          warnx ("identity == NULL");
678
+          continue;
679
+        }
680
+
681
+      SecCertificateRef certificate = NULL;
682
+      SecIdentityCopyCertificate (identity, &certificate);
683
+      if (certificate == NULL)
684
+        {
685
+          warnx ("SecIdentityCopyCertificate() returned NULL");
686
+          continue;
687
+        }
688
+
689
+      CertDataRef pCertData2 = createCertDataFromCertificate(certificate);
690
+      if (pCertData2 == NULL)
691
+        {
692
+          warnx ("createCertDataFromCertificate() returned NULL");
693
+          goto release_cert;
694
+        }
695
+      bool bMatches = certDataMatchesTemplate(pCertData2, pCertDataTemplate);
696
+      bool bExpired = certExpired(certificate);
697
+      destroyCertData(pCertData2);
698
+
699
+      if (bMatches && !bExpired)
700
+        {
701
+          CFDateRef notBeforeDate = GetDateFieldFromCertificate(certificate, kSecOIDX509V1ValidityNotBefore);
702
+          if (!notBeforeDate)
703
+            {
704
+              warnx ("GetDateFieldFromCertificate() returned NULL");
705
+              goto release_cert;
706
+            }
707
+          if (bestIdentity == NULL)
708
+            {
709
+              CFRetain(identity);
710
+              bestIdentity = identity;
711
+
712
+              bestNotBeforeDate = notBeforeDate;
713
+              CFRetain(notBeforeDate);
714
+            }
715
+          else if (CFDateCompare(bestNotBeforeDate, notBeforeDate, NULL) == kCFCompareLessThan)
716
+            {
717
+              CFRelease(bestIdentity);
718
+              CFRetain(identity);
719
+              bestIdentity = identity;
720
+
721
+              bestNotBeforeDate = notBeforeDate;
722
+              CFRetain(notBeforeDate);
723
+            }
724
+          CFRelease(notBeforeDate);
725
+        }
726
+    release_cert:
727
+      CFRelease(certificate);
728
+    }
729
+  CFRelease(result);
730
+
731
+  return bestIdentity;
732
+}
0 733
new file mode 100644
... ...
@@ -0,0 +1,46 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>
8
+ *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com>
9
+ *
10
+ *  This program is free software; you can redistribute it and/or modify
11
+ *  it under the terms of the GNU General Public License version 2
12
+ *  as published by the Free Software Foundation.
13
+ *
14
+ *  This program is distributed in the hope that it will be useful,
15
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ *  GNU General Public License for more details.
18
+ *
19
+ *  You should have received a copy of the GNU General Public License
20
+ *  along with this program (see the file COPYING included with this
21
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
22
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
+ */
24
+#ifndef __cert_data_h__
25
+#define __cert_data_h__
26
+
27
+#include <CoreFoundation/CoreFoundation.h>
28
+#include <Security/Security.h>
29
+
30
+typedef struct _CertData
31
+{
32
+	CFArrayRef subject;
33
+	CFArrayRef issuer;
34
+	CFStringRef serial;
35
+	CFStringRef md5, sha1;
36
+} CertData, *CertDataRef;
37
+
38
+CertDataRef createCertDataFromCertificate(SecCertificateRef certificate);
39
+CertDataRef createCertDataFromString(const char *description);
40
+void destroyCertData(CertDataRef pCertData);
41
+bool certDataMatchesTemplate(CertDataRef pCertData, CertDataRef pTemplate);
42
+void printCertData(CertDataRef pCertData);
43
+SecIdentityRef findIdentity(CertDataRef pCertDataTemplate);
44
+
45
+#endif
0 46
new file mode 100644
... ...
@@ -0,0 +1,94 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>
8
+ *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com>
9
+ *
10
+ *  This program is free software; you can redistribute it and/or modify
11
+ *  it under the terms of the GNU General Public License version 2
12
+ *  as published by the Free Software Foundation.
13
+ *
14
+ *  This program is distributed in the hope that it will be useful,
15
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ *  GNU General Public License for more details.
18
+ *
19
+ *  You should have received a copy of the GNU General Public License
20
+ *  along with this program (see the file COPYING included with this
21
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
22
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
+ */
24
+
25
+/*
26
+#include "config.h"
27
+#include "syshead.h"
28
+#include "common.h"
29
+#include "buffer.h"
30
+#include "error.h"
31
+*/
32
+
33
+#include "common_osx.h"
34
+#include <err.h>
35
+
36
+void printCFString(CFStringRef str)
37
+{
38
+  CFIndex bufferLength = CFStringGetLength(str) + 1;
39
+  char *pBuffer = (char*)malloc(sizeof(char) * bufferLength);
40
+  CFStringGetCString(str, pBuffer, bufferLength, kCFStringEncodingUTF8);
41
+  warnx("%s\n", pBuffer);
42
+  free(pBuffer);
43
+}
44
+
45
+char* cfstringToCstr(CFStringRef str)
46
+{
47
+  CFIndex bufferLength = CFStringGetLength(str) + 1;
48
+  char *pBuffer = (char*)malloc(sizeof(char) * bufferLength);
49
+  CFStringGetCString(str, pBuffer, bufferLength, kCFStringEncodingUTF8);
50
+  return pBuffer;
51
+}
52
+
53
+void appendHexChar(CFMutableStringRef str, unsigned char halfByte)
54
+{
55
+  if (halfByte < 10)
56
+    {
57
+      CFStringAppendFormat (str, NULL, CFSTR("%d"), halfByte);
58
+    }
59
+  else
60
+    {
61
+      char tmp[2] = {'A'+halfByte-10, 0};
62
+      CFStringAppendCString(str, tmp, kCFStringEncodingUTF8);
63
+    }
64
+}
65
+
66
+CFStringRef createHexString(unsigned char *pData, int length)
67
+{
68
+  unsigned char byte, low, high;
69
+  int i;
70
+  CFMutableStringRef str = CFStringCreateMutable(NULL, 0);
71
+
72
+  for(i = 0;i < length;i++)
73
+    {
74
+      byte = pData[i];
75
+      low = byte & 0x0F;
76
+      high = (byte >> 4);
77
+
78
+      appendHexChar(str, high);
79
+      appendHexChar(str, low);
80
+
81
+      if (i != (length - 1))
82
+        CFStringAppendCString(str, " ", kCFStringEncodingUTF8);
83
+    }
84
+
85
+  return str;
86
+}
87
+
88
+void printHex(unsigned char *pData, int length)
89
+{
90
+  CFStringRef hexStr = createHexString(pData, length);
91
+  printCFString(hexStr);
92
+  CFRelease(hexStr);
93
+}
0 94
new file mode 100644
... ...
@@ -0,0 +1,36 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>
8
+ *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com>
9
+ *
10
+ *  This program is free software; you can redistribute it and/or modify
11
+ *  it under the terms of the GNU General Public License version 2
12
+ *  as published by the Free Software Foundation.
13
+ *
14
+ *  This program is distributed in the hope that it will be useful,
15
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ *  GNU General Public License for more details.
18
+ *
19
+ *  You should have received a copy of the GNU General Public License
20
+ *  along with this program (see the file COPYING included with this
21
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
22
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
+ */
24
+
25
+#ifndef __common_osx_h__
26
+#define __common_osx_h__
27
+
28
+#include <CoreFoundation/CoreFoundation.h>
29
+
30
+void printCFString(CFStringRef str);
31
+char* cfstringToCstr(CFStringRef str);
32
+CFStringRef createHexString(unsigned char *pData, int length);
33
+void printHex(unsigned char *pData, int length);
34
+
35
+#endif //__Common_osx_h__
0 36
new file mode 100644
... ...
@@ -0,0 +1,75 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>
8
+ *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com>
9
+ *
10
+ *  This program is free software; you can redistribute it and/or modify
11
+ *  it under the terms of the GNU General Public License version 2
12
+ *  as published by the Free Software Foundation.
13
+ *
14
+ *  This program is distributed in the hope that it will be useful,
15
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ *  GNU General Public License for more details.
18
+ *
19
+ *  You should have received a copy of the GNU General Public License
20
+ *  along with this program (see the file COPYING included with this
21
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
22
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
+ */
24
+
25
+
26
+#include <CommonCrypto/CommonDigest.h>
27
+#include <Security/SecKey.h>
28
+#include <Security/Security.h>
29
+
30
+#include "crypto_osx.h"
31
+#include <err.h>
32
+
33
+void printErrorMsg(const char *func, CFErrorRef error)
34
+{
35
+  CFStringRef desc = CFErrorCopyDescription(error);
36
+  warnx("%s failed: %s", func, CFStringGetCStringPtr(desc, kCFStringEncodingUTF8));
37
+  CFRelease(desc);
38
+}
39
+
40
+void printErrorStatusMsg(const char *func, OSStatus status)
41
+{
42
+  CFStringRef error;
43
+  error = SecCopyErrorMessageString(status, NULL);
44
+  if (error)
45
+    {
46
+      warnx("%s failed: %s", func, CFStringGetCStringPtr(error, kCFStringEncodingUTF8));
47
+      CFRelease(error);
48
+    }
49
+  else
50
+    warnx("%s failed: %X", func, (int)status);
51
+}
52
+
53
+void signData(SecIdentityRef identity, const uint8_t *from, int flen, uint8_t *to, size_t *tlen)
54
+{
55
+  SecKeyRef privateKey = NULL;
56
+  OSStatus status;
57
+
58
+  status = SecIdentityCopyPrivateKey(identity,  &privateKey);
59
+  if (status != noErr)
60
+    {
61
+      printErrorStatusMsg("signData: SecIdentityCopyPrivateKey", status);
62
+      *tlen = 0;
63
+      return;
64
+    }
65
+
66
+  status = SecKeyRawSign(privateKey, kSecPaddingPKCS1, from, flen, to, tlen);
67
+  CFRelease(privateKey);
68
+  if (status != noErr)
69
+    {
70
+      printErrorStatusMsg("signData: SecKeyRawSign", status);
71
+      *tlen = 0;
72
+      return;
73
+    }
74
+}
0 75
new file mode 100644
... ...
@@ -0,0 +1,44 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org>
8
+ *  Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com>
9
+ *
10
+ *  This program is free software; you can redistribute it and/or modify
11
+ *  it under the terms of the GNU General Public License version 2
12
+ *  as published by the Free Software Foundation.
13
+ *
14
+ *  This program is distributed in the hope that it will be useful,
15
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ *  GNU General Public License for more details.
18
+ *
19
+ *  You should have received a copy of the GNU General Public License
20
+ *  along with this program (see the file COPYING included with this
21
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
22
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
+ */
24
+
25
+#ifndef __crypto_osx_h__
26
+#define __crypto_osx_h__
27
+
28
+#include <CoreFoundation/CoreFoundation.h>
29
+#include <Security/Security.h>
30
+
31
+extern OSStatus SecKeyRawSign (
32
+   SecKeyRef key,
33
+   SecPadding padding,
34
+   const uint8_t *dataToSign,
35
+   size_t dataToSignLen,
36
+   uint8_t *sig,
37
+   size_t *sigLen
38
+);
39
+
40
+void signData(SecIdentityRef identity, const uint8_t *from, int flen, uint8_t *to, size_t *tlen);
41
+void printErrorMsg(const char *func, CFErrorRef error);
42
+
43
+#endif //__crypto_osx_h__
0 44
new file mode 100644
... ...
@@ -0,0 +1,161 @@
0
+.TH keychain-mcd 8
1
+.SH NAME
2
+
3
+keychain-mcd \- Mac OS X Keychain management daemon for OpenVPN
4
+
5
+.SH SYNOPSIS
6
+
7
+.B keychain-mcd
8
+.I identity-template management-server-ip management-server-port
9
+[
10
+.I password-file
11
+]
12
+
13
+.SH DESCRIPTION
14
+
15
+.B keychain-mcd
16
+is Mac OS X Keychain management daemon for OpenVPN.
17
+It loads the certificate and private key from the Mac OSX Keychain (Mac OSX Only).
18
+.B keychain-mcd
19
+connects to OpenVPN via management interface and handles
20
+certificate and private key commands (namely
21
+.B NEED-CERTIFICATE
22
+and
23
+.B RSA-SIGN
24
+commands).
25
+
26
+.B keychain-mcd
27
+makes it possible to use any smart card supported by Mac OSX using the tokend interface, but also any
28
+kind of certificate, residing in the Keychain, where you have access to
29
+the private key.  This option has been tested on the client side with an Aladdin eToken
30
+on Mac OSX Leopard and with software certificates stored in the Keychain on Mac OS X.
31
+
32
+Note that Mac OS X might need to present the user with an authentication GUI when the Keychain
33
+is accessed by keychain-mcd.
34
+
35
+Use
36
+.B keychain-mcd
37
+along with
38
+.B --management-external-key
39
+and/or
40
+.B --management-external-cert
41
+passed to
42
+.B openvpn.
43
+
44
+.SH OPTIONS
45
+
46
+.TP
47
+.BR identity-template
48
+
49
+A select string which is used to choose a keychain identity from
50
+Mac OS X Keychain or
51
+.I auto
52
+if the identity template is passed from openvpn.
53
+
54
+\fBSubject\fR, \fBIssuer\fR, \fBSerial\fR, \fBSHA1\fR, \fBMD5\fR selectors can be used.
55
+
56
+To select a certificate based on a string search in the
57
+certificate's subject and/or issuer:
58
+
59
+.nf
60
+
61
+"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"
62
+
63
+.fi
64
+
65
+.I "Distinguished Name Component Abbreviations:"
66
+.br
67
+o = organization
68
+.br
69
+ou = organizational unit
70
+.br
71
+c = country
72
+.br
73
+l = locality
74
+.br
75
+st = state
76
+.br
77
+cn = common name
78
+.br
79
+e = email
80
+.br
81
+
82
+All of the distinguished name components are optional, although you do need to specify at least one of them.  You can
83
+add spaces around the '/' and '=' characters, e.g. "SUBJECT: c = US / o = Apple Inc.".  You do not need to specify
84
+both the subject and the issuer, one or the other will work fine.
85
+The identity searching algorithm will return the
86
+certificate it finds that matches all of the criteria you have specified.
87
+If there are several certificates matching all of the criteria then the youngest certificate is returned
88
+(i.e. with the greater "not before" validity field).
89
+You can also include the MD5 and/or SHA1 thumbprints and/or serial number
90
+along with the subject and issuer.
91
+
92
+To select a certificate based on certificate's MD5 or SHA1 thumbprint:
93
+
94
+.nf
95
+"SHA1: 30 F7 3A 7A B7 73 2A 98 54 33 4A A7 00 6F 6E AC EC D1 EF 02"
96
+
97
+"MD5: D5 F5 11 F1 38 EB 5F 4D CF 23 B6 94 E8 33 D8 B5"
98
+.fi
99
+
100
+Again, you can include both the SHA1 and the MD5 thumbprints, but you can also use just one of them.
101
+The thumbprint hex strings can easily be copy-and-pasted from the OSX Keychain Access GUI in the Applications/Utilities folder.
102
+The hex string comparison is not case sensitive.
103
+
104
+To select a certificate based on certificate's serial number:
105
+
106
+"Serial: 3E 9B 6F 02 00 00 00 01 1F 20"
107
+
108
+If
109
+.BR identity-template
110
+equals to
111
+.I auto
112
+then the actual identity template is
113
+obtained from argument of NEED-CERTIFICATE notification of openvpn.
114
+In this case the argument of NEED-CERTIFICATE must begin with 'macosx-keychain:' prefix
115
+and the rest of it must contain the actual identity template in the format described above.
116
+
117
+
118
+.TP
119
+.BR management-server-ip
120
+OpenVPN management IP to connect to.
121
+Both IPv4 and IPv6 addresses can be used.
122
+
123
+.TP
124
+.BR management-server-port
125
+OpenVPN management port to connect to.
126
+Use
127
+.B unix
128
+for
129
+.I management-server-port
130
+and socket path for
131
+.I management-server-ip
132
+to connect to a local unix socket.
133
+
134
+.TP
135
+.BR password-file
136
+
137
+Password file containing the management password on first line.
138
+The password will be used to connect to
139
+.B openvpn
140
+management interface.
141
+
142
+Pass
143
+.I password-file
144
+to
145
+.B keychain-mcd
146
+if
147
+.I pw-file
148
+was specified in
149
+.B --management
150
+option to
151
+.B openvpn.
152
+
153
+
154
+.SH AUTHOR
155
+
156
+Vasily Kulikov <segoon@openwall.com>
157
+
158
+.SH "SEE ALSO"
159
+
160
+.BR openvpn (8)
0 161
new file mode 100644
... ...
@@ -0,0 +1,255 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2015 Vasily Kulikov <segoon@openwall.com>
8
+ *
9
+ *  This program is free software; you can redistribute it and/or modify
10
+ *  it under the terms of the GNU General Public License version 2
11
+ *  as published by the Free Software Foundation.
12
+ *
13
+ *  This program is distributed in the hope that it will be useful,
14
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ *  GNU General Public License for more details.
17
+ *
18
+ *  You should have received a copy of the GNU General Public License
19
+ *  along with this program (see the file COPYING included with this
20
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
21
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
+ */
23
+
24
+
25
+#include <sys/socket.h>
26
+#include <netinet/in.h>
27
+#include <arpa/inet.h>
28
+#include <sys/un.h>
29
+#include <err.h>
30
+#include <netdb.h>
31
+
32
+#include <Security/Security.h>
33
+#include <CoreServices/CoreServices.h>
34
+
35
+#include "cert_data.h"
36
+#include "crypto_osx.h"
37
+#include "../../src/openvpn/base64.h"
38
+
39
+
40
+SecIdentityRef template_to_identity(const char *template)
41
+{
42
+    SecIdentityRef identity;
43
+    CertDataRef pCertDataTemplate = createCertDataFromString(template);
44
+    if (pCertDataTemplate == NULL)
45
+        errx(1, "Bad certificate template");
46
+    identity = findIdentity(pCertDataTemplate);
47
+    if (identity == NULL)
48
+        errx(1, "No such identify");
49
+    fprintf(stderr, "Identity found\n");
50
+    destroyCertData(pCertDataTemplate);
51
+    return identity;
52
+}
53
+
54
+int connect_to_management_server(const char *ip, const char *port)
55
+{
56
+    int fd;
57
+    struct sockaddr_un addr_un;
58
+    struct sockaddr *addr;
59
+    size_t addr_len;
60
+
61
+    if (strcmp(port, "unix") == 0) {
62
+        addr = (struct sockaddr*)&addr_un;
63
+        addr_len = sizeof(addr_un);
64
+
65
+        addr_un.sun_family = AF_UNIX;
66
+        strncpy(addr_un.sun_path, ip, sizeof(addr_un.sun_path));
67
+        fd = socket(AF_UNIX, SOCK_STREAM, 0);
68
+    }
69
+    else {
70
+        int rv;
71
+        struct addrinfo *result;
72
+        struct addrinfo hints;
73
+
74
+        memset(&hints, 0, sizeof(hints));
75
+        hints.ai_family = AF_UNSPEC;
76
+        hints.ai_socktype = SOCK_STREAM;
77
+
78
+        rv = getaddrinfo(ip, port, &hints, &result);
79
+        if (rv < 0)
80
+            errx(1, "getaddrinfo: %s", gai_strerror(rv));
81
+        if (result == NULL)
82
+            errx(1, "getaddrinfo returned 0 addressed");
83
+
84
+        /* Use the first found address */
85
+        fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
86
+        addr = result->ai_addr;
87
+        addr_len = result->ai_addrlen;
88
+    }
89
+    if (fd < 0)
90
+        err(1, "socket");
91
+
92
+    if (connect(fd, addr, addr_len) < 0)
93
+        err(1, "connect");
94
+
95
+    return fd;
96
+}
97
+
98
+int is_prefix(const char *s, const char *prefix)
99
+{
100
+    return strncmp(s, prefix, strlen(prefix)) == 0;
101
+}
102
+
103
+void handle_rsasign(FILE *man_file, SecIdentityRef identity, const char *input)
104
+{
105
+    const char *input_b64 = strchr(input, ':') + 1;
106
+    char *input_binary;
107
+    int input_len;
108
+    char *output_binary;
109
+    size_t output_len;
110
+    char *output_b64;
111
+
112
+    input_len = strlen(input_b64)*8/6 + 4;
113
+    input_binary = malloc(input_len);
114
+    input_len = openvpn_base64_decode(input_b64, input_binary, input_len);
115
+    if (input_len < 0)
116
+        errx(1, "openvpn_base64_decode: overflow");
117
+
118
+    output_len = 1024;
119
+    output_binary = malloc(output_len);
120
+    signData(identity, (const uint8_t *)input_binary, input_len, (uint8_t *)output_binary, &output_len);
121
+    if (output_len == 0)
122
+        errx(1, "handle_rsasign: failed to sign data");
123
+
124
+    openvpn_base64_encode(output_binary, output_len, &output_b64);
125
+    fprintf(man_file, "rsa-sig\n%s\nEND\n", output_b64);
126
+    free(output_b64);
127
+    free(input_binary);
128
+    free(output_binary);
129
+
130
+    fprintf(stderr, "Handled RSA_SIGN command\n");
131
+}
132
+
133
+void handle_needcertificate(FILE *man_file, SecIdentityRef identity)
134
+{
135
+    OSStatus status;
136
+    SecCertificateRef certificate = NULL;
137
+    CFDataRef data;
138
+    const unsigned char *cert;
139
+    size_t cert_len;
140
+    char *result_b64, *tmp_b64;
141
+
142
+    status = SecIdentityCopyCertificate(identity, &certificate);
143
+    if (status != noErr) {
144
+        const char *msg = GetMacOSStatusErrorString(status);
145
+        err(1, "SecIdentityCopyCertificate() failed: %s", msg);
146
+    }
147
+
148
+    data = SecCertificateCopyData(certificate);
149
+    if (data == NULL)
150
+        err(1, "SecCertificateCopyData() returned NULL");
151
+
152
+    cert = CFDataGetBytePtr(data);
153
+    cert_len = CFDataGetLength(data);
154
+
155
+    openvpn_base64_encode(cert, cert_len, &result_b64);
156
+#if 0
157
+    fprintf(stderr, "certificate %s\n", result_b64);
158
+#endif
159
+
160
+    fprintf(man_file, "certificate\n");
161
+    fprintf(man_file, "-----BEGIN CERTIFICATE-----\n");
162
+    tmp_b64 = result_b64;
163
+    while (strlen(tmp_b64) > 64) {
164
+      fprintf(man_file, "%.64s\n", tmp_b64);
165
+      tmp_b64 += 64;
166
+    }
167
+    if (*tmp_b64)
168
+      fprintf(man_file, "%s\n", tmp_b64);
169
+    fprintf(man_file, "-----END CERTIFICATE-----\n");
170
+    fprintf(man_file, "END\n");
171
+
172
+    free(result_b64);
173
+    CFRelease(data);
174
+    CFRelease(certificate);
175
+
176
+    fprintf(stderr, "Handled NEED 'cert' command\n");
177
+}
178
+
179
+void management_loop(SecIdentityRef identity, int man_fd, const char *password)
180
+{
181
+    char *buffer = NULL;
182
+    size_t buffer_len = 0;
183
+    FILE *man = fdopen(man_fd, "w+");
184
+    if (man == 0)
185
+        err(1, "fdopen");
186
+
187
+    if (password)
188
+        fprintf(man, "%s\n", password);
189
+
190
+    while (1) {
191
+        if (getline(&buffer, &buffer_len, man) < 0)
192
+            err(1, "getline");
193
+#if 0
194
+        fprintf(stderr, "M: %s", buffer);
195
+#endif
196
+
197
+        if (is_prefix(buffer, ">RSA_SIGN:"))
198
+            handle_rsasign(man, identity, buffer);
199
+        if (is_prefix(buffer, ">NEED-CERTIFICATE")) {
200
+            if (!identity) {
201
+                const char prefix[] = ">NEED-CERTIFICATE:macosx-keychain:";
202
+                if (!is_prefix(buffer, prefix))
203
+                  errx(1, "No identity template is passed via command line and " \
204
+                      "NEED-CERTIFICATE management interface command " \
205
+                      "misses 'macosx-keychain' prefix.");
206
+                identity = template_to_identity(buffer+strlen(prefix));
207
+            }
208
+            handle_needcertificate(man, identity);
209
+        }
210
+        if (is_prefix(buffer, ">FATAL"))
211
+            fprintf(stderr, "Fatal message from OpenVPN: %s\n", buffer+7);
212
+        if (is_prefix(buffer, ">INFO"))
213
+            fprintf(stderr, "INFO message from OpenVPN: %s\n", buffer+6);
214
+    }
215
+}
216
+
217
+char *read_password(const char *fname)
218
+{
219
+    char *password = NULL;
220
+    FILE *pwf = fopen(fname, "r");
221
+    size_t n = 0;
222
+
223
+    if (pwf == NULL)
224
+        errx(1, "fopen(%s) failed", fname);
225
+    if (getline(&password, &n, pwf) < 0)
226
+        err(1, "getline");
227
+    fclose(pwf);
228
+    return password;
229
+}
230
+
231
+int main(int argc, char* argv[])
232
+{
233
+    if (argc < 4)
234
+        err(1, "usage: %s <identity_template> <management_ip> <management_port> [<pw-file>]", argv[0]);
235
+
236
+    char *identity_template = argv[1];
237
+    char *s_ip = argv[2];
238
+    char *s_port = argv[3];
239
+    char *password = NULL;
240
+    int man_fd;
241
+
242
+    if (argc > 4) {
243
+        char *s_pw_file = argv[4];
244
+        password = read_password(s_pw_file);
245
+    }
246
+
247
+    SecIdentityRef identity = NULL;
248
+    if (strcmp(identity_template, "auto"))
249
+        identity = template_to_identity(identity_template);
250
+    man_fd = connect_to_management_server(s_ip, s_port);
251
+    fprintf(stderr, "Successfully connected to openvpn\n");
252
+
253
+    management_loop(identity, man_fd, password);
254
+}
... ...
@@ -777,6 +777,28 @@ correct signature.
777 777
 This capability is intended to allow the use of arbitrary cryptographic
778 778
 service providers with OpenVPN via the management interface.
779 779
 
780
+COMMAND -- certificate (OpenVPN 2.4 or higher)
781
+----------------------------------------------
782
+Provides support for external storage of the certificate. Requires the
783
+--management-external-cert option. This option can be used instead of "cert"
784
+in client mode. On SSL protocol initialization a notification will be sent
785
+to the management interface with a hint as follows:
786
+
787
+>NEED-CERTIFICATE:macosx-keychain:subject:o=OpenVPN-TEST
788
+
789
+The management interface client should use the hint to obtain the specific
790
+SSL certificate and then return base64 encoded certificate as follows:
791
+
792
+certificate
793
+[BASE64_CERT_LINE]
794
+.
795
+.
796
+.
797
+END
798
+
799
+This capability is intended to allow the use of certificates
800
+stored outside of the filesystem (e.g. in Mac OS X Keychain)
801
+with OpenVPN via the management interface.
780 802
 
781 803
 OUTPUT FORMAT
782 804
 -------------
... ...
@@ -2591,6 +2591,15 @@ Allows usage for external private key file instead of
2591 2591
 option (client-only).
2592 2592
 .\"*********************************************************
2593 2593
 .TP
2594
+.B \-\-management-external-cert certificate-hint
2595
+Allows usage for external certificate instead of
2596
+.B \-\-cert
2597
+option (client-only).
2598
+.B certificate-hint
2599
+is an arbitrary string which is passed to a management
2600
+interface client as an argument of NEED-CERTIFICATE notification.
2601
+.\"*********************************************************
2602
+.TP
2594 2603
 .B \-\-management-forget-disconnect
2595 2604
 Make OpenVPN forget passwords when management session
2596 2605
 disconnects.
... ...
@@ -1066,8 +1066,10 @@ buffer_list_peek (struct buffer_list *ol)
1066 1066
 }
1067 1067
 
1068 1068
 void
1069
-buffer_list_aggregate (struct buffer_list *bl, const size_t max)
1069
+buffer_list_aggregate_separator (struct buffer_list *bl, const size_t max, const char *sep)
1070 1070
 {
1071
+  int sep_len = strlen(sep);
1072
+
1071 1073
   if (bl->head)
1072 1074
     {
1073 1075
       struct buffer_entry *more = bl->head;
... ...
@@ -1075,7 +1077,7 @@ buffer_list_aggregate (struct buffer_list *bl, const size_t max)
1075 1075
       int count = 0;
1076 1076
       for (count = 0; more && size <= max; ++count)
1077 1077
 	{
1078
-	  size += BLEN(&more->buf);
1078
+	  size += BLEN(&more->buf) + sep_len;
1079 1079
 	  more = more->next;
1080 1080
 	}
1081 1081
 
... ...
@@ -1092,6 +1094,7 @@ buffer_list_aggregate (struct buffer_list *bl, const size_t max)
1092 1092
 	    {
1093 1093
 	      struct buffer_entry *next = e->next;
1094 1094
 	      buf_copy (&f->buf, &e->buf);
1095
+	      buf_write(&f->buf, sep, sep_len);
1095 1096
 	      free_buf (&e->buf);
1096 1097
 	      free (e);
1097 1098
 	      e = next;
... ...
@@ -1105,6 +1108,12 @@ buffer_list_aggregate (struct buffer_list *bl, const size_t max)
1105 1105
 }
1106 1106
 
1107 1107
 void
1108
+buffer_list_aggregate (struct buffer_list *bl, const size_t max)
1109
+{
1110
+  buffer_list_aggregate_separator(bl, max, "");
1111
+}
1112
+
1113
+void
1108 1114
 buffer_list_pop (struct buffer_list *ol)
1109 1115
 {
1110 1116
   if (ol && ol->head)
... ...
@@ -931,6 +931,7 @@ void buffer_list_advance (struct buffer_list *ol, int n);
931 931
 void buffer_list_pop (struct buffer_list *ol);
932 932
 
933 933
 void buffer_list_aggregate (struct buffer_list *bl, const size_t max);
934
+void buffer_list_aggregate_separator (struct buffer_list *bl, const size_t max, const char *sep);
934 935
 
935 936
 struct buffer_list *buffer_list_file (const char *fn, int max_line_len);
936 937
 #endif /* BUFFER_H */
... ...
@@ -113,6 +113,8 @@ man_help ()
113 113
 #ifdef MANAGMENT_EXTERNAL_KEY
114 114
   msg (M_CLIENT, "rsa-sig                : Enter an RSA signature in response to >RSA_SIGN challenge");
115 115
   msg (M_CLIENT, "                         Enter signature base64 on subsequent lines followed by END");
116
+  msg (M_CLIENT, "certificate            : Enter a client certificate in response to >NEED-CERT challenge");
117
+  msg (M_CLIENT, "                         Enter certificate base64 on subsequent lines followed by END");
116 118
 #endif
117 119
   msg (M_CLIENT, "signal s               : Send signal s to daemon,");
118 120
   msg (M_CLIENT, "                         s = SIGHUP|SIGTERM|SIGUSR1|SIGUSR2.");
... ...
@@ -868,6 +870,12 @@ in_extra_dispatch (struct management *man)
868 868
       man->connection.ext_key_input = man->connection.in_extra;
869 869
       man->connection.in_extra = NULL;
870 870
       return;
871
+    case IEC_CERTIFICATE:
872
+      man->connection.ext_cert_state = EKS_READY;
873
+      buffer_list_free (man->connection.ext_cert_input);
874
+      man->connection.ext_cert_input = man->connection.in_extra;
875
+      man->connection.in_extra = NULL;
876
+      return;
871 877
 #endif
872 878
     }
873 879
    in_extra_reset (&man->connection, IER_RESET);
... ...
@@ -1030,6 +1038,20 @@ man_rsa_sig (struct management *man)
1030 1030
     msg (M_CLIENT, "ERROR: The rsa-sig command is not currently available");
1031 1031
 }
1032 1032
 
1033
+static void
1034
+man_certificate (struct management *man)
1035
+{
1036
+  struct man_connection *mc = &man->connection;
1037
+  if (mc->ext_cert_state == EKS_SOLICIT)
1038
+    {
1039
+      mc->ext_cert_state = EKS_INPUT;
1040
+      mc->in_extra_cmd = IEC_CERTIFICATE;
1041
+      in_extra_reset (mc, IER_NEW);
1042
+    }
1043
+  else
1044
+    msg (M_CLIENT, "ERROR: The certificate command is not currently available");
1045
+}
1046
+
1033 1047
 #endif
1034 1048
 
1035 1049
 static void
... ...
@@ -1311,6 +1333,10 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch
1311 1311
     {
1312 1312
       man_rsa_sig (man);
1313 1313
     }
1314
+  else if (streq (p[0], "certificate"))
1315
+    {
1316
+      man_certificate (man);
1317
+    }
1314 1318
 #endif
1315 1319
 #ifdef ENABLE_PKCS11
1316 1320
   else if (streq (p[0], "pkcs11-id-count"))
... ...
@@ -3097,15 +3123,14 @@ management_query_user_pass (struct management *man,
3097 3097
 
3098 3098
 #ifdef MANAGMENT_EXTERNAL_KEY
3099 3099
 
3100
-char * /* returns allocated base64 signature */
3101
-management_query_rsa_sig (struct management *man,
3102
-			  const char *b64_data)
3100
+int
3101
+management_query_multiline (struct management *man,
3102
+			  const char *b64_data, const char *prompt, const char *cmd, int *state, struct buffer_list **input)
3103 3103
 {
3104 3104
   struct gc_arena gc = gc_new ();
3105
-  char *ret = NULL;
3105
+  int ret = 0;
3106 3106
   volatile int signal_received = 0;
3107 3107
   struct buffer alert_msg = clear_buf();
3108
-  struct buffer *buf;
3109 3108
   const bool standalone_disabled_save = man->persist.standalone_disabled;
3110 3109
   struct man_connection *mc = &man->connection;
3111 3110
 
... ...
@@ -3114,10 +3139,15 @@ management_query_rsa_sig (struct management *man,
3114 3114
       man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
3115 3115
       man->persist.special_state_msg = NULL;
3116 3116
 
3117
-      mc->ext_key_state = EKS_SOLICIT;
3117
+      *state = EKS_SOLICIT;
3118 3118
 
3119
-      alert_msg = alloc_buf_gc (strlen(b64_data)+64, &gc);
3120
-      buf_printf (&alert_msg, ">RSA_SIGN:%s", b64_data);
3119
+      if (b64_data) {
3120
+        alert_msg = alloc_buf_gc (strlen(b64_data)+strlen(prompt)+3, &gc);
3121
+        buf_printf (&alert_msg, ">%s:%s", prompt, b64_data);
3122
+      } else {
3123
+        alert_msg = alloc_buf_gc (strlen(prompt)+3, &gc);
3124
+        buf_printf (&alert_msg, ">%s", prompt);
3125
+      }
3121 3126
 
3122 3127
       man_wait_for_client_connection (man, &signal_received, 0, MWCC_OTHER_WAIT);
3123 3128
 
... ...
@@ -3135,40 +3165,107 @@ management_query_rsa_sig (struct management *man,
3135 3135
 	    man_check_for_signals (&signal_received);
3136 3136
 	  if (signal_received)
3137 3137
 	    goto done;
3138
-	} while (mc->ext_key_state != EKS_READY);
3138
+	} while (*state != EKS_READY);
3139 3139
 
3140
-      if (buffer_list_defined(mc->ext_key_input))
3141
-	{
3142
-	  buffer_list_aggregate (mc->ext_key_input, 2048);
3143
-	  buf = buffer_list_peek (mc->ext_key_input);
3144
-	  if (buf && BLEN(buf) > 0)
3145
-	    {
3146
-	      ret = (char *) malloc(BLEN(buf)+1);
3147
-	      check_malloc_return(ret);
3148
-	      memcpy(ret, buf->data, BLEN(buf));
3149
-	      ret[BLEN(buf)] = '\0';
3150
-	    }
3151
-	}
3140
+      ret = 1;
3152 3141
     }
3153 3142
 
3154 3143
  done:
3155
-  if (mc->ext_key_state == EKS_READY && ret)
3156
-    msg (M_CLIENT, "SUCCESS: rsa-sig command succeeded");
3157
-  else if (mc->ext_key_state == EKS_INPUT || mc->ext_key_state == EKS_READY)
3158
-    msg (M_CLIENT, "ERROR: rsa-sig command failed");
3144
+  if (*state == EKS_READY && ret)
3145
+    msg (M_CLIENT, "SUCCESS: %s command succeeded", cmd);
3146
+  else if (*state == EKS_INPUT || *state == EKS_READY)
3147
+    msg (M_CLIENT, "ERROR: %s command failed", cmd);
3159 3148
 
3160 3149
   /* revert state */
3161 3150
   man->persist.standalone_disabled = standalone_disabled_save;
3162 3151
   man->persist.special_state_msg = NULL;
3163 3152
   in_extra_reset (mc, IER_RESET);
3164
-  mc->ext_key_state = EKS_UNDEF;
3165
-  buffer_list_free (mc->ext_key_input);
3166
-  mc->ext_key_input = NULL;
3153
+  *state = EKS_UNDEF;
3167 3154
 
3168 3155
   gc_free (&gc);
3169 3156
   return ret;
3170 3157
 }
3171 3158
 
3159
+char * /* returns allocated base64 signature */
3160
+management_query_multiline_flatten_newline (struct management *man,
3161
+    const char *b64_data, const char *prompt, const char *cmd, int *state, struct buffer_list **input)
3162
+{
3163
+  int ok;
3164
+  char *result = NULL;
3165
+  struct buffer *buf;
3166
+
3167
+  ok = management_query_multiline(man, b64_data, prompt, cmd, state, input);
3168
+  if (ok && buffer_list_defined(*input))
3169
+  {
3170
+    buffer_list_aggregate_separator (*input, 10000, "\n");
3171
+    buf = buffer_list_peek (*input);
3172
+    if (buf && BLEN(buf) > 0)
3173
+    {
3174
+      result = (char *) malloc(BLEN(buf)+1);
3175
+      check_malloc_return(result);
3176
+      memcpy(result, buf->data, BLEN(buf));
3177
+      result[BLEN(buf)] = '\0';
3178
+    }
3179
+  }
3180
+
3181
+  buffer_list_free (*input);
3182
+  *input = NULL;
3183
+
3184
+  return result;
3185
+}
3186
+
3187
+char * /* returns allocated base64 signature */
3188
+management_query_multiline_flatten (struct management *man,
3189
+    const char *b64_data, const char *prompt, const char *cmd, int *state, struct buffer_list **input)
3190
+{
3191
+  int ok;
3192
+  char *result = NULL;
3193
+  struct buffer *buf;
3194
+
3195
+  ok = management_query_multiline(man, b64_data, prompt, cmd, state, input);
3196
+  if (ok && buffer_list_defined(*input))
3197
+  {
3198
+    buffer_list_aggregate (*input, 2048);
3199
+    buf = buffer_list_peek (*input);
3200
+    if (buf && BLEN(buf) > 0)
3201
+    {
3202
+      result = (char *) malloc(BLEN(buf)+1);
3203
+      check_malloc_return(result);
3204
+      memcpy(result, buf->data, BLEN(buf));
3205
+      result[BLEN(buf)] = '\0';
3206
+    }
3207
+  }
3208
+
3209
+  buffer_list_free (*input);
3210
+  *input = NULL;
3211
+
3212
+  return result;
3213
+}
3214
+
3215
+char * /* returns allocated base64 signature */
3216
+management_query_rsa_sig (struct management *man,
3217
+			  const char *b64_data)
3218
+{
3219
+  return management_query_multiline_flatten(man, b64_data, "RSA_SIGN", "rsa-sign",
3220
+      &man->connection.ext_key_state, &man->connection.ext_key_input);
3221
+}
3222
+
3223
+
3224
+char* management_query_cert (struct management *man, const char *cert_name)
3225
+{
3226
+  const char prompt_1[] = "NEED-CERTIFICATE:";
3227
+  struct buffer buf_prompt = alloc_buf(strlen(cert_name) + 20);
3228
+  buf_write(&buf_prompt, prompt_1, strlen(prompt_1));
3229
+  buf_write(&buf_prompt, cert_name, strlen(cert_name)+1); // +1 for \0
3230
+
3231
+  char *result;
3232
+  result = management_query_multiline_flatten_newline(management,
3233
+      NULL, (char*)buf_bptr(&buf_prompt), "certificate",
3234
+      &man->connection.ext_cert_state, &man->connection.ext_cert_input);
3235
+  free_buf(&buf_prompt);
3236
+  return result;
3237
+}
3238
+
3172 3239
 #endif
3173 3240
 
3174 3241
 /*
... ...
@@ -268,6 +268,7 @@ struct man_connection {
268 268
 # define IEC_CLIENT_AUTH 1
269 269
 # define IEC_CLIENT_PF   2
270 270
 # define IEC_RSA_SIGN    3
271
+# define IEC_CERTIFICATE 4
271 272
   int in_extra_cmd;
272 273
   struct buffer_list *in_extra;
273 274
 #ifdef MANAGEMENT_DEF_AUTH
... ...
@@ -281,6 +282,8 @@ struct man_connection {
281 281
 # define EKS_READY   3
282 282
   int ext_key_state;
283 283
   struct buffer_list *ext_key_input;
284
+  int ext_cert_state;
285
+  struct buffer_list *ext_cert_input;
284 286
 #endif
285 287
 #endif
286 288
   struct event_set *es;
... ...
@@ -338,6 +341,7 @@ struct management *management_init (void);
338 338
 #define MF_UP_DOWN          (1<<10)
339 339
 #define MF_QUERY_REMOTE     (1<<11)
340 340
 #define MF_QUERY_PROXY      (1<<12)
341
+#define MF_EXTERNAL_CERT    (1<<13)
341 342
 
342 343
 bool management_open (struct management *man,
343 344
 		      const char *addr,
... ...
@@ -420,6 +424,7 @@ void management_learn_addr (struct management *management,
420 420
 #ifdef MANAGMENT_EXTERNAL_KEY
421 421
 
422 422
 char *management_query_rsa_sig (struct management *man, const char *b64_data);
423
+char* management_query_cert (struct management *man, const char *cert_name);
423 424
 
424 425
 #endif
425 426
 
... ...
@@ -1576,6 +1576,11 @@ show_settings (const struct options *o)
1576 1576
   SHOW_STR (ca_file);
1577 1577
   SHOW_STR (ca_path);
1578 1578
   SHOW_STR (dh_file);
1579
+#ifdef MANAGMENT_EXTERNAL_KEY
1580
+  if((o->management_flags & MF_EXTERNAL_CERT))
1581
+	SHOW_PARM ("cert_file","EXTERNAL_CERT","%s");
1582
+  else
1583
+#endif
1579 1584
   SHOW_STR (cert_file);
1580 1585
 
1581 1586
 #ifdef MANAGMENT_EXTERNAL_KEY
... ...
@@ -2152,6 +2157,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
2152 2152
 #ifdef MANAGMENT_EXTERNAL_KEY
2153 2153
 	if (options->management_flags & MF_EXTERNAL_KEY)
2154 2154
 	  msg(M_USAGE, "Parameter --management-external-key cannot be used when --pkcs11-provider is also specified.");
2155
+	if (options->management_flags & MF_EXTERNAL_CERT)
2156
+	  msg(M_USAGE, "Parameter --management-external-cert cannot be used when --pkcs11-provider is also specified.");
2155 2157
 #endif
2156 2158
 	if (options->pkcs12_file)
2157 2159
 	  msg(M_USAGE, "Parameter --pkcs12 cannot be used when --pkcs11-provider is also specified.");
... ...
@@ -2183,6 +2190,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
2183 2183
 #ifdef MANAGMENT_EXTERNAL_KEY
2184 2184
           if (options->management_flags & MF_EXTERNAL_KEY)
2185 2185
 	    msg(M_USAGE, "Parameter --management-external-key cannot be used when --cryptoapicert is also specified.");
2186
+          if (options->management_flags & MF_EXTERNAL_CERT)
2187
+	    msg(M_USAGE, "Parameter --management-external-cert cannot be used when --cryptoapicert is also specified.");
2186 2188
 #endif
2187 2189
 	}
2188 2190
       else
... ...
@@ -2200,7 +2209,9 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
2200 2200
 	    msg(M_USAGE, "Parameter --key cannot be used when --pkcs12 is also specified.");
2201 2201
 #ifdef MANAGMENT_EXTERNAL_KEY
2202 2202
           if (options->management_flags & MF_EXTERNAL_KEY)
2203
-	    msg(M_USAGE, "Parameter --external-management-key cannot be used when --pkcs12 is also specified.");
2203
+	    msg(M_USAGE, "Parameter --management-external-key cannot be used when --pkcs12 is also specified.");
2204
+          if (options->management_flags & MF_EXTERNAL_CERT)
2205
+	    msg(M_USAGE, "Parameter --management-external-cert cannot be used when --pkcs12 is also specified.");
2204 2206
 #endif
2205 2207
 #endif
2206 2208
         }
... ...
@@ -2242,6 +2253,9 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
2242 2242
 	    }
2243 2243
 	  else
2244 2244
 	    {
2245
+#ifdef MANAGMENT_EXTERNAL_KEY
2246
+          if (!(options->management_flags & MF_EXTERNAL_CERT))
2247
+#endif
2245 2248
 	      notnull (options->cert_file, "certificate file (--cert) or PKCS#12 file (--pkcs12)");
2246 2249
 #ifdef MANAGMENT_EXTERNAL_KEY
2247 2250
           if (!(options->management_flags & MF_EXTERNAL_KEY))
... ...
@@ -4244,6 +4258,12 @@ add_option (struct options *options,
4244 4244
       VERIFY_PERMISSION (OPT_P_GENERAL);
4245 4245
       options->management_flags |= MF_EXTERNAL_KEY;
4246 4246
     }
4247
+  else if (streq (p[0], "management-external-cert") && p[1])
4248
+    {
4249
+      VERIFY_PERMISSION (OPT_P_GENERAL);
4250
+      options->management_flags |= MF_EXTERNAL_CERT;
4251
+      options->management_certificate = p[1];
4252
+    }
4247 4253
 #endif
4248 4254
 #ifdef MANAGEMENT_DEF_AUTH
4249 4255
   else if (streq (p[0], "management-client-auth"))
... ...
@@ -370,6 +370,7 @@ struct options
370 370
 
371 371
   /* Mask of MF_ values of manage.h */
372 372
   unsigned int management_flags;
373
+  const char *management_certificate;
373 374
 #endif
374 375
 
375 376
 #ifdef ENABLE_PLUGIN
... ...
@@ -520,10 +520,19 @@ init_ssl (const struct options *options, struct tls_root_ctx *new_ctx)
520 520
     }
521 521
 #endif
522 522
 #ifdef MANAGMENT_EXTERNAL_KEY
523
-  else if ((options->management_flags & MF_EXTERNAL_KEY) && options->cert_file)
524
-    {
525
-      tls_ctx_use_external_private_key(new_ctx, options->cert_file,
526
-	  options->cert_file_inline);
523
+  else if ((options->management_flags & MF_EXTERNAL_KEY) &&
524
+           (options->cert_file || options->management_flags & MF_EXTERNAL_CERT))
525
+    {
526
+      if (options->cert_file) {
527
+        tls_ctx_use_external_private_key(new_ctx, options->cert_file,
528
+          options->cert_file_inline);
529
+      } else {
530
+        char *external_certificate = management_query_cert(management,
531
+            options->management_certificate);
532
+        tls_ctx_use_external_private_key(new_ctx, INLINE_FILE_TAG,
533
+            external_certificate);
534
+        free(external_certificate);
535
+      }
527 536
     }
528 537
 #endif
529 538
   else