ChangeLog/patches: update Git 2.7.2 -> 2.7.4
[git-osx-installer.git] / patches / curl / stcompat.c
blob418acddb53ef01766b23a44ab084a27dd489b41b
1 /*
3 stcompat.c -- SecureTransport compatibility implementation
4 Copyright (C) 2014,2015 Kyle J. McKay. All rights reserved.
6 If this software is included as part of a build of
7 the cURL library, it may be used under the same license
8 terms as the cURL library.
10 Otherwise the GPLv2 license applies, see
11 http://www.gnu.org/licenses/gpl-2.0-standalone.html
13 This software 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.
19 #undef sprintf
20 #include <Security/Security.h>
21 #include <CommonCrypto/CommonDigest.h>
22 #include <limits.h>
23 #include <objc/objc-runtime.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <sys/types.h>
29 #include <sys/uio.h>
30 #include <unistd.h>
31 #include <dirent.h>
32 #include <arpa/inet.h>
33 #include <pwd.h>
34 #include <crt_externs.h>
35 #include <pthread.h>
36 #include "stcompat.h"
38 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
39 #include <dlfcn.h>
40 __attribute__((constructor,used)) static void stcompat_initialize(void);
41 #endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
43 typedef struct data_s {
44 const uint8_t *d;
45 size_t l;
46 } data_t;
48 extern CFStringRef CFStringCreateWithBytesNoCopy(
49 CFAllocatorRef alloc,
50 const UInt8 *bytes,
51 CFIndex numBytes,
52 CFStringEncoding encoding,
53 Boolean isExternalRepresentation,
54 CFAllocatorRef contentsDeallocator); /* available 10.4 but not in header */
55 extern CFStringRef NSTemporaryDirectory(void);
56 static Boolean CheckPubKeyOkayInt(CFDataRef d, data_t *pubkey, int flags);
57 static pthread_mutex_t keych_mutex = PTHREAD_MUTEX_INITIALIZER;
59 /* Macro result is true on success */
60 #define MUTEX_LOCK() (!pthread_mutex_lock(&keych_mutex))
61 #define MUTEX_UNLOCK() (!pthread_mutex_unlock(&keych_mutex))
63 static CFStringRef CFCopyTemporaryDirectory(void)
65 id pool = objc_msgSend(objc_getClass("NSAutoreleasePool"), sel_getUid("new"));
66 CFStringRef dir = (CFStringRef)NSTemporaryDirectory();
67 if (dir) CFRetain(dir);
68 objc_msgSend(pool, sel_getUid("drain"));
69 if (dir) {
70 unsigned len = (unsigned)CFStringGetLength(dir);
71 unsigned l = len;
72 while (l > 1 &&
73 (unsigned)CFStringGetCharacterAtIndex(dir, l-1) == (unsigned)'/') {
74 --l;
76 if (l < len) {
77 CFStringRef old = dir;
78 dir = CFStringCreateWithSubstring(
79 kCFAllocatorDefault, dir, CFRangeMake(0, l));
80 if (dir)
81 CFRelease(old);
82 else
83 dir = old;
86 return dir;
89 char *CFStringCreateUTF8String(CFStringRef s, Boolean release)
91 size_t m;
92 char *c;
94 if (!s) return NULL;
95 m = (size_t)CFStringGetMaximumSizeForEncoding(
96 CFStringGetLength(s), kCFStringEncodingUTF8) + 1;
97 c = (char *)malloc(m);
98 if (!c) {
99 if (release) CFRelease(s);
100 return NULL;
102 if (!CFStringGetCString(s, c, m, kCFStringEncodingUTF8)) {
103 free(c);
104 c = NULL;
106 if (release) CFRelease(s);
107 return c;
110 static CFStringRef MakeVisibleString(CFStringRef in)
112 CFStringRef nullbyte;
113 CFMutableStringRef m;
114 if (!in) return in;
115 nullbyte = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, (UInt8 *)"\0",
116 1, kCFStringEncodingASCII, false, kCFAllocatorNull);
117 if (!nullbyte) return in;
118 m = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, in);
119 if (!m) return in;
120 CFRelease(in);
121 CFStringFindAndReplace(m, nullbyte, CFSTR("\\000"),
122 CFRangeMake(0, CFStringGetLength(m)), 0);
123 CFRelease(nullbyte);
124 return m;
127 CFDataRef CFDataCreateWithContentsOfFile(CFAllocatorRef a, const char *f)
129 char buff[4096];
130 CFMutableDataRef d = CFDataCreateMutable(a, 0);
131 int fd;
132 ssize_t cnt;
133 if (!d) return NULL;
134 fd = open(f, O_RDONLY);
135 if (fd < 0) {
136 CFRelease(d);
137 return NULL;
139 do {
140 cnt = read(fd, buff, sizeof(buff));
141 if (cnt > 0) CFDataAppendBytes(d, (UInt8 *)buff, (size_t)cnt);
142 } while (cnt > 0);
143 close(fd);
144 if (cnt) {
145 CFRelease(d);
146 return NULL;
148 return d;
151 #undef memmem
152 #define memmem(v1,l1,v2,l2) cmemmem(v1,l1,v2,l2)
153 static void *cmemmem(const void *_m, size_t ml, const void *_s, size_t sl)
155 const char *m = (const char *)_m;
156 const char *s = (const char *)_s;
157 if (!ml || !sl || ml < sl) return NULL;
158 if (sl == 1) return memchr(m, *s, ml);
159 if (ml == sl) return (void *)(memcmp(m, s, sl) ? NULL : m);
160 do {
161 size_t o;
162 const char *p = memchr(m, *s, ml);
163 if (!p) return NULL;
164 o = p - m;
165 ml -= o;
166 m += o;
167 if (ml < sl) return NULL;
168 if (!memcmp(m, s, sl)) return (void *)m;
169 ++m;
170 --ml;
171 } while (ml >= sl);
172 return NULL;
175 CF_INLINE int is_eol(int c)
177 return c == '\n' || c == '\r';
180 CF_INLINE int is_lb(int c)
182 return c == '\n' || c == '\r' || c == '\f';
185 CF_INLINE int is_prnt(int c)
187 return c >= ' ' && c <= '~';
190 CF_INLINE int is_spc(int c)
192 return c == ' ' || c == '\t';
195 static int has_lb(const void *_m, size_t l)
197 const char *m = (const char *)_m;
198 while (l) {
199 if (is_lb(*m)) return 1;
200 --l;
201 ++m;
203 return 0;
206 static int has_prnt(const void *_m, size_t l)
208 const char *m = (const char *)_m;
209 while (l) {
210 if (!is_prnt(*m)) return 0;
211 --l;
212 ++m;
214 return 1;
218 * returns start of "-----BEGIN XXXX-----\n" line or NULL if not found/error
219 * If returns NULL then *ot == 0 means not found, *ot == -1 means bad line
220 * If returns non-NULL then *ol is length through "-----\n" and *ot is length
221 * of "XXXX" part (which obviously starts at return value + 11 for BEGIN or
222 * value + 9 for END)
223 * If e is non-zero look for ----END rather than ----BEGIN
225 static const char *find_be(const void *_m, size_t l, size_t *ol, int *ot, int e)
227 const char *m = (const char *)_m;
228 const char *origm = m;
229 const char *marker = e ? "-----END " : "-----BEGIN ";
230 size_t mkl = e ? 9 : 11;
231 *ot = 0;
232 while (l) {
233 const char *t;
234 const char *p = (char *)memmem(m, l, marker, mkl);
235 if (!p) return NULL;
236 l -= (p - m) + mkl;
237 m = p + mkl;
238 if (p > origm && !is_eol(p[-1])) continue;
239 t = (char *)memmem(m, l, "-----", 5);
240 if (!t) return NULL;
241 l -= (t - m);
242 if (l > 5 && !is_eol(t[5])) continue;
243 if (has_lb(p, t-p)) continue;
244 if ((size_t)(t-p) > (76 - mkl - 5) || !has_prnt(p, t-p)) {
245 *ot = -1;
246 return NULL;
248 *ot = (int)(t - m);
249 l -= 5;
250 m = t + 5;
251 if (l && *m == '\r') {
252 ++m;
253 --l;
255 if (l && *m == '\n') {
256 ++m;
257 --l;
259 *ol = m - p;
260 return p;
262 return NULL;
265 typedef enum pemtype_e {
266 pemtype_unknown,
267 pemtype_certificate, /* "CERTIFICATE" or "TRUSTED CERTIFICATE" or "X509 CERTIFICATE" */
268 pemtype_crl, /* "X509 CRL" */
269 pemtype_publickey, /* "PUBLIC KEY" */
270 pemtype_privatekey_rsa /* "RSA PRIVATE KEY" */
271 } pemtype_t;
273 typedef struct peminfo_s {
274 const char *start; /* Armour start "-----BEGIN XXXXX-----\n" */
275 size_t len; /* Length through armour end "-----END XXXXX-----\n" */
276 /* Body starts after "-----BEGIN XXXXX-----\n" */
277 const char *body;
278 size_t bodylen; /* Length though "\n" BEFORE final "-----END XXXXX-----\n" */
279 /* Kind starts at start + 11 */
280 size_t kindlen; /* length of "XXXXX" from "-----BEGIN XXXXX-----\n" */
281 pemtype_t type;
282 } peminfo_t;
284 static int nextpem(const char *p, size_t l, peminfo_t *o)
286 size_t beglen, endlen;
287 int begtype, endtype;
288 const char *end;
289 const char *beg = find_be(p, l, &beglen, &begtype, 0);
290 if (!beg) return begtype;
291 end = find_be(p + beglen, l - beglen, &endlen, &endtype, 1);
292 if (!end || begtype != endtype || memcmp(beg+11, end+9, (size_t)begtype))
293 return -1;
294 o->start = beg;
295 o->len = (end + endlen) - beg;
296 o->body = beg + beglen;
297 o->bodylen = end - (beg + beglen);
298 o->kindlen = (size_t)begtype;
299 if (begtype == 11 && !memcmp(beg + 11, "CERTIFICATE", 11)) {
300 o->type = pemtype_certificate;
301 } else if (begtype == 19 && !memcmp(beg + 11, "TRUSTED CERTIFICATE", 19)) {
302 o->type = pemtype_certificate;
303 } else if (begtype == 16 && !memcmp(beg + 11, "X509 CERTIFICATE", 16)) {
304 o->type = pemtype_certificate;
305 } else if (begtype == 15 && !memcmp(beg + 11, "RSA PRIVATE KEY", 15)) {
306 o->type = pemtype_privatekey_rsa;
307 } else if (begtype == 10 && !memcmp(beg + 11, "PUBLIC KEY", 10)) {
308 o->type = pemtype_publickey;
309 } else if (begtype == 8 && !memcmp(beg + 11, "X509 CRL", 8)) {
310 o->type = pemtype_crl;
311 } else {
312 o->type = pemtype_unknown;
314 return (int)((o->start + o->len) - p);
317 #define BASE64CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
318 #define BASE64SIZE(n) ((((unsigned)(n)+2)/3)*4)
320 static const signed char b64tab[256] = {
321 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
322 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
323 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x3E,-1,-1,-1,0x3F,
324 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,-1,-1,-1,0x40,-1,-1,
325 -1,0,1,2,3,4,5,6,7,8,9,0x0A,0x0B,0x0C,0x0D,0x0E,
326 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,-1,-1,-1,-1,-1,
327 -1,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
328 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,-1,-1,-1,-1,-1,
329 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
330 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
331 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
332 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
333 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
334 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
335 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
336 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
339 static void convert24(const uint8_t *input, uint8_t *output)
341 output[0] = ((b64tab[input[0]]&0x3F)<<2)|((b64tab[input[1]]&0x3F)>>4);
342 output[1] = (((b64tab[input[1]]&0x3F)&0x0F)<<4)|((b64tab[input[2]]&0x3F)>>2);
343 output[2] = (((b64tab[input[2]]&0x3F)&0x03)<<6)|(b64tab[input[3]]&0x3F);
346 static CFDataRef CFDataCreateFromBase64(CFAllocatorRef a, const void *_b, size_t l)
348 uint8_t inp[4];
349 uint8_t out[3];
350 int i;
351 CFMutableDataRef d;
352 const uint8_t *p = (uint8_t *)_b;
353 if (l && !p) return NULL;
354 d = CFDataCreateMutable(a, 0);
355 if (!d) return NULL;
356 if (!l) return d;
357 for (i=0; l; ++p, --l) {
358 uint8_t c = *p;
359 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f')
360 continue;
361 if (b64tab[c] < 0) {
362 CFRelease(d);
363 return NULL;
365 inp[i++] = c;
366 if (i == 4) {
367 convert24(inp, out);
368 i = 0;
369 if (inp[3] == '=') {
370 CFDataAppendBytes(d, out, inp[2] == '=' ? 1 : 2);
371 break;
373 CFDataAppendBytes(d, out, 3);
376 if (i != 0) {
377 CFRelease(d);
378 return NULL;
380 return d;
383 static SecCertificateRef createvalidcert(CFDataRef d)
385 SecCertificateRef cert = cSecCertificateCreateWithData(kCFAllocatorDefault, d);
386 if (!cert) return NULL;
387 if (!CheckCertOkay(cert)) {
388 CFRelease(cert);
389 return NULL;
391 return cert;
394 CFArrayRef CreateCertsArrayWithData(CFDataRef d, const errinfo_t *e)
396 const char *certs, *p;
397 size_t certslen, plen, cnt = 1;
398 CFMutableArrayRef a;
399 if (!d) return NULL;
400 certs = (char *)CFDataGetBytePtr(d);
401 certslen = (size_t)CFDataGetLength(d);
402 a = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
403 if (!a) return NULL;
404 p = certs;
405 plen = certslen;
406 while (plen) {
407 peminfo_t pem;
408 int readcnt = nextpem(p, plen, &pem);
409 if (!readcnt && p == certs) {
410 /* assume it's a DER cert */
411 SecCertificateRef cert;
412 CFDataRef der = CFDataCreate(kCFAllocatorDefault, (UInt8 *)certs, certslen);
413 if (!der) {
414 CFRelease(a);
415 return NULL;
417 cert = createvalidcert(der);
418 CFRelease(der);
419 if (!cert) {
420 if (e)
421 e->f(e->u, "Invalid CA certificate bad DER data");
422 CFRelease(a);
423 return NULL;
425 CFArrayAppendValue(a, cert);
426 CFRelease(cert);
427 return a;
428 } else if (readcnt == -1) {
429 if (e)
430 e->f(e->u, "Invalid CA certificate #%u (offset %u) in bundle",
431 (unsigned)cnt, (unsigned)(p-certs));
432 CFRelease(a);
433 return NULL;
434 } else if (readcnt && pem.type == pemtype_certificate) {
435 CFDataRef der = CFDataCreateFromBase64(kCFAllocatorDefault, pem.body, pem.bodylen);
436 SecCertificateRef cert;
437 if (!der) {
438 if (e)
439 e->f(e->u, "Invalid CA certificate #%u (offset %u) bad base 64 in bundle",
440 (unsigned)cnt, (unsigned)(pem.start-certs));
441 CFRelease(a);
442 return NULL;
444 cert = createvalidcert(der);
445 CFRelease(der);
446 if (!cert) {
447 if (e)
448 e->f(e->u, "Invalid CA certificate #%u (offset %u) bad cert data in bundle",
449 (unsigned)cnt, (unsigned)(pem.start-certs));
450 CFRelease(a);
451 return NULL;
453 CFArrayAppendValue(a, cert);
454 CFRelease(cert);
455 ++cnt;
456 } else if (!readcnt) break;
457 plen -= (pem.start + pem.len) - p;
458 p = pem.start + pem.len;
460 if (!CFArrayGetCount(a)) {
461 CFRelease(a);
462 a = NULL;
464 return a;
467 static void CFArrayAppendValue_data(CFMutableArrayRef a, const data_t *d)
469 if (d && d->d && d->l) {
470 CFDataRef data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)d->d, d->l);
471 if (data) {
472 CFArrayAppendValue(a, data);
473 CFRelease(data);
478 CFArrayRef CreatePubKeyArrayWithData(CFDataRef d, const errinfo_t *e)
480 const char *keys, *p;
481 size_t keyslen, plen, cnt = 1;
482 CFMutableArrayRef a;
483 if (!d) return NULL;
484 keys = (char *)CFDataGetBytePtr(d);
485 keyslen = (size_t)CFDataGetLength(d);
486 a = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
487 if (!a) return NULL;
488 p = keys;
489 plen = keyslen;
490 while (plen) {
491 peminfo_t pem;
492 data_t pubkey;
493 int readcnt = nextpem(p, plen, &pem);
494 if (!readcnt && p == keys) {
495 /* assume it's a DER public key */
496 CFDataRef der = CFDataCreate(kCFAllocatorDefault, (UInt8 *)keys, keyslen);
497 if (!der) {
498 CFRelease(a);
499 return NULL;
501 if (!CheckPubKeyOkayInt(der, &pubkey, 0x01)) {
502 CFRelease(der);
503 if (e)
504 e->f(e->u, "Invalid public key bad DER data");
505 CFRelease(a);
506 return NULL;
508 CFArrayAppendValue_data(a, &pubkey);
509 CFRelease(der);
510 return a;
511 } else if (readcnt == -1) {
512 if (e)
513 e->f(e->u, "Invalid public key #%u (offset %u) in bundle",
514 (unsigned)cnt, (unsigned)(p-keys));
515 CFRelease(a);
516 return NULL;
517 } else if (readcnt && (pem.type == pemtype_publickey ||
518 pem.type == pemtype_certificate)) {
519 CFDataRef der = CFDataCreateFromBase64(kCFAllocatorDefault, pem.body, pem.bodylen);
520 if (!der) {
521 if (e)
522 e->f(e->u, "Invalid public key #%u (offset %u) bad base 64 in bundle",
523 (unsigned)cnt, (unsigned)(pem.start-keys));
524 CFRelease(a);
525 return NULL;
527 if (!CheckPubKeyOkayInt(der, &pubkey, 0x01)) {
528 CFRelease(der);
529 if (e)
530 e->f(e->u, "Invalid public key #%u (offset %u) bad public key data in bundle",
531 (unsigned)cnt, (unsigned)(pem.start-keys));
532 CFRelease(a);
533 return NULL;
535 CFArrayAppendValue_data(a, &pubkey);
536 CFRelease(der);
537 ++cnt;
538 } else if (!readcnt) break;
539 plen -= (pem.start + pem.len) - p;
540 p = pem.start + pem.len;
542 if (!CFArrayGetCount(a)) {
543 CFRelease(a);
544 a = NULL;
546 return a;
549 static void skip_space_semi(const char **pstr)
551 while (is_spc(**pstr)) ++(*pstr);
552 if (**pstr == ';') ++(*pstr);
553 while (is_spc(**pstr)) ++(*pstr);
556 static bool skip_sha256_prefix(const char **pstr)
558 skip_space_semi(pstr);
559 if (**pstr != 's' && **pstr != 'S') return 0;
560 ++(*pstr);
561 if (**pstr != 'h' && **pstr != 'H') return 0;
562 ++(*pstr);
563 if (**pstr != 'a' && **pstr != 'A') return 0;
564 ++(*pstr);
565 if (!strncmp(*pstr, "256//", 5)) {
566 *pstr += 5;
567 return 1;
569 return 0;
572 Boolean IsSha256HashList(const char *hashlist)
574 if (!hashlist || !*hashlist)
575 return 0;
576 return skip_sha256_prefix(&hashlist);
579 CFArrayRef CreatePubKeySha256Array(const char *hashlist, const errinfo_t *e)
581 const char *orig = hashlist;
582 CFMutableArrayRef a = NULL;
583 if (!hashlist || !*hashlist)
584 return NULL;
585 a = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
586 if (!a) return NULL;
587 for (;;) {
588 CFDataRef blob;
589 size_t l;
590 if (!skip_sha256_prefix(&hashlist)) {
591 if (e)
592 e->f(e->u, "Invalid public key sha256 base64 hash -- "
593 "missing \"sha256//\" prefix at string offset %u",
594 (unsigned)(hashlist-orig));
595 CFRelease(a);
596 return NULL;
598 l = strspn(hashlist, BASE64CHARS);
599 if (l != BASE64SIZE(CC_SHA256_DIGEST_LENGTH)) {
600 if (e)
601 e->f(e->u, "Invalid public key sha256 base64 hash -- "
602 "wrong base64 length %u (should be %u) at string offset %u",
603 (unsigned)l, (unsigned)BASE64SIZE(CC_SHA256_DIGEST_LENGTH),
604 (unsigned)(hashlist-orig));
605 CFRelease(a);
606 return NULL;
608 blob = CFDataCreateFromBase64(kCFAllocatorDefault, hashlist, l);
609 if (!blob) {
610 if (e)
611 e->f(e->u, "out of memory");
612 CFRelease(a);
613 return NULL;
615 if ((size_t)CFDataGetLength(blob) != (size_t)CC_SHA256_DIGEST_LENGTH) {
616 if (e)
617 e->f(e->u, "Invalid public key sha256 base64 hash -- "
618 "wrong hash length %u (should be %u) at string offset %u",
619 (unsigned)CFDataGetLength(blob), (unsigned)CC_SHA256_DIGEST_LENGTH,
620 (unsigned)(hashlist-orig));
621 CFRelease(blob);
622 CFRelease(a);
623 return NULL;
625 hashlist += l;
626 CFArrayAppendValue(a, blob);
627 CFRelease(blob);
628 skip_space_semi(&hashlist);
629 if (!*hashlist) break;
631 if (!CFArrayGetCount(a)) {
632 CFRelease(a);
633 a = NULL;
635 return a;
638 typedef struct homedirs_s {
639 /* note that we use geteuid and not getuid because any created files will
640 * end up being owned by geteuid and therefore using geteuid()'s HOME wil
641 * end up being the least disruptive and also if geteuid() != getuid() then
642 * we probably can't read getuid()'s HOME anyway so that's a guaranteed fail */
643 char *home; /* "HOME=..." from getpwuid(geteuid()) if different from environ */
644 char *cur_home; /* "HOME=..." as found in environ if home set otherwise NULL */
645 } homedirs_t;
647 static char *find_home_env(void)
649 char ***eptr = _NSGetEnviron();
650 char **ptr;
651 if (!eptr) return NULL;
652 ptr = *eptr;
653 while (*ptr && strncmp(*ptr, "HOME=", 5)) {
654 ++ptr;
656 return *ptr ? *ptr : NULL;
659 static void get_home_dirs(homedirs_t *dirs)
661 struct passwd *pwinf;
663 if (!dirs) return;
664 dirs->home = NULL;
665 dirs->cur_home = find_home_env();
666 pwinf = getpwuid(geteuid());
667 if (pwinf && pwinf->pw_dir &&
668 (!dirs->cur_home || strcmp(dirs->cur_home+5, pwinf->pw_dir)))
669 asprintf(&dirs->home, "HOME=%s", pwinf->pw_dir);
670 if (!dirs->home)
671 dirs->cur_home = NULL;
674 static void free_home_dirs(homedirs_t *dirs)
676 if (dirs)
677 free(dirs->home);
680 typedef struct tempch_s {
681 SecKeychainRef ref;
682 homedirs_t dirs;
683 char pw[16]; /* random 15-character (0x20-0x7e), NULL terminated password */
684 char loc[1]; /* Always will have at least a NULL byte */
685 } tempch_t;
687 static void gen_rand_pw(void *_out, size_t len)
689 unsigned char *out = (unsigned char *)_out;
690 int fd;
691 if (!out || !len) return;
692 fd = open("/dev/random", O_RDONLY);
693 if (fd) {
694 do {
695 ssize_t cnt, i;
696 do {
697 cnt = read(fd, out, len);
698 } while (cnt == -1 && errno == EINTR);
699 if (cnt <= 0) return;
700 for (i = 0; i < cnt; ++i) {
701 out[i] = (unsigned char)((((unsigned)out[i] * 95) >> 8) + 32);
703 len -= (size_t)cnt;
704 } while (len);
705 close(fd);
709 static tempch_t *new_temp_keych(void)
711 tempch_t *ans;
712 char newdir[PATH_MAX];
713 Boolean okay;
714 CFStringRef tempdir = CFCopyTemporaryDirectory();
716 if (!tempdir) return NULL;
717 okay = CFStringGetCString(tempdir, newdir, sizeof(newdir) - 32, kCFStringEncodingUTF8);
718 CFRelease(tempdir);
719 if (!okay) return NULL;
720 strcat(newdir, "/tch.XXXXXX");
721 ans = (tempch_t *)malloc(sizeof(tempch_t) + strlen(newdir) + 14 /* "/temp.keychain" */);
722 if (!ans) return NULL;
723 ans->ref = NULL;
724 strcpy(ans->loc, newdir);
725 strlcpy(ans->pw, "(:vCZ\"t{UA-zl3g", sizeof(ans->pw)); /* fallback if random fails */
726 gen_rand_pw(ans->pw, sizeof(ans->pw)-1);
727 ans->pw[sizeof(ans->pw)-1] = '\0';
728 if (!mkdtemp(ans->loc)) {
729 free(ans);
730 return NULL;
732 strcat(ans->loc, "/temp.keychain");
733 get_home_dirs(&ans->dirs);
734 return ans;
737 static void del_temp_keych(tempch_t *keych)
739 size_t l;
740 if (!keych) return;
741 l = strlen(keych->loc);
742 if (l > 14 && !strcmp(keych->loc + (l - 14), "/temp.keychain")) {
743 DIR *d;
744 if (keych->ref) {
745 int needs_reset = 0;
746 (void)MUTEX_LOCK();
747 if (keych->dirs.home) {
748 keych->dirs.cur_home = find_home_env();
749 if (!keych->dirs.cur_home || strcmp(keych->dirs.cur_home, keych->dirs.home)) {
750 needs_reset = 1;
751 putenv(keych->dirs.home);
754 (void)SecKeychainLock(keych->ref);
755 (void)SecKeychainDelete(keych->ref);
756 if (needs_reset) {
757 if (keych->dirs.cur_home)
758 putenv(keych->dirs.cur_home);
759 else
760 unsetenv("HOME");
762 CFRelease(keych->ref);
763 keych->ref = NULL;
764 (void)MUTEX_UNLOCK();
766 unlink(keych->loc);
767 keych->loc[l - 14] = '\0';
768 /* the keychain code may leave dot, possibly comma and yet other turds
769 * we may have to remove */
770 d = opendir(keych->loc);
771 if (d) {
772 struct dirent *ent;
773 while ((ent=readdir(d)) != NULL) {
774 char turd[PATH_MAX];
775 if (ent->d_name[0] == '.' &&
776 (ent->d_name[1] == '\0'
777 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) continue;
778 snprintf(turd, sizeof(turd), "%s/%s", keych->loc, ent->d_name);
779 unlink(turd);
781 closedir(d);
783 rmdir(keych->loc);
784 free_home_dirs(&keych->dirs);
785 free(keych);
789 static CFDataRef extract_key_copy(CFDataRef pemseq, int *outpem)
791 const char *p = (char *)CFDataGetBytePtr(pemseq);
792 const char *origp = p;
793 size_t l = (size_t)CFDataGetLength(pemseq);
794 size_t origl = l;
795 *outpem = 0;
796 while (l) {
797 peminfo_t pem;
798 int readcnt = nextpem(p, l, &pem);
799 if (!readcnt && p == origp) {
800 /* Assume it's DER data */
801 CFRetain(pemseq);
802 return pemseq;
804 if (!readcnt || readcnt == -1) return NULL;
805 if (pem.type == pemtype_privatekey_rsa) {
806 *outpem = 1;
807 if (pem.start == origp && pem.len == origl) {
808 CFRetain(pemseq);
809 return pemseq;
811 return CFDataCreate(kCFAllocatorDefault, (uint8_t *)pem.start, pem.len);
813 l -= (size_t)readcnt;
814 p += (size_t)readcnt;
816 return NULL;
819 SecIdentityRef cSecIdentityCreateWithCertificateAndKeyData(
820 SecCertificateRef cert, CFDataRef keydata, CFTypeRef pw, CFStringRef hint,
821 void **kh)
823 int ispem = 0;
824 CFDataRef rawkey = NULL;
825 tempch_t *keych = NULL;
826 int err;
827 SecKeychainRef keychain = NULL;
828 SecExternalFormat format;
829 SecExternalItemType type;
830 SecItemImportExportKeyParameters params;
831 CFArrayRef items = NULL;
832 SecKeyRef key = NULL;
833 SecIdentityRef ans = NULL;
835 if (!cert || !kh) return NULL;
836 if (keydata)
837 rawkey = extract_key_copy(keydata, &ispem);
838 (void)MUTEX_LOCK();
839 while (rawkey) {
840 CFArrayRef searchlist = NULL;
841 keych = new_temp_keych();
842 if (!keych) break;
843 /* SecKeychainCreate has the side effect of adding the new keychain to
844 * the search list which will make it show up in other apps.
845 * SecKeychainDelete removes it from the search list, or we can also get
846 * the search list before the create and restore it right after.
847 * By immediately restoring the search list, we avoid having the new
848 * private key we're importing be searchable by default in other apps.
849 * If we are running with HOME != ~geteuid() then we likely have no
850 * ~/Library/Preferences/com.apple.security.plist which means the system
851 * will "helpfully" set the default keychain to this new keychain we've
852 * just created which is very bad. If Xcode is running it will listen
853 * to that event and then call SecKeychainSetDefault with that very
854 * same temporary keychain (I have no idea why it does this stupid thing)
855 * and that will make it permanent for the user. Ugh. To avoid this,
856 * we temporarily set HOME to getpwuid(geteuid())->pw_dir while we are
857 * creating the temporary keychain and then put HOME back the way it was
858 * immediately thereafter. Git likes to run tests with HOME set to
859 * alternate locations so it's prudent to handle this. */
860 if (keych->dirs.home)
861 putenv(keych->dirs.home);
862 err = SecKeychainCopySearchList(&searchlist);
863 if (!err && searchlist)
864 err = SecKeychainCreate(keych->loc, sizeof(keych->pw), keych->pw, false,
865 NULL, &keychain);
866 if (searchlist) {
867 if (!err)
868 err = SecKeychainSetSearchList(searchlist);
869 CFRelease(searchlist);
871 if (keych->dirs.home) {
872 if (keych->dirs.cur_home)
873 putenv(keych->dirs.cur_home);
874 else
875 unsetenv("HOME");
877 if (err || !keychain)
878 break;
879 keych->ref = keychain;
880 err = SecKeychainUnlock(keychain, sizeof(keych->pw), keych->pw, true);
881 if (err) break;
883 SecKeychainSettings settings;
884 settings.version = SEC_KEYCHAIN_SETTINGS_VERS1;
885 settings.lockOnSleep = false;
886 settings.useLockInterval = false;
887 settings.lockInterval = INT_MAX;
888 (void)SecKeychainSetSettings(keychain, &settings);
890 format = ispem ? kSecFormatWrappedOpenSSL : kSecFormatOpenSSL;
891 type = kSecItemTypePrivateKey;
892 memset(&params, 0, sizeof(params));
893 params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
894 params.flags = kSecKeyImportOnlyOne|kSecKeyNoAccessControl;
895 if (pw)
896 params.passphrase = pw;
897 else {
898 params.flags |= kSecKeySecurePassphrase;
899 /* Note that params.alertTitle is ignored */
900 params.alertPrompt = hint;
902 err = cSecItemImport(rawkey, NULL, &format, &type,
903 ispem?kSecItemPemArmour:0, &params, keychain, &items);
904 CFRelease(rawkey);
905 if (!err && items && CFArrayGetCount(items) == 1 &&
906 CFGetTypeID((CFTypeRef)CFArrayGetValueAtIndex(items, 0)) == SecKeyGetTypeID()) {
907 key = (SecKeyRef)CFArrayGetValueAtIndex(items, 0);
908 CFRetain(key);
910 if (items) CFRelease(items);
911 break;
913 if (key) {
914 /* If we have a key we must also have a keychain */
915 err = cSecIdentityCreateWithCertificate(keychain, cert, &ans);
916 CFRelease(key);
918 (void)MUTEX_UNLOCK();
919 /* We MUST NOT call SecKeychainDelete because that will purge all copies of
920 * the keychain from memory. We've already removed it from the search list
921 * so we just release it and remove the disk files instead in order to allow
922 * the in memory copy to remain unmolested. Unfortunately on older systems
923 * this is not good enough, so we have to leave the keychain itself around. */
924 if (!ans && keych) {
925 del_temp_keych(keych);
926 keych = NULL;
928 if (!ans && (!rawkey || (!ispem && !key))) {
929 /* Try again with the default keychain list, but only if a key was not
930 * provided or was provided in non-PEM and we failed to import it. */
931 err = cSecIdentityCreateWithCertificate(NULL, cert, &ans);
933 if (ans)
934 *kh = keych;
935 else
936 del_temp_keych(keych);
937 return ans;
940 void DisposeIdentityKeychainHandle(void *ch)
942 del_temp_keych((tempch_t *)ch);
945 CFArrayRef CreateClientAuthWithCertificatesAndKeyData(CFArrayRef certs,
946 CFDataRef keydata, CFTypeRef pw,
947 CFStringRef hint, void **kh)
949 CFMutableArrayRef ans;
950 size_t count, i;
951 SecCertificateRef cert;
952 SecIdentityRef identity;
954 if (!certs || !keydata) return NULL;
955 count = (size_t)CFArrayGetCount(certs);
956 if (count < 1) return NULL;
957 cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, 0);
958 if (CFGetTypeID(cert) != SecCertificateGetTypeID()) return NULL;
959 ans = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks);
960 if (!ans) return NULL;
961 identity = cSecIdentityCreateWithCertificateAndKeyData(cert, keydata, pw,
962 hint, kh);
963 if (!identity) {
964 CFRelease(ans);
965 return NULL;
967 CFArrayAppendValue(ans, identity);
968 for (i = 1; i < count; ++i) {
969 CFArrayAppendValue(ans, CFArrayGetValueAtIndex(certs, i));
971 return ans;
974 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
976 #ifndef kCFCoreFoundationVersionNumber10_8
977 #define kCFCoreFoundationVersionNumber10_8 744.00
978 #endif
980 typedef enum {
981 small_0,
982 small_1,
983 small_2,
984 small_3,
985 small_4,
986 small_5
987 } SmallEnum;
989 typedef struct {
990 size_t Length;
991 uint8_t *Data;
992 } cCSSM_DATA;
994 static struct {
995 OSStatus (*fSSLSetTrustedRoots)(SSLContextRef, CFArrayRef, Boolean);
996 OSStatus (*fSSLGetPeerSecTrust)(SSLContextRef, SecTrustRef *);
997 OSStatus (*fSSLCopyPeerTrust)(SSLContextRef, SecTrustRef *);
998 OSStatus (*fSecTrustGetResult)(SecTrustRef, SecTrustResultType *,
999 CFArrayRef *, CSSM_TP_APPLE_EVIDENCE_INFO **);
1000 OSStatus (*fSecTrustSetAnchorCertificatesOnly)(SecTrustRef, Boolean);
1001 OSStatus (*fSSLGetPeerCertificates)(SSLContextRef cxt, CFArrayRef *certs);
1002 OSStatus (*fSSLCopyPeerCertificates)(SSLContextRef cxt, CFArrayRef *certs);
1003 OSStatus (*fSSLSetProtocolVersionEnabled)(SSLContextRef cxt, SmallEnum, Boolean);
1004 OSStatus (*fSSLSetProtocolVersionMin)(SSLContextRef cxt, SmallEnum);
1005 OSStatus (*fSSLSetProtocolVersionMax)(SSLContextRef cxt, SmallEnum);
1006 OSStatus (*fSSLSetSessionOption)(SSLContextRef, SmallEnum, Boolean);
1007 SecCertificateRef (*fSecCertificateCreateWithData)(CFAllocatorRef, CFDataRef);
1008 OSStatus (*fSecCertificateCreateFromData)(const cCSSM_DATA *, CSSM_CERT_TYPE,
1009 CSSM_CERT_ENCODING, SecCertificateRef *);
1010 OSStatus (*fSecCertificateGetData)(SecCertificateRef, cCSSM_DATA *);
1011 CFDataRef (*fSecCertificateCopyData)(SecCertificateRef);
1012 OSStatus (*fSecKeychainItemImport)(
1013 CFDataRef,CFStringRef,SecExternalFormat *,SecExternalItemType *,
1014 SecItemImportExportFlags,const SecKeyImportExportParameters *,
1015 SecKeychainRef,CFArrayRef *);
1016 OSStatus (*fSecItemImport)(
1017 CFDataRef,CFStringRef,SecExternalFormat *,SecExternalItemType *,
1018 SecItemImportExportFlags,const SecItemImportExportKeyParameters *,
1019 SecKeychainRef,CFArrayRef *);
1020 OSStatus (*fSecIdentityCreateWithCertificate)(CFTypeRef,SecCertificateRef,SecIdentityRef *);
1021 OSStatus (*fSSLNewContext)(Boolean,SSLContextRef *);
1022 OSStatus (*fSSLDisposeContext)(SSLContextRef);
1023 SSLContextRef (*fSSLCreateContext)(CFAllocatorRef,SmallEnum,SmallEnum);
1024 OSStatus (*fSecKeychainSearchCreateFromAttributes)(CFTypeRef,int,
1025 const SecKeychainAttributeList *,SecKeychainSearchRef *);
1026 OSStatus (*fSecKeychainSearchCopyNext)(SecKeychainSearchRef,SecKeychainItemRef *);
1027 } fnc;
1029 static void stcompat_initialize(void)
1031 #define LOOKUP(name) *((void **)&fnc.f##name) = dlsym(RTLD_NEXT, #name)
1032 LOOKUP(SSLSetTrustedRoots);
1033 LOOKUP(SSLGetPeerSecTrust);
1034 LOOKUP(SSLCopyPeerTrust);
1035 LOOKUP(SecTrustGetResult);
1036 LOOKUP(SecTrustSetAnchorCertificatesOnly);
1037 LOOKUP(SSLGetPeerCertificates);
1038 LOOKUP(SSLCopyPeerCertificates);
1039 LOOKUP(SSLSetProtocolVersionEnabled);
1040 LOOKUP(SSLSetProtocolVersionMin);
1041 LOOKUP(SSLSetProtocolVersionMax);
1042 LOOKUP(SSLSetSessionOption);
1043 LOOKUP(SecCertificateCreateWithData);
1044 LOOKUP(SecCertificateCreateFromData);
1045 LOOKUP(SecCertificateGetData);
1046 LOOKUP(SecCertificateCopyData);
1047 LOOKUP(SecKeychainItemImport);
1048 LOOKUP(SecItemImport);
1049 LOOKUP(SecIdentityCreateWithCertificate);
1050 LOOKUP(SSLNewContext);
1051 LOOKUP(SSLDisposeContext);
1052 LOOKUP(SSLCreateContext);
1053 LOOKUP(SecKeychainSearchCreateFromAttributes);
1054 LOOKUP(SecKeychainSearchCopyNext);
1055 #undef LOOKUP
1058 OSStatus cSSLSetTrustedRoots(SSLContextRef cxt, CFArrayRef rts, Boolean replace)
1060 if (fnc.fSSLSetTrustedRoots)
1061 return fnc.fSSLSetTrustedRoots(cxt, rts, replace);
1062 return unimpErr;
1065 OSStatus cSSLCopyPeerTrust(SSLContextRef cxt, SecTrustRef *trust)
1067 if (fnc.fSSLCopyPeerTrust)
1068 return fnc.fSSLCopyPeerTrust(cxt, trust);
1069 if (fnc.fSSLGetPeerSecTrust) {
1070 OSStatus err = fnc.fSSLGetPeerSecTrust(cxt, trust);
1071 if (!err && *trust)
1072 CFRetain(*trust);
1073 return err;
1075 return unimpErr;
1078 OSStatus cSecTrustGetResult(SecTrustRef trust, SecTrustResultType *result,
1079 CFArrayRef *certChain, CSSM_TP_APPLE_EVIDENCE_INFO **statusChain)
1081 if (fnc.fSecTrustGetResult)
1082 return fnc.fSecTrustGetResult(trust, result, certChain, statusChain);
1083 return unimpErr;
1086 OSStatus cSSLCopyPeerCertificates(SSLContextRef cxt, CFArrayRef *certs)
1088 if (!certs || !cxt) return paramErr;
1089 *certs = NULL;
1090 if (fnc.fSSLCopyPeerCertificates)
1091 return fnc.fSSLCopyPeerCertificates(cxt, certs);
1092 if (fnc.fSSLGetPeerCertificates) {
1093 OSStatus err = fnc.fSSLGetPeerCertificates(cxt, certs);
1094 if (!err && *certs) {
1095 size_t i, c = (size_t)CFArrayGetCount(*certs);
1096 for (i = 0; i < c; ++i) {
1097 CFTypeRef item = (CFTypeRef)CFArrayGetValueAtIndex(*certs, i);
1098 if (item) CFRelease(item);
1101 return err;
1103 return unimpErr;
1106 OSStatus cSecTrustSetAnchorCertificatesOnly(SecTrustRef cxt, Boolean anchorsOnly)
1108 if (fnc.fSecTrustSetAnchorCertificatesOnly)
1109 return fnc.fSecTrustSetAnchorCertificatesOnly(cxt, anchorsOnly);
1110 return unimpErr;
1113 OSStatus cSSLSetProtocolVersionMinMax(SSLContextRef cxt, int minVer, int maxVer)
1115 OSStatus err;
1117 if (minVer < 0 || maxVer < 0 || minVer > 8 || maxVer > 8 || minVer > maxVer)
1118 return paramErr;
1120 if (minVer == kSSLProtocolUnknown) minVer = kSSLProtocol3;
1121 if (minVer == kSSLProtocolAll) minVer = kSSLProtocol3;
1122 if (minVer == kSSLProtocol3Only) minVer = kSSLProtocol3;
1123 if (minVer == kTLSProtocol1Only) minVer = kTLSProtocol1;
1125 if (maxVer == kSSLProtocol3Only) maxVer = kSSLProtocol3;
1126 if (maxVer == kTLSProtocol1Only) maxVer = kTLSProtocol1;
1127 if (maxVer == kSSLProtocolAll) maxVer = kTLSProtocol12;
1128 if (maxVer == kSSLProtocolUnknown) maxVer = kTLSProtocol12;
1130 if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_8 &&
1131 minVer <= kTLSProtocol1 && maxVer > kTLSProtocol1)
1132 maxVer = kTLSProtocol1;
1134 if (fnc.fSSLSetProtocolVersionMin && fnc.fSSLSetProtocolVersionMax) {
1135 err = fnc.fSSLSetProtocolVersionMin(cxt, minVer);
1136 if (!err)
1137 err = fnc.fSSLSetProtocolVersionMax(cxt, maxVer);
1138 return err;
1140 if (fnc.fSSLSetProtocolVersionEnabled) {
1141 #define ENABLEPROTO(x) fnc.fSSLSetProtocolVersionEnabled(cxt, (int)(x), \
1142 minVer <= x && x <= maxVer)
1143 err = ENABLEPROTO(kSSLProtocol2);
1144 if (err && minVer > kSSLProtocol2) err = noErr; /* ignore SSL2 disable error */
1145 if (!err) ENABLEPROTO(kSSLProtocol3);
1146 if (!err) ENABLEPROTO(kTLSProtocol1);
1147 if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber10_8 ||
1148 maxVer > kTLSProtocol1) {
1149 if (!err) ENABLEPROTO(kTLSProtocol11);
1150 if (!err) ENABLEPROTO(kTLSProtocol12);
1152 #undef ENABLEPROTO
1153 return err;
1155 return unimpErr;
1158 OSStatus cSSLSetSessionOption(SSLContextRef cxt, int option, Boolean value)
1160 if (fnc.fSSLSetSessionOption)
1161 return fnc.fSSLSetSessionOption(cxt, option, value);
1162 else
1163 return unimpErr;
1166 SecCertificateRef cSecCertificateCreateWithData(CFAllocatorRef a, CFDataRef d)
1168 if (fnc.fSecCertificateCreateWithData)
1169 return fnc.fSecCertificateCreateWithData(a, d);
1170 else if (fnc.fSecCertificateCreateFromData) {
1171 cCSSM_DATA certdata;
1172 OSStatus err;
1173 SecCertificateRef cacert = NULL;
1174 if (!d) return NULL;
1175 certdata.Length = (size_t)CFDataGetLength(d);
1176 certdata.Data = (uint8 *)CFDataGetBytePtr(d);
1177 err = fnc.fSecCertificateCreateFromData(&certdata, CSSM_CERT_X_509v3,
1178 CSSM_CERT_ENCODING_DER, &cacert);
1179 if (err)
1180 cacert = NULL;
1181 return cacert;
1182 } else
1183 return NULL;
1186 CFDataRef cSecCertificateCopyData(SecCertificateRef c)
1188 if (!c) return NULL;
1189 if (CFGetTypeID(c) != SecCertificateGetTypeID()) return NULL;
1190 if (fnc.fSecCertificateCopyData)
1191 return fnc.fSecCertificateCopyData(c);
1192 if (fnc.fSecCertificateGetData) {
1193 cCSSM_DATA certdata;
1194 OSStatus err = fnc.fSecCertificateGetData(c, &certdata);
1195 if (err || !certdata.Data || !certdata.Length) return NULL;
1196 return CFDataCreate(kCFAllocatorDefault, certdata.Data, certdata.Length);
1198 return NULL;
1201 Boolean BlobsEqual(CFDataRef d1, CFDataRef d2)
1203 size_t l1, l2;
1204 Boolean ans = false;
1206 if (!d1 || !d2)
1207 return false;
1208 l1 = CFDataGetLength(d1);
1209 l2 = CFDataGetLength(d2);
1210 if (l1 == l2) {
1211 const void *p1 = (void *)CFDataGetBytePtr(d1);
1212 const void *p2 = (void *)CFDataGetBytePtr(d2);
1213 ans = memcmp(p1, p2, l1) == 0;
1215 return ans;
1218 Boolean SecCertsEqual(SecCertificateRef c1, SecCertificateRef c2)
1220 CFDataRef d1, d2;
1221 Boolean ans = false;
1222 d1 = cSecCertificateCopyData(c1);
1223 if (!d1) return false;
1224 d2 = cSecCertificateCopyData(c2);
1225 if (!d2) {
1226 CFRelease(d1);
1227 return false;
1229 ans = BlobsEqual(d1, d2);
1230 CFRelease(d1);
1231 CFRelease(d2);
1232 return ans;
1235 Boolean BlobInArray(CFDataRef d, CFArrayRef a)
1237 size_t i, cnt;
1238 if (!d || !a || !CFArrayGetCount(a)) return false;
1239 cnt = CFArrayGetCount(a);
1240 for (i = 0; i < cnt; ++i) {
1241 if (BlobsEqual(d, (CFDataRef)CFArrayGetValueAtIndex(a, i)))
1242 return true;
1244 return false;
1247 Boolean SecCertInArray(SecCertificateRef c, CFArrayRef a)
1249 size_t i, cnt;
1250 if (!c || !a || !CFArrayGetCount(a)) return false;
1251 cnt = CFArrayGetCount(a);
1252 for (i = 0; i < cnt; ++i) {
1253 if (SecCertsEqual(c, (SecCertificateRef)CFArrayGetValueAtIndex(a, i)))
1254 return true;
1256 return false;
1259 OSStatus CopyIdentityWithLabel(const char *label, SecIdentityRef *out)
1261 if (fnc.fSecKeychainSearchCreateFromAttributes &&
1262 fnc.fSecKeychainSearchCopyNext) {
1263 SecIdentityRef ans = NULL;
1264 SecCertificateRef cert = NULL;
1265 SecKeychainAttribute at;
1266 SecKeychainAttributeList al;
1267 SecKeychainSearchRef sr;
1268 OSStatus err;
1270 at.tag = kSecLabelItemAttr;
1271 at.length = strlen(label);
1272 at.data = (char *)label;
1273 al.count = 1;
1274 al.attr = &at;
1275 err = fnc.fSecKeychainSearchCreateFromAttributes(NULL,
1276 kSecCertificateItemClass, &al, &sr);
1277 *out = NULL;
1278 if (err || !sr) return err;
1279 while ((err = fnc.fSecKeychainSearchCopyNext(sr, (SecKeychainItemRef *)&cert)) == noErr) {
1280 if (!cert) continue;
1281 if (CFGetTypeID(cert) != SecCertificateGetTypeID()) {CFRelease(cert); continue;}
1282 err = cSecIdentityCreateWithCertificate(NULL, cert, &ans);
1283 CFRelease(cert);
1284 if (!err && ans) break;
1286 CFRelease(sr);
1287 if (ans) {
1288 *out = ans;
1289 return noErr;
1291 return errSecItemNotFound;
1293 return unimpErr;
1296 OSStatus cSecItemImport(
1297 CFDataRef importedData, CFStringRef fileNameOrExtension,
1298 SecExternalFormat *inputFormat, SecExternalItemType *itemType,
1299 SecItemImportExportFlags flags, const SecItemImportExportKeyParameters *keyParams,
1300 SecKeychainRef importKeychain, CFArrayRef *outItems)
1302 if (fnc.fSecItemImport)
1303 return fnc.fSecItemImport(importedData, fileNameOrExtension, inputFormat,
1304 itemType, flags, keyParams, importKeychain, outItems);
1305 else if (fnc.fSecKeychainItemImport) {
1306 SecKeyImportExportParameters oldKeyParams;
1307 SecKeyImportExportParameters *op = NULL;
1308 if (keyParams) {
1309 op = &oldKeyParams;
1310 memset(&oldKeyParams, 0, sizeof(oldKeyParams));
1311 oldKeyParams.version = keyParams->version;
1312 oldKeyParams.flags = keyParams->flags;
1313 oldKeyParams.passphrase = keyParams->passphrase;
1314 oldKeyParams.alertTitle = keyParams->alertTitle;
1315 oldKeyParams.alertPrompt = keyParams->alertPrompt;
1316 oldKeyParams.accessRef = keyParams->accessRef;
1317 /* We punt on keyUsage and keyAttributes and do not convert them */
1319 return fnc.fSecKeychainItemImport(importedData, fileNameOrExtension, inputFormat,
1320 itemType, flags, op, importKeychain, outItems);
1322 return unimpErr;
1325 OSStatus cSecIdentityCreateWithCertificate(CFTypeRef k, SecCertificateRef c,
1326 SecIdentityRef *i)
1328 /* The documentation lies and this is actually present in later 10.4 versions */
1329 if (fnc.fSecIdentityCreateWithCertificate)
1330 return fnc.fSecIdentityCreateWithCertificate(k, c, i);
1331 return unimpErr;
1334 SSLContextRef cSSLCreateContext(CFAllocatorRef a, int ps, int ct)
1336 if (fnc.fSSLCreateContext)
1337 return fnc.fSSLCreateContext(a, ps, ct);
1338 if ((ps != kSSLServerSide && ps != kSSLClientSide) || (ct != kSSLStreamType))
1339 return NULL;
1340 if (fnc.fSSLNewContext && fnc.fSSLDisposeContext) {
1341 SSLContextRef cxt;
1342 OSStatus err = fnc.fSSLNewContext(ps == kSSLServerSide, &cxt);
1343 return err ? NULL : cxt;
1345 return NULL;
1348 void cSSLDisposeContext(SSLContextRef c)
1350 if (fnc.fSSLCreateContext)
1351 CFRelease(c);
1352 else if (fnc.fSSLDisposeContext)
1353 fnc.fSSLDisposeContext(c);
1356 CF_INLINE bool is_ldh(int c)
1358 return
1359 ('A' <= c && c <= 'Z') ||
1360 ('a' <= c && c <= 'z') ||
1361 ('0' <= c && c <= '9') ||
1362 c == '-';
1365 CF_INLINE size_t get_label_len(const char *p, size_t l)
1367 size_t ans = 0;
1368 while (l-- && is_ldh(*p++)) ++ans;
1369 return ans;
1372 static bool is_dns_name(const void *_p, size_t l, bool wcok)
1374 const char *p = (char *)_p;
1375 size_t idx = 0;
1377 if (!p) return false;
1378 if (l >= 1 && p[l-1] == '.') --l;
1379 if (!l) return false;
1380 if (l > 255) return false;
1381 do {
1382 size_t lablen = get_label_len(p, l);
1383 if (lablen > 63) return false;
1384 if (!idx && !lablen && wcok && l >= 2 && p[0] == '*' && p[1] == '.') lablen=1;
1385 if (!lablen) return false;
1386 if (p[0] == '-' || p[lablen - 1] == '-') return false;
1387 if (lablen < l) {
1388 if (p[lablen] != '.') return false;
1389 ++lablen;
1391 l -= lablen;
1392 p += lablen;
1393 ++idx;
1394 } while (l);
1395 return true;
1398 CF_INLINE char clc(char c)
1400 return 'A' <= c && c <= 'Z' ? c - 'A' + 'a' : c;
1403 CF_INLINE bool matchicase(const char *p1, const char *p2, size_t l)
1405 while (l--) {
1406 if (clc(*p1++) != clc(*p2++)) return false;
1408 return true;
1411 static bool peername_matches_id(const char *peername, CFDataRef idrawname)
1413 size_t pl, il, idx;
1414 const char *idname;
1415 if (!peername || !idrawname) return false;
1416 idname = (const char *)CFDataGetBytePtr(idrawname);
1417 if (!idname) return false;
1418 pl = strlen(peername);
1419 il = (size_t)CFDataGetLength(idrawname);
1420 if (!is_dns_name(peername, pl, false) || !is_dns_name(idname, il, true))
1421 return false;
1422 idx = 0;
1423 if (peername[pl - 1] == '.') --pl;
1424 if (idname[il - 1] == '.') --il;
1425 if (pl > 255 || il > 255)
1426 return false;
1427 while (pl && il) {
1428 size_t pll = get_label_len(peername, pl);
1429 size_t ill = get_label_len(idname, il);
1430 if (!idx && !ill && il >= 2 && idname[0] == '*' && idname[1] == '.') ill=1;
1431 if (pll < pl) {
1432 if (peername[pll] != '.') return false;
1433 ++pll;
1435 if (ill < il) {
1436 if (idname[ill] != '.') return false;
1437 ++ill;
1439 if (idx || idname[0] != '*') {
1440 if (pll != ill) return false;
1441 if (!matchicase(peername, idname, pll)) return false;
1443 peername += pll;
1444 pl -= pll;
1445 idname += ill;
1446 il -= ill;
1447 ++idx;
1449 return !pl && !il;
1452 CF_INLINE size_t get_num_len(const char *p, size_t l)
1454 size_t ans = 0;
1455 while (l-- && '0' <= *p && *p <= '9') {
1456 ++ans;
1457 ++p;
1459 return ans;
1462 Boolean IsIPv4Name(const void *_p, size_t l)
1464 const char *p = (char *)_p;
1465 size_t idx = 0;
1467 if (!p || l < 7) return false;
1468 do {
1469 size_t lablen;
1470 if (++idx > 4) return false;
1471 lablen = get_num_len(p, l);
1472 if (lablen > 3) return false;
1473 if (lablen >= 2 && *p == '0') return false;
1474 else if (lablen == 3) {
1475 if (*p >= '3') return false;
1476 if (*p == '2') {
1477 if (p[1] >= '6') return false;
1478 if (p[1] == '5' && p[2] > '5') return false;
1481 if (lablen < l) {
1482 if (p[lablen] != '.') return false;
1483 ++lablen;
1485 l -= lablen;
1486 p += lablen;
1487 } while (l);
1488 return idx == 4;
1491 static bool parse_ipv4_name(const void *_p, size_t l, uint8_t ipv4[4])
1493 unsigned short s[4];
1494 char ipv4str[16];
1495 const char *p = (char *)_p;
1496 if (!IsIPv4Name(p, l) || l > 15) return false;
1497 memcpy(ipv4str, p, l);
1498 ipv4str[l] = 0;
1499 if (sscanf(ipv4str, "%hu.%hu.%hu.%hu", s, s+1, s+2, s+3) == 4) {
1500 ipv4[0] = (uint8_t)s[0];
1501 ipv4[1] = (uint8_t)s[1];
1502 ipv4[2] = (uint8_t)s[2];
1503 ipv4[3] = (uint8_t)s[3];
1504 return true;
1506 return false;
1509 static bool parse_ipv6_name(const void *_p, size_t l, uint8_t ipv6[16])
1511 char ipv6str[INET6_ADDRSTRLEN];
1512 const char *p = (char *)_p;
1513 const char *pct;
1514 if (!p) return false;
1515 if (l >= 1 && p[0] == '[') {
1516 ++p;
1517 --l;
1519 if (l >= 1 && p[l-1] == ']') --l;
1520 pct = (char *)(l ? memchr(p, '%', l) : NULL);
1521 if (pct) l = pct - p;
1522 if (l < 3 || l >= INET6_ADDRSTRLEN) return false;
1523 memcpy(ipv6str, p, l);
1524 ipv6str[l] = 0;
1525 return inet_pton(AF_INET6, ipv6str, ipv6) == 1;
1528 #define U(x) ((const uint8_t *)(x))
1529 static const data_t OID_BasicConstraints = {U("\006\003\125\035\023"), 5};
1530 static const data_t OID_SubjectAltName = {U("\006\003\125\035\021"), 5};
1531 static const data_t OID_SubjectKeyIdentifier = {U("\006\003\125\035\016"), 5};
1532 static const data_t OID_AuthorityKeyIdentifier = {U("\006\003\125\035\043"), 5};
1533 static const data_t OID_CommonName = {U("\006\003\125\004\003"), 5};
1534 #undef U
1536 typedef struct der_atom_s {
1537 uint8_t clas; /* 0, 1, 2, or 3 */
1538 uint8_t cons; /* 0 or 1 */
1539 uint8_t rawtag; /* raw value of first byte of tag */
1540 uint32_t tag; /* tag value */
1541 size_t hl; /* length of header excluding actual data */
1542 size_t dl; /* length of actual data */
1543 } der_atom_t;
1545 typedef struct der_cert_s {
1546 uint8_t vers; /* 0 => v1, 1 => v2, 2 => v3 */
1547 uint8_t caFlag; /* 0 unless basic constraints present then 0x80=critial 0x01=value */
1548 uint8_t isCA; /* true if caFlag==0x81 or subject==issuer && vers < 2 */
1549 uint8_t isRoot; /* true if isCA and subject == issuer */
1550 char notBefore[16]; /* Not before date either 13 chars or 15 chars plus Nul */
1551 char notAfter[16]; /* Not after date see notBefore for format */
1552 data_t subject; /* points to sequence */
1553 data_t subjectPubKey; /* points to subjectPublicKeyInfo sequence */
1554 data_t subjectAltNames; /* null unless v3 extension present, points to sequence */
1555 data_t subjectKeyId; /* null unless v3 extension present, points to raw bytes */
1556 data_t issuer; /* points to sequence */
1557 data_t issuerKeyId; /* null unless v3 extension present, points to raw bytes */
1558 } der_cert_t;
1560 static bool read_der_atom(const data_t *d, der_atom_t *o)
1562 uint8_t byte;
1563 uint32_t tag;
1564 size_t pos, len;
1565 if (!d || !d->d || !d->l || !o) return false;
1566 o->clas = (*d->d >> 6) & 0x3;
1567 o->cons = (*d->d >> 5) & 0x1;
1568 o->rawtag = *d->d;
1569 tag = *d->d & 0x1f;
1570 pos = 1;
1571 if (tag == 0x1f) {
1572 tag = 0;
1573 do {
1574 if (pos >= d->l) return false;
1575 tag <<= 7;
1576 byte = d->d[pos++];
1577 tag |= byte & 0x7f;
1578 } while (byte & 0x80);
1580 o->tag = tag;
1581 if (pos >= d->l) return false;
1582 byte = d->d[pos++];
1583 if (byte & 0x80) {
1584 unsigned cnt = byte & 0x7f;
1585 if (!cnt || pos + cnt > d->l) return false;
1586 len = 0;
1587 do {
1588 len <<= 8;
1589 len |= d->d[pos++];
1590 } while (--cnt);
1591 } else {
1592 len = byte;
1594 if (pos + len > d->l) return false;
1595 o->hl = pos;
1596 o->dl = len;
1597 return true;
1600 /* return true if _d->d points at a valid DER atom that is _d->l bytes long
1601 * or less. If exact_length_match_only is set the DER atom MUST be exactly
1602 * _d->l bytes long. If the atom at _d->d is a set or sequence, then its
1603 * elements are also examined recursively to make sure they are also valid. */
1604 static bool is_der(const data_t *_d, bool exact_length_match_only)
1606 bool first = true;
1607 data_t d;
1608 if (!_d || !_d->d || !_d->l) return false;
1609 d.d = _d->d;
1610 d.l = _d->l;
1611 do {
1612 der_atom_t atom;
1613 if (!read_der_atom(&d, &atom)) return false;
1614 d.l -= atom.hl;
1615 d.d += atom.hl;
1616 if ((atom.rawtag & 0xfe) != 0x30) {
1617 d.l -= atom.dl;
1618 d.d += atom.dl;
1619 if (first) break;
1620 } else if (first) {
1621 d.l = atom.dl;
1623 first = false;
1624 } while (d.l);
1625 return !d.l && (d.d == _d->d + _d->l || !exact_length_match_only);
1628 /* true is returned if data is not NULL and matches:
1629 * SEQUENCE {
1630 * SEQUENCE {
1631 * OBJECT ID,
1632 * optional...
1633 * },
1634 * BIT STRING {
1635 * whatever...
1636 * },
1637 * } == length of data
1639 static bool check_der_pubkey(const data_t *_d)
1641 data_t d;
1642 der_atom_t atom;
1644 if (!_d || !_d->d || !_d->l) return false;
1645 if (!is_der(_d, true)) return false;
1646 d.d = _d->d;
1647 d.l = _d->l;
1648 if (!read_der_atom(&d, &atom)) return false;
1649 if (atom.rawtag != 0x30) return false;
1650 d.l = atom.dl;
1651 d.d += atom.hl;
1652 if (!read_der_atom(&d, &atom)) return false;
1653 if (atom.rawtag != 0x30) return false;
1654 if (!atom.dl || d.d[atom.hl] != 0x06) return false;
1655 d.l -= atom.hl + atom.dl;
1656 d.d += atom.hl + atom.dl;
1657 if (!read_der_atom(&d, &atom)) return false;
1658 if (atom.rawtag != 0x03) return false;
1659 return true;
1662 static int data_matches(const data_t *o1, const data_t *o2)
1664 if (!o1 || !o2 || !o1->l || !o2->l || o1->l != o2->l)
1665 return 0;
1666 return memcmp(o1->d, o2->d, o1->l) == 0;
1669 static bool read_der_cert(const data_t *_d, der_cert_t *o)
1671 data_t d;
1672 der_atom_t atom;
1674 if (!_d || !_d->d || !_d->l || !o) return false;
1675 if (!is_der(_d, true)) return false;
1676 d.d = _d->d;
1677 d.l = _d->l;
1678 memset(o, 0, sizeof(*o));
1679 if (!read_der_atom(&d, &atom)) return false;
1680 if (atom.rawtag != 0x30) return false;
1681 d.l = atom.dl;
1682 d.d += atom.hl;
1683 if (!read_der_atom(&d, &atom)) return false;
1684 if (atom.rawtag != 0x30) return false;
1685 d.l = atom.dl;
1686 d.d += atom.hl;
1687 if (!read_der_atom(&d, &atom)) return false;
1688 if (atom.rawtag == 0xA0) {
1689 d.l -= atom.hl;
1690 d.d += atom.hl;
1691 if (atom.dl != 3 || d.d[0] != 2 || d.d[1] != 1) return false;
1692 o->vers = d.d[2]; /* not validated */
1693 d.l -= atom.dl;
1694 d.d += atom.dl;
1695 if (!read_der_atom(&d, &atom)) return false;
1696 } else {
1697 o->vers = 0; /* implied v1 */
1699 if (atom.rawtag != 2) return false;
1700 /* skip serialNumber */
1701 d.l -= atom.hl + atom.dl;
1702 d.d += atom.hl + atom.dl;
1703 if (!read_der_atom(&d, &atom)) return false;
1704 if (atom.rawtag != 0x30) return false;
1705 /* skip signature */
1706 d.l -= atom.hl + atom.dl;
1707 d.d += atom.hl + atom.dl;
1708 if (!read_der_atom(&d, &atom)) return false;
1709 if (atom.rawtag != 0x30) return false;
1710 o->issuer.d = d.d;
1711 o->issuer.l = atom.hl + atom.dl;
1712 d.l -= atom.hl + atom.dl;
1713 d.d += atom.hl + atom.dl;
1714 if (!read_der_atom(&d, &atom)) return false;
1715 if (atom.rawtag != 0x30) return false;
1717 /* parse validity */
1718 data_t vdate;
1719 vdate.d = d.d + atom.hl;
1720 vdate.l = atom.dl;
1721 d.l -= atom.hl + atom.dl;
1722 d.d += atom.hl + atom.dl;
1723 if (!read_der_atom(&vdate, &atom)) return false;
1724 if (atom.rawtag != 0x17 && atom.rawtag != 0x18) return false;
1725 if (atom.rawtag == 0x17 && atom.dl != 13) return false;
1726 if (atom.rawtag == 0x18 && atom.dl != 15) return false;
1727 memcpy(o->notBefore, vdate.d + atom.hl, atom.dl);
1728 vdate.l += atom.hl + atom.dl;
1729 vdate.d += atom.hl + atom.dl;
1730 if (!read_der_atom(&vdate, &atom)) return false;
1731 if (atom.rawtag != 0x17 && atom.rawtag != 0x18) return false;
1732 if (atom.rawtag == 0x17 && atom.dl != 13) return false;
1733 if (atom.rawtag == 0x18 && atom.dl != 15) return false;
1734 memcpy(o->notAfter, vdate.d + atom.hl, atom.dl);
1735 if (vdate.d + atom.hl + atom.dl != d.d) return false;
1737 if (!read_der_atom(&d, &atom)) return false;
1738 if (atom.rawtag != 0x30) return false;
1739 o->subject.d = d.d;
1740 o->subject.l = atom.hl + atom.dl;
1741 d.l -= atom.hl + atom.dl;
1742 d.d += atom.hl + atom.dl;
1743 if (!read_der_atom(&d, &atom)) return false;
1744 if (atom.rawtag != 0x30) return false;
1745 o->subjectPubKey.d = d.d;
1746 o->subjectPubKey.l = atom.hl + atom.dl;
1747 if (!check_der_pubkey(&o->subjectPubKey)) return false;
1748 d.l -= atom.hl + atom.dl;
1749 d.d += atom.hl + atom.dl;
1750 do {
1751 if (o->vers != 2 || !d.l) break;
1752 if (!read_der_atom(&d, &atom)) return false;
1753 if (atom.rawtag == 0x81) {
1754 /* skip issuerUniqueID */
1755 d.l -= atom.hl + atom.dl;
1756 d.d += atom.hl + atom.dl;
1757 if (!d.l) break;
1758 if (!read_der_atom(&d, &atom)) return false;
1760 if (atom.rawtag == 0x82) {
1761 /* skip subjectUniqueID */
1762 d.l -= atom.hl + atom.dl;
1763 d.d += atom.hl + atom.dl;
1764 if (!d.l) break;
1765 if (!read_der_atom(&d, &atom)) return false;
1767 if (atom.rawtag != 0xA3) return false;
1768 /* found v3 extensions */
1769 d.l = atom.dl;
1770 d.d += atom.hl;
1771 if (!read_der_atom(&d, &atom)) return false;
1772 if (atom.rawtag != 0x30) return false;
1773 d.l -= atom.hl;
1774 d.d += atom.hl;
1775 do {
1776 uint8_t crit = 0;
1777 data_t oid, value;
1778 if (!read_der_atom(&d, &atom)) return false;
1779 if (atom.rawtag != 0x30) return false;
1780 d.l -= atom.hl;
1781 d.d += atom.hl;
1782 if (!read_der_atom(&d, &atom)) return false;
1783 if (atom.rawtag != 6) return false;
1784 oid.d = d.d;
1785 oid.l = atom.hl + atom.dl;
1786 d.l -= atom.hl + atom.dl;
1787 d.d += atom.hl + atom.dl;
1788 if (!read_der_atom(&d, &atom)) return false;
1789 if (atom.rawtag == 1) {
1790 /* skip over boolean but record its value */
1791 if (atom.dl != 1) return false;
1792 crit = *(d.d + atom.hl);
1793 d.l -= atom.hl + atom.dl;
1794 d.d += atom.hl + atom.dl;
1795 if (!read_der_atom(&d, &atom)) return false;
1797 if (atom.rawtag != 4) return false;
1798 d.l -= atom.hl;
1799 d.d += atom.hl;
1800 value.d = d.d;
1801 value.l = atom.dl;
1802 d.l -= atom.dl;
1803 d.d += atom.dl;
1804 if (data_matches(&oid, &OID_BasicConstraints)) {
1805 if (!read_der_atom(&value, &atom)) return false;
1806 if (atom.rawtag != 0x30) return false;
1807 value.l = atom.dl;
1808 value.d += atom.hl;
1809 if (!value.l) {
1810 /* CA flag is false and was properly omitted */
1811 o->caFlag = crit ? 0x80 : 0;
1812 } else {
1813 if (!read_der_atom(&value, &atom)) return false;
1814 if (atom.rawtag == 1) {
1815 /* CA flag is present */
1816 if (atom.dl != 1) return false;
1817 o->caFlag = (crit ? 0x80 : 0) | (*(value.d + atom.hl) ? 0x1 : 0);
1820 } else if (data_matches(&oid, &OID_SubjectAltName)) {
1821 o->subjectAltNames.d = value.d;
1822 o->subjectAltNames.l = value.l;
1823 } else if (data_matches(&oid, &OID_SubjectKeyIdentifier)) {
1824 if (!read_der_atom(&value, &atom)) return false;
1825 if (atom.rawtag != 4) return false;
1826 o->subjectKeyId.d = value.d + atom.hl;
1827 o->subjectKeyId.l = atom.dl;
1828 } else if (data_matches(&oid, &OID_AuthorityKeyIdentifier)) {
1829 if (!read_der_atom(&value, &atom)) return false;
1830 if (atom.rawtag != 0x30) return false;
1831 value.l = atom.dl;
1832 value.d += atom.hl;
1833 if (!read_der_atom(&value, &atom)) return false;
1834 if (atom.rawtag == 0x80) {
1835 o->issuerKeyId.d = value.d + atom.hl;
1836 o->issuerKeyId.l = atom.dl;
1839 } while (d.l);
1840 } while (0);
1841 if (o->vers >= 2) {
1842 o->isCA = (o->caFlag|0x80) == 0x81; /* HACK: some old CAs aren't critical! */
1843 o->isRoot = (o->isCA && o->subject.l && o->subject.l == o->issuer.l &&
1844 memcmp(o->subject.d, o->issuer.d, o->subject.l) == 0) ? 1 : 0;
1845 } else {
1846 o->isCA = (o->subject.l && o->subject.l == o->issuer.l &&
1847 memcmp(o->subject.d, o->issuer.d, o->subject.l) == 0) ? 1 : 0;
1848 o->isRoot = o->isCA;
1850 return true;
1853 Boolean CheckCertOkay(SecCertificateRef _cert)
1855 CFDataRef d = cSecCertificateCopyData(_cert);
1856 data_t data;
1857 der_cert_t cert;
1858 Boolean ans;
1860 if (!d) return false;
1861 data.d = CFDataGetBytePtr(d);
1862 data.l = CFDataGetLength(d);
1863 ans = read_der_cert(&data, &cert);
1864 CFRelease(d);
1865 return ans;
1868 /* flags & 0x01 to extract pub keys from certificates */
1869 static Boolean CheckPubKeyOkayInt(CFDataRef d, data_t *pubkey, int flags)
1871 data_t data;
1873 if (!d || !pubkey) return false;
1874 data.d = CFDataGetBytePtr(d);
1875 data.l = CFDataGetLength(d);
1876 if (check_der_pubkey(&data)) {
1877 *pubkey = data;
1878 return true;
1880 if (flags & 0x01) {
1881 der_cert_t cert;
1882 if (read_der_cert(&data, &cert)) {
1883 *pubkey = cert.subjectPubKey;
1884 return true;
1887 return false;
1890 Boolean CheckPubKeyOkay(CFDataRef d)
1892 data_t data;
1893 return CheckPubKeyOkayInt(d, &data, 0);
1896 static void append_hex_dump(CFMutableStringRef s, const void *_d, size_t l)
1898 const unsigned char *d = (unsigned char *)_d;
1899 CFStringAppendCString(s, "<", kCFStringEncodingASCII);
1900 while (l--) {
1901 char byte[3];
1902 sprintf(byte, "%02X", *d++);
1903 CFStringAppendCString(s, byte, kCFStringEncodingASCII);
1905 CFStringAppendCString(s, ">", kCFStringEncodingASCII);
1908 static CFStringRef CopyCertKeyId(SecCertificateRef _cert, bool issuer)
1910 CFDataRef d = cSecCertificateCopyData(_cert);
1911 CFMutableStringRef ans = CFStringCreateMutable(kCFAllocatorDefault, 0);
1912 bool good = false;
1914 for (;;) {
1915 data_t data;
1916 const data_t *key;
1917 der_cert_t cert;
1918 size_t i;
1920 if (!d || !ans) break;
1921 data.d = CFDataGetBytePtr(d);
1922 data.l = CFDataGetLength(d);
1923 if (!read_der_cert(&data, &cert)) break;
1924 key = issuer ? &cert.issuerKeyId : &cert.subjectKeyId;
1925 if (!key->d || !key->l) break;
1926 for (i = 0; i < key->l; ++i) {
1927 char hexbyte[4];
1928 sprintf(hexbyte, "%02X%s", (unsigned)key->d[i], i+1 == key->l ? "" : ":");
1929 CFStringAppendCString(ans, hexbyte, kCFStringEncodingASCII);
1931 good = true;
1932 break;
1934 if (d) CFRelease(d);
1935 if (!good && ans) {CFRelease(ans); ans=NULL;}
1936 return ans;
1939 typedef struct oid_entry_s {
1940 size_t l;
1941 const char *oid;
1942 const char *name;
1943 } oid_entry_t;
1945 static const oid_entry_t oid_table[] = {
1946 {5, "\006\003\125\004\003", "CN"},
1947 {5, "\006\003\125\004\004", "SN"},
1948 {5, "\006\003\125\004\005", "serialNumber"},
1949 {5, "\006\003\125\004\006", "C"},
1950 {5, "\006\003\125\004\007", "L"},
1951 {5, "\006\003\125\004\010", "ST"},
1952 {5, "\006\003\125\004\011", "street"},
1953 {5, "\006\003\125\004\012", "O"},
1954 {5, "\006\003\125\004\013", "OU"},
1955 {5, "\006\003\125\004\014", "title"},
1956 {5, "\006\003\125\004\015", "description"},
1957 {5, "\006\003\125\004\017", "businessCategory"},
1958 {5, "\006\003\125\004\021", "postalCode"},
1959 {5, "\006\003\125\004\024", "telephoneNumber"},
1960 {5, "\006\003\125\004\027", "facsimileTelephoneNumber"},
1961 {5, "\006\003\125\004\052", "GN"},
1962 {5, "\006\003\125\004\053", "initials"},
1963 {5, "\006\003\125\004\054", "generationQualifier"},
1964 {5, "\006\003\125\004\056", "dnQualifier"},
1965 {5, "\006\003\125\004\101", "pseudonym"},
1966 {5, "\006\003\125\004\141", "organizationIdentifier"},
1967 {11, "\006\011\052\206\110\206\367\015\001\011\001", "emailAddress"},
1968 {12, "\006\012\011\222\046\211\223\362\054\144\001\001", "UID"},
1969 {12, "\006\012\011\222\046\211\223\362\054\144\001\031", "DC"},
1970 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\001", "jurisdictionOfIncorporationLocality"},
1971 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\002", "jurisdictionOfIncorporationStateOrProvince"},
1972 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\003", "jurisdictionOfIncorporationCountry"}
1974 #define oid_table_size (sizeof(oid_table)/sizeof(oid_table[0]))
1976 static int comp_entry(const void *_e1, const void *_e2)
1978 const oid_entry_t *o1 = (oid_entry_t *)_e1;
1979 const oid_entry_t *o2 = (oid_entry_t *)_e2;
1980 size_t min = o1->l;
1981 int ans;
1982 if (o2->l < min) min = o2->l;
1983 ans = memcmp(o1->oid, o2->oid, min);
1984 if (ans) return ans;
1985 if (o1->l < o2->l) return -1;
1986 if (o1->l > o2->l) return 1;
1987 return 0;
1990 static void append_oid_name(CFMutableStringRef s, const char *prefix,
1991 const void *_oid, size_t l, const char *suffix)
1993 oid_entry_t find, *ans;
1994 find.oid = (char *)_oid;
1995 find.l = l;
1996 find.name = NULL;
1997 ans = (oid_entry_t *)
1998 bsearch(&find, oid_table, oid_table_size, sizeof(find), comp_entry);
1999 if (prefix && *prefix)
2000 CFStringAppendCString(s, prefix, kCFStringEncodingASCII);
2001 if (ans)
2002 CFStringAppendCString(s, ans->name, kCFStringEncodingASCII);
2003 else {
2004 CFMutableStringRef temp = CFStringCreateMutable(kCFAllocatorDefault, 0);
2005 const uint8_t *oid = (uint8_t *)_oid;
2006 bool bad = false;
2007 size_t orig_l = l;
2008 const uint8_t *orig_oid = oid;
2009 if (!temp || l < 3 || *oid != 6)
2010 bad = true;
2011 if (!bad) {
2012 data_t data;
2013 der_atom_t atom;
2014 data.d = oid;
2015 data.l = l;
2016 if (!read_der_atom(&data, &atom))
2017 bad = true;
2018 if (!bad && (atom.rawtag != 6 || atom.dl < 1))
2019 bad = true;
2020 if (!bad) {
2021 oid = data.d + atom.hl;
2022 l = atom.dl;
2023 if (l + atom.hl != orig_l)
2024 bad = true;
2027 if (!bad) {
2028 size_t idx = 0;
2029 do {
2030 unsigned idval = 0;
2031 uint8_t byte;
2032 do {
2033 if (!l) {bad=true; break;}
2034 idval <<= 7;
2035 byte = *oid++;
2036 --l;
2037 idval |= byte & 0x7f;
2038 } while (!bad && (byte & 0x80));
2039 if (bad) break;
2040 if (!idx) {
2041 char twoids[32];
2042 unsigned x, y;
2043 if (idval < 40) {
2044 x = 0; y = idval;
2045 } else if (idval < 80) {
2046 x = 1; y = idval - 40;
2047 } else {
2048 x = 2; y = idval - 80;
2050 snprintf(twoids, sizeof(twoids), "%u.%u", x, y);
2051 CFStringAppendCString(temp, twoids, kCFStringEncodingASCII);
2052 idx += 2;
2053 } else {
2054 char oneid[16];
2055 snprintf(oneid, sizeof(oneid), ".%u", idval);
2056 CFStringAppendCString(temp, oneid, kCFStringEncodingASCII);
2057 ++idx;
2059 } while (l && !bad);
2061 if (bad || l || !temp || !CFStringGetLength(temp))
2062 append_hex_dump(s, orig_oid, orig_l);
2063 else
2064 CFStringAppend(s, temp);
2065 if (temp)
2066 CFRelease(temp);
2068 if (suffix && *suffix)
2069 CFStringAppendCString(s, suffix, kCFStringEncodingASCII);
2072 #define DER_TAG_UTF8STRING 12
2073 #define DER_TAG_NUMERICSTRING 18
2074 #define DER_TAG_PRINTABLESTRING 19
2075 #define DER_TAG_TELETEXSTRING 20
2076 #define DER_TAG_VIDEOTEXSTRING 21
2077 #define DER_TAG_IA5STRING 22
2078 #define DER_TAG_GRAPHICSTRING 25
2079 #define DER_TAG_VISIBLESTRING 26
2080 #define DER_TAG_GENERALSTRING 27
2081 #define DER_TAG_UNIVERSALSTRING 28
2082 #define DER_TAG_BMPSTRING 30
2084 /* flags:
2085 * 0x01 => strings only
2086 * 0x02 => 8-bit strings only
2087 * 0x04 => CN Ids strings only
2088 * 0x08 => wildcard CN okay
2089 * 0x10 => create output string
2091 static bool append_attr_value(CFMutableStringRef *s, const void *_d,
2092 const der_atom_t *a, unsigned flags)
2094 const uint8_t *d = (uint8_t *)_d;
2095 CFStringBuiltInEncodings encoding = kCFStringEncodingASCII;
2096 CFStringRef temp;
2097 if (s && !(flags & 0x10) && !*s) return false;
2098 if (!s || !d || !a || !a->dl) return false;
2099 switch (a->rawtag) {
2100 case DER_TAG_UTF8STRING:
2101 case DER_TAG_GRAPHICSTRING:
2102 case DER_TAG_GENERALSTRING:
2103 case DER_TAG_UNIVERSALSTRING:
2104 encoding = kCFStringEncodingUTF8; break;
2105 case DER_TAG_NUMERICSTRING:
2106 case DER_TAG_PRINTABLESTRING:
2107 case DER_TAG_IA5STRING:
2108 encoding = kCFStringEncodingASCII; break;
2109 case DER_TAG_TELETEXSTRING:
2110 case DER_TAG_VIDEOTEXSTRING:
2111 case DER_TAG_VISIBLESTRING:
2112 encoding = kCFStringEncodingISOLatin1; break;
2113 case DER_TAG_BMPSTRING:
2114 if (flags & 0x06) return false;
2115 encoding = kCFStringEncodingUnicode; break;
2116 default:
2117 if (flags & 0x05) return false;
2118 append_hex_dump(*s, d, a->hl + a->dl);
2119 return true;
2121 if (flags & 0x04 && !is_dns_name(d+a->hl, a->dl, !!(flags & 0x08)))
2122 return false;
2123 temp = CFStringCreateWithBytes(kCFAllocatorDefault, d+a->hl, a->dl,
2124 encoding, true);
2125 if (temp) {
2126 if (flags & 0x10)
2127 *s = CFStringCreateMutable(kCFAllocatorDefault, 0);
2128 if (*s)
2129 CFStringAppend(*s, temp);
2130 CFRelease(temp);
2131 } else {
2132 if (flags & 0x05) return false;
2133 append_hex_dump(*s, d, a->hl + a->dl);
2135 return *s != NULL;
2138 CF_INLINE int is_dig(char c)
2140 return '0' <= c && c <= '9';
2143 static void append_year_string(CFMutableStringRef cfstr, const char *vstr)
2145 size_t vl = strlen(vstr);
2146 if ((vl == 13 || vl == 15) && vstr[vl - 1] == 'Z') {
2147 size_t off = 4;
2148 unsigned short uc[4];
2149 if (vl == 13 && is_dig(vstr[0]) && is_dig(vstr[1])) {
2150 int yr2 = (vstr[0] - '0') * 10 + (vstr[1] - '0');
2151 if (yr2 >= 50) {
2152 uc[0] = '1';
2153 uc[1] = '9';
2154 } else {
2155 uc[0] = '2';
2156 uc[1] = '0';
2158 uc[2] = (unsigned char)vstr[0];
2159 uc[3] = (unsigned char)vstr[1];
2160 off = 2;
2161 } else {
2162 uc[0] = vstr[0];
2163 uc[1] = vstr[1];
2164 uc[2] = vstr[2];
2165 uc[3] = vstr[3];
2167 CFStringAppendCharacters(cfstr, uc, 4);
2168 uc[0] = '-';
2169 uc[1] = vstr[off];
2170 uc[2] = vstr[off+1];
2171 uc[3] = '-';
2172 CFStringAppendCharacters(cfstr, uc, 4);
2173 uc[0] = vstr[off+2];
2174 uc[1] = vstr[off+3];
2175 uc[2] = 'T';
2176 CFStringAppendCharacters(cfstr, uc, 3);
2177 uc[0] = vstr[off+4];
2178 uc[1] = vstr[off+5];
2179 uc[2] = ':';
2180 CFStringAppendCharacters(cfstr, uc, 3);
2181 uc[0] = vstr[off+6];
2182 uc[1] = vstr[off+7];
2183 CFStringAppendCharacters(cfstr, uc, 3);
2184 uc[0] = vstr[off+8];
2185 uc[1] = vstr[off+9];
2186 uc[2] = 'Z';
2187 CFStringAppendCharacters(cfstr, uc, 3);
2188 } else {
2189 CFStringAppendCString(cfstr, vstr, kCFStringEncodingASCII);
2193 void CopyCertValidity(SecCertificateRef _cert, CFStringRef *_nb, CFStringRef *_na)
2195 CFDataRef d = cSecCertificateCopyData(_cert);
2196 CFMutableStringRef nb, na;
2197 data_t data;
2198 der_cert_t cert;
2200 if (!d || !_nb || !_na) return;
2201 *_nb = NULL;
2202 *_na = NULL;
2203 data.d = CFDataGetBytePtr(d);
2204 data.l = CFDataGetLength(d);
2205 if (!read_der_cert(&data, &cert)) {
2206 CFRelease(d);
2207 return;
2209 CFRelease(d);
2210 nb = CFStringCreateMutable(kCFAllocatorDefault, 0);
2211 if (!nb)
2212 return;
2213 na = CFStringCreateMutable(kCFAllocatorDefault, 0);
2214 if (!na) {
2215 CFRelease(nb);
2216 return;
2218 append_year_string(nb, cert.notBefore);
2219 append_year_string(na, cert.notAfter);
2220 *_nb = nb;
2221 *_na = na;
2224 static CFStringRef CopyCertName(SecCertificateRef _cert, bool issuer)
2226 CFDataRef d = cSecCertificateCopyData(_cert);
2227 CFMutableStringRef ans = CFStringCreateMutable(kCFAllocatorDefault, 0);
2228 bool good = false;
2230 for (;;) {
2231 data_t data;
2232 const data_t *name;
2233 der_cert_t cert;
2234 der_atom_t atom;
2235 bool badset = false;
2237 if (!d || !ans) break;
2238 data.d = CFDataGetBytePtr(d);
2239 data.l = CFDataGetLength(d);
2240 if (!read_der_cert(&data, &cert)) break;
2241 name = issuer ? &cert.issuer : &cert.subject;
2242 data.d = name->d;
2243 data.l = name->l;
2244 if (data.d && data.l) {
2245 if (!read_der_atom(&data, &atom)) break;
2246 if (atom.rawtag != 0x30) break;
2247 data.l -= atom.hl;
2248 data.d += atom.hl;
2249 while (data.l) {
2250 data_t set;
2251 unsigned setidx = 0;
2252 badset = true;
2253 if (!read_der_atom(&data, &atom)) break;
2254 if (atom.rawtag != 0x31) break;
2255 set.d = data.d + atom.hl;
2256 set.l = atom.dl;
2257 data.l -= atom.hl + atom.dl;
2258 data.d += atom.hl + atom.dl;
2259 for (;;) {
2260 data_t oid;
2261 if (!read_der_atom(&set, &atom)) break;
2262 if (atom.rawtag != 0x30) break;
2263 set.l -= atom.hl;
2264 set.d += atom.hl;
2265 if (!read_der_atom(&set, &atom)) break;
2266 if (atom.rawtag != 6) break;
2267 oid.d = set.d;
2268 oid.l = atom.hl + atom.dl;
2269 set.l -= atom.hl + atom.dl;
2270 set.d += atom.hl + atom.dl;
2271 if (!read_der_atom(&set, &atom)) break;
2272 append_oid_name(ans, setidx++?"/+":"/", oid.d, oid.l, "=");
2273 append_attr_value(&ans, set.d, &atom, 0);
2274 set.l -= atom.hl + atom.dl;
2275 set.d += atom.hl + atom.dl;
2276 if (!set.l) {
2277 badset=false;
2278 break;
2281 if (badset) break;
2283 if (badset || data.l) break;
2284 good = true;
2286 break;
2288 if (d) CFRelease(d);
2289 if (!good && ans) {CFRelease(ans); ans=NULL;}
2290 return MakeVisibleString(ans);
2293 CFStringRef CopyCertSubject(SecCertificateRef _cert)
2295 return CopyCertName(_cert, false);
2298 CFStringRef CopyCertSubjectKeyId(SecCertificateRef _cert)
2300 return CopyCertKeyId(_cert, false);
2303 CFStringRef CopyCertIssuer(SecCertificateRef _cert)
2305 return CopyCertName(_cert, true);
2308 CFStringRef CopyCertIssuerKeyId(SecCertificateRef _cert)
2310 return CopyCertKeyId(_cert, true);
2313 /* return CFArrayRef if arr else CFStringRef
2314 * flags:
2315 * 0x01 includes DNS alts
2316 * 0x02 includes IPv4 alts
2317 * 0x04 includes IPv6 alts
2318 * 0x08 include IP other alts
2320 static CFTypeRef CopyCertSubjectAltNamesInt(const der_cert_t *cert, bool arr, unsigned flags)
2322 data_t data;
2323 CFTypeRef ans = arr ?
2324 (CFTypeRef)CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks) :
2325 (CFTypeRef)CFStringCreateMutable(kCFAllocatorDefault, 0);
2326 bool good = false;
2328 do {
2329 der_atom_t atom;
2331 if (!cert || !ans) break;
2332 if (!cert->subjectAltNames.d || !cert->subjectAltNames.l) break;
2333 data.d = cert->subjectAltNames.d;
2334 data.l = cert->subjectAltNames.l;
2335 if (!read_der_atom(&data, &atom)) break;
2336 if (atom.rawtag != 0x30) break;
2337 data.l -= atom.hl;
2338 data.d += atom.hl;
2339 do {
2340 if (!read_der_atom(&data, &atom)) break;
2341 if (atom.rawtag == 0x82 && (flags & 0x01)) {
2342 CFStringRef temp;
2343 if (!arr && CFStringGetLength((CFStringRef)ans))
2344 CFStringAppendCString((CFMutableStringRef)ans, ",", kCFStringEncodingASCII);
2345 temp = CFStringCreateWithBytes(kCFAllocatorDefault, data.d+atom.hl,
2346 atom.dl, kCFStringEncodingASCII, true);
2347 if (!temp) break;
2348 if (arr)
2349 CFArrayAppendValue((CFMutableArrayRef)ans, temp);
2350 else
2351 CFStringAppend((CFMutableStringRef)ans, temp);
2352 CFRelease(temp);
2353 } else if (atom.rawtag == 0x87 && (flags & 0x0e)) {
2354 if ((atom.dl == 4 && (flags & 0x02)) ||
2355 (atom.dl == 16 && (flags & 0x04)) ||
2356 (atom.dl != 4 && atom.dl != 16 && (flags & 0x08))) {
2357 if (arr) {
2358 CFDataRef dtemp = CFDataCreate(kCFAllocatorDefault, data.d+atom.hl, atom.dl);
2359 if (!dtemp) break;
2360 CFArrayAppendValue((CFMutableArrayRef)ans, dtemp);
2361 CFRelease(dtemp);
2362 } else {
2363 if (CFStringGetLength((CFStringRef)ans))
2364 CFStringAppendCString((CFMutableStringRef)ans, ",", kCFStringEncodingASCII);
2365 if (atom.dl == 4) {
2366 char ipv4str[16];
2367 const uint8_t *ip = data.d+atom.hl;
2368 sprintf(ipv4str, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
2369 CFStringAppendCString((CFMutableStringRef)ans, ipv4str, kCFStringEncodingASCII);
2370 } else if (atom.dl == 16) {
2371 char ntopbuff[INET6_ADDRSTRLEN];
2372 if (!inet_ntop(AF_INET6, data.d+atom.hl, ntopbuff, sizeof(ntopbuff)))
2373 break;
2374 CFStringAppendCString((CFMutableStringRef)ans, ntopbuff, kCFStringEncodingASCII);
2375 } else {
2376 append_hex_dump((CFMutableStringRef)ans, data.d+atom.hl, atom.dl);
2381 data.l -= atom.hl + atom.dl;
2382 data.d += atom.hl + atom.dl;
2383 } while (data.l);
2384 if (!data.l && ( (arr && CFArrayGetCount((CFArrayRef)ans) ) ||
2385 (!arr && CFStringGetLength((CFStringRef)ans)) ))
2386 good = true;
2387 } while (0);
2388 if (!good && ans) {CFRelease(ans); ans=NULL;}
2389 return ans;
2392 static CFArrayRef CopyCertSubjectCNIds(const der_cert_t *cert)
2394 CFMutableArrayRef ans =
2395 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
2396 bool good = false;
2398 do {
2399 data_t data;
2400 der_atom_t atom;
2402 if (!ans || !cert || !cert->subject.d || !cert->subject.l) break;
2403 data.d = cert->subject.d;
2404 data.l = cert->subject.l;
2405 if (!read_der_atom(&data, &atom)) break;
2406 if (atom.rawtag != 0x30) break;
2407 data.l -= atom.hl;
2408 data.d += atom.hl;
2409 while (data.l) {
2410 data_t set;
2411 if (!read_der_atom(&data, &atom)) break;
2412 if (atom.rawtag != 0x31) break;
2413 set.d = data.d + atom.hl;
2414 set.l = atom.dl;
2415 data.l -= atom.hl + atom.dl;
2416 data.d += atom.hl + atom.dl;
2417 if (!read_der_atom(&set, &atom)) break;
2418 if (atom.rawtag != 0x30) break;
2419 if (atom.hl + atom.dl == set.l) { /* single-value CN only */
2420 CFMutableStringRef cnid;
2421 data_t oid;
2422 set.l -= atom.hl;
2423 set.d += atom.hl;
2424 if (!read_der_atom(&set, &atom)) break;
2425 if (atom.rawtag != 6) break;
2426 oid.d = set.d;
2427 oid.l = atom.hl + atom.dl;
2428 if (data_matches(&oid, &OID_CommonName)) {
2429 set.l -= atom.hl + atom.dl;
2430 set.d += atom.hl + atom.dl;
2431 if (!read_der_atom(&set, &atom)) break;
2432 if (append_attr_value(&cnid, set.d, &atom, 0x1f) && cnid) {
2433 CFArrayAppendValue(ans, cnid);
2434 CFRelease(cnid);
2439 if (!data.l)
2440 good = true;
2441 } while (0);
2442 if (ans && (!good || !CFArrayGetCount(ans))) {
2443 CFRelease(ans);
2444 ans=NULL;
2446 return ans;
2449 CFStringRef CopyCertSubjectAltNamesString(SecCertificateRef _cert)
2451 CFDataRef d = cSecCertificateCopyData(_cert);
2452 data_t data;
2453 der_cert_t cert;
2454 CFStringRef ans = NULL;
2456 if (!d) return NULL;
2457 data.d = CFDataGetBytePtr(d);
2458 data.l = CFDataGetLength(d);
2459 if (read_der_cert(&data, &cert))
2460 ans = (CFStringRef)CopyCertSubjectAltNamesInt(&cert, false, 0x0f);
2461 CFRelease(d);
2462 return MakeVisibleString(ans);
2465 /* mode:
2466 * 0 = DNS/CN ids
2467 * 4 = IPv4 ids
2468 * 16 = IPv6 ids
2470 static CFArrayRef CopyCertSubjectIds(der_cert_t *c, unsigned mode)
2472 CFArrayRef ans = NULL;
2473 if (!c || (mode != 0 && mode != 4 && mode != 16)) return NULL;
2474 if (mode == 4)
2475 return ans = (CFArrayRef)CopyCertSubjectAltNamesInt(c, true, 0x02);
2476 if (mode == 16)
2477 return ans = (CFArrayRef)CopyCertSubjectAltNamesInt(c, true, 0x04);
2478 else {
2479 ans = (CFArrayRef)CopyCertSubjectAltNamesInt(c, true, 0x01);
2480 if (!ans || !CFArrayGetCount(ans)) {
2481 if (ans) CFRelease(ans);
2482 ans = CopyCertSubjectCNIds(c);
2485 if (ans && !CFArrayGetCount(ans)) {
2486 CFRelease(ans);
2487 ans = NULL;
2489 return ans;
2492 OSStatus VerifyTrustChain(SecTrustRef trust, CFArrayRef customRootsOrNull,
2493 unsigned certFlags, unsigned flags, const char *peername,
2494 CFArrayRef pinnedKeySet)
2496 SecTrustResultType result;
2497 CFArrayRef chain = NULL;
2498 CSSM_TP_APPLE_EVIDENCE_INFO *evidence;
2499 bool pkonly = (certFlags & 0x02) ? true : false;
2500 bool explicitCertsOnly = (certFlags & 0x01) ? true : false;
2501 bool nameonly = (certFlags & 0x04) ? true : false;
2502 OSStatus err;
2503 size_t i, cnt;
2504 if ((pinnedKeySet && !CFArrayGetCount(pinnedKeySet)) || (pkonly && !pinnedKeySet))
2505 return paramErr;
2506 if (nameonly && !pkonly && (!peername || !*peername))
2507 return paramErr;
2508 err = cSecTrustGetResult(trust, &result, &chain, &evidence);
2509 if (err == errSecTrustNotAvailable) {
2510 /* We need to evaluate first */
2511 CFArrayRef anchors = customRootsOrNull;
2512 if (chain) CFRelease(chain);
2513 if (anchors)
2514 CFRetain(anchors);
2515 else {
2516 err = SecTrustCopyAnchorCertificates(&anchors);
2517 if (err) return err;
2519 err = SecTrustSetAnchorCertificates(trust, anchors);
2520 CFRelease(anchors);
2521 if (err) return err;
2522 err = cSecTrustSetAnchorCertificatesOnly(trust, customRootsOrNull ? true : false);
2523 if (err && err != unimpErr) return err;
2524 err = SecTrustEvaluate(trust, &result);
2525 if (err && !pkonly && !nameonly) return err;
2526 chain = NULL;
2527 err = cSecTrustGetResult(trust, &result, &chain, &evidence);
2529 if (err) {
2530 if (chain) CFRelease(chain);
2531 return err;
2533 if (!chain || !evidence || !CFArrayGetCount(chain)) {
2534 if (chain) CFRelease(chain);
2535 return errSSLXCertChainInvalid;
2537 if (pkonly) goto pinned_key_check;
2538 cnt = (size_t)CFArrayGetCount(chain);
2539 if ((peername && *peername) ||
2540 (!(flags & CSSM_TP_ACTION_LEAF_IS_CA) &&
2541 !(evidence[0].StatusBits & CSSM_CERT_STATUS_IS_ROOT))) {
2542 CFDataRef certder = cSecCertificateCopyData(
2543 (SecCertificateRef)CFArrayGetValueAtIndex(chain, 0));
2544 data_t der;
2545 der_cert_t cert;
2546 if (!certder)
2547 return errSSLBadCert;
2548 der.d = CFDataGetBytePtr(certder);
2549 der.l = CFDataGetLength(certder);
2550 if (!read_der_cert(&der, &cert))
2551 err = errSSLBadCert;
2553 /* First confirm we have a host name match. SecureTransport should do
2554 * this for us (but will not give us a decent result code) except that
2555 * it has problems with IPv6 address matching */
2556 if (!err && peername && *peername) {
2557 size_t peerlen = strlen(peername);
2558 union {
2559 uint8_t ipv6[16];
2560 uint8_t ipv4[4];
2561 } ipa;
2562 int mode = -1;
2563 if (parse_ipv4_name(peername, peerlen, ipa.ipv4)) mode = 4;
2564 else if (is_dns_name(peername, peerlen, false)) mode = 0;
2565 else if (parse_ipv6_name(peername, peerlen, ipa.ipv6)) mode = 16;
2566 if (mode == -1)
2567 /* if we can't parse peername it can't possibly match! */
2568 err = errSSLHostNameMismatch;
2569 if (!err) {
2570 CFArrayRef ids = CopyCertSubjectIds(&cert, mode);
2571 if (ids && !CFArrayGetCount(ids)) {
2572 CFRelease(ids);
2573 ids = NULL;
2575 if (!ids)
2576 /* if we don't have anything to match against it can't possibly match! */
2577 err = errSSLHostNameMismatch;
2578 else {
2579 size_t j, idcnt = CFArrayGetCount(ids);
2580 bool matched = false;
2581 for (j = 0; j < idcnt; ++j) {
2582 CFTypeRef oneid = (CFTypeRef)CFArrayGetValueAtIndex(ids, j);
2583 if (mode) {
2584 const uint8_t *p;
2585 size_t l;
2586 if (CFDataGetTypeID() != CFGetTypeID(oneid)) continue;
2587 p = (uint8_t *)CFDataGetBytePtr((CFDataRef)oneid);
2588 l = (size_t)CFDataGetLength((CFDataRef)oneid);
2589 if (l != (size_t)mode) continue;
2590 if (memcmp(ipa.ipv6, p, l) == 0) {
2591 matched = true;
2592 break;
2594 } else {
2595 CFDataRef dnsname;
2596 if (CFStringGetTypeID() != CFGetTypeID(oneid)) continue;
2597 dnsname = CFStringCreateExternalRepresentation(
2598 kCFAllocatorDefault, (CFStringRef)oneid,
2599 kCFStringEncodingASCII, 0);
2600 if (!dnsname) continue;
2601 if (peername_matches_id(peername, dnsname))
2602 matched = true;
2603 CFRelease(dnsname);
2604 if (matched)
2605 break;
2608 CFRelease(ids);
2609 if (!matched)
2610 err = errSSLHostNameMismatch;
2615 /* Confirm that the first certificate is NOT a CA (otherwise it's not a
2616 * valid chain), but again SecureTransport should have already checked that
2617 * for us. CSSM_TP_ACTION_LEAF_IS_CA overrides. Also we never check the
2618 * root certificate even if it's also the leaf. */
2619 if (!nameonly && !err && !(flags & CSSM_TP_ACTION_LEAF_IS_CA) &&
2620 !(evidence[0].StatusBits & CSSM_CERT_STATUS_IS_ROOT) && cert.isCA)
2621 err = errSSLXCertChainInvalid;
2623 CFRelease(certder);
2624 if (err) return err;
2626 if (nameonly) goto pinned_key_check;
2627 if (explicitCertsOnly) {
2628 /* Check all but root */
2629 for (i = 0; i < cnt; ++i) {
2630 if (!(evidence[i].StatusBits & CSSM_CERT_STATUS_IS_IN_INPUT_CERTS) &&
2631 !(evidence[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT)) {
2632 /* If the magical cert had not appeared, the chain would have stopped
2633 * here and the error would be no root, so return that error */
2634 CFRelease(chain);
2635 return errSSLNoRootCert;
2639 if (!(flags & CSSM_TP_ACTION_ALLOW_EXPIRED) ||
2640 !(flags & CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT)) {
2641 /* check for expired or not yet valid certs */
2642 for (i = 0; i < cnt; ++i) {
2643 if ((flags & CSSM_TP_ACTION_ALLOW_EXPIRED) &&
2644 !(evidence[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT))
2645 continue;
2646 if ((flags & CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT) &&
2647 (evidence[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT))
2648 continue;
2649 if (evidence[i].StatusBits & CSSM_CERT_STATUS_EXPIRED) {
2650 CFRelease(chain);
2651 return errSSLCertExpired;
2653 if (evidence[i].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) {
2654 CFRelease(chain);
2655 return errSSLCertNotYetValid;
2659 /* check for no root */
2660 if (!(evidence[cnt-1].StatusBits & CSSM_CERT_STATUS_IS_ROOT)) {
2661 CFRelease(chain);
2662 return errSSLNoRootCert;
2664 /* check for unknown root */
2665 if (!(evidence[cnt-1].StatusBits & CSSM_CERT_STATUS_IS_IN_ANCHORS)) {
2666 CFRelease(chain);
2667 return errSSLUnknownRootCert;
2669 if (customRootsOrNull) {
2670 /* make sure we're not using a gratuitous root, Mac OS X likes to just
2671 * go ahead and use its anchors sometimes despite settings to the contrary */
2672 if (!SecCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(chain, cnt-1),
2673 customRootsOrNull)) {
2674 CFRelease(chain);
2675 return errSSLNoRootCert;
2678 /* everything looks good, so check the trust result code now */
2679 switch (result) {
2680 case kSecTrustResultProceed:
2681 case kSecTrustResultUnspecified:
2682 /* good result */
2683 break;
2684 case kSecTrustResultDeny:
2685 /* DENIED! */
2686 CFRelease(chain);
2687 return errSecTrustSettingDeny;
2688 default:
2689 /* everything else (confirm, invalid, recoverable, fatal, other) */
2690 CFRelease(chain);
2691 return errSecNotTrusted;
2693 pinned_key_check:
2694 if (pinnedKeySet) {
2695 CFDataRef certder = cSecCertificateCopyData(
2696 (SecCertificateRef)CFArrayGetValueAtIndex(chain, 0));
2697 data_t der;
2698 der_cert_t cert;
2699 CFDataRef peerPubKey;
2700 bool pinok;
2701 if (!certder)
2702 return errSSLBadCert;
2703 der.d = CFDataGetBytePtr(certder);
2704 der.l = CFDataGetLength(certder);
2705 if (!read_der_cert(&der, &cert)) {
2706 CFRelease(certder);
2707 return errSSLBadCert;
2709 if (certFlags & 0x08) {
2710 unsigned char sha256[CC_SHA256_DIGEST_LENGTH];
2711 CC_SHA256_CTX ctx;
2712 CC_SHA256_Init(&ctx);
2713 CC_SHA256_Update(&ctx, cert.subjectPubKey.d, (CC_LONG)cert.subjectPubKey.l);
2714 CC_SHA256_Final(sha256, &ctx);
2715 peerPubKey = CFDataCreate(kCFAllocatorDefault, sha256, sizeof(sha256));
2716 } else {
2717 peerPubKey = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
2718 cert.subjectPubKey.d, cert.subjectPubKey.l, kCFAllocatorNull);
2720 if (!peerPubKey) {
2721 CFRelease(certder);
2722 return memFullErr;
2724 pinok = BlobInArray(peerPubKey, pinnedKeySet);
2725 CFRelease(peerPubKey);
2726 CFRelease(certder);
2727 if (!pinok)
2728 return errSecPinnedKeyMismatch;
2730 CFRelease(chain);
2731 return noErr;
2734 #elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
2736 #error iOS is not currently supported
2738 #endif /* TARGET_OS_EMBEDDED || TARGET_OS_IPHONE */