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.
20 #include <Security/Security.h>
21 #include <CommonCrypto/CommonDigest.h>
23 #include <objc/objc-runtime.h>
28 #include <sys/types.h>
32 #include <arpa/inet.h>
34 #include <crt_externs.h>
38 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
40 __attribute__((constructor
,used
)) static void stcompat_initialize(void);
41 #endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
49 typedef struct data_s
{
54 extern CFStringRef
CFStringCreateWithBytesNoCopy(
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"));
176 unsigned len
= (unsigned)CFStringGetLength(dir
);
179 (unsigned)CFStringGetCharacterAtIndex(dir
, l
-1) == (unsigned)'/') {
183 CFStringRef old
= dir
;
184 dir
= CFStringCreateWithSubstring(
185 kCFAllocatorDefault
, dir
, CFRangeMake(0, l
));
195 char *CFStringCreateUTF8String(CFStringRef s
, Boolean release
)
201 m
= (size_t)CFStringGetMaximumSizeForEncoding(
202 CFStringGetLength(s
), kCFStringEncodingUTF8
) + 1;
203 c
= (char *)malloc(m
);
205 if (release
) CFRelease(s
);
208 if (!CFStringGetCString(s
, c
, m
, kCFStringEncodingUTF8
)) {
212 if (release
) CFRelease(s
);
216 static CFStringRef
MakeVisibleString(CFStringRef in
)
218 CFStringRef nullbyte
;
219 CFMutableStringRef m
;
221 nullbyte
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, (UInt8
*)"\0",
222 1, kCFStringEncodingASCII
, false, kCFAllocatorNull
);
223 if (!nullbyte
) return in
;
224 m
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, in
);
227 CFStringFindAndReplace(m
, nullbyte
, CFSTR("\\000"),
228 CFRangeMake(0, CFStringGetLength(m
)), 0);
233 CFDataRef
CFDataCreateWithContentsOfFile(CFAllocatorRef a
, const char *f
)
236 CFMutableDataRef d
= CFDataCreateMutable(a
, 0);
240 fd
= open(f
, O_RDONLY
);
246 cnt
= read(fd
, buff
, sizeof(buff
));
247 if (cnt
> 0) CFDataAppendBytes(d
, (UInt8
*)buff
, (size_t)cnt
);
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
);
268 const char *p
= memchr(m
, *s
, ml
);
273 if (ml
< sl
) return NULL
;
274 if (!memcmp(m
, s
, sl
)) return (void *)m
;
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
;
305 if (is_lb(*m
)) return 1;
312 static int has_prnt(const void *_m
, size_t l
)
314 const char *m
= (const char *)_m
;
316 if (!is_prnt(*m
)) return 0;
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
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;
340 const char *p
= (char *)memmem(m
, l
, marker
, mkl
);
344 if (p
> origm
&& !is_eol(p
[-1])) continue;
345 t
= (char *)memmem(m
, l
, "-----", 5);
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
)) {
357 if (l
&& *m
== '\r') {
361 if (l
&& *m
== '\n') {
371 typedef enum pemtype_e
{
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" */
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" */
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" */
390 static int nextpem(const char *p
, size_t l
, peminfo_t
*o
)
392 size_t beglen
, endlen
;
393 int begtype
, endtype
;
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
))
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
;
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
)
458 const uint8_t *p
= (uint8_t *)_b
;
459 if (l
&& !p
) return NULL
;
460 d
= CFDataCreateMutable(a
, 0);
463 for (i
=0; l
; ++p
, --l
) {
465 if (c
== ' ' || c
== '\t' || c
== '\r' || c
== '\n' || c
== '\f')
476 CFDataAppendBytes(d
, out
, inp
[2] == '=' ? 1 : 2);
479 CFDataAppendBytes(d
, out
, 3);
489 static SecCertificateRef
createvalidcert(CFDataRef d
)
491 SecCertificateRef cert
= cSecCertificateCreateWithData(kCFAllocatorDefault
, d
);
492 if (!cert
) return NULL
;
493 if (!CheckCertOkay(cert
)) {
500 CFArrayRef
CreateCertsArrayWithData(CFDataRef d
, const errinfo_t
*e
)
502 const char *certs
, *p
;
503 size_t certslen
, plen
, cnt
= 1;
506 certs
= (char *)CFDataGetBytePtr(d
);
507 certslen
= (size_t)CFDataGetLength(d
);
508 a
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
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
);
523 cert
= createvalidcert(der
);
527 e
->f(e
->u
, "Invalid CA certificate bad DER data");
531 CFArrayAppendValue(a
, cert
);
534 } else if (readcnt
== -1) {
536 e
->f(e
->u
, "Invalid CA certificate #%u (offset %u) in bundle",
537 (unsigned)cnt
, (unsigned)(p
-certs
));
540 } else if (readcnt
&& pem
.type
== pemtype_certificate
) {
541 CFDataRef der
= CFDataCreateFromBase64(kCFAllocatorDefault
, pem
.body
, pem
.bodylen
);
542 SecCertificateRef cert
;
545 e
->f(e
->u
, "Invalid CA certificate #%u (offset %u) bad base 64 in bundle",
546 (unsigned)cnt
, (unsigned)(pem
.start
-certs
));
550 cert
= createvalidcert(der
);
554 e
->f(e
->u
, "Invalid CA certificate #%u (offset %u) bad cert data in bundle",
555 (unsigned)cnt
, (unsigned)(pem
.start
-certs
));
559 CFArrayAppendValue(a
, cert
);
562 } else if (!readcnt
) break;
563 plen
-= (pem
.start
+ pem
.len
) - p
;
564 p
= pem
.start
+ pem
.len
;
566 if (!CFArrayGetCount(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
);
578 CFArrayAppendValue(a
, data
);
584 CFArrayRef
CreatePubKeyArrayWithData(CFDataRef d
, const errinfo_t
*e
)
586 const char *keys
, *p
;
587 size_t keyslen
, plen
, cnt
= 1;
590 keys
= (char *)CFDataGetBytePtr(d
);
591 keyslen
= (size_t)CFDataGetLength(d
);
592 a
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
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
);
607 if (!CheckPubKeyOkayInt(der
, &pubkey
, 0x01)) {
610 e
->f(e
->u
, "Invalid public key bad DER data");
614 CFArrayAppendValue_data(a
, &pubkey
);
617 } else if (readcnt
== -1) {
619 e
->f(e
->u
, "Invalid public key #%u (offset %u) in bundle",
620 (unsigned)cnt
, (unsigned)(p
-keys
));
623 } else if (readcnt
&& (pem
.type
== pemtype_publickey
||
624 pem
.type
== pemtype_certificate
)) {
625 CFDataRef der
= CFDataCreateFromBase64(kCFAllocatorDefault
, pem
.body
, pem
.bodylen
);
628 e
->f(e
->u
, "Invalid public key #%u (offset %u) bad base 64 in bundle",
629 (unsigned)cnt
, (unsigned)(pem
.start
-keys
));
633 if (!CheckPubKeyOkayInt(der
, &pubkey
, 0x01)) {
636 e
->f(e
->u
, "Invalid public key #%u (offset %u) bad public key data in bundle",
637 (unsigned)cnt
, (unsigned)(pem
.start
-keys
));
641 CFArrayAppendValue_data(a
, &pubkey
);
644 } else if (!readcnt
) break;
645 plen
-= (pem
.start
+ pem
.len
) - p
;
646 p
= pem
.start
+ pem
.len
;
648 if (!CFArrayGetCount(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;
667 if (**pstr
!= 'h' && **pstr
!= 'H') return 0;
669 if (**pstr
!= 'a' && **pstr
!= 'A') return 0;
671 if (!strncmp(*pstr
, "256//", 5)) {
678 Boolean
IsSha256HashList(const char *hashlist
)
680 if (!hashlist
|| !*hashlist
)
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
)
691 a
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
696 if (!skip_sha256_prefix(&hashlist
)) {
698 e
->f(e
->u
, "Invalid public key sha256 base64 hash -- "
699 "missing \"sha256//\" prefix at string offset %u",
700 (unsigned)(hashlist
-orig
));
704 l
= strspn(hashlist
, BASE64CHARS
);
705 if (l
!= BASE64SIZE(CC_SHA256_DIGEST_LENGTH
)) {
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
));
714 blob
= CFDataCreateFromBase64(kCFAllocatorDefault
, hashlist
, l
);
717 e
->f(e
->u
, "out of memory");
721 if ((size_t)CFDataGetLength(blob
) != (size_t)CC_SHA256_DIGEST_LENGTH
) {
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
));
732 CFArrayAppendValue(a
, blob
);
734 skip_space_semi(&hashlist
);
735 if (!*hashlist
) break;
737 if (!CFArrayGetCount(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 */
753 static char *find_home_env(void)
755 char ***eptr
= _NSGetEnviron();
757 if (!eptr
) return NULL
;
759 while (*ptr
&& strncmp(*ptr
, "HOME=", 5)) {
762 return *ptr
? *ptr
: NULL
;
765 static void get_home_dirs(homedirs_t
*dirs
)
767 struct passwd
*pwinf
;
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
);
777 dirs
->cur_home
= NULL
;
780 static void free_home_dirs(homedirs_t
*dirs
)
786 typedef struct tempch_s
{
789 char pw
[16]; /* random 15-character (0x20-0x7e), NULL terminated password */
790 char loc
[1]; /* Always will have at least a NULL byte */
793 static void gen_rand_pw(void *_out
, size_t len
)
795 unsigned char *out
= (unsigned char *)_out
;
797 if (!out
|| !len
) return;
798 fd
= open("/dev/random", O_RDONLY
);
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);
815 static tempch_t
*new_temp_keych(void)
818 char newdir
[PATH_MAX
];
820 CFStringRef tempdir
= CFCopyTemporaryDirectory();
822 if (!tempdir
) return NULL
;
823 okay
= CFStringGetCString(tempdir
, newdir
, sizeof(newdir
) - 32, kCFStringEncodingUTF8
);
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
;
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
)) {
838 strcat(ans
->loc
, "/temp.keychain");
839 get_home_dirs(&ans
->dirs
);
843 static void del_temp_keych(tempch_t
*keych
)
847 l
= strlen(keych
->loc
);
848 if (l
> 14 && !strcmp(keych
->loc
+ (l
- 14), "/temp.keychain")) {
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
)) {
857 putenv(keych
->dirs
.home
);
860 (void)SecKeychainLock(keych
->ref
);
861 (void)SecKeychainDelete(keych
->ref
);
863 if (keych
->dirs
.cur_home
)
864 putenv(keych
->dirs
.cur_home
);
868 CFRelease(keych
->ref
);
870 (void)MUTEX_UNLOCK();
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
);
879 while ((ent
=readdir(d
)) != NULL
) {
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
);
890 free_home_dirs(&keych
->dirs
);
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
);
904 int readcnt
= nextpem(p
, l
, &pem
);
905 if (!readcnt
&& p
== origp
) {
906 /* Assume it's DER data */
910 if (!readcnt
|| readcnt
== -1) return NULL
;
911 if (pem
.type
== pemtype_privatekey_rsa
) {
913 if (pem
.start
== origp
&& pem
.len
== origl
) {
917 return CFDataCreate(kCFAllocatorDefault
, (uint8_t *)pem
.start
, pem
.len
);
919 l
-= (size_t)readcnt
;
920 p
+= (size_t)readcnt
;
925 SecIdentityRef
cSecIdentityCreateWithCertificateAndKeyData(
926 SecCertificateRef cert
, CFDataRef keydata
, CFTypeRef pw
, CFStringRef hint
,
930 CFDataRef rawkey
= NULL
;
931 tempch_t
*keych
= NULL
;
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
;
943 rawkey
= extract_key_copy(keydata
, &ispem
);
946 CFArrayRef searchlist
= NULL
;
947 keych
= new_temp_keych();
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,
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
);
987 if (err
|| !keychain
)
989 keych
->ref
= keychain
;
990 err
= SecKeychainUnlock(keychain
, sizeof(keych
->pw
), keych
->pw
, true);
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(¶ms
, 0, sizeof(params
));
1003 params
.version
= SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION
;
1004 params
.flags
= kSecKeyImportOnlyOne
|kSecKeyNoAccessControl
;
1006 params
.passphrase
= pw
;
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, ¶ms
, keychain
, &items
);
1015 if (!err
&& items
&& CFArrayGetCount(items
) == 1 &&
1016 CFGetTypeID((CFTypeRef
)CFArrayGetValueAtIndex(items
, 0)) == SecKeyGetTypeID()) {
1017 key
= (SecKeyRef
)CFArrayGetValueAtIndex(items
, 0);
1020 if (items
) CFRelease(items
);
1024 /* If we have a key we must also have a keychain */
1025 err
= cSecIdentityCreateWithCertificate(keychain
, cert
, &ans
);
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
);
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
);
1046 del_temp_keych(keych
);
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
;
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
,
1077 CFArrayAppendValue(ans
, identity
);
1078 for (i
= 1; i
< count
; ++i
) {
1079 CFArrayAppendValue(ans
, CFArrayGetValueAtIndex(certs
, i
));
1084 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
1086 #ifndef kCFCoreFoundationVersionNumber10_8
1087 #define kCFCoreFoundationVersionNumber10_8 744.00
1089 #ifndef kCFCoreFoundationVersionNumber10_8_3
1090 #define kCFCoreFoundationVersionNumber10_8_3 744.18
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
*);
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
);
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
;
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
;
1193 ordering
= a
->order
- b
->order
;
1197 size_t cSSLSortCiphers(SSLCipherSuite
*a
, size_t n
)
1199 struct cipher_order
*sortmem
;
1205 sortmem
= (struct cipher_order
*)malloc(n
* sizeof(struct cipher_order
));
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
)
1217 sortmem
[i
].tier
= entry
->tier
;
1218 sortmem
[i
].order
= entry
->order
;
1219 if (sortmem
[i
].order
< 0)
1220 sortmem
[i
].order
= (int)i
;
1222 sortmem
[i
].tier
=9999;
1223 sortmem
[i
].order
= (int)i
;
1226 qsort(sortmem
, n
, sizeof(struct cipher_order
), sort1
);
1228 for (i
=0; i
<n
; ++i
) {
1229 if (notbad
< 0 && sortmem
[i
].tier
== 9999)
1231 a
[i
] = sortmem
[i
].cipher
;
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
);
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
);
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
);
1265 OSStatus
cSSLCopyPeerCertificates(SSLContextRef cxt
, CFArrayRef
*certs
)
1267 if (!certs
|| !cxt
) return paramErr
;
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
);
1285 OSStatus
cSecTrustSetAnchorCertificatesOnly(SecTrustRef cxt
, Boolean anchorsOnly
)
1287 if (fnc
.fSecTrustSetAnchorCertificatesOnly
)
1288 return fnc
.fSecTrustSetAnchorCertificatesOnly(cxt
, anchorsOnly
);
1292 OSStatus
cSSLSetProtocolVersionMinMax(SSLContextRef cxt
, int minVer
, int maxVer
)
1296 if (minVer
< 0 || maxVer
< 0 || minVer
> 8 || maxVer
> 8 || minVer
> maxVer
)
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
);
1316 err
= fnc
.fSSLSetProtocolVersionMax(cxt
, maxVer
);
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
);
1337 OSStatus
cSSLSetSessionOption(SSLContextRef cxt
, int option
, Boolean value
)
1339 if (fnc
.fSSLSetSessionOption
)
1340 return fnc
.fSSLSetSessionOption(cxt
, option
, value
);
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
;
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
);
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
);
1380 Boolean
BlobsEqual(CFDataRef d1
, CFDataRef d2
)
1383 Boolean ans
= false;
1387 l1
= CFDataGetLength(d1
);
1388 l2
= CFDataGetLength(d2
);
1390 const void *p1
= (void *)CFDataGetBytePtr(d1
);
1391 const void *p2
= (void *)CFDataGetBytePtr(d2
);
1392 ans
= memcmp(p1
, p2
, l1
) == 0;
1397 Boolean
SecCertsEqual(SecCertificateRef c1
, SecCertificateRef c2
)
1400 Boolean ans
= false;
1401 d1
= cSecCertificateCopyData(c1
);
1402 if (!d1
) return false;
1403 d2
= cSecCertificateCopyData(c2
);
1408 ans
= BlobsEqual(d1
, d2
);
1414 Boolean
BlobInArray(CFDataRef d
, CFArrayRef a
)
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
)))
1426 Boolean
SecCertInArray(SecCertificateRef c
, CFArrayRef a
)
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
)))
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
;
1449 at
.tag
= kSecLabelItemAttr
;
1450 at
.length
= strlen(label
);
1451 at
.data
= (char *)label
;
1454 err
= fnc
.fSecKeychainSearchCreateFromAttributes(NULL
,
1455 kSecCertificateItemClass
, &al
, &sr
);
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
);
1463 if (!err
&& ans
) break;
1470 return errSecItemNotFound
;
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
;
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
);
1504 OSStatus
cSecIdentityCreateWithCertificate(CFTypeRef k
, SecCertificateRef c
,
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
);
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
))
1519 if (fnc
.fSSLNewContext
&& fnc
.fSSLDisposeContext
) {
1521 OSStatus err
= fnc
.fSSLNewContext(ps
== kSSLServerSide
, &cxt
);
1522 return err
? NULL
: cxt
;
1527 void cSSLDisposeContext(SSLContextRef c
)
1529 if (fnc
.fSSLCreateContext
)
1531 else if (fnc
.fSSLDisposeContext
)
1532 fnc
.fSSLDisposeContext(c
);
1535 CF_INLINE
bool is_ldh(int c
)
1538 ('A' <= c
&& c
<= 'Z') ||
1539 ('a' <= c
&& c
<= 'z') ||
1540 ('0' <= c
&& c
<= '9') ||
1544 CF_INLINE
size_t get_label_len(const char *p
, size_t l
)
1547 while (l
-- && is_ldh(*p
++)) ++ans
;
1551 static bool is_dns_name(const void *_p
, size_t l
, bool wcok
)
1553 const char *p
= (char *)_p
;
1556 if (!p
) return false;
1557 if (l
>= 1 && p
[l
-1] == '.') --l
;
1558 if (!l
) return false;
1559 if (l
> 255) return false;
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;
1567 if (p
[lablen
] != '.') return false;
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
)
1585 if (clc(*p1
++) != clc(*p2
++)) return false;
1590 static bool peername_matches_id(const char *peername
, CFDataRef idrawname
)
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))
1602 if (peername
[pl
- 1] == '.') --pl
;
1603 if (idname
[il
- 1] == '.') --il
;
1604 if (pl
> 255 || il
> 255)
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;
1611 if (peername
[pll
] != '.') return false;
1615 if (idname
[ill
] != '.') return false;
1618 if (idx
|| idname
[0] != '*') {
1619 if (pll
!= ill
) return false;
1620 if (!matchicase(peername
, idname
, pll
)) return false;
1631 CF_INLINE
size_t get_num_len(const char *p
, size_t l
)
1634 while (l
-- && '0' <= *p
&& *p
<= '9') {
1641 Boolean
IsIPv4Name(const void *_p
, size_t l
)
1643 const char *p
= (char *)_p
;
1646 if (!p
|| l
< 7) return false;
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;
1656 if (p
[1] >= '6') return false;
1657 if (p
[1] == '5' && p
[2] > '5') return false;
1661 if (p
[lablen
] != '.') return false;
1670 static bool parse_ipv4_name(const void *_p
, size_t l
, uint8_t ipv4
[4])
1672 unsigned short s
[4];
1674 const char *p
= (char *)_p
;
1675 if (!IsIPv4Name(p
, l
) || l
> 15) return false;
1676 memcpy(ipv4str
, p
, l
);
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];
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
;
1693 if (!p
) return false;
1694 if (l
>= 1 && p
[0] == '[') {
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
);
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};
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 */
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 */
1739 static bool read_der_atom(const data_t
*d
, der_atom_t
*o
)
1744 if (!d
|| !d
->d
|| !d
->l
|| !o
) return false;
1745 o
->clas
= (*d
->d
>> 6) & 0x3;
1746 o
->cons
= (*d
->d
>> 5) & 0x1;
1753 if (pos
>= d
->l
) return false;
1757 } while (byte
& 0x80);
1760 if (pos
>= d
->l
) return false;
1763 unsigned cnt
= byte
& 0x7f;
1764 if (!cnt
|| pos
+ cnt
> d
->l
) return false;
1773 if (pos
+ len
> d
->l
) return false;
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
)
1787 if (!_d
|| !_d
->d
|| !_d
->l
) return false;
1792 if (!read_der_atom(&d
, &atom
)) return false;
1795 if ((atom
.rawtag
& 0xfe) != 0x30) {
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:
1816 * } == length of data
1818 static bool check_der_pubkey(const data_t
*_d
)
1823 if (!_d
|| !_d
->d
|| !_d
->l
) return false;
1824 if (!is_der(_d
, true)) return false;
1827 if (!read_der_atom(&d
, &atom
)) return false;
1828 if (atom
.rawtag
!= 0x30) return false;
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;
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
)
1845 return memcmp(o1
->d
, o2
->d
, o1
->l
) == 0;
1848 static bool read_der_cert(const data_t
*_d
, der_cert_t
*o
)
1853 if (!_d
|| !_d
->d
|| !_d
->l
|| !o
) return false;
1854 if (!is_der(_d
, true)) return false;
1857 memset(o
, 0, sizeof(*o
));
1858 if (!read_der_atom(&d
, &atom
)) return false;
1859 if (atom
.rawtag
!= 0x30) return false;
1862 if (!read_der_atom(&d
, &atom
)) return false;
1863 if (atom
.rawtag
!= 0x30) return false;
1866 if (!read_der_atom(&d
, &atom
)) return false;
1867 if (atom
.rawtag
== 0xA0) {
1870 if (atom
.dl
!= 3 || d
.d
[0] != 2 || d
.d
[1] != 1) return false;
1871 o
->vers
= d
.d
[2]; /* not validated */
1874 if (!read_der_atom(&d
, &atom
)) return false;
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;
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 */
1898 vdate
.d
= d
.d
+ atom
.hl
;
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;
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
;
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
;
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
;
1944 if (!read_der_atom(&d
, &atom
)) return false;
1946 if (atom
.rawtag
!= 0xA3) return false;
1947 /* found v3 extensions */
1950 if (!read_der_atom(&d
, &atom
)) return false;
1951 if (atom
.rawtag
!= 0x30) return false;
1957 if (!read_der_atom(&d
, &atom
)) return false;
1958 if (atom
.rawtag
!= 0x30) return false;
1961 if (!read_der_atom(&d
, &atom
)) return false;
1962 if (atom
.rawtag
!= 6) return false;
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;
1983 if (data_matches(&oid
, &OID_BasicConstraints
)) {
1984 if (!read_der_atom(&value
, &atom
)) return false;
1985 if (atom
.rawtag
!= 0x30) return false;
1989 /* CA flag is false and was properly omitted */
1990 o
->caFlag
= crit
? 0x80 : 0;
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;
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
;
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;
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
;
2032 Boolean
CheckCertOkay(SecCertificateRef _cert
)
2034 CFDataRef d
= cSecCertificateCopyData(_cert
);
2039 if (!d
) return false;
2040 data
.d
= CFDataGetBytePtr(d
);
2041 data
.l
= CFDataGetLength(d
);
2042 ans
= read_der_cert(&data
, &cert
);
2047 /* flags & 0x01 to extract pub keys from certificates */
2048 static Boolean
CheckPubKeyOkayInt(CFDataRef d
, data_t
*pubkey
, int flags
)
2052 if (!d
|| !pubkey
) return false;
2053 data
.d
= CFDataGetBytePtr(d
);
2054 data
.l
= CFDataGetLength(d
);
2055 if (check_der_pubkey(&data
)) {
2061 if (read_der_cert(&data
, &cert
)) {
2062 *pubkey
= cert
.subjectPubKey
;
2069 Boolean
CheckPubKeyOkay(CFDataRef d
)
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
);
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);
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
) {
2107 sprintf(hexbyte
, "%02X%s", (unsigned)key
->d
[i
], i
+1 == key
->l
? "" : ":");
2108 CFStringAppendCString(ans
, hexbyte
, kCFStringEncodingASCII
);
2113 if (d
) CFRelease(d
);
2114 if (!good
&& ans
) {CFRelease(ans
); ans
=NULL
;}
2118 typedef struct oid_entry_s
{
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
;
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;
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
;
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
);
2181 CFStringAppendCString(s
, ans
->name
, kCFStringEncodingASCII
);
2183 CFMutableStringRef temp
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2184 const uint8_t *oid
= (uint8_t *)_oid
;
2187 const uint8_t *orig_oid
= oid
;
2188 if (!temp
|| l
< 3 || *oid
!= 6)
2195 if (!read_der_atom(&data
, &atom
))
2197 if (!bad
&& (atom
.rawtag
!= 6 || atom
.dl
< 1))
2200 oid
= data
.d
+ atom
.hl
;
2202 if (l
+ atom
.hl
!= orig_l
)
2212 if (!l
) {bad
=true; break;}
2216 idval
|= byte
& 0x7f;
2217 } while (!bad
&& (byte
& 0x80));
2224 } else if (idval
< 80) {
2225 x
= 1; y
= idval
- 40;
2227 x
= 2; y
= idval
- 80;
2229 snprintf(twoids
, sizeof(twoids
), "%u.%u", x
, y
);
2230 CFStringAppendCString(temp
, twoids
, kCFStringEncodingASCII
);
2234 snprintf(oneid
, sizeof(oneid
), ".%u", idval
);
2235 CFStringAppendCString(temp
, oneid
, kCFStringEncodingASCII
);
2238 } while (l
&& !bad
);
2240 if (bad
|| l
|| !temp
|| !CFStringGetLength(temp
))
2241 append_hex_dump(s
, orig_oid
, orig_l
);
2243 CFStringAppend(s
, 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
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
;
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;
2296 if (flags
& 0x05) return false;
2297 append_hex_dump(*s
, d
, a
->hl
+ a
->dl
);
2300 if (flags
& 0x04 && !is_dns_name(d
+a
->hl
, a
->dl
, !!(flags
& 0x08)))
2302 temp
= CFStringCreateWithBytes(kCFAllocatorDefault
, d
+a
->hl
, a
->dl
,
2306 *s
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2308 CFStringAppend(*s
, temp
);
2311 if (flags
& 0x05) return false;
2312 append_hex_dump(*s
, d
, a
->hl
+ a
->dl
);
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') {
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');
2337 uc
[2] = (unsigned char)vstr
[0];
2338 uc
[3] = (unsigned char)vstr
[1];
2346 CFStringAppendCharacters(cfstr
, uc
, 4);
2349 uc
[2] = vstr
[off
+1];
2351 CFStringAppendCharacters(cfstr
, uc
, 4);
2352 uc
[0] = vstr
[off
+2];
2353 uc
[1] = vstr
[off
+3];
2355 CFStringAppendCharacters(cfstr
, uc
, 3);
2356 uc
[0] = vstr
[off
+4];
2357 uc
[1] = vstr
[off
+5];
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];
2366 CFStringAppendCharacters(cfstr
, uc
, 3);
2368 CFStringAppendCString(cfstr
, vstr
, kCFStringEncodingASCII
);
2372 void CopyCertValidity(SecCertificateRef _cert
, CFStringRef
*_nb
, CFStringRef
*_na
)
2374 CFDataRef d
= cSecCertificateCopyData(_cert
);
2375 CFMutableStringRef nb
, na
;
2379 if (!d
|| !_nb
|| !_na
) return;
2382 data
.d
= CFDataGetBytePtr(d
);
2383 data
.l
= CFDataGetLength(d
);
2384 if (!read_der_cert(&data
, &cert
)) {
2389 nb
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2392 na
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2397 append_year_string(nb
, cert
.notBefore
);
2398 append_year_string(na
, cert
.notAfter
);
2403 static CFStringRef
CopyCertName(SecCertificateRef _cert
, bool issuer
)
2405 CFDataRef d
= cSecCertificateCopyData(_cert
);
2406 CFMutableStringRef ans
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
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
;
2423 if (data
.d
&& data
.l
) {
2424 if (!read_der_atom(&data
, &atom
)) break;
2425 if (atom
.rawtag
!= 0x30) break;
2430 unsigned setidx
= 0;
2432 if (!read_der_atom(&data
, &atom
)) break;
2433 if (atom
.rawtag
!= 0x31) break;
2434 set
.d
= data
.d
+ atom
.hl
;
2436 data
.l
-= atom
.hl
+ atom
.dl
;
2437 data
.d
+= atom
.hl
+ atom
.dl
;
2440 if (!read_der_atom(&set
, &atom
)) break;
2441 if (atom
.rawtag
!= 0x30) break;
2444 if (!read_der_atom(&set
, &atom
)) break;
2445 if (atom
.rawtag
!= 6) break;
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
;
2462 if (badset
|| data
.l
) 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
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
)
2502 CFTypeRef ans
= arr
?
2503 (CFTypeRef
)CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
) :
2504 (CFTypeRef
)CFStringCreateMutable(kCFAllocatorDefault
, 0);
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;
2519 if (!read_der_atom(&data
, &atom
)) break;
2520 if (atom
.rawtag
== 0x82 && (flags
& 0x01)) {
2522 if (!arr
&& CFStringGetLength((CFStringRef
)ans
))
2523 CFStringAppendCString((CFMutableStringRef
)ans
, ",", kCFStringEncodingASCII
);
2524 temp
= CFStringCreateWithBytes(kCFAllocatorDefault
, data
.d
+atom
.hl
,
2525 atom
.dl
, kCFStringEncodingASCII
, true);
2528 CFArrayAppendValue((CFMutableArrayRef
)ans
, temp
);
2530 CFStringAppend((CFMutableStringRef
)ans
, 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))) {
2537 CFDataRef dtemp
= CFDataCreate(kCFAllocatorDefault
, data
.d
+atom
.hl
, atom
.dl
);
2539 CFArrayAppendValue((CFMutableArrayRef
)ans
, dtemp
);
2542 if (CFStringGetLength((CFStringRef
)ans
))
2543 CFStringAppendCString((CFMutableStringRef
)ans
, ",", kCFStringEncodingASCII
);
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
)))
2553 CFStringAppendCString((CFMutableStringRef
)ans
, ntopbuff
, kCFStringEncodingASCII
);
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
;
2563 if (!data
.l
&& ( (arr
&& CFArrayGetCount((CFArrayRef
)ans
) ) ||
2564 (!arr
&& CFStringGetLength((CFStringRef
)ans
)) ))
2567 if (!good
&& ans
) {CFRelease(ans
); ans
=NULL
;}
2571 static CFArrayRef
CopyCertSubjectCNIds(const der_cert_t
*cert
)
2573 CFMutableArrayRef ans
=
2574 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
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;
2590 if (!read_der_atom(&data
, &atom
)) break;
2591 if (atom
.rawtag
!= 0x31) break;
2592 set
.d
= data
.d
+ atom
.hl
;
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
;
2603 if (!read_der_atom(&set
, &atom
)) break;
2604 if (atom
.rawtag
!= 6) break;
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
);
2621 if (ans
&& (!good
|| !CFArrayGetCount(ans
))) {
2628 CFStringRef
CopyCertSubjectAltNamesString(SecCertificateRef _cert
)
2630 CFDataRef d
= cSecCertificateCopyData(_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);
2641 return MakeVisibleString(ans
);
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
;
2654 return ans
= (CFArrayRef
)CopyCertSubjectAltNamesInt(c
, true, 0x02);
2656 return ans
= (CFArrayRef
)CopyCertSubjectAltNamesInt(c
, true, 0x04);
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
)) {
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;
2683 if ((pinnedKeySet
&& !CFArrayGetCount(pinnedKeySet
)) || (pkonly
&& !pinnedKeySet
))
2685 if (nameonly
&& !pkonly
&& (!peername
|| !*peername
))
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
);
2695 err
= SecTrustCopyAnchorCertificates(&anchors
);
2696 if (err
) return err
;
2698 err
= SecTrustSetAnchorCertificates(trust
, 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
;
2706 err
= cSecTrustGetResult(trust
, &result
, &chain
, &evidence
);
2709 if (chain
) CFRelease(chain
);
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));
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
);
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;
2746 /* if we can't parse peername it can't possibly match! */
2747 err
= errSSLHostNameMismatch
;
2749 CFArrayRef ids
= CopyCertSubjectIds(&cert
, mode
);
2750 if (ids
&& !CFArrayGetCount(ids
)) {
2755 /* if we don't have anything to match against it can't possibly match! */
2756 err
= errSSLHostNameMismatch
;
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
);
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) {
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
))
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
;
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 */
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
))
2825 if ((flags
& CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
) &&
2826 (evidence
[i
].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
))
2828 if (evidence
[i
].StatusBits
& CSSM_CERT_STATUS_EXPIRED
) {
2830 return errSSLCertExpired
;
2832 if (evidence
[i
].StatusBits
& CSSM_CERT_STATUS_NOT_VALID_YET
) {
2834 return errSSLCertNotYetValid
;
2838 /* check for no root */
2839 if (!(evidence
[cnt
-1].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
)) {
2841 return errSSLNoRootCert
;
2843 /* check for unknown root */
2844 if (!(evidence
[cnt
-1].StatusBits
& CSSM_CERT_STATUS_IS_IN_ANCHORS
)) {
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
)) {
2854 return errSSLNoRootCert
;
2857 /* everything looks good, so check the trust result code now */
2859 case kSecTrustResultProceed
:
2860 case kSecTrustResultUnspecified
:
2863 case kSecTrustResultDeny
:
2866 return errSecTrustSettingDeny
;
2868 /* everything else (confirm, invalid, recoverable, fatal, other) */
2870 return errSecNotTrusted
;
2874 CFDataRef certder
= cSecCertificateCopyData(
2875 (SecCertificateRef
)CFArrayGetValueAtIndex(chain
, 0));
2878 CFDataRef peerPubKey
;
2881 return errSSLBadCert
;
2882 der
.d
= CFDataGetBytePtr(certder
);
2883 der
.l
= CFDataGetLength(certder
);
2884 if (!read_der_cert(&der
, &cert
)) {
2886 return errSSLBadCert
;
2888 if (certFlags
& 0x08) {
2889 unsigned char sha256
[CC_SHA256_DIGEST_LENGTH
];
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
));
2896 peerPubKey
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
2897 cert
.subjectPubKey
.d
, cert
.subjectPubKey
.l
, kCFAllocatorNull
);
2903 pinok
= BlobInArray(peerPubKey
, pinnedKeySet
);
2904 CFRelease(peerPubKey
);
2907 return errSecPinnedKeyMismatch
;
2913 #elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
2915 #error iOS is not currently supported
2917 #endif /* TARGET_OS_EMBEDDED || TARGET_OS_IPHONE */