patches: more minor updates
[git-osx-installer.git] / patches / curl / stcompat.c
blob52e6789e48620f0c268a6da6136108cb1ef18eff
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 struct cipher_order {
44 int tier;
45 int order;
46 unsigned cipher;
49 typedef struct data_s {
50 const uint8_t *d;
51 size_t l;
52 } data_t;
54 extern CFStringRef CFStringCreateWithBytesNoCopy(
55 CFAllocatorRef alloc,
56 const UInt8 *bytes,
57 CFIndex numBytes,
58 CFStringEncoding encoding,
59 Boolean isExternalRepresentation,
60 CFAllocatorRef contentsDeallocator); /* available 10.4 but not in header */
61 extern CFStringRef NSTemporaryDirectory(void);
62 static Boolean CheckPubKeyOkayInt(CFDataRef d, data_t *pubkey, int flags);
63 static pthread_mutex_t keych_mutex = PTHREAD_MUTEX_INITIALIZER;
65 /* Macro result is true on success */
66 #define MUTEX_LOCK() (!pthread_mutex_lock(&keych_mutex))
67 #define MUTEX_UNLOCK() (!pthread_mutex_unlock(&keych_mutex))
69 static const struct cipher_order order_table[] = {
70 { 2, -1, TLS_RSA_WITH_RC4_128_MD5 },
71 { 2, -1, TLS_RSA_WITH_RC4_128_SHA },
72 { 2, -1, TLS_RSA_WITH_3DES_EDE_CBC_SHA },
73 { 2, -1, TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA },
74 { 2, -1, TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA },
75 { 1, 26, TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA },
76 { 1, 7, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA },
77 { 2, -1, SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA },
78 { 1, 32, TLS_RSA_WITH_AES_128_CBC_SHA },
79 { 2, -1, TLS_DH_DSS_WITH_AES_128_CBC_SHA },
80 { 2, -1, TLS_DH_RSA_WITH_AES_128_CBC_SHA },
81 { 1, 25, TLS_DHE_DSS_WITH_AES_128_CBC_SHA },
82 { 1, 6, TLS_DHE_RSA_WITH_AES_128_CBC_SHA },
83 { 1, 31, TLS_RSA_WITH_AES_256_CBC_SHA },
84 { 2, -1, TLS_DH_DSS_WITH_AES_256_CBC_SHA },
85 { 2, -1, TLS_DH_RSA_WITH_AES_256_CBC_SHA },
86 { 1, 24, TLS_DHE_DSS_WITH_AES_256_CBC_SHA },
87 { 1, 5, TLS_DHE_RSA_WITH_AES_256_CBC_SHA },
88 { 1, 30, TLS_RSA_WITH_AES_128_CBC_SHA256 },
89 { 1, 28, TLS_RSA_WITH_AES_256_CBC_SHA256 },
90 { 2, -1, TLS_DH_DSS_WITH_AES_128_CBC_SHA256 },
91 { 2, -1, TLS_DH_RSA_WITH_AES_128_CBC_SHA256 },
92 { 1, 23, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 },
93 { 1, 4, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 },
94 { 2, -1, TLS_DH_DSS_WITH_AES_256_CBC_SHA256 },
95 { 2, -1, TLS_DH_RSA_WITH_AES_256_CBC_SHA256 },
96 { 1, 21, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 },
97 { 1, 2, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 },
98 { 2, -1, TLS_PSK_WITH_RC4_128_SHA },
99 { 2, -1, TLS_PSK_WITH_3DES_EDE_CBC_SHA },
100 { 2, -1, TLS_PSK_WITH_AES_128_CBC_SHA },
101 { 2, -1, TLS_PSK_WITH_AES_256_CBC_SHA },
102 { 2, -1, TLS_DHE_PSK_WITH_RC4_128_SHA },
103 { 2, -1, TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA },
104 { 2, -1, TLS_DHE_PSK_WITH_AES_128_CBC_SHA },
105 { 2, -1, TLS_DHE_PSK_WITH_AES_256_CBC_SHA },
106 { 2, -1, TLS_RSA_PSK_WITH_RC4_128_SHA },
107 { 2, -1, TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA },
108 { 2, -1, TLS_RSA_PSK_WITH_AES_128_CBC_SHA },
109 { 2, -1, TLS_RSA_PSK_WITH_AES_256_CBC_SHA },
110 { 1, 29, TLS_RSA_WITH_AES_128_GCM_SHA256 },
111 { 1, 27, TLS_RSA_WITH_AES_256_GCM_SHA384 },
112 { 1, 3, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 },
113 { 1, 1, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 },
114 { 2, -1, TLS_DH_RSA_WITH_AES_128_GCM_SHA256 },
115 { 2, -1, TLS_DH_RSA_WITH_AES_256_GCM_SHA384 },
116 { 1, 22, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 },
117 { 1, 20, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 },
118 { 2, -1, TLS_DH_DSS_WITH_AES_128_GCM_SHA256 },
119 { 2, -1, TLS_DH_DSS_WITH_AES_256_GCM_SHA384 },
120 { 2, -1, TLS_PSK_WITH_AES_128_GCM_SHA256 },
121 { 2, -1, TLS_PSK_WITH_AES_256_GCM_SHA384 },
122 { 2, -1, TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 },
123 { 2, -1, TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 },
124 { 2, -1, TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 },
125 { 2, -1, TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 },
126 { 2, -1, TLS_PSK_WITH_AES_128_CBC_SHA256 },
127 { 2, -1, TLS_PSK_WITH_AES_256_CBC_SHA384 },
128 { 2, -1, TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 },
129 { 2, -1, TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 },
130 { 2, -1, TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 },
131 { 2, -1, TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 },
132 { 2, -1, TLS_ECDH_ECDSA_WITH_RC4_128_SHA },
133 { 2, -1, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA },
134 { 2, -1, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA },
135 { 2, -1, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA },
136 { 2, -1, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA },
137 { 2, -1, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA },
138 { 1, 19, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA },
139 { 1, 18, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA },
140 { 2, -1, TLS_ECDH_RSA_WITH_RC4_128_SHA },
141 { 2, -1, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA },
142 { 2, -1, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA },
143 { 2, -1, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA },
144 { 2, -1, TLS_ECDHE_RSA_WITH_RC4_128_SHA },
145 { 2, -1, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA },
146 { 1, 13, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA },
147 { 1, 12, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA },
148 { 1, 17, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 },
149 { 1, 15, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 },
150 { 2, -1, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 },
151 { 2, -1, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 },
152 { 1, 11, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 },
153 { 1, 9, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 },
154 { 2, -1, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 },
155 { 2, -1, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 },
156 { 1, 16, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 },
157 { 1, 14, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 },
158 { 2, -1, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 },
159 { 2, -1, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 },
160 { 1, 10, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 },
161 { 1, 8, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 },
162 { 2, -1, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 },
163 { 2, -1, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 },
164 { 2, -1, SSL_RSA_WITH_RC2_CBC_MD5 },
165 { 2, -1, SSL_RSA_WITH_DES_CBC_MD5 },
166 { 2, -1, SSL_RSA_WITH_3DES_EDE_CBC_MD5 }
169 static CFStringRef CFCopyTemporaryDirectory(void)
171 id pool = objc_msgSend(objc_getClass("NSAutoreleasePool"), sel_getUid("new"));
172 CFStringRef dir = (CFStringRef)NSTemporaryDirectory();
173 if (dir) CFRetain(dir);
174 objc_msgSend(pool, sel_getUid("drain"));
175 if (dir) {
176 unsigned len = (unsigned)CFStringGetLength(dir);
177 unsigned l = len;
178 while (l > 1 &&
179 (unsigned)CFStringGetCharacterAtIndex(dir, l-1) == (unsigned)'/') {
180 --l;
182 if (l < len) {
183 CFStringRef old = dir;
184 dir = CFStringCreateWithSubstring(
185 kCFAllocatorDefault, dir, CFRangeMake(0, l));
186 if (dir)
187 CFRelease(old);
188 else
189 dir = old;
192 return dir;
195 char *CFStringCreateUTF8String(CFStringRef s, Boolean release)
197 size_t m;
198 char *c;
200 if (!s) return NULL;
201 m = (size_t)CFStringGetMaximumSizeForEncoding(
202 CFStringGetLength(s), kCFStringEncodingUTF8) + 1;
203 c = (char *)malloc(m);
204 if (!c) {
205 if (release) CFRelease(s);
206 return NULL;
208 if (!CFStringGetCString(s, c, m, kCFStringEncodingUTF8)) {
209 free(c);
210 c = NULL;
212 if (release) CFRelease(s);
213 return c;
216 static CFStringRef MakeVisibleString(CFStringRef in)
218 CFStringRef nullbyte;
219 CFMutableStringRef m;
220 if (!in) return in;
221 nullbyte = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, (UInt8 *)"\0",
222 1, kCFStringEncodingASCII, false, kCFAllocatorNull);
223 if (!nullbyte) return in;
224 m = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, in);
225 if (!m) return in;
226 CFRelease(in);
227 CFStringFindAndReplace(m, nullbyte, CFSTR("\\000"),
228 CFRangeMake(0, CFStringGetLength(m)), 0);
229 CFRelease(nullbyte);
230 return m;
233 CFDataRef CFDataCreateWithContentsOfFile(CFAllocatorRef a, const char *f)
235 char buff[4096];
236 CFMutableDataRef d = CFDataCreateMutable(a, 0);
237 int fd;
238 ssize_t cnt;
239 if (!d) return NULL;
240 fd = open(f, O_RDONLY);
241 if (fd < 0) {
242 CFRelease(d);
243 return NULL;
245 do {
246 cnt = read(fd, buff, sizeof(buff));
247 if (cnt > 0) CFDataAppendBytes(d, (UInt8 *)buff, (size_t)cnt);
248 } while (cnt > 0);
249 close(fd);
250 if (cnt) {
251 CFRelease(d);
252 return NULL;
254 return d;
257 #undef memmem
258 #define memmem(v1,l1,v2,l2) cmemmem(v1,l1,v2,l2)
259 static void *cmemmem(const void *_m, size_t ml, const void *_s, size_t sl)
261 const char *m = (const char *)_m;
262 const char *s = (const char *)_s;
263 if (!ml || !sl || ml < sl) return NULL;
264 if (sl == 1) return memchr(m, *s, ml);
265 if (ml == sl) return (void *)(memcmp(m, s, sl) ? NULL : m);
266 do {
267 size_t o;
268 const char *p = memchr(m, *s, ml);
269 if (!p) return NULL;
270 o = p - m;
271 ml -= o;
272 m += o;
273 if (ml < sl) return NULL;
274 if (!memcmp(m, s, sl)) return (void *)m;
275 ++m;
276 --ml;
277 } while (ml >= sl);
278 return NULL;
281 CF_INLINE int is_eol(int c)
283 return c == '\n' || c == '\r';
286 CF_INLINE int is_lb(int c)
288 return c == '\n' || c == '\r' || c == '\f';
291 CF_INLINE int is_prnt(int c)
293 return c >= ' ' && c <= '~';
296 CF_INLINE int is_spc(int c)
298 return c == ' ' || c == '\t';
301 static int has_lb(const void *_m, size_t l)
303 const char *m = (const char *)_m;
304 while (l) {
305 if (is_lb(*m)) return 1;
306 --l;
307 ++m;
309 return 0;
312 static int has_prnt(const void *_m, size_t l)
314 const char *m = (const char *)_m;
315 while (l) {
316 if (!is_prnt(*m)) return 0;
317 --l;
318 ++m;
320 return 1;
324 * returns start of "-----BEGIN XXXX-----\n" line or NULL if not found/error
325 * If returns NULL then *ot == 0 means not found, *ot == -1 means bad line
326 * If returns non-NULL then *ol is length through "-----\n" and *ot is length
327 * of "XXXX" part (which obviously starts at return value + 11 for BEGIN or
328 * value + 9 for END)
329 * If e is non-zero look for ----END rather than ----BEGIN
331 static const char *find_be(const void *_m, size_t l, size_t *ol, int *ot, int e)
333 const char *m = (const char *)_m;
334 const char *origm = m;
335 const char *marker = e ? "-----END " : "-----BEGIN ";
336 size_t mkl = e ? 9 : 11;
337 *ot = 0;
338 while (l) {
339 const char *t;
340 const char *p = (char *)memmem(m, l, marker, mkl);
341 if (!p) return NULL;
342 l -= (p - m) + mkl;
343 m = p + mkl;
344 if (p > origm && !is_eol(p[-1])) continue;
345 t = (char *)memmem(m, l, "-----", 5);
346 if (!t) return NULL;
347 l -= (t - m);
348 if (l > 5 && !is_eol(t[5])) continue;
349 if (has_lb(p, t-p)) continue;
350 if ((size_t)(t-p) > (76 - mkl - 5) || !has_prnt(p, t-p)) {
351 *ot = -1;
352 return NULL;
354 *ot = (int)(t - m);
355 l -= 5;
356 m = t + 5;
357 if (l && *m == '\r') {
358 ++m;
359 --l;
361 if (l && *m == '\n') {
362 ++m;
363 --l;
365 *ol = m - p;
366 return p;
368 return NULL;
371 typedef enum pemtype_e {
372 pemtype_unknown,
373 pemtype_certificate, /* "CERTIFICATE" or "TRUSTED CERTIFICATE" or "X509 CERTIFICATE" */
374 pemtype_crl, /* "X509 CRL" */
375 pemtype_publickey, /* "PUBLIC KEY" */
376 pemtype_privatekey_rsa /* "RSA PRIVATE KEY" */
377 } pemtype_t;
379 typedef struct peminfo_s {
380 const char *start; /* Armour start "-----BEGIN XXXXX-----\n" */
381 size_t len; /* Length through armour end "-----END XXXXX-----\n" */
382 /* Body starts after "-----BEGIN XXXXX-----\n" */
383 const char *body;
384 size_t bodylen; /* Length though "\n" BEFORE final "-----END XXXXX-----\n" */
385 /* Kind starts at start + 11 */
386 size_t kindlen; /* length of "XXXXX" from "-----BEGIN XXXXX-----\n" */
387 pemtype_t type;
388 } peminfo_t;
390 static int nextpem(const char *p, size_t l, peminfo_t *o)
392 size_t beglen, endlen;
393 int begtype, endtype;
394 const char *end;
395 const char *beg = find_be(p, l, &beglen, &begtype, 0);
396 if (!beg) return begtype;
397 end = find_be(p + beglen, l - beglen, &endlen, &endtype, 1);
398 if (!end || begtype != endtype || memcmp(beg+11, end+9, (size_t)begtype))
399 return -1;
400 o->start = beg;
401 o->len = (end + endlen) - beg;
402 o->body = beg + beglen;
403 o->bodylen = end - (beg + beglen);
404 o->kindlen = (size_t)begtype;
405 if (begtype == 11 && !memcmp(beg + 11, "CERTIFICATE", 11)) {
406 o->type = pemtype_certificate;
407 } else if (begtype == 19 && !memcmp(beg + 11, "TRUSTED CERTIFICATE", 19)) {
408 o->type = pemtype_certificate;
409 } else if (begtype == 16 && !memcmp(beg + 11, "X509 CERTIFICATE", 16)) {
410 o->type = pemtype_certificate;
411 } else if (begtype == 15 && !memcmp(beg + 11, "RSA PRIVATE KEY", 15)) {
412 o->type = pemtype_privatekey_rsa;
413 } else if (begtype == 10 && !memcmp(beg + 11, "PUBLIC KEY", 10)) {
414 o->type = pemtype_publickey;
415 } else if (begtype == 8 && !memcmp(beg + 11, "X509 CRL", 8)) {
416 o->type = pemtype_crl;
417 } else {
418 o->type = pemtype_unknown;
420 return (int)((o->start + o->len) - p);
423 #define BASE64CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
424 #define BASE64SIZE(n) ((((unsigned)(n)+2)/3)*4)
426 static const signed char b64tab[256] = {
427 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
428 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
429 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x3E,-1,-1,-1,0x3F,
430 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,-1,-1,-1,0x40,-1,-1,
431 -1,0,1,2,3,4,5,6,7,8,9,0x0A,0x0B,0x0C,0x0D,0x0E,
432 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,-1,-1,-1,-1,-1,
433 -1,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
434 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,-1,-1,-1,-1,-1,
435 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
436 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
437 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
438 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
439 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
440 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
441 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
442 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
445 static void convert24(const uint8_t *input, uint8_t *output)
447 output[0] = ((b64tab[input[0]]&0x3F)<<2)|((b64tab[input[1]]&0x3F)>>4);
448 output[1] = (((b64tab[input[1]]&0x3F)&0x0F)<<4)|((b64tab[input[2]]&0x3F)>>2);
449 output[2] = (((b64tab[input[2]]&0x3F)&0x03)<<6)|(b64tab[input[3]]&0x3F);
452 static CFDataRef CFDataCreateFromBase64(CFAllocatorRef a, const void *_b, size_t l)
454 uint8_t inp[4];
455 uint8_t out[3];
456 int i;
457 CFMutableDataRef d;
458 const uint8_t *p = (uint8_t *)_b;
459 if (l && !p) return NULL;
460 d = CFDataCreateMutable(a, 0);
461 if (!d) return NULL;
462 if (!l) return d;
463 for (i=0; l; ++p, --l) {
464 uint8_t c = *p;
465 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f')
466 continue;
467 if (b64tab[c] < 0) {
468 CFRelease(d);
469 return NULL;
471 inp[i++] = c;
472 if (i == 4) {
473 convert24(inp, out);
474 i = 0;
475 if (inp[3] == '=') {
476 CFDataAppendBytes(d, out, inp[2] == '=' ? 1 : 2);
477 break;
479 CFDataAppendBytes(d, out, 3);
482 if (i != 0) {
483 CFRelease(d);
484 return NULL;
486 return d;
489 static SecCertificateRef createvalidcert(CFDataRef d)
491 SecCertificateRef cert = cSecCertificateCreateWithData(kCFAllocatorDefault, d);
492 if (!cert) return NULL;
493 if (!CheckCertOkay(cert)) {
494 CFRelease(cert);
495 return NULL;
497 return cert;
500 CFArrayRef CreateCertsArrayWithData(CFDataRef d, const errinfo_t *e)
502 const char *certs, *p;
503 size_t certslen, plen, cnt = 1;
504 CFMutableArrayRef a;
505 if (!d) return NULL;
506 certs = (char *)CFDataGetBytePtr(d);
507 certslen = (size_t)CFDataGetLength(d);
508 a = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
509 if (!a) return NULL;
510 p = certs;
511 plen = certslen;
512 while (plen) {
513 peminfo_t pem;
514 int readcnt = nextpem(p, plen, &pem);
515 if (!readcnt && p == certs) {
516 /* assume it's a DER cert */
517 SecCertificateRef cert;
518 CFDataRef der = CFDataCreate(kCFAllocatorDefault, (UInt8 *)certs, certslen);
519 if (!der) {
520 CFRelease(a);
521 return NULL;
523 cert = createvalidcert(der);
524 CFRelease(der);
525 if (!cert) {
526 if (e)
527 e->f(e->u, "Invalid CA certificate bad DER data");
528 CFRelease(a);
529 return NULL;
531 CFArrayAppendValue(a, cert);
532 CFRelease(cert);
533 return a;
534 } else if (readcnt == -1) {
535 if (e)
536 e->f(e->u, "Invalid CA certificate #%u (offset %u) in bundle",
537 (unsigned)cnt, (unsigned)(p-certs));
538 CFRelease(a);
539 return NULL;
540 } else if (readcnt && pem.type == pemtype_certificate) {
541 CFDataRef der = CFDataCreateFromBase64(kCFAllocatorDefault, pem.body, pem.bodylen);
542 SecCertificateRef cert;
543 if (!der) {
544 if (e)
545 e->f(e->u, "Invalid CA certificate #%u (offset %u) bad base 64 in bundle",
546 (unsigned)cnt, (unsigned)(pem.start-certs));
547 CFRelease(a);
548 return NULL;
550 cert = createvalidcert(der);
551 CFRelease(der);
552 if (!cert) {
553 if (e)
554 e->f(e->u, "Invalid CA certificate #%u (offset %u) bad cert data in bundle",
555 (unsigned)cnt, (unsigned)(pem.start-certs));
556 CFRelease(a);
557 return NULL;
559 CFArrayAppendValue(a, cert);
560 CFRelease(cert);
561 ++cnt;
562 } else if (!readcnt) break;
563 plen -= (pem.start + pem.len) - p;
564 p = pem.start + pem.len;
566 if (!CFArrayGetCount(a)) {
567 CFRelease(a);
568 a = NULL;
570 return a;
573 static void CFArrayAppendValue_data(CFMutableArrayRef a, const data_t *d)
575 if (d && d->d && d->l) {
576 CFDataRef data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)d->d, d->l);
577 if (data) {
578 CFArrayAppendValue(a, data);
579 CFRelease(data);
584 CFArrayRef CreatePubKeyArrayWithData(CFDataRef d, const errinfo_t *e)
586 const char *keys, *p;
587 size_t keyslen, plen, cnt = 1;
588 CFMutableArrayRef a;
589 if (!d) return NULL;
590 keys = (char *)CFDataGetBytePtr(d);
591 keyslen = (size_t)CFDataGetLength(d);
592 a = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
593 if (!a) return NULL;
594 p = keys;
595 plen = keyslen;
596 while (plen) {
597 peminfo_t pem;
598 data_t pubkey;
599 int readcnt = nextpem(p, plen, &pem);
600 if (!readcnt && p == keys) {
601 /* assume it's a DER public key */
602 CFDataRef der = CFDataCreate(kCFAllocatorDefault, (UInt8 *)keys, keyslen);
603 if (!der) {
604 CFRelease(a);
605 return NULL;
607 if (!CheckPubKeyOkayInt(der, &pubkey, 0x01)) {
608 CFRelease(der);
609 if (e)
610 e->f(e->u, "Invalid public key bad DER data");
611 CFRelease(a);
612 return NULL;
614 CFArrayAppendValue_data(a, &pubkey);
615 CFRelease(der);
616 return a;
617 } else if (readcnt == -1) {
618 if (e)
619 e->f(e->u, "Invalid public key #%u (offset %u) in bundle",
620 (unsigned)cnt, (unsigned)(p-keys));
621 CFRelease(a);
622 return NULL;
623 } else if (readcnt && (pem.type == pemtype_publickey ||
624 pem.type == pemtype_certificate)) {
625 CFDataRef der = CFDataCreateFromBase64(kCFAllocatorDefault, pem.body, pem.bodylen);
626 if (!der) {
627 if (e)
628 e->f(e->u, "Invalid public key #%u (offset %u) bad base 64 in bundle",
629 (unsigned)cnt, (unsigned)(pem.start-keys));
630 CFRelease(a);
631 return NULL;
633 if (!CheckPubKeyOkayInt(der, &pubkey, 0x01)) {
634 CFRelease(der);
635 if (e)
636 e->f(e->u, "Invalid public key #%u (offset %u) bad public key data in bundle",
637 (unsigned)cnt, (unsigned)(pem.start-keys));
638 CFRelease(a);
639 return NULL;
641 CFArrayAppendValue_data(a, &pubkey);
642 CFRelease(der);
643 ++cnt;
644 } else if (!readcnt) break;
645 plen -= (pem.start + pem.len) - p;
646 p = pem.start + pem.len;
648 if (!CFArrayGetCount(a)) {
649 CFRelease(a);
650 a = NULL;
652 return a;
655 static void skip_space_semi(const char **pstr)
657 while (is_spc(**pstr)) ++(*pstr);
658 if (**pstr == ';') ++(*pstr);
659 while (is_spc(**pstr)) ++(*pstr);
662 static bool skip_sha256_prefix(const char **pstr)
664 skip_space_semi(pstr);
665 if (**pstr != 's' && **pstr != 'S') return 0;
666 ++(*pstr);
667 if (**pstr != 'h' && **pstr != 'H') return 0;
668 ++(*pstr);
669 if (**pstr != 'a' && **pstr != 'A') return 0;
670 ++(*pstr);
671 if (!strncmp(*pstr, "256//", 5)) {
672 *pstr += 5;
673 return 1;
675 return 0;
678 Boolean IsSha256HashList(const char *hashlist)
680 if (!hashlist || !*hashlist)
681 return 0;
682 return skip_sha256_prefix(&hashlist);
685 CFArrayRef CreatePubKeySha256Array(const char *hashlist, const errinfo_t *e)
687 const char *orig = hashlist;
688 CFMutableArrayRef a = NULL;
689 if (!hashlist || !*hashlist)
690 return NULL;
691 a = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
692 if (!a) return NULL;
693 for (;;) {
694 CFDataRef blob;
695 size_t l;
696 if (!skip_sha256_prefix(&hashlist)) {
697 if (e)
698 e->f(e->u, "Invalid public key sha256 base64 hash -- "
699 "missing \"sha256//\" prefix at string offset %u",
700 (unsigned)(hashlist-orig));
701 CFRelease(a);
702 return NULL;
704 l = strspn(hashlist, BASE64CHARS);
705 if (l != BASE64SIZE(CC_SHA256_DIGEST_LENGTH)) {
706 if (e)
707 e->f(e->u, "Invalid public key sha256 base64 hash -- "
708 "wrong base64 length %u (should be %u) at string offset %u",
709 (unsigned)l, (unsigned)BASE64SIZE(CC_SHA256_DIGEST_LENGTH),
710 (unsigned)(hashlist-orig));
711 CFRelease(a);
712 return NULL;
714 blob = CFDataCreateFromBase64(kCFAllocatorDefault, hashlist, l);
715 if (!blob) {
716 if (e)
717 e->f(e->u, "out of memory");
718 CFRelease(a);
719 return NULL;
721 if ((size_t)CFDataGetLength(blob) != (size_t)CC_SHA256_DIGEST_LENGTH) {
722 if (e)
723 e->f(e->u, "Invalid public key sha256 base64 hash -- "
724 "wrong hash length %u (should be %u) at string offset %u",
725 (unsigned)CFDataGetLength(blob), (unsigned)CC_SHA256_DIGEST_LENGTH,
726 (unsigned)(hashlist-orig));
727 CFRelease(blob);
728 CFRelease(a);
729 return NULL;
731 hashlist += l;
732 CFArrayAppendValue(a, blob);
733 CFRelease(blob);
734 skip_space_semi(&hashlist);
735 if (!*hashlist) break;
737 if (!CFArrayGetCount(a)) {
738 CFRelease(a);
739 a = NULL;
741 return a;
744 typedef struct homedirs_s {
745 /* note that we use geteuid and not getuid because any created files will
746 * end up being owned by geteuid and therefore using geteuid()'s HOME wil
747 * end up being the least disruptive and also if geteuid() != getuid() then
748 * we probably can't read getuid()'s HOME anyway so that's a guaranteed fail */
749 char *home; /* "HOME=..." from getpwuid(geteuid()) if different from environ */
750 char *cur_home; /* "HOME=..." as found in environ if home set otherwise NULL */
751 } homedirs_t;
753 static char *find_home_env(void)
755 char ***eptr = _NSGetEnviron();
756 char **ptr;
757 if (!eptr) return NULL;
758 ptr = *eptr;
759 while (*ptr && strncmp(*ptr, "HOME=", 5)) {
760 ++ptr;
762 return *ptr ? *ptr : NULL;
765 static void get_home_dirs(homedirs_t *dirs)
767 struct passwd *pwinf;
769 if (!dirs) return;
770 dirs->home = NULL;
771 dirs->cur_home = find_home_env();
772 pwinf = getpwuid(geteuid());
773 if (pwinf && pwinf->pw_dir &&
774 (!dirs->cur_home || strcmp(dirs->cur_home+5, pwinf->pw_dir)))
775 asprintf(&dirs->home, "HOME=%s", pwinf->pw_dir);
776 if (!dirs->home)
777 dirs->cur_home = NULL;
780 static void free_home_dirs(homedirs_t *dirs)
782 if (dirs)
783 free(dirs->home);
786 typedef struct tempch_s {
787 SecKeychainRef ref;
788 homedirs_t dirs;
789 char pw[16]; /* random 15-character (0x20-0x7e), NULL terminated password */
790 char loc[1]; /* Always will have at least a NULL byte */
791 } tempch_t;
793 static void gen_rand_pw(void *_out, size_t len)
795 unsigned char *out = (unsigned char *)_out;
796 int fd;
797 if (!out || !len) return;
798 fd = open("/dev/random", O_RDONLY);
799 if (fd) {
800 do {
801 ssize_t cnt, i;
802 do {
803 cnt = read(fd, out, len);
804 } while (cnt == -1 && errno == EINTR);
805 if (cnt <= 0) return;
806 for (i = 0; i < cnt; ++i) {
807 out[i] = (unsigned char)((((unsigned)out[i] * 95) >> 8) + 32);
809 len -= (size_t)cnt;
810 } while (len);
811 close(fd);
815 static tempch_t *new_temp_keych(void)
817 tempch_t *ans;
818 char newdir[PATH_MAX];
819 Boolean okay;
820 CFStringRef tempdir = CFCopyTemporaryDirectory();
822 if (!tempdir) return NULL;
823 okay = CFStringGetCString(tempdir, newdir, sizeof(newdir) - 32, kCFStringEncodingUTF8);
824 CFRelease(tempdir);
825 if (!okay) return NULL;
826 strcat(newdir, "/tch.XXXXXX");
827 ans = (tempch_t *)malloc(sizeof(tempch_t) + strlen(newdir) + 14 /* "/temp.keychain" */);
828 if (!ans) return NULL;
829 ans->ref = NULL;
830 strcpy(ans->loc, newdir);
831 strlcpy(ans->pw, "(:vCZ\"t{UA-zl3g", sizeof(ans->pw)); /* fallback if random fails */
832 gen_rand_pw(ans->pw, sizeof(ans->pw)-1);
833 ans->pw[sizeof(ans->pw)-1] = '\0';
834 if (!mkdtemp(ans->loc)) {
835 free(ans);
836 return NULL;
838 strcat(ans->loc, "/temp.keychain");
839 get_home_dirs(&ans->dirs);
840 return ans;
843 static void del_temp_keych(tempch_t *keych)
845 size_t l;
846 if (!keych) return;
847 l = strlen(keych->loc);
848 if (l > 14 && !strcmp(keych->loc + (l - 14), "/temp.keychain")) {
849 DIR *d;
850 if (keych->ref) {
851 int needs_reset = 0;
852 (void)MUTEX_LOCK();
853 if (keych->dirs.home) {
854 keych->dirs.cur_home = find_home_env();
855 if (!keych->dirs.cur_home || strcmp(keych->dirs.cur_home, keych->dirs.home)) {
856 needs_reset = 1;
857 putenv(keych->dirs.home);
860 (void)SecKeychainLock(keych->ref);
861 (void)SecKeychainDelete(keych->ref);
862 if (needs_reset) {
863 if (keych->dirs.cur_home)
864 putenv(keych->dirs.cur_home);
865 else
866 unsetenv("HOME");
868 CFRelease(keych->ref);
869 keych->ref = NULL;
870 (void)MUTEX_UNLOCK();
872 unlink(keych->loc);
873 keych->loc[l - 14] = '\0';
874 /* the keychain code may leave dot, possibly comma and yet other turds
875 * we may have to remove */
876 d = opendir(keych->loc);
877 if (d) {
878 struct dirent *ent;
879 while ((ent=readdir(d)) != NULL) {
880 char turd[PATH_MAX];
881 if (ent->d_name[0] == '.' &&
882 (ent->d_name[1] == '\0'
883 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) continue;
884 snprintf(turd, sizeof(turd), "%s/%s", keych->loc, ent->d_name);
885 unlink(turd);
887 closedir(d);
889 rmdir(keych->loc);
890 free_home_dirs(&keych->dirs);
891 free(keych);
895 static CFDataRef extract_key_copy(CFDataRef pemseq, int *outpem)
897 const char *p = (char *)CFDataGetBytePtr(pemseq);
898 const char *origp = p;
899 size_t l = (size_t)CFDataGetLength(pemseq);
900 size_t origl = l;
901 *outpem = 0;
902 while (l) {
903 peminfo_t pem;
904 int readcnt = nextpem(p, l, &pem);
905 if (!readcnt && p == origp) {
906 /* Assume it's DER data */
907 CFRetain(pemseq);
908 return pemseq;
910 if (!readcnt || readcnt == -1) return NULL;
911 if (pem.type == pemtype_privatekey_rsa) {
912 *outpem = 1;
913 if (pem.start == origp && pem.len == origl) {
914 CFRetain(pemseq);
915 return pemseq;
917 return CFDataCreate(kCFAllocatorDefault, (uint8_t *)pem.start, pem.len);
919 l -= (size_t)readcnt;
920 p += (size_t)readcnt;
922 return NULL;
925 SecIdentityRef cSecIdentityCreateWithCertificateAndKeyData(
926 SecCertificateRef cert, CFDataRef keydata, CFTypeRef pw, CFStringRef hint,
927 void **kh)
929 int ispem = 0;
930 CFDataRef rawkey = NULL;
931 tempch_t *keych = NULL;
932 int err;
933 SecKeychainRef keychain = NULL;
934 SecExternalFormat format;
935 SecExternalItemType type;
936 SecItemImportExportKeyParameters params;
937 CFArrayRef items = NULL;
938 SecKeyRef key = NULL;
939 SecIdentityRef ans = NULL;
941 if (!cert || !kh) return NULL;
942 if (keydata)
943 rawkey = extract_key_copy(keydata, &ispem);
944 (void)MUTEX_LOCK();
945 while (rawkey) {
946 CFArrayRef searchlist = NULL;
947 keych = new_temp_keych();
948 if (!keych) break;
949 /* SecKeychainCreate has the side effect of adding the new keychain to
950 * the search list which will make it show up in other apps.
951 * SecKeychainDelete removes it from the search list, or we can also get
952 * the search list before the create and restore it right after.
953 * By immediately restoring the search list, we avoid having the new
954 * private key we're importing be searchable by default in other apps.
955 * If we are running with HOME != ~geteuid() then we likely have no
956 * ~/Library/Preferences/com.apple.security.plist which means the system
957 * will "helpfully" set the default keychain to this new keychain we've
958 * just created which is very bad. If Xcode is running it will listen
959 * to that event and then call SecKeychainSetDefault with that very
960 * same temporary keychain (I have no idea why it does this stupid thing)
961 * and that will make it permanent for the user. Ugh. To avoid this,
962 * we temporarily set HOME to getpwuid(geteuid())->pw_dir while we are
963 * creating the temporary keychain and then put HOME back the way it was
964 * immediately thereafter. Git likes to run tests with HOME set to
965 * alternate locations so it's prudent to handle this. */
966 if (keych->dirs.home)
967 putenv(keych->dirs.home);
968 err = SecKeychainCopySearchList(&searchlist);
969 if (!err && searchlist)
970 err = SecKeychainCreate(keych->loc, sizeof(keych->pw), keych->pw, false,
971 NULL, &keychain);
972 if (searchlist) {
973 if (!err)
974 /* This shouldn't fail, but we also shouldn't die if it does because
975 * we've already got the new keychain and can still go ahead. It could
976 * mean the new temporary keychain is visible longer than we'd like
977 * though, but there's nothing we can do about that. */
978 (void)SecKeychainSetSearchList(searchlist);
979 CFRelease(searchlist);
981 if (keych->dirs.home) {
982 if (keych->dirs.cur_home)
983 putenv(keych->dirs.cur_home);
984 else
985 unsetenv("HOME");
987 if (err || !keychain)
988 break;
989 keych->ref = keychain;
990 err = SecKeychainUnlock(keychain, sizeof(keych->pw), keych->pw, true);
991 if (err) break;
993 SecKeychainSettings settings;
994 settings.version = SEC_KEYCHAIN_SETTINGS_VERS1;
995 settings.lockOnSleep = false;
996 settings.useLockInterval = false;
997 settings.lockInterval = INT_MAX;
998 (void)SecKeychainSetSettings(keychain, &settings);
1000 format = ispem ? kSecFormatWrappedOpenSSL : kSecFormatOpenSSL;
1001 type = kSecItemTypePrivateKey;
1002 memset(&params, 0, sizeof(params));
1003 params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
1004 params.flags = kSecKeyImportOnlyOne|kSecKeyNoAccessControl;
1005 if (pw)
1006 params.passphrase = pw;
1007 else {
1008 params.flags |= kSecKeySecurePassphrase;
1009 /* Note that params.alertTitle is ignored */
1010 params.alertPrompt = hint;
1012 err = cSecItemImport(rawkey, NULL, &format, &type,
1013 ispem?kSecItemPemArmour:0, &params, keychain, &items);
1014 CFRelease(rawkey);
1015 if (!err && items && CFArrayGetCount(items) == 1 &&
1016 CFGetTypeID((CFTypeRef)CFArrayGetValueAtIndex(items, 0)) == SecKeyGetTypeID()) {
1017 key = (SecKeyRef)CFArrayGetValueAtIndex(items, 0);
1018 CFRetain(key);
1020 if (items) CFRelease(items);
1021 break;
1023 if (key) {
1024 /* If we have a key we must also have a keychain */
1025 err = cSecIdentityCreateWithCertificate(keychain, cert, &ans);
1026 CFRelease(key);
1028 (void)MUTEX_UNLOCK();
1029 /* We MUST NOT call SecKeychainDelete because that will purge all copies of
1030 * the keychain from memory. We've already removed it from the search list
1031 * so we just release it and remove the disk files instead in order to allow
1032 * the in memory copy to remain unmolested. Unfortunately on older systems
1033 * this is not good enough, so we have to leave the keychain itself around. */
1034 if (!ans && keych) {
1035 del_temp_keych(keych);
1036 keych = NULL;
1038 if (!ans && (!rawkey || (!ispem && !key))) {
1039 /* Try again with the default keychain list, but only if a key was not
1040 * provided or was provided in non-PEM and we failed to import it. */
1041 err = cSecIdentityCreateWithCertificate(NULL, cert, &ans);
1043 if (ans)
1044 *kh = keych;
1045 else
1046 del_temp_keych(keych);
1047 return ans;
1050 void DisposeIdentityKeychainHandle(void *ch)
1052 del_temp_keych((tempch_t *)ch);
1055 CFArrayRef CreateClientAuthWithCertificatesAndKeyData(CFArrayRef certs,
1056 CFDataRef keydata, CFTypeRef pw,
1057 CFStringRef hint, void **kh)
1059 CFMutableArrayRef ans;
1060 size_t count, i;
1061 SecCertificateRef cert;
1062 SecIdentityRef identity;
1064 if (!certs || !keydata) return NULL;
1065 count = (size_t)CFArrayGetCount(certs);
1066 if (count < 1) return NULL;
1067 cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, 0);
1068 if (CFGetTypeID(cert) != SecCertificateGetTypeID()) return NULL;
1069 ans = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks);
1070 if (!ans) return NULL;
1071 identity = cSecIdentityCreateWithCertificateAndKeyData(cert, keydata, pw,
1072 hint, kh);
1073 if (!identity) {
1074 CFRelease(ans);
1075 return NULL;
1077 CFArrayAppendValue(ans, identity);
1078 for (i = 1; i < count; ++i) {
1079 CFArrayAppendValue(ans, CFArrayGetValueAtIndex(certs, i));
1081 return ans;
1084 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
1086 #ifndef kCFCoreFoundationVersionNumber10_8
1087 #define kCFCoreFoundationVersionNumber10_8 744.00
1088 #endif
1089 #ifndef kCFCoreFoundationVersionNumber10_8_3
1090 #define kCFCoreFoundationVersionNumber10_8_3 744.18
1091 #endif
1093 typedef enum {
1094 small_0,
1095 small_1,
1096 small_2,
1097 small_3,
1098 small_4,
1099 small_5
1100 } SmallEnum;
1102 typedef struct {
1103 size_t Length;
1104 uint8_t *Data;
1105 } cCSSM_DATA;
1107 static struct {
1108 OSStatus (*fSSLSetTrustedRoots)(SSLContextRef, CFArrayRef, Boolean);
1109 OSStatus (*fSSLGetPeerSecTrust)(SSLContextRef, SecTrustRef *);
1110 OSStatus (*fSSLCopyPeerTrust)(SSLContextRef, SecTrustRef *);
1111 OSStatus (*fSecTrustGetResult)(SecTrustRef, SecTrustResultType *,
1112 CFArrayRef *, CSSM_TP_APPLE_EVIDENCE_INFO **);
1113 OSStatus (*fSecTrustSetAnchorCertificatesOnly)(SecTrustRef, Boolean);
1114 OSStatus (*fSSLGetPeerCertificates)(SSLContextRef cxt, CFArrayRef *certs);
1115 OSStatus (*fSSLCopyPeerCertificates)(SSLContextRef cxt, CFArrayRef *certs);
1116 OSStatus (*fSSLSetProtocolVersionEnabled)(SSLContextRef cxt, SmallEnum, Boolean);
1117 OSStatus (*fSSLSetProtocolVersionMin)(SSLContextRef cxt, SmallEnum);
1118 OSStatus (*fSSLSetProtocolVersionMax)(SSLContextRef cxt, SmallEnum);
1119 OSStatus (*fSSLSetSessionOption)(SSLContextRef, SmallEnum, Boolean);
1120 SecCertificateRef (*fSecCertificateCreateWithData)(CFAllocatorRef, CFDataRef);
1121 OSStatus (*fSecCertificateCreateFromData)(const cCSSM_DATA *, CSSM_CERT_TYPE,
1122 CSSM_CERT_ENCODING, SecCertificateRef *);
1123 OSStatus (*fSecCertificateGetData)(SecCertificateRef, cCSSM_DATA *);
1124 CFDataRef (*fSecCertificateCopyData)(SecCertificateRef);
1125 OSStatus (*fSecKeychainItemImport)(
1126 CFDataRef,CFStringRef,SecExternalFormat *,SecExternalItemType *,
1127 SecItemImportExportFlags,const SecKeyImportExportParameters *,
1128 SecKeychainRef,CFArrayRef *);
1129 OSStatus (*fSecItemImport)(
1130 CFDataRef,CFStringRef,SecExternalFormat *,SecExternalItemType *,
1131 SecItemImportExportFlags,const SecItemImportExportKeyParameters *,
1132 SecKeychainRef,CFArrayRef *);
1133 OSStatus (*fSecIdentityCreateWithCertificate)(CFTypeRef,SecCertificateRef,SecIdentityRef *);
1134 OSStatus (*fSSLNewContext)(Boolean,SSLContextRef *);
1135 OSStatus (*fSSLDisposeContext)(SSLContextRef);
1136 SSLContextRef (*fSSLCreateContext)(CFAllocatorRef,SmallEnum,SmallEnum);
1137 OSStatus (*fSecKeychainSearchCreateFromAttributes)(CFTypeRef,int,
1138 const SecKeychainAttributeList *,SecKeychainSearchRef *);
1139 OSStatus (*fSecKeychainSearchCopyNext)(SecKeychainSearchRef,SecKeychainItemRef *);
1140 } fnc;
1142 static void stcompat_initialize(void)
1144 #define LOOKUP(name) *((void **)&fnc.f##name) = dlsym(RTLD_NEXT, #name)
1145 LOOKUP(SSLSetTrustedRoots);
1146 LOOKUP(SSLGetPeerSecTrust);
1147 LOOKUP(SSLCopyPeerTrust);
1148 LOOKUP(SecTrustGetResult);
1149 LOOKUP(SecTrustSetAnchorCertificatesOnly);
1150 LOOKUP(SSLGetPeerCertificates);
1151 LOOKUP(SSLCopyPeerCertificates);
1152 LOOKUP(SSLSetProtocolVersionEnabled);
1153 LOOKUP(SSLSetProtocolVersionMin);
1154 LOOKUP(SSLSetProtocolVersionMax);
1155 LOOKUP(SSLSetSessionOption);
1156 LOOKUP(SecCertificateCreateWithData);
1157 LOOKUP(SecCertificateCreateFromData);
1158 LOOKUP(SecCertificateGetData);
1159 LOOKUP(SecCertificateCopyData);
1160 LOOKUP(SecKeychainItemImport);
1161 LOOKUP(SecItemImport);
1162 LOOKUP(SecIdentityCreateWithCertificate);
1163 LOOKUP(SSLNewContext);
1164 LOOKUP(SSLDisposeContext);
1165 LOOKUP(SSLCreateContext);
1166 LOOKUP(SecKeychainSearchCreateFromAttributes);
1167 LOOKUP(SecKeychainSearchCopyNext);
1168 #undef LOOKUP
1171 static int search1(const void *p1, const void *p2)
1173 const struct cipher_order *a = (struct cipher_order *)p1;
1174 const struct cipher_order *b = (struct cipher_order *)p2;
1175 return (int)a->cipher - (int)b->cipher;
1178 static const struct cipher_order *find_entry(SSLCipherSuite c)
1180 struct cipher_order s;
1181 s.cipher = c;
1182 return (const struct cipher_order *)
1183 bsearch(&s, order_table, sizeof(order_table)/sizeof(order_table[0]),
1184 sizeof(struct cipher_order), search1);
1187 static int sort1(const void *p1, const void *p2)
1189 const struct cipher_order *a = (struct cipher_order *)p1;
1190 const struct cipher_order *b = (struct cipher_order *)p2;
1191 int ordering = a->tier - b->tier;
1192 if (!ordering)
1193 ordering = a->order - b->order;
1194 return ordering;
1197 size_t cSSLSortCiphers(SSLCipherSuite *a, size_t n)
1199 struct cipher_order *sortmem;
1200 size_t i;
1201 int notbad;
1203 if (!a || !n)
1204 return 0;
1205 sortmem = (struct cipher_order *)malloc(n * sizeof(struct cipher_order));
1206 if (!sortmem)
1207 return n;
1208 for (i=0; i<n; ++i) {
1209 const struct cipher_order *entry = find_entry(a[i]);
1210 sortmem[i].cipher = a[i];
1211 if (a[i] >= TLS_ECDH_ECDSA_WITH_NULL_SHA &&
1212 a[i] <= TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 &&
1213 kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber10_8 &&
1214 kCFCoreFoundationVersionNumber <= kCFCoreFoundationVersionNumber10_8_3)
1215 entry = NULL;
1216 if (entry) {
1217 sortmem[i].tier = entry->tier;
1218 sortmem[i].order = entry->order;
1219 if (sortmem[i].order < 0)
1220 sortmem[i].order = (int)i;
1221 } else {
1222 sortmem[i].tier=9999;
1223 sortmem[i].order = (int)i;
1226 qsort(sortmem, n, sizeof(struct cipher_order), sort1);
1227 notbad = -1;
1228 for (i=0; i<n; ++i) {
1229 if (notbad < 0 && sortmem[i].tier == 9999)
1230 notbad = (int)i;
1231 a[i] = sortmem[i].cipher;
1233 free(sortmem);
1234 return notbad >= 0 ? (size_t)notbad : n;
1237 OSStatus cSSLSetTrustedRoots(SSLContextRef cxt, CFArrayRef rts, Boolean replace)
1239 if (fnc.fSSLSetTrustedRoots)
1240 return fnc.fSSLSetTrustedRoots(cxt, rts, replace);
1241 return unimpErr;
1244 OSStatus cSSLCopyPeerTrust(SSLContextRef cxt, SecTrustRef *trust)
1246 if (fnc.fSSLCopyPeerTrust)
1247 return fnc.fSSLCopyPeerTrust(cxt, trust);
1248 if (fnc.fSSLGetPeerSecTrust) {
1249 OSStatus err = fnc.fSSLGetPeerSecTrust(cxt, trust);
1250 if (!err && *trust)
1251 CFRetain(*trust);
1252 return err;
1254 return unimpErr;
1257 OSStatus cSecTrustGetResult(SecTrustRef trust, SecTrustResultType *result,
1258 CFArrayRef *certChain, CSSM_TP_APPLE_EVIDENCE_INFO **statusChain)
1260 if (fnc.fSecTrustGetResult)
1261 return fnc.fSecTrustGetResult(trust, result, certChain, statusChain);
1262 return unimpErr;
1265 OSStatus cSSLCopyPeerCertificates(SSLContextRef cxt, CFArrayRef *certs)
1267 if (!certs || !cxt) return paramErr;
1268 *certs = NULL;
1269 if (fnc.fSSLCopyPeerCertificates)
1270 return fnc.fSSLCopyPeerCertificates(cxt, certs);
1271 if (fnc.fSSLGetPeerCertificates) {
1272 OSStatus err = fnc.fSSLGetPeerCertificates(cxt, certs);
1273 if (!err && *certs) {
1274 size_t i, c = (size_t)CFArrayGetCount(*certs);
1275 for (i = 0; i < c; ++i) {
1276 CFTypeRef item = (CFTypeRef)CFArrayGetValueAtIndex(*certs, i);
1277 if (item) CFRelease(item);
1280 return err;
1282 return unimpErr;
1285 OSStatus cSecTrustSetAnchorCertificatesOnly(SecTrustRef cxt, Boolean anchorsOnly)
1287 if (fnc.fSecTrustSetAnchorCertificatesOnly)
1288 return fnc.fSecTrustSetAnchorCertificatesOnly(cxt, anchorsOnly);
1289 return unimpErr;
1292 OSStatus cSSLSetProtocolVersionMinMax(SSLContextRef cxt, int minVer, int maxVer)
1294 OSStatus err;
1296 if (minVer < 0 || maxVer < 0 || minVer > 8 || maxVer > 8 || minVer > maxVer)
1297 return paramErr;
1299 if (minVer == kSSLProtocolUnknown) minVer = kSSLProtocol3;
1300 if (minVer == kSSLProtocolAll) minVer = kSSLProtocol3;
1301 if (minVer == kSSLProtocol3Only) minVer = kSSLProtocol3;
1302 if (minVer == kTLSProtocol1Only) minVer = kTLSProtocol1;
1304 if (maxVer == kSSLProtocol3Only) maxVer = kSSLProtocol3;
1305 if (maxVer == kTLSProtocol1Only) maxVer = kTLSProtocol1;
1306 if (maxVer == kSSLProtocolAll) maxVer = kTLSProtocol12;
1307 if (maxVer == kSSLProtocolUnknown) maxVer = kTLSProtocol12;
1309 if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_8 &&
1310 minVer <= kTLSProtocol1 && maxVer > kTLSProtocol1)
1311 maxVer = kTLSProtocol1;
1313 if (fnc.fSSLSetProtocolVersionMin && fnc.fSSLSetProtocolVersionMax) {
1314 err = fnc.fSSLSetProtocolVersionMin(cxt, minVer);
1315 if (!err)
1316 err = fnc.fSSLSetProtocolVersionMax(cxt, maxVer);
1317 return err;
1319 if (fnc.fSSLSetProtocolVersionEnabled) {
1320 #define ENABLEPROTO(x) fnc.fSSLSetProtocolVersionEnabled(cxt, (int)(x), \
1321 minVer <= x && x <= maxVer)
1322 err = ENABLEPROTO(kSSLProtocol2);
1323 if (err && minVer > kSSLProtocol2) err = noErr; /* ignore SSL2 disable error */
1324 if (!err) ENABLEPROTO(kSSLProtocol3);
1325 if (!err) ENABLEPROTO(kTLSProtocol1);
1326 if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber10_8 ||
1327 maxVer > kTLSProtocol1) {
1328 if (!err) ENABLEPROTO(kTLSProtocol11);
1329 if (!err) ENABLEPROTO(kTLSProtocol12);
1331 #undef ENABLEPROTO
1332 return err;
1334 return unimpErr;
1337 OSStatus cSSLSetSessionOption(SSLContextRef cxt, int option, Boolean value)
1339 if (fnc.fSSLSetSessionOption)
1340 return fnc.fSSLSetSessionOption(cxt, option, value);
1341 else
1342 return unimpErr;
1345 SecCertificateRef cSecCertificateCreateWithData(CFAllocatorRef a, CFDataRef d)
1347 if (fnc.fSecCertificateCreateWithData)
1348 return fnc.fSecCertificateCreateWithData(a, d);
1349 else if (fnc.fSecCertificateCreateFromData) {
1350 cCSSM_DATA certdata;
1351 OSStatus err;
1352 SecCertificateRef cacert = NULL;
1353 if (!d) return NULL;
1354 certdata.Length = (size_t)CFDataGetLength(d);
1355 certdata.Data = (uint8 *)CFDataGetBytePtr(d);
1356 err = fnc.fSecCertificateCreateFromData(&certdata, CSSM_CERT_X_509v3,
1357 CSSM_CERT_ENCODING_DER, &cacert);
1358 if (err)
1359 cacert = NULL;
1360 return cacert;
1361 } else
1362 return NULL;
1365 CFDataRef cSecCertificateCopyData(SecCertificateRef c)
1367 if (!c) return NULL;
1368 if (CFGetTypeID(c) != SecCertificateGetTypeID()) return NULL;
1369 if (fnc.fSecCertificateCopyData)
1370 return fnc.fSecCertificateCopyData(c);
1371 if (fnc.fSecCertificateGetData) {
1372 cCSSM_DATA certdata;
1373 OSStatus err = fnc.fSecCertificateGetData(c, &certdata);
1374 if (err || !certdata.Data || !certdata.Length) return NULL;
1375 return CFDataCreate(kCFAllocatorDefault, certdata.Data, certdata.Length);
1377 return NULL;
1380 Boolean BlobsEqual(CFDataRef d1, CFDataRef d2)
1382 size_t l1, l2;
1383 Boolean ans = false;
1385 if (!d1 || !d2)
1386 return false;
1387 l1 = CFDataGetLength(d1);
1388 l2 = CFDataGetLength(d2);
1389 if (l1 == l2) {
1390 const void *p1 = (void *)CFDataGetBytePtr(d1);
1391 const void *p2 = (void *)CFDataGetBytePtr(d2);
1392 ans = memcmp(p1, p2, l1) == 0;
1394 return ans;
1397 Boolean SecCertsEqual(SecCertificateRef c1, SecCertificateRef c2)
1399 CFDataRef d1, d2;
1400 Boolean ans = false;
1401 d1 = cSecCertificateCopyData(c1);
1402 if (!d1) return false;
1403 d2 = cSecCertificateCopyData(c2);
1404 if (!d2) {
1405 CFRelease(d1);
1406 return false;
1408 ans = BlobsEqual(d1, d2);
1409 CFRelease(d1);
1410 CFRelease(d2);
1411 return ans;
1414 Boolean BlobInArray(CFDataRef d, CFArrayRef a)
1416 size_t i, cnt;
1417 if (!d || !a || !CFArrayGetCount(a)) return false;
1418 cnt = CFArrayGetCount(a);
1419 for (i = 0; i < cnt; ++i) {
1420 if (BlobsEqual(d, (CFDataRef)CFArrayGetValueAtIndex(a, i)))
1421 return true;
1423 return false;
1426 Boolean SecCertInArray(SecCertificateRef c, CFArrayRef a)
1428 size_t i, cnt;
1429 if (!c || !a || !CFArrayGetCount(a)) return false;
1430 cnt = CFArrayGetCount(a);
1431 for (i = 0; i < cnt; ++i) {
1432 if (SecCertsEqual(c, (SecCertificateRef)CFArrayGetValueAtIndex(a, i)))
1433 return true;
1435 return false;
1438 OSStatus CopyIdentityWithLabel(const char *label, SecIdentityRef *out)
1440 if (fnc.fSecKeychainSearchCreateFromAttributes &&
1441 fnc.fSecKeychainSearchCopyNext) {
1442 SecIdentityRef ans = NULL;
1443 SecCertificateRef cert = NULL;
1444 SecKeychainAttribute at;
1445 SecKeychainAttributeList al;
1446 SecKeychainSearchRef sr;
1447 OSStatus err;
1449 at.tag = kSecLabelItemAttr;
1450 at.length = strlen(label);
1451 at.data = (char *)label;
1452 al.count = 1;
1453 al.attr = &at;
1454 err = fnc.fSecKeychainSearchCreateFromAttributes(NULL,
1455 kSecCertificateItemClass, &al, &sr);
1456 *out = NULL;
1457 if (err || !sr) return err;
1458 while ((err = fnc.fSecKeychainSearchCopyNext(sr, (SecKeychainItemRef *)&cert)) == noErr) {
1459 if (!cert) continue;
1460 if (CFGetTypeID(cert) != SecCertificateGetTypeID()) {CFRelease(cert); continue;}
1461 err = cSecIdentityCreateWithCertificate(NULL, cert, &ans);
1462 CFRelease(cert);
1463 if (!err && ans) break;
1465 CFRelease(sr);
1466 if (ans) {
1467 *out = ans;
1468 return noErr;
1470 return errSecItemNotFound;
1472 return unimpErr;
1475 OSStatus cSecItemImport(
1476 CFDataRef importedData, CFStringRef fileNameOrExtension,
1477 SecExternalFormat *inputFormat, SecExternalItemType *itemType,
1478 SecItemImportExportFlags flags, const SecItemImportExportKeyParameters *keyParams,
1479 SecKeychainRef importKeychain, CFArrayRef *outItems)
1481 if (fnc.fSecItemImport)
1482 return fnc.fSecItemImport(importedData, fileNameOrExtension, inputFormat,
1483 itemType, flags, keyParams, importKeychain, outItems);
1484 else if (fnc.fSecKeychainItemImport) {
1485 SecKeyImportExportParameters oldKeyParams;
1486 SecKeyImportExportParameters *op = NULL;
1487 if (keyParams) {
1488 op = &oldKeyParams;
1489 memset(&oldKeyParams, 0, sizeof(oldKeyParams));
1490 oldKeyParams.version = keyParams->version;
1491 oldKeyParams.flags = keyParams->flags;
1492 oldKeyParams.passphrase = keyParams->passphrase;
1493 oldKeyParams.alertTitle = keyParams->alertTitle;
1494 oldKeyParams.alertPrompt = keyParams->alertPrompt;
1495 oldKeyParams.accessRef = keyParams->accessRef;
1496 /* We punt on keyUsage and keyAttributes and do not convert them */
1498 return fnc.fSecKeychainItemImport(importedData, fileNameOrExtension, inputFormat,
1499 itemType, flags, op, importKeychain, outItems);
1501 return unimpErr;
1504 OSStatus cSecIdentityCreateWithCertificate(CFTypeRef k, SecCertificateRef c,
1505 SecIdentityRef *i)
1507 /* The documentation lies and this is actually present in later 10.4 versions */
1508 if (fnc.fSecIdentityCreateWithCertificate)
1509 return fnc.fSecIdentityCreateWithCertificate(k, c, i);
1510 return unimpErr;
1513 SSLContextRef cSSLCreateContext(CFAllocatorRef a, int ps, int ct)
1515 if (fnc.fSSLCreateContext)
1516 return fnc.fSSLCreateContext(a, ps, ct);
1517 if ((ps != kSSLServerSide && ps != kSSLClientSide) || (ct != kSSLStreamType))
1518 return NULL;
1519 if (fnc.fSSLNewContext && fnc.fSSLDisposeContext) {
1520 SSLContextRef cxt;
1521 OSStatus err = fnc.fSSLNewContext(ps == kSSLServerSide, &cxt);
1522 return err ? NULL : cxt;
1524 return NULL;
1527 void cSSLDisposeContext(SSLContextRef c)
1529 if (fnc.fSSLCreateContext)
1530 CFRelease(c);
1531 else if (fnc.fSSLDisposeContext)
1532 fnc.fSSLDisposeContext(c);
1535 CF_INLINE bool is_ldh(int c)
1537 return
1538 ('A' <= c && c <= 'Z') ||
1539 ('a' <= c && c <= 'z') ||
1540 ('0' <= c && c <= '9') ||
1541 c == '-';
1544 CF_INLINE size_t get_label_len(const char *p, size_t l)
1546 size_t ans = 0;
1547 while (l-- && is_ldh(*p++)) ++ans;
1548 return ans;
1551 static bool is_dns_name(const void *_p, size_t l, bool wcok)
1553 const char *p = (char *)_p;
1554 size_t idx = 0;
1556 if (!p) return false;
1557 if (l >= 1 && p[l-1] == '.') --l;
1558 if (!l) return false;
1559 if (l > 255) return false;
1560 do {
1561 size_t lablen = get_label_len(p, l);
1562 if (lablen > 63) return false;
1563 if (!idx && !lablen && wcok && l >= 2 && p[0] == '*' && p[1] == '.') lablen=1;
1564 if (!lablen) return false;
1565 if (p[0] == '-' || p[lablen - 1] == '-') return false;
1566 if (lablen < l) {
1567 if (p[lablen] != '.') return false;
1568 ++lablen;
1570 l -= lablen;
1571 p += lablen;
1572 ++idx;
1573 } while (l);
1574 return true;
1577 CF_INLINE char clc(char c)
1579 return 'A' <= c && c <= 'Z' ? c - 'A' + 'a' : c;
1582 CF_INLINE bool matchicase(const char *p1, const char *p2, size_t l)
1584 while (l--) {
1585 if (clc(*p1++) != clc(*p2++)) return false;
1587 return true;
1590 static bool peername_matches_id(const char *peername, CFDataRef idrawname)
1592 size_t pl, il, idx;
1593 const char *idname;
1594 if (!peername || !idrawname) return false;
1595 idname = (const char *)CFDataGetBytePtr(idrawname);
1596 if (!idname) return false;
1597 pl = strlen(peername);
1598 il = (size_t)CFDataGetLength(idrawname);
1599 if (!is_dns_name(peername, pl, false) || !is_dns_name(idname, il, true))
1600 return false;
1601 idx = 0;
1602 if (peername[pl - 1] == '.') --pl;
1603 if (idname[il - 1] == '.') --il;
1604 if (pl > 255 || il > 255)
1605 return false;
1606 while (pl && il) {
1607 size_t pll = get_label_len(peername, pl);
1608 size_t ill = get_label_len(idname, il);
1609 if (!idx && !ill && il >= 2 && idname[0] == '*' && idname[1] == '.') ill=1;
1610 if (pll < pl) {
1611 if (peername[pll] != '.') return false;
1612 ++pll;
1614 if (ill < il) {
1615 if (idname[ill] != '.') return false;
1616 ++ill;
1618 if (idx || idname[0] != '*') {
1619 if (pll != ill) return false;
1620 if (!matchicase(peername, idname, pll)) return false;
1622 peername += pll;
1623 pl -= pll;
1624 idname += ill;
1625 il -= ill;
1626 ++idx;
1628 return !pl && !il;
1631 CF_INLINE size_t get_num_len(const char *p, size_t l)
1633 size_t ans = 0;
1634 while (l-- && '0' <= *p && *p <= '9') {
1635 ++ans;
1636 ++p;
1638 return ans;
1641 Boolean IsIPv4Name(const void *_p, size_t l)
1643 const char *p = (char *)_p;
1644 size_t idx = 0;
1646 if (!p || l < 7) return false;
1647 do {
1648 size_t lablen;
1649 if (++idx > 4) return false;
1650 lablen = get_num_len(p, l);
1651 if (lablen > 3) return false;
1652 if (lablen >= 2 && *p == '0') return false;
1653 else if (lablen == 3) {
1654 if (*p >= '3') return false;
1655 if (*p == '2') {
1656 if (p[1] >= '6') return false;
1657 if (p[1] == '5' && p[2] > '5') return false;
1660 if (lablen < l) {
1661 if (p[lablen] != '.') return false;
1662 ++lablen;
1664 l -= lablen;
1665 p += lablen;
1666 } while (l);
1667 return idx == 4;
1670 static bool parse_ipv4_name(const void *_p, size_t l, uint8_t ipv4[4])
1672 unsigned short s[4];
1673 char ipv4str[16];
1674 const char *p = (char *)_p;
1675 if (!IsIPv4Name(p, l) || l > 15) return false;
1676 memcpy(ipv4str, p, l);
1677 ipv4str[l] = 0;
1678 if (sscanf(ipv4str, "%hu.%hu.%hu.%hu", s, s+1, s+2, s+3) == 4) {
1679 ipv4[0] = (uint8_t)s[0];
1680 ipv4[1] = (uint8_t)s[1];
1681 ipv4[2] = (uint8_t)s[2];
1682 ipv4[3] = (uint8_t)s[3];
1683 return true;
1685 return false;
1688 static bool parse_ipv6_name(const void *_p, size_t l, uint8_t ipv6[16])
1690 char ipv6str[INET6_ADDRSTRLEN];
1691 const char *p = (char *)_p;
1692 const char *pct;
1693 if (!p) return false;
1694 if (l >= 1 && p[0] == '[') {
1695 ++p;
1696 --l;
1698 if (l >= 1 && p[l-1] == ']') --l;
1699 pct = (char *)(l ? memchr(p, '%', l) : NULL);
1700 if (pct) l = pct - p;
1701 if (l < 3 || l >= INET6_ADDRSTRLEN) return false;
1702 memcpy(ipv6str, p, l);
1703 ipv6str[l] = 0;
1704 return inet_pton(AF_INET6, ipv6str, ipv6) == 1;
1707 #define U(x) ((const uint8_t *)(x))
1708 static const data_t OID_BasicConstraints = {U("\006\003\125\035\023"), 5};
1709 static const data_t OID_SubjectAltName = {U("\006\003\125\035\021"), 5};
1710 static const data_t OID_SubjectKeyIdentifier = {U("\006\003\125\035\016"), 5};
1711 static const data_t OID_AuthorityKeyIdentifier = {U("\006\003\125\035\043"), 5};
1712 static const data_t OID_CommonName = {U("\006\003\125\004\003"), 5};
1713 #undef U
1715 typedef struct der_atom_s {
1716 uint8_t clas; /* 0, 1, 2, or 3 */
1717 uint8_t cons; /* 0 or 1 */
1718 uint8_t rawtag; /* raw value of first byte of tag */
1719 uint32_t tag; /* tag value */
1720 size_t hl; /* length of header excluding actual data */
1721 size_t dl; /* length of actual data */
1722 } der_atom_t;
1724 typedef struct der_cert_s {
1725 uint8_t vers; /* 0 => v1, 1 => v2, 2 => v3 */
1726 uint8_t caFlag; /* 0 unless basic constraints present then 0x80=critial 0x01=value */
1727 uint8_t isCA; /* true if caFlag==0x81 or subject==issuer && vers < 2 */
1728 uint8_t isRoot; /* true if isCA and subject == issuer */
1729 char notBefore[16]; /* Not before date either 13 chars or 15 chars plus Nul */
1730 char notAfter[16]; /* Not after date see notBefore for format */
1731 data_t subject; /* points to sequence */
1732 data_t subjectPubKey; /* points to subjectPublicKeyInfo sequence */
1733 data_t subjectAltNames; /* null unless v3 extension present, points to sequence */
1734 data_t subjectKeyId; /* null unless v3 extension present, points to raw bytes */
1735 data_t issuer; /* points to sequence */
1736 data_t issuerKeyId; /* null unless v3 extension present, points to raw bytes */
1737 } der_cert_t;
1739 static bool read_der_atom(const data_t *d, der_atom_t *o)
1741 uint8_t byte;
1742 uint32_t tag;
1743 size_t pos, len;
1744 if (!d || !d->d || !d->l || !o) return false;
1745 o->clas = (*d->d >> 6) & 0x3;
1746 o->cons = (*d->d >> 5) & 0x1;
1747 o->rawtag = *d->d;
1748 tag = *d->d & 0x1f;
1749 pos = 1;
1750 if (tag == 0x1f) {
1751 tag = 0;
1752 do {
1753 if (pos >= d->l) return false;
1754 tag <<= 7;
1755 byte = d->d[pos++];
1756 tag |= byte & 0x7f;
1757 } while (byte & 0x80);
1759 o->tag = tag;
1760 if (pos >= d->l) return false;
1761 byte = d->d[pos++];
1762 if (byte & 0x80) {
1763 unsigned cnt = byte & 0x7f;
1764 if (!cnt || pos + cnt > d->l) return false;
1765 len = 0;
1766 do {
1767 len <<= 8;
1768 len |= d->d[pos++];
1769 } while (--cnt);
1770 } else {
1771 len = byte;
1773 if (pos + len > d->l) return false;
1774 o->hl = pos;
1775 o->dl = len;
1776 return true;
1779 /* return true if _d->d points at a valid DER atom that is _d->l bytes long
1780 * or less. If exact_length_match_only is set the DER atom MUST be exactly
1781 * _d->l bytes long. If the atom at _d->d is a set or sequence, then its
1782 * elements are also examined recursively to make sure they are also valid. */
1783 static bool is_der(const data_t *_d, bool exact_length_match_only)
1785 bool first = true;
1786 data_t d;
1787 if (!_d || !_d->d || !_d->l) return false;
1788 d.d = _d->d;
1789 d.l = _d->l;
1790 do {
1791 der_atom_t atom;
1792 if (!read_der_atom(&d, &atom)) return false;
1793 d.l -= atom.hl;
1794 d.d += atom.hl;
1795 if ((atom.rawtag & 0xfe) != 0x30) {
1796 d.l -= atom.dl;
1797 d.d += atom.dl;
1798 if (first) break;
1799 } else if (first) {
1800 d.l = atom.dl;
1802 first = false;
1803 } while (d.l);
1804 return !d.l && (d.d == _d->d + _d->l || !exact_length_match_only);
1807 /* true is returned if data is not NULL and matches:
1808 * SEQUENCE {
1809 * SEQUENCE {
1810 * OBJECT ID,
1811 * optional...
1812 * },
1813 * BIT STRING {
1814 * whatever...
1815 * },
1816 * } == length of data
1818 static bool check_der_pubkey(const data_t *_d)
1820 data_t d;
1821 der_atom_t atom;
1823 if (!_d || !_d->d || !_d->l) return false;
1824 if (!is_der(_d, true)) return false;
1825 d.d = _d->d;
1826 d.l = _d->l;
1827 if (!read_der_atom(&d, &atom)) return false;
1828 if (atom.rawtag != 0x30) return false;
1829 d.l = atom.dl;
1830 d.d += atom.hl;
1831 if (!read_der_atom(&d, &atom)) return false;
1832 if (atom.rawtag != 0x30) return false;
1833 if (!atom.dl || d.d[atom.hl] != 0x06) return false;
1834 d.l -= atom.hl + atom.dl;
1835 d.d += atom.hl + atom.dl;
1836 if (!read_der_atom(&d, &atom)) return false;
1837 if (atom.rawtag != 0x03) return false;
1838 return true;
1841 static int data_matches(const data_t *o1, const data_t *o2)
1843 if (!o1 || !o2 || !o1->l || !o2->l || o1->l != o2->l)
1844 return 0;
1845 return memcmp(o1->d, o2->d, o1->l) == 0;
1848 static bool read_der_cert(const data_t *_d, der_cert_t *o)
1850 data_t d;
1851 der_atom_t atom;
1853 if (!_d || !_d->d || !_d->l || !o) return false;
1854 if (!is_der(_d, true)) return false;
1855 d.d = _d->d;
1856 d.l = _d->l;
1857 memset(o, 0, sizeof(*o));
1858 if (!read_der_atom(&d, &atom)) return false;
1859 if (atom.rawtag != 0x30) return false;
1860 d.l = atom.dl;
1861 d.d += atom.hl;
1862 if (!read_der_atom(&d, &atom)) return false;
1863 if (atom.rawtag != 0x30) return false;
1864 d.l = atom.dl;
1865 d.d += atom.hl;
1866 if (!read_der_atom(&d, &atom)) return false;
1867 if (atom.rawtag == 0xA0) {
1868 d.l -= atom.hl;
1869 d.d += atom.hl;
1870 if (atom.dl != 3 || d.d[0] != 2 || d.d[1] != 1) return false;
1871 o->vers = d.d[2]; /* not validated */
1872 d.l -= atom.dl;
1873 d.d += atom.dl;
1874 if (!read_der_atom(&d, &atom)) return false;
1875 } else {
1876 o->vers = 0; /* implied v1 */
1878 if (atom.rawtag != 2) return false;
1879 /* skip serialNumber */
1880 d.l -= atom.hl + atom.dl;
1881 d.d += atom.hl + atom.dl;
1882 if (!read_der_atom(&d, &atom)) return false;
1883 if (atom.rawtag != 0x30) return false;
1884 /* skip signature */
1885 d.l -= atom.hl + atom.dl;
1886 d.d += atom.hl + atom.dl;
1887 if (!read_der_atom(&d, &atom)) return false;
1888 if (atom.rawtag != 0x30) return false;
1889 o->issuer.d = d.d;
1890 o->issuer.l = atom.hl + atom.dl;
1891 d.l -= atom.hl + atom.dl;
1892 d.d += atom.hl + atom.dl;
1893 if (!read_der_atom(&d, &atom)) return false;
1894 if (atom.rawtag != 0x30) return false;
1896 /* parse validity */
1897 data_t vdate;
1898 vdate.d = d.d + atom.hl;
1899 vdate.l = atom.dl;
1900 d.l -= atom.hl + atom.dl;
1901 d.d += atom.hl + atom.dl;
1902 if (!read_der_atom(&vdate, &atom)) return false;
1903 if (atom.rawtag != 0x17 && atom.rawtag != 0x18) return false;
1904 if (atom.rawtag == 0x17 && atom.dl != 13) return false;
1905 if (atom.rawtag == 0x18 && atom.dl != 15) return false;
1906 memcpy(o->notBefore, vdate.d + atom.hl, atom.dl);
1907 vdate.l += atom.hl + atom.dl;
1908 vdate.d += atom.hl + atom.dl;
1909 if (!read_der_atom(&vdate, &atom)) return false;
1910 if (atom.rawtag != 0x17 && atom.rawtag != 0x18) return false;
1911 if (atom.rawtag == 0x17 && atom.dl != 13) return false;
1912 if (atom.rawtag == 0x18 && atom.dl != 15) return false;
1913 memcpy(o->notAfter, vdate.d + atom.hl, atom.dl);
1914 if (vdate.d + atom.hl + atom.dl != d.d) return false;
1916 if (!read_der_atom(&d, &atom)) return false;
1917 if (atom.rawtag != 0x30) return false;
1918 o->subject.d = d.d;
1919 o->subject.l = atom.hl + atom.dl;
1920 d.l -= atom.hl + atom.dl;
1921 d.d += atom.hl + atom.dl;
1922 if (!read_der_atom(&d, &atom)) return false;
1923 if (atom.rawtag != 0x30) return false;
1924 o->subjectPubKey.d = d.d;
1925 o->subjectPubKey.l = atom.hl + atom.dl;
1926 if (!check_der_pubkey(&o->subjectPubKey)) return false;
1927 d.l -= atom.hl + atom.dl;
1928 d.d += atom.hl + atom.dl;
1929 do {
1930 if (o->vers != 2 || !d.l) break;
1931 if (!read_der_atom(&d, &atom)) return false;
1932 if (atom.rawtag == 0x81) {
1933 /* skip issuerUniqueID */
1934 d.l -= atom.hl + atom.dl;
1935 d.d += atom.hl + atom.dl;
1936 if (!d.l) break;
1937 if (!read_der_atom(&d, &atom)) return false;
1939 if (atom.rawtag == 0x82) {
1940 /* skip subjectUniqueID */
1941 d.l -= atom.hl + atom.dl;
1942 d.d += atom.hl + atom.dl;
1943 if (!d.l) break;
1944 if (!read_der_atom(&d, &atom)) return false;
1946 if (atom.rawtag != 0xA3) return false;
1947 /* found v3 extensions */
1948 d.l = atom.dl;
1949 d.d += atom.hl;
1950 if (!read_der_atom(&d, &atom)) return false;
1951 if (atom.rawtag != 0x30) return false;
1952 d.l -= atom.hl;
1953 d.d += atom.hl;
1954 do {
1955 uint8_t crit = 0;
1956 data_t oid, value;
1957 if (!read_der_atom(&d, &atom)) return false;
1958 if (atom.rawtag != 0x30) return false;
1959 d.l -= atom.hl;
1960 d.d += atom.hl;
1961 if (!read_der_atom(&d, &atom)) return false;
1962 if (atom.rawtag != 6) return false;
1963 oid.d = d.d;
1964 oid.l = atom.hl + atom.dl;
1965 d.l -= atom.hl + atom.dl;
1966 d.d += atom.hl + atom.dl;
1967 if (!read_der_atom(&d, &atom)) return false;
1968 if (atom.rawtag == 1) {
1969 /* skip over boolean but record its value */
1970 if (atom.dl != 1) return false;
1971 crit = *(d.d + atom.hl);
1972 d.l -= atom.hl + atom.dl;
1973 d.d += atom.hl + atom.dl;
1974 if (!read_der_atom(&d, &atom)) return false;
1976 if (atom.rawtag != 4) return false;
1977 d.l -= atom.hl;
1978 d.d += atom.hl;
1979 value.d = d.d;
1980 value.l = atom.dl;
1981 d.l -= atom.dl;
1982 d.d += atom.dl;
1983 if (data_matches(&oid, &OID_BasicConstraints)) {
1984 if (!read_der_atom(&value, &atom)) return false;
1985 if (atom.rawtag != 0x30) return false;
1986 value.l = atom.dl;
1987 value.d += atom.hl;
1988 if (!value.l) {
1989 /* CA flag is false and was properly omitted */
1990 o->caFlag = crit ? 0x80 : 0;
1991 } else {
1992 if (!read_der_atom(&value, &atom)) return false;
1993 if (atom.rawtag == 1) {
1994 /* CA flag is present */
1995 if (atom.dl != 1) return false;
1996 o->caFlag = (crit ? 0x80 : 0) | (*(value.d + atom.hl) ? 0x1 : 0);
1999 } else if (data_matches(&oid, &OID_SubjectAltName)) {
2000 o->subjectAltNames.d = value.d;
2001 o->subjectAltNames.l = value.l;
2002 } else if (data_matches(&oid, &OID_SubjectKeyIdentifier)) {
2003 if (!read_der_atom(&value, &atom)) return false;
2004 if (atom.rawtag != 4) return false;
2005 o->subjectKeyId.d = value.d + atom.hl;
2006 o->subjectKeyId.l = atom.dl;
2007 } else if (data_matches(&oid, &OID_AuthorityKeyIdentifier)) {
2008 if (!read_der_atom(&value, &atom)) return false;
2009 if (atom.rawtag != 0x30) return false;
2010 value.l = atom.dl;
2011 value.d += atom.hl;
2012 if (!read_der_atom(&value, &atom)) return false;
2013 if (atom.rawtag == 0x80) {
2014 o->issuerKeyId.d = value.d + atom.hl;
2015 o->issuerKeyId.l = atom.dl;
2018 } while (d.l);
2019 } while (0);
2020 if (o->vers >= 2) {
2021 o->isCA = (o->caFlag|0x80) == 0x81; /* HACK: some old CAs aren't critical! */
2022 o->isRoot = (o->isCA && o->subject.l && o->subject.l == o->issuer.l &&
2023 memcmp(o->subject.d, o->issuer.d, o->subject.l) == 0) ? 1 : 0;
2024 } else {
2025 o->isCA = (o->subject.l && o->subject.l == o->issuer.l &&
2026 memcmp(o->subject.d, o->issuer.d, o->subject.l) == 0) ? 1 : 0;
2027 o->isRoot = o->isCA;
2029 return true;
2032 Boolean CheckCertOkay(SecCertificateRef _cert)
2034 CFDataRef d = cSecCertificateCopyData(_cert);
2035 data_t data;
2036 der_cert_t cert;
2037 Boolean ans;
2039 if (!d) return false;
2040 data.d = CFDataGetBytePtr(d);
2041 data.l = CFDataGetLength(d);
2042 ans = read_der_cert(&data, &cert);
2043 CFRelease(d);
2044 return ans;
2047 /* flags & 0x01 to extract pub keys from certificates */
2048 static Boolean CheckPubKeyOkayInt(CFDataRef d, data_t *pubkey, int flags)
2050 data_t data;
2052 if (!d || !pubkey) return false;
2053 data.d = CFDataGetBytePtr(d);
2054 data.l = CFDataGetLength(d);
2055 if (check_der_pubkey(&data)) {
2056 *pubkey = data;
2057 return true;
2059 if (flags & 0x01) {
2060 der_cert_t cert;
2061 if (read_der_cert(&data, &cert)) {
2062 *pubkey = cert.subjectPubKey;
2063 return true;
2066 return false;
2069 Boolean CheckPubKeyOkay(CFDataRef d)
2071 data_t data;
2072 return CheckPubKeyOkayInt(d, &data, 0);
2075 static void append_hex_dump(CFMutableStringRef s, const void *_d, size_t l)
2077 const unsigned char *d = (unsigned char *)_d;
2078 CFStringAppendCString(s, "<", kCFStringEncodingASCII);
2079 while (l--) {
2080 char byte[3];
2081 sprintf(byte, "%02X", *d++);
2082 CFStringAppendCString(s, byte, kCFStringEncodingASCII);
2084 CFStringAppendCString(s, ">", kCFStringEncodingASCII);
2087 static CFStringRef CopyCertKeyId(SecCertificateRef _cert, bool issuer)
2089 CFDataRef d = cSecCertificateCopyData(_cert);
2090 CFMutableStringRef ans = CFStringCreateMutable(kCFAllocatorDefault, 0);
2091 bool good = false;
2093 for (;;) {
2094 data_t data;
2095 const data_t *key;
2096 der_cert_t cert;
2097 size_t i;
2099 if (!d || !ans) break;
2100 data.d = CFDataGetBytePtr(d);
2101 data.l = CFDataGetLength(d);
2102 if (!read_der_cert(&data, &cert)) break;
2103 key = issuer ? &cert.issuerKeyId : &cert.subjectKeyId;
2104 if (!key->d || !key->l) break;
2105 for (i = 0; i < key->l; ++i) {
2106 char hexbyte[4];
2107 sprintf(hexbyte, "%02X%s", (unsigned)key->d[i], i+1 == key->l ? "" : ":");
2108 CFStringAppendCString(ans, hexbyte, kCFStringEncodingASCII);
2110 good = true;
2111 break;
2113 if (d) CFRelease(d);
2114 if (!good && ans) {CFRelease(ans); ans=NULL;}
2115 return ans;
2118 typedef struct oid_entry_s {
2119 size_t l;
2120 const char *oid;
2121 const char *name;
2122 } oid_entry_t;
2124 static const oid_entry_t oid_table[] = {
2125 {5, "\006\003\125\004\003", "CN"},
2126 {5, "\006\003\125\004\004", "SN"},
2127 {5, "\006\003\125\004\005", "serialNumber"},
2128 {5, "\006\003\125\004\006", "C"},
2129 {5, "\006\003\125\004\007", "L"},
2130 {5, "\006\003\125\004\010", "ST"},
2131 {5, "\006\003\125\004\011", "street"},
2132 {5, "\006\003\125\004\012", "O"},
2133 {5, "\006\003\125\004\013", "OU"},
2134 {5, "\006\003\125\004\014", "title"},
2135 {5, "\006\003\125\004\015", "description"},
2136 {5, "\006\003\125\004\017", "businessCategory"},
2137 {5, "\006\003\125\004\021", "postalCode"},
2138 {5, "\006\003\125\004\024", "telephoneNumber"},
2139 {5, "\006\003\125\004\027", "facsimileTelephoneNumber"},
2140 {5, "\006\003\125\004\052", "GN"},
2141 {5, "\006\003\125\004\053", "initials"},
2142 {5, "\006\003\125\004\054", "generationQualifier"},
2143 {5, "\006\003\125\004\056", "dnQualifier"},
2144 {5, "\006\003\125\004\101", "pseudonym"},
2145 {5, "\006\003\125\004\141", "organizationIdentifier"},
2146 {11, "\006\011\052\206\110\206\367\015\001\011\001", "emailAddress"},
2147 {12, "\006\012\011\222\046\211\223\362\054\144\001\001", "UID"},
2148 {12, "\006\012\011\222\046\211\223\362\054\144\001\031", "DC"},
2149 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\001", "jurisdictionOfIncorporationLocality"},
2150 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\002", "jurisdictionOfIncorporationStateOrProvince"},
2151 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\003", "jurisdictionOfIncorporationCountry"}
2153 #define oid_table_size (sizeof(oid_table)/sizeof(oid_table[0]))
2155 static int comp_entry(const void *_e1, const void *_e2)
2157 const oid_entry_t *o1 = (oid_entry_t *)_e1;
2158 const oid_entry_t *o2 = (oid_entry_t *)_e2;
2159 size_t min = o1->l;
2160 int ans;
2161 if (o2->l < min) min = o2->l;
2162 ans = memcmp(o1->oid, o2->oid, min);
2163 if (ans) return ans;
2164 if (o1->l < o2->l) return -1;
2165 if (o1->l > o2->l) return 1;
2166 return 0;
2169 static void append_oid_name(CFMutableStringRef s, const char *prefix,
2170 const void *_oid, size_t l, const char *suffix)
2172 oid_entry_t find, *ans;
2173 find.oid = (char *)_oid;
2174 find.l = l;
2175 find.name = NULL;
2176 ans = (oid_entry_t *)
2177 bsearch(&find, oid_table, oid_table_size, sizeof(find), comp_entry);
2178 if (prefix && *prefix)
2179 CFStringAppendCString(s, prefix, kCFStringEncodingASCII);
2180 if (ans)
2181 CFStringAppendCString(s, ans->name, kCFStringEncodingASCII);
2182 else {
2183 CFMutableStringRef temp = CFStringCreateMutable(kCFAllocatorDefault, 0);
2184 const uint8_t *oid = (uint8_t *)_oid;
2185 bool bad = false;
2186 size_t orig_l = l;
2187 const uint8_t *orig_oid = oid;
2188 if (!temp || l < 3 || *oid != 6)
2189 bad = true;
2190 if (!bad) {
2191 data_t data;
2192 der_atom_t atom;
2193 data.d = oid;
2194 data.l = l;
2195 if (!read_der_atom(&data, &atom))
2196 bad = true;
2197 if (!bad && (atom.rawtag != 6 || atom.dl < 1))
2198 bad = true;
2199 if (!bad) {
2200 oid = data.d + atom.hl;
2201 l = atom.dl;
2202 if (l + atom.hl != orig_l)
2203 bad = true;
2206 if (!bad) {
2207 size_t idx = 0;
2208 do {
2209 unsigned idval = 0;
2210 uint8_t byte;
2211 do {
2212 if (!l) {bad=true; break;}
2213 idval <<= 7;
2214 byte = *oid++;
2215 --l;
2216 idval |= byte & 0x7f;
2217 } while (!bad && (byte & 0x80));
2218 if (bad) break;
2219 if (!idx) {
2220 char twoids[32];
2221 unsigned x, y;
2222 if (idval < 40) {
2223 x = 0; y = idval;
2224 } else if (idval < 80) {
2225 x = 1; y = idval - 40;
2226 } else {
2227 x = 2; y = idval - 80;
2229 snprintf(twoids, sizeof(twoids), "%u.%u", x, y);
2230 CFStringAppendCString(temp, twoids, kCFStringEncodingASCII);
2231 idx += 2;
2232 } else {
2233 char oneid[16];
2234 snprintf(oneid, sizeof(oneid), ".%u", idval);
2235 CFStringAppendCString(temp, oneid, kCFStringEncodingASCII);
2236 ++idx;
2238 } while (l && !bad);
2240 if (bad || l || !temp || !CFStringGetLength(temp))
2241 append_hex_dump(s, orig_oid, orig_l);
2242 else
2243 CFStringAppend(s, temp);
2244 if (temp)
2245 CFRelease(temp);
2247 if (suffix && *suffix)
2248 CFStringAppendCString(s, suffix, kCFStringEncodingASCII);
2251 #define DER_TAG_UTF8STRING 12
2252 #define DER_TAG_NUMERICSTRING 18
2253 #define DER_TAG_PRINTABLESTRING 19
2254 #define DER_TAG_TELETEXSTRING 20
2255 #define DER_TAG_VIDEOTEXSTRING 21
2256 #define DER_TAG_IA5STRING 22
2257 #define DER_TAG_GRAPHICSTRING 25
2258 #define DER_TAG_VISIBLESTRING 26
2259 #define DER_TAG_GENERALSTRING 27
2260 #define DER_TAG_UNIVERSALSTRING 28
2261 #define DER_TAG_BMPSTRING 30
2263 /* flags:
2264 * 0x01 => strings only
2265 * 0x02 => 8-bit strings only
2266 * 0x04 => CN Ids strings only
2267 * 0x08 => wildcard CN okay
2268 * 0x10 => create output string
2270 static bool append_attr_value(CFMutableStringRef *s, const void *_d,
2271 const der_atom_t *a, unsigned flags)
2273 const uint8_t *d = (uint8_t *)_d;
2274 CFStringBuiltInEncodings encoding = kCFStringEncodingASCII;
2275 CFStringRef temp;
2276 if (s && !(flags & 0x10) && !*s) return false;
2277 if (!s || !d || !a || !a->dl) return false;
2278 switch (a->rawtag) {
2279 case DER_TAG_UTF8STRING:
2280 case DER_TAG_GRAPHICSTRING:
2281 case DER_TAG_GENERALSTRING:
2282 case DER_TAG_UNIVERSALSTRING:
2283 encoding = kCFStringEncodingUTF8; break;
2284 case DER_TAG_NUMERICSTRING:
2285 case DER_TAG_PRINTABLESTRING:
2286 case DER_TAG_IA5STRING:
2287 encoding = kCFStringEncodingASCII; break;
2288 case DER_TAG_TELETEXSTRING:
2289 case DER_TAG_VIDEOTEXSTRING:
2290 case DER_TAG_VISIBLESTRING:
2291 encoding = kCFStringEncodingISOLatin1; break;
2292 case DER_TAG_BMPSTRING:
2293 if (flags & 0x06) return false;
2294 encoding = kCFStringEncodingUnicode; break;
2295 default:
2296 if (flags & 0x05) return false;
2297 append_hex_dump(*s, d, a->hl + a->dl);
2298 return true;
2300 if (flags & 0x04 && !is_dns_name(d+a->hl, a->dl, !!(flags & 0x08)))
2301 return false;
2302 temp = CFStringCreateWithBytes(kCFAllocatorDefault, d+a->hl, a->dl,
2303 encoding, true);
2304 if (temp) {
2305 if (flags & 0x10)
2306 *s = CFStringCreateMutable(kCFAllocatorDefault, 0);
2307 if (*s)
2308 CFStringAppend(*s, temp);
2309 CFRelease(temp);
2310 } else {
2311 if (flags & 0x05) return false;
2312 append_hex_dump(*s, d, a->hl + a->dl);
2314 return *s != NULL;
2317 CF_INLINE int is_dig(char c)
2319 return '0' <= c && c <= '9';
2322 static void append_year_string(CFMutableStringRef cfstr, const char *vstr)
2324 size_t vl = strlen(vstr);
2325 if ((vl == 13 || vl == 15) && vstr[vl - 1] == 'Z') {
2326 size_t off = 4;
2327 unsigned short uc[4];
2328 if (vl == 13 && is_dig(vstr[0]) && is_dig(vstr[1])) {
2329 int yr2 = (vstr[0] - '0') * 10 + (vstr[1] - '0');
2330 if (yr2 >= 50) {
2331 uc[0] = '1';
2332 uc[1] = '9';
2333 } else {
2334 uc[0] = '2';
2335 uc[1] = '0';
2337 uc[2] = (unsigned char)vstr[0];
2338 uc[3] = (unsigned char)vstr[1];
2339 off = 2;
2340 } else {
2341 uc[0] = vstr[0];
2342 uc[1] = vstr[1];
2343 uc[2] = vstr[2];
2344 uc[3] = vstr[3];
2346 CFStringAppendCharacters(cfstr, uc, 4);
2347 uc[0] = '-';
2348 uc[1] = vstr[off];
2349 uc[2] = vstr[off+1];
2350 uc[3] = '-';
2351 CFStringAppendCharacters(cfstr, uc, 4);
2352 uc[0] = vstr[off+2];
2353 uc[1] = vstr[off+3];
2354 uc[2] = 'T';
2355 CFStringAppendCharacters(cfstr, uc, 3);
2356 uc[0] = vstr[off+4];
2357 uc[1] = vstr[off+5];
2358 uc[2] = ':';
2359 CFStringAppendCharacters(cfstr, uc, 3);
2360 uc[0] = vstr[off+6];
2361 uc[1] = vstr[off+7];
2362 CFStringAppendCharacters(cfstr, uc, 3);
2363 uc[0] = vstr[off+8];
2364 uc[1] = vstr[off+9];
2365 uc[2] = 'Z';
2366 CFStringAppendCharacters(cfstr, uc, 3);
2367 } else {
2368 CFStringAppendCString(cfstr, vstr, kCFStringEncodingASCII);
2372 void CopyCertValidity(SecCertificateRef _cert, CFStringRef *_nb, CFStringRef *_na)
2374 CFDataRef d = cSecCertificateCopyData(_cert);
2375 CFMutableStringRef nb, na;
2376 data_t data;
2377 der_cert_t cert;
2379 if (!d || !_nb || !_na) return;
2380 *_nb = NULL;
2381 *_na = NULL;
2382 data.d = CFDataGetBytePtr(d);
2383 data.l = CFDataGetLength(d);
2384 if (!read_der_cert(&data, &cert)) {
2385 CFRelease(d);
2386 return;
2388 CFRelease(d);
2389 nb = CFStringCreateMutable(kCFAllocatorDefault, 0);
2390 if (!nb)
2391 return;
2392 na = CFStringCreateMutable(kCFAllocatorDefault, 0);
2393 if (!na) {
2394 CFRelease(nb);
2395 return;
2397 append_year_string(nb, cert.notBefore);
2398 append_year_string(na, cert.notAfter);
2399 *_nb = nb;
2400 *_na = na;
2403 static CFStringRef CopyCertName(SecCertificateRef _cert, bool issuer)
2405 CFDataRef d = cSecCertificateCopyData(_cert);
2406 CFMutableStringRef ans = CFStringCreateMutable(kCFAllocatorDefault, 0);
2407 bool good = false;
2409 for (;;) {
2410 data_t data;
2411 const data_t *name;
2412 der_cert_t cert;
2413 der_atom_t atom;
2414 bool badset = false;
2416 if (!d || !ans) break;
2417 data.d = CFDataGetBytePtr(d);
2418 data.l = CFDataGetLength(d);
2419 if (!read_der_cert(&data, &cert)) break;
2420 name = issuer ? &cert.issuer : &cert.subject;
2421 data.d = name->d;
2422 data.l = name->l;
2423 if (data.d && data.l) {
2424 if (!read_der_atom(&data, &atom)) break;
2425 if (atom.rawtag != 0x30) break;
2426 data.l -= atom.hl;
2427 data.d += atom.hl;
2428 while (data.l) {
2429 data_t set;
2430 unsigned setidx = 0;
2431 badset = true;
2432 if (!read_der_atom(&data, &atom)) break;
2433 if (atom.rawtag != 0x31) break;
2434 set.d = data.d + atom.hl;
2435 set.l = atom.dl;
2436 data.l -= atom.hl + atom.dl;
2437 data.d += atom.hl + atom.dl;
2438 for (;;) {
2439 data_t oid;
2440 if (!read_der_atom(&set, &atom)) break;
2441 if (atom.rawtag != 0x30) break;
2442 set.l -= atom.hl;
2443 set.d += atom.hl;
2444 if (!read_der_atom(&set, &atom)) break;
2445 if (atom.rawtag != 6) break;
2446 oid.d = set.d;
2447 oid.l = atom.hl + atom.dl;
2448 set.l -= atom.hl + atom.dl;
2449 set.d += atom.hl + atom.dl;
2450 if (!read_der_atom(&set, &atom)) break;
2451 append_oid_name(ans, setidx++?"/+":"/", oid.d, oid.l, "=");
2452 append_attr_value(&ans, set.d, &atom, 0);
2453 set.l -= atom.hl + atom.dl;
2454 set.d += atom.hl + atom.dl;
2455 if (!set.l) {
2456 badset=false;
2457 break;
2460 if (badset) break;
2462 if (badset || data.l) break;
2463 good = true;
2465 break;
2467 if (d) CFRelease(d);
2468 if (!good && ans) {CFRelease(ans); ans=NULL;}
2469 return MakeVisibleString(ans);
2472 CFStringRef CopyCertSubject(SecCertificateRef _cert)
2474 return CopyCertName(_cert, false);
2477 CFStringRef CopyCertSubjectKeyId(SecCertificateRef _cert)
2479 return CopyCertKeyId(_cert, false);
2482 CFStringRef CopyCertIssuer(SecCertificateRef _cert)
2484 return CopyCertName(_cert, true);
2487 CFStringRef CopyCertIssuerKeyId(SecCertificateRef _cert)
2489 return CopyCertKeyId(_cert, true);
2492 /* return CFArrayRef if arr else CFStringRef
2493 * flags:
2494 * 0x01 includes DNS alts
2495 * 0x02 includes IPv4 alts
2496 * 0x04 includes IPv6 alts
2497 * 0x08 include IP other alts
2499 static CFTypeRef CopyCertSubjectAltNamesInt(const der_cert_t *cert, bool arr, unsigned flags)
2501 data_t data;
2502 CFTypeRef ans = arr ?
2503 (CFTypeRef)CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks) :
2504 (CFTypeRef)CFStringCreateMutable(kCFAllocatorDefault, 0);
2505 bool good = false;
2507 do {
2508 der_atom_t atom;
2510 if (!cert || !ans) break;
2511 if (!cert->subjectAltNames.d || !cert->subjectAltNames.l) break;
2512 data.d = cert->subjectAltNames.d;
2513 data.l = cert->subjectAltNames.l;
2514 if (!read_der_atom(&data, &atom)) break;
2515 if (atom.rawtag != 0x30) break;
2516 data.l -= atom.hl;
2517 data.d += atom.hl;
2518 do {
2519 if (!read_der_atom(&data, &atom)) break;
2520 if (atom.rawtag == 0x82 && (flags & 0x01)) {
2521 CFStringRef temp;
2522 if (!arr && CFStringGetLength((CFStringRef)ans))
2523 CFStringAppendCString((CFMutableStringRef)ans, ",", kCFStringEncodingASCII);
2524 temp = CFStringCreateWithBytes(kCFAllocatorDefault, data.d+atom.hl,
2525 atom.dl, kCFStringEncodingASCII, true);
2526 if (!temp) break;
2527 if (arr)
2528 CFArrayAppendValue((CFMutableArrayRef)ans, temp);
2529 else
2530 CFStringAppend((CFMutableStringRef)ans, temp);
2531 CFRelease(temp);
2532 } else if (atom.rawtag == 0x87 && (flags & 0x0e)) {
2533 if ((atom.dl == 4 && (flags & 0x02)) ||
2534 (atom.dl == 16 && (flags & 0x04)) ||
2535 (atom.dl != 4 && atom.dl != 16 && (flags & 0x08))) {
2536 if (arr) {
2537 CFDataRef dtemp = CFDataCreate(kCFAllocatorDefault, data.d+atom.hl, atom.dl);
2538 if (!dtemp) break;
2539 CFArrayAppendValue((CFMutableArrayRef)ans, dtemp);
2540 CFRelease(dtemp);
2541 } else {
2542 if (CFStringGetLength((CFStringRef)ans))
2543 CFStringAppendCString((CFMutableStringRef)ans, ",", kCFStringEncodingASCII);
2544 if (atom.dl == 4) {
2545 char ipv4str[16];
2546 const uint8_t *ip = data.d+atom.hl;
2547 sprintf(ipv4str, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
2548 CFStringAppendCString((CFMutableStringRef)ans, ipv4str, kCFStringEncodingASCII);
2549 } else if (atom.dl == 16) {
2550 char ntopbuff[INET6_ADDRSTRLEN];
2551 if (!inet_ntop(AF_INET6, data.d+atom.hl, ntopbuff, sizeof(ntopbuff)))
2552 break;
2553 CFStringAppendCString((CFMutableStringRef)ans, ntopbuff, kCFStringEncodingASCII);
2554 } else {
2555 append_hex_dump((CFMutableStringRef)ans, data.d+atom.hl, atom.dl);
2560 data.l -= atom.hl + atom.dl;
2561 data.d += atom.hl + atom.dl;
2562 } while (data.l);
2563 if (!data.l && ( (arr && CFArrayGetCount((CFArrayRef)ans) ) ||
2564 (!arr && CFStringGetLength((CFStringRef)ans)) ))
2565 good = true;
2566 } while (0);
2567 if (!good && ans) {CFRelease(ans); ans=NULL;}
2568 return ans;
2571 static CFArrayRef CopyCertSubjectCNIds(const der_cert_t *cert)
2573 CFMutableArrayRef ans =
2574 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
2575 bool good = false;
2577 do {
2578 data_t data;
2579 der_atom_t atom;
2581 if (!ans || !cert || !cert->subject.d || !cert->subject.l) break;
2582 data.d = cert->subject.d;
2583 data.l = cert->subject.l;
2584 if (!read_der_atom(&data, &atom)) break;
2585 if (atom.rawtag != 0x30) break;
2586 data.l -= atom.hl;
2587 data.d += atom.hl;
2588 while (data.l) {
2589 data_t set;
2590 if (!read_der_atom(&data, &atom)) break;
2591 if (atom.rawtag != 0x31) break;
2592 set.d = data.d + atom.hl;
2593 set.l = atom.dl;
2594 data.l -= atom.hl + atom.dl;
2595 data.d += atom.hl + atom.dl;
2596 if (!read_der_atom(&set, &atom)) break;
2597 if (atom.rawtag != 0x30) break;
2598 if (atom.hl + atom.dl == set.l) { /* single-value CN only */
2599 CFMutableStringRef cnid;
2600 data_t oid;
2601 set.l -= atom.hl;
2602 set.d += atom.hl;
2603 if (!read_der_atom(&set, &atom)) break;
2604 if (atom.rawtag != 6) break;
2605 oid.d = set.d;
2606 oid.l = atom.hl + atom.dl;
2607 if (data_matches(&oid, &OID_CommonName)) {
2608 set.l -= atom.hl + atom.dl;
2609 set.d += atom.hl + atom.dl;
2610 if (!read_der_atom(&set, &atom)) break;
2611 if (append_attr_value(&cnid, set.d, &atom, 0x1f) && cnid) {
2612 CFArrayAppendValue(ans, cnid);
2613 CFRelease(cnid);
2618 if (!data.l)
2619 good = true;
2620 } while (0);
2621 if (ans && (!good || !CFArrayGetCount(ans))) {
2622 CFRelease(ans);
2623 ans=NULL;
2625 return ans;
2628 CFStringRef CopyCertSubjectAltNamesString(SecCertificateRef _cert)
2630 CFDataRef d = cSecCertificateCopyData(_cert);
2631 data_t data;
2632 der_cert_t cert;
2633 CFStringRef ans = NULL;
2635 if (!d) return NULL;
2636 data.d = CFDataGetBytePtr(d);
2637 data.l = CFDataGetLength(d);
2638 if (read_der_cert(&data, &cert))
2639 ans = (CFStringRef)CopyCertSubjectAltNamesInt(&cert, false, 0x0f);
2640 CFRelease(d);
2641 return MakeVisibleString(ans);
2644 /* mode:
2645 * 0 = DNS/CN ids
2646 * 4 = IPv4 ids
2647 * 16 = IPv6 ids
2649 static CFArrayRef CopyCertSubjectIds(der_cert_t *c, unsigned mode)
2651 CFArrayRef ans = NULL;
2652 if (!c || (mode != 0 && mode != 4 && mode != 16)) return NULL;
2653 if (mode == 4)
2654 return ans = (CFArrayRef)CopyCertSubjectAltNamesInt(c, true, 0x02);
2655 if (mode == 16)
2656 return ans = (CFArrayRef)CopyCertSubjectAltNamesInt(c, true, 0x04);
2657 else {
2658 ans = (CFArrayRef)CopyCertSubjectAltNamesInt(c, true, 0x01);
2659 if (!ans || !CFArrayGetCount(ans)) {
2660 if (ans) CFRelease(ans);
2661 ans = CopyCertSubjectCNIds(c);
2664 if (ans && !CFArrayGetCount(ans)) {
2665 CFRelease(ans);
2666 ans = NULL;
2668 return ans;
2671 OSStatus VerifyTrustChain(SecTrustRef trust, CFArrayRef customRootsOrNull,
2672 unsigned certFlags, unsigned flags, const char *peername,
2673 CFArrayRef pinnedKeySet)
2675 SecTrustResultType result;
2676 CFArrayRef chain = NULL;
2677 CSSM_TP_APPLE_EVIDENCE_INFO *evidence;
2678 bool pkonly = (certFlags & 0x02) ? true : false;
2679 bool explicitCertsOnly = (certFlags & 0x01) ? true : false;
2680 bool nameonly = (certFlags & 0x04) ? true : false;
2681 OSStatus err;
2682 size_t i, cnt;
2683 if ((pinnedKeySet && !CFArrayGetCount(pinnedKeySet)) || (pkonly && !pinnedKeySet))
2684 return paramErr;
2685 if (nameonly && !pkonly && (!peername || !*peername))
2686 return paramErr;
2687 err = cSecTrustGetResult(trust, &result, &chain, &evidence);
2688 if (err == errSecTrustNotAvailable) {
2689 /* We need to evaluate first */
2690 CFArrayRef anchors = customRootsOrNull;
2691 if (chain) CFRelease(chain);
2692 if (anchors)
2693 CFRetain(anchors);
2694 else {
2695 err = SecTrustCopyAnchorCertificates(&anchors);
2696 if (err) return err;
2698 err = SecTrustSetAnchorCertificates(trust, anchors);
2699 CFRelease(anchors);
2700 if (err) return err;
2701 err = cSecTrustSetAnchorCertificatesOnly(trust, customRootsOrNull ? true : false);
2702 if (err && err != unimpErr) return err;
2703 err = SecTrustEvaluate(trust, &result);
2704 if (err && !pkonly && !nameonly) return err;
2705 chain = NULL;
2706 err = cSecTrustGetResult(trust, &result, &chain, &evidence);
2708 if (err) {
2709 if (chain) CFRelease(chain);
2710 return err;
2712 if (!chain || !evidence || !CFArrayGetCount(chain)) {
2713 if (chain) CFRelease(chain);
2714 return errSSLXCertChainInvalid;
2716 if (pkonly) goto pinned_key_check;
2717 cnt = (size_t)CFArrayGetCount(chain);
2718 if ((peername && *peername) ||
2719 (!(flags & CSSM_TP_ACTION_LEAF_IS_CA) &&
2720 !(evidence[0].StatusBits & CSSM_CERT_STATUS_IS_ROOT))) {
2721 CFDataRef certder = cSecCertificateCopyData(
2722 (SecCertificateRef)CFArrayGetValueAtIndex(chain, 0));
2723 data_t der;
2724 der_cert_t cert;
2725 if (!certder)
2726 return errSSLBadCert;
2727 der.d = CFDataGetBytePtr(certder);
2728 der.l = CFDataGetLength(certder);
2729 if (!read_der_cert(&der, &cert))
2730 err = errSSLBadCert;
2732 /* First confirm we have a host name match. SecureTransport should do
2733 * this for us (but will not give us a decent result code) except that
2734 * it has problems with IPv6 address matching */
2735 if (!err && peername && *peername) {
2736 size_t peerlen = strlen(peername);
2737 union {
2738 uint8_t ipv6[16];
2739 uint8_t ipv4[4];
2740 } ipa;
2741 int mode = -1;
2742 if (parse_ipv4_name(peername, peerlen, ipa.ipv4)) mode = 4;
2743 else if (is_dns_name(peername, peerlen, false)) mode = 0;
2744 else if (parse_ipv6_name(peername, peerlen, ipa.ipv6)) mode = 16;
2745 if (mode == -1)
2746 /* if we can't parse peername it can't possibly match! */
2747 err = errSSLHostNameMismatch;
2748 if (!err) {
2749 CFArrayRef ids = CopyCertSubjectIds(&cert, mode);
2750 if (ids && !CFArrayGetCount(ids)) {
2751 CFRelease(ids);
2752 ids = NULL;
2754 if (!ids)
2755 /* if we don't have anything to match against it can't possibly match! */
2756 err = errSSLHostNameMismatch;
2757 else {
2758 size_t j, idcnt = CFArrayGetCount(ids);
2759 bool matched = false;
2760 for (j = 0; j < idcnt; ++j) {
2761 CFTypeRef oneid = (CFTypeRef)CFArrayGetValueAtIndex(ids, j);
2762 if (mode) {
2763 const uint8_t *p;
2764 size_t l;
2765 if (CFDataGetTypeID() != CFGetTypeID(oneid)) continue;
2766 p = (uint8_t *)CFDataGetBytePtr((CFDataRef)oneid);
2767 l = (size_t)CFDataGetLength((CFDataRef)oneid);
2768 if (l != (size_t)mode) continue;
2769 if (memcmp(ipa.ipv6, p, l) == 0) {
2770 matched = true;
2771 break;
2773 } else {
2774 CFDataRef dnsname;
2775 if (CFStringGetTypeID() != CFGetTypeID(oneid)) continue;
2776 dnsname = CFStringCreateExternalRepresentation(
2777 kCFAllocatorDefault, (CFStringRef)oneid,
2778 kCFStringEncodingASCII, 0);
2779 if (!dnsname) continue;
2780 if (peername_matches_id(peername, dnsname))
2781 matched = true;
2782 CFRelease(dnsname);
2783 if (matched)
2784 break;
2787 CFRelease(ids);
2788 if (!matched)
2789 err = errSSLHostNameMismatch;
2794 /* Confirm that the first certificate is NOT a CA (otherwise it's not a
2795 * valid chain), but again SecureTransport should have already checked that
2796 * for us. CSSM_TP_ACTION_LEAF_IS_CA overrides. Also we never check the
2797 * root certificate even if it's also the leaf. */
2798 if (!nameonly && !err && !(flags & CSSM_TP_ACTION_LEAF_IS_CA) &&
2799 !(evidence[0].StatusBits & CSSM_CERT_STATUS_IS_ROOT) && cert.isCA)
2800 err = errSSLXCertChainInvalid;
2802 CFRelease(certder);
2803 if (err) return err;
2805 if (nameonly) goto pinned_key_check;
2806 if (explicitCertsOnly) {
2807 /* Check all but root */
2808 for (i = 0; i < cnt; ++i) {
2809 if (!(evidence[i].StatusBits & CSSM_CERT_STATUS_IS_IN_INPUT_CERTS) &&
2810 !(evidence[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT)) {
2811 /* If the magical cert had not appeared, the chain would have stopped
2812 * here and the error would be no root, so return that error */
2813 CFRelease(chain);
2814 return errSSLNoRootCert;
2818 if (!(flags & CSSM_TP_ACTION_ALLOW_EXPIRED) ||
2819 !(flags & CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT)) {
2820 /* check for expired or not yet valid certs */
2821 for (i = 0; i < cnt; ++i) {
2822 if ((flags & CSSM_TP_ACTION_ALLOW_EXPIRED) &&
2823 !(evidence[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT))
2824 continue;
2825 if ((flags & CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT) &&
2826 (evidence[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT))
2827 continue;
2828 if (evidence[i].StatusBits & CSSM_CERT_STATUS_EXPIRED) {
2829 CFRelease(chain);
2830 return errSSLCertExpired;
2832 if (evidence[i].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) {
2833 CFRelease(chain);
2834 return errSSLCertNotYetValid;
2838 /* check for no root */
2839 if (!(evidence[cnt-1].StatusBits & CSSM_CERT_STATUS_IS_ROOT)) {
2840 CFRelease(chain);
2841 return errSSLNoRootCert;
2843 /* check for unknown root */
2844 if (!(evidence[cnt-1].StatusBits & CSSM_CERT_STATUS_IS_IN_ANCHORS)) {
2845 CFRelease(chain);
2846 return errSSLUnknownRootCert;
2848 if (customRootsOrNull) {
2849 /* make sure we're not using a gratuitous root, Mac OS X likes to just
2850 * go ahead and use its anchors sometimes despite settings to the contrary */
2851 if (!SecCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(chain, cnt-1),
2852 customRootsOrNull)) {
2853 CFRelease(chain);
2854 return errSSLNoRootCert;
2857 /* everything looks good, so check the trust result code now */
2858 switch (result) {
2859 case kSecTrustResultProceed:
2860 case kSecTrustResultUnspecified:
2861 /* good result */
2862 break;
2863 case kSecTrustResultDeny:
2864 /* DENIED! */
2865 CFRelease(chain);
2866 return errSecTrustSettingDeny;
2867 default:
2868 /* everything else (confirm, invalid, recoverable, fatal, other) */
2869 CFRelease(chain);
2870 return errSecNotTrusted;
2872 pinned_key_check:
2873 if (pinnedKeySet) {
2874 CFDataRef certder = cSecCertificateCopyData(
2875 (SecCertificateRef)CFArrayGetValueAtIndex(chain, 0));
2876 data_t der;
2877 der_cert_t cert;
2878 CFDataRef peerPubKey;
2879 bool pinok;
2880 if (!certder)
2881 return errSSLBadCert;
2882 der.d = CFDataGetBytePtr(certder);
2883 der.l = CFDataGetLength(certder);
2884 if (!read_der_cert(&der, &cert)) {
2885 CFRelease(certder);
2886 return errSSLBadCert;
2888 if (certFlags & 0x08) {
2889 unsigned char sha256[CC_SHA256_DIGEST_LENGTH];
2890 CC_SHA256_CTX ctx;
2891 CC_SHA256_Init(&ctx);
2892 CC_SHA256_Update(&ctx, cert.subjectPubKey.d, (CC_LONG)cert.subjectPubKey.l);
2893 CC_SHA256_Final(sha256, &ctx);
2894 peerPubKey = CFDataCreate(kCFAllocatorDefault, sha256, sizeof(sha256));
2895 } else {
2896 peerPubKey = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
2897 cert.subjectPubKey.d, cert.subjectPubKey.l, kCFAllocatorNull);
2899 if (!peerPubKey) {
2900 CFRelease(certder);
2901 return memFullErr;
2903 pinok = BlobInArray(peerPubKey, pinnedKeySet);
2904 CFRelease(peerPubKey);
2905 CFRelease(certder);
2906 if (!pinok)
2907 return errSecPinnedKeyMismatch;
2909 CFRelease(chain);
2910 return noErr;
2913 #elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
2915 #error iOS is not currently supported
2917 #endif /* TARGET_OS_EMBEDDED || TARGET_OS_IPHONE */