/ OSX / sec / Security / SecTrustStatusCodes.c
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  }