darwinssl: update to latest that provides private key pw hint
[git-osx-installer.git] / patches / curl / stcompat.c
blobb57f13b984c0858ced4823c118eff2849fe4f376
1 /*
3 stcompat.c -- SecureTransport compatibility implementation
4 Copyright (C) 2014 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 <limits.h>
22 #include <objc/objc-runtime.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <sys/types.h>
28 #include <sys/uio.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 #include <arpa/inet.h>
32 #include <pwd.h>
33 #include <crt_externs.h>
34 #include "stcompat.h"
36 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
37 #include <dlfcn.h>
38 __attribute__((constructor,used)) static void stcompat_initialize(void);
39 #endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
41 extern CFStringRef CFStringCreateWithBytesNoCopy(
42 CFAllocatorRef alloc,
43 const UInt8 *bytes,
44 CFIndex numBytes,
45 CFStringEncoding encoding,
46 Boolean isExternalRepresentation,
47 CFAllocatorRef contentsDeallocator); /* available 10.4 but not in header */
48 extern CFStringRef NSTemporaryDirectory(void);
50 static CFStringRef CFCopyTemporaryDirectory(void)
52 id pool = objc_msgSend(objc_getClass("NSAutoreleasePool"), sel_getUid("new"));
53 CFStringRef dir = (CFStringRef)NSTemporaryDirectory();
54 if (dir) CFRetain(dir);
55 objc_msgSend(pool, sel_getUid("drain"));
56 if (dir) {
57 unsigned len = (unsigned)CFStringGetLength(dir);
58 unsigned l = len;
59 while (l > 1 &&
60 (unsigned)CFStringGetCharacterAtIndex(dir, l-1) == (unsigned)'/') {
61 --l;
63 if (l < len) {
64 CFStringRef old = dir;
65 dir = CFStringCreateWithSubstring(
66 kCFAllocatorDefault, dir, CFRangeMake(0, l));
67 if (dir)
68 CFRelease(old);
69 else
70 dir = old;
73 return dir;
76 char *CFStringCreateUTF8String(CFStringRef s, Boolean release)
78 size_t m;
79 char *c;
81 if (!s) return NULL;
82 m = (size_t)CFStringGetMaximumSizeForEncoding(
83 CFStringGetLength(s), kCFStringEncodingUTF8) + 1;
84 c = (char *)malloc(m);
85 if (!c) {
86 if (release) CFRelease(s);
87 return NULL;
89 if (!CFStringGetCString(s, c, m, kCFStringEncodingUTF8)) {
90 free(c);
91 c = NULL;
93 if (release) CFRelease(s);
94 return c;
97 CFDataRef CFDataCreateWithContentsOfFile(CFAllocatorRef a, const char *f)
99 char buff[4096];
100 CFMutableDataRef d = CFDataCreateMutable(a, 0);
101 int fd;
102 ssize_t cnt;
103 if (!d) return NULL;
104 fd = open(f, O_RDONLY);
105 if (fd < 0) {
106 CFRelease(d);
107 return NULL;
109 do {
110 cnt = read(fd, buff, sizeof(buff));
111 if (cnt > 0) CFDataAppendBytes(d, (UInt8 *)buff, (size_t)cnt);
112 } while (cnt > 0);
113 close(fd);
114 if (cnt) {
115 CFRelease(d);
116 return NULL;
118 return d;
121 #undef memmem
122 #define memmem(v1,l1,v2,l2) cmemmem(v1,l1,v2,l2)
123 static void *cmemmem(const void *_m, size_t ml, const void *_s, size_t sl)
125 const char *m = (const char *)_m;
126 const char *s = (const char *)_s;
127 if (!ml || !sl || ml < sl) return NULL;
128 if (sl == 1) return memchr(m, *s, ml);
129 if (ml == sl) return (void *)(memcmp(m, s, sl) ? NULL : m);
130 do {
131 size_t o;
132 const char *p = memchr(m, *s, ml);
133 if (!p) return NULL;
134 o = p - m;
135 ml -= o;
136 m += o;
137 if (ml < sl) return NULL;
138 if (!memcmp(m, s, sl)) return (void *)m;
139 ++m;
140 --ml;
141 } while (ml >= sl);
142 return NULL;
145 CF_INLINE int is_eol(int c)
147 return c == '\n' || c == '\r';
150 CF_INLINE int is_lb(int c)
152 return c == '\n' || c == '\r' || c == '\f';
155 CF_INLINE int is_prnt(int c)
157 return c >= ' ' && c <= '~';
160 static int has_lb(const void *_m, size_t l)
162 const char *m = (const char *)_m;
163 while (l) {
164 if (is_lb(*m)) return 1;
165 --l;
166 ++m;
168 return 0;
171 static int has_prnt(const void *_m, size_t l)
173 const char *m = (const char *)_m;
174 while (l) {
175 if (!is_prnt(*m)) return 0;
176 --l;
177 ++m;
179 return 1;
183 * returns start of "-----BEGIN XXXX-----\n" line or NULL if not found/error
184 * If returns NULL then *ot == 0 means not found, *ot == -1 means bad line
185 * If returns non-NULL then *ol is length through "-----\n" and *ot is length
186 * of "XXXX" part (which obviously starts at return value + 11 for BEGIN or
187 * value + 9 for END)
188 * If e is non-zero look for ----END rather than ----BEGIN
190 static const char *find_be(const void *_m, size_t l, size_t *ol, int *ot, int e)
192 const char *m = (const char *)_m;
193 const char *origm = m;
194 const char *marker = e ? "-----END " : "-----BEGIN ";
195 size_t mkl = e ? 9 : 11;
196 *ot = 0;
197 while (l) {
198 const char *t;
199 const char *p = (char *)memmem(m, l, marker, mkl);
200 if (!p) return NULL;
201 l -= (p - m) + mkl;
202 m = p + mkl;
203 if (p > origm && !is_eol(p[-1])) continue;
204 t = (char *)memmem(m, l, "-----", 5);
205 if (!t) return NULL;
206 l -= (t - m);
207 if (l > 5 && !is_eol(t[5])) continue;
208 if (has_lb(p, t-p)) continue;
209 if ((size_t)(t-p) > (76 - mkl - 5) || !has_prnt(p, t-p)) {
210 *ot = -1;
211 return NULL;
213 *ot = (int)(t - m);
214 l -= 5;
215 m = t + 5;
216 if (l && *m == '\r') {
217 ++m;
218 --l;
220 if (l && *m == '\n') {
221 ++m;
222 --l;
224 *ol = m - p;
225 return p;
227 return NULL;
230 typedef enum {
231 pemtype_unknown,
232 pemtype_certificate, /* "CERTIFICATE" or "TRUSTED CERTIFICATE" */
233 pemtype_privatekey_rsa /* "RSA PRIVATE KEY" */
234 } pemtype_t;
236 typedef struct {
237 const char *start; /* Armour start "-----BEGIN XXXXX-----\n" */
238 size_t len; /* Length through armour end "-----END XXXXX-----\n" */
239 /* Body starts after "-----BEGIN XXXXX-----\n" */
240 const char *body;
241 size_t bodylen; /* Length though "\n" BEFORE final "-----END XXXXX-----\n" */
242 /* Kind starts at start + 11 */
243 size_t kindlen; /* length of "XXXXX" from "-----BEGIN XXXXX-----\n" */
244 pemtype_t type;
245 } peminfo_t;
247 static int nextpem(const char *p, size_t l, peminfo_t *o)
249 size_t beglen, endlen;
250 int begtype, endtype;
251 const char *end;
252 const char *beg = find_be(p, l, &beglen, &begtype, 0);
253 if (!beg) return begtype;
254 end = find_be(p + beglen, l - beglen, &endlen, &endtype, 1);
255 if (!end || begtype != endtype || memcmp(beg+11, end+9, (size_t)begtype))
256 return -1;
257 o->start = beg;
258 o->len = (end + endlen) - beg;
259 o->body = beg + beglen;
260 o->bodylen = end - (beg + beglen);
261 o->kindlen = (size_t)begtype;
262 if (begtype == 11 && !memcmp(beg + 11, "CERTIFICATE", 11)) {
263 o->type = pemtype_certificate;
264 } else if (begtype == 19 && !memcmp(beg + 11, "TRUSTED CERTIFICATE", 19)) {
265 o->type = pemtype_certificate;
266 } else if (begtype == 15 && !memcmp(beg + 11, "RSA PRIVATE KEY", 15)) {
267 o->type = pemtype_privatekey_rsa;
268 } else {
269 o->type = pemtype_unknown;
271 return (int)((o->start + o->len) - p);
274 static const signed char b64tab[256] = {
275 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
276 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
277 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x3E,-1,-1,-1,0x3F,
278 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,-1,-1,-1,0x40,-1,-1,
279 -1,0,1,2,3,4,5,6,7,8,9,0x0A,0x0B,0x0C,0x0D,0x0E,
280 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,-1,-1,-1,-1,-1,
281 -1,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
282 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,-1,-1,-1,-1,-1,
283 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
284 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
285 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
286 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
287 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
288 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
289 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
290 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
293 static void convert24(const uint8_t *input, uint8_t *output)
295 output[0] = ((b64tab[input[0]]&0x3F)<<2)|((b64tab[input[1]]&0x3F)>>4);
296 output[1] = (((b64tab[input[1]]&0x3F)&0x0F)<<4)|((b64tab[input[2]]&0x3F)>>2);
297 output[2] = (((b64tab[input[2]]&0x3F)&0x03)<<6)|(b64tab[input[3]]&0x3F);
300 static CFDataRef CFDataCreateFromBase64(CFAllocatorRef a, const void *_b, size_t l)
302 uint8_t inp[4];
303 uint8_t out[3];
304 int i;
305 CFMutableDataRef d;
306 const uint8_t *p = (uint8_t *)_b;
307 if (l && !p) return NULL;
308 d = CFDataCreateMutable(a, 0);
309 if (!d) return NULL;
310 if (!l) return d;
311 for (i=0; l; ++p, --l) {
312 uint8_t c = *p;
313 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f')
314 continue;
315 if (b64tab[c] < 0) {
316 CFRelease(d);
317 return NULL;
319 inp[i++] = c;
320 if (i == 4) {
321 convert24(inp, out);
322 i = 0;
323 if (inp[3] == '=') {
324 CFDataAppendBytes(d, out, inp[2] == '=' ? 1 : 2);
325 break;
327 CFDataAppendBytes(d, out, 3);
330 if (i != 0) {
331 CFRelease(d);
332 return NULL;
334 return d;
337 static SecCertificateRef createvalidcert(CFDataRef d)
339 SecCertificateRef cert = cSecCertificateCreateWithData(kCFAllocatorDefault, d);
340 if (!cert) return NULL;
341 if (!CheckCertOkay(cert)) {
342 CFRelease(cert);
343 return NULL;
345 return cert;
348 CFArrayRef CreateCertsArrayWithData(CFDataRef d, const errinfo_t *e)
350 const char *certs, *p;
351 size_t certslen, plen, cnt = 1;
352 CFMutableArrayRef a;
353 if (!d) return NULL;
354 certs = (char *)CFDataGetBytePtr(d);
355 certslen = (size_t)CFDataGetLength(d);
356 a = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
357 if (!a) return NULL;
358 p = certs;
359 plen = certslen;
360 while (plen) {
361 peminfo_t pem;
362 int readcnt = nextpem(p, plen, &pem);
363 if (!readcnt && p == certs) {
364 /* assume it's a DER cert */
365 SecCertificateRef cert;
366 CFDataRef der = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
367 (UInt8 *)certs, certslen, kCFAllocatorNull);
368 if (!der) {
369 CFRelease(a);
370 return NULL;
372 cert = createvalidcert(der);
373 CFRelease(der);
374 if (!cert) {
375 if (e)
376 e->f(e->u, "Invalid CA certificate bad DER data");
377 CFRelease(a);
378 return NULL;
380 CFArrayAppendValue(a, cert);
381 CFRelease(cert);
382 return a;
383 } else if (readcnt == -1) {
384 if (e)
385 e->f(e->u, "Invalid CA certificate #%u (offset %u) in bundle",
386 (unsigned)cnt, (unsigned)(p-certs));
387 CFRelease(a);
388 return NULL;
389 } else if (readcnt && pem.type == pemtype_certificate) {
390 CFDataRef der = CFDataCreateFromBase64(kCFAllocatorDefault, pem.body, pem.bodylen);
391 SecCertificateRef cert;
392 if (!der) {
393 if (e)
394 e->f(e->u, "Invalid CA certificate #%u (offset %u) bad base 64 in bundle",
395 (unsigned)cnt, (unsigned)(pem.start-certs));
396 CFRelease(a);
397 return NULL;
399 cert = createvalidcert(der);
400 CFRelease(der);
401 if (!cert) {
402 if (e)
403 e->f(e->u, "Invalid CA certificate #%u (offset %u) bad cert data in bundle",
404 (unsigned)cnt, (unsigned)(pem.start-certs));
405 CFRelease(a);
406 return NULL;
408 CFArrayAppendValue(a, cert);
409 CFRelease(cert);
410 ++cnt;
411 } else if (!readcnt) break;
412 plen -= (pem.start + pem.len) - p;
413 p = pem.start + pem.len;
415 if (!CFArrayGetCount(a)) {
416 CFRelease(a);
417 a = NULL;
419 return a;
422 typedef struct {
423 /* note that we use geteuid and not getuid because any created files will
424 * end up being owned by geteuid and therefore using geteuid()'s HOME wil
425 * end up being the least disruptive and also if geteuid() != getuid() then
426 * we probably can't read getuid()'s HOME anyway so that's a guaranteed fail */
427 char *home; /* "HOME=..." from getpwuid(geteuid()) if different from environ */
428 char *cur_home; /* "HOME=..." as found in environ if home set otherwise NULL */
429 } homedirs_t;
431 static char *find_home_env(void)
433 char ***eptr = _NSGetEnviron();
434 char **ptr;
435 if (!eptr) return NULL;
436 ptr = *eptr;
437 while (*ptr && strncmp(*ptr, "HOME=", 5)) {
438 ++ptr;
440 return *ptr ? *ptr : NULL;
443 static void get_home_dirs(homedirs_t *dirs)
445 struct passwd *pwinf;
447 if (!dirs) return;
448 dirs->home = NULL;
449 dirs->cur_home = find_home_env();
450 pwinf = getpwuid(geteuid());
451 if (pwinf && pwinf->pw_dir &&
452 (!dirs->cur_home || strcmp(dirs->cur_home+5, pwinf->pw_dir)))
453 asprintf(&dirs->home, "HOME=%s", pwinf->pw_dir);
454 if (!dirs->home)
455 dirs->cur_home = NULL;
458 static void free_home_dirs(homedirs_t *dirs)
460 if (dirs)
461 free(dirs->home);
464 typedef struct {
465 SecKeychainRef ref;
466 char pw[16]; /* random 15-character (0x20-0x7e), NULL terminated password */
467 char loc[1]; /* Always will have at least a NULL byte */
468 } tempch_t;
470 static void gen_rand_pw(void *_out, size_t len)
472 unsigned char *out = (unsigned char *)_out;
473 int fd;
474 if (!out || !len) return;
475 fd = open("/dev/random", O_RDONLY);
476 if (fd) {
477 do {
478 ssize_t cnt, i;
479 do {
480 cnt = read(fd, out, len);
481 } while (cnt == -1 && errno == EINTR);
482 if (cnt <= 0) return;
483 for (i = 0; i < cnt; ++i) {
484 out[i] = (unsigned char)((((unsigned)out[i] * 95) >> 8) + 32);
486 len -= (size_t)cnt;
487 } while (len);
488 close(fd);
492 static tempch_t *new_temp_keych(void)
494 tempch_t *ans;
495 char newdir[PATH_MAX];
496 Boolean okay;
497 CFStringRef tempdir = CFCopyTemporaryDirectory();
499 if (!tempdir) return NULL;
500 okay = CFStringGetCString(tempdir, newdir, sizeof(newdir) - 32, kCFStringEncodingUTF8);
501 CFRelease(tempdir);
502 if (!okay) return NULL;
503 strcat(newdir, "/tch.XXXXXX");
504 ans = (tempch_t *)malloc(sizeof(tempch_t) + strlen(newdir) + 14 /* "/temp.keychain" */);
505 if (!ans) return NULL;
506 ans->ref = NULL;
507 strcpy(ans->loc, newdir);
508 strlcpy(ans->pw, "(:vCZ\"t{UA-zl3g", sizeof(ans->pw)); /* fallback if random fails */
509 gen_rand_pw(ans->pw, sizeof(ans->pw)-1);
510 ans->pw[sizeof(ans->pw)-1] = '\0';
511 if (!mkdtemp(ans->loc)) {
512 free(ans);
513 return NULL;
515 strcat(ans->loc, "/temp.keychain");
516 return ans;
519 static void del_temp_keych(tempch_t *keych)
521 size_t l;
522 if (!keych) return;
523 l = strlen(keych->loc);
524 if (l > 14 && !strcmp(keych->loc + (l - 14), "/temp.keychain")) {
525 DIR *d;
526 if (keych->ref) {
527 (void)SecKeychainLock(keych->ref);
528 (void)SecKeychainDelete(keych->ref);
529 CFRelease(keych->ref);
530 keych->ref = NULL;
532 unlink(keych->loc);
533 keych->loc[l - 14] = '\0';
534 /* the keychain code may leave dot, possibly comma and yet other turds
535 * we may have to remove */
536 d = opendir(keych->loc);
537 if (d) {
538 struct dirent *ent;
539 while ((ent=readdir(d)) != NULL) {
540 char turd[PATH_MAX];
541 if (ent->d_name[0] == '.' &&
542 (ent->d_name[1] == '\0'
543 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) continue;
544 snprintf(turd, sizeof(turd), "%s/%s", keych->loc, ent->d_name);
545 unlink(turd);
547 closedir(d);
549 rmdir(keych->loc);
550 free(keych);
554 static CFDataRef extract_key_copy(CFDataRef pemseq, int *outpem)
556 const char *p = (char *)CFDataGetBytePtr(pemseq);
557 const char *origp = p;
558 size_t l = (size_t)CFDataGetLength(pemseq);
559 size_t origl = l;
560 *outpem = 0;
561 while (l) {
562 peminfo_t pem;
563 int readcnt = nextpem(p, l, &pem);
564 if (!readcnt && p == origp) {
565 /* Assume it's DER data */
566 CFRetain(pemseq);
567 return pemseq;
569 if (!readcnt || readcnt == -1) return NULL;
570 if (pem.type == pemtype_privatekey_rsa) {
571 *outpem = 1;
572 if (pem.start == origp && pem.len == origl) {
573 CFRetain(pemseq);
574 return pemseq;
576 return CFDataCreate(kCFAllocatorDefault, (uint8_t *)pem.start, pem.len);
578 l -= (size_t)readcnt;
579 p += (size_t)readcnt;
581 return NULL;
584 SecIdentityRef cSecIdentityCreateWithCertificateAndKeyData(
585 SecCertificateRef cert, CFDataRef keydata, CFTypeRef pw, CFStringRef hint,
586 void **kh)
588 int ispem = 0;
589 CFDataRef rawkey = NULL;
590 tempch_t *keych = NULL;
591 int err;
592 SecKeychainRef keychain = NULL;
593 SecExternalFormat format;
594 SecExternalItemType type;
595 SecItemImportExportKeyParameters params;
596 CFArrayRef items = NULL;
597 SecKeyRef key = NULL;
598 SecIdentityRef ans = NULL;
600 if (!cert || !kh) return NULL;
601 if (keydata)
602 rawkey = extract_key_copy(keydata, &ispem);
603 while (rawkey) {
604 homedirs_t dirs;
605 CFArrayRef searchlist = NULL;
606 keych = new_temp_keych();
607 if (!keych) break;
608 /* SecKeychainCreate has the side effect of adding the new keychain to
609 * the search list which will make it show up in other apps.
610 * SecKeychainDelete removes it from the search list, or we can also get
611 * the search list before the create and restore it right after.
612 * By immediately restoring the search list, we avoid having the new
613 * private key we're importing be searchable by default in other apps.
614 * If we are running with HOME != ~geteuid() then we likely have no
615 * ~/Library/Preferences/com.apple.security.plist which means the system
616 * will "helpfully" set the default keychain to this new keychain we've
617 * just created which is very bad. If Xcode is running it will listen
618 * to that event and then call SecKeychainSetDefault with that very
619 * same temporary keychain (I have no idea why it does this stupid thing)
620 * and that will make it permanent for the user. Ugh. To avoid this,
621 * we temporarily set HOME to getpwuid(geteuid())->pw_dir while we are
622 * creating the temporary keychain and then put HOME back the way it was
623 * immediately thereafter. Git likes to run tests with HOME set to
624 * alternate locations so it's prudent to handle this. */
625 get_home_dirs(&dirs);
626 if (dirs.home)
627 putenv(dirs.home);
628 err = SecKeychainCopySearchList(&searchlist);
629 if (err || !searchlist) {
630 free_home_dirs(&dirs);
631 break;
633 err = SecKeychainCreate(keych->loc, sizeof(keych->pw), keych->pw, false,
634 NULL, &keychain);
635 if (err || !keychain) {
636 free_home_dirs(&dirs);
637 CFRelease(searchlist);
638 break;
640 keych->ref = keychain;
641 err = SecKeychainSetSearchList(searchlist);
642 if (dirs.home) {
643 if (dirs.cur_home)
644 putenv(dirs.cur_home);
645 else
646 unsetenv("HOME");
648 free_home_dirs(&dirs);
649 CFRelease(searchlist);
650 if (err) break;
651 err = SecKeychainUnlock(keychain, sizeof(keych->pw), keych->pw, true);
652 if (err) break;
654 SecKeychainSettings settings;
655 settings.version = SEC_KEYCHAIN_SETTINGS_VERS1;
656 settings.lockOnSleep = false;
657 settings.useLockInterval = false;
658 settings.lockInterval = INT_MAX;
659 (void)SecKeychainSetSettings(keychain, &settings);
661 format = ispem ? kSecFormatWrappedOpenSSL : kSecFormatOpenSSL;
662 type = kSecItemTypePrivateKey;
663 memset(&params, 0, sizeof(params));
664 params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
665 params.flags = kSecKeyImportOnlyOne|kSecKeyNoAccessControl;
666 if (pw)
667 params.passphrase = pw;
668 else {
669 params.flags |= kSecKeySecurePassphrase;
670 /* Note that params.alertTitle is ignored */
671 params.alertPrompt = hint;
673 err = cSecItemImport(rawkey, NULL, &format, &type,
674 ispem?kSecItemPemArmour:0, &params, keychain, &items);
675 CFRelease(rawkey);
676 if (!err && items && CFArrayGetCount(items) == 1 &&
677 CFGetTypeID((CFTypeRef)CFArrayGetValueAtIndex(items, 0)) == SecKeyGetTypeID()) {
678 key = (SecKeyRef)CFArrayGetValueAtIndex(items, 0);
679 CFRetain(key);
681 if (items) CFRelease(items);
682 break;
684 if (key) {
685 /* If we have a key we must also have a keychain */
686 err = cSecIdentityCreateWithCertificate(keychain, cert, &ans);
687 CFRelease(key);
689 /* We MUST NOT call SecKeychainDelete because that will purge all copies of
690 * the keychain from memory. We've already removed it from the search list
691 * so we just release it and remove the disk files instead in order to allow
692 * the in memory copy to remain unmolested. Unfortunately on older systems
693 * this is not good enough, so we have to leave the keychain itself around. */
694 if (!ans && keych) {
695 del_temp_keych(keych);
696 keych = NULL;
698 if (!ans && (!rawkey || (!ispem && !key))) {
699 /* Try again with the default keychain list, but only if a key was not
700 * provided or was provided in non-PEM and we failed to import it. */
701 err = cSecIdentityCreateWithCertificate(NULL, cert, &ans);
703 if (ans)
704 *kh = keych;
705 else
706 del_temp_keych(keych);
707 return ans;
710 void DisposeIdentityKeychainHandle(void *ch)
712 del_temp_keych((tempch_t *)ch);
715 CFArrayRef CreateClientAuthWithCertificatesAndKeyData(CFArrayRef certs,
716 CFDataRef keydata, CFTypeRef pw,
717 CFStringRef hint, void **kh)
719 CFMutableArrayRef ans;
720 size_t count, i;
721 SecCertificateRef cert;
722 SecIdentityRef identity;
724 if (!certs || !keydata) return NULL;
725 count = (size_t)CFArrayGetCount(certs);
726 if (count < 1) return NULL;
727 cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, 0);
728 if (CFGetTypeID(cert) != SecCertificateGetTypeID()) return NULL;
729 ans = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks);
730 if (!ans) return NULL;
731 identity = cSecIdentityCreateWithCertificateAndKeyData(cert, keydata, pw,
732 hint, kh);
733 if (!identity) {
734 CFRelease(ans);
735 return NULL;
737 CFArrayAppendValue(ans, identity);
738 for (i = 1; i < count; ++i) {
739 CFArrayAppendValue(ans, CFArrayGetValueAtIndex(certs, i));
741 return ans;
744 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
746 #ifndef kCFCoreFoundationVersionNumber10_8
747 #define kCFCoreFoundationVersionNumber10_8 744.00
748 #endif
750 typedef enum {
751 small_0,
752 small_1,
753 small_2,
754 small_3,
755 small_4,
756 small_5
757 } SmallEnum;
759 typedef struct {
760 size_t Length;
761 uint8_t *Data;
762 } cCSSM_DATA;
764 static struct {
765 OSStatus (*fSSLSetTrustedRoots)(SSLContextRef, CFArrayRef, Boolean);
766 OSStatus (*fSSLGetPeerSecTrust)(SSLContextRef, SecTrustRef *);
767 OSStatus (*fSSLCopyPeerTrust)(SSLContextRef, SecTrustRef *);
768 OSStatus (*fSecTrustGetResult)(SecTrustRef, SecTrustResultType *,
769 CFArrayRef *, CSSM_TP_APPLE_EVIDENCE_INFO **);
770 OSStatus (*fSecTrustSetAnchorCertificatesOnly)(SecTrustRef, Boolean);
771 OSStatus (*fSSLGetPeerCertificates)(SSLContextRef cxt, CFArrayRef *certs);
772 OSStatus (*fSSLCopyPeerCertificates)(SSLContextRef cxt, CFArrayRef *certs);
773 OSStatus (*fSSLSetProtocolVersionEnabled)(SSLContextRef cxt, SmallEnum, Boolean);
774 OSStatus (*fSSLSetProtocolVersionMin)(SSLContextRef cxt, SmallEnum);
775 OSStatus (*fSSLSetProtocolVersionMax)(SSLContextRef cxt, SmallEnum);
776 OSStatus (*fSSLSetSessionOption)(SSLContextRef, SmallEnum, Boolean);
777 SecCertificateRef (*fSecCertificateCreateWithData)(CFAllocatorRef, CFDataRef);
778 OSStatus (*fSecCertificateCreateFromData)(const cCSSM_DATA *, CSSM_CERT_TYPE,
779 CSSM_CERT_ENCODING, SecCertificateRef *);
780 OSStatus (*fSecCertificateGetData)(SecCertificateRef, cCSSM_DATA *);
781 CFDataRef (*fSecCertificateCopyData)(SecCertificateRef);
782 OSStatus (*fSecKeychainItemImport)(
783 CFDataRef,CFStringRef,SecExternalFormat *,SecExternalItemType *,
784 SecItemImportExportFlags,const SecKeyImportExportParameters *,
785 SecKeychainRef,CFArrayRef *);
786 OSStatus (*fSecItemImport)(
787 CFDataRef,CFStringRef,SecExternalFormat *,SecExternalItemType *,
788 SecItemImportExportFlags,const SecItemImportExportKeyParameters *,
789 SecKeychainRef,CFArrayRef *);
790 OSStatus (*fSecIdentityCreateWithCertificate)(CFTypeRef,SecCertificateRef,SecIdentityRef *);
791 OSStatus (*fSSLNewContext)(Boolean,SSLContextRef *);
792 OSStatus (*fSSLDisposeContext)(SSLContextRef);
793 SSLContextRef (*fSSLCreateContext)(CFAllocatorRef,SmallEnum,SmallEnum);
794 OSStatus (*fSecKeychainSearchCreateFromAttributes)(CFTypeRef,int,
795 const SecKeychainAttributeList *,SecKeychainSearchRef *);
796 OSStatus (*fSecKeychainSearchCopyNext)(SecKeychainSearchRef,SecKeychainItemRef *);
797 } fnc;
799 static void stcompat_initialize(void)
801 #define LOOKUP(name) *((void **)&fnc.f##name) = dlsym(RTLD_NEXT, #name)
802 LOOKUP(SSLSetTrustedRoots);
803 LOOKUP(SSLGetPeerSecTrust);
804 LOOKUP(SSLCopyPeerTrust);
805 LOOKUP(SecTrustGetResult);
806 LOOKUP(SecTrustSetAnchorCertificatesOnly);
807 LOOKUP(SSLGetPeerCertificates);
808 LOOKUP(SSLCopyPeerCertificates);
809 LOOKUP(SSLSetProtocolVersionEnabled);
810 LOOKUP(SSLSetProtocolVersionMin);
811 LOOKUP(SSLSetProtocolVersionMax);
812 LOOKUP(SSLSetSessionOption);
813 LOOKUP(SecCertificateCreateWithData);
814 LOOKUP(SecCertificateCreateFromData);
815 LOOKUP(SecCertificateGetData);
816 LOOKUP(SecCertificateCopyData);
817 LOOKUP(SecKeychainItemImport);
818 LOOKUP(SecItemImport);
819 LOOKUP(SecIdentityCreateWithCertificate);
820 LOOKUP(SSLNewContext);
821 LOOKUP(SSLDisposeContext);
822 LOOKUP(SSLCreateContext);
823 LOOKUP(SecKeychainSearchCreateFromAttributes);
824 LOOKUP(SecKeychainSearchCopyNext);
825 #undef LOOKUP
828 OSStatus cSSLSetTrustedRoots(SSLContextRef cxt, CFArrayRef rts, Boolean replace)
830 if (fnc.fSSLSetTrustedRoots)
831 return fnc.fSSLSetTrustedRoots(cxt, rts, replace);
832 return unimpErr;
835 OSStatus cSSLCopyPeerTrust(SSLContextRef cxt, SecTrustRef *trust)
837 if (fnc.fSSLCopyPeerTrust)
838 return fnc.fSSLCopyPeerTrust(cxt, trust);
839 if (fnc.fSSLGetPeerSecTrust) {
840 OSStatus err = fnc.fSSLGetPeerSecTrust(cxt, trust);
841 if (!err) CFRetain(*trust);
842 return err;
844 return unimpErr;
847 OSStatus cSecTrustGetResult(SecTrustRef trust, SecTrustResultType *result,
848 CFArrayRef *certChain, CSSM_TP_APPLE_EVIDENCE_INFO **statusChain)
850 if (fnc.fSecTrustGetResult)
851 return fnc.fSecTrustGetResult(trust, result, certChain, statusChain);
852 return unimpErr;
855 OSStatus cSSLCopyPeerCertificates(SSLContextRef cxt, CFArrayRef *certs)
857 if (!certs || !cxt) return paramErr;
858 *certs = NULL;
859 if (fnc.fSSLCopyPeerCertificates)
860 return fnc.fSSLCopyPeerCertificates(cxt, certs);
861 if (fnc.fSSLGetPeerCertificates) {
862 OSStatus err = fnc.fSSLGetPeerCertificates(cxt, certs);
863 if (!err && *certs) {
864 size_t i, c = (size_t)CFArrayGetCount(*certs);
865 for (i = 0; i < c; ++i) {
866 CFTypeRef item = (CFTypeRef)CFArrayGetValueAtIndex(*certs, i);
867 if (item) CFRelease(item);
870 return err;
872 return unimpErr;
875 OSStatus cSecTrustSetAnchorCertificatesOnly(SecTrustRef cxt, Boolean anchorsOnly)
877 if (fnc.fSecTrustSetAnchorCertificatesOnly)
878 return fnc.fSecTrustSetAnchorCertificatesOnly(cxt, anchorsOnly);
879 return unimpErr;
882 OSStatus cSSLSetProtocolVersionMinMax(SSLContextRef cxt, int minVer, int maxVer)
884 OSStatus err;
886 if (minVer < 0 || maxVer < 0 || minVer > 8 || maxVer > 8 || minVer > maxVer)
887 return paramErr;
889 if (minVer == kSSLProtocolUnknown) minVer = kSSLProtocol3;
890 if (minVer == kSSLProtocolAll) minVer = kSSLProtocol3;
891 if (minVer == kSSLProtocol3Only) minVer = kSSLProtocol3;
892 if (minVer == kTLSProtocol1Only) minVer = kTLSProtocol1;
894 if (maxVer == kSSLProtocol3Only) maxVer = kSSLProtocol3;
895 if (maxVer == kTLSProtocol1Only) maxVer = kTLSProtocol1;
896 if (maxVer == kSSLProtocolAll) maxVer = kTLSProtocol12;
897 if (maxVer == kSSLProtocolUnknown) maxVer = kTLSProtocol12;
899 if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_8 &&
900 minVer <= kTLSProtocol1 && maxVer > kTLSProtocol1)
901 maxVer = kTLSProtocol1;
903 if (fnc.fSSLSetProtocolVersionMin && fnc.fSSLSetProtocolVersionMax) {
904 err = fnc.fSSLSetProtocolVersionMin(cxt, minVer);
905 if (!err)
906 err = fnc.fSSLSetProtocolVersionMax(cxt, maxVer);
907 return err;
909 if (fnc.fSSLSetProtocolVersionEnabled) {
910 #define ENABLEPROTO(x) fnc.fSSLSetProtocolVersionEnabled(cxt, (int)(x), \
911 minVer <= x && x <= maxVer)
912 err = ENABLEPROTO(kSSLProtocol2);
913 if (err && minVer > kSSLProtocol2) err = noErr; /* ignore SSL2 disable error */
914 if (!err) ENABLEPROTO(kSSLProtocol3);
915 if (!err) ENABLEPROTO(kTLSProtocol1);
916 if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber10_8 ||
917 maxVer > kTLSProtocol1) {
918 if (!err) ENABLEPROTO(kTLSProtocol11);
919 if (!err) ENABLEPROTO(kTLSProtocol12);
921 #undef ENABLEPROTO
922 return err;
924 return unimpErr;
927 OSStatus cSSLSetSessionOption(SSLContextRef cxt, int option, Boolean value)
929 if (fnc.fSSLSetSessionOption)
930 return fnc.fSSLSetSessionOption(cxt, option, value);
931 else
932 return unimpErr;
935 SecCertificateRef cSecCertificateCreateWithData(CFAllocatorRef a, CFDataRef d)
937 if (fnc.fSecCertificateCreateWithData)
938 return fnc.fSecCertificateCreateWithData(a, d);
939 else if (fnc.fSecCertificateCreateFromData) {
940 cCSSM_DATA certdata;
941 OSStatus err;
942 SecCertificateRef cacert = NULL;
943 if (!d) return NULL;
944 certdata.Length = (size_t)CFDataGetLength(d);
945 certdata.Data = (uint8 *)CFDataGetBytePtr(d);
946 err = fnc.fSecCertificateCreateFromData(&certdata, CSSM_CERT_X_509v3,
947 CSSM_CERT_ENCODING_DER, &cacert);
948 if (err)
949 cacert = NULL;
950 return cacert;
951 } else
952 return NULL;
955 CFDataRef cSecCertificateCopyData(SecCertificateRef c)
957 if (!c) return NULL;
958 if (CFGetTypeID(c) != SecCertificateGetTypeID()) return NULL;
959 if (fnc.fSecCertificateCopyData)
960 return fnc.fSecCertificateCopyData(c);
961 if (fnc.fSecCertificateGetData) {
962 cCSSM_DATA certdata;
963 OSStatus err = fnc.fSecCertificateGetData(c, &certdata);
964 if (err || !certdata.Data || !certdata.Length) return NULL;
965 return CFDataCreate(kCFAllocatorDefault, certdata.Data, certdata.Length);
967 return NULL;
970 Boolean SecCertsEqual(SecCertificateRef c1, SecCertificateRef c2)
972 CFDataRef d1, d2;
973 size_t l1, l2;
974 Boolean ans = false;
975 d1 = cSecCertificateCopyData(c1);
976 if (!d1) return false;
977 d2 = cSecCertificateCopyData(c2);
978 if (!d2) {
979 CFRelease(d1);
980 return false;
982 l1 = CFDataGetLength(d1);
983 l2 = CFDataGetLength(d2);
984 if (l1 == l2) {
985 const void *p1 = (void *)CFDataGetBytePtr(d1);
986 const void *p2 = (void *)CFDataGetBytePtr(d2);
987 ans = memcmp(p1, p2, l1) == 0;
989 CFRelease(d1);
990 CFRelease(d2);
991 return ans;
994 Boolean SecCertInArray(SecCertificateRef c, CFArrayRef a)
996 size_t i, cnt;
997 if (!c || !a || !CFArrayGetCount(a)) return false;
998 cnt = CFArrayGetCount(a);
999 for (i = 0; i < cnt; ++i) {
1000 if (SecCertsEqual(c, (SecCertificateRef)CFArrayGetValueAtIndex(a, i)))
1001 return true;
1003 return false;
1006 OSStatus CopyIdentityWithLabel(const char *label, SecIdentityRef *out)
1008 if (fnc.fSecKeychainSearchCreateFromAttributes &&
1009 fnc.fSecKeychainSearchCopyNext) {
1010 SecIdentityRef ans = NULL;
1011 SecCertificateRef cert = NULL;
1012 SecKeychainAttribute at;
1013 SecKeychainAttributeList al;
1014 SecKeychainSearchRef sr;
1015 OSStatus err;
1017 at.tag = kSecLabelItemAttr;
1018 at.length = strlen(label);
1019 at.data = (char *)label;
1020 al.count = 1;
1021 al.attr = &at;
1022 err = fnc.fSecKeychainSearchCreateFromAttributes(NULL,
1023 kSecCertificateItemClass, &al, &sr);
1024 *out = NULL;
1025 if (err || !sr) return err;
1026 while ((err = fnc.fSecKeychainSearchCopyNext(sr, (SecKeychainItemRef *)&cert)) == noErr) {
1027 if (!cert) continue;
1028 if (CFGetTypeID(cert) != SecCertificateGetTypeID()) {CFRelease(cert); continue;}
1029 err = cSecIdentityCreateWithCertificate(NULL, cert, &ans);
1030 CFRelease(cert);
1031 if (!err && ans) break;
1033 CFRelease(sr);
1034 if (ans) {
1035 *out = ans;
1036 return noErr;
1038 return errSecItemNotFound;
1040 return unimpErr;
1043 OSStatus cSecItemImport(
1044 CFDataRef importedData, CFStringRef fileNameOrExtension,
1045 SecExternalFormat *inputFormat, SecExternalItemType *itemType,
1046 SecItemImportExportFlags flags, const SecItemImportExportKeyParameters *keyParams,
1047 SecKeychainRef importKeychain, CFArrayRef *outItems)
1049 if (fnc.fSecItemImport)
1050 return fnc.fSecItemImport(importedData, fileNameOrExtension, inputFormat,
1051 itemType, flags, keyParams, importKeychain, outItems);
1052 else if (fnc.fSecKeychainItemImport) {
1053 SecKeyImportExportParameters oldKeyParams;
1054 SecKeyImportExportParameters *op = NULL;
1055 if (keyParams) {
1056 op = &oldKeyParams;
1057 memset(&oldKeyParams, 0, sizeof(oldKeyParams));
1058 oldKeyParams.version = keyParams->version;
1059 oldKeyParams.flags = keyParams->flags;
1060 oldKeyParams.passphrase = keyParams->passphrase;
1061 oldKeyParams.alertTitle = keyParams->alertTitle;
1062 oldKeyParams.alertPrompt = keyParams->alertPrompt;
1063 oldKeyParams.accessRef = keyParams->accessRef;
1064 /* We punt on keyUsage and keyAttributes and do not convert them */
1066 return fnc.fSecKeychainItemImport(importedData, fileNameOrExtension, inputFormat,
1067 itemType, flags, op, importKeychain, outItems);
1069 return unimpErr;
1072 OSStatus cSecIdentityCreateWithCertificate(CFTypeRef k, SecCertificateRef c,
1073 SecIdentityRef *i)
1075 /* The documentation lies and this is actually present in later 10.4 versions */
1076 if (fnc.fSecIdentityCreateWithCertificate)
1077 return fnc.fSecIdentityCreateWithCertificate(k, c, i);
1078 return unimpErr;
1081 SSLContextRef cSSLCreateContext(CFAllocatorRef a, int ps, int ct)
1083 if (fnc.fSSLCreateContext)
1084 return fnc.fSSLCreateContext(a, ps, ct);
1085 if ((ps != kSSLServerSide && ps != kSSLClientSide) || (ct != kSSLStreamType))
1086 return NULL;
1087 if (fnc.fSSLNewContext && fnc.fSSLDisposeContext) {
1088 SSLContextRef cxt;
1089 OSStatus err = fnc.fSSLNewContext(ps == kSSLServerSide, &cxt);
1090 return err ? NULL : cxt;
1092 return NULL;
1095 void cSSLDisposeContext(SSLContextRef c)
1097 if (fnc.fSSLCreateContext)
1098 CFRelease(c);
1099 else if (fnc.fSSLDisposeContext)
1100 fnc.fSSLDisposeContext(c);
1103 CF_INLINE bool is_ldh(int c)
1105 return
1106 ('A' <= c && c <= 'Z') ||
1107 ('a' <= c && c <= 'z') ||
1108 ('0' <= c && c <= '9') ||
1109 c == '-';
1112 CF_INLINE size_t get_label_len(const char *p, size_t l)
1114 size_t ans = 0;
1115 while (l-- && is_ldh(*p++)) ++ans;
1116 return ans;
1119 static bool is_dns_name(const void *_p, size_t l, bool wcok)
1121 const char *p = (char *)_p;
1122 size_t idx = 0;
1124 if (!p) return false;
1125 if (l >= 1 && p[l-1] == '.') --l;
1126 if (!l) return false;
1127 if (l > 255) return false;
1128 do {
1129 size_t lablen = get_label_len(p, l);
1130 if (lablen > 63) return false;
1131 if (!idx && !lablen && wcok && l >= 2 && p[0] == '*' && p[1] == '.') lablen=1;
1132 if (!lablen) return false;
1133 if (p[0] == '-' || p[lablen - 1] == '-') return false;
1134 if (lablen < l) {
1135 if (p[lablen] != '.') return false;
1136 ++lablen;
1138 l -= lablen;
1139 p += lablen;
1140 ++idx;
1141 } while (l);
1142 return true;
1145 CF_INLINE char clc(char c)
1147 return 'A' <= c && c <= 'Z' ? c - 'A' + 'a' : c;
1150 CF_INLINE bool matchicase(const char *p1, const char *p2, size_t l)
1152 while (l--) {
1153 if (clc(*p1++) != clc(*p2++)) return false;
1155 return true;
1158 static bool peername_matches_id(const char *peername, const char *idname)
1160 size_t pl, il, idx;
1161 if (!peername || !idname) return false;
1162 pl = strlen(peername);
1163 il = strlen(idname);
1164 if (!is_dns_name(peername, pl, false) || !is_dns_name(idname, il, true))
1165 return false;
1166 idx = 0;
1167 if (peername[pl - 1] == '.') --pl;
1168 if (idname[il - 1] == '.') --il;
1169 while (pl && il) {
1170 size_t pll = get_label_len(peername, pl);
1171 size_t ill = get_label_len(idname, il);
1172 if (!idx && !ill && il >= 2 && idname[0] == '*' && idname[1] == '.') ill=1;
1173 if (pll < pl) {
1174 if (peername[pll] != '.') return false;
1175 ++pll;
1177 if (ill < il) {
1178 if (idname[ill] != '.') return false;
1179 ++ill;
1181 if (idx || idname[0] != '*') {
1182 if (pll != ill) return false;
1183 if (!matchicase(peername, idname, pll)) return false;
1185 peername += pll;
1186 pl -= pll;
1187 idname += ill;
1188 il -= ill;
1189 ++idx;
1191 return !pl && !il;
1194 CF_INLINE size_t get_num_len(const char *p, size_t l)
1196 size_t ans = 0;
1197 while (l-- && '0' <= *p && *p <= '9') {
1198 ++ans;
1199 ++p;
1201 return ans;
1204 Boolean IsIPv4Name(const void *_p, size_t l)
1206 const char *p = (char *)_p;
1207 size_t idx = 0;
1209 if (!p || l < 7) return false;
1210 do {
1211 size_t lablen;
1212 if (++idx > 4) return false;
1213 lablen = get_num_len(p, l);
1214 if (lablen > 3) return false;
1215 if (lablen >= 2 && *p == '0') return false;
1216 else if (lablen == 3) {
1217 if (*p >= '3') return false;
1218 if (*p == '2') {
1219 if (p[1] >= '6') return false;
1220 if (p[1] == '5' && p[2] > '5') return false;
1223 if (lablen < l) {
1224 if (p[lablen] != '.') return false;
1225 ++lablen;
1227 l -= lablen;
1228 p += lablen;
1229 } while (l);
1230 return idx == 4;
1233 static bool parse_ipv4_name(const void *_p, size_t l, uint8_t ipv4[4])
1235 unsigned short s[4];
1236 char ipv4str[16];
1237 const char *p = (char *)_p;
1238 if (!IsIPv4Name(p, l) || l > 15) return false;
1239 memcpy(ipv4str, p, l);
1240 ipv4str[l] = 0;
1241 if (sscanf(ipv4str, "%hu.%hu.%hu.%hu", s, s+1, s+2, s+3) == 4) {
1242 ipv4[0] = (uint8_t)s[0];
1243 ipv4[1] = (uint8_t)s[1];
1244 ipv4[2] = (uint8_t)s[2];
1245 ipv4[3] = (uint8_t)s[3];
1246 return true;
1248 return false;
1251 static bool parse_ipv6_name(const void *_p, size_t l, uint8_t ipv6[16])
1253 char ipv6str[INET6_ADDRSTRLEN];
1254 const char *p = (char *)_p;
1255 const char *pct;
1256 if (!p) return false;
1257 if (l >= 1 && p[0] == '[') {
1258 ++p;
1259 --l;
1261 if (l >= 1 && p[l-1] == ']') --l;
1262 pct = (char *)(l ? memchr(p, '%', l) : NULL);
1263 if (pct) l = pct - p;
1264 if (l < 3 || l >= INET6_ADDRSTRLEN) return false;
1265 memcpy(ipv6str, p, l);
1266 ipv6str[l] = 0;
1267 return inet_pton(AF_INET6, ipv6str, ipv6) == 1;
1270 typedef struct {
1271 const uint8_t *d;
1272 size_t l;
1273 } data_t;
1275 #define U(x) ((const uint8_t *)(x))
1276 static const data_t OID_BasicConstraints = {U("\006\003\125\035\023"), 5};
1277 static const data_t OID_SubjectAltName = {U("\006\003\125\035\021"), 5};
1278 static const data_t OID_SubjectKeyIdentifier = {U("\006\003\125\035\016"), 5};
1279 static const data_t OID_AuthorityKeyIdentifier = {U("\006\003\125\035\043"), 5};
1280 static const data_t OID_CommonName = {U("\006\003\125\004\003"), 5};
1281 #undef U
1283 typedef struct {
1284 uint8_t clas; /* 0, 1, 2, or 3 */
1285 uint8_t cons; /* 0 or 1 */
1286 uint8_t rawtag; /* raw value of first byte of tag */
1287 uint32_t tag; /* tag value */
1288 size_t hl; /* length of header excluding actual data */
1289 size_t dl; /* length of actual data */
1290 } der_atom_t;
1292 typedef struct {
1293 uint8_t vers; /* 0 => v1, 1 => v2, 2 => v3 */
1294 uint8_t caFlag; /* 0 unless basic constraints present then 0x80=critial 0x01=value */
1295 uint8_t isCA; /* true if caFlag==0x81 or subject==issuer && vers < 2 */
1296 uint8_t isRoot; /* true if isCA and subject == issuer */
1297 data_t subject; /* points to sequence */
1298 data_t subjectAltNames; /* null unless v3 extension present, points to sequence */
1299 data_t subjectKeyId; /* null unless v3 extension present, points to raw bytes */
1300 data_t issuer; /* points to sequence */
1301 data_t issuerKeyId; /* null unless v3 extension present, points to raw bytes */
1302 } der_cert_t;
1304 static bool read_der_atom(const data_t *d, der_atom_t *o)
1306 uint8_t byte;
1307 uint32_t tag;
1308 size_t pos, len;
1309 if (!d || !d->d || !d->l || !o) return false;
1310 o->clas = (*d->d >> 6) & 0x3;
1311 o->cons = (*d->d >> 5) & 0x1;
1312 o->rawtag = *d->d;
1313 tag = *d->d & 0x1f;
1314 pos = 1;
1315 if (tag == 0x1f) {
1316 tag = 0;
1317 do {
1318 if (pos >= d->l) return false;
1319 tag <<= 7;
1320 byte = d->d[pos++];
1321 tag |= byte & 0x7f;
1322 } while (byte & 0x80);
1324 o->tag = tag;
1325 if (pos >= d->l) return false;
1326 byte = d->d[pos++];
1327 if (byte & 0x80) {
1328 unsigned cnt = byte & 0x7f;
1329 if (!cnt || pos + cnt > d->l) return false;
1330 len = 0;
1331 do {
1332 len <<= 8;
1333 len |= d->d[pos++];
1334 } while (--cnt);
1335 } else {
1336 len = byte;
1338 if (pos + len > d->l) return false;
1339 o->hl = pos;
1340 o->dl = len;
1341 return true;
1344 static bool is_der(const data_t *_d, bool exact_length_match_only)
1346 data_t d;
1347 if (!_d || !_d->d || !_d->l) return false;
1348 d.d = _d->d;
1349 d.l = _d->l;
1350 do {
1351 der_atom_t atom;
1352 if (!read_der_atom(&d, &atom)) return false;
1353 d.l -= atom.hl;
1354 d.d += atom.hl;
1355 if ((atom.rawtag & 0xfe) != 0x30) {
1356 d.l -= atom.dl;
1357 d.d += atom.dl;
1359 } while (d.l);
1360 return d.d == _d->d + _d->l || !exact_length_match_only;
1363 static int data_matches(const data_t *o1, const data_t *o2)
1365 if (!o1 || !o2 || !o1->l || !o2->l || o1->l != o2->l)
1366 return 0;
1367 return memcmp(o1->d, o2->d, o1->l) == 0;
1370 static bool read_der_cert(const data_t *_d, der_cert_t *o)
1372 data_t d;
1373 der_atom_t atom;
1375 if (!_d || !_d->d || !_d->l || !o) return false;
1376 if (!is_der(_d, true)) return false;
1377 d.d = _d->d;
1378 d.l = _d->l;
1379 memset(o, 0, sizeof(*o));
1380 if (!read_der_atom(&d, &atom)) return false;
1381 if (atom.rawtag != 0x30) return false;
1382 d.l = atom.dl;
1383 d.d += atom.hl;
1384 if (!read_der_atom(&d, &atom)) return false;
1385 if (atom.rawtag != 0x30) return false;
1386 d.l = atom.dl;
1387 d.d += atom.hl;
1388 if (!read_der_atom(&d, &atom)) return false;
1389 if (atom.rawtag == 0xA0) {
1390 d.l -= atom.hl;
1391 d.d += atom.hl;
1392 if (atom.dl != 3 || d.d[0] != 2 || d.d[1] != 1) return false;
1393 o->vers = d.d[2]; /* not validated */
1394 d.l -= atom.dl;
1395 d.d += atom.dl;
1396 if (!read_der_atom(&d, &atom)) return false;
1397 } else {
1398 o->vers = 0; /* implied v1 */
1400 if (atom.rawtag != 2) return false;
1401 /* skip serialNumber */
1402 d.l -= atom.hl + atom.dl;
1403 d.d += atom.hl + atom.dl;
1404 if (!read_der_atom(&d, &atom)) return false;
1405 if (atom.rawtag != 0x30) return false;
1406 /* skip signature */
1407 d.l -= atom.hl + atom.dl;
1408 d.d += atom.hl + atom.dl;
1409 if (!read_der_atom(&d, &atom)) return false;
1410 if (atom.rawtag != 0x30) return false;
1411 o->issuer.d = d.d;
1412 o->issuer.l = atom.hl + atom.dl;
1413 d.l -= atom.hl + atom.dl;
1414 d.d += atom.hl + atom.dl;
1415 if (!read_der_atom(&d, &atom)) return false;
1416 if (atom.rawtag != 0x30) return false;
1417 /* skip validity */
1418 d.l -= atom.hl + atom.dl;
1419 d.d += atom.hl + atom.dl;
1420 if (!read_der_atom(&d, &atom)) return false;
1421 if (atom.rawtag != 0x30) return false;
1422 o->subject.d = d.d;
1423 o->subject.l = atom.hl + atom.dl;
1424 d.l -= atom.hl + atom.dl;
1425 d.d += atom.hl + atom.dl;
1426 if (!read_der_atom(&d, &atom)) return false;
1427 if (atom.rawtag != 0x30) return false;
1428 /* skip subjectPublicKeyInfo */
1429 d.l -= atom.hl + atom.dl;
1430 d.d += atom.hl + atom.dl;
1431 do {
1432 if (o->vers != 2 || !d.l) break;
1433 if (!read_der_atom(&d, &atom)) return false;
1434 if (atom.rawtag == 0x81) {
1435 /* skip issuerUniqueID */
1436 d.l -= atom.hl + atom.dl;
1437 d.d += atom.hl + atom.dl;
1438 if (!d.l) break;
1439 if (!read_der_atom(&d, &atom)) return false;
1441 if (atom.rawtag == 0x82) {
1442 /* skip subjectUniqueID */
1443 d.l -= atom.hl + atom.dl;
1444 d.d += atom.hl + atom.dl;
1445 if (!d.l) break;
1446 if (!read_der_atom(&d, &atom)) return false;
1448 if (atom.rawtag != 0xA3) return false;
1449 /* found v3 extensions */
1450 d.l = atom.dl;
1451 d.d += atom.hl;
1452 if (!read_der_atom(&d, &atom)) return false;
1453 if (atom.rawtag != 0x30) return false;
1454 d.l -= atom.hl;
1455 d.d += atom.hl;
1456 do {
1457 uint8_t crit = 0;
1458 data_t oid, value;
1459 if (!read_der_atom(&d, &atom)) return false;
1460 if (atom.rawtag != 0x30) return false;
1461 d.l -= atom.hl;
1462 d.d += atom.hl;
1463 if (!read_der_atom(&d, &atom)) return false;
1464 if (atom.rawtag != 6) return false;
1465 oid.d = d.d;
1466 oid.l = atom.hl + atom.dl;
1467 d.l -= atom.hl + atom.dl;
1468 d.d += atom.hl + atom.dl;
1469 if (!read_der_atom(&d, &atom)) return false;
1470 if (atom.rawtag == 1) {
1471 /* skip over boolean but record its value */
1472 if (atom.dl != 1) return false;
1473 crit = *(d.d + atom.hl);
1474 d.l -= atom.hl + atom.dl;
1475 d.d += atom.hl + atom.dl;
1476 if (!read_der_atom(&d, &atom)) return false;
1478 if (atom.rawtag != 4) return false;
1479 d.l -= atom.hl;
1480 d.d += atom.hl;
1481 value.d = d.d;
1482 value.l = atom.dl;
1483 d.l -= atom.dl;
1484 d.d += atom.dl;
1485 if (data_matches(&oid, &OID_BasicConstraints)) {
1486 if (!read_der_atom(&value, &atom)) return false;
1487 if (atom.rawtag != 0x30) return false;
1488 value.l = atom.dl;
1489 value.d += atom.hl;
1490 if (!value.l) {
1491 /* CA flag is false and was properly omitted */
1492 o->caFlag = crit ? 0x80 : 0;
1493 } else {
1494 if (!read_der_atom(&value, &atom)) return false;
1495 if (atom.rawtag == 1) {
1496 /* CA flag is present */
1497 if (atom.dl != 1) return false;
1498 o->caFlag = (crit ? 0x80 : 0) | (*(value.d + atom.hl) ? 0x1 : 0);
1501 } else if (data_matches(&oid, &OID_SubjectAltName)) {
1502 o->subjectAltNames.d = value.d;
1503 o->subjectAltNames.l = value.l;
1504 } else if (data_matches(&oid, &OID_SubjectKeyIdentifier)) {
1505 if (!read_der_atom(&value, &atom)) return false;
1506 if (atom.rawtag != 4) return false;
1507 o->subjectKeyId.d = value.d + atom.hl;
1508 o->subjectKeyId.l = atom.dl;
1509 } else if (data_matches(&oid, &OID_AuthorityKeyIdentifier)) {
1510 if (!read_der_atom(&value, &atom)) return false;
1511 if (atom.rawtag != 0x30) return false;
1512 value.l = atom.dl;
1513 value.d += atom.hl;
1514 if (!read_der_atom(&value, &atom)) return false;
1515 if (atom.rawtag == 0x80) {
1516 o->issuerKeyId.d = value.d + atom.hl;
1517 o->issuerKeyId.l = atom.dl;
1520 } while (d.l);
1521 } while (0);
1522 if (o->vers >= 2) {
1523 o->isCA = (o->caFlag|0x80) == 0x81; /* HACK: some old CAs aren't critical! */
1524 o->isRoot = (o->isCA && o->subject.l && o->subject.l == o->issuer.l &&
1525 memcmp(o->subject.d, o->issuer.d, o->subject.l) == 0) ? 1 : 0;
1526 } else {
1527 o->isCA = (o->subject.l && o->subject.l == o->issuer.l &&
1528 memcmp(o->subject.d, o->issuer.d, o->subject.l) == 0) ? 1 : 0;
1529 o->isRoot = o->isCA;
1531 return true;
1534 Boolean CheckCertOkay(SecCertificateRef _cert)
1536 CFDataRef d = cSecCertificateCopyData(_cert);
1537 data_t data;
1538 der_cert_t cert;
1539 Boolean ans;
1541 if (!d) return false;
1542 data.d = CFDataGetBytePtr(d);
1543 data.l = CFDataGetLength(d);
1544 ans = read_der_cert(&data, &cert);
1545 CFRelease(d);
1546 return ans;
1549 static void append_hex_dump(CFMutableStringRef s, const void *_d, size_t l)
1551 const unsigned char *d = (unsigned char *)_d;
1552 CFStringAppendCString(s, "<", kCFStringEncodingASCII);
1553 while (l--) {
1554 char byte[3];
1555 sprintf(byte, "%02X", *d++);
1556 CFStringAppendCString(s, byte, kCFStringEncodingASCII);
1558 CFStringAppendCString(s, ">", kCFStringEncodingASCII);
1561 static CFStringRef CopyCertKeyId(SecCertificateRef _cert, bool issuer)
1563 CFDataRef d = cSecCertificateCopyData(_cert);
1564 CFMutableStringRef ans = CFStringCreateMutable(kCFAllocatorDefault, 0);
1565 bool good = false;
1567 for (;;) {
1568 data_t data;
1569 const data_t *key;
1570 der_cert_t cert;
1571 size_t i;
1573 if (!d || !ans) break;
1574 data.d = CFDataGetBytePtr(d);
1575 data.l = CFDataGetLength(d);
1576 if (!read_der_cert(&data, &cert)) break;
1577 key = issuer ? &cert.issuerKeyId : &cert.subjectKeyId;
1578 if (!key->d || !key->l) break;
1579 for (i = 0; i < key->l; ++i) {
1580 char hexbyte[4];
1581 sprintf(hexbyte, "%02X%s", (unsigned)key->d[i], i+1 == key->l ? "" : ":");
1582 CFStringAppendCString(ans, hexbyte, kCFStringEncodingASCII);
1584 good = true;
1585 break;
1587 if (d) CFRelease(d);
1588 if (!good && ans) {CFRelease(ans); ans=NULL;}
1589 return ans;
1592 typedef struct {
1593 size_t l;
1594 const char *oid;
1595 const char *name;
1596 } oid_entry_t;
1598 static const oid_entry_t oid_table[] = {
1599 {5, "\006\003\125\004\003", "CN"},
1600 {5, "\006\003\125\004\004", "SN"},
1601 {5, "\006\003\125\004\005", "serialNumber"},
1602 {5, "\006\003\125\004\006", "C"},
1603 {5, "\006\003\125\004\007", "L"},
1604 {5, "\006\003\125\004\010", "ST"},
1605 {5, "\006\003\125\004\011", "street"},
1606 {5, "\006\003\125\004\012", "O"},
1607 {5, "\006\003\125\004\013", "OU"},
1608 {5, "\006\003\125\004\014", "title"},
1609 {5, "\006\003\125\004\015", "description"},
1610 {5, "\006\003\125\004\017", "businessCategory"},
1611 {5, "\006\003\125\004\021", "postalCode"},
1612 {5, "\006\003\125\004\024", "telephoneNumber"},
1613 {5, "\006\003\125\004\027", "facsimileTelephoneNumber"},
1614 {5, "\006\003\125\004\052", "GN"},
1615 {5, "\006\003\125\004\053", "initials"},
1616 {5, "\006\003\125\004\054", "generationQualifier"},
1617 {5, "\006\003\125\004\056", "dnQualifier"},
1618 {5, "\006\003\125\004\101", "pseudonym"},
1619 {5, "\006\003\125\004\141", "organizationIdentifier"},
1620 {11, "\006\011\052\206\110\206\367\015\001\011\001", "emailAddress"},
1621 {12, "\006\012\011\222\046\211\223\362\054\144\001\001", "UID"},
1622 {12, "\006\012\011\222\046\211\223\362\054\144\001\031", "DC"},
1623 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\001", "jurisdictionOfIncorporationLocality"},
1624 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\002", "jurisdictionOfIncorporationStateOrProvince"},
1625 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\003", "jurisdictionOfIncorporationCountry"}
1627 #define oid_table_size (sizeof(oid_table)/sizeof(oid_table[0]))
1629 static int comp_entry(const void *_e1, const void *_e2)
1631 const oid_entry_t *o1 = (oid_entry_t *)_e1;
1632 const oid_entry_t *o2 = (oid_entry_t *)_e2;
1633 size_t min = o1->l;
1634 int ans;
1635 if (o2->l < min) min = o2->l;
1636 ans = memcmp(o1->oid, o2->oid, min);
1637 if (ans) return ans;
1638 if (o1->l < o2->l) return -1;
1639 if (o1->l > o2->l) return 1;
1640 return 0;
1643 static void append_oid_name(CFMutableStringRef s, const char *prefix,
1644 const void *_oid, size_t l, const char *suffix)
1646 oid_entry_t find, *ans;
1647 find.oid = (char *)_oid;
1648 find.l = l;
1649 find.name = NULL;
1650 ans = (oid_entry_t *)
1651 bsearch(&find, oid_table, oid_table_size, sizeof(find), comp_entry);
1652 if (prefix && *prefix)
1653 CFStringAppendCString(s, prefix, kCFStringEncodingASCII);
1654 if (ans)
1655 CFStringAppendCString(s, ans->name, kCFStringEncodingASCII);
1656 else {
1657 CFMutableStringRef temp = CFStringCreateMutable(kCFAllocatorDefault, 0);
1658 const uint8_t *oid = (uint8_t *)_oid;
1659 bool bad = false;
1660 size_t orig_l = l;
1661 const uint8_t *orig_oid = oid;
1662 if (!temp || l < 3 || *oid != 6)
1663 bad = true;
1664 if (!bad) {
1665 data_t data;
1666 der_atom_t atom;
1667 data.d = oid;
1668 data.l = l;
1669 if (!read_der_atom(&data, &atom))
1670 bad = true;
1671 if (!bad && (atom.rawtag != 6 || atom.dl < 1))
1672 bad = true;
1673 if (!bad) {
1674 oid = data.d + atom.hl;
1675 l = atom.dl;
1676 if (l + atom.hl != orig_l)
1677 bad = true;
1680 if (!bad) {
1681 size_t idx = 0;
1682 do {
1683 unsigned idval = 0;
1684 uint8_t byte;
1685 do {
1686 if (!l) {bad=true; break;}
1687 idval <<= 7;
1688 byte = *oid++;
1689 --l;
1690 idval |= byte & 0x7f;
1691 } while (!bad && (byte & 0x80));
1692 if (bad) break;
1693 if (!idx) {
1694 char twoids[32];
1695 unsigned x, y;
1696 if (idval < 40) {
1697 x = 0; y = idval;
1698 } else if (idval < 80) {
1699 x = 1; y = idval - 40;
1700 } else {
1701 x = 2; y = idval - 80;
1703 snprintf(twoids, sizeof(twoids), "%u.%u", x, y);
1704 CFStringAppendCString(temp, twoids, kCFStringEncodingASCII);
1705 idx += 2;
1706 } else {
1707 char oneid[16];
1708 snprintf(oneid, sizeof(oneid), ".%u", idval);
1709 CFStringAppendCString(temp, oneid, kCFStringEncodingASCII);
1710 ++idx;
1712 } while (l && !bad);
1714 if (bad || l || !temp || !CFStringGetLength(temp))
1715 append_hex_dump(s, orig_oid, orig_l);
1716 else
1717 CFStringAppend(s, temp);
1718 if (temp)
1719 CFRelease(temp);
1721 if (suffix && *suffix)
1722 CFStringAppendCString(s, suffix, kCFStringEncodingASCII);
1725 #define DER_TAG_UTF8STRING 12
1726 #define DER_TAG_NUMERICSTRING 18
1727 #define DER_TAG_PRINTABLESTRING 19
1728 #define DER_TAG_TELETEXSTRING 20
1729 #define DER_TAG_VIDEOTEXSTRING 21
1730 #define DER_TAG_IA5STRING 22
1731 #define DER_TAG_GRAPHICSTRING 25
1732 #define DER_TAG_VISIBLESTRING 26
1733 #define DER_TAG_GENERALSTRING 27
1734 #define DER_TAG_UNIVERSALSTRING 28
1735 #define DER_TAG_BMPSTRING 30
1737 /* flags:
1738 * 0x01 => strings only
1739 * 0x02 => 8-bit strings only
1740 * 0x04 => CN Ids strings only
1741 * 0x08 => wildcard CN okay
1742 * 0x10 => create output string
1744 static bool append_attr_value(CFMutableStringRef *s, const void *_d,
1745 const der_atom_t *a, unsigned flags)
1747 const uint8_t *d = (uint8_t *)_d;
1748 CFStringBuiltInEncodings encoding = kCFStringEncodingASCII;
1749 CFStringRef temp;
1750 if (s && !(flags & 0x10) && !*s) return false;
1751 if (!s || !d || !a || !a->dl) return false;
1752 switch (a->rawtag) {
1753 case DER_TAG_UTF8STRING:
1754 case DER_TAG_GRAPHICSTRING:
1755 case DER_TAG_GENERALSTRING:
1756 case DER_TAG_UNIVERSALSTRING:
1757 encoding = kCFStringEncodingUTF8; break;
1758 case DER_TAG_NUMERICSTRING:
1759 case DER_TAG_PRINTABLESTRING:
1760 case DER_TAG_IA5STRING:
1761 encoding = kCFStringEncodingASCII; break;
1762 case DER_TAG_TELETEXSTRING:
1763 case DER_TAG_VIDEOTEXSTRING:
1764 case DER_TAG_VISIBLESTRING:
1765 encoding = kCFStringEncodingISOLatin1; break;
1766 case DER_TAG_BMPSTRING:
1767 if (flags & 0x06) return false;
1768 encoding = kCFStringEncodingUnicode; break;
1769 default:
1770 if (flags & 0x05) return false;
1771 append_hex_dump(*s, d, a->hl + a->dl);
1772 return true;
1774 if (flags & 0x04 && !is_dns_name(d+a->hl, a->dl, !!(flags & 0x08)))
1775 return false;
1776 temp = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, d+a->hl, a->dl,
1777 encoding, true, kCFAllocatorNull);
1778 if (temp) {
1779 if (flags & 0x10)
1780 *s = CFStringCreateMutable(kCFAllocatorDefault, 0);
1781 if (*s)
1782 CFStringAppend(*s, temp);
1783 CFRelease(temp);
1784 } else {
1785 if (flags & 0x05) return false;
1786 append_hex_dump(*s, d, a->hl + a->dl);
1788 return *s != NULL;
1791 static CFStringRef CopyCertName(SecCertificateRef _cert, bool issuer)
1793 CFDataRef d = cSecCertificateCopyData(_cert);
1794 CFMutableStringRef ans = CFStringCreateMutable(kCFAllocatorDefault, 0);
1795 bool good = false;
1797 for (;;) {
1798 data_t data;
1799 const data_t *name;
1800 der_cert_t cert;
1801 der_atom_t atom;
1802 bool badset = false;
1804 if (!d || !ans) break;
1805 data.d = CFDataGetBytePtr(d);
1806 data.l = CFDataGetLength(d);
1807 if (!read_der_cert(&data, &cert)) break;
1808 name = issuer ? &cert.issuer : &cert.subject;
1809 data.d = name->d;
1810 data.l = name->l;
1811 if (data.d && data.l) {
1812 if (!read_der_atom(&data, &atom)) break;
1813 if (atom.rawtag != 0x30) break;
1814 data.l -= atom.hl;
1815 data.d += atom.hl;
1816 while (data.l) {
1817 data_t set;
1818 unsigned setidx = 0;
1819 badset = true;
1820 if (!read_der_atom(&data, &atom)) break;
1821 if (atom.rawtag != 0x31) break;
1822 set.d = data.d + atom.hl;
1823 set.l = atom.dl;
1824 data.l -= atom.hl + atom.dl;
1825 data.d += atom.hl + atom.dl;
1826 for (;;) {
1827 data_t oid;
1828 if (!read_der_atom(&set, &atom)) break;
1829 if (atom.rawtag != 0x30) break;
1830 set.l -= atom.hl;
1831 set.d += atom.hl;
1832 if (!read_der_atom(&set, &atom)) break;
1833 if (atom.rawtag != 6) break;
1834 oid.d = set.d;
1835 oid.l = atom.hl + atom.dl;
1836 set.l -= atom.hl + atom.dl;
1837 set.d += atom.hl + atom.dl;
1838 if (!read_der_atom(&set, &atom)) break;
1839 append_oid_name(ans, setidx++?"/+":"/", oid.d, oid.l, "=");
1840 append_attr_value(&ans, set.d, &atom, 0);
1841 set.l -= atom.hl + atom.dl;
1842 set.d += atom.hl + atom.dl;
1843 if (!set.l) {
1844 badset=false;
1845 break;
1848 if (badset) break;
1850 if (badset || data.l) break;
1851 good = true;
1853 break;
1855 if (d) CFRelease(d);
1856 if (!good && ans) {CFRelease(ans); ans=NULL;}
1857 return ans;
1860 CFStringRef CopyCertSubject(SecCertificateRef _cert)
1862 return CopyCertName(_cert, false);
1865 CFStringRef CopyCertSubjectKeyId(SecCertificateRef _cert)
1867 return CopyCertKeyId(_cert, false);
1870 CFStringRef CopyCertIssuer(SecCertificateRef _cert)
1872 return CopyCertName(_cert, true);
1875 CFStringRef CopyCertIssuerKeyId(SecCertificateRef _cert)
1877 return CopyCertKeyId(_cert, true);
1880 /* return CFArrayRef if arr else CFStringRef
1881 * flags:
1882 * 0x01 includes DNS alts
1883 * 0x02 includes IPv4 alts
1884 * 0x04 includes IPv6 alts
1885 * 0x08 include IP other alts
1887 static CFTypeRef CopyCertSubjectAltNamesInt(const der_cert_t *cert, bool arr, unsigned flags)
1889 data_t data;
1890 CFTypeRef ans = arr ?
1891 (CFTypeRef)CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks) :
1892 (CFTypeRef)CFStringCreateMutable(kCFAllocatorDefault, 0);
1893 bool good = false;
1895 do {
1896 der_atom_t atom;
1898 if (!cert || !ans) break;
1899 if (!cert->subjectAltNames.d || !cert->subjectAltNames.l) break;
1900 data.d = cert->subjectAltNames.d;
1901 data.l = cert->subjectAltNames.l;
1902 if (!read_der_atom(&data, &atom)) break;
1903 if (atom.rawtag != 0x30) break;
1904 data.l -= atom.hl;
1905 data.d += atom.hl;
1906 do {
1907 if (!read_der_atom(&data, &atom)) break;
1908 if (atom.rawtag == 0x82 && (flags & 0x01)) {
1909 CFStringRef temp;
1910 if (!arr && CFStringGetLength((CFStringRef)ans))
1911 CFStringAppendCString((CFMutableStringRef)ans, ",", kCFStringEncodingASCII);
1912 temp = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, data.d+atom.hl,
1913 atom.dl, kCFStringEncodingASCII, true, kCFAllocatorNull);
1914 if (!temp) break;
1915 if (arr)
1916 CFArrayAppendValue((CFMutableArrayRef)ans, temp);
1917 else
1918 CFStringAppend((CFMutableStringRef)ans, temp);
1919 CFRelease(temp);
1920 } else if (atom.rawtag == 0x87 && (flags & 0x0e)) {
1921 if ((atom.dl == 4 && (flags & 0x02)) ||
1922 (atom.dl == 16 && (flags & 0x04)) ||
1923 (atom.dl != 4 && atom.dl != 16 && (flags & 0x08))) {
1924 if (arr) {
1925 CFDataRef dtemp = CFDataCreate(kCFAllocatorDefault, data.d+atom.hl, atom.dl);
1926 if (!dtemp) break;
1927 CFArrayAppendValue((CFMutableArrayRef)ans, dtemp);
1928 CFRelease(dtemp);
1929 } else {
1930 if (CFStringGetLength((CFStringRef)ans))
1931 CFStringAppendCString((CFMutableStringRef)ans, ",", kCFStringEncodingASCII);
1932 if (atom.dl == 4) {
1933 char ipv4str[16];
1934 const uint8_t *ip = data.d+atom.hl;
1935 sprintf(ipv4str, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
1936 CFStringAppendCString((CFMutableStringRef)ans, ipv4str, kCFStringEncodingASCII);
1937 } else if (atom.dl == 16) {
1938 char ntopbuff[INET6_ADDRSTRLEN];
1939 if (!inet_ntop(AF_INET6, data.d+atom.hl, ntopbuff, sizeof(ntopbuff)))
1940 break;
1941 CFStringAppendCString((CFMutableStringRef)ans, ntopbuff, kCFStringEncodingASCII);
1942 } else {
1943 append_hex_dump((CFMutableStringRef)ans, data.d+atom.hl, atom.dl);
1948 data.l -= atom.hl + atom.dl;
1949 data.d += atom.hl + atom.dl;
1950 } while (data.l);
1951 if (!data.l && ( (arr && CFArrayGetCount((CFArrayRef)ans) ) ||
1952 (!arr && CFStringGetLength((CFStringRef)ans)) ))
1953 good = true;
1954 } while (0);
1955 if (!good && ans) {CFRelease(ans); ans=NULL;}
1956 return ans;
1959 static CFArrayRef CopyCertSubjectCNIds(const der_cert_t *cert)
1961 CFMutableArrayRef ans =
1962 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1963 bool good = false;
1965 do {
1966 data_t data;
1967 der_atom_t atom;
1969 if (!ans || !cert || !cert->subject.d || !cert->subject.l) break;
1970 data.d = cert->subject.d;
1971 data.l = cert->subject.l;
1972 if (!read_der_atom(&data, &atom)) break;
1973 if (atom.rawtag != 0x30) break;
1974 data.l -= atom.hl;
1975 data.d += atom.hl;
1976 while (data.l) {
1977 data_t set;
1978 if (!read_der_atom(&data, &atom)) break;
1979 if (atom.rawtag != 0x31) break;
1980 set.d = data.d + atom.hl;
1981 set.l = atom.dl;
1982 data.l -= atom.hl + atom.dl;
1983 data.d += atom.hl + atom.dl;
1984 if (!read_der_atom(&set, &atom)) break;
1985 if (atom.rawtag != 0x30) break;
1986 if (atom.hl + atom.dl == set.l) { /* single-value CN only */
1987 CFMutableStringRef cnid;
1988 data_t oid;
1989 set.l -= atom.hl;
1990 set.d += atom.hl;
1991 if (!read_der_atom(&set, &atom)) break;
1992 if (atom.rawtag != 6) break;
1993 oid.d = set.d;
1994 oid.l = atom.hl + atom.dl;
1995 if (data_matches(&oid, &OID_CommonName)) {
1996 set.l -= atom.hl + atom.dl;
1997 set.d += atom.hl + atom.dl;
1998 if (!read_der_atom(&set, &atom)) break;
1999 if (append_attr_value(&cnid, set.d, &atom, 0x1f) && cnid) {
2000 CFArrayAppendValue(ans, cnid);
2001 CFRelease(cnid);
2006 if (!data.l)
2007 good = true;
2008 } while (0);
2009 if (ans && (!good || !CFArrayGetCount(ans))) {
2010 CFRelease(ans);
2011 ans=NULL;
2013 return ans;
2016 CFStringRef CopyCertSubjectAltNamesString(SecCertificateRef _cert)
2018 CFDataRef d = cSecCertificateCopyData(_cert);
2019 data_t data;
2020 der_cert_t cert;
2021 CFStringRef ans = NULL;
2023 if (!d) return NULL;
2024 data.d = CFDataGetBytePtr(d);
2025 data.l = CFDataGetLength(d);
2026 if (read_der_cert(&data, &cert))
2027 ans = (CFStringRef)CopyCertSubjectAltNamesInt(&cert, false, 0x0f);
2028 CFRelease(d);
2029 return ans;
2032 /* mode:
2033 * 0 = DNS/CN ids
2034 * 4 = IPv4 ids
2035 * 16 = IPv6 ids
2037 static CFArrayRef CopyCertSubjectIds(der_cert_t *c, unsigned mode)
2039 CFArrayRef ans = NULL;
2040 if (!c || (mode != 0 && mode != 4 && mode != 16)) return NULL;
2041 if (mode == 4)
2042 return ans = (CFArrayRef)CopyCertSubjectAltNamesInt(c, true, 0x02);
2043 if (mode == 16)
2044 return ans = (CFArrayRef)CopyCertSubjectAltNamesInt(c, true, 0x04);
2045 else {
2046 ans = (CFArrayRef)CopyCertSubjectAltNamesInt(c, true, 0x01);
2047 if (!ans || !CFArrayGetCount(ans)) {
2048 if (ans) CFRelease(ans);
2049 ans = CopyCertSubjectCNIds(c);
2052 if (ans && !CFArrayGetCount(ans)) {
2053 CFRelease(ans);
2054 ans = NULL;
2056 return ans;
2059 OSStatus VerifyTrustChain(SecTrustRef trust, CFArrayRef customRootsOrNull,
2060 unsigned explicitCertsOnly, unsigned flags, const char *peername)
2062 SecTrustResultType result;
2063 CFArrayRef chain = NULL;
2064 CSSM_TP_APPLE_EVIDENCE_INFO *evidence;
2065 OSStatus err = cSecTrustGetResult(trust, &result, &chain, &evidence);
2066 size_t i, cnt;
2067 if (err == errSecTrustNotAvailable) {
2068 /* We need to evaluate first */
2069 CFArrayRef anchors = customRootsOrNull;
2070 if (chain) CFRelease(chain);
2071 if (anchors)
2072 CFRetain(anchors);
2073 else {
2074 err = SecTrustCopyAnchorCertificates(&anchors);
2075 if (err) return err;
2077 err = SecTrustSetAnchorCertificates(trust, anchors);
2078 CFRelease(anchors);
2079 if (err) return err;
2080 err = cSecTrustSetAnchorCertificatesOnly(trust, customRootsOrNull ? true : false);
2081 if (err && err != unimpErr) return err;
2082 err = SecTrustEvaluate(trust, &result);
2083 if (err) return err;
2084 chain = NULL;
2085 err = cSecTrustGetResult(trust, &result, &chain, &evidence);
2087 if (err) {
2088 if (chain) CFRelease(chain);
2089 return err;
2091 if (!chain || !evidence || !CFArrayGetCount(chain)) {
2092 if (chain) CFRelease(chain);
2093 return errSSLXCertChainInvalid;
2095 cnt = (size_t)CFArrayGetCount(chain);
2096 if ((peername && *peername) ||
2097 (!(flags & CSSM_TP_ACTION_LEAF_IS_CA) &&
2098 !(evidence[0].StatusBits & CSSM_CERT_STATUS_IS_ROOT))) {
2099 CFDataRef certder = cSecCertificateCopyData(
2100 (SecCertificateRef)CFArrayGetValueAtIndex(chain, 0));
2101 data_t der;
2102 der_cert_t cert;
2103 if (!certder)
2104 return errSSLBadCert;
2105 der.d = CFDataGetBytePtr(certder);
2106 der.l = CFDataGetLength(certder);
2107 if (!read_der_cert(&der, &cert))
2108 err = errSSLBadCert;
2110 /* First confirm we have a host name match. SecureTransport should do
2111 * this for us (but will not give us a decent result code) except that
2112 * it has problems with IPv6 address matching */
2113 if (!err && peername && *peername) {
2114 size_t peerlen = strlen(peername);
2115 union {
2116 uint8_t ipv6[16];
2117 uint8_t ipv4[4];
2118 } ipa;
2119 int mode = -1;
2120 if (parse_ipv4_name(peername, peerlen, ipa.ipv4)) mode = 4;
2121 else if (is_dns_name(peername, peerlen, false)) mode = 0;
2122 else if (parse_ipv6_name(peername, peerlen, ipa.ipv6)) mode = 16;
2123 if (mode == -1)
2124 /* if we can't parse peername it can't possibly match! */
2125 err = errSSLHostNameMismatch;
2126 if (!err) {
2127 CFArrayRef ids = CopyCertSubjectIds(&cert, mode);
2128 if (ids && !CFArrayGetCount(ids)) {
2129 CFRelease(ids);
2130 ids = NULL;
2132 if (!ids)
2133 /* if we don't have anything to match against it can't possibly match! */
2134 err = errSSLHostNameMismatch;
2135 else {
2136 size_t j, idcnt = CFArrayGetCount(ids);
2137 bool matched = false;
2138 for (j = 0; j < idcnt; ++j) {
2139 CFTypeRef oneid = (CFTypeRef)CFArrayGetValueAtIndex(ids, j);
2140 if (mode) {
2141 const uint8_t *p;
2142 size_t l;
2143 if (CFDataGetTypeID() != CFGetTypeID(oneid)) continue;
2144 p = (uint8_t *)CFDataGetBytePtr((CFDataRef)oneid);
2145 l = (size_t)CFDataGetLength((CFDataRef)oneid);
2146 if (l != (size_t)mode) continue;
2147 if (memcmp(ipa.ipv6, p, l) == 0) {
2148 matched = true;
2149 break;
2151 } else {
2152 char dnsname[256];
2153 if (CFStringGetTypeID() != CFGetTypeID(oneid)) continue;
2154 if (!CFStringGetCString((CFStringRef)oneid, dnsname,
2155 sizeof(dnsname), kCFStringEncodingASCII))
2156 continue;
2157 if (peername_matches_id(peername, dnsname)) {
2158 matched = true;
2159 break;
2163 CFRelease(ids);
2164 if (!matched)
2165 err = errSSLHostNameMismatch;
2170 /* Confirm that the first certificate is NOT a CA (otherwise it's not a
2171 * valid chain), but again SecureTransport should have already checked that
2172 * for us. CSSM_TP_ACTION_LEAF_IS_CA overrides. Also we never check the
2173 * root certificate even if it's also the leaf. */
2174 if (!err && !(flags & CSSM_TP_ACTION_LEAF_IS_CA) &&
2175 !(evidence[0].StatusBits & CSSM_CERT_STATUS_IS_ROOT) && cert.isCA)
2176 err = errSSLXCertChainInvalid;
2178 CFRelease(certder);
2179 if (err) return err;
2181 if (explicitCertsOnly & 0x01) {
2182 /* Check all but root */
2183 for (i = 0; i < cnt; ++i) {
2184 if (!(evidence[i].StatusBits & CSSM_CERT_STATUS_IS_IN_INPUT_CERTS) &&
2185 !(evidence[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT)) {
2186 /* If the magical cert had not appeared, the chain would have stopped
2187 * here and the error would be no root, so return that error */
2188 CFRelease(chain);
2189 return errSSLNoRootCert;
2193 if (!(flags & CSSM_TP_ACTION_ALLOW_EXPIRED) ||
2194 !(flags & CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT)) {
2195 /* check for expired or not yet valid certs */
2196 for (i = 0; i < cnt; ++i) {
2197 if ((flags & CSSM_TP_ACTION_ALLOW_EXPIRED) &&
2198 !(evidence[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT))
2199 continue;
2200 if ((flags & CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT) &&
2201 (evidence[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT))
2202 continue;
2203 if (evidence[i].StatusBits & CSSM_CERT_STATUS_EXPIRED) {
2204 CFRelease(chain);
2205 return errSSLCertExpired;
2207 if (evidence[i].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) {
2208 CFRelease(chain);
2209 return errSSLCertNotYetValid;
2213 /* check for no root */
2214 if (!(evidence[cnt-1].StatusBits & CSSM_CERT_STATUS_IS_ROOT)) {
2215 CFRelease(chain);
2216 return errSSLNoRootCert;
2218 /* check for unknown root */
2219 if (!(evidence[cnt-1].StatusBits & CSSM_CERT_STATUS_IS_IN_ANCHORS)) {
2220 CFRelease(chain);
2221 return errSSLUnknownRootCert;
2223 if (customRootsOrNull) {
2224 /* make sure we're not using a gratuitous root, Mac OS X likes to just
2225 * go ahead and use its anchors sometimes despite settings to the contrary */
2226 if (!SecCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(chain, cnt-1),
2227 customRootsOrNull)) {
2228 CFRelease(chain);
2229 return errSSLNoRootCert;
2232 CFRelease(chain);
2233 /* everything looks good, so check the trust result code now */
2234 switch (result) {
2235 case kSecTrustResultProceed:
2236 case kSecTrustResultUnspecified:
2237 /* good result */
2238 return noErr;
2239 case kSecTrustResultDeny:
2240 /* DENIED! */
2241 return errSecTrustSettingDeny;
2242 default:
2243 /* drop out */;
2245 /* everything else (confirm, invalid, recoverable, fatal, other) */
2246 return errSecNotTrusted;
2249 #elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
2251 #error iOS is not currently supported
2253 #endif /* TARGET_OS_EMBEDDED || TARGET_OS_IPHONE */