3 stcompat.c -- SecureTransport compatibility implementation
4 Copyright (C) 2014 Kyle J. McKay. All rights reserved.
6 If this software is included as part of a build of
7 the cURL library, it may be used under the same license
8 terms as the cURL library.
10 Otherwise the GPLv2 license applies, see
11 http://www.gnu.org/licenses/gpl-2.0-standalone.html
13 This software is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 #include <Security/Security.h>
22 #include <objc/objc-runtime.h>
27 #include <sys/types.h>
31 #include <arpa/inet.h>
33 #include <crt_externs.h>
36 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
38 __attribute__((constructor
,used
)) static void stcompat_initialize(void);
39 #endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
41 extern CFStringRef
CFStringCreateWithBytesNoCopy(
45 CFStringEncoding encoding
,
46 Boolean isExternalRepresentation
,
47 CFAllocatorRef contentsDeallocator
); /* available 10.4 but not in header */
48 extern CFStringRef
NSTemporaryDirectory(void);
50 static CFStringRef
CFCopyTemporaryDirectory(void)
52 id pool
= objc_msgSend(objc_getClass("NSAutoreleasePool"), sel_getUid("new"));
53 CFStringRef dir
= (CFStringRef
)NSTemporaryDirectory();
54 if (dir
) CFRetain(dir
);
55 objc_msgSend(pool
, sel_getUid("drain"));
57 unsigned len
= (unsigned)CFStringGetLength(dir
);
60 (unsigned)CFStringGetCharacterAtIndex(dir
, l
-1) == (unsigned)'/') {
64 CFStringRef old
= dir
;
65 dir
= CFStringCreateWithSubstring(
66 kCFAllocatorDefault
, dir
, CFRangeMake(0, l
));
76 char *CFStringCreateUTF8String(CFStringRef s
, Boolean release
)
82 m
= (size_t)CFStringGetMaximumSizeForEncoding(
83 CFStringGetLength(s
), kCFStringEncodingUTF8
) + 1;
84 c
= (char *)malloc(m
);
86 if (release
) CFRelease(s
);
89 if (!CFStringGetCString(s
, c
, m
, kCFStringEncodingUTF8
)) {
93 if (release
) CFRelease(s
);
97 CFDataRef
CFDataCreateWithContentsOfFile(CFAllocatorRef a
, const char *f
)
100 CFMutableDataRef d
= CFDataCreateMutable(a
, 0);
104 fd
= open(f
, O_RDONLY
);
110 cnt
= read(fd
, buff
, sizeof(buff
));
111 if (cnt
> 0) CFDataAppendBytes(d
, (UInt8
*)buff
, (size_t)cnt
);
122 #define memmem(v1,l1,v2,l2) cmemmem(v1,l1,v2,l2)
123 static void *cmemmem(const void *_m
, size_t ml
, const void *_s
, size_t sl
)
125 const char *m
= (const char *)_m
;
126 const char *s
= (const char *)_s
;
127 if (!ml
|| !sl
|| ml
< sl
) return NULL
;
128 if (sl
== 1) return memchr(m
, *s
, ml
);
129 if (ml
== sl
) return (void *)(memcmp(m
, s
, sl
) ? NULL
: m
);
132 const char *p
= memchr(m
, *s
, ml
);
137 if (ml
< sl
) return NULL
;
138 if (!memcmp(m
, s
, sl
)) return (void *)m
;
145 CF_INLINE
int is_eol(int c
)
147 return c
== '\n' || c
== '\r';
150 CF_INLINE
int is_lb(int c
)
152 return c
== '\n' || c
== '\r' || c
== '\f';
155 CF_INLINE
int is_prnt(int c
)
157 return c
>= ' ' && c
<= '~';
160 static int has_lb(const void *_m
, size_t l
)
162 const char *m
= (const char *)_m
;
164 if (is_lb(*m
)) return 1;
171 static int has_prnt(const void *_m
, size_t l
)
173 const char *m
= (const char *)_m
;
175 if (!is_prnt(*m
)) return 0;
183 * returns start of "-----BEGIN XXXX-----\n" line or NULL if not found/error
184 * If returns NULL then *ot == 0 means not found, *ot == -1 means bad line
185 * If returns non-NULL then *ol is length through "-----\n" and *ot is length
186 * of "XXXX" part (which obviously starts at return value + 11 for BEGIN or
188 * If e is non-zero look for ----END rather than ----BEGIN
190 static const char *find_be(const void *_m
, size_t l
, size_t *ol
, int *ot
, int e
)
192 const char *m
= (const char *)_m
;
193 const char *origm
= m
;
194 const char *marker
= e
? "-----END " : "-----BEGIN ";
195 size_t mkl
= e
? 9 : 11;
199 const char *p
= (char *)memmem(m
, l
, marker
, mkl
);
203 if (p
> origm
&& !is_eol(p
[-1])) continue;
204 t
= (char *)memmem(m
, l
, "-----", 5);
207 if (l
> 5 && !is_eol(t
[5])) continue;
208 if (has_lb(p
, t
-p
)) continue;
209 if ((size_t)(t
-p
) > (76 - mkl
- 5) || !has_prnt(p
, t
-p
)) {
216 if (l
&& *m
== '\r') {
220 if (l
&& *m
== '\n') {
232 pemtype_certificate
, /* "CERTIFICATE" or "TRUSTED CERTIFICATE" */
233 pemtype_privatekey_rsa
/* "RSA PRIVATE KEY" */
237 const char *start
; /* Armour start "-----BEGIN XXXXX-----\n" */
238 size_t len
; /* Length through armour end "-----END XXXXX-----\n" */
239 /* Body starts after "-----BEGIN XXXXX-----\n" */
241 size_t bodylen
; /* Length though "\n" BEFORE final "-----END XXXXX-----\n" */
242 /* Kind starts at start + 11 */
243 size_t kindlen
; /* length of "XXXXX" from "-----BEGIN XXXXX-----\n" */
247 static int nextpem(const char *p
, size_t l
, peminfo_t
*o
)
249 size_t beglen
, endlen
;
250 int begtype
, endtype
;
252 const char *beg
= find_be(p
, l
, &beglen
, &begtype
, 0);
253 if (!beg
) return begtype
;
254 end
= find_be(p
+ beglen
, l
- beglen
, &endlen
, &endtype
, 1);
255 if (!end
|| begtype
!= endtype
|| memcmp(beg
+11, end
+9, (size_t)begtype
))
258 o
->len
= (end
+ endlen
) - beg
;
259 o
->body
= beg
+ beglen
;
260 o
->bodylen
= end
- (beg
+ beglen
);
261 o
->kindlen
= (size_t)begtype
;
262 if (begtype
== 11 && !memcmp(beg
+ 11, "CERTIFICATE", 11)) {
263 o
->type
= pemtype_certificate
;
264 } else if (begtype
== 19 && !memcmp(beg
+ 11, "TRUSTED CERTIFICATE", 19)) {
265 o
->type
= pemtype_certificate
;
266 } else if (begtype
== 15 && !memcmp(beg
+ 11, "RSA PRIVATE KEY", 15)) {
267 o
->type
= pemtype_privatekey_rsa
;
269 o
->type
= pemtype_unknown
;
271 return (int)((o
->start
+ o
->len
) - p
);
274 static const signed char b64tab
[256] = {
275 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
276 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
277 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x3E,-1,-1,-1,0x3F,
278 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,-1,-1,-1,0x40,-1,-1,
279 -1,0,1,2,3,4,5,6,7,8,9,0x0A,0x0B,0x0C,0x0D,0x0E,
280 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,-1,-1,-1,-1,-1,
281 -1,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
282 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,-1,-1,-1,-1,-1,
283 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
284 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
285 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
286 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
287 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
288 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
289 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
290 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
293 static void convert24(const uint8_t *input
, uint8_t *output
)
295 output
[0] = ((b64tab
[input
[0]]&0x3F)<<2)|((b64tab
[input
[1]]&0x3F)>>4);
296 output
[1] = (((b64tab
[input
[1]]&0x3F)&0x0F)<<4)|((b64tab
[input
[2]]&0x3F)>>2);
297 output
[2] = (((b64tab
[input
[2]]&0x3F)&0x03)<<6)|(b64tab
[input
[3]]&0x3F);
300 static CFDataRef
CFDataCreateFromBase64(CFAllocatorRef a
, const void *_b
, size_t l
)
306 const uint8_t *p
= (uint8_t *)_b
;
307 if (l
&& !p
) return NULL
;
308 d
= CFDataCreateMutable(a
, 0);
311 for (i
=0; l
; ++p
, --l
) {
313 if (c
== ' ' || c
== '\t' || c
== '\r' || c
== '\n' || c
== '\f')
324 CFDataAppendBytes(d
, out
, inp
[2] == '=' ? 1 : 2);
327 CFDataAppendBytes(d
, out
, 3);
337 static SecCertificateRef
createvalidcert(CFDataRef d
)
339 SecCertificateRef cert
= cSecCertificateCreateWithData(kCFAllocatorDefault
, d
);
340 if (!cert
) return NULL
;
341 if (!CheckCertOkay(cert
)) {
348 CFArrayRef
CreateCertsArrayWithData(CFDataRef d
, const errinfo_t
*e
)
350 const char *certs
, *p
;
351 size_t certslen
, plen
, cnt
= 1;
354 certs
= (char *)CFDataGetBytePtr(d
);
355 certslen
= (size_t)CFDataGetLength(d
);
356 a
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
362 int readcnt
= nextpem(p
, plen
, &pem
);
363 if (!readcnt
&& p
== certs
) {
364 /* assume it's a DER cert */
365 SecCertificateRef cert
;
366 CFDataRef der
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
367 (UInt8
*)certs
, certslen
, kCFAllocatorNull
);
372 cert
= createvalidcert(der
);
376 e
->f(e
->u
, "Invalid CA certificate bad DER data");
380 CFArrayAppendValue(a
, cert
);
383 } else if (readcnt
== -1) {
385 e
->f(e
->u
, "Invalid CA certificate #%u (offset %u) in bundle",
386 (unsigned)cnt
, (unsigned)(p
-certs
));
389 } else if (readcnt
&& pem
.type
== pemtype_certificate
) {
390 CFDataRef der
= CFDataCreateFromBase64(kCFAllocatorDefault
, pem
.body
, pem
.bodylen
);
391 SecCertificateRef cert
;
394 e
->f(e
->u
, "Invalid CA certificate #%u (offset %u) bad base 64 in bundle",
395 (unsigned)cnt
, (unsigned)(pem
.start
-certs
));
399 cert
= createvalidcert(der
);
403 e
->f(e
->u
, "Invalid CA certificate #%u (offset %u) bad cert data in bundle",
404 (unsigned)cnt
, (unsigned)(pem
.start
-certs
));
408 CFArrayAppendValue(a
, cert
);
411 } else if (!readcnt
) break;
412 plen
-= (pem
.start
+ pem
.len
) - p
;
413 p
= pem
.start
+ pem
.len
;
415 if (!CFArrayGetCount(a
)) {
423 /* note that we use geteuid and not getuid because any created files will
424 * end up being owned by geteuid and therefore using geteuid()'s HOME wil
425 * end up being the least disruptive and also if geteuid() != getuid() then
426 * we probably can't read getuid()'s HOME anyway so that's a guaranteed fail */
427 char *home
; /* "HOME=..." from getpwuid(geteuid()) if different from environ */
428 char *cur_home
; /* "HOME=..." as found in environ if home set otherwise NULL */
431 static char *find_home_env(void)
433 char ***eptr
= _NSGetEnviron();
435 if (!eptr
) return NULL
;
437 while (*ptr
&& strncmp(*ptr
, "HOME=", 5)) {
440 return *ptr
? *ptr
: NULL
;
443 static void get_home_dirs(homedirs_t
*dirs
)
445 struct passwd
*pwinf
;
449 dirs
->cur_home
= find_home_env();
450 pwinf
= getpwuid(geteuid());
451 if (pwinf
&& pwinf
->pw_dir
&&
452 (!dirs
->cur_home
|| strcmp(dirs
->cur_home
+5, pwinf
->pw_dir
)))
453 asprintf(&dirs
->home
, "HOME=%s", pwinf
->pw_dir
);
455 dirs
->cur_home
= NULL
;
458 static void free_home_dirs(homedirs_t
*dirs
)
466 char pw
[16]; /* random 15-character (0x20-0x7e), NULL terminated password */
467 char loc
[1]; /* Always will have at least a NULL byte */
470 static void gen_rand_pw(void *_out
, size_t len
)
472 unsigned char *out
= (unsigned char *)_out
;
474 if (!out
|| !len
) return;
475 fd
= open("/dev/random", O_RDONLY
);
480 cnt
= read(fd
, out
, len
);
481 } while (cnt
== -1 && errno
== EINTR
);
482 if (cnt
<= 0) return;
483 for (i
= 0; i
< cnt
; ++i
) {
484 out
[i
] = (unsigned char)((((unsigned)out
[i
] * 95) >> 8) + 32);
492 static tempch_t
*new_temp_keych(void)
495 char newdir
[PATH_MAX
];
497 CFStringRef tempdir
= CFCopyTemporaryDirectory();
499 if (!tempdir
) return NULL
;
500 okay
= CFStringGetCString(tempdir
, newdir
, sizeof(newdir
) - 32, kCFStringEncodingUTF8
);
502 if (!okay
) return NULL
;
503 strcat(newdir
, "/tch.XXXXXX");
504 ans
= (tempch_t
*)malloc(sizeof(tempch_t
) + strlen(newdir
) + 14 /* "/temp.keychain" */);
505 if (!ans
) return NULL
;
507 strcpy(ans
->loc
, newdir
);
508 strlcpy(ans
->pw
, "(:vCZ\"t{UA-zl3g", sizeof(ans
->pw
)); /* fallback if random fails */
509 gen_rand_pw(ans
->pw
, sizeof(ans
->pw
)-1);
510 ans
->pw
[sizeof(ans
->pw
)-1] = '\0';
511 if (!mkdtemp(ans
->loc
)) {
515 strcat(ans
->loc
, "/temp.keychain");
519 static void del_temp_keych(tempch_t
*keych
)
523 l
= strlen(keych
->loc
);
524 if (l
> 14 && !strcmp(keych
->loc
+ (l
- 14), "/temp.keychain")) {
527 (void)SecKeychainLock(keych
->ref
);
528 (void)SecKeychainDelete(keych
->ref
);
529 CFRelease(keych
->ref
);
533 keych
->loc
[l
- 14] = '\0';
534 /* the keychain code may leave dot, possibly comma and yet other turds
535 * we may have to remove */
536 d
= opendir(keych
->loc
);
539 while ((ent
=readdir(d
)) != NULL
) {
541 if (ent
->d_name
[0] == '.' &&
542 (ent
->d_name
[1] == '\0'
543 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0'))) continue;
544 snprintf(turd
, sizeof(turd
), "%s/%s", keych
->loc
, ent
->d_name
);
554 static CFDataRef
extract_key_copy(CFDataRef pemseq
, int *outpem
)
556 const char *p
= (char *)CFDataGetBytePtr(pemseq
);
557 const char *origp
= p
;
558 size_t l
= (size_t)CFDataGetLength(pemseq
);
563 int readcnt
= nextpem(p
, l
, &pem
);
564 if (!readcnt
&& p
== origp
) {
565 /* Assume it's DER data */
569 if (!readcnt
|| readcnt
== -1) return NULL
;
570 if (pem
.type
== pemtype_privatekey_rsa
) {
572 if (pem
.start
== origp
&& pem
.len
== origl
) {
576 return CFDataCreate(kCFAllocatorDefault
, (uint8_t *)pem
.start
, pem
.len
);
578 l
-= (size_t)readcnt
;
579 p
+= (size_t)readcnt
;
584 SecIdentityRef
cSecIdentityCreateWithCertificateAndKeyData(
585 SecCertificateRef cert
, CFDataRef keydata
, CFTypeRef pw
, CFStringRef hint
,
589 CFDataRef rawkey
= NULL
;
590 tempch_t
*keych
= NULL
;
592 SecKeychainRef keychain
= NULL
;
593 SecExternalFormat format
;
594 SecExternalItemType type
;
595 SecItemImportExportKeyParameters params
;
596 CFArrayRef items
= NULL
;
597 SecKeyRef key
= NULL
;
598 SecIdentityRef ans
= NULL
;
600 if (!cert
|| !kh
) return NULL
;
602 rawkey
= extract_key_copy(keydata
, &ispem
);
605 CFArrayRef searchlist
= NULL
;
606 keych
= new_temp_keych();
608 /* SecKeychainCreate has the side effect of adding the new keychain to
609 * the search list which will make it show up in other apps.
610 * SecKeychainDelete removes it from the search list, or we can also get
611 * the search list before the create and restore it right after.
612 * By immediately restoring the search list, we avoid having the new
613 * private key we're importing be searchable by default in other apps.
614 * If we are running with HOME != ~geteuid() then we likely have no
615 * ~/Library/Preferences/com.apple.security.plist which means the system
616 * will "helpfully" set the default keychain to this new keychain we've
617 * just created which is very bad. If Xcode is running it will listen
618 * to that event and then call SecKeychainSetDefault with that very
619 * same temporary keychain (I have no idea why it does this stupid thing)
620 * and that will make it permanent for the user. Ugh. To avoid this,
621 * we temporarily set HOME to getpwuid(geteuid())->pw_dir while we are
622 * creating the temporary keychain and then put HOME back the way it was
623 * immediately thereafter. Git likes to run tests with HOME set to
624 * alternate locations so it's prudent to handle this. */
625 get_home_dirs(&dirs
);
628 err
= SecKeychainCopySearchList(&searchlist
);
629 if (err
|| !searchlist
) {
630 free_home_dirs(&dirs
);
633 err
= SecKeychainCreate(keych
->loc
, sizeof(keych
->pw
), keych
->pw
, false,
635 if (err
|| !keychain
) {
636 free_home_dirs(&dirs
);
637 CFRelease(searchlist
);
640 keych
->ref
= keychain
;
641 err
= SecKeychainSetSearchList(searchlist
);
644 putenv(dirs
.cur_home
);
648 free_home_dirs(&dirs
);
649 CFRelease(searchlist
);
651 err
= SecKeychainUnlock(keychain
, sizeof(keych
->pw
), keych
->pw
, true);
654 SecKeychainSettings settings
;
655 settings
.version
= SEC_KEYCHAIN_SETTINGS_VERS1
;
656 settings
.lockOnSleep
= false;
657 settings
.useLockInterval
= false;
658 settings
.lockInterval
= INT_MAX
;
659 (void)SecKeychainSetSettings(keychain
, &settings
);
661 format
= ispem
? kSecFormatWrappedOpenSSL
: kSecFormatOpenSSL
;
662 type
= kSecItemTypePrivateKey
;
663 memset(¶ms
, 0, sizeof(params
));
664 params
.version
= SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION
;
665 params
.flags
= kSecKeyImportOnlyOne
|kSecKeyNoAccessControl
;
667 params
.passphrase
= pw
;
669 params
.flags
|= kSecKeySecurePassphrase
;
670 /* Note that params.alertTitle is ignored */
671 params
.alertPrompt
= hint
;
673 err
= cSecItemImport(rawkey
, NULL
, &format
, &type
,
674 ispem
?kSecItemPemArmour
:0, ¶ms
, keychain
, &items
);
676 if (!err
&& items
&& CFArrayGetCount(items
) == 1 &&
677 CFGetTypeID((CFTypeRef
)CFArrayGetValueAtIndex(items
, 0)) == SecKeyGetTypeID()) {
678 key
= (SecKeyRef
)CFArrayGetValueAtIndex(items
, 0);
681 if (items
) CFRelease(items
);
685 /* If we have a key we must also have a keychain */
686 err
= cSecIdentityCreateWithCertificate(keychain
, cert
, &ans
);
689 /* We MUST NOT call SecKeychainDelete because that will purge all copies of
690 * the keychain from memory. We've already removed it from the search list
691 * so we just release it and remove the disk files instead in order to allow
692 * the in memory copy to remain unmolested. Unfortunately on older systems
693 * this is not good enough, so we have to leave the keychain itself around. */
695 del_temp_keych(keych
);
698 if (!ans
&& (!rawkey
|| (!ispem
&& !key
))) {
699 /* Try again with the default keychain list, but only if a key was not
700 * provided or was provided in non-PEM and we failed to import it. */
701 err
= cSecIdentityCreateWithCertificate(NULL
, cert
, &ans
);
706 del_temp_keych(keych
);
710 void DisposeIdentityKeychainHandle(void *ch
)
712 del_temp_keych((tempch_t
*)ch
);
715 CFArrayRef
CreateClientAuthWithCertificatesAndKeyData(CFArrayRef certs
,
716 CFDataRef keydata
, CFTypeRef pw
,
717 CFStringRef hint
, void **kh
)
719 CFMutableArrayRef ans
;
721 SecCertificateRef cert
;
722 SecIdentityRef identity
;
724 if (!certs
|| !keydata
) return NULL
;
725 count
= (size_t)CFArrayGetCount(certs
);
726 if (count
< 1) return NULL
;
727 cert
= (SecCertificateRef
)CFArrayGetValueAtIndex(certs
, 0);
728 if (CFGetTypeID(cert
) != SecCertificateGetTypeID()) return NULL
;
729 ans
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
730 if (!ans
) return NULL
;
731 identity
= cSecIdentityCreateWithCertificateAndKeyData(cert
, keydata
, pw
,
737 CFArrayAppendValue(ans
, identity
);
738 for (i
= 1; i
< count
; ++i
) {
739 CFArrayAppendValue(ans
, CFArrayGetValueAtIndex(certs
, i
));
744 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
746 #ifndef kCFCoreFoundationVersionNumber10_8
747 #define kCFCoreFoundationVersionNumber10_8 744.00
765 OSStatus (*fSSLSetTrustedRoots
)(SSLContextRef
, CFArrayRef
, Boolean
);
766 OSStatus (*fSSLGetPeerSecTrust
)(SSLContextRef
, SecTrustRef
*);
767 OSStatus (*fSSLCopyPeerTrust
)(SSLContextRef
, SecTrustRef
*);
768 OSStatus (*fSecTrustGetResult
)(SecTrustRef
, SecTrustResultType
*,
769 CFArrayRef
*, CSSM_TP_APPLE_EVIDENCE_INFO
**);
770 OSStatus (*fSecTrustSetAnchorCertificatesOnly
)(SecTrustRef
, Boolean
);
771 OSStatus (*fSSLGetPeerCertificates
)(SSLContextRef cxt
, CFArrayRef
*certs
);
772 OSStatus (*fSSLCopyPeerCertificates
)(SSLContextRef cxt
, CFArrayRef
*certs
);
773 OSStatus (*fSSLSetProtocolVersionEnabled
)(SSLContextRef cxt
, SmallEnum
, Boolean
);
774 OSStatus (*fSSLSetProtocolVersionMin
)(SSLContextRef cxt
, SmallEnum
);
775 OSStatus (*fSSLSetProtocolVersionMax
)(SSLContextRef cxt
, SmallEnum
);
776 OSStatus (*fSSLSetSessionOption
)(SSLContextRef
, SmallEnum
, Boolean
);
777 SecCertificateRef (*fSecCertificateCreateWithData
)(CFAllocatorRef
, CFDataRef
);
778 OSStatus (*fSecCertificateCreateFromData
)(const cCSSM_DATA
*, CSSM_CERT_TYPE
,
779 CSSM_CERT_ENCODING
, SecCertificateRef
*);
780 OSStatus (*fSecCertificateGetData
)(SecCertificateRef
, cCSSM_DATA
*);
781 CFDataRef (*fSecCertificateCopyData
)(SecCertificateRef
);
782 OSStatus (*fSecKeychainItemImport
)(
783 CFDataRef
,CFStringRef
,SecExternalFormat
*,SecExternalItemType
*,
784 SecItemImportExportFlags
,const SecKeyImportExportParameters
*,
785 SecKeychainRef
,CFArrayRef
*);
786 OSStatus (*fSecItemImport
)(
787 CFDataRef
,CFStringRef
,SecExternalFormat
*,SecExternalItemType
*,
788 SecItemImportExportFlags
,const SecItemImportExportKeyParameters
*,
789 SecKeychainRef
,CFArrayRef
*);
790 OSStatus (*fSecIdentityCreateWithCertificate
)(CFTypeRef
,SecCertificateRef
,SecIdentityRef
*);
791 OSStatus (*fSSLNewContext
)(Boolean
,SSLContextRef
*);
792 OSStatus (*fSSLDisposeContext
)(SSLContextRef
);
793 SSLContextRef (*fSSLCreateContext
)(CFAllocatorRef
,SmallEnum
,SmallEnum
);
794 OSStatus (*fSecKeychainSearchCreateFromAttributes
)(CFTypeRef
,int,
795 const SecKeychainAttributeList
*,SecKeychainSearchRef
*);
796 OSStatus (*fSecKeychainSearchCopyNext
)(SecKeychainSearchRef
,SecKeychainItemRef
*);
799 static void stcompat_initialize(void)
801 #define LOOKUP(name) *((void **)&fnc.f##name) = dlsym(RTLD_NEXT, #name)
802 LOOKUP(SSLSetTrustedRoots
);
803 LOOKUP(SSLGetPeerSecTrust
);
804 LOOKUP(SSLCopyPeerTrust
);
805 LOOKUP(SecTrustGetResult
);
806 LOOKUP(SecTrustSetAnchorCertificatesOnly
);
807 LOOKUP(SSLGetPeerCertificates
);
808 LOOKUP(SSLCopyPeerCertificates
);
809 LOOKUP(SSLSetProtocolVersionEnabled
);
810 LOOKUP(SSLSetProtocolVersionMin
);
811 LOOKUP(SSLSetProtocolVersionMax
);
812 LOOKUP(SSLSetSessionOption
);
813 LOOKUP(SecCertificateCreateWithData
);
814 LOOKUP(SecCertificateCreateFromData
);
815 LOOKUP(SecCertificateGetData
);
816 LOOKUP(SecCertificateCopyData
);
817 LOOKUP(SecKeychainItemImport
);
818 LOOKUP(SecItemImport
);
819 LOOKUP(SecIdentityCreateWithCertificate
);
820 LOOKUP(SSLNewContext
);
821 LOOKUP(SSLDisposeContext
);
822 LOOKUP(SSLCreateContext
);
823 LOOKUP(SecKeychainSearchCreateFromAttributes
);
824 LOOKUP(SecKeychainSearchCopyNext
);
828 OSStatus
cSSLSetTrustedRoots(SSLContextRef cxt
, CFArrayRef rts
, Boolean replace
)
830 if (fnc
.fSSLSetTrustedRoots
)
831 return fnc
.fSSLSetTrustedRoots(cxt
, rts
, replace
);
835 OSStatus
cSSLCopyPeerTrust(SSLContextRef cxt
, SecTrustRef
*trust
)
837 if (fnc
.fSSLCopyPeerTrust
)
838 return fnc
.fSSLCopyPeerTrust(cxt
, trust
);
839 if (fnc
.fSSLGetPeerSecTrust
) {
840 OSStatus err
= fnc
.fSSLGetPeerSecTrust(cxt
, trust
);
841 if (!err
) CFRetain(*trust
);
847 OSStatus
cSecTrustGetResult(SecTrustRef trust
, SecTrustResultType
*result
,
848 CFArrayRef
*certChain
, CSSM_TP_APPLE_EVIDENCE_INFO
**statusChain
)
850 if (fnc
.fSecTrustGetResult
)
851 return fnc
.fSecTrustGetResult(trust
, result
, certChain
, statusChain
);
855 OSStatus
cSSLCopyPeerCertificates(SSLContextRef cxt
, CFArrayRef
*certs
)
857 if (!certs
|| !cxt
) return paramErr
;
859 if (fnc
.fSSLCopyPeerCertificates
)
860 return fnc
.fSSLCopyPeerCertificates(cxt
, certs
);
861 if (fnc
.fSSLGetPeerCertificates
) {
862 OSStatus err
= fnc
.fSSLGetPeerCertificates(cxt
, certs
);
863 if (!err
&& *certs
) {
864 size_t i
, c
= (size_t)CFArrayGetCount(*certs
);
865 for (i
= 0; i
< c
; ++i
) {
866 CFTypeRef item
= (CFTypeRef
)CFArrayGetValueAtIndex(*certs
, i
);
867 if (item
) CFRelease(item
);
875 OSStatus
cSecTrustSetAnchorCertificatesOnly(SecTrustRef cxt
, Boolean anchorsOnly
)
877 if (fnc
.fSecTrustSetAnchorCertificatesOnly
)
878 return fnc
.fSecTrustSetAnchorCertificatesOnly(cxt
, anchorsOnly
);
882 OSStatus
cSSLSetProtocolVersionMinMax(SSLContextRef cxt
, int minVer
, int maxVer
)
886 if (minVer
< 0 || maxVer
< 0 || minVer
> 8 || maxVer
> 8 || minVer
> maxVer
)
889 if (minVer
== kSSLProtocolUnknown
) minVer
= kSSLProtocol3
;
890 if (minVer
== kSSLProtocolAll
) minVer
= kSSLProtocol3
;
891 if (minVer
== kSSLProtocol3Only
) minVer
= kSSLProtocol3
;
892 if (minVer
== kTLSProtocol1Only
) minVer
= kTLSProtocol1
;
894 if (maxVer
== kSSLProtocol3Only
) maxVer
= kSSLProtocol3
;
895 if (maxVer
== kTLSProtocol1Only
) maxVer
= kTLSProtocol1
;
896 if (maxVer
== kSSLProtocolAll
) maxVer
= kTLSProtocol12
;
897 if (maxVer
== kSSLProtocolUnknown
) maxVer
= kTLSProtocol12
;
899 if (kCFCoreFoundationVersionNumber
< kCFCoreFoundationVersionNumber10_8
&&
900 minVer
<= kTLSProtocol1
&& maxVer
> kTLSProtocol1
)
901 maxVer
= kTLSProtocol1
;
903 if (fnc
.fSSLSetProtocolVersionMin
&& fnc
.fSSLSetProtocolVersionMax
) {
904 err
= fnc
.fSSLSetProtocolVersionMin(cxt
, minVer
);
906 err
= fnc
.fSSLSetProtocolVersionMax(cxt
, maxVer
);
909 if (fnc
.fSSLSetProtocolVersionEnabled
) {
910 #define ENABLEPROTO(x) fnc.fSSLSetProtocolVersionEnabled(cxt, (int)(x), \
911 minVer <= x && x <= maxVer)
912 err
= ENABLEPROTO(kSSLProtocol2
);
913 if (err
&& minVer
> kSSLProtocol2
) err
= noErr
; /* ignore SSL2 disable error */
914 if (!err
) ENABLEPROTO(kSSLProtocol3
);
915 if (!err
) ENABLEPROTO(kTLSProtocol1
);
916 if (kCFCoreFoundationVersionNumber
>= kCFCoreFoundationVersionNumber10_8
||
917 maxVer
> kTLSProtocol1
) {
918 if (!err
) ENABLEPROTO(kTLSProtocol11
);
919 if (!err
) ENABLEPROTO(kTLSProtocol12
);
927 OSStatus
cSSLSetSessionOption(SSLContextRef cxt
, int option
, Boolean value
)
929 if (fnc
.fSSLSetSessionOption
)
930 return fnc
.fSSLSetSessionOption(cxt
, option
, value
);
935 SecCertificateRef
cSecCertificateCreateWithData(CFAllocatorRef a
, CFDataRef d
)
937 if (fnc
.fSecCertificateCreateWithData
)
938 return fnc
.fSecCertificateCreateWithData(a
, d
);
939 else if (fnc
.fSecCertificateCreateFromData
) {
942 SecCertificateRef cacert
= NULL
;
944 certdata
.Length
= (size_t)CFDataGetLength(d
);
945 certdata
.Data
= (uint8
*)CFDataGetBytePtr(d
);
946 err
= fnc
.fSecCertificateCreateFromData(&certdata
, CSSM_CERT_X_509v3
,
947 CSSM_CERT_ENCODING_DER
, &cacert
);
955 CFDataRef
cSecCertificateCopyData(SecCertificateRef c
)
958 if (CFGetTypeID(c
) != SecCertificateGetTypeID()) return NULL
;
959 if (fnc
.fSecCertificateCopyData
)
960 return fnc
.fSecCertificateCopyData(c
);
961 if (fnc
.fSecCertificateGetData
) {
963 OSStatus err
= fnc
.fSecCertificateGetData(c
, &certdata
);
964 if (err
|| !certdata
.Data
|| !certdata
.Length
) return NULL
;
965 return CFDataCreate(kCFAllocatorDefault
, certdata
.Data
, certdata
.Length
);
970 Boolean
SecCertsEqual(SecCertificateRef c1
, SecCertificateRef c2
)
975 d1
= cSecCertificateCopyData(c1
);
976 if (!d1
) return false;
977 d2
= cSecCertificateCopyData(c2
);
982 l1
= CFDataGetLength(d1
);
983 l2
= CFDataGetLength(d2
);
985 const void *p1
= (void *)CFDataGetBytePtr(d1
);
986 const void *p2
= (void *)CFDataGetBytePtr(d2
);
987 ans
= memcmp(p1
, p2
, l1
) == 0;
994 Boolean
SecCertInArray(SecCertificateRef c
, CFArrayRef a
)
997 if (!c
|| !a
|| !CFArrayGetCount(a
)) return false;
998 cnt
= CFArrayGetCount(a
);
999 for (i
= 0; i
< cnt
; ++i
) {
1000 if (SecCertsEqual(c
, (SecCertificateRef
)CFArrayGetValueAtIndex(a
, i
)))
1006 OSStatus
CopyIdentityWithLabel(const char *label
, SecIdentityRef
*out
)
1008 if (fnc
.fSecKeychainSearchCreateFromAttributes
&&
1009 fnc
.fSecKeychainSearchCopyNext
) {
1010 SecIdentityRef ans
= NULL
;
1011 SecCertificateRef cert
= NULL
;
1012 SecKeychainAttribute at
;
1013 SecKeychainAttributeList al
;
1014 SecKeychainSearchRef sr
;
1017 at
.tag
= kSecLabelItemAttr
;
1018 at
.length
= strlen(label
);
1019 at
.data
= (char *)label
;
1022 err
= fnc
.fSecKeychainSearchCreateFromAttributes(NULL
,
1023 kSecCertificateItemClass
, &al
, &sr
);
1025 if (err
|| !sr
) return err
;
1026 while ((err
= fnc
.fSecKeychainSearchCopyNext(sr
, (SecKeychainItemRef
*)&cert
)) == noErr
) {
1027 if (!cert
) continue;
1028 if (CFGetTypeID(cert
) != SecCertificateGetTypeID()) {CFRelease(cert
); continue;}
1029 err
= cSecIdentityCreateWithCertificate(NULL
, cert
, &ans
);
1031 if (!err
&& ans
) break;
1038 return errSecItemNotFound
;
1043 OSStatus
cSecItemImport(
1044 CFDataRef importedData
, CFStringRef fileNameOrExtension
,
1045 SecExternalFormat
*inputFormat
, SecExternalItemType
*itemType
,
1046 SecItemImportExportFlags flags
, const SecItemImportExportKeyParameters
*keyParams
,
1047 SecKeychainRef importKeychain
, CFArrayRef
*outItems
)
1049 if (fnc
.fSecItemImport
)
1050 return fnc
.fSecItemImport(importedData
, fileNameOrExtension
, inputFormat
,
1051 itemType
, flags
, keyParams
, importKeychain
, outItems
);
1052 else if (fnc
.fSecKeychainItemImport
) {
1053 SecKeyImportExportParameters oldKeyParams
;
1054 SecKeyImportExportParameters
*op
= NULL
;
1057 memset(&oldKeyParams
, 0, sizeof(oldKeyParams
));
1058 oldKeyParams
.version
= keyParams
->version
;
1059 oldKeyParams
.flags
= keyParams
->flags
;
1060 oldKeyParams
.passphrase
= keyParams
->passphrase
;
1061 oldKeyParams
.alertTitle
= keyParams
->alertTitle
;
1062 oldKeyParams
.alertPrompt
= keyParams
->alertPrompt
;
1063 oldKeyParams
.accessRef
= keyParams
->accessRef
;
1064 /* We punt on keyUsage and keyAttributes and do not convert them */
1066 return fnc
.fSecKeychainItemImport(importedData
, fileNameOrExtension
, inputFormat
,
1067 itemType
, flags
, op
, importKeychain
, outItems
);
1072 OSStatus
cSecIdentityCreateWithCertificate(CFTypeRef k
, SecCertificateRef c
,
1075 /* The documentation lies and this is actually present in later 10.4 versions */
1076 if (fnc
.fSecIdentityCreateWithCertificate
)
1077 return fnc
.fSecIdentityCreateWithCertificate(k
, c
, i
);
1081 SSLContextRef
cSSLCreateContext(CFAllocatorRef a
, int ps
, int ct
)
1083 if (fnc
.fSSLCreateContext
)
1084 return fnc
.fSSLCreateContext(a
, ps
, ct
);
1085 if ((ps
!= kSSLServerSide
&& ps
!= kSSLClientSide
) || (ct
!= kSSLStreamType
))
1087 if (fnc
.fSSLNewContext
&& fnc
.fSSLDisposeContext
) {
1089 OSStatus err
= fnc
.fSSLNewContext(ps
== kSSLServerSide
, &cxt
);
1090 return err
? NULL
: cxt
;
1095 void cSSLDisposeContext(SSLContextRef c
)
1097 if (fnc
.fSSLCreateContext
)
1099 else if (fnc
.fSSLDisposeContext
)
1100 fnc
.fSSLDisposeContext(c
);
1103 CF_INLINE
bool is_ldh(int c
)
1106 ('A' <= c
&& c
<= 'Z') ||
1107 ('a' <= c
&& c
<= 'z') ||
1108 ('0' <= c
&& c
<= '9') ||
1112 CF_INLINE
size_t get_label_len(const char *p
, size_t l
)
1115 while (l
-- && is_ldh(*p
++)) ++ans
;
1119 static bool is_dns_name(const void *_p
, size_t l
, bool wcok
)
1121 const char *p
= (char *)_p
;
1124 if (!p
) return false;
1125 if (l
>= 1 && p
[l
-1] == '.') --l
;
1126 if (!l
) return false;
1127 if (l
> 255) return false;
1129 size_t lablen
= get_label_len(p
, l
);
1130 if (lablen
> 63) return false;
1131 if (!idx
&& !lablen
&& wcok
&& l
>= 2 && p
[0] == '*' && p
[1] == '.') lablen
=1;
1132 if (!lablen
) return false;
1133 if (p
[0] == '-' || p
[lablen
- 1] == '-') return false;
1135 if (p
[lablen
] != '.') return false;
1145 CF_INLINE
char clc(char c
)
1147 return 'A' <= c
&& c
<= 'Z' ? c
- 'A' + 'a' : c
;
1150 CF_INLINE
bool matchicase(const char *p1
, const char *p2
, size_t l
)
1153 if (clc(*p1
++) != clc(*p2
++)) return false;
1158 static bool peername_matches_id(const char *peername
, const char *idname
)
1161 if (!peername
|| !idname
) return false;
1162 pl
= strlen(peername
);
1163 il
= strlen(idname
);
1164 if (!is_dns_name(peername
, pl
, false) || !is_dns_name(idname
, il
, true))
1167 if (peername
[pl
- 1] == '.') --pl
;
1168 if (idname
[il
- 1] == '.') --il
;
1170 size_t pll
= get_label_len(peername
, pl
);
1171 size_t ill
= get_label_len(idname
, il
);
1172 if (!idx
&& !ill
&& il
>= 2 && idname
[0] == '*' && idname
[1] == '.') ill
=1;
1174 if (peername
[pll
] != '.') return false;
1178 if (idname
[ill
] != '.') return false;
1181 if (idx
|| idname
[0] != '*') {
1182 if (pll
!= ill
) return false;
1183 if (!matchicase(peername
, idname
, pll
)) return false;
1194 CF_INLINE
size_t get_num_len(const char *p
, size_t l
)
1197 while (l
-- && '0' <= *p
&& *p
<= '9') {
1204 Boolean
IsIPv4Name(const void *_p
, size_t l
)
1206 const char *p
= (char *)_p
;
1209 if (!p
|| l
< 7) return false;
1212 if (++idx
> 4) return false;
1213 lablen
= get_num_len(p
, l
);
1214 if (lablen
> 3) return false;
1215 if (lablen
>= 2 && *p
== '0') return false;
1216 else if (lablen
== 3) {
1217 if (*p
>= '3') return false;
1219 if (p
[1] >= '6') return false;
1220 if (p
[1] == '5' && p
[2] > '5') return false;
1224 if (p
[lablen
] != '.') return false;
1233 static bool parse_ipv4_name(const void *_p
, size_t l
, uint8_t ipv4
[4])
1235 unsigned short s
[4];
1237 const char *p
= (char *)_p
;
1238 if (!IsIPv4Name(p
, l
) || l
> 15) return false;
1239 memcpy(ipv4str
, p
, l
);
1241 if (sscanf(ipv4str
, "%hu.%hu.%hu.%hu", s
, s
+1, s
+2, s
+3) == 4) {
1242 ipv4
[0] = (uint8_t)s
[0];
1243 ipv4
[1] = (uint8_t)s
[1];
1244 ipv4
[2] = (uint8_t)s
[2];
1245 ipv4
[3] = (uint8_t)s
[3];
1251 static bool parse_ipv6_name(const void *_p
, size_t l
, uint8_t ipv6
[16])
1253 char ipv6str
[INET6_ADDRSTRLEN
];
1254 const char *p
= (char *)_p
;
1256 if (!p
) return false;
1257 if (l
>= 1 && p
[0] == '[') {
1261 if (l
>= 1 && p
[l
-1] == ']') --l
;
1262 pct
= (char *)(l
? memchr(p
, '%', l
) : NULL
);
1263 if (pct
) l
= pct
- p
;
1264 if (l
< 3 || l
>= INET6_ADDRSTRLEN
) return false;
1265 memcpy(ipv6str
, p
, l
);
1267 return inet_pton(AF_INET6
, ipv6str
, ipv6
) == 1;
1275 #define U(x) ((const uint8_t *)(x))
1276 static const data_t OID_BasicConstraints
= {U("\006\003\125\035\023"), 5};
1277 static const data_t OID_SubjectAltName
= {U("\006\003\125\035\021"), 5};
1278 static const data_t OID_SubjectKeyIdentifier
= {U("\006\003\125\035\016"), 5};
1279 static const data_t OID_AuthorityKeyIdentifier
= {U("\006\003\125\035\043"), 5};
1280 static const data_t OID_CommonName
= {U("\006\003\125\004\003"), 5};
1284 uint8_t clas
; /* 0, 1, 2, or 3 */
1285 uint8_t cons
; /* 0 or 1 */
1286 uint8_t rawtag
; /* raw value of first byte of tag */
1287 uint32_t tag
; /* tag value */
1288 size_t hl
; /* length of header excluding actual data */
1289 size_t dl
; /* length of actual data */
1293 uint8_t vers
; /* 0 => v1, 1 => v2, 2 => v3 */
1294 uint8_t caFlag
; /* 0 unless basic constraints present then 0x80=critial 0x01=value */
1295 uint8_t isCA
; /* true if caFlag==0x81 or subject==issuer && vers < 2 */
1296 uint8_t isRoot
; /* true if isCA and subject == issuer */
1297 data_t subject
; /* points to sequence */
1298 data_t subjectAltNames
; /* null unless v3 extension present, points to sequence */
1299 data_t subjectKeyId
; /* null unless v3 extension present, points to raw bytes */
1300 data_t issuer
; /* points to sequence */
1301 data_t issuerKeyId
; /* null unless v3 extension present, points to raw bytes */
1304 static bool read_der_atom(const data_t
*d
, der_atom_t
*o
)
1309 if (!d
|| !d
->d
|| !d
->l
|| !o
) return false;
1310 o
->clas
= (*d
->d
>> 6) & 0x3;
1311 o
->cons
= (*d
->d
>> 5) & 0x1;
1318 if (pos
>= d
->l
) return false;
1322 } while (byte
& 0x80);
1325 if (pos
>= d
->l
) return false;
1328 unsigned cnt
= byte
& 0x7f;
1329 if (!cnt
|| pos
+ cnt
> d
->l
) return false;
1338 if (pos
+ len
> d
->l
) return false;
1344 static bool is_der(const data_t
*_d
, bool exact_length_match_only
)
1347 if (!_d
|| !_d
->d
|| !_d
->l
) return false;
1352 if (!read_der_atom(&d
, &atom
)) return false;
1355 if ((atom
.rawtag
& 0xfe) != 0x30) {
1360 return d
.d
== _d
->d
+ _d
->l
|| !exact_length_match_only
;
1363 static int data_matches(const data_t
*o1
, const data_t
*o2
)
1365 if (!o1
|| !o2
|| !o1
->l
|| !o2
->l
|| o1
->l
!= o2
->l
)
1367 return memcmp(o1
->d
, o2
->d
, o1
->l
) == 0;
1370 static bool read_der_cert(const data_t
*_d
, der_cert_t
*o
)
1375 if (!_d
|| !_d
->d
|| !_d
->l
|| !o
) return false;
1376 if (!is_der(_d
, true)) return false;
1379 memset(o
, 0, sizeof(*o
));
1380 if (!read_der_atom(&d
, &atom
)) return false;
1381 if (atom
.rawtag
!= 0x30) return false;
1384 if (!read_der_atom(&d
, &atom
)) return false;
1385 if (atom
.rawtag
!= 0x30) return false;
1388 if (!read_der_atom(&d
, &atom
)) return false;
1389 if (atom
.rawtag
== 0xA0) {
1392 if (atom
.dl
!= 3 || d
.d
[0] != 2 || d
.d
[1] != 1) return false;
1393 o
->vers
= d
.d
[2]; /* not validated */
1396 if (!read_der_atom(&d
, &atom
)) return false;
1398 o
->vers
= 0; /* implied v1 */
1400 if (atom
.rawtag
!= 2) return false;
1401 /* skip serialNumber */
1402 d
.l
-= atom
.hl
+ atom
.dl
;
1403 d
.d
+= atom
.hl
+ atom
.dl
;
1404 if (!read_der_atom(&d
, &atom
)) return false;
1405 if (atom
.rawtag
!= 0x30) return false;
1406 /* skip signature */
1407 d
.l
-= atom
.hl
+ atom
.dl
;
1408 d
.d
+= atom
.hl
+ atom
.dl
;
1409 if (!read_der_atom(&d
, &atom
)) return false;
1410 if (atom
.rawtag
!= 0x30) return false;
1412 o
->issuer
.l
= atom
.hl
+ atom
.dl
;
1413 d
.l
-= atom
.hl
+ atom
.dl
;
1414 d
.d
+= atom
.hl
+ atom
.dl
;
1415 if (!read_der_atom(&d
, &atom
)) return false;
1416 if (atom
.rawtag
!= 0x30) return false;
1418 d
.l
-= atom
.hl
+ atom
.dl
;
1419 d
.d
+= atom
.hl
+ atom
.dl
;
1420 if (!read_der_atom(&d
, &atom
)) return false;
1421 if (atom
.rawtag
!= 0x30) return false;
1423 o
->subject
.l
= atom
.hl
+ atom
.dl
;
1424 d
.l
-= atom
.hl
+ atom
.dl
;
1425 d
.d
+= atom
.hl
+ atom
.dl
;
1426 if (!read_der_atom(&d
, &atom
)) return false;
1427 if (atom
.rawtag
!= 0x30) return false;
1428 /* skip subjectPublicKeyInfo */
1429 d
.l
-= atom
.hl
+ atom
.dl
;
1430 d
.d
+= atom
.hl
+ atom
.dl
;
1432 if (o
->vers
!= 2 || !d
.l
) break;
1433 if (!read_der_atom(&d
, &atom
)) return false;
1434 if (atom
.rawtag
== 0x81) {
1435 /* skip issuerUniqueID */
1436 d
.l
-= atom
.hl
+ atom
.dl
;
1437 d
.d
+= atom
.hl
+ atom
.dl
;
1439 if (!read_der_atom(&d
, &atom
)) return false;
1441 if (atom
.rawtag
== 0x82) {
1442 /* skip subjectUniqueID */
1443 d
.l
-= atom
.hl
+ atom
.dl
;
1444 d
.d
+= atom
.hl
+ atom
.dl
;
1446 if (!read_der_atom(&d
, &atom
)) return false;
1448 if (atom
.rawtag
!= 0xA3) return false;
1449 /* found v3 extensions */
1452 if (!read_der_atom(&d
, &atom
)) return false;
1453 if (atom
.rawtag
!= 0x30) return false;
1459 if (!read_der_atom(&d
, &atom
)) return false;
1460 if (atom
.rawtag
!= 0x30) return false;
1463 if (!read_der_atom(&d
, &atom
)) return false;
1464 if (atom
.rawtag
!= 6) return false;
1466 oid
.l
= atom
.hl
+ atom
.dl
;
1467 d
.l
-= atom
.hl
+ atom
.dl
;
1468 d
.d
+= atom
.hl
+ atom
.dl
;
1469 if (!read_der_atom(&d
, &atom
)) return false;
1470 if (atom
.rawtag
== 1) {
1471 /* skip over boolean but record its value */
1472 if (atom
.dl
!= 1) return false;
1473 crit
= *(d
.d
+ atom
.hl
);
1474 d
.l
-= atom
.hl
+ atom
.dl
;
1475 d
.d
+= atom
.hl
+ atom
.dl
;
1476 if (!read_der_atom(&d
, &atom
)) return false;
1478 if (atom
.rawtag
!= 4) return false;
1485 if (data_matches(&oid
, &OID_BasicConstraints
)) {
1486 if (!read_der_atom(&value
, &atom
)) return false;
1487 if (atom
.rawtag
!= 0x30) return false;
1491 /* CA flag is false and was properly omitted */
1492 o
->caFlag
= crit
? 0x80 : 0;
1494 if (!read_der_atom(&value
, &atom
)) return false;
1495 if (atom
.rawtag
== 1) {
1496 /* CA flag is present */
1497 if (atom
.dl
!= 1) return false;
1498 o
->caFlag
= (crit
? 0x80 : 0) | (*(value
.d
+ atom
.hl
) ? 0x1 : 0);
1501 } else if (data_matches(&oid
, &OID_SubjectAltName
)) {
1502 o
->subjectAltNames
.d
= value
.d
;
1503 o
->subjectAltNames
.l
= value
.l
;
1504 } else if (data_matches(&oid
, &OID_SubjectKeyIdentifier
)) {
1505 if (!read_der_atom(&value
, &atom
)) return false;
1506 if (atom
.rawtag
!= 4) return false;
1507 o
->subjectKeyId
.d
= value
.d
+ atom
.hl
;
1508 o
->subjectKeyId
.l
= atom
.dl
;
1509 } else if (data_matches(&oid
, &OID_AuthorityKeyIdentifier
)) {
1510 if (!read_der_atom(&value
, &atom
)) return false;
1511 if (atom
.rawtag
!= 0x30) return false;
1514 if (!read_der_atom(&value
, &atom
)) return false;
1515 if (atom
.rawtag
== 0x80) {
1516 o
->issuerKeyId
.d
= value
.d
+ atom
.hl
;
1517 o
->issuerKeyId
.l
= atom
.dl
;
1523 o
->isCA
= (o
->caFlag
|0x80) == 0x81; /* HACK: some old CAs aren't critical! */
1524 o
->isRoot
= (o
->isCA
&& o
->subject
.l
&& o
->subject
.l
== o
->issuer
.l
&&
1525 memcmp(o
->subject
.d
, o
->issuer
.d
, o
->subject
.l
) == 0) ? 1 : 0;
1527 o
->isCA
= (o
->subject
.l
&& o
->subject
.l
== o
->issuer
.l
&&
1528 memcmp(o
->subject
.d
, o
->issuer
.d
, o
->subject
.l
) == 0) ? 1 : 0;
1529 o
->isRoot
= o
->isCA
;
1534 Boolean
CheckCertOkay(SecCertificateRef _cert
)
1536 CFDataRef d
= cSecCertificateCopyData(_cert
);
1541 if (!d
) return false;
1542 data
.d
= CFDataGetBytePtr(d
);
1543 data
.l
= CFDataGetLength(d
);
1544 ans
= read_der_cert(&data
, &cert
);
1549 static void append_hex_dump(CFMutableStringRef s
, const void *_d
, size_t l
)
1551 const unsigned char *d
= (unsigned char *)_d
;
1552 CFStringAppendCString(s
, "<", kCFStringEncodingASCII
);
1555 sprintf(byte
, "%02X", *d
++);
1556 CFStringAppendCString(s
, byte
, kCFStringEncodingASCII
);
1558 CFStringAppendCString(s
, ">", kCFStringEncodingASCII
);
1561 static CFStringRef
CopyCertKeyId(SecCertificateRef _cert
, bool issuer
)
1563 CFDataRef d
= cSecCertificateCopyData(_cert
);
1564 CFMutableStringRef ans
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1573 if (!d
|| !ans
) break;
1574 data
.d
= CFDataGetBytePtr(d
);
1575 data
.l
= CFDataGetLength(d
);
1576 if (!read_der_cert(&data
, &cert
)) break;
1577 key
= issuer
? &cert
.issuerKeyId
: &cert
.subjectKeyId
;
1578 if (!key
->d
|| !key
->l
) break;
1579 for (i
= 0; i
< key
->l
; ++i
) {
1581 sprintf(hexbyte
, "%02X%s", (unsigned)key
->d
[i
], i
+1 == key
->l
? "" : ":");
1582 CFStringAppendCString(ans
, hexbyte
, kCFStringEncodingASCII
);
1587 if (d
) CFRelease(d
);
1588 if (!good
&& ans
) {CFRelease(ans
); ans
=NULL
;}
1598 static const oid_entry_t oid_table
[] = {
1599 {5, "\006\003\125\004\003", "CN"},
1600 {5, "\006\003\125\004\004", "SN"},
1601 {5, "\006\003\125\004\005", "serialNumber"},
1602 {5, "\006\003\125\004\006", "C"},
1603 {5, "\006\003\125\004\007", "L"},
1604 {5, "\006\003\125\004\010", "ST"},
1605 {5, "\006\003\125\004\011", "street"},
1606 {5, "\006\003\125\004\012", "O"},
1607 {5, "\006\003\125\004\013", "OU"},
1608 {5, "\006\003\125\004\014", "title"},
1609 {5, "\006\003\125\004\015", "description"},
1610 {5, "\006\003\125\004\017", "businessCategory"},
1611 {5, "\006\003\125\004\021", "postalCode"},
1612 {5, "\006\003\125\004\024", "telephoneNumber"},
1613 {5, "\006\003\125\004\027", "facsimileTelephoneNumber"},
1614 {5, "\006\003\125\004\052", "GN"},
1615 {5, "\006\003\125\004\053", "initials"},
1616 {5, "\006\003\125\004\054", "generationQualifier"},
1617 {5, "\006\003\125\004\056", "dnQualifier"},
1618 {5, "\006\003\125\004\101", "pseudonym"},
1619 {5, "\006\003\125\004\141", "organizationIdentifier"},
1620 {11, "\006\011\052\206\110\206\367\015\001\011\001", "emailAddress"},
1621 {12, "\006\012\011\222\046\211\223\362\054\144\001\001", "UID"},
1622 {12, "\006\012\011\222\046\211\223\362\054\144\001\031", "DC"},
1623 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\001", "jurisdictionOfIncorporationLocality"},
1624 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\002", "jurisdictionOfIncorporationStateOrProvince"},
1625 {13, "\006\013\053\006\001\004\001\202\067\074\002\001\003", "jurisdictionOfIncorporationCountry"}
1627 #define oid_table_size (sizeof(oid_table)/sizeof(oid_table[0]))
1629 static int comp_entry(const void *_e1
, const void *_e2
)
1631 const oid_entry_t
*o1
= (oid_entry_t
*)_e1
;
1632 const oid_entry_t
*o2
= (oid_entry_t
*)_e2
;
1635 if (o2
->l
< min
) min
= o2
->l
;
1636 ans
= memcmp(o1
->oid
, o2
->oid
, min
);
1637 if (ans
) return ans
;
1638 if (o1
->l
< o2
->l
) return -1;
1639 if (o1
->l
> o2
->l
) return 1;
1643 static void append_oid_name(CFMutableStringRef s
, const char *prefix
,
1644 const void *_oid
, size_t l
, const char *suffix
)
1646 oid_entry_t find
, *ans
;
1647 find
.oid
= (char *)_oid
;
1650 ans
= (oid_entry_t
*)
1651 bsearch(&find
, oid_table
, oid_table_size
, sizeof(find
), comp_entry
);
1652 if (prefix
&& *prefix
)
1653 CFStringAppendCString(s
, prefix
, kCFStringEncodingASCII
);
1655 CFStringAppendCString(s
, ans
->name
, kCFStringEncodingASCII
);
1657 CFMutableStringRef temp
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1658 const uint8_t *oid
= (uint8_t *)_oid
;
1661 const uint8_t *orig_oid
= oid
;
1662 if (!temp
|| l
< 3 || *oid
!= 6)
1669 if (!read_der_atom(&data
, &atom
))
1671 if (!bad
&& (atom
.rawtag
!= 6 || atom
.dl
< 1))
1674 oid
= data
.d
+ atom
.hl
;
1676 if (l
+ atom
.hl
!= orig_l
)
1686 if (!l
) {bad
=true; break;}
1690 idval
|= byte
& 0x7f;
1691 } while (!bad
&& (byte
& 0x80));
1698 } else if (idval
< 80) {
1699 x
= 1; y
= idval
- 40;
1701 x
= 2; y
= idval
- 80;
1703 snprintf(twoids
, sizeof(twoids
), "%u.%u", x
, y
);
1704 CFStringAppendCString(temp
, twoids
, kCFStringEncodingASCII
);
1708 snprintf(oneid
, sizeof(oneid
), ".%u", idval
);
1709 CFStringAppendCString(temp
, oneid
, kCFStringEncodingASCII
);
1712 } while (l
&& !bad
);
1714 if (bad
|| l
|| !temp
|| !CFStringGetLength(temp
))
1715 append_hex_dump(s
, orig_oid
, orig_l
);
1717 CFStringAppend(s
, temp
);
1721 if (suffix
&& *suffix
)
1722 CFStringAppendCString(s
, suffix
, kCFStringEncodingASCII
);
1725 #define DER_TAG_UTF8STRING 12
1726 #define DER_TAG_NUMERICSTRING 18
1727 #define DER_TAG_PRINTABLESTRING 19
1728 #define DER_TAG_TELETEXSTRING 20
1729 #define DER_TAG_VIDEOTEXSTRING 21
1730 #define DER_TAG_IA5STRING 22
1731 #define DER_TAG_GRAPHICSTRING 25
1732 #define DER_TAG_VISIBLESTRING 26
1733 #define DER_TAG_GENERALSTRING 27
1734 #define DER_TAG_UNIVERSALSTRING 28
1735 #define DER_TAG_BMPSTRING 30
1738 * 0x01 => strings only
1739 * 0x02 => 8-bit strings only
1740 * 0x04 => CN Ids strings only
1741 * 0x08 => wildcard CN okay
1742 * 0x10 => create output string
1744 static bool append_attr_value(CFMutableStringRef
*s
, const void *_d
,
1745 const der_atom_t
*a
, unsigned flags
)
1747 const uint8_t *d
= (uint8_t *)_d
;
1748 CFStringBuiltInEncodings encoding
= kCFStringEncodingASCII
;
1750 if (s
&& !(flags
& 0x10) && !*s
) return false;
1751 if (!s
|| !d
|| !a
|| !a
->dl
) return false;
1752 switch (a
->rawtag
) {
1753 case DER_TAG_UTF8STRING
:
1754 case DER_TAG_GRAPHICSTRING
:
1755 case DER_TAG_GENERALSTRING
:
1756 case DER_TAG_UNIVERSALSTRING
:
1757 encoding
= kCFStringEncodingUTF8
; break;
1758 case DER_TAG_NUMERICSTRING
:
1759 case DER_TAG_PRINTABLESTRING
:
1760 case DER_TAG_IA5STRING
:
1761 encoding
= kCFStringEncodingASCII
; break;
1762 case DER_TAG_TELETEXSTRING
:
1763 case DER_TAG_VIDEOTEXSTRING
:
1764 case DER_TAG_VISIBLESTRING
:
1765 encoding
= kCFStringEncodingISOLatin1
; break;
1766 case DER_TAG_BMPSTRING
:
1767 if (flags
& 0x06) return false;
1768 encoding
= kCFStringEncodingUnicode
; break;
1770 if (flags
& 0x05) return false;
1771 append_hex_dump(*s
, d
, a
->hl
+ a
->dl
);
1774 if (flags
& 0x04 && !is_dns_name(d
+a
->hl
, a
->dl
, !!(flags
& 0x08)))
1776 temp
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, d
+a
->hl
, a
->dl
,
1777 encoding
, true, kCFAllocatorNull
);
1780 *s
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1782 CFStringAppend(*s
, temp
);
1785 if (flags
& 0x05) return false;
1786 append_hex_dump(*s
, d
, a
->hl
+ a
->dl
);
1791 static CFStringRef
CopyCertName(SecCertificateRef _cert
, bool issuer
)
1793 CFDataRef d
= cSecCertificateCopyData(_cert
);
1794 CFMutableStringRef ans
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1802 bool badset
= false;
1804 if (!d
|| !ans
) break;
1805 data
.d
= CFDataGetBytePtr(d
);
1806 data
.l
= CFDataGetLength(d
);
1807 if (!read_der_cert(&data
, &cert
)) break;
1808 name
= issuer
? &cert
.issuer
: &cert
.subject
;
1811 if (data
.d
&& data
.l
) {
1812 if (!read_der_atom(&data
, &atom
)) break;
1813 if (atom
.rawtag
!= 0x30) break;
1818 unsigned setidx
= 0;
1820 if (!read_der_atom(&data
, &atom
)) break;
1821 if (atom
.rawtag
!= 0x31) break;
1822 set
.d
= data
.d
+ atom
.hl
;
1824 data
.l
-= atom
.hl
+ atom
.dl
;
1825 data
.d
+= atom
.hl
+ atom
.dl
;
1828 if (!read_der_atom(&set
, &atom
)) break;
1829 if (atom
.rawtag
!= 0x30) break;
1832 if (!read_der_atom(&set
, &atom
)) break;
1833 if (atom
.rawtag
!= 6) break;
1835 oid
.l
= atom
.hl
+ atom
.dl
;
1836 set
.l
-= atom
.hl
+ atom
.dl
;
1837 set
.d
+= atom
.hl
+ atom
.dl
;
1838 if (!read_der_atom(&set
, &atom
)) break;
1839 append_oid_name(ans
, setidx
++?"/+":"/", oid
.d
, oid
.l
, "=");
1840 append_attr_value(&ans
, set
.d
, &atom
, 0);
1841 set
.l
-= atom
.hl
+ atom
.dl
;
1842 set
.d
+= atom
.hl
+ atom
.dl
;
1850 if (badset
|| data
.l
) break;
1855 if (d
) CFRelease(d
);
1856 if (!good
&& ans
) {CFRelease(ans
); ans
=NULL
;}
1860 CFStringRef
CopyCertSubject(SecCertificateRef _cert
)
1862 return CopyCertName(_cert
, false);
1865 CFStringRef
CopyCertSubjectKeyId(SecCertificateRef _cert
)
1867 return CopyCertKeyId(_cert
, false);
1870 CFStringRef
CopyCertIssuer(SecCertificateRef _cert
)
1872 return CopyCertName(_cert
, true);
1875 CFStringRef
CopyCertIssuerKeyId(SecCertificateRef _cert
)
1877 return CopyCertKeyId(_cert
, true);
1880 /* return CFArrayRef if arr else CFStringRef
1882 * 0x01 includes DNS alts
1883 * 0x02 includes IPv4 alts
1884 * 0x04 includes IPv6 alts
1885 * 0x08 include IP other alts
1887 static CFTypeRef
CopyCertSubjectAltNamesInt(const der_cert_t
*cert
, bool arr
, unsigned flags
)
1890 CFTypeRef ans
= arr
?
1891 (CFTypeRef
)CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
) :
1892 (CFTypeRef
)CFStringCreateMutable(kCFAllocatorDefault
, 0);
1898 if (!cert
|| !ans
) break;
1899 if (!cert
->subjectAltNames
.d
|| !cert
->subjectAltNames
.l
) break;
1900 data
.d
= cert
->subjectAltNames
.d
;
1901 data
.l
= cert
->subjectAltNames
.l
;
1902 if (!read_der_atom(&data
, &atom
)) break;
1903 if (atom
.rawtag
!= 0x30) break;
1907 if (!read_der_atom(&data
, &atom
)) break;
1908 if (atom
.rawtag
== 0x82 && (flags
& 0x01)) {
1910 if (!arr
&& CFStringGetLength((CFStringRef
)ans
))
1911 CFStringAppendCString((CFMutableStringRef
)ans
, ",", kCFStringEncodingASCII
);
1912 temp
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, data
.d
+atom
.hl
,
1913 atom
.dl
, kCFStringEncodingASCII
, true, kCFAllocatorNull
);
1916 CFArrayAppendValue((CFMutableArrayRef
)ans
, temp
);
1918 CFStringAppend((CFMutableStringRef
)ans
, temp
);
1920 } else if (atom
.rawtag
== 0x87 && (flags
& 0x0e)) {
1921 if ((atom
.dl
== 4 && (flags
& 0x02)) ||
1922 (atom
.dl
== 16 && (flags
& 0x04)) ||
1923 (atom
.dl
!= 4 && atom
.dl
!= 16 && (flags
& 0x08))) {
1925 CFDataRef dtemp
= CFDataCreate(kCFAllocatorDefault
, data
.d
+atom
.hl
, atom
.dl
);
1927 CFArrayAppendValue((CFMutableArrayRef
)ans
, dtemp
);
1930 if (CFStringGetLength((CFStringRef
)ans
))
1931 CFStringAppendCString((CFMutableStringRef
)ans
, ",", kCFStringEncodingASCII
);
1934 const uint8_t *ip
= data
.d
+atom
.hl
;
1935 sprintf(ipv4str
, "%u.%u.%u.%u", ip
[0], ip
[1], ip
[2], ip
[3]);
1936 CFStringAppendCString((CFMutableStringRef
)ans
, ipv4str
, kCFStringEncodingASCII
);
1937 } else if (atom
.dl
== 16) {
1938 char ntopbuff
[INET6_ADDRSTRLEN
];
1939 if (!inet_ntop(AF_INET6
, data
.d
+atom
.hl
, ntopbuff
, sizeof(ntopbuff
)))
1941 CFStringAppendCString((CFMutableStringRef
)ans
, ntopbuff
, kCFStringEncodingASCII
);
1943 append_hex_dump((CFMutableStringRef
)ans
, data
.d
+atom
.hl
, atom
.dl
);
1948 data
.l
-= atom
.hl
+ atom
.dl
;
1949 data
.d
+= atom
.hl
+ atom
.dl
;
1951 if (!data
.l
&& ( (arr
&& CFArrayGetCount((CFArrayRef
)ans
) ) ||
1952 (!arr
&& CFStringGetLength((CFStringRef
)ans
)) ))
1955 if (!good
&& ans
) {CFRelease(ans
); ans
=NULL
;}
1959 static CFArrayRef
CopyCertSubjectCNIds(const der_cert_t
*cert
)
1961 CFMutableArrayRef ans
=
1962 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1969 if (!ans
|| !cert
|| !cert
->subject
.d
|| !cert
->subject
.l
) break;
1970 data
.d
= cert
->subject
.d
;
1971 data
.l
= cert
->subject
.l
;
1972 if (!read_der_atom(&data
, &atom
)) break;
1973 if (atom
.rawtag
!= 0x30) break;
1978 if (!read_der_atom(&data
, &atom
)) break;
1979 if (atom
.rawtag
!= 0x31) break;
1980 set
.d
= data
.d
+ atom
.hl
;
1982 data
.l
-= atom
.hl
+ atom
.dl
;
1983 data
.d
+= atom
.hl
+ atom
.dl
;
1984 if (!read_der_atom(&set
, &atom
)) break;
1985 if (atom
.rawtag
!= 0x30) break;
1986 if (atom
.hl
+ atom
.dl
== set
.l
) { /* single-value CN only */
1987 CFMutableStringRef cnid
;
1991 if (!read_der_atom(&set
, &atom
)) break;
1992 if (atom
.rawtag
!= 6) break;
1994 oid
.l
= atom
.hl
+ atom
.dl
;
1995 if (data_matches(&oid
, &OID_CommonName
)) {
1996 set
.l
-= atom
.hl
+ atom
.dl
;
1997 set
.d
+= atom
.hl
+ atom
.dl
;
1998 if (!read_der_atom(&set
, &atom
)) break;
1999 if (append_attr_value(&cnid
, set
.d
, &atom
, 0x1f) && cnid
) {
2000 CFArrayAppendValue(ans
, cnid
);
2009 if (ans
&& (!good
|| !CFArrayGetCount(ans
))) {
2016 CFStringRef
CopyCertSubjectAltNamesString(SecCertificateRef _cert
)
2018 CFDataRef d
= cSecCertificateCopyData(_cert
);
2021 CFStringRef ans
= NULL
;
2023 if (!d
) return NULL
;
2024 data
.d
= CFDataGetBytePtr(d
);
2025 data
.l
= CFDataGetLength(d
);
2026 if (read_der_cert(&data
, &cert
))
2027 ans
= (CFStringRef
)CopyCertSubjectAltNamesInt(&cert
, false, 0x0f);
2037 static CFArrayRef
CopyCertSubjectIds(der_cert_t
*c
, unsigned mode
)
2039 CFArrayRef ans
= NULL
;
2040 if (!c
|| (mode
!= 0 && mode
!= 4 && mode
!= 16)) return NULL
;
2042 return ans
= (CFArrayRef
)CopyCertSubjectAltNamesInt(c
, true, 0x02);
2044 return ans
= (CFArrayRef
)CopyCertSubjectAltNamesInt(c
, true, 0x04);
2046 ans
= (CFArrayRef
)CopyCertSubjectAltNamesInt(c
, true, 0x01);
2047 if (!ans
|| !CFArrayGetCount(ans
)) {
2048 if (ans
) CFRelease(ans
);
2049 ans
= CopyCertSubjectCNIds(c
);
2052 if (ans
&& !CFArrayGetCount(ans
)) {
2059 OSStatus
VerifyTrustChain(SecTrustRef trust
, CFArrayRef customRootsOrNull
,
2060 unsigned explicitCertsOnly
, unsigned flags
, const char *peername
)
2062 SecTrustResultType result
;
2063 CFArrayRef chain
= NULL
;
2064 CSSM_TP_APPLE_EVIDENCE_INFO
*evidence
;
2065 OSStatus err
= cSecTrustGetResult(trust
, &result
, &chain
, &evidence
);
2067 if (err
== errSecTrustNotAvailable
) {
2068 /* We need to evaluate first */
2069 CFArrayRef anchors
= customRootsOrNull
;
2070 if (chain
) CFRelease(chain
);
2074 err
= SecTrustCopyAnchorCertificates(&anchors
);
2075 if (err
) return err
;
2077 err
= SecTrustSetAnchorCertificates(trust
, anchors
);
2079 if (err
) return err
;
2080 err
= cSecTrustSetAnchorCertificatesOnly(trust
, customRootsOrNull
? true : false);
2081 if (err
&& err
!= unimpErr
) return err
;
2082 err
= SecTrustEvaluate(trust
, &result
);
2083 if (err
) return err
;
2085 err
= cSecTrustGetResult(trust
, &result
, &chain
, &evidence
);
2088 if (chain
) CFRelease(chain
);
2091 if (!chain
|| !evidence
|| !CFArrayGetCount(chain
)) {
2092 if (chain
) CFRelease(chain
);
2093 return errSSLXCertChainInvalid
;
2095 cnt
= (size_t)CFArrayGetCount(chain
);
2096 if ((peername
&& *peername
) ||
2097 (!(flags
& CSSM_TP_ACTION_LEAF_IS_CA
) &&
2098 !(evidence
[0].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
))) {
2099 CFDataRef certder
= cSecCertificateCopyData(
2100 (SecCertificateRef
)CFArrayGetValueAtIndex(chain
, 0));
2104 return errSSLBadCert
;
2105 der
.d
= CFDataGetBytePtr(certder
);
2106 der
.l
= CFDataGetLength(certder
);
2107 if (!read_der_cert(&der
, &cert
))
2108 err
= errSSLBadCert
;
2110 /* First confirm we have a host name match. SecureTransport should do
2111 * this for us (but will not give us a decent result code) except that
2112 * it has problems with IPv6 address matching */
2113 if (!err
&& peername
&& *peername
) {
2114 size_t peerlen
= strlen(peername
);
2120 if (parse_ipv4_name(peername
, peerlen
, ipa
.ipv4
)) mode
= 4;
2121 else if (is_dns_name(peername
, peerlen
, false)) mode
= 0;
2122 else if (parse_ipv6_name(peername
, peerlen
, ipa
.ipv6
)) mode
= 16;
2124 /* if we can't parse peername it can't possibly match! */
2125 err
= errSSLHostNameMismatch
;
2127 CFArrayRef ids
= CopyCertSubjectIds(&cert
, mode
);
2128 if (ids
&& !CFArrayGetCount(ids
)) {
2133 /* if we don't have anything to match against it can't possibly match! */
2134 err
= errSSLHostNameMismatch
;
2136 size_t j
, idcnt
= CFArrayGetCount(ids
);
2137 bool matched
= false;
2138 for (j
= 0; j
< idcnt
; ++j
) {
2139 CFTypeRef oneid
= (CFTypeRef
)CFArrayGetValueAtIndex(ids
, j
);
2143 if (CFDataGetTypeID() != CFGetTypeID(oneid
)) continue;
2144 p
= (uint8_t *)CFDataGetBytePtr((CFDataRef
)oneid
);
2145 l
= (size_t)CFDataGetLength((CFDataRef
)oneid
);
2146 if (l
!= (size_t)mode
) continue;
2147 if (memcmp(ipa
.ipv6
, p
, l
) == 0) {
2153 if (CFStringGetTypeID() != CFGetTypeID(oneid
)) continue;
2154 if (!CFStringGetCString((CFStringRef
)oneid
, dnsname
,
2155 sizeof(dnsname
), kCFStringEncodingASCII
))
2157 if (peername_matches_id(peername
, dnsname
)) {
2165 err
= errSSLHostNameMismatch
;
2170 /* Confirm that the first certificate is NOT a CA (otherwise it's not a
2171 * valid chain), but again SecureTransport should have already checked that
2172 * for us. CSSM_TP_ACTION_LEAF_IS_CA overrides. Also we never check the
2173 * root certificate even if it's also the leaf. */
2174 if (!err
&& !(flags
& CSSM_TP_ACTION_LEAF_IS_CA
) &&
2175 !(evidence
[0].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
) && cert
.isCA
)
2176 err
= errSSLXCertChainInvalid
;
2179 if (err
) return err
;
2181 if (explicitCertsOnly
& 0x01) {
2182 /* Check all but root */
2183 for (i
= 0; i
< cnt
; ++i
) {
2184 if (!(evidence
[i
].StatusBits
& CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
) &&
2185 !(evidence
[i
].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
)) {
2186 /* If the magical cert had not appeared, the chain would have stopped
2187 * here and the error would be no root, so return that error */
2189 return errSSLNoRootCert
;
2193 if (!(flags
& CSSM_TP_ACTION_ALLOW_EXPIRED
) ||
2194 !(flags
& CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
)) {
2195 /* check for expired or not yet valid certs */
2196 for (i
= 0; i
< cnt
; ++i
) {
2197 if ((flags
& CSSM_TP_ACTION_ALLOW_EXPIRED
) &&
2198 !(evidence
[i
].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
))
2200 if ((flags
& CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
) &&
2201 (evidence
[i
].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
))
2203 if (evidence
[i
].StatusBits
& CSSM_CERT_STATUS_EXPIRED
) {
2205 return errSSLCertExpired
;
2207 if (evidence
[i
].StatusBits
& CSSM_CERT_STATUS_NOT_VALID_YET
) {
2209 return errSSLCertNotYetValid
;
2213 /* check for no root */
2214 if (!(evidence
[cnt
-1].StatusBits
& CSSM_CERT_STATUS_IS_ROOT
)) {
2216 return errSSLNoRootCert
;
2218 /* check for unknown root */
2219 if (!(evidence
[cnt
-1].StatusBits
& CSSM_CERT_STATUS_IS_IN_ANCHORS
)) {
2221 return errSSLUnknownRootCert
;
2223 if (customRootsOrNull
) {
2224 /* make sure we're not using a gratuitous root, Mac OS X likes to just
2225 * go ahead and use its anchors sometimes despite settings to the contrary */
2226 if (!SecCertInArray((SecCertificateRef
)CFArrayGetValueAtIndex(chain
, cnt
-1),
2227 customRootsOrNull
)) {
2229 return errSSLNoRootCert
;
2233 /* everything looks good, so check the trust result code now */
2235 case kSecTrustResultProceed
:
2236 case kSecTrustResultUnspecified
:
2239 case kSecTrustResultDeny
:
2241 return errSecTrustSettingDeny
;
2245 /* everything else (confirm, invalid, recoverable, fatal, other) */
2246 return errSecNotTrusted
;
2249 #elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
2251 #error iOS is not currently supported
2253 #endif /* TARGET_OS_EMBEDDED || TARGET_OS_IPHONE */