2 * Copyright (c) 2002-2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * TrustRevocation.cpp - private revocation policy manipulation
28 #include <security_keychain/Trust.h>
29 #include <security_utilities/cfutilities.h>
30 #include <security_utilities/simpleprefs.h>
31 #include <CoreFoundation/CFData.h>
32 #include "SecBridge.h"
33 #include <Security/cssmapplePriv.h>
34 #include <Security/oidsalg.h>
37 * These may go into an SPI header for the SecTrust object.
40 /* this revocation policy disabled */
42 /* try, but tolerate inability to complete */
44 /* require successful revocation check if certificate indicates
45 * the policy is supported */
46 kSecRequireIfPresentInCertificate
,
47 /* require for every cert */
48 kSecRequireForAllCertificates
49 } SecRevocationPolicyStyle
;
51 using namespace KeychainCore
;
54 * Given an app-specified array of Policies, determine if at least one of them
55 * matches the given policy OID.
57 bool Trust::policySpecified(CFArrayRef policies
, const CSSM_OID
&inOid
)
59 if(policies
== NULL
) {
62 CFIndex numPolicies
= CFArrayGetCount(policies
);
63 for(CFIndex dex
=0; dex
<numPolicies
; dex
++) {
64 SecPolicyRef secPol
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, dex
);
65 SecPointer
<Policy
> pol
= Policy::required(SecPolicyRef(secPol
));
66 const CssmOid
&oid
= pol
->oid();
67 if(oid
== CssmOid::overlay(inOid
)) {
75 * Given an app-specified array of Policies, determine if at least one of them
76 * is an explicit revocation policy.
78 bool Trust::revocationPolicySpecified(CFArrayRef policies
)
80 if(policies
== NULL
) {
83 CFIndex numPolicies
= CFArrayGetCount(policies
);
84 for(CFIndex dex
=0; dex
<numPolicies
; dex
++) {
85 SecPolicyRef secPol
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, dex
);
86 SecPointer
<Policy
> pol
= Policy::required(SecPolicyRef(secPol
));
87 const CssmOid
&oid
= pol
->oid();
88 if(oid
== CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL
)) {
91 if(oid
== CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP
)) {
98 CFMutableArrayRef
Trust::addSpecifiedRevocationPolicies(
102 /* policies specified by SPI not implemented */
106 void Trust::freeSpecifiedRevocationPolicies(
111 /* shouldn't be called */
112 MacOSError::throwMe(unimpErr
);
115 static SecRevocationPolicyStyle
parseRevStyle(CFStringRef val
)
117 if(CFEqual(val
, kSecRevocationOff
)) {
120 else if(CFEqual(val
, kSecRevocationBestAttempt
)) {
121 return kSecBestAttempt
;
123 else if(CFEqual(val
, kSecRevocationRequireIfPresent
)) {
124 return kSecRequireIfPresentInCertificate
;
126 else if(CFEqual(val
, kSecRevocationRequireForAll
)) {
127 return kSecRequireForAllCertificates
;
134 CFDictionaryRef
Trust::defaultRevocationSettings()
137 defaults read ~/Library/Preferences/com.apple.security.revocation
139 CRLStyle = BestAttempt;
140 CRLSufficientPerCert = 1;
141 OCSPStyle = BestAttempt;
142 OCSPSufficientPerCert = 1;
143 RevocationFirst = OCSP;
146 const void *keys
[] = {
147 kSecRevocationCrlStyle
,
148 kSecRevocationCRLSufficientPerCert
,
149 kSecRevocationOcspStyle
,
150 kSecRevocationOCSPSufficientPerCert
,
151 kSecRevocationWhichFirst
153 const void *values
[] = {
154 kSecRevocationBestAttempt
,
156 kSecRevocationBestAttempt
,
158 kSecRevocationOcspFirst
161 return CFDictionaryCreate(kCFAllocatorDefault
, keys
,
162 values
, sizeof(keys
) / sizeof(*keys
),
163 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
166 CFMutableArrayRef
Trust::addPreferenceRevocationPolicies(
172 /* any per-user prefs? */
173 Dictionary
* pd
= Dictionary::CreateDictionary(kSecRevocationDomain
, Dictionary::US_User
, true);
184 pd
= Dictionary::CreateDictionary(kSecRevocationDomain
, Dictionary::US_System
, true);
193 CFDictionaryRef tempDict
= defaultRevocationSettings();
194 if (tempDict
== NULL
)
197 pd
= new Dictionary(tempDict
);
201 auto_ptr
<Dictionary
> prefsDict(pd
);
206 SecRevocationPolicyStyle ocspStyle
= kSecBestAttempt
;
207 SecRevocationPolicyStyle crlStyle
= kSecBestAttempt
;
208 SecPointer
<Policy
> ocspPolicy
;
209 SecPointer
<Policy
> crlPolicy
;
211 /* Are any revocation policies enabled? */
212 val
= prefsDict
->getStringValue(kSecRevocationOcspStyle
);
214 ocspStyle
= parseRevStyle(val
);
215 if(ocspStyle
!= kSecDisabled
) {
219 val
= prefsDict
->getStringValue(kSecRevocationCrlStyle
);
221 crlStyle
= parseRevStyle(val
);
222 if(crlStyle
!= kSecDisabled
) {
226 if(!doCrl
&& !doOcsp
) {
230 /* which policy first? */
231 bool ocspFirst
= true; // default if both present
232 if(doCrl
&& doOcsp
) {
233 val
= prefsDict
->getStringValue(kSecRevocationWhichFirst
);
234 if((val
!= NULL
) && CFEqual(val
, kSecRevocationCrlFirst
)) {
239 /* We're adding something to mPolicies, so make a copy we can work with */
240 CFMutableArrayRef policies
= CFArrayCreateMutableCopy(NULL
, 0, mPolicies
);
241 if(policies
== NULL
) {
242 throw std::bad_alloc();
246 /* Cook up a new Policy object */
247 ocspPolicy
= new Policy(mTP
, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP
));
248 CSSM_APPLE_TP_OCSP_OPTIONS opts
;
249 memset(&opts
, 0, sizeof(opts
));
250 opts
.Version
= CSSM_APPLE_TP_OCSP_OPTS_VERSION
;
252 /* Now fill in the OCSP-related blanks */
257 case kSecBestAttempt
:
258 /* default, nothing to set */
260 case kSecRequireIfPresentInCertificate
:
261 opts
.Flags
|= CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT
;
263 case kSecRequireForAllCertificates
:
264 opts
.Flags
|= CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT
;
268 if(prefsDict
->getBoolValue(kSecRevocationOCSPSufficientPerCert
)) {
269 opts
.Flags
|= CSSM_TP_ACTION_OCSP_SUFFICIENT
;
272 val
= prefsDict
->getStringValue(kSecOCSPLocalResponder
);
274 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
,
275 val
, kCFStringEncodingUTF8
, 0);
276 CFIndex len
= CFDataGetLength(cfData
);
277 opts
.LocalResponder
= (CSSM_DATA_PTR
)alloc
.malloc(sizeof(CSSM_DATA
));
278 opts
.LocalResponder
->Data
= (uint8
*)alloc
.malloc(len
);
279 opts
.LocalResponder
->Length
= len
;
280 memmove(opts
.LocalResponder
->Data
, CFDataGetBytePtr(cfData
), len
);
284 /* Policy manages its own copy of this data */
285 CSSM_DATA optData
= {sizeof(opts
), (uint8
*)&opts
};
286 ocspPolicy
->value() = optData
;
291 /* Cook up a new Policy object */
292 crlPolicy
= new Policy(mTP
, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL
));
293 CSSM_APPLE_TP_CRL_OPTIONS opts
;
294 memset(&opts
, 0, sizeof(opts
));
295 opts
.Version
= CSSM_APPLE_TP_CRL_OPTS_VERSION
;
297 /* Now fill in the CRL-related blanks */
298 opts
.CrlFlags
= CSSM_TP_ACTION_FETCH_CRL_FROM_NET
; // default true
303 case kSecBestAttempt
:
304 /* default, nothing to set */
306 case kSecRequireIfPresentInCertificate
:
307 opts
.CrlFlags
|= CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT
;
309 case kSecRequireForAllCertificates
:
310 opts
.CrlFlags
|= CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT
;
313 if(prefsDict
->getBoolValue(kSecRevocationCRLSufficientPerCert
)) {
314 opts
.CrlFlags
|= CSSM_TP_ACTION_CRL_SUFFICIENT
;
317 /* Policy manages its own copy of this data */
318 CSSM_DATA optData
= {sizeof(opts
), (uint8
*)&opts
};
319 crlPolicy
->value() = optData
;
323 /* append in order */
327 /* these SecCFObject go away when the policies array does */
328 CFArrayAppendValue(policies
, ocspPolicy
->handle(false));
329 CFArrayAppendValue(policies
, crlPolicy
->handle(false));
332 CFArrayAppendValue(policies
, crlPolicy
->handle(false));
333 CFArrayAppendValue(policies
, ocspPolicy
->handle(false));
337 CFArrayAppendValue(policies
, ocspPolicy
->handle(false));
343 CFArrayAppendValue(policies
, crlPolicy
->handle(false));
349 * Called when we created the last numAdded Policies in the specified Policy array
351 void Trust::freePreferenceRevocationPolicies(
356 uint32 numPolicies
= (uint32
)CFArrayGetCount(policies
);
357 if(numPolicies
< numAdded
) {
358 /* should never happen - throw? */
362 for(unsigned dex
=numPolicies
-numAdded
; dex
<numPolicies
; dex
++) {
363 SecPolicyRef secPol
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, dex
);
364 //SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol));
365 Policy
*pol
= Policy::required(secPol
);
366 const CssmOid
&oid
= pol
->oid(); // required
367 const CssmData
&optData
= pol
->value(); // optional
370 if(oid
== CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL
)) {
371 /* currently no CRL-specific policy data */
373 else if(oid
== CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP
)) {
374 CSSM_APPLE_TP_OCSP_OPTIONS
*opts
= (CSSM_APPLE_TP_OCSP_OPTIONS
*)optData
.Data
;
375 if(opts
->LocalResponder
!= NULL
) {
376 if(opts
->LocalResponder
->Data
!= NULL
) {
377 alloc
.free(opts
->LocalResponder
->Data
);
379 alloc
.free(opts
->LocalResponder
);
382 // managed by Policy alloc.free(optData.Data);
389 * Comparator function to correctly order revocation policies.
391 static CFComparisonResult
compareRevocationPolicies(
396 SecPointer
<Policy
> pol1
= Policy::required(SecPolicyRef(policy1
));
397 SecPointer
<Policy
> pol2
= Policy::required(SecPolicyRef(policy2
));
398 const CssmOid
&oid1
= pol1
->oid();
399 const CssmOid
&oid2
= pol2
->oid();
401 return kCFCompareEqualTo
;
403 bool ocspFirst
= true;
404 if(context
!= NULL
&& CFEqual((CFBooleanRef
)context
, kCFBooleanFalse
)) {
407 const CssmOid lastRevocationOid
= (ocspFirst
) ?
408 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL
) :
409 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP
);
410 const CssmOid firstRevocationOid
= (ocspFirst
) ?
411 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP
) :
412 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL
);
413 if(oid1
== lastRevocationOid
) {
414 /* should be ordered last, after all other policies */
415 return kCFCompareGreaterThan
;
417 if(oid1
== firstRevocationOid
) {
418 /* should be ordered after any policy except lastRevocationOid */
419 if(oid2
== lastRevocationOid
) {
420 return kCFCompareLessThan
;
422 return kCFCompareGreaterThan
;
424 /* normal policy in first position, anything else in second position */
425 return kCFCompareLessThan
;
429 * This method reorders any revocation policies which may be present
430 * in the provided array so they are at the end and evaluated last.
432 void Trust::orderRevocationPolicies(
433 CFMutableArrayRef policies
)
435 if(!policies
|| CFGetTypeID(policies
) != CFArrayGetTypeID()) {
438 /* check revocation prefs to determine which policy goes first */
439 CFBooleanRef ocspFirst
= kCFBooleanTrue
;
440 Dictionary
* pd
= Dictionary::CreateDictionary(kSecRevocationDomain
, Dictionary::US_User
, true);
445 auto_ptr
<Dictionary
> prefsDict(pd
);
446 CFStringRef val
= prefsDict
->getStringValue(kSecRevocationWhichFirst
);
447 if((val
!= NULL
) && CFEqual(val
, kSecRevocationCrlFirst
)) {
448 ocspFirst
= kCFBooleanFalse
;
453 CFShow(policies
); // before sort
454 CFArraySortValues(policies
, CFRangeMake(0, CFArrayGetCount(policies
)), compareRevocationPolicies
, (void*)ocspFirst
);
455 CFShow(policies
); // after sort, to see what changed
456 // check that policy order is what we expect
457 CFIndex numPolicies
= CFArrayGetCount(policies
);
458 for(CFIndex dex
=0; dex
<numPolicies
; dex
++) {
459 SecPolicyRef secPol
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, dex
);
460 SecPointer
<Policy
> pol
= Policy::required(SecPolicyRef(secPol
));
461 const CssmOid
&oid
= pol
->oid();
462 if(oid
== CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP
)) {
463 CFStringRef s
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("idx %d = OCSP"), dex
);
467 else if(oid
== CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL
)) {
468 CFStringRef s
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("idx %d = CRL"), dex
);
473 CFStringRef s
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("idx %d = normal"), dex
);
479 CFArraySortValues(policies
, CFRangeMake(0, CFArrayGetCount(policies
)), compareRevocationPolicies
, (void*)ocspFirst
);
484 * This method returns a copy of the mPolicies array which ensures that
485 * revocation checking (preferably OCSP, otherwise CRL) will be attempted.
487 * If OCSP is already in the mPolicies array, this makes sure the
488 * CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT and CSSM_TP_ACTION_OCSP_SUFFICIENT
489 * flags are set. If it's not already in the array, a new policy object is added.
491 * If CRL is already in the mPolicies array, this makes sure the
492 * CSSM_TP_ACTION_FETCH_CRL_FROM_NET and CSSM_TP_ACTION_CRL_SUFFICIENT flags are
493 * set. If it's not already in the array, a new policy object is added.
495 * Caller is responsible for releasing the returned policies array.
497 CFMutableArrayRef
Trust::forceRevocationPolicies(
502 SecPointer
<Policy
> ocspPolicy
;
503 SecPointer
<Policy
> crlPolicy
;
504 CSSM_APPLE_TP_OCSP_OPT_FLAGS ocspFlags
;
505 CSSM_APPLE_TP_CRL_OPT_FLAGS crlFlags
;
506 bool hasOcspPolicy
= false;
507 bool hasCrlPolicy
= false;
510 ocspFlags
= CSSM_TP_ACTION_OCSP_SUFFICIENT
;
511 crlFlags
= CSSM_TP_ACTION_FETCH_CRL_FROM_NET
| CSSM_TP_ACTION_CRL_SUFFICIENT
;
512 if (requirePerCert
) {
513 ocspFlags
|= CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT
;
514 crlFlags
|= CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT
;
517 CFIndex numPolicies
= (mPolicies
) ? CFArrayGetCount(mPolicies
) : 0;
518 for(CFIndex dex
=0; dex
<numPolicies
; dex
++) {
519 SecPolicyRef secPol
= (SecPolicyRef
)CFArrayGetValueAtIndex(mPolicies
, dex
);
520 SecPointer
<Policy
> pol
= Policy::required(SecPolicyRef(secPol
));
521 const CssmOid
&oid
= pol
->oid();
522 const CssmData
&optData
= pol
->value();
523 if(oid
== CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP
)) {
524 // make sure OCSP options are set correctly
525 CSSM_APPLE_TP_OCSP_OPTIONS
*opts
= (CSSM_APPLE_TP_OCSP_OPTIONS
*)optData
.Data
;
527 opts
->Flags
|= ocspFlags
;
529 CSSM_APPLE_TP_OCSP_OPTIONS newOpts
;
530 memset(&newOpts
, 0, sizeof(newOpts
));
531 newOpts
.Version
= CSSM_APPLE_TP_OCSP_OPTS_VERSION
;
532 newOpts
.Flags
= ocspFlags
;
533 CSSM_DATA optData
= {sizeof(newOpts
), (uint8
*)&newOpts
};
534 pol
->value() = optData
;
536 hasOcspPolicy
= true;
538 else if(oid
== CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL
)) {
539 // make sure CRL options are set correctly
540 CSSM_APPLE_TP_CRL_OPTIONS
*opts
= (CSSM_APPLE_TP_CRL_OPTIONS
*)optData
.Data
;
542 opts
->CrlFlags
|= crlFlags
;
544 CSSM_APPLE_TP_CRL_OPTIONS newOpts
;
545 memset(&newOpts
, 0, sizeof(newOpts
));
546 newOpts
.Version
= CSSM_APPLE_TP_CRL_OPTS_VERSION
;
547 newOpts
.CrlFlags
= crlFlags
;
548 CSSM_DATA optData
= {sizeof(newOpts
), (uint8
*)&newOpts
};
549 pol
->value() = optData
;
555 /* We're potentially adding something to mPolicies, so make a copy we can work with */
556 CFMutableArrayRef policies
= CFArrayCreateMutableCopy(NULL
, 0, mPolicies
);
557 if(policies
== NULL
) {
558 throw std::bad_alloc();
562 /* Cook up a new Policy object */
563 ocspPolicy
= new Policy(mTP
, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP
));
564 CSSM_APPLE_TP_OCSP_OPTIONS opts
;
565 memset(&opts
, 0, sizeof(opts
));
566 opts
.Version
= CSSM_APPLE_TP_OCSP_OPTS_VERSION
;
567 opts
.Flags
= ocspFlags
;
569 /* Check prefs dict for local responder info */
570 Dictionary
*prefsDict
= NULL
;
571 try { /* per-user prefs */
572 prefsDict
= Dictionary::CreateDictionary(kSecRevocationDomain
, Dictionary::US_User
, true);
573 if (!prefsDict
->dict()) {
579 if(prefsDict
== NULL
) {
580 try { /* system prefs */
581 prefsDict
= Dictionary::CreateDictionary(kSecRevocationDomain
, Dictionary::US_System
, true);
582 if (!prefsDict
->dict()) {
589 if(prefsDict
!= NULL
) {
590 CFStringRef val
= prefsDict
->getStringValue(kSecOCSPLocalResponder
);
592 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
,
593 val
, kCFStringEncodingUTF8
, 0);
594 CFIndex len
= CFDataGetLength(cfData
);
595 opts
.LocalResponder
= (CSSM_DATA_PTR
)alloc
.malloc(sizeof(CSSM_DATA
));
596 opts
.LocalResponder
->Data
= (uint8
*)alloc
.malloc(len
);
597 opts
.LocalResponder
->Length
= len
;
598 memmove(opts
.LocalResponder
->Data
, CFDataGetBytePtr(cfData
), len
);
603 /* Policy manages its own copy of the options data */
604 CSSM_DATA optData
= {sizeof(opts
), (uint8
*)&opts
};
605 ocspPolicy
->value() = optData
;
607 /* Policies array retains the Policy object */
608 CFArrayAppendValue(policies
, ocspPolicy
->handle(false));
611 if(prefsDict
!= NULL
)
616 /* Cook up a new Policy object */
617 crlPolicy
= new Policy(mTP
, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL
));
618 CSSM_APPLE_TP_CRL_OPTIONS opts
;
619 memset(&opts
, 0, sizeof(opts
));
620 opts
.Version
= CSSM_APPLE_TP_CRL_OPTS_VERSION
;
621 opts
.CrlFlags
= crlFlags
;
623 /* Policy manages its own copy of this data */
624 CSSM_DATA optData
= {sizeof(opts
), (uint8
*)&opts
};
625 crlPolicy
->value() = optData
;
627 /* Policies array retains the Policy object */
628 CFArrayAppendValue(policies
, crlPolicy
->handle(false));