/ OSX / sec / Security / SecPolicyLeafCallbacks.c
SecPolicyLeafCallbacks.c
  1  /*
  2   * Copyright (c) 2008-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  
 24  /*
 25   * SecPolicyLeafCallbacks.c - Callbacks for SecPolicy for verifying leafs
 26   */
 27  
 28  #include <AssertMacros.h>
 29  #include <CoreFoundation/CFDictionary.h>
 30  #include <Security/SecPolicyPriv.h>
 31  #include <Security/SecPolicyInternal.h>
 32  #include <Security/SecCertificateInternal.h>
 33  #include <Security/SecFramework.h>
 34  #include <utilities/SecCFWrappers.h>
 35  #include <utilities/SecInternalReleasePriv.h>
 36  #include <wctype.h>
 37  #include <dlfcn.h>
 38  #include <libDER/oids.h>
 39  
 40  /*
 41   * MARK: SecPolicyCheckCert Functions
 42   * All SecPolicyCheckCert* return false if the cert fails the check and true if it succeeds.
 43   */
 44  
 45  typedef bool (*SecPolicyCheckCertFunction)(SecCertificateRef cert, CFTypeRef pvcValue);
 46  
 47  /* This one is different from SecPolicyCheckCriticalExtensions because
 48   that one is an empty stub. The CriticalExtensions check is done in
 49   SecPolicyCheckBasicCertificateProcessing. */
 50  bool SecPolicyCheckCertCriticalExtensions(SecCertificateRef cert, CFTypeRef __unused pvcValue) {
 51      if (SecCertificateHasUnknownCriticalExtension(cert)) {
 52          /* Certificate contains one or more unknown critical extensions. */
 53          return false;
 54      }
 55      return true;
 56  }
 57  
 58  static bool keyusage_allows(SecKeyUsage keyUsage, CFTypeRef xku) {
 59      if (!xku || CFGetTypeID(xku) != CFNumberGetTypeID())
 60          return false;
 61  
 62      SInt32 dku;
 63      CFNumberGetValue((CFNumberRef)xku, kCFNumberSInt32Type, &dku);
 64      SecKeyUsage ku = (SecKeyUsage)dku;
 65      /* kSecKeyUsageUnspecified is a special sentinel for allowing a missing KU extension */
 66      if (ku == kSecKeyUsageUnspecified) {
 67          return (keyUsage == ku);
 68      }
 69      return (keyUsage & ku) == ku;
 70  }
 71  
 72  bool SecPolicyCheckCertKeyUsage(SecCertificateRef cert, CFTypeRef pvcValue) {
 73      SecKeyUsage keyUsage = SecCertificateGetKeyUsage(cert);
 74      bool match = false;
 75      CFTypeRef xku = pvcValue;
 76      if (isArray(xku)) {
 77          CFIndex ix, count = CFArrayGetCount(xku);
 78          for (ix = 0; ix < count; ++ix) {
 79              CFTypeRef ku = CFArrayGetValueAtIndex(xku, ix);
 80              if (keyusage_allows(keyUsage, ku)) {
 81                  match = true;
 82                  break;
 83              }
 84          }
 85      } else {
 86          match = keyusage_allows(keyUsage, xku);
 87      }
 88      return match;
 89  }
 90  
 91  static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage,
 92                                      CFDataRef xeku) {
 93      if (!xeku)
 94          return false;
 95      if (extendedKeyUsage) {
 96          CFRange all = { 0, CFArrayGetCount(extendedKeyUsage) };
 97          return CFArrayContainsValue(extendedKeyUsage, all, xeku);
 98      } else {
 99          /* Certificate has no extended key usage, only a match if the policy
100           contains a 0 length CFDataRef. */
101          return CFDataGetLength((CFDataRef)xeku) == 0;
102      }
103  }
104  
105  static bool isExtendedKeyUsageAllowed(CFArrayRef extendedKeyUsage,
106                                        CFTypeRef xeku) {
107      if (!xeku) {
108          return false;
109      }
110      if(CFGetTypeID(xeku) == CFDataGetTypeID()) {
111          return extendedkeyusage_allows(extendedKeyUsage, xeku);
112      } else if (CFGetTypeID(xeku) == CFStringGetTypeID()) {
113          CFDataRef eku = SecCertificateCreateOidDataFromString(NULL, xeku);
114          if (eku) {
115              bool result = extendedkeyusage_allows(extendedKeyUsage, eku);
116              CFRelease(eku);
117              return result;
118          }
119      }
120      return false;
121  }
122  
123  bool SecPolicyCheckCertExtendedKeyUsage(SecCertificateRef cert, CFTypeRef pvcValue) {
124      CFArrayRef certExtendedKeyUsage = SecCertificateCopyExtendedKeyUsage(cert);
125      bool match = false;
126      CFTypeRef xeku = pvcValue;
127      if (isArray(xeku)) {
128          CFIndex ix, count = CFArrayGetCount(xeku);
129          for (ix = 0; ix < count; ix++) {
130              CFTypeRef eku = CFArrayGetValueAtIndex(xeku, ix);
131              if (isExtendedKeyUsageAllowed(certExtendedKeyUsage, eku)) {
132                  match = true;
133                  break;
134              }
135          }
136      } else {
137          match = isExtendedKeyUsageAllowed(certExtendedKeyUsage, xeku);
138      }
139      CFReleaseSafe(certExtendedKeyUsage);
140      return match;
141  }
142  
143  bool SecPolicyCheckCertNonEmptySubject(SecCertificateRef cert, CFTypeRef __unused pvcValue) {
144      /* If the certificate has a subject, or
145       if it doesn't, and it's the leaf and not a CA,
146       and also has a critical subjectAltName extension it's valid. */
147      if (!SecCertificateHasSubject(cert)) {
148          if (SecCertificateIsCA(cert)) {
149              /* CA certificate has empty subject. */
150              return false;
151          } else {
152              if (!SecCertificateHasCriticalSubjectAltName(cert)) {
153                  /* Leaf certificate with empty subject does not have
154                   a critical subject alt name extension. */
155                  return false;
156              }
157          }
158      }
159      return true;
160  }
161  
162  /* We have a wildcard reference identifier that looks like "*." followed by 2 or
163     more labels. Use CFNetwork's function for determining if those labels comprise
164     a top-level domain. We need to dlopen since CFNetwork is a client of ours. */
165  typedef bool (*CFNIsTLD_f)(CFStringRef domain);
166  bool SecDNSIsTLD(CFStringRef reference) {
167      bool result = false; /* fail open for allocation and symbol lookup failures */
168      static CFNIsTLD_f CFNIsDomainTopLevelFunctionPtr = NULL;
169      static dispatch_once_t onceToken;
170      CFStringRef presentedDomain = NULL;
171  
172      dispatch_once(&onceToken, ^{
173          void *framework = dlopen("/System/Library/Frameworks/CFNetwork.framework/CFNetwork", RTLD_LAZY);
174          if (framework) {
175              CFNIsDomainTopLevelFunctionPtr = dlsym(framework, "_CFHostIsDomainTopLevelForCertificatePolicy");
176          }
177      });
178  
179      require_quiet(CFNIsDomainTopLevelFunctionPtr, out);
180      CFIndex referenceLen = CFStringGetLength(reference);
181  
182      /* reference identifier is too short, we should fail it */
183      require_action_quiet(referenceLen > 2, out, result = true);
184  
185      require_quiet(presentedDomain = CFStringCreateWithSubstring(NULL, reference,
186                                                                  CFRangeMake(2, referenceLen - 2)),
187                    out);
188      result = CFNIsDomainTopLevelFunctionPtr(presentedDomain);
189  
190  out:
191      CFReleaseNull(presentedDomain);
192      return result;
193  }
194  
195  /* Compare hostname, to a server name obtained from the server's cert
196   Obtained from the SubjectAltName or the CommonName entry in the Subject.
197   Limited wildcard checking is performed here as outlined in RFC 6125
198   Section 6.4.3.
199  
200   We adhere to the (SHOULD NOT) guidance in rules 1 and 2, and we choose
201   never to accept partial-label wildcards even though they are allowed by
202   rule 3.
203  
204   We use the language from RFC 6125, particularly the following definitions:
205  
206   presented identifier:  An identifier that is presented by a server to
207   a client within a PKIX certificate when the client attempts to
208   establish secure communication with the server; the certificate
209   can include one or more presented identifiers of different types,
210   and if the server hosts more than one domain then the certificate
211   might present distinct identifiers for each domain.
212  
213   reference identifier:  An identifier, constructed from a source
214   domain and optionally an application service type, used by the
215   client for matching purposes when examining presented identifiers.
216  
217   */
218  static bool SecDNSMatch(CFStringRef reference, CFStringRef presented) {
219      CFArrayRef referenceLabels = NULL, presentedLabels = NULL;
220      bool result = false;
221  
222      /* A trailing '.' in the reference identifier is allowed as a mechanism
223       to force TLS renegotiation. Strip it before parsing labels. */
224      CFIndex referenceLen = CFStringGetLength(reference);
225      require_quiet(referenceLen > 0, noMatch);
226      if ('.' == CFStringGetCharacterAtIndex(reference, referenceLen - 1)) {
227          CFStringRef truncatedReference = CFStringCreateWithSubstring(NULL, reference,
228                                                                       CFRangeMake(0, referenceLen - 1));
229          referenceLabels = CFStringCreateArrayBySeparatingStrings(NULL, truncatedReference, CFSTR("."));
230          CFReleaseNull(truncatedReference);
231          require_quiet(referenceLabels, noMatch);
232      } else {
233      require_quiet(referenceLabels = CFStringCreateArrayBySeparatingStrings(NULL, reference, CFSTR(".")),
234                    noMatch);
235      }
236  
237      require_quiet(presentedLabels = CFStringCreateArrayBySeparatingStrings(NULL, presented, CFSTR(".")),
238                    noMatch);
239  
240      /* Reference Identifier and Presented Identifier must have the same number of labels
241         because a wildcard in the presented identifier can only match a single label in the
242         reference identifier. */
243      require_quiet(CFArrayGetCount(referenceLabels) == CFArrayGetCount(presentedLabels), noMatch);
244  
245      CFIndex ix, count = CFArrayGetCount(referenceLabels);
246      for (ix = count - 1; ix >= 0; ix--) {
247          CFStringRef rlabel = NULL, plabel = NULL;
248          require_quiet(rlabel = CFArrayGetValueAtIndex(referenceLabels, ix), noMatch);
249          require_quiet(plabel = CFArrayGetValueAtIndex(presentedLabels, ix), noMatch);
250          if (CFEqual(plabel, CFSTR("*"))) {
251              /* must only occur in left-most label */
252              require_quiet(ix == 0, noMatch);
253  
254              /* must not occur before single-label TLD */
255              require_quiet(count > 2 && ix != count - 2, noMatch);
256  
257              /* must not occur before a multi-label gTLD */
258              require_quiet(!SecDNSIsTLD(presented), noMatch);
259          } else {
260              /* partial-label wildcards are disallowed */
261              CFRange partialRange = CFStringFind(plabel, CFSTR("*"), 0);
262              require_quiet(partialRange.location == kCFNotFound && partialRange.length == 0 ,
263                            noMatch);
264  
265              /* not a wildcard, so labels must match exactly */
266              require_quiet(CFStringCompare(rlabel, plabel, kCFCompareCaseInsensitive) == kCFCompareEqualTo, noMatch);
267          }
268      }
269  
270      result = true;
271  
272  noMatch:
273      CFReleaseNull(referenceLabels);
274      CFReleaseNull(presentedLabels);
275      return result;
276  }
277  
278  bool SecPolicyCheckCertSSLHostname(SecCertificateRef cert, CFTypeRef pvcValue) {
279      /* @@@ Consider what to do if the caller passes in no hostname.  Should
280       we then still fail if the leaf has no dnsNames or IPAddresses at all? */
281      CFStringRef hostName = pvcValue;
282      if (!isString(hostName)) {
283          /* @@@ We can't return an error here and making the evaluation fail
284           won't help much either. */
285          return false;
286      }
287  
288      bool dnsMatch = false;
289      CFArrayRef dnsNames = SecCertificateCopyDNSNamesFromSAN(cert);
290      if (dnsNames) {
291          CFIndex ix, count = CFArrayGetCount(dnsNames);
292          for (ix = 0; ix < count; ++ix) {
293              CFStringRef dns = (CFStringRef)CFArrayGetValueAtIndex(dnsNames, ix);
294              if (SecDNSMatch(hostName, dns)) {
295                  dnsMatch = true;
296                  break;
297              }
298          }
299          CFRelease(dnsNames);
300      }
301  
302      if (!dnsMatch) {
303          /* Check whether hostname is an IPv4 or IPv6 address */
304          CFDataRef hostIPData = SecFrameworkCopyIPAddressData(hostName);
305          if (hostIPData) {
306              /* Check address against IP addresses in the SAN extension,
307                 obtained by SecCertificateCopyIPAddresses. Comparisons
308                 must always use the canonical data representation of the
309                 address, since string notation may omit zeros, etc. */
310              CFArrayRef ipAddresses = SecCertificateCopyIPAddressDatas(cert);
311              CFIndex ix, count = (ipAddresses) ? CFArrayGetCount(ipAddresses) : 0;
312              for (ix = 0; ix < count && !dnsMatch; ++ix) {
313                  CFDataRef ipAddress = (CFDataRef)CFArrayGetValueAtIndex(ipAddresses, ix);
314                  if (CFEqualSafe(hostIPData, ipAddress)) {
315                      dnsMatch = true;
316                  }
317              }
318              CFReleaseSafe(ipAddresses);
319              CFReleaseSafe(hostIPData);
320          }
321      }
322  
323      return dnsMatch;
324  }
325  
326  bool SecPolicyCheckCertEmail(SecCertificateRef cert, CFTypeRef pvcValue) {
327      CFStringRef email = pvcValue;
328      bool match = false;
329      if (!isString(email)) {
330          /* We can't return an error here and making the evaluation fail
331           won't help much either. */
332          return false;
333      }
334  
335      CFArrayRef addrs = SecCertificateCopyRFC822Names(cert);
336      if (addrs) {
337          CFIndex ix, count = CFArrayGetCount(addrs);
338          for (ix = 0; ix < count; ++ix) {
339              CFStringRef addr = (CFStringRef)CFArrayGetValueAtIndex(addrs, ix);
340              if (!CFStringCompare(email, addr, kCFCompareCaseInsensitive)) {
341                  match = true;
342                  break;
343              }
344          }
345          CFRelease(addrs);
346      }
347  
348      return match;
349  }
350  
351  bool SecPolicyCheckCertTemporalValidity(SecCertificateRef cert, CFTypeRef pvcValue) {
352      CFAbsoluteTime verifyTime = CFDateGetAbsoluteTime(pvcValue);
353      if (!SecCertificateIsValid(cert, verifyTime)) {
354          /* Leaf certificate has expired. */
355          return false;
356      }
357      return true;
358  }
359  
360  bool SecPolicyCheckCertSubjectCommonNamePrefix(SecCertificateRef cert, CFTypeRef pvcValue) {
361      CFStringRef prefix = pvcValue;
362      bool match = true;
363      if (!isString(prefix)) {
364          /* @@@ We can't return an error here and making the evaluation fail
365           won't help much either. */
366          return false;
367      }
368      CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
369      if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
370          !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames, 0), prefix)) {
371          /* Common Name prefix mismatch. */
372          match = false;
373      }
374      CFReleaseSafe(commonNames);
375      return match;
376  }
377  
378  bool SecPolicyCheckCertSubjectCommonName(SecCertificateRef cert, CFTypeRef pvcValue) {
379      CFStringRef common_name = pvcValue;
380      bool match = true;
381      if (!isString(common_name)) {
382          /* @@@ We can't return an error here and making the evaluation fail
383           won't help much either. */
384          return false;
385      }
386      CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
387      if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
388          !CFEqual(common_name, CFArrayGetValueAtIndex(commonNames, 0))) {
389          /* Common Name mismatch. */
390          match = false;
391      }
392      CFReleaseSafe(commonNames);
393      return match;
394  }
395  
396  bool SecPolicyCheckCertSubjectCommonNameTEST(SecCertificateRef cert, CFTypeRef pvcValue) {
397      CFStringRef common_name = pvcValue;
398      bool match = true;
399      if (!isString(common_name)) {
400          /* @@@ We can't return an error here and making the evaluation fail
401           won't help much either. */
402          return false;
403      }
404      CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
405      if (!commonNames || CFArrayGetCount(commonNames) != 1) {
406          CFStringRef cert_common_name = CFArrayGetValueAtIndex(commonNames, 0);
407          CFStringRef test_common_name = common_name ?
408          CFStringCreateWithFormat(kCFAllocatorDefault,
409                                   NULL, CFSTR("TEST %@ TEST"), common_name) :
410          NULL;
411          if (!CFEqual(common_name, cert_common_name) &&
412              (!test_common_name || !CFEqual(test_common_name, cert_common_name)))
413          /* Common Name mismatch. */
414              match = false;
415          CFReleaseSafe(test_common_name);
416      }
417      CFReleaseSafe(commonNames);
418      return match;
419  }
420  
421  bool SecPolicyCheckCertNotValidBefore(SecCertificateRef cert, CFTypeRef pvcValue) {
422      CFDateRef date = pvcValue;
423      if (!isDate(date)) {
424          /* @@@ We can't return an error here and making the evaluation fail
425           won't help much either. */
426          return false;
427      }
428      CFAbsoluteTime at = CFDateGetAbsoluteTime(date);
429      if (SecCertificateNotValidBefore(cert) <= at) {
430          /* Leaf certificate has not valid before that is too old. */
431          return false;
432      }
433      return true;
434  }
435  
436  bool SecPolicyCheckCertSubjectOrganization(SecCertificateRef cert, CFTypeRef pvcValue) {
437      CFStringRef org = pvcValue;
438      bool match = true;
439      if (!isString(org)) {
440          /* @@@ We can't return an error here and making the evaluation fail
441           won't help much either. */
442          return false;
443      }
444      CFArrayRef organization = SecCertificateCopyOrganization(cert);
445      if (!organization || CFArrayGetCount(organization) != 1 ||
446          !CFEqual(org, CFArrayGetValueAtIndex(organization, 0))) {
447          /* Leaf Subject Organization mismatch. */
448          match = false;
449      }
450      CFReleaseSafe(organization);
451      return match;
452  }
453  
454  bool SecPolicyCheckCertSubjectOrganizationalUnit(SecCertificateRef cert, CFTypeRef pvcValue) {
455      CFStringRef orgUnit = pvcValue;
456      bool match = true;
457      if (!isString(orgUnit)) {
458          /* @@@ We can't return an error here and making the evaluation fail
459           won't help much either. */
460          return false;
461      }
462      CFArrayRef organizationalUnit = SecCertificateCopyOrganizationalUnit(cert);
463      if (!organizationalUnit || CFArrayGetCount(organizationalUnit) != 1 ||
464          !CFEqual(orgUnit, CFArrayGetValueAtIndex(organizationalUnit, 0))) {
465          /* Leaf Subject Organizational Unit mismatch. */
466          match = false;
467      }
468      CFReleaseSafe(organizationalUnit);
469      return match;
470  }
471  
472  bool SecPolicyCheckCertSubjectCountry(SecCertificateRef cert, CFTypeRef pvcValue) {
473      CFStringRef country = pvcValue;
474      bool match = true;
475      if (!isString(country)) {
476          /* @@@ We can't return an error here and making the evaluation fail
477           won't help much either. */
478          return false;
479      }
480      CFArrayRef certCountry = SecCertificateCopyCountry(cert);
481      if (!certCountry || CFArrayGetCount(certCountry) != 1 ||
482          !CFEqual(country, CFArrayGetValueAtIndex(certCountry, 0))) {
483          /* Subject Country mismatch. */
484          match = false;
485      }
486      CFReleaseSafe(certCountry);
487      return match;
488  }
489  
490  bool SecPolicyCheckCertEAPTrustedServerNames(SecCertificateRef cert, CFTypeRef pvcValue) {
491      CFArrayRef trustedServerNames = pvcValue;
492      /* No names specified means we accept any name. */
493      if (!trustedServerNames)
494          return true;
495      if (!isArray(trustedServerNames)) {
496          /* @@@ We can't return an error here and making the evaluation fail
497           won't help much either. */
498          return false;
499      }
500  
501      CFIndex tsnCount = CFArrayGetCount(trustedServerNames);
502      bool dnsMatch = false;
503      CFArrayRef dnsNames = SecCertificateCopyDNSNames(cert);
504      if (dnsNames) {
505          CFIndex ix, count = CFArrayGetCount(dnsNames);
506          // @@@ This is O(N^2) unfortunately we can't do better easily unless
507          // we don't do wildcard matching. */
508          for (ix = 0; !dnsMatch && ix < count; ++ix) {
509              CFStringRef dns = (CFStringRef)CFArrayGetValueAtIndex(dnsNames, ix);
510              CFIndex tix;
511              for (tix = 0; tix < tsnCount; ++tix) {
512                  CFStringRef serverName =
513                  (CFStringRef)CFArrayGetValueAtIndex(trustedServerNames, tix);
514                  if (!isString(serverName)) {
515                      /* @@@ We can't return an error here and making the
516                       evaluation fail won't help much either. */
517                      CFReleaseSafe(dnsNames);
518                      return false;
519                  }
520                  /* we purposefully reverse the arguments here such that dns names
521                   from the cert are matched against a server name list, where
522                   the server names list can contain wildcards and the dns name
523                   cannot.  References: http://support.microsoft.com/kb/941123
524                   It's easy to find occurrences where people tried to use
525                   wildcard certificates and were told that those don't work
526                   in this context. */
527                  if (SecDNSMatch(dns, serverName)) {
528                      dnsMatch = true;
529                      break;
530                  }
531              }
532          }
533          CFRelease(dnsNames);
534      }
535  
536      return dnsMatch;
537  }
538  
539  bool SecPolicyCheckCertLeafMarkerOid(SecCertificateRef cert, CFTypeRef pvcValue) {
540      if (pvcValue && SecCertificateHasMarkerExtension(cert, pvcValue)) {
541          return true;
542      }
543  
544      return false;
545  }
546  
547  bool SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(SecCertificateRef cert,
548                                                        CFTypeRef pvcValue) {
549      if (CFGetTypeID(pvcValue) == CFArrayGetTypeID()) {
550          CFIndex ix, length = CFArrayGetCount(pvcValue);
551          for (ix = 0; ix < length; ix++)
552              if (SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert,
553                      CFArrayGetValueAtIndex((CFArrayRef)pvcValue, ix))) {
554                  return true;
555              }
556      } else if (CFGetTypeID(pvcValue) == CFDataGetTypeID()  ||
557                 CFGetTypeID(pvcValue) == CFStringGetTypeID()) {
558          return (NULL != SecCertificateGetExtensionValue(cert, pvcValue));
559      }
560      return false;
561  }
562  
563  /*
564   * The value is a dictionary. The dictionary contains keys indicating
565   * whether the value is for Prod or QA. The values are the same as
566   * in the options dictionary for SecPolicyCheckLeafMarkerOid.
567   */
568  bool SecPolicyCheckCertLeafMarkersProdAndQA(SecCertificateRef cert, CFTypeRef pvcValue)
569  {
570      CFTypeRef prodValue = CFDictionaryGetValue(pvcValue, kSecPolicyLeafMarkerProd);
571  
572      if (!SecPolicyCheckCertLeafMarkerOid(cert, prodValue)) {
573          bool result = false;
574          return result;
575      }
576      return true;
577  }
578  
579  static CFSetRef copyCertificatePolicies(SecCertificateRef cert) {
580      CFMutableSetRef policies = NULL;
581      policies = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
582      if (!policies) return NULL;
583  
584      const SecCECertificatePolicies *cp = SecCertificateGetCertificatePolicies(cert);
585      size_t policy_ix, policy_count = cp ? cp->numPolicies : 0;
586      for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
587          CFDataRef oidData = NULL;
588          DERItem *policyOID = &cp->policies[policy_ix].policyIdentifier;
589          oidData = CFDataCreate(kCFAllocatorDefault, policyOID->data, policyOID->length);
590          CFSetAddValue(policies, oidData);
591          CFReleaseSafe(oidData);
592      }
593      return policies;
594  }
595  
596  static bool checkPolicyOidData(SecCertificateRef cert , CFDataRef oid) {
597      CFSetRef policies = copyCertificatePolicies(cert);
598      bool found = false;
599      if (policies && CFSetContainsValue(policies, oid)) {
600          found = true;
601      }
602      CFReleaseSafe(policies);
603      return found;
604  }
605  
606  /* This one is different from SecPolicyCheckCertificatePolicyOid because
607     that one checks the whole chain. (And uses policy_set_t...) */
608  bool SecPolicyCheckCertCertificatePolicy(SecCertificateRef cert, CFTypeRef pvcValue) {
609      CFTypeRef value = pvcValue;
610      bool result = false;
611  
612      if (CFGetTypeID(value) == CFDataGetTypeID())
613      {
614          result = checkPolicyOidData(cert, value);
615      } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
616          CFDataRef dataOid = SecCertificateCreateOidDataFromString(NULL, value);
617          if (dataOid) {
618              result = checkPolicyOidData(cert, dataOid);
619              CFRelease(dataOid);
620          }
621      }
622      return result;
623  }
624  
625  bool SecPolicyCheckCertWeakKeySize(SecCertificateRef cert, CFTypeRef __unused pvcValue) {
626      if (cert && SecCertificateIsWeakKey(cert)) {
627          /* Leaf certificate has a weak key. */
628          return false;
629      }
630      return true;
631  }
632  
633  bool SecPolicyCheckCertKeySize(SecCertificateRef cert, CFTypeRef pvcValue) {
634      CFDictionaryRef keySizes = pvcValue;
635      if (!SecCertificateIsAtLeastMinKeySize(cert, keySizes)) {
636          return false;
637      }
638      return true;
639  }
640  
641  bool SecPolicyCheckCertWeakSignature(SecCertificateRef cert, CFTypeRef __unused pvcValue) {
642      bool result = true;
643      CFMutableArrayRef disallowedHashes = CFArrayCreateMutable(NULL, 3, &kCFTypeArrayCallBacks);
644      if (!disallowedHashes) {
645          return result;
646      }
647      CFArrayAppendValue(disallowedHashes, kSecSignatureDigestAlgorithmMD2);
648      CFArrayAppendValue(disallowedHashes, kSecSignatureDigestAlgorithmMD4);
649      CFArrayAppendValue(disallowedHashes, kSecSignatureDigestAlgorithmMD5);
650  
651      /* Weak Signature failures only for non-self-signed certs */
652      Boolean isSelfSigned = false;
653      OSStatus status = SecCertificateIsSelfSigned(cert, &isSelfSigned);
654      if (!SecPolicyCheckCertSignatureHashAlgorithms(cert, disallowedHashes) && (status != errSecSuccess || !isSelfSigned)) {
655          result = false;
656      }
657      CFReleaseSafe(disallowedHashes);
658      return result;
659  }
660  
661  static CFStringRef convertSignatureHashAlgorithm(SecSignatureHashAlgorithm algorithmEnum) {
662      const void *digests[] = { kSecSignatureDigestAlgorithmUnknown,
663          kSecSignatureDigestAlgorithmMD2,
664          kSecSignatureDigestAlgorithmMD4,
665          kSecSignatureDigestAlgorithmMD5,
666          kSecSignatureDigestAlgorithmSHA1,
667          kSecSignatureDigestAlgorithmSHA224,
668          kSecSignatureDigestAlgorithmSHA256,
669          kSecSignatureDigestAlgorithmSHA384,
670          kSecSignatureDigestAlgorithmSHA512,
671      };
672      return digests[algorithmEnum];
673  }
674  
675  bool SecPolicyCheckCertSignatureHashAlgorithms(SecCertificateRef cert, CFTypeRef pvcValue) {
676      /* skip signature algorithm check on self-signed certs */
677      Boolean isSelfSigned = false;
678      OSStatus status = SecCertificateIsSelfSigned(cert, &isSelfSigned);
679      if ((status == errSecSuccess) && isSelfSigned) {
680          return true;
681      }
682  
683      CFArrayRef disallowedHashAlgorithms = pvcValue;
684      CFStringRef certAlg = convertSignatureHashAlgorithm(SecCertificateGetSignatureHashAlgorithm(cert));
685      if (CFArrayContainsValue(disallowedHashAlgorithms, CFRangeMake(0,CFArrayGetCount(disallowedHashAlgorithms)), certAlg)) {
686          return false;
687      }
688      return true;
689  }
690  
691  bool SecPolicyCheckCertUnparseableExtension(SecCertificateRef cert, CFTypeRef pvcValue) {
692      if (SecCertificateGetUnparseableKnownExtension(cert) != kCFNotFound) {
693          return false;
694      }
695      return true;
696  }
697  
698  bool SecPolicyCheckCertNotCA(SecCertificateRef cert, CFTypeRef pvcValue) {
699      if (SecCertificateIsCA(cert)) {
700          return false;
701      }
702      return true;
703  }
704  
705  /*
706   * MARK: SecLeafPVC functions
707   */
708  static CFDictionaryRef SecLeafPVCCopyCallbacks(void) {
709      CFMutableDictionaryRef leafCallbacks = NULL;
710      leafCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
711                                                          &kCFTypeDictionaryKeyCallBacks, NULL);
712  
713  #undef POLICYCHECKMACRO
714  #define __PC_ADD_CHECK_(NAME)
715  #define __PC_ADD_CHECK_O(NAME) CFDictionaryAddValue(leafCallbacks, \
716  kSecPolicyCheck##NAME, SecPolicyCheckCert##NAME);
717  
718  #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
719  __PC_ADD_CHECK_##LEAFONLY(NAME)
720  #include "SecPolicyChecks.list"
721  
722      return leafCallbacks;
723  }
724  
725  void SecLeafPVCInit(SecLeafPVCRef pvc, SecCertificateRef leaf, CFArrayRef policies,
726                      CFAbsoluteTime verifyTime) {
727      secdebug("alloc", "leafpvc %p", pvc);
728      // Weird logging policies crashes.
729      //secdebug("policy", "%@", policies);
730      pvc->leaf = CFRetainSafe(leaf);
731      pvc->policies = CFRetainSafe(policies);
732      pvc->verifyTime = verifyTime;
733      pvc->callbacks = SecLeafPVCCopyCallbacks();
734      pvc->policyIX = 0;
735      pvc->result = true;
736  
737      CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
738                                                                    &kCFTypeDictionaryValueCallBacks);
739      pvc->details = CFArrayCreate(kCFAllocatorDefault, (const void **)&certDetail, 1,
740                                   &kCFTypeArrayCallBacks);
741      CFRelease(certDetail);
742  }
743  
744  
745  void SecLeafPVCDelete(SecLeafPVCRef pvc) {
746      secdebug("alloc", "delete leaf pvc %p", pvc);
747      CFReleaseNull(pvc->policies);
748      CFReleaseNull(pvc->details);
749      CFReleaseNull(pvc->callbacks);
750      CFReleaseNull(pvc->leaf);
751  }
752  
753  static bool SecLeafPVCSetResultForced(SecLeafPVCRef pvc,
754                                        CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
755  
756      secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key, "leaf",
757               (force ? "force" : ""), result);
758  
759      /* If this is not something the current policy cares about ignore
760       this error and return true so our caller continues evaluation. */
761      if (!force) {
762          /* @@@ The right long term fix might be to check if none of the passed
763           in policies contain this key, since not all checks are run for all
764           policies. */
765          SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
766          if (policy && !CFDictionaryContainsKey(policy->_options, key))
767              return true;
768      }
769  
770      /* @@@ Check to see if the SecTrustSettings for the certificate in question
771       tell us to ignore this error. */
772      pvc->result = false;
773      if (!pvc->details)
774          return false;
775  
776      CFMutableDictionaryRef detail =
777      (CFMutableDictionaryRef)CFArrayGetValueAtIndex(pvc->details, ix);
778  
779      /* Perhaps detail should have an array of results per key?  As it stands
780       in the case of multiple policy failures the last failure stands.  */
781      CFDictionarySetValue(detail, key, result);
782  
783      return true;
784  }
785  
786  static bool SecLeafPVCSetResult(SecLeafPVCRef pvc,
787                                  CFStringRef key, CFIndex ix, CFTypeRef result) {
788      return SecLeafPVCSetResultForced(pvc, key, ix, result, false);
789  }
790  
791  static void SecLeafPVCValidateKey(const void *key, const void *value,
792                                    void *context) {
793      SecLeafPVCRef pvc = (SecLeafPVCRef)context;
794  
795      /* If our caller doesn't want full details and we failed earlier there is
796       no point in doing additional checks. */
797      if (!pvc->result && !pvc->details)
798          return;
799  
800      SecPolicyCheckCertFunction fcn = (SecPolicyCheckCertFunction) CFDictionaryGetValue(pvc->callbacks, key);
801      if (!fcn) {
802          pvc->result = false;
803          return;
804      }
805  
806      /* kSecPolicyCheckTemporalValidity is special */
807      if (CFEqual(key, kSecPolicyCheckTemporalValidity)) {
808          CFDateRef verifyDate = CFDateCreate(NULL, pvc->verifyTime);
809          if(!fcn(pvc->leaf, verifyDate)) {
810              SecLeafPVCSetResult(pvc, key, 0, kCFBooleanFalse);
811          }
812          CFReleaseSafe(verifyDate);
813      } else {
814          /* get pvcValue from current policy */
815          SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
816          if (!policy) {
817              pvc->result = false;
818              return;
819          }
820          CFTypeRef pvcValue = (CFTypeRef)CFDictionaryGetValue(policy->_options, key);
821          if(!fcn(pvc->leaf, pvcValue)) {
822              SecLeafPVCSetResult(pvc, key, 0, kCFBooleanFalse);
823          }
824      }
825  }
826  
827  bool SecLeafPVCLeafChecks(SecLeafPVCRef pvc) {
828      pvc->result = true;
829      CFArrayRef policies = pvc->policies;
830      CFIndex ix, count = CFArrayGetCount(policies);
831      for (ix = 0; ix < count; ++ix) {
832          SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, ix);
833          pvc->policyIX = ix;
834          /* Validate all keys for all policies. */
835          CFDictionaryApplyFunction(policy->_options, SecLeafPVCValidateKey, pvc);
836          if (!pvc->result && !pvc->details)
837              return pvc->result;
838      }
839      return pvc->result;
840  }