SecTrustStatusCodes.c
1 /* 2 * Copyright (c) 2017-2019 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 * 23 * SecTrustStatusCodes.c - map trust result details to status codes 24 * 25 */ 26 27 #include <Security/Security.h> 28 #include <Security/SecTrustPriv.h> 29 #include <Security/SecPolicyPriv.h> 30 #include <Security/SecCertificatePriv.h> 31 #include <Security/SecInternal.h> 32 #include <Security/SecTrustStatusCodes.h> 33 #include <CoreFoundation/CoreFoundation.h> 34 #include <libDER/oids.h> 35 #ifdef DARLING 36 // if i had to guess, i'd say Apple changed up libDER when they stopped releasing it and now 37 // their "private" OIDs are included in the regular `libDER/oids.h` header 38 // 39 // if this error keeps popping up, i'll stop manually including `libDER/oidsPriv.h` and instead patch 40 // `libDER/oids.h` to include `libDER/oidsPriv.h` 41 #include <libDER/oidsPriv.h> 42 #endif 43 44 struct resultmap_entry_s { 45 const CFStringRef checkstr; 46 const int32_t resultcode; 47 }; 48 typedef struct resultmap_entry_s resultmap_entry_t; 49 50 const resultmap_entry_t resultmap[] = { 51 #undef POLICYCHECKMACRO 52 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \ 53 { CFSTR(#NAME), CSSMERR }, 54 #include "SecPolicyChecks.list" 55 }; 56 57 static bool SecTrustDetailsHaveEKULeafErrorOnly(CFArrayRef details) 58 { 59 CFIndex ix, count = (details) ? CFArrayGetCount(details) : 0; 60 bool hasDisqualifyingError = false; 61 for (ix = 0; ix < count; ix++) { 62 CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix); 63 if (ix == 0) { // Leaf 64 if (CFDictionaryGetCount(detail) != 1 || // One error 65 CFDictionaryGetValue(detail, kSecPolicyCheckExtendedKeyUsage) != kCFBooleanFalse) { 66 hasDisqualifyingError = true; 67 break; 68 } 69 } else { 70 if (CFDictionaryGetCount(detail) > 0) { // No errors on other certs 71 hasDisqualifyingError = true; 72 break; 73 } 74 } 75 } 76 if (hasDisqualifyingError) { 77 return false; 78 } 79 return true; 80 } 81 82 // Returns true if both of the following are true: 83 // - policy is Apple SW Update Signing 84 // - leaf certificate has the oidAppleExtendedKeyUsageCodeSigningDev EKU purpose 85 // 86 static bool SecTrustIsDevelopmentUpdateSigning(SecTrustRef trust) 87 { 88 bool result = false; 89 CFArrayRef policies = NULL; /* must release */ 90 SecPolicyRef policy = NULL; /* must release */ 91 SecCertificateRef cert = NULL; 92 CFArrayRef ekus = NULL; /* must release */ 93 CFDataRef eku = NULL; /* must release */ 94 const DERItem *oid = &oidAppleExtendedKeyUsageCodeSigningDev; 95 96 /* Apple SW Update Signing policy check */ 97 if ((SecTrustCopyPolicies(trust, &policies) != errSecSuccess) || 98 ((policy = SecPolicyCreateAppleSWUpdateSigning()) == NULL) || 99 (!CFArrayContainsValue(policies, CFRangeMake(0, CFArrayGetCount(policies)), policy))) { 100 goto exit; 101 } 102 103 /* Apple Code Signing Dev EKU check */ 104 if (((cert = SecTrustGetCertificateAtIndex(trust, 0)) == NULL) || 105 ((ekus = SecCertificateCopyExtendedKeyUsage(cert)) == NULL) || 106 ((eku = CFDataCreate(kCFAllocatorDefault, oid->data, oid->length)) == NULL) || 107 (!CFArrayContainsValue(ekus, CFRangeMake(0, CFArrayGetCount(ekus)), eku))) { 108 goto exit; 109 } 110 111 result = true; 112 113 exit: 114 CFReleaseSafe(eku); 115 CFReleaseSafe(ekus); 116 CFReleaseSafe(policies); 117 CFReleaseSafe(policy); 118 return result; 119 } 120 121 // 122 // Returns a malloced array of SInt32 values, with the length in numStatusCodes, 123 // for the certificate specified by chain index in the given SecTrustRef. 124 // 125 // To match legacy behavior, the array actually allocates one element more than the 126 // value of numStatusCodes; if the certificate is revoked, the additional element 127 // at the end contains the CrlReason value. 128 // 129 // Caller must free the returned pointer. 130 // 131 SInt32 *SecTrustCopyStatusCodes(SecTrustRef trust, 132 CFIndex index, CFIndex *numStatusCodes) 133 { 134 if (!trust || !numStatusCodes) { 135 return NULL; 136 } 137 *numStatusCodes = 0; 138 CFArrayRef details = SecTrustCopyFilteredDetails(trust); 139 CFIndex chainLength = (details) ? CFArrayGetCount(details) : 0; 140 if (!(index < chainLength)) { 141 CFReleaseSafe(details); 142 return NULL; 143 } 144 CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, index); 145 CFIndex ix, detailCount = CFDictionaryGetCount(detail); 146 *numStatusCodes = (unsigned int)detailCount; 147 148 // Allocate one more entry than we need; this is used to store a CrlReason 149 // at the end of the array. 150 SInt32 *statusCodes = (SInt32*)malloc((detailCount+1) * sizeof(SInt32)); 151 statusCodes[*numStatusCodes] = 0; 152 153 const unsigned int resultmaplen = sizeof(resultmap) / sizeof(resultmap_entry_t); 154 const void *keys[detailCount]; 155 CFDictionaryGetKeysAndValues(detail, &keys[0], NULL); 156 for (ix = 0; ix < detailCount; ix++) { 157 CFStringRef key = (CFStringRef)keys[ix]; 158 SInt32 statusCode = 0; 159 for (unsigned int mapix = 0; mapix < resultmaplen; mapix++) { 160 CFStringRef str = (CFStringRef) resultmap[mapix].checkstr; 161 if (CFStringCompare(str, key, 0) == kCFCompareEqualTo) { 162 statusCode = (SInt32) resultmap[mapix].resultcode; 163 break; 164 } 165 } 166 if (statusCode == (SInt32)0x80012407) { /* CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE */ 167 // To match legacy behavior, we return a more specific result code if this is a 168 // development signing certificate being evaluated for Apple SW Update Signing. 169 // [27362805,41179903] 170 if (index == 0 && 171 SecTrustIsDevelopmentUpdateSigning(trust) && 172 SecTrustDetailsHaveEKULeafErrorOnly(details)) { 173 statusCode = (SInt32)0x80012433; /* CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT */ 174 } 175 } else if (statusCode == (SInt32)0x8001210C) { /* CSSMERR_TP_CERT_REVOKED */ 176 SInt32 reason; 177 CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(detail, key); 178 if (number && CFNumberGetValue(number, kCFNumberSInt32Type, &reason)) { 179 statusCodes[*numStatusCodes] = (SInt32)reason; 180 } 181 } 182 statusCodes[ix] = statusCode; 183 } 184 185 CFReleaseSafe(details); 186 return statusCodes; 187 }