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)) */
43 typedef struct data_s
{
48 extern CFStringRef
CFStringCreateWithBytesNoCopy(
52 CFStringEncoding encoding
,
53 Boolean isExternalRepresentation
,
54 CFAllocatorRef contentsDeallocator
); /* available 10.4 but not in header */
55 extern CFStringRef
NSTemporaryDirectory(void);
56 static Boolean
CheckPubKeyOkayInt(CFDataRef d
, data_t
*pubkey
, int flags
);
57 static pthread_mutex_t keych_mutex
= PTHREAD_MUTEX_INITIALIZER
;
59 /* Macro result is true on success */
60 #define MUTEX_LOCK() (!pthread_mutex_lock(&keych_mutex))
61 #define MUTEX_UNLOCK() (!pthread_mutex_unlock(&keych_mutex))
63 static CFStringRef
CFCopyTemporaryDirectory(void)
65 id pool
= objc_msgSend(objc_getClass("NSAutoreleasePool"), sel_getUid("new"));
66 CFStringRef dir
= (CFStringRef
)NSTemporaryDirectory();
67 if (dir
) CFRetain(dir
);
68 objc_msgSend(pool
, sel_getUid("drain"));
70 unsigned len
= (unsigned)CFStringGetLength(dir
);
73 (unsigned)CFStringGetCharacterAtIndex(dir
, l
-1) == (unsigned)'/') {
77 CFStringRef old
= dir
;
78 dir
= CFStringCreateWithSubstring(
79 kCFAllocatorDefault
, dir
, CFRangeMake(0, l
));
89 char *CFStringCreateUTF8String(CFStringRef s
, Boolean release
)
95 m
= (size_t)CFStringGetMaximumSizeForEncoding(
96 CFStringGetLength(s
), kCFStringEncodingUTF8
) + 1;
97 c
= (char *)malloc(m
);
99 if (release
) CFRelease(s
);
102 if (!CFStringGetCString(s
, c
, m
, kCFStringEncodingUTF8
)) {
106 if (release
) CFRelease(s
);
110 static CFStringRef
MakeVisibleString(CFStringRef in
)
112 CFStringRef nullbyte
;
113 CFMutableStringRef m
;
115 nullbyte
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, (UInt8
*)"\0",
116 1, kCFStringEncodingASCII
, false, kCFAllocatorNull
);
117 if (!nullbyte
) return in
;
118 m
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, in
);
121 CFStringFindAndReplace(m
, nullbyte
, CFSTR("\\000"),
122 CFRangeMake(0, CFStringGetLength(m
)), 0);
127 CFDataRef
CFDataCreateWithContentsOfFile(CFAllocatorRef a
, const char *f
)
130 CFMutableDataRef d
= CFDataCreateMutable(a
, 0);
134 fd
= open(f
, O_RDONLY
);
140 cnt
= read(fd
, buff
, sizeof(buff
));
141 if (cnt
> 0) CFDataAppendBytes(d
, (UInt8
*)buff
, (size_t)cnt
);
152 #define memmem(v1,l1,v2,l2) cmemmem(v1,l1,v2,l2)
153 static void *cmemmem(const void *_m
, size_t ml
, const void *_s
, size_t sl
)
155 const char *m
= (const char *)_m
;
156 const char *s
= (const char *)_s
;
157 if (!ml
|| !sl
|| ml
< sl
) return NULL
;
158 if (sl
== 1) return memchr(m
, *s
, ml
);
159 if (ml
== sl
) return (void *)(memcmp(m
, s
, sl
) ? NULL
: m
);
162 const char *p
= memchr(m
, *s
, ml
);
167 if (ml
< sl
) return NULL
;
168 if (!memcmp(m
, s
, sl
)) return (void *)m
;
175 CF_INLINE
int is_eol(int c
)
177 return c
== '\n' || c
== '\r';
180 CF_INLINE
int is_lb(int c
)
182 return c
== '\n' || c
== '\r' || c
== '\f';
185 CF_INLINE
int is_prnt(int c
)
187 return c
>= ' ' && c
<= '~';
190 CF_INLINE
int is_spc(int c
)
192 return c
== ' ' || c
== '\t';
195 static int has_lb(const void *_m
, size_t l
)
197 const char *m
= (const char *)_m
;
199 if (is_lb(*m
)) return 1;
206 static int has_prnt(const void *_m
, size_t l
)
208 const char *m
= (const char *)_m
;
210 if (!is_prnt(*m
)) return 0;
218 * returns start of "-----BEGIN XXXX-----\n" line or NULL if not found/error
219 * If returns NULL then *ot == 0 means not found, *ot == -1 means bad line
220 * If returns non-NULL then *ol is length through "-----\n" and *ot is length
221 * of "XXXX" part (which obviously starts at return value + 11 for BEGIN or
223 * If e is non-zero look for ----END rather than ----BEGIN
225 static const char *find_be(const void *_m
, size_t l
, size_t *ol
, int *ot
, int e
)
227 const char *m
= (const char *)_m
;
228 const char *origm
= m
;
229 const char *marker
= e
? "-----END " : "-----BEGIN ";
230 size_t mkl
= e
? 9 : 11;
234 const char *p
= (char *)memmem(m
, l
, marker
, mkl
);
238 if (p
> origm
&& !is_eol(p
[-1])) continue;
239 t
= (char *)memmem(m
, l
, "-----", 5);
242 if (l
> 5 && !is_eol(t
[5])) continue;
243 if (has_lb(p
, t
-p
)) continue;
244 if ((size_t)(t
-p
) > (76 - mkl
- 5) || !has_prnt(p
, t
-p
)) {
251 if (l
&& *m
== '\r') {
255 if (l
&& *m
== '\n') {
265 typedef enum pemtype_e
{
267 pemtype_certificate
, /* "CERTIFICATE" or "TRUSTED CERTIFICATE" or "X509 CERTIFICATE" */
268 pemtype_crl
, /* "X509 CRL" */
269 pemtype_publickey
, /* "PUBLIC KEY" */
270 pemtype_privatekey_rsa
/* "RSA PRIVATE KEY" */
273 typedef struct peminfo_s
{
274 const char *start
; /* Armour start "-----BEGIN XXXXX-----\n" */
275 size_t len
; /* Length through armour end "-----END XXXXX-----\n" */
276 /* Body starts after "-----BEGIN XXXXX-----\n" */
278 size_t bodylen
; /* Length though "\n" BEFORE final "-----END XXXXX-----\n" */
279 /* Kind starts at start + 11 */
280 size_t kindlen
; /* length of "XXXXX" from "-----BEGIN XXXXX-----\n" */
284 static int nextpem(const char *p
, size_t l
, peminfo_t
*o
)
286 size_t beglen
, endlen
;
287 int begtype
, endtype
;
289 const char *beg
= find_be(p
, l
, &beglen
, &begtype
, 0);
290 if (!beg
) return begtype
;
291 end
= find_be(p
+ beglen
, l
- beglen
, &endlen
, &endtype
, 1);
292 if (!end
|| begtype
!= endtype
|| memcmp(beg
+11, end
+9, (size_t)begtype
))
295 o
->len
= (end
+ endlen
) - beg
;
296 o
->body
= beg
+ beglen
;
297 o
->bodylen
= end
- (beg
+ beglen
);
298 o
->kindlen
= (size_t)begtype
;
299 if (begtype
== 11 && !memcmp(beg
+ 11, "CERTIFICATE", 11)) {
300 o
->type
= pemtype_certificate
;
301 } else if (begtype
== 19 && !memcmp(beg
+ 11, "TRUSTED CERTIFICATE", 19)) {
302 o
->type
= pemtype_certificate
;
303 } else if (begtype
== 16 && !memcmp(beg
+ 11, "X509 CERTIFICATE", 16)) {
304 o
->type
= pemtype_certificate
;
305 } else if (begtype
== 15 && !memcmp(beg
+ 11, "RSA PRIVATE KEY", 15)) {
306 o
->type
= pemtype_privatekey_rsa
;
307 } else if (begtype
== 10 && !memcmp(beg
+ 11, "PUBLIC KEY", 10)) {
308 o
->type
= pemtype_publickey
;
309 } else if (begtype
== 8 && !memcmp(beg
+ 11, "X509 CRL", 8)) {
310 o
->type
= pemtype_crl
;
312 o
->type
= pemtype_unknown
;
314 return (int)((o
->start
+ o
->len
) - p
);
317 #define BASE64CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
318 #define BASE64SIZE(n) ((((unsigned)(n)+2)/3)*4)
320 static const signed char b64tab
[256] = {
321 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
322 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
323 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x3E,-1,-1,-1,0x3F,
324 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,-1,-1,-1,0x40,-1,-1,
325 -1,0,1,2,3,4,5,6,7,8,9,0x0A,0x0B,0x0C,0x0D,0x0E,
326 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,-1,-1,-1,-1,-1,
327 -1,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
328 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,-1,-1,-1,-1,-1,
329 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
330 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
331 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
332 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
333 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
334 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
335 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
336 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
339 static void convert24(const uint8_t *input
, uint8_t *output
)
341 output
[0] = ((b64tab
[input
[0]]&0x3F)<<2)|((b64tab
[input
[1]]&0x3F)>>4);
342 output
[1] = (((b64tab
[input
[1]]&0x3F)&0x0F)<<4)|((b64tab
[input
[2]]&0x3F)>>2);
343 output
[2] = (((b64tab
[input
[2]]&0x3F)&0x03)<<6)|(b64tab
[input
[3]]&0x3F);
346 static CFDataRef
CFDataCreateFromBase64(CFAllocatorRef a
, const void *_b
, size_t l
)
352 const uint8_t *p
= (uint8_t *)_b
;
353 if (l
&& !p
) return NULL
;
354 d
= CFDataCreateMutable(a
, 0);
357 for (i
=0; l
; ++p
, --l
) {
359 if (c
== ' ' || c
== '\t' || c
== '\r' || c
== '\n' || c
== '\f')
370 CFDataAppendBytes(d
, out
, inp
[2] == '=' ? 1 : 2);
373 CFDataAppendBytes(d
, out
, 3);
383 static SecCertificateRef
createvalidcert(CFDataRef d
)
385 SecCertificateRef cert
= cSecCertificateCreateWithData(kCFAllocatorDefault
, d
);
386 if (!cert
) return NULL
;
387 if (!CheckCertOkay(cert
)) {
394 CFArrayRef
CreateCertsArrayWithData(CFDataRef d
, const errinfo_t
*e
)
396 const char *certs
, *p
;
397 size_t certslen
, plen
, cnt
= 1;
400 certs
= (char *)CFDataGetBytePtr(d
);
401 certslen
= (size_t)CFDataGetLength(d
);
402 a
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
408 int readcnt
= nextpem(p
, plen
, &pem
);
409 if (!readcnt
&& p
== certs
) {
410 /* assume it's a DER cert */
411 SecCertificateRef cert
;
412 CFDataRef der
= CFDataCreate(kCFAllocatorDefault
, (UInt8
*)certs
, certslen
);
417 cert
= createvalidcert(der
);
421 e
->f(e
->u
, "Invalid CA certificate bad DER data");
425 CFArrayAppendValue(a
, cert
);
428 } else if (readcnt
== -1) {
430 e
->f(e
->u
, "Invalid CA certificate #%u (offset %u) in bundle",
431 (unsigned)cnt
, (unsigned)(p
-certs
));
434 } else if (readcnt
&& pem
.type
== pemtype_certificate
) {
435 CFDataRef der
= CFDataCreateFromBase64(kCFAllocatorDefault
, pem
.body
, pem
.bodylen
);
436 SecCertificateRef cert
;
439 e
->f(e
->u
, "Invalid CA certificate #%u (offset %u) bad base 64 in bundle",
440 (unsigned)cnt
, (unsigned)(pem
.start
-certs
));
444 cert
= createvalidcert(der
);
448 e
->f(e
->u
, "Invalid CA certificate #%u (offset %u) bad cert data in bundle",
449 (unsigned)cnt
, (unsigned)(pem
.start
-certs
));
453 CFArrayAppendValue(a
, cert
);
456 } else if (!readcnt
) break;
457 plen
-= (pem
.start
+ pem
.len
) - p
;
458 p
= pem
.start
+ pem
.len
;
460 if (!CFArrayGetCount(a
)) {
467 static void CFArrayAppendValue_data(CFMutableArrayRef a
, const data_t
*d
)
469 if (d
&& d
->d
&& d
->l
) {
470 CFDataRef data
= CFDataCreate(kCFAllocatorDefault
, (UInt8
*)d
->d
, d
->l
);
472 CFArrayAppendValue(a
, data
);
478 CFArrayRef
CreatePubKeyArrayWithData(CFDataRef d
, const errinfo_t
*e
)
480 const char *keys
, *p
;
481 size_t keyslen
, plen
, cnt
= 1;
484 keys
= (char *)CFDataGetBytePtr(d
);
485 keyslen
= (size_t)CFDataGetLength(d
);
486 a
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
493 int readcnt
= nextpem(p
, plen
, &pem
);
494 if (!readcnt
&& p
== keys
) {
495 /* assume it's a DER public key */
496 CFDataRef der
= CFDataCreate(kCFAllocatorDefault
, (UInt8
*)keys
, keyslen
);
501 if (!CheckPubKeyOkayInt(der
, &pubkey
, 0x01)) {
504 e
->f(e
->u
, "Invalid public key bad DER data");
508 CFArrayAppendValue_data(a
, &pubkey
);
511 } else if (readcnt
== -1) {
513 e
->f(e
->u
, "Invalid public key #%u (offset %u) in bundle",
514 (unsigned)cnt
, (unsigned)(p
-keys
));
517 } else if (readcnt
&& (pem
.type
== pemtype_publickey
||
518 pem
.type
== pemtype_certificate
)) {
519 CFDataRef der
= CFDataCreateFromBase64(kCFAllocatorDefault
, pem
.body
, pem
.bodylen
);
522 e
->f(e
->u
, "Invalid public key #%u (offset %u) bad base 64 in bundle",
523 (unsigned)cnt
, (unsigned)(pem
.start
-keys
));
527 if (!CheckPubKeyOkayInt(der
, &pubkey
, 0x01)) {
530 e
->f(e
->u
, "Invalid public key #%u (offset %u) bad public key data in bundle",
531 (unsigned)cnt
, (unsigned)(pem
.start
-keys
));
535 CFArrayAppendValue_data(a
, &pubkey
);
538 } else if (!readcnt
) break;
539 plen
-= (pem
.start
+ pem
.len
) - p
;
540 p
= pem
.start
+ pem
.len
;
542 if (!CFArrayGetCount(a
)) {
549 static void skip_space_semi(const char **pstr
)
551 while (is_spc(**pstr
)) ++(*pstr
);
552 if (**pstr
== ';') ++(*pstr
);
553 while (is_spc(**pstr
)) ++(*pstr
);
556 static bool skip_sha256_prefix(const char **pstr
)
558 skip_space_semi(pstr
);
559 if (**pstr
!= 's' && **pstr
!= 'S') return 0;
561 if (**pstr
!= 'h' && **pstr
!= 'H') return 0;
563 if (**pstr
!= 'a' && **pstr
!= 'A') return 0;
565 if (!strncmp(*pstr
, "256//", 5)) {
572 Boolean
IsSha256HashList(const char *hashlist
)
574 if (!hashlist
|| !*hashlist
)
576 return skip_sha256_prefix(&hashlist
);
579 CFArrayRef
CreatePubKeySha256Array(const char *hashlist
, const errinfo_t
*e
)
581 const char *orig
= hashlist
;
582 CFMutableArrayRef a
= NULL
;
583 if (!hashlist
|| !*hashlist
)
585 a
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
590 if (!skip_sha256_prefix(&hashlist
)) {
592 e
->f(e
->u
, "Invalid public key sha256 base64 hash -- "
593 "missing \"sha256//\" prefix at string offset %u",
594 (unsigned)(hashlist
-orig
));
598 l
= strspn(hashlist
, BASE64CHARS
);
599 if (l
!= BASE64SIZE(CC_SHA256_DIGEST_LENGTH
)) {
601 e
->f(e
->u
, "Invalid public key sha256 base64 hash -- "
602 "wrong base64 length %u (should be %u) at string offset %u",
603 (unsigned)l
, (unsigned)BASE64SIZE(CC_SHA256_DIGEST_LENGTH
),
604 (unsigned)(hashlist
-orig
));
608 blob
= CFDataCreateFromBase64(kCFAllocatorDefault
, hashlist
, l
);
611 e
->f(e
->u
, "out of memory");
615 if ((size_t)CFDataGetLength(blob
) != (size_t)CC_SHA256_DIGEST_LENGTH
) {
617 e
->f(e
->u
, "Invalid public key sha256 base64 hash -- "
618 "wrong hash length %u (should be %u) at string offset %u",
619 (unsigned)CFDataGetLength(blob
), (unsigned)CC_SHA256_DIGEST_LENGTH
,
620 (unsigned)(hashlist
-orig
));
626 CFArrayAppendValue(a
, blob
);
628 skip_space_semi(&hashlist
);
629 if (!*hashlist
) break;
631 if (!CFArrayGetCount(a
)) {
638 typedef struct homedirs_s
{
639 /* note that we use geteuid and not getuid because any created files will
640 * end up being owned by geteuid and therefore using geteuid()'s HOME wil
641 * end up being the least disruptive and also if geteuid() != getuid() then
642 * we probably can't read getuid()'s HOME anyway so that's a guaranteed fail */
643 char *home
; /* "HOME=..." from getpwuid(geteuid()) if different from environ */
644 char *cur_home
; /* "HOME=..." as found in environ if home set otherwise NULL */
647 static char *find_home_env(void)
649 char ***eptr
= _NSGetEnviron();
651 if (!eptr
) return NULL
;
653 while (*ptr
&& strncmp(*ptr
, "HOME=", 5)) {
656 return *ptr
? *ptr
: NULL
;
659 static void get_home_dirs(homedirs_t
*dirs
)
661 struct passwd
*pwinf
;
665 dirs
->cur_home
= find_home_env();
666 pwinf
= getpwuid(geteuid());
667 if (pwinf
&& pwinf
->pw_dir
&&
668 (!dirs
->cur_home
|| strcmp(dirs
->cur_home
+5, pwinf
->pw_dir
)))
669 asprintf(&dirs
->home
, "HOME=%s", pwinf
->pw_dir
);
671 dirs
->cur_home
= NULL
;
674 static void free_home_dirs(homedirs_t
*dirs
)
680 typedef struct tempch_s
{
683 char pw
[16]; /* random 15-character (0x20-0x7e), NULL terminated password */
684 char loc
[1]; /* Always will have at least a NULL byte */
687 static void gen_rand_pw(void *_out
, size_t len
)
689 unsigned char *out
= (unsigned char *)_out
;
691 if (!out
|| !len
) return;
692 fd
= open("/dev/random", O_RDONLY
);
697 cnt
= read(fd
, out
, len
);
698 } while (cnt
== -1 && errno
== EINTR
);
699 if (cnt
<= 0) return;
700 for (i
= 0; i
< cnt
; ++i
) {
701 out
[i
] = (unsigned char)((((unsigned)out
[i
] * 95) >> 8) + 32);
709 static tempch_t
*new_temp_keych(void)
712 char newdir
[PATH_MAX
];
714 CFStringRef tempdir
= CFCopyTemporaryDirectory();
716 if (!tempdir
) return NULL
;
717 okay
= CFStringGetCString(tempdir
, newdir
, sizeof(newdir
) - 32, kCFStringEncodingUTF8
);
719 if (!okay
) return NULL
;
720 strcat(newdir
, "/tch.XXXXXX");
721 ans
= (tempch_t
*)malloc(sizeof(tempch_t
) + strlen(newdir
) + 14 /* "/temp.keychain" */);
722 if (!ans
) return NULL
;
724 strcpy(ans
->loc
, newdir
);
725 strlcpy(ans
->pw
, "(:vCZ\"t{UA-zl3g", sizeof(ans
->pw
)); /* fallback if random fails */
726 gen_rand_pw(ans
->pw
, sizeof(ans
->pw
)-1);
727 ans
->pw
[sizeof(ans
->pw
)-1] = '\0';
728 if (!mkdtemp(ans
->loc
)) {
732 strcat(ans
->loc
, "/temp.keychain");
733 get_home_dirs(&ans
->dirs
);
737 static void del_temp_keych(tempch_t
*keych
)
741 l
= strlen(keych
->loc
);
742 if (l
> 14 && !strcmp(keych
->loc
+ (l
- 14), "/temp.keychain")) {
747 if (keych
->dirs
.home
) {
748 keych
->dirs
.cur_home
= find_home_env();
749 if (!keych
->dirs
.cur_home
|| strcmp(keych
->dirs
.cur_home
, keych
->dirs
.home
)) {
751 putenv(keych
->dirs
.home
);
754 (void)SecKeychainLock(keych
->ref
);
755 (void)SecKeychainDelete(keych
->ref
);
757 if (keych
->dirs
.cur_home
)
758 putenv(keych
->dirs
.cur_home
);
762 CFRelease(keych
->ref
);
764 (void)MUTEX_UNLOCK();
767 keych
->loc
[l
- 14] = '\0';
768 /* the keychain code may leave dot, possibly comma and yet other turds
769 * we may have to remove */
770 d
= opendir(keych
->loc
);
773 while ((ent
=readdir(d
)) != NULL
) {
775 if (ent
->d_name
[0] == '.' &&
776 (ent
->d_name
[1] == '\0'
777 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0'))) continue;
778 snprintf(turd
, sizeof(turd
), "%s/%s", keych
->loc
, ent
->d_name
);
784 free_home_dirs(&keych
->dirs
);
789 static CFDataRef
extract_key_copy(CFDataRef pemseq
, int *outpem
)
791 const char *p
= (char *)CFDataGetBytePtr(pemseq
);
792 const char *origp
= p
;
793 size_t l
= (size_t)CFDataGetLength(pemseq
);
798 int readcnt
= nextpem(p
, l
, &pem
);
799 if (!readcnt
&& p
== origp
) {
800 /* Assume it's DER data */
804 if (!readcnt
|| readcnt
== -1) return NULL
;
805 if (pem
.type
== pemtype_privatekey_rsa
) {
807 if (pem
.start
== origp
&& pem
.len
== origl
) {
811 return CFDataCreate(kCFAllocatorDefault
, (uint8_t *)pem
.start
, pem
.len
);
813 l
-= (size_t)readcnt
;
814 p
+= (size_t)readcnt
;
819 SecIdentityRef
cSecIdentityCreateWithCertificateAndKeyData(
820 SecCertificateRef cert
, CFDataRef keydata
, CFTypeRef pw
, CFStringRef hint
,
824 CFDataRef rawkey
= NULL
;
825 tempch_t
*keych
= NULL
;
827 SecKeychainRef keychain
= NULL
;
828 SecExternalFormat format
;
829 SecExternalItemType type
;
830 SecItemImportExportKeyParameters params
;
831 CFArrayRef items
= NULL
;
832 SecKeyRef key
= NULL
;
833 SecIdentityRef ans
= NULL
;
835 if (!cert
|| !kh
) return NULL
;
837 rawkey
= extract_key_copy(keydata
, &ispem
);
840 CFArrayRef searchlist
= NULL
;
841 keych
= new_temp_keych();
843 /* SecKeychainCreate has the side effect of adding the new keychain to
844 * the search list which will make it show up in other apps.
845 * SecKeychainDelete removes it from the search list, or we can also get
846 * the search list before the create and restore it right after.
847 * By immediately restoring the search list, we avoid having the new
848 * private key we're importing be searchable by default in other apps.
849 * If we are running with HOME != ~geteuid() then we likely have no
850 * ~/Library/Preferences/com.apple.security.plist which means the system
851 * will "helpfully" set the default keychain to this new keychain we've
852 * just created which is very bad. If Xcode is running it will listen
853 * to that event and then call SecKeychainSetDefault with that very
854 * same temporary keychain (I have no idea why it does this stupid thing)
855 * and that will make it permanent for the user. Ugh. To avoid this,
856 * we temporarily set HOME to getpwuid(geteuid())->pw_dir while we are
857 * creating the temporary keychain and then put HOME back the way it was
858 * immediately thereafter. Git likes to run tests with HOME set to
859 * alternate locations so it's prudent to handle this. */
860 if (keych
->dirs
.home
)
861 putenv(keych
->dirs
.home
);
862 err
= SecKeychainCopySearchList(&searchlist
);
863 if (!err
&& searchlist
)
864 err
= SecKeychainCreate(keych
->loc
, sizeof(keych
->pw
), keych
->pw
, false,
868 err
= SecKeychainSetSearchList(searchlist
);
869 CFRelease(searchlist
);
871 if (keych
->dirs
.home
) {
872 if (keych
->dirs
.cur_home
)
873 putenv(keych
->dirs
.cur_home
);
877 if (err
|| !keychain
)
879 keych
->ref
= keychain
;
880 err
= SecKeychainUnlock(keychain
, sizeof(keych
->pw
), keych
->pw
, true);
883 SecKeychainSettings settings
;
884 settings
.version
= SEC_KEYCHAIN_SETTINGS_VERS1
;
885 settings
.lockOnSleep
= false;
886 settings
.useLockInterval
= false;
887 settings
.lockInterval
= INT_MAX
;
888 (void)SecKeychainSetSettings(keychain
, &settings
);
890 format
= ispem
? kSecFormatWrappedOpenSSL
: kSecFormatOpenSSL
;
891 type
= kSecItemTypePrivateKey
;
892 memset(¶ms
, 0, sizeof(params
));
893 params
.version
= SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION
;
894 params
.flags
= kSecKeyImportOnlyOne
|kSecKeyNoAccessControl
;
896 params
.passphrase
= pw
;
898 params
.flags
|= kSecKeySecurePassphrase
;
899 /* Note that params.alertTitle is ignored */
900 params
.alertPrompt
= hint
;
902 err
= cSecItemImport(rawkey
, NULL
, &format
, &type
,
903 ispem
?kSecItemPemArmour
:0, ¶ms
, keychain
, &items
);
905 if (!err
&& items
&& CFArrayGetCount(items
) == 1 &&
906 CFGetTypeID((CFTypeRef
)CFArrayGetValueAtIndex(items
, 0)) == SecKeyGetTypeID()) {
907 key
= (SecKeyRef
)CFArrayGetValueAtIndex(items
, 0);
910 if (items
) CFRelease(items
);
914 /* If we have a key we must also have a keychain */
915 err
= cSecIdentityCreateWithCertificate(keychain
, cert
, &ans
);
918 (void)MUTEX_UNLOCK();
919 /* We MUST NOT call SecKeychainDelete because that will purge all copies of
920 * the keychain from memory. We've already removed it from the search list
921 * so we just release it and remove the disk files instead in order to allow
922 * the in memory copy to remain unmolested. Unfortunately on older systems
923 * this is not good enough, so we have to leave the keychain itself around. */
925 del_temp_keych(keych
);
928 if (!ans
&& (!rawkey
|| (!ispem
&& !key
))) {
929 /* Try again with the default keychain list, but only if a key was not
930 * provided or was provided in non-PEM and we failed to import it. */
931 err
= cSecIdentityCreateWithCertificate(NULL
, cert
, &ans
);
936 del_temp_keych(keych
);
940 void DisposeIdentityKeychainHandle(void *ch
)
942 del_temp_keych((tempch_t
*)ch
);
945 CFArrayRef
CreateClientAuthWithCertificatesAndKeyData(CFArrayRef certs
,
946 CFDataRef keydata
, CFTypeRef pw
,
947 CFStringRef hint
, void **kh
)
949 CFMutableArrayRef ans
;
951 SecCertificateRef cert
;
952 SecIdentityRef identity
;
954 if (!certs
|| !keydata
) return NULL
;
955 count
= (size_t)CFArrayGetCount(certs
);
956 if (count
< 1) return NULL
;
957 cert
= (SecCertificateRef
)CFArrayGetValueAtIndex(certs
, 0);
958 if (CFGetTypeID(cert
) != SecCertificateGetTypeID()) return NULL
;
959 ans
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
960 if (!ans
) return NULL
;
961 identity
= cSecIdentityCreateWithCertificateAndKeyData(cert
, keydata
, pw
,
967 CFArrayAppendValue(ans
, identity
);
968 for (i
= 1; i
< count
; ++i
) {
969 CFArrayAppendValue(ans
, CFArrayGetValueAtIndex(certs
, i
));
974 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
976 #ifndef kCFCoreFoundationVersionNumber10_8
977 #define kCFCoreFoundationVersionNumber10_8 744.00
995 OSStatus (*fSSLSetTrustedRoots
)(SSLContextRef
, CFArrayRef
, Boolean
);
996 OSStatus (*fSSLGetPeerSecTrust
)(SSLContextRef
, SecTrustRef
*);
997 OSStatus (*fSSLCopyPeerTrust
)(SSLContextRef
, SecTrustRef
*);
998 OSStatus (*fSecTrustGetResult
)(SecTrustRef
, SecTrustResultType
*,
999 CFArrayRef
*, CSSM_TP_APPLE_EVIDENCE_INFO
**);
1000 OSStatus (*fSecTrustSetAnchorCertificatesOnly
)(SecTrustRef
, Boolean
);
1001 OSStatus (*fSSLGetPeerCertificates
)(SSLContextRef cxt
, CFArrayRef
*certs
);
1002 OSStatus (*fSSLCopyPeerCertificates
)(SSLContextRef cxt
, CFArrayRef
*certs
);
1003 OSStatus (*fSSLSetProtocolVersionEnabled
)(SSLContextRef cxt
, SmallEnum
, Boolean
);
1004 OSStatus (*fSSLSetProtocolVersionMin
)(SSLContextRef cxt
, SmallEnum
);
1005 OSStatus (*fSSLSetProtocolVersionMax
)(SSLContextRef cxt
, SmallEnum
);
1006 OSStatus (*fSSLSetSessionOption
)(SSLContextRef
, SmallEnum
, Boolean
);
1007 SecCertificateRef (*fSecCertificateCreateWithData
)(CFAllocatorRef
, CFDataRef
);
1008 OSStatus (*fSecCertificateCreateFromData
)(const cCSSM_DATA
*, CSSM_CERT_TYPE
,
1009 CSSM_CERT_ENCODING
, SecCertificateRef
*);
1010 OSStatus (*fSecCertificateGetData
)(SecCertificateRef
, cCSSM_DATA
*);
1011 CFDataRef (*fSecCertificateCopyData
)(SecCertificateRef
);
1012 OSStatus (*fSecKeychainItemImport
)(
1013 CFDataRef
,CFStringRef
,SecExternalFormat
*,SecExternalItemType
*,
1014 SecItemImportExportFlags
,const SecKeyImportExportParameters
*,
1015 SecKeychainRef
,CFArrayRef
*);
1016 OSStatus (*fSecItemImport
)(
1017 CFDataRef
,CFStringRef
,SecExternalFormat
*,SecExternalItemType
*,
1018 SecItemImportExportFlags
,const SecItemImportExportKeyParameters
*,
1019 SecKeychainRef
,CFArrayRef
*);
1020 OSStatus (*fSecIdentityCreateWithCertificate
)(CFTypeRef
,SecCertificateRef
,SecIdentityRef
*);
1021 OSStatus (*fSSLNewContext
)(Boolean
,SSLContextRef
*);
1022 OSStatus (*fSSLDisposeContext
)(SSLContextRef
);
1023 SSLContextRef (*fSSLCreateContext
)(CFAllocatorRef
,SmallEnum
,SmallEnum
);
1024 OSStatus (*fSecKeychainSearchCreateFromAttributes
)(CFTypeRef
,int,
1025 const SecKeychainAttributeList
*,SecKeychainSearchRef
*);
1026 OSStatus (*fSecKeychainSearchCopyNext
)(SecKeychainSearchRef
,SecKeychainItemRef
*);
1029 static void stcompat_initialize(void)
1031 #define LOOKUP(name) *((void **)&fnc.f##name) = dlsym(RTLD_NEXT, #name)
1032 LOOKUP(SSLSetTrustedRoots
);
1033 LOOKUP(SSLGetPeerSecTrust
);
1034 LOOKUP(SSLCopyPeerTrust
);
1035 LOOKUP(SecTrustGetResult
);
1036 LOOKUP(SecTrustSetAnchorCertificatesOnly
);
1037 LOOKUP(SSLGetPeerCertificates
);
1038 LOOKUP(SSLCopyPeerCertificates
);
1039 LOOKUP(SSLSetProtocolVersionEnabled
);
1040 LOOKUP(SSLSetProtocolVersionMin
);
1041 LOOKUP(SSLSetProtocolVersionMax
);
1042 LOOKUP(SSLSetSessionOption
);
1043 LOOKUP(SecCertificateCreateWithData
);
1044 LOOKUP(SecCertificateCreateFromData
);
1045 LOOKUP(SecCertificateGetData
);
1046 LOOKUP(SecCertificateCopyData
);
1047 LOOKUP(SecKeychainItemImport
);
1048 LOOKUP(SecItemImport
);
1049 LOOKUP(SecIdentityCreateWithCertificate
);
1050 LOOKUP(SSLNewContext
);
1051 LOOKUP(SSLDisposeContext
);
1052 LOOKUP(SSLCreateContext
);
1053 LOOKUP(SecKeychainSearchCreateFromAttributes
);
1054 LOOKUP(SecKeychainSearchCopyNext
);
1058 OSStatus
cSSLSetTrustedRoots(SSLContextRef cxt
, CFArrayRef rts
, Boolean replace
)
1060 if (fnc
.fSSLSetTrustedRoots
)
1061 return fnc
.fSSLSetTrustedRoots(cxt
, rts
, replace
);
1065 OSStatus
cSSLCopyPeerTrust(SSLContextRef cxt
, SecTrustRef
*trust
)
1067 if (fnc
.fSSLCopyPeerTrust
)
1068 return fnc
.fSSLCopyPeerTrust(cxt
, trust
);
1069 if (fnc
.fSSLGetPeerSecTrust
) {
1070 OSStatus err
= fnc
.fSSLGetPeerSecTrust(cxt
, trust
);
1078 OSStatus
cSecTrustGetResult(SecTrustRef trust
, SecTrustResultType
*result
,
1079 CFArrayRef
*certChain
, CSSM_TP_APPLE_EVIDENCE_INFO
**statusChain
)
1081 if (fnc
.fSecTrustGetResult
)
1082 return fnc
.fSecTrustGetResult(trust
, result
, certChain
, statusChain
);
1086 OSStatus
cSSLCopyPeerCertificates(SSLContextRef cxt
, CFArrayRef
*certs
)
1088 if (!certs
|| !cxt
) return paramErr
;
1090 if (fnc
.fSSLCopyPeerCertificates
)
1091 return fnc
.fSSLCopyPeerCertificates(cxt
, certs
);
1092 if (fnc
.fSSLGetPeerCertificates
) {
1093 OSStatus err
= fnc
.fSSLGetPeerCertificates(cxt
, certs
);
1094 if (!err
&& *certs
) {
1095 size_t i
, c
= (size_t)CFArrayGetCount(*certs
);
1096 for (i
= 0; i
< c
; ++i
) {
1097 CFTypeRef item
= (CFTypeRef
)CFArrayGetValueAtIndex(*certs
, i
);
1098 if (item
) CFRelease(item
);
1106 OSStatus
cSecTrustSetAnchorCertificatesOnly(SecTrustRef cxt
, Boolean anchorsOnly
)
1108 if (fnc
.fSecTrustSetAnchorCertificatesOnly
)
1109 return fnc
.fSecTrustSetAnchorCertificatesOnly(cxt
, anchorsOnly
);
1113 OSStatus
cSSLSetProtocolVersionMinMax(SSLContextRef cxt
, int minVer
, int maxVer
)
1117 if (minVer
< 0 || maxVer
< 0 || minVer
> 8 || maxVer
> 8 || minVer
> maxVer
)
1120 if (minVer
== kSSLProtocolUnknown
) minVer
= kSSLProtocol3
;
1121 if (minVer
== kSSLProtocolAll
) minVer
= kSSLProtocol3
;
1122 if (minVer
== kSSLProtocol3Only
) minVer
= kSSLProtocol3
;
1123 if (minVer
== kTLSProtocol1Only
) minVer
= kTLSProtocol1
;
1125 if (maxVer
== kSSLProtocol3Only
) maxVer
= kSSLProtocol3
;
1126 if (maxVer
== kTLSProtocol1Only
) maxVer
= kTLSProtocol1
;
1127 if (maxVer
== kSSLProtocolAll
) maxVer
= kTLSProtocol12
;
1128 if (maxVer
== kSSLProtocolUnknown
) maxVer
= kTLSProtocol12
;
1130 if (kCFCoreFoundationVersionNumber
< kCFCoreFoundationVersionNumber10_8
&&
1131 minVer
<= kTLSProtocol1
&& maxVer
> kTLSProtocol1
)
1132 maxVer
= kTLSProtocol1
;
1134 if (fnc
.fSSLSetProtocolVersionMin
&& fnc
.fSSLSetProtocolVersionMax
) {
1135 err
= fnc
.fSSLSetProtocolVersionMin(cxt
, minVer
);
1137 err
= fnc
.fSSLSetProtocolVersionMax(cxt
, maxVer
);
1140 if (fnc
.fSSLSetProtocolVersionEnabled
) {
1141 #define ENABLEPROTO(x) fnc.fSSLSetProtocolVersionEnabled(cxt, (int)(x), \
1142 minVer <= x && x <= maxVer)
1143 err
= ENABLEPROTO(kSSLProtocol2
);
1144 if (err
&& minVer
> kSSLProtocol2
) err
= noErr
; /* ignore SSL2 disable error */
1145 if (!err
) ENABLEPROTO(kSSLProtocol3
);
1146 if (!err
) ENABLEPROTO(kTLSProtocol1
);
1147 if (kCFCoreFoundationVersionNumber
>= kCFCoreFoundationVersionNumber10_8
||
1148 maxVer
> kTLSProtocol1
) {
1149 if (!err
) ENABLEPROTO(kTLSProtocol11
);
1150 if (!err
) ENABLEPROTO(kTLSProtocol12
);
1158 OSStatus
cSSLSetSessionOption(SSLContextRef cxt
, int option
, Boolean value
)
1160 if (fnc
.fSSLSetSessionOption
)
1161 return fnc
.fSSLSetSessionOption(cxt
, option
, value
);
1166 SecCertificateRef
cSecCertificateCreateWithData(CFAllocatorRef a
, CFDataRef d
)
1168 if (fnc
.fSecCertificateCreateWithData
)
1169 return fnc
.fSecCertificateCreateWithData(a
, d
);
1170 else if (fnc
.fSecCertificateCreateFromData
) {
1171 cCSSM_DATA certdata
;
1173 SecCertificateRef cacert
= NULL
;
1174 if (!d
) return NULL
;
1175 certdata
.Length
= (size_t)CFDataGetLength(d
);
1176 certdata
.Data
= (uint8
*)CFDataGetBytePtr(d
);
1177 err
= fnc
.fSecCertificateCreateFromData(&certdata
, CSSM_CERT_X_509v3
,
1178 CSSM_CERT_ENCODING_DER
, &cacert
);
1186 CFDataRef
cSecCertificateCopyData(SecCertificateRef c
)
1188 if (!c
) return NULL
;
1189 if (CFGetTypeID(c
) != SecCertificateGetTypeID()) return NULL
;
1190 if (fnc
.fSecCertificateCopyData
)
1191 return fnc
.fSecCertificateCopyData(c
);
1192 if (fnc
.fSecCertificateGetData
) {
1193 cCSSM_DATA certdata
;
1194 OSStatus err
= fnc
.fSecCertificateGetData(c
, &certdata
);
1195 if (err
|| !certdata
.Data
|| !certdata
.Length
) return NULL
;
1196 return CFDataCreate(kCFAllocatorDefault
, certdata
.Data
, certdata
.Length
);
1201 Boolean
BlobsEqual(CFDataRef d1
, CFDataRef d2
)
1204 Boolean ans
= false;
1208 l1
= CFDataGetLength(d1
);
1209 l2
= CFDataGetLength(d2
);
1211 const void *p1
= (void *)CFDataGetBytePtr(d1
);
1212 const void *p2
= (void *)CFDataGetBytePtr(d2
);
1213 ans
= memcmp(p1
, p2
, l1
) == 0;
1218 Boolean
SecCertsEqual(SecCertificateRef c1
, SecCertificateRef c2
)
1221 Boolean ans
= false;
1222 d1
= cSecCertificateCopyData(c1
);
1223 if (!d1
) return false;
1224 d2
= cSecCertificateCopyData(c2
);
1229 ans
= BlobsEqual(d1
, d2
);
1235 Boolean
BlobInArray(CFDataRef d
, CFArrayRef a
)
1238 if (!d
|| !a
|| !CFArrayGetCount(a
)) return false;
1239 cnt
= CFArrayGetCount(a
);
1240 for (i
= 0; i
< cnt
; ++i
) {
1241 if (BlobsEqual(d
, (CFDataRef
)CFArrayGetValueAtIndex(a
, i
)))
1247 Boolean
SecCertInArray(SecCertificateRef c
, CFArrayRef a
)
1250 if (!c
|| !a
|| !CFArrayGetCount(a
)) return false;
1251 cnt
= CFArrayGetCount(a
);
1252 for (i
= 0; i
< cnt
; ++i
) {
1253 if (SecCertsEqual(c
, (SecCertificateRef
)CFArrayGetValueAtIndex(a
, i
)))
1259 OSStatus
CopyIdentityWithLabel(const char *label
, SecIdentityRef
*out
)
1261 if (fnc
.fSecKeychainSearchCreateFromAttributes
&&
1262 fnc
.fSecKeychainSearchCopyNext
) {
1263 SecIdentityRef ans
= NULL
;
1264 SecCertificateRef cert
= NULL
;
1265 SecKeychainAttribute at
;
1266 SecKeychainAttributeList al
;
1267 SecKeychainSearchRef sr
;
1270 at
.tag
= kSecLabelItemAttr
;
1271 at
.length
= strlen(label
);
1272 at
.data
= (char *)label
;
1275 err
= fnc
.fSecKeychainSearchCreateFromAttributes(NULL
,
1276 kSecCertificateItemClass
, &al
, &sr
);
1278 if (err
|| !sr
) return err
;
1279 while ((err
= fnc
.fSecKeychainSearchCopyNext(sr
, (SecKeychainItemRef
*)&cert
)) == noErr
) {
1280 if (!cert
) continue;
1281 if (CFGetTypeID(cert
) != SecCertificateGetTypeID()) {CFRelease(cert
); continue;}
1282 err
= cSecIdentityCreateWithCertificate(NULL
, cert
, &ans
);
1284 if (!err
&& ans
) break;
1291 return errSecItemNotFound
;
1296 OSStatus
cSecItemImport(
1297 CFDataRef importedData
, CFStringRef fileNameOrExtension
,
1298 SecExternalFormat
*inputFormat
, SecExternalItemType
*itemType
,
1299 SecItemImportExportFlags flags
, const SecItemImportExportKeyParameters
*keyParams
,
1300 SecKeychainRef importKeychain
, CFArrayRef
*outItems
)
1302 if (fnc
.fSecItemImport
)
1303 return fnc
.fSecItemImport(importedData
, fileNameOrExtension
, inputFormat
,
1304 itemType
, flags
, keyParams
, importKeychain
, outItems
);
1305 else if (fnc
.fSecKeychainItemImport
) {
1306 SecKeyImportExportParameters oldKeyParams
;
1307 SecKeyImportExportParameters
*op
= NULL
;
1310 memset(&oldKeyParams
, 0, sizeof(oldKeyParams
));
1311 oldKeyParams
.version
= keyParams
->version
;
1312 oldKeyParams
.flags
= keyParams
->flags
;
1313 oldKeyParams
.passphrase
= keyParams
->passphrase
;
1314 oldKeyParams
.alertTitle
= keyParams
->alertTitle
;
1315 oldKeyParams
.alertPrompt
= keyParams
->alertPrompt
;
1316 oldKeyParams
.accessRef
= keyParams
->accessRef
;
1317 /* We punt on keyUsage and keyAttributes and do not convert them */
1319 return fnc
.fSecKeychainItemImport(importedData
, fileNameOrExtension
, inputFormat
,
1320 itemType
, flags
, op
, importKeychain
, outItems
);
1325 OSStatus
cSecIdentityCreateWithCertificate(CFTypeRef k
, SecCertificateRef c
,
1328 /* The documentation lies and this is actually present in later 10.4 versions */
1329 if (fnc
.fSecIdentityCreateWithCertificate
)
1330 return fnc
.fSecIdentityCreateWithCertificate(k
, c
, i
);
1334 SSLContextRef
cSSLCreateContext(CFAllocatorRef a
, int ps
, int ct
)
1336 if (fnc
.fSSLCreateContext
)
1337 return fnc
.fSSLCreateContext(a
, ps
, ct
);
1338 if ((ps
!= kSSLServerSide
&& ps
!= kSSLClientSide
) || (ct
!= kSSLStreamType
))
1340 if (fnc
.fSSLNewContext
&& fnc
.fSSLDisposeContext
) {
1342 OSStatus err
= fnc
.fSSLNewContext(ps
== kSSLServerSide
, &cxt
);
1343 return err
? NULL
: cxt
;
1348 void cSSLDisposeContext(SSLContextRef c
)
1350 if (fnc
.fSSLCreateContext
)
1352 else if (fnc
.fSSLDisposeContext
)
1353 fnc
.fSSLDisposeContext(c
);
1356 CF_INLINE
bool is_ldh(int c
)
1359 ('A' <= c
&& c
<= 'Z') ||
1360 ('a' <= c
&& c
<= 'z') ||
1361 ('0' <= c
&& c
<= '9') ||
1365 CF_INLINE
size_t get_label_len(const char *p
, size_t l
)
1368 while (l
-- && is_ldh(*p
++)) ++ans
;
1372 static bool is_dns_name(const void *_p
, size_t l
, bool wcok
)
1374 const char *p
= (char *)_p
;
1377 if (!p
) return false;
1378 if (l
>= 1 && p
[l
-1] == '.') --l
;
1379 if (!l
) return false;
1380 if (l
> 255) return false;
1382 size_t lablen
= get_label_len(p
, l
);
1383 if (lablen
> 63) return false;
1384 if (!idx
&& !lablen
&& wcok
&& l
>= 2 && p
[0] == '*' && p
[1] == '.') lablen
=1;
1385 if (!lablen
) return false;
1386 if (p
[0] == '-' || p
[lablen
- 1] == '-') return false;
1388 if (p
[lablen
] != '.') return false;
1398 CF_INLINE
char clc(char c
)
1400 return 'A' <= c
&& c
<= 'Z' ? c
- 'A' + 'a' : c
;
1403 CF_INLINE
bool matchicase(const char *p1
, const char *p2
, size_t l
)
1406 if (clc(*p1
++) != clc(*p2
++)) return false;
1411 static bool peername_matches_id(const char *peername
, CFDataRef idrawname
)
1415 if (!peername
|| !idrawname
) return false;
1416 idname
= (const char *)CFDataGetBytePtr(idrawname
);
1417 if (!idname
) return false;
1418 pl
= strlen(peername
);
1419 il
= (size_t)CFDataGetLength(idrawname
);
1420 if (!is_dns_name(peername
, pl
, false) || !is_dns_name(idname
, il
, true))
1423 if (peername
[pl
- 1] == '.') --pl
;
1424 if (idname
[il
- 1] == '.') --il
;
1425 if (pl
> 255 || il
> 255)
1428 size_t pll
= get_label_len(peername
, pl
);
1429 size_t ill
= get_label_len(idname
, il
);
1430 if (!idx
&& !ill
&& il
>= 2 && idname
[0] == '*' && idname
[1] == '.') ill
=1;
1432 if (peername
[pll
] != '.') return false;
1436 if (idname
[ill
] != '.') return false;
1439 if (idx
|| idname
[0] != '*') {
1440 if (pll
!= ill
) return false;
1441 if (!matchicase(peername
, idname
, pll
)) return false;
1452 CF_INLINE
size_t get_num_len(const char *p
, size_t l
)
1455 while (l
-- && '0' <= *p
&& *p
<= '9') {
1462 Boolean
IsIPv4Name(const void *_p
, size_t l
)
1464 const char *p
= (char *)_p
;
1467 if (!p
|| l
< 7) return false;
1470 if (++idx
> 4) return false;
1471 lablen
= get_num_len(p
, l
);
1472 if (lablen
> 3) return false;
1473 if (lablen
>= 2 && *p
== '0') return false;
1474 else if (lablen
== 3) {
1475 if (*p
>= '3') return false;
1477 if (p
[1] >= '6') return false;
1478 if (p
[1] == '5' && p
[2] > '5') return false;
1482 if (p
[lablen
] != '.') return false;
1491 static bool parse_ipv4_name(const void *_p
, size_t l
, uint8_t ipv4
[4])
1493 unsigned short s
[4];
1495 const char *p
= (char *)_p
;
1496 if (!IsIPv4Name(p
, l
) || l
> 15) return false;
1497 memcpy(ipv4str
, p
, l
);
1499 if (sscanf(ipv4str
, "%hu.%hu.%hu.%hu", s
, s
+1, s
+2, s
+3) == 4) {
1500 ipv4
[0] = (uint8_t)s
[0];
1501 ipv4
[1] = (uint8_t)s
[1];
1502 ipv4
[2] = (uint8_t)s
[2];
1503 ipv4
[3] = (uint8_t)s
[3];
1509 static bool parse_ipv6_name(const void *_p
, size_t l
, uint8_t ipv6
[16])
1511 char ipv6str
[INET6_ADDRSTRLEN
];
1512 const char *p
= (char *)_p
;
1514 if (!p
) return false;
1515 if (l
>= 1 && p
[0] == '[') {
1519 if (l
>= 1 && p
[l
-1] == ']') --l
;
1520 pct
= (char *)(l
? memchr(p
, '%', l
) : NULL
);
1521 if (pct
) l
= pct
- p
;
1522 if (l
< 3 || l
>= INET6_ADDRSTRLEN
) return false;
1523 memcpy(ipv6str
, p
, l
);
1525 return inet_pton(AF_INET6
, ipv6str
, ipv6
) == 1;
1528 #define U(x) ((const uint8_t *)(x))
1529 static const data_t OID_BasicConstraints
= {U("\006\003\125\035\023"), 5};
1530 static const data_t OID_SubjectAltName
= {U("\006\003\125\035\021"), 5};
1531 static const data_t OID_SubjectKeyIdentifier
= {U("\006\003\125\035\016"), 5};
1532 static const data_t OID_AuthorityKeyIdentifier
= {U("\006\003\125\035\043"), 5};
1533 static const data_t OID_CommonName
= {U("\006\003\125\004\003"), 5};
1536 typedef struct der_atom_s
{
1537 uint8_t clas
; /* 0, 1, 2, or 3 */
1538 uint8_t cons
; /* 0 or 1 */
1539 uint8_t rawtag
; /* raw value of first byte of tag */
1540 uint32_t tag
; /* tag value */
1541 size_t hl
; /* length of header excluding actual data */
1542 size_t dl
; /* length of actual data */
1545 typedef struct der_cert_s
{
1546 uint8_t vers
; /* 0 => v1, 1 => v2, 2 => v3 */
1547 uint8_t caFlag
; /* 0 unless basic constraints present then 0x80=critial 0x01=value */
1548 uint8_t isCA
; /* true if caFlag==0x81 or subject==issuer && vers < 2 */
1549 uint8_t isRoot
; /* true if isCA and subject == issuer */
1550 char notBefore
[16]; /* Not before date either 13 chars or 15 chars plus Nul */
1551 char notAfter
[16]; /* Not after date see notBefore for format */
1552 data_t subject
; /* points to sequence */
1553 data_t subjectPubKey
; /* points to subjectPublicKeyInfo sequence */
1554 data_t subjectAltNames
; /* null unless v3 extension present, points to sequence */
1555 data_t subjectKeyId
; /* null unless v3 extension present, points to raw bytes */
1556 data_t issuer
; /* points to sequence */
1557 data_t issuerKeyId
; /* null unless v3 extension present, points to raw bytes */
1560 static bool read_der_atom(const data_t
*d
, der_atom_t
*o
)
1565 if (!d
|| !d
->d
|| !d
->l
|| !o
) return false;
1566 o
->clas
= (*d
->d
>> 6) & 0x3;
1567 o
->cons
= (*d
->d
>> 5) & 0x1;
1574 if (pos
>= d
->l
) return false;
1578 } while (byte
& 0x80);
1581 if (pos
>= d
->l
) return false;
1584 unsigned cnt
= byte
& 0x7f;
1585 if (!cnt
|| pos
+ cnt
> d
->l
) return false;
1594 if (pos
+ len
> d
->l
) return false;
1600 /* return true if _d->d points at a valid DER atom that is _d->l bytes long
1601 * or less. If exact_length_match_only is set the DER atom MUST be exactly
1602 * _d->l bytes long. If the atom at _d->d is a set or sequence, then its
1603 * elements are also examined recursively to make sure they are also valid. */
1604 static bool is_der(const data_t
*_d
, bool exact_length_match_only
)
1608 if (!_d
|| !_d
->d
|| !_d
->l
) return false;
1613 if (!read_der_atom(&d
, &atom
)) return false;
1616 if ((atom
.rawtag
& 0xfe) != 0x30) {
1625 return !d
.l
&& (d
.d
== _d
->d
+ _d
->l
|| !exact_length_match_only
);
1628 /* true is returned if data is not NULL and matches:
1637 * } == length of data
1639 static bool check_der_pubkey(const data_t
*_d
)
1644 if (!_d
|| !_d
->d
|| !_d
->l
) return false;
1645 if (!is_der(_d
, true)) return false;
1648 if (!read_der_atom(&d
, &atom
)) return false;
1649 if (atom
.rawtag
!= 0x30) return false;
1652 if (!read_der_atom(&d
, &atom
)) return false;
1653 if (atom
.rawtag
!= 0x30) return false;
1654 if (!atom
.dl
|| d
.d
[atom
.hl
] != 0x06) return false;
1655 d
.l
-= atom
.hl
+ atom
.dl
;
1656 d
.d
+= atom
.hl
+ atom
.dl
;
1657 if (!read_der_atom(&d
, &atom
)) return false;
1658 if (atom
.rawtag
!= 0x03) return false;
1662 static int data_matches(const data_t
*o1
, const data_t
*o2
)
1664 if (!o1
|| !o2
|| !o1
->l
|| !o2
->l
|| o1
->l
!= o2
->l
)
1666 return memcmp(o1
->d
, o2
->d
, o1
->l
) == 0;
1669 static bool read_der_cert(const data_t
*_d
, der_cert_t
*o
)
1674 if (!_d
|| !_d
->d
|| !_d
->l
|| !o
) return false;
1675 if (!is_der(_d
, true)) return false;
1678 memset(o
, 0, sizeof(*o
));
1679 if (!read_der_atom(&d
, &atom
)) return false;
1680 if (atom
.rawtag
!= 0x30) return false;
1683 if (!read_der_atom(&d
, &atom
)) return false;
1684 if (atom
.rawtag
!= 0x30) return false;
1687 if (!read_der_atom(&d
, &atom
)) return false;
1688 if (atom
.rawtag
== 0xA0) {
1691 if (atom
.dl
!= 3 || d
.d
[0] != 2 || d
.d
[1] != 1) return false;
1692 o
->vers
= d
.d
[2]; /* not validated */
1695 if (!read_der_atom(&d
, &atom
)) return false;
1697 o
->vers
= 0; /* implied v1 */
1699 if (atom
.rawtag
!= 2) return false;
1700 /* skip serialNumber */
1701 d
.l
-= atom
.hl
+ atom
.dl
;
1702 d
.d
+= atom
.hl
+ atom
.dl
;
1703 if (!read_der_atom(&d
, &atom
)) return false;
1704 if (atom
.rawtag
!= 0x30) return false;
1705 /* skip signature */
1706 d
.l
-= atom
.hl
+ atom
.dl
;
1707 d
.d
+= atom
.hl
+ atom
.dl
;
1708 if (!read_der_atom(&d
, &atom
)) return false;
1709 if (atom
.rawtag
!= 0x30) return false;
1711 o
->issuer
.l
= atom
.hl
+ atom
.dl
;
1712 d
.l
-= atom
.hl
+ atom
.dl
;
1713 d
.d
+= atom
.hl
+ atom
.dl
;
1714 if (!read_der_atom(&d
, &atom
)) return false;
1715 if (atom
.rawtag
!= 0x30) return false;
1717 /* parse validity */
1719 vdate
.d
= d
.d
+ atom
.hl
;
1721 d
.l
-= atom
.hl
+ atom
.dl
;
1722 d
.d
+= atom
.hl
+ atom
.dl
;
1723 if (!read_der_atom(&vdate
, &atom
)) return false;
1724 if (atom
.rawtag
!= 0x17 && atom
.rawtag
!= 0x18) return false;
1725 if (atom
.rawtag
== 0x17 && atom
.dl
!= 13) return false;
1726 if (atom
.rawtag
== 0x18 && atom
.dl
!= 15) return false;
1727 memcpy(o
->notBefore
, vdate
.d
+ atom
.hl
, atom
.dl
);
1728 vdate
.l
+= atom
.hl
+ atom
.dl
;
1729 vdate
.d
+= atom
.hl
+ atom
.dl
;
1730 if (!read_der_atom(&vdate
, &atom
)) return false;
1731 if (atom
.rawtag
!= 0x17 && atom
.rawtag
!= 0x18) return false;
1732 if (atom
.rawtag
== 0x17 && atom
.dl
!= 13) return false;
1733 if (atom
.rawtag
== 0x18 && atom
.dl
!= 15) return false;
1734 memcpy(o
->notAfter
, vdate
.d
+ atom
.hl
, atom
.dl
);
1735 if (vdate
.d
+ atom
.hl
+ atom
.dl
!= d
.d
) return false;
1737 if (!read_der_atom(&d
, &atom
)) return false;
1738 if (atom
.rawtag
!= 0x30) return false;
1740 o
->subject
.l
= atom
.hl
+ atom
.dl
;
1741 d
.l
-= atom
.hl
+ atom
.dl
;
1742 d
.d
+= atom
.hl
+ atom
.dl
;
1743 if (!read_der_atom(&d
, &atom
)) return false;
1744 if (atom
.rawtag
!= 0x30) return false;
1745 o
->subjectPubKey
.d
= d
.d
;
1746 o
->subjectPubKey
.l
= atom
.hl
+ atom
.dl
;
1747 if (!check_der_pubkey(&o
->subjectPubKey
)) return false;
1748 d
.l
-= atom
.hl
+ atom
.dl
;
1749 d
.d
+= atom
.hl
+ atom
.dl
;
1751 if (o
->vers
!= 2 || !d
.l
) break;
1752 if (!read_der_atom(&d
, &atom
)) return false;
1753 if (atom
.rawtag
== 0x81) {
1754 /* skip issuerUniqueID */
1755 d
.l
-= atom
.hl
+ atom
.dl
;
1756 d
.d
+= atom
.hl
+ atom
.dl
;
1758 if (!read_der_atom(&d
, &atom
)) return false;
1760 if (atom
.rawtag
== 0x82) {
1761 /* skip subjectUniqueID */
1762 d
.l
-= atom
.hl
+ atom
.dl
;
1763 d
.d
+= atom
.hl
+ atom
.dl
;
1765 if (!read_der_atom(&d
, &atom
)) return false;
1767 if (atom
.rawtag
!= 0xA3) return false;
1768 /* found v3 extensions */
1771 if (!read_der_atom(&d
, &atom
)) return false;
1772 if (atom
.rawtag
!= 0x30) return false;
1778 if (!read_der_atom(&d
, &atom
)) return false;
1779 if (atom
.rawtag
!= 0x30) return false;
1782 if (!read_der_atom(&d
, &atom
)) return false;
1783 if (atom
.rawtag
!= 6) return false;
1785 oid
.l
= atom
.hl
+ atom
.dl
;
1786 d
.l
-= atom
.hl
+ atom
.dl
;
1787 d
.d
+= atom
.hl
+ atom
.dl
;
1788 if (!read_der_atom(&d
, &atom
)) return false;
1789 if (atom
.rawtag
== 1) {
1790 /* skip over boolean but record its value */
1791 if (atom
.dl
!= 1) return false;
1792 crit
= *(d
.d
+ atom
.hl
);
1793 d
.l
-= atom
.hl
+ atom
.dl
;
1794 d
.d
+= atom
.hl
+ atom
.dl
;
1795 if (!read_der_atom(&d
, &atom
)) return false;
1797 if (atom
.rawtag
!= 4) return false;
1804 if (data_matches(&oid
, &OID_BasicConstraints
)) {
1805 if (!read_der_atom(&value
, &atom
)) return false;
1806 if (atom
.rawtag
!= 0x30) return false;
1810 /* CA flag is false and was properly omitted */
1811 o
->caFlag
= crit
? 0x80 : 0;
1813 if (!read_der_atom(&value
, &atom
)) return false;
1814 if (atom
.rawtag
== 1) {
1815 /* CA flag is present */
1816 if (atom
.dl
!= 1) return false;
1817 o
->caFlag
= (crit
? 0x80 : 0) | (*(value
.d
+ atom
.hl
) ? 0x1 : 0);
1820 } else if (data_matches(&oid
, &OID_SubjectAltName
)) {
1821 o
->subjectAltNames
.d
= value
.d
;
1822 o
->subjectAltNames
.l
= value
.l
;
1823 } else if (data_matches(&oid
, &OID_SubjectKeyIdentifier
)) {
1824 if (!read_der_atom(&value
, &atom
)) return false;
1825 if (atom
.rawtag
!= 4) return false;
1826 o
->subjectKeyId
.d
= value
.d
+ atom
.hl
;
1827 o
->subjectKeyId
.l
= atom
.dl
;
1828 } else if (data_matches(&oid
, &OID_AuthorityKeyIdentifier
)) {
1829 if (!read_der_atom(&value
, &atom
)) return false;
1830 if (atom
.rawtag
!= 0x30) return false;
1833 if (!read_der_atom(&value
, &atom
)) return false;
1834 if (atom
.rawtag
== 0x80) {
1835 o
->issuerKeyId
.d
= value
.d
+ atom
.hl
;
1836 o
->issuerKeyId
.l
= atom
.dl
;
1842 o
->isCA
= (o
->caFlag
|0x80) == 0x81; /* HACK: some old CAs aren't critical! */
1843 o
->isRoot
= (o
->isCA
&& o
->subject
.l
&& o
->subject
.l
== o
->issuer
.l
&&
1844 memcmp(o
->subject
.d
, o
->issuer
.d
, o
->subject
.l
) == 0) ? 1 : 0;
1846 o
->isCA
= (o
->subject
.l
&& o
->subject
.l
== o
->issuer
.l
&&
1847 memcmp(o
->subject
.d
, o
->issuer
.d
, o
->subject
.l
) == 0) ? 1 : 0;
1848 o
->isRoot
= o
->isCA
;
1853 Boolean
CheckCertOkay(SecCertificateRef _cert
)
1855 CFDataRef d
= cSecCertificateCopyData(_cert
);
1860 if (!d
) return false;
1861 data
.d
= CFDataGetBytePtr(d
);
1862 data
.l
= CFDataGetLength(d
);
1863 ans
= read_der_cert(&data
, &cert
);
1868 /* flags & 0x01 to extract pub keys from certificates */
1869 static Boolean
CheckPubKeyOkayInt(CFDataRef d
, data_t
*pubkey
, int flags
)
1873 if (!d
|| !pubkey
) return false;
1874 data
.d
= CFDataGetBytePtr(d
);
1875 data
.l
= CFDataGetLength(d
);
1876 if (check_der_pubkey(&data
)) {
1882 if (read_der_cert(&data
, &cert
)) {
1883 *pubkey
= cert
.subjectPubKey
;
1890 Boolean
CheckPubKeyOkay(CFDataRef d
)
1893 return CheckPubKeyOkayInt(d
, &data
, 0);
1896 static void append_hex_dump(CFMutableStringRef s
, const void *_d
, size_t l
)
1898 const unsigned char *d
= (unsigned char *)_d
;
1899 CFStringAppendCString(s
, "<", kCFStringEncodingASCII
);
1902 sprintf(byte
, "%02X", *d
++);
1903 CFStringAppendCString(s
, byte
, kCFStringEncodingASCII
);
1905 CFStringAppendCString(s
, ">", kCFStringEncodingASCII
);
1908 static CFStringRef
CopyCertKeyId(SecCertificateRef _cert
, bool issuer
)
1910 CFDataRef d
= cSecCertificateCopyData(_cert
);
1911 CFMutableStringRef ans
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1920 if (!d
|| !ans
) break;
1921 data
.d
= CFDataGetBytePtr(d
);
1922 data
.l
= CFDataGetLength(d
);
1923 if (!read_der_cert(&data
, &cert
)) break;
1924 key
= issuer
? &cert
.issuerKeyId
: &cert
.subjectKeyId
;
1925 if (!key
->d
|| !key
->l
) break;
1926 for (i
= 0; i
< key
->l
; ++i
) {
1928 sprintf(hexbyte
, "%02X%s", (unsigned)key
->d
[i
], i
+1 == key
->l
? "" : ":");
1929 CFStringAppendCString(ans
, hexbyte
, kCFStringEncodingASCII
);
1934 if (d
) CFRelease(d
);
1935 if (!good
&& ans
) {CFRelease(ans
); ans
=NULL
;}
1939 typedef struct oid_entry_s
{
1945 static const oid_entry_t oid_table
[] = {
1946 {5, "\006\003\125\004\003", "CN"},
1947 {5, "\006\003\125\004\004", "SN"},
1948 {5, "\006\003\125\004\005", "serialNumber"},
1949 {5, "\006\003\125\004\006", "C"},
1950 {5, "\006\003\125\004\007", "L"},
1951 {5, "\006\003\125\004\010", "ST"},
1952 {5, "\006\003\125\004\011", "street"},
1953 {5, "\006\003\125\004\012", "O"},
1954 {5, "\006\003\125\004\013", "OU"},
1955 {5, "\006\003\125\004\014", "title"},
1956 {5, "\006\003\125\004\015", "description"},
1957 {5, "\006\003\125\004\017", "businessCategory"},
1958 {5, "\006\003\125\004\021", "postalCode"},
1959 {5, "\006\003\125\004\024", "telephoneNumber"},
1960 {5, "\006\003\125\004\027", "facsimileTelephoneNumber"},
1961 {5, "\006\003\125\004\052", "GN"},
1962 {5, "\006\003\125\004\053", "initials"},
1963 {5, "\006\003\125\004\054", "generationQualifier"},
1964 {5, "\006\003\125\004\056", "dnQualifier"},
1965 {5, "\006\003\125\004\101", "pseudonym"},
1966 {5, "\006\003\125\004\141", "organizationIdentifier"},
1967 {11, "\006\011\052\206\110\206\367\015\001\011\001", "emailAddress"},
1968 {12, "\006\012\011\222\046\211\223\362\054\144\001\001", "UID"},
1969 {12, "\006\012\011\222\046\211\223\362\054\144\001\031", "DC"},
1970 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\001", "jurisdictionOfIncorporationLocality"},
1971 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\002", "jurisdictionOfIncorporationStateOrProvince"},
1972 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\003", "jurisdictionOfIncorporationCountry"}
1974 #define oid_table_size (sizeof(oid_table)/sizeof(oid_table[0]))
1976 static int comp_entry(const void *_e1
, const void *_e2
)
1978 const oid_entry_t
*o1
= (oid_entry_t
*)_e1
;
1979 const oid_entry_t
*o2
= (oid_entry_t
*)_e2
;
1982 if (o2
->l
< min
) min
= o2
->l
;
1983 ans
= memcmp(o1
->oid
, o2
->oid
, min
);
1984 if (ans
) return ans
;
1985 if (o1
->l
< o2
->l
) return -1;
1986 if (o1
->l
> o2
->l
) return 1;
1990 static void append_oid_name(CFMutableStringRef s
, const char *prefix
,
1991 const void *_oid
, size_t l
, const char *suffix
)
1993 oid_entry_t find
, *ans
;
1994 find
.oid
= (char *)_oid
;
1997 ans
= (oid_entry_t
*)
1998 bsearch(&find
, oid_table
, oid_table_size
, sizeof(find
), comp_entry
);
1999 if (prefix
&& *prefix
)
2000 CFStringAppendCString(s
, prefix
, kCFStringEncodingASCII
);
2002 CFStringAppendCString(s
, ans
->name
, kCFStringEncodingASCII
);
2004 CFMutableStringRef temp
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2005 const uint8_t *oid
= (uint8_t *)_oid
;
2008 const uint8_t *orig_oid
= oid
;
2009 if (!temp
|| l
< 3 || *oid
!= 6)
2016 if (!read_der_atom(&data
, &atom
))
2018 if (!bad
&& (atom
.rawtag
!= 6 || atom
.dl
< 1))
2021 oid
= data
.d
+ atom
.hl
;
2023 if (l
+ atom
.hl
!= orig_l
)
2033 if (!l
) {bad
=true; break;}
2037 idval
|= byte
& 0x7f;
2038 } while (!bad
&& (byte
& 0x80));
2045 } else if (idval
< 80) {
2046 x
= 1; y
= idval
- 40;
2048 x
= 2; y
= idval
- 80;
2050 snprintf(twoids
, sizeof(twoids
), "%u.%u", x
, y
);
2051 CFStringAppendCString(temp
, twoids
, kCFStringEncodingASCII
);
2055 snprintf(oneid
, sizeof(oneid
), ".%u", idval
);
2056 CFStringAppendCString(temp
, oneid
, kCFStringEncodingASCII
);
2059 } while (l
&& !bad
);
2061 if (bad
|| l
|| !temp
|| !CFStringGetLength(temp
))
2062 append_hex_dump(s
, orig_oid
, orig_l
);
2064 CFStringAppend(s
, temp
);
2068 if (suffix
&& *suffix
)
2069 CFStringAppendCString(s
, suffix
, kCFStringEncodingASCII
);
2072 #define DER_TAG_UTF8STRING 12
2073 #define DER_TAG_NUMERICSTRING 18
2074 #define DER_TAG_PRINTABLESTRING 19
2075 #define DER_TAG_TELETEXSTRING 20
2076 #define DER_TAG_VIDEOTEXSTRING 21
2077 #define DER_TAG_IA5STRING 22
2078 #define DER_TAG_GRAPHICSTRING 25
2079 #define DER_TAG_VISIBLESTRING 26
2080 #define DER_TAG_GENERALSTRING 27
2081 #define DER_TAG_UNIVERSALSTRING 28
2082 #define DER_TAG_BMPSTRING 30
2085 * 0x01 => strings only
2086 * 0x02 => 8-bit strings only
2087 * 0x04 => CN Ids strings only
2088 * 0x08 => wildcard CN okay
2089 * 0x10 => create output string
2091 static bool append_attr_value(CFMutableStringRef
*s
, const void *_d
,
2092 const der_atom_t
*a
, unsigned flags
)
2094 const uint8_t *d
= (uint8_t *)_d
;
2095 CFStringBuiltInEncodings encoding
= kCFStringEncodingASCII
;
2097 if (s
&& !(flags
& 0x10) && !*s
) return false;
2098 if (!s
|| !d
|| !a
|| !a
->dl
) return false;
2099 switch (a
->rawtag
) {
2100 case DER_TAG_UTF8STRING
:
2101 case DER_TAG_GRAPHICSTRING
:
2102 case DER_TAG_GENERALSTRING
:
2103 case DER_TAG_UNIVERSALSTRING
:
2104 encoding
= kCFStringEncodingUTF8
; break;
2105 case DER_TAG_NUMERICSTRING
:
2106 case DER_TAG_PRINTABLESTRING
:
2107 case DER_TAG_IA5STRING
:
2108 encoding
= kCFStringEncodingASCII
; break;
2109 case DER_TAG_TELETEXSTRING
:
2110 case DER_TAG_VIDEOTEXSTRING
:
2111 case DER_TAG_VISIBLESTRING
:
2112 encoding
= kCFStringEncodingISOLatin1
; break;
2113 case DER_TAG_BMPSTRING
:
2114 if (flags
& 0x06) return false;
2115 encoding
= kCFStringEncodingUnicode
; break;
2117 if (flags
& 0x05) return false;
2118 append_hex_dump(*s
, d
, a
->hl
+ a
->dl
);
2121 if (flags
& 0x04 && !is_dns_name(d
+a
->hl
, a
->dl
, !!(flags
& 0x08)))
2123 temp
= CFStringCreateWithBytes(kCFAllocatorDefault
, d
+a
->hl
, a
->dl
,
2127 *s
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2129 CFStringAppend(*s
, temp
);
2132 if (flags
& 0x05) return false;
2133 append_hex_dump(*s
, d
, a
->hl
+ a
->dl
);
2138 CF_INLINE
int is_dig(char c
)
2140 return '0' <= c
&& c
<= '9';
2143 static void append_year_string(CFMutableStringRef cfstr
, const char *vstr
)
2145 size_t vl
= strlen(vstr
);
2146 if ((vl
== 13 || vl
== 15) && vstr
[vl
- 1] == 'Z') {
2148 unsigned short uc
[4];
2149 if (vl
== 13 && is_dig(vstr
[0]) && is_dig(vstr
[1])) {
2150 int yr2
= (vstr
[0] - '0') * 10 + (vstr
[1] - '0');
2158 uc
[2] = (unsigned char)vstr
[0];
2159 uc
[3] = (unsigned char)vstr
[1];
2167 CFStringAppendCharacters(cfstr
, uc
, 4);
2170 uc
[2] = vstr
[off
+1];
2172 CFStringAppendCharacters(cfstr
, uc
, 4);
2173 uc
[0] = vstr
[off
+2];
2174 uc
[1] = vstr
[off
+3];
2176 CFStringAppendCharacters(cfstr
, uc
, 3);
2177 uc
[0] = vstr
[off
+4];
2178 uc
[1] = vstr
[off
+5];
2180 CFStringAppendCharacters(cfstr
, uc
, 3);
2181 uc
[0] = vstr
[off
+6];
2182 uc
[1] = vstr
[off
+7];
2183 CFStringAppendCharacters(cfstr
, uc
, 3);
2184 uc
[0] = vstr
[off
+8];
2185 uc
[1] = vstr
[off
+9];
2187 CFStringAppendCharacters(cfstr
, uc
, 3);
2189 CFStringAppendCString(cfstr
, vstr
, kCFStringEncodingASCII
);
2193 void CopyCertValidity(SecCertificateRef _cert
, CFStringRef
*_nb
, CFStringRef
*_na
)
2195 CFDataRef d
= cSecCertificateCopyData(_cert
);
2196 CFMutableStringRef nb
, na
;
2200 if (!d
|| !_nb
|| !_na
) return;
2203 data
.d
= CFDataGetBytePtr(d
);
2204 data
.l
= CFDataGetLength(d
);
2205 if (!read_der_cert(&data
, &cert
)) {
2210 nb
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2213 na
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2218 append_year_string(nb
, cert
.notBefore
);
2219 append_year_string(na
, cert
.notAfter
);
2224 static CFStringRef
CopyCertName(SecCertificateRef _cert
, bool issuer
)
2226 CFDataRef d
= cSecCertificateCopyData(_cert
);
2227 CFMutableStringRef ans
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2235 bool badset
= false;
2237 if (!d
|| !ans
) break;
2238 data
.d
= CFDataGetBytePtr(d
);
2239 data
.l
= CFDataGetLength(d
);
2240 if (!read_der_cert(&data
, &cert
)) break;
2241 name
= issuer
? &cert
.issuer
: &cert
.subject
;
2244 if (data
.d
&& data
.l
) {
2245 if (!read_der_atom(&data
, &atom
)) break;
2246 if (atom
.rawtag
!= 0x30) break;
2251 unsigned setidx
= 0;
2253 if (!read_der_atom(&data
, &atom
)) break;
2254 if (atom
.rawtag
!= 0x31) break;
2255 set
.d
= data
.d
+ atom
.hl
;
2257 data
.l
-= atom
.hl
+ atom
.dl
;
2258 data
.d
+= atom
.hl
+ atom
.dl
;
2261 if (!read_der_atom(&set
, &atom
)) break;
2262 if (atom
.rawtag
!= 0x30) break;
2265 if (!read_der_atom(&set
, &atom
)) break;
2266 if (atom
.rawtag
!= 6) break;
2268 oid
.l
= atom
.hl
+ atom
.dl
;
2269 set
.l
-= atom
.hl
+ atom
.dl
;
2270 set
.d
+= atom
.hl
+ atom
.dl
;
2271 if (!read_der_atom(&set
, &atom
)) break;
2272 append_oid_name(ans
, setidx
++?"/+":"/", oid
.d
, oid
.l
, "=");
2273 append_attr_value(&ans
, set
.d
, &atom
, 0);
2274 set
.l
-= atom
.hl
+ atom
.dl
;
2275 set
.d
+= atom
.hl
+ atom
.dl
;
2283 if (badset
|| data
.l
) break;
2288 if (d
) CFRelease(d
);
2289 if (!good
&& ans
) {CFRelease(ans
); ans
=NULL
;}
2290 return MakeVisibleString(ans
);
2293 CFStringRef
CopyCertSubject(SecCertificateRef _cert
)
2295 return CopyCertName(_cert
, false);
2298 CFStringRef
CopyCertSubjectKeyId(SecCertificateRef _cert
)
2300 return CopyCertKeyId(_cert
, false);
2303 CFStringRef
CopyCertIssuer(SecCertificateRef _cert
)
2305 return CopyCertName(_cert
, true);
2308 CFStringRef
CopyCertIssuerKeyId(SecCertificateRef _cert
)
2310 return CopyCertKeyId(_cert
, true);
2313 /* return CFArrayRef if arr else CFStringRef
2315 * 0x01 includes DNS alts
2316 * 0x02 includes IPv4 alts
2317 * 0x04 includes IPv6 alts
2318 * 0x08 include IP other alts
2320 static CFTypeRef
CopyCertSubjectAltNamesInt(const der_cert_t
*cert
, bool arr
, unsigned flags
)
2323 CFTypeRef ans
= arr
?
2324 (CFTypeRef
)CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
) :
2325 (CFTypeRef
)CFStringCreateMutable(kCFAllocatorDefault
, 0);
2331 if (!cert
|| !ans
) break;
2332 if (!cert
->subjectAltNames
.d
|| !cert
->subjectAltNames
.l
) break;
2333 data
.d
= cert
->subjectAltNames
.d
;
2334 data
.l
= cert
->subjectAltNames
.l
;
2335 if (!read_der_atom(&data
, &atom
)) break;
2336 if (atom
.rawtag
!= 0x30) break;
2340 if (!read_der_atom(&data
, &atom
)) break;
2341 if (atom
.rawtag
== 0x82 && (flags
& 0x01)) {
2343 if (!arr
&& CFStringGetLength((CFStringRef
)ans
))
2344 CFStringAppendCString((CFMutableStringRef
)ans
, ",", kCFStringEncodingASCII
);
2345 temp
= CFStringCreateWithBytes(kCFAllocatorDefault
, data
.d
+atom
.hl
,
2346 atom
.dl
, kCFStringEncodingASCII
, true);
2349 CFArrayAppendValue((CFMutableArrayRef
)ans
, temp
);
2351 CFStringAppend((CFMutableStringRef
)ans
, temp
);
2353 } else if (atom
.rawtag
== 0x87 && (flags
& 0x0e)) {
2354 if ((atom
.dl
== 4 && (flags
& 0x02)) ||
2355 (atom
.dl
== 16 && (flags
& 0x04)) ||
2356 (atom
.dl
!= 4 && atom
.dl
!= 16 && (flags
& 0x08))) {
2358 CFDataRef dtemp
= CFDataCreate(kCFAllocatorDefault
, data
.d
+atom
.hl
, atom
.dl
);
2360 CFArrayAppendValue((CFMutableArrayRef
)ans
, dtemp
);
2363 if (CFStringGetLength((CFStringRef
)ans
))
2364 CFStringAppendCString((CFMutableStringRef
)ans
, ",", kCFStringEncodingASCII
);
2367 const uint8_t *ip
= data
.d
+atom
.hl
;
2368 sprintf(ipv4str
, "%u.%u.%u.%u", ip
[0], ip
[1], ip
[2], ip
[3]);
2369 CFStringAppendCString((CFMutableStringRef
)ans
, ipv4str
, kCFStringEncodingASCII
);
2370 } else if (atom
.dl
== 16) {
2371 char ntopbuff
[INET6_ADDRSTRLEN
];
2372 if (!inet_ntop(AF_INET6
, data
.d
+atom
.hl
, ntopbuff
, sizeof(ntopbuff
)))
2374 CFStringAppendCString((CFMutableStringRef
)ans
, ntopbuff
, kCFStringEncodingASCII
);
2376 append_hex_dump((CFMutableStringRef
)ans
, data
.d
+atom
.hl
, atom
.dl
);
2381 data
.l
-= atom
.hl
+ atom
.dl
;
2382 data
.d
+= atom
.hl
+ atom
.dl
;
2384 if (!data
.l
&& ( (arr
&& CFArrayGetCount((CFArrayRef
)ans
) ) ||
2385 (!arr
&& CFStringGetLength((CFStringRef
)ans
)) ))
2388 if (!good
&& ans
) {CFRelease(ans
); ans
=NULL
;}
2392 static CFArrayRef
CopyCertSubjectCNIds(const der_cert_t
*cert
)
2394 CFMutableArrayRef ans
=
2395 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2402 if (!ans
|| !cert
|| !cert
->subject
.d
|| !cert
->subject
.l
) break;
2403 data
.d
= cert
->subject
.d
;
2404 data
.l
= cert
->subject
.l
;
2405 if (!read_der_atom(&data
, &atom
)) break;
2406 if (atom
.rawtag
!= 0x30) break;
2411 if (!read_der_atom(&data
, &atom
)) break;
2412 if (atom
.rawtag
!= 0x31) break;
2413 set
.d
= data
.d
+ atom
.hl
;
2415 data
.l
-= atom
.hl
+ atom
.dl
;
2416 data
.d
+= atom
.hl
+ atom
.dl
;
2417 if (!read_der_atom(&set
, &atom
)) break;
2418 if (atom
.rawtag
!= 0x30) break;
2419 if (atom
.hl
+ atom
.dl
== set
.l
) { /* single-value CN only */
2420 CFMutableStringRef cnid
;
2424 if (!read_der_atom(&set
, &atom
)) break;
2425 if (atom
.rawtag
!= 6) break;
2427 oid
.l
= atom
.hl
+ atom
.dl
;
2428 if (data_matches(&oid
, &OID_CommonName
)) {
2429 set
.l
-= atom
.hl
+ atom
.dl
;
2430 set
.d
+= atom
.hl
+ atom
.dl
;
2431 if (!read_der_atom(&set
, &atom
)) break;
2432 if (append_attr_value(&cnid
, set
.d
, &atom
, 0x1f) && cnid
) {
2433 CFArrayAppendValue(ans
, cnid
);
2442 if (ans
&& (!good
|| !CFArrayGetCount(ans
))) {
2449 CFStringRef
CopyCertSubjectAltNamesString(SecCertificateRef _cert
)
2451 CFDataRef d
= cSecCertificateCopyData(_cert
);
2454 CFStringRef ans
= NULL
;
2456 if (!d
) return NULL
;
2457 data
.d
= CFDataGetBytePtr(d
);
2458 data
.l
= CFDataGetLength(d
);
2459 if (read_der_cert(&data
, &cert
))
2460 ans
= (CFStringRef
)CopyCertSubjectAltNamesInt(&cert
, false, 0x0f);
2462 return MakeVisibleString(ans
);
2470 static CFArrayRef
CopyCertSubjectIds(der_cert_t
*c
, unsigned mode
)
2472 CFArrayRef ans
= NULL
;
2473 if (!c
|| (mode
!= 0 && mode
!= 4 && mode
!= 16)) return NULL
;
2475 return ans
= (CFArrayRef
)CopyCertSubjectAltNamesInt(c
, true, 0x02);
2477 return ans
= (CFArrayRef
)CopyCertSubjectAltNamesInt(c
, true, 0x04);
2479 ans
= (CFArrayRef
)CopyCertSubjectAltNamesInt(c
, true, 0x01);
2480 if (!ans
|| !CFArrayGetCount(ans
)) {
2481 if (ans
) CFRelease(ans
);
2482 ans
= CopyCertSubjectCNIds(c
);
2485 if (ans
&& !CFArrayGetCount(ans
)) {
2492 OSStatus
VerifyTrustChain(SecTrustRef trust
, CFArrayRef customRootsOrNull
,
2493 unsigned certFlags
, unsigned flags
, const char *peername
,
2494 CFArrayRef pinnedKeySet
)
2496 SecTrustResultType result
;
2497 CFArrayRef chain
= NULL
;
2498 CSSM_TP_APPLE_EVIDENCE_INFO
*evidence
;
2499 bool pkonly
= (certFlags
& 0x02) ? true : false;
2500 bool explicitCertsOnly
= (certFlags
& 0x01) ? true : false;
2501 bool nameonly
= (certFlags
& 0x04) ? true : false;
2504 if ((pinnedKeySet
&& !CFArrayGetCount(pinnedKeySet
)) || (pkonly
&& !pinnedKeySet
))
2506 if (nameonly
&& !pkonly
&& (!peername
|| !*peername
))
2508 err
= cSecTrustGetResult(trust
, &result
, &chain
, &evidence
);
2509 if (err
== errSecTrustNotAvailable
) {
2510 /* We need to evaluate first */
2511 CFArrayRef anchors
= customRootsOrNull
;
2512 if (chain
) CFRelease(chain
);
2516 err
= SecTrustCopyAnchorCertificates(&anchors
);
2517 if (err
) return err
;
2519 err
= SecTrustSetAnchorCertificates(trust
, anchors
);
2521 if (err
) return err
;
2522 err
= cSecTrustSetAnchorCertificatesOnly(trust
, customRootsOrNull
? true : false);
2523 if (err
&& err
!= unimpErr
) return err
;
2524 err
= SecTrustEvaluate(trust
, &result
);
2525 if (err
&& !pkonly
&& !nameonly
) return err
;
2527 err
= cSecTrustGetResult(trust
, &result
, &chain
, &evidence
);
2530 if (chain
) CFRelease(chain
);
2533 if (!chain
|| !evidence
|| !CFArrayGetCount(chain
)) {
2534 if (chain
) CFRelease(chain
);
2535 return errSSLXCertChainInvalid
;
2537 if (pkonly
) goto pinned_key_check
;
2538 cnt
= (size_t)CFArrayGetCount(chain
);
2539 if ((peername
&& *peername
) ||
2540 (!(flags
& CSSM_TP_ACTION_LEAF_IS_CA
) &&
2541 !(evidence
[0].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
))) {
2542 CFDataRef certder
= cSecCertificateCopyData(
2543 (SecCertificateRef
)CFArrayGetValueAtIndex(chain
, 0));
2547 return errSSLBadCert
;
2548 der
.d
= CFDataGetBytePtr(certder
);
2549 der
.l
= CFDataGetLength(certder
);
2550 if (!read_der_cert(&der
, &cert
))
2551 err
= errSSLBadCert
;
2553 /* First confirm we have a host name match. SecureTransport should do
2554 * this for us (but will not give us a decent result code) except that
2555 * it has problems with IPv6 address matching */
2556 if (!err
&& peername
&& *peername
) {
2557 size_t peerlen
= strlen(peername
);
2563 if (parse_ipv4_name(peername
, peerlen
, ipa
.ipv4
)) mode
= 4;
2564 else if (is_dns_name(peername
, peerlen
, false)) mode
= 0;
2565 else if (parse_ipv6_name(peername
, peerlen
, ipa
.ipv6
)) mode
= 16;
2567 /* if we can't parse peername it can't possibly match! */
2568 err
= errSSLHostNameMismatch
;
2570 CFArrayRef ids
= CopyCertSubjectIds(&cert
, mode
);
2571 if (ids
&& !CFArrayGetCount(ids
)) {
2576 /* if we don't have anything to match against it can't possibly match! */
2577 err
= errSSLHostNameMismatch
;
2579 size_t j
, idcnt
= CFArrayGetCount(ids
);
2580 bool matched
= false;
2581 for (j
= 0; j
< idcnt
; ++j
) {
2582 CFTypeRef oneid
= (CFTypeRef
)CFArrayGetValueAtIndex(ids
, j
);
2586 if (CFDataGetTypeID() != CFGetTypeID(oneid
)) continue;
2587 p
= (uint8_t *)CFDataGetBytePtr((CFDataRef
)oneid
);
2588 l
= (size_t)CFDataGetLength((CFDataRef
)oneid
);
2589 if (l
!= (size_t)mode
) continue;
2590 if (memcmp(ipa
.ipv6
, p
, l
) == 0) {
2596 if (CFStringGetTypeID() != CFGetTypeID(oneid
)) continue;
2597 dnsname
= CFStringCreateExternalRepresentation(
2598 kCFAllocatorDefault
, (CFStringRef
)oneid
,
2599 kCFStringEncodingASCII
, 0);
2600 if (!dnsname
) continue;
2601 if (peername_matches_id(peername
, dnsname
))
2610 err
= errSSLHostNameMismatch
;
2615 /* Confirm that the first certificate is NOT a CA (otherwise it's not a
2616 * valid chain), but again SecureTransport should have already checked that
2617 * for us. CSSM_TP_ACTION_LEAF_IS_CA overrides. Also we never check the
2618 * root certificate even if it's also the leaf. */
2619 if (!nameonly
&& !err
&& !(flags
& CSSM_TP_ACTION_LEAF_IS_CA
) &&
2620 !(evidence
[0].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
) && cert
.isCA
)
2621 err
= errSSLXCertChainInvalid
;
2624 if (err
) return err
;
2626 if (nameonly
) goto pinned_key_check
;
2627 if (explicitCertsOnly
) {
2628 /* Check all but root */
2629 for (i
= 0; i
< cnt
; ++i
) {
2630 if (!(evidence
[i
].StatusBits
& CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
) &&
2631 !(evidence
[i
].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
)) {
2632 /* If the magical cert had not appeared, the chain would have stopped
2633 * here and the error would be no root, so return that error */
2635 return errSSLNoRootCert
;
2639 if (!(flags
& CSSM_TP_ACTION_ALLOW_EXPIRED
) ||
2640 !(flags
& CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
)) {
2641 /* check for expired or not yet valid certs */
2642 for (i
= 0; i
< cnt
; ++i
) {
2643 if ((flags
& CSSM_TP_ACTION_ALLOW_EXPIRED
) &&
2644 !(evidence
[i
].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
))
2646 if ((flags
& CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
) &&
2647 (evidence
[i
].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
))
2649 if (evidence
[i
].StatusBits
& CSSM_CERT_STATUS_EXPIRED
) {
2651 return errSSLCertExpired
;
2653 if (evidence
[i
].StatusBits
& CSSM_CERT_STATUS_NOT_VALID_YET
) {
2655 return errSSLCertNotYetValid
;
2659 /* check for no root */
2660 if (!(evidence
[cnt
-1].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
)) {
2662 return errSSLNoRootCert
;
2664 /* check for unknown root */
2665 if (!(evidence
[cnt
-1].StatusBits
& CSSM_CERT_STATUS_IS_IN_ANCHORS
)) {
2667 return errSSLUnknownRootCert
;
2669 if (customRootsOrNull
) {
2670 /* make sure we're not using a gratuitous root, Mac OS X likes to just
2671 * go ahead and use its anchors sometimes despite settings to the contrary */
2672 if (!SecCertInArray((SecCertificateRef
)CFArrayGetValueAtIndex(chain
, cnt
-1),
2673 customRootsOrNull
)) {
2675 return errSSLNoRootCert
;
2678 /* everything looks good, so check the trust result code now */
2680 case kSecTrustResultProceed
:
2681 case kSecTrustResultUnspecified
:
2684 case kSecTrustResultDeny
:
2687 return errSecTrustSettingDeny
;
2689 /* everything else (confirm, invalid, recoverable, fatal, other) */
2691 return errSecNotTrusted
;
2695 CFDataRef certder
= cSecCertificateCopyData(
2696 (SecCertificateRef
)CFArrayGetValueAtIndex(chain
, 0));
2699 CFDataRef peerPubKey
;
2702 return errSSLBadCert
;
2703 der
.d
= CFDataGetBytePtr(certder
);
2704 der
.l
= CFDataGetLength(certder
);
2705 if (!read_der_cert(&der
, &cert
)) {
2707 return errSSLBadCert
;
2709 if (certFlags
& 0x08) {
2710 unsigned char sha256
[CC_SHA256_DIGEST_LENGTH
];
2712 CC_SHA256_Init(&ctx
);
2713 CC_SHA256_Update(&ctx
, cert
.subjectPubKey
.d
, (CC_LONG
)cert
.subjectPubKey
.l
);
2714 CC_SHA256_Final(sha256
, &ctx
);
2715 peerPubKey
= CFDataCreate(kCFAllocatorDefault
, sha256
, sizeof(sha256
));
2717 peerPubKey
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
2718 cert
.subjectPubKey
.d
, cert
.subjectPubKey
.l
, kCFAllocatorNull
);
2724 pinok
= BlobInArray(peerPubKey
, pinnedKeySet
);
2725 CFRelease(peerPubKey
);
2728 return errSecPinnedKeyMismatch
;
2734 #elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
2736 #error iOS is not currently supported
2738 #endif /* TARGET_OS_EMBEDDED || TARGET_OS_IPHONE */