]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
5c19dc3a A |
2 | * Copyright (c) 2005,2011-2015 Apple Inc. All Rights Reserved. |
3 | * | |
b1ab9ed8 | 4 | * @APPLE_LICENSE_HEADER_START@ |
5c19dc3a | 5 | * |
b1ab9ed8 A |
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. | |
5c19dc3a | 12 | * |
b1ab9ed8 A |
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. | |
5c19dc3a | 20 | * |
b1ab9ed8 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
24 | /* | |
5c19dc3a | 25 | * TrustSettings.h - class to manage cert trust settings. |
b1ab9ed8 | 26 | * |
b1ab9ed8 A |
27 | */ |
28 | ||
29 | #include "TrustSettings.h" | |
30 | #include "TrustSettingsSchema.h" | |
31 | #include "SecTrustSettings.h" | |
32 | #include "TrustSettingsUtils.h" | |
33 | #include "TrustKeychains.h" | |
b1ab9ed8 A |
34 | #include "Certificate.h" |
35 | #include "cssmdatetime.h" | |
427c49bc | 36 | #include <Security/SecBase.h> |
b1ab9ed8 | 37 | #include "SecTrustedApplicationPriv.h" |
5c19dc3a | 38 | #include <security_utilities/errors.h> |
b1ab9ed8 A |
39 | #include <security_utilities/debugging.h> |
40 | #include <security_utilities/logging.h> | |
41 | #include <security_utilities/cfutilities.h> | |
42 | #include <security_utilities/alloc.h> | |
6b200bc3 A |
43 | #include <security_utilities/casts.h> |
44 | #include <utilities/SecCFRelease.h> | |
fa7225c8 | 45 | #include <Security/Authorization.h> |
b1ab9ed8 A |
46 | #include <Security/cssmapplePriv.h> |
47 | #include <Security/oidscert.h> | |
5c19dc3a A |
48 | #include <Security/SecCertificatePriv.h> |
49 | #include <Security/SecPolicyPriv.h> | |
b1ab9ed8 A |
50 | #include <security_keychain/KCCursor.h> |
51 | #include <security_ocspd/ocspdClient.h> | |
52 | #include <CoreFoundation/CoreFoundation.h> | |
53 | #include <assert.h> | |
fa7225c8 | 54 | #include <dispatch/dispatch.h> |
b1ab9ed8 | 55 | #include <sys/stat.h> |
fa7225c8 | 56 | #include <syslog.h> |
b1ab9ed8 | 57 | |
fa7225c8 A |
58 | #if 0 |
59 | #define trustSettingsDbg(args...) syslog(LOG_ERR, ## args) | |
60 | #define trustSettingsEvalDbg(args...) syslog(LOG_ERR, ## args) | |
61 | #else | |
62 | #define trustSettingsDbg(args...) secinfo("trustSettings", ## args) | |
63 | #define trustSettingsEvalDbg(args...) secinfo("trustSettingsEval", ## args) | |
64 | #endif | |
b1ab9ed8 | 65 | |
5c19dc3a | 66 | /* |
b1ab9ed8 A |
67 | * Common error return for "malformed TrustSettings record" |
68 | */ | |
69 | #define errSecInvalidTrustedRootRecord errSecInvalidTrustSettings | |
70 | ||
71 | using namespace KeychainCore; | |
72 | ||
73 | #pragma mark --- Static functions --- | |
74 | ||
5c19dc3a | 75 | /* |
b1ab9ed8 A |
76 | * Comparator atoms to determine if an app's specified usage |
77 | * matches an individual trust setting. Each returns true on a match, false | |
5c19dc3a | 78 | * if the trust setting does not match the app's spec. |
b1ab9ed8 A |
79 | * |
80 | * A match fails iff: | |
81 | * | |
5c19dc3a A |
82 | * -- the app has specified a field, and the cert has a spec for that |
83 | * field, and the two specs do not match; | |
b1ab9ed8 A |
84 | * |
85 | * OR | |
86 | * | |
87 | * -- the cert has a spec for the field and the app hasn't specified the field | |
88 | */ | |
89 | static bool tsCheckPolicy( | |
90 | const CSSM_OID *appPolicy, | |
91 | CFDataRef certPolicy) | |
92 | { | |
93 | if(certPolicy != NULL) { | |
94 | if(appPolicy == NULL) { | |
95 | trustSettingsEvalDbg("tsCheckPolicy: certPolicy, !appPolicy"); | |
96 | return false; | |
97 | } | |
98 | unsigned cLen = (unsigned)CFDataGetLength(certPolicy); | |
99 | const UInt8 *cData = CFDataGetBytePtr(certPolicy); | |
100 | if((cLen != appPolicy->Length) || memcmp(appPolicy->Data, cData, cLen)) { | |
101 | trustSettingsEvalDbg("tsCheckPolicy: policy mismatch"); | |
102 | return false; | |
103 | } | |
104 | } | |
105 | return true; | |
106 | } | |
107 | ||
108 | /* | |
109 | * This one's slightly different: the match is for *this* app, not one | |
5c19dc3a | 110 | * specified by the app. |
b1ab9ed8 A |
111 | */ |
112 | static bool tsCheckApp( | |
113 | CFDataRef certApp) | |
114 | { | |
115 | if(certApp != NULL) { | |
116 | SecTrustedApplicationRef appRef; | |
117 | OSStatus ortn; | |
118 | ortn = SecTrustedApplicationCreateWithExternalRepresentation(certApp, &appRef); | |
119 | if(ortn) { | |
fa7225c8 | 120 | trustSettingsDbg("tsCheckApp: bad trustedApp data"); |
b1ab9ed8 A |
121 | return false; |
122 | } | |
123 | ortn = SecTrustedApplicationValidateWithPath(appRef, NULL); | |
124 | if(ortn) { | |
125 | /* Not this app */ | |
126 | return false; | |
127 | } | |
128 | } | |
5c19dc3a | 129 | |
b1ab9ed8 A |
130 | return true; |
131 | } | |
132 | ||
133 | static bool tsCheckKeyUse( | |
134 | SecTrustSettingsKeyUsage appKeyUse, | |
135 | CFNumberRef certKeyUse) | |
136 | { | |
137 | if(certKeyUse != NULL) { | |
138 | SInt32 certUse; | |
139 | CFNumberGetValue(certKeyUse, kCFNumberSInt32Type, &certUse); | |
140 | SecTrustSettingsKeyUsage cku = (SecTrustSettingsKeyUsage)certUse; | |
141 | if(cku == kSecTrustSettingsKeyUseAny) { | |
142 | /* explicitly allows anything */ | |
143 | return true; | |
144 | } | |
145 | /* cert specification must be a superset of app's intended use */ | |
146 | if(appKeyUse == 0) { | |
147 | trustSettingsEvalDbg("tsCheckKeyUse: certKeyUsage, !appKeyUsage"); | |
148 | return false; | |
149 | } | |
5c19dc3a | 150 | |
b1ab9ed8 A |
151 | if((cku & appKeyUse) != appKeyUse) { |
152 | trustSettingsEvalDbg("tsCheckKeyUse: keyUse mismatch"); | |
153 | return false; | |
154 | } | |
155 | } | |
156 | return true; | |
157 | } | |
158 | ||
159 | static bool tsCheckPolicyStr( | |
160 | const char *appPolicyStr, | |
161 | CFStringRef certPolicyStr) | |
162 | { | |
163 | if(certPolicyStr != NULL) { | |
164 | if(appPolicyStr == NULL) { | |
165 | trustSettingsEvalDbg("tsCheckPolicyStr: certPolicyStr, !appPolicyStr"); | |
166 | return false; | |
167 | } | |
168 | /* Let CF do the string compare */ | |
5c19dc3a | 169 | CFStringRef cfPolicyStr = CFStringCreateWithCString(NULL, appPolicyStr, |
b1ab9ed8 A |
170 | kCFStringEncodingUTF8); |
171 | if(cfPolicyStr == NULL) { | |
172 | /* I really don't see how this can happen */ | |
173 | trustSettingsEvalDbg("tsCheckPolicyStr: policyStr string conversion error"); | |
174 | return false; | |
175 | } | |
5c19dc3a | 176 | |
b1ab9ed8 A |
177 | // Some trust setting strings were created with a NULL character at the |
178 | // end, which was included in the length. Strip those off before compare | |
5c19dc3a | 179 | |
b1ab9ed8 A |
180 | CFMutableStringRef certPolicyStrNoNULL = CFStringCreateMutableCopy(NULL, 0, certPolicyStr); |
181 | if (certPolicyStrNoNULL == NULL) { | |
182 | /* I really don't see how this can happen either */ | |
183 | trustSettingsEvalDbg("tsCheckPolicyStr: policyStr string conversion error 2"); | |
6b200bc3 | 184 | CFReleaseNull(cfPolicyStr); |
b1ab9ed8 A |
185 | return false; |
186 | } | |
5c19dc3a A |
187 | |
188 | CFStringFindAndReplace(certPolicyStrNoNULL, CFSTR("\00"), | |
b1ab9ed8 | 189 | CFSTR(""), CFRangeMake(0, CFStringGetLength(certPolicyStrNoNULL)), kCFCompareBackwards); |
5c19dc3a | 190 | |
b1ab9ed8 A |
191 | CFComparisonResult res = CFStringCompare(cfPolicyStr, certPolicyStrNoNULL, 0); |
192 | CFRelease(cfPolicyStr); | |
193 | CFRelease(certPolicyStrNoNULL); | |
194 | if(res != kCFCompareEqualTo) { | |
195 | trustSettingsEvalDbg("tsCheckPolicyStr: policyStr mismatch"); | |
196 | return false; | |
197 | } | |
198 | } | |
199 | return true; | |
200 | } | |
201 | ||
5c19dc3a A |
202 | /* |
203 | * Determine if a cert's trust settings dictionary satisfies the specified | |
b1ab9ed8 A |
204 | * usage constraints. Returns true if so. |
205 | * Only certs with a SecTrustSettingsResult of kSecTrustSettingsResultTrustRoot | |
206 | * or kSecTrustSettingsResultTrustAsRoot will match. | |
207 | */ | |
208 | static bool qualifyUsageWithCertDict( | |
209 | CFDictionaryRef certDict, | |
210 | const CSSM_OID *policyOID, /* optional */ | |
211 | const char *policyStr, /* optional */ | |
212 | SecTrustSettingsKeyUsage keyUsage, /* optional; default = any (actually "all" here) */ | |
213 | bool onlyRoots) | |
214 | { | |
215 | /* this array is optional */ | |
216 | CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict, | |
217 | kTrustRecordTrustSettings); | |
218 | CFIndex numSpecs = 0; | |
219 | if(trustSettings != NULL) { | |
220 | numSpecs = CFArrayGetCount(trustSettings); | |
221 | } | |
222 | if(numSpecs == 0) { | |
223 | /* | |
224 | * Trivial case: cert has no trust settings, indicating that | |
5c19dc3a | 225 | * it's used for everything. |
b1ab9ed8 A |
226 | */ |
227 | trustSettingsEvalDbg("qualifyUsageWithCertDict: no trust settings"); | |
228 | return true; | |
229 | } | |
230 | for(CFIndex addDex=0; addDex<numSpecs; addDex++) { | |
231 | CFDictionaryRef tsDict = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, | |
232 | addDex); | |
5c19dc3a | 233 | |
b1ab9ed8 | 234 | /* per-cert specs: all optional */ |
5c19dc3a | 235 | CFDataRef certPolicy = (CFDataRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 236 | kSecTrustSettingsPolicy); |
5c19dc3a | 237 | CFDataRef certApp = (CFDataRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 238 | kSecTrustSettingsApplication); |
5c19dc3a | 239 | CFStringRef certPolicyStr = (CFStringRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 240 | kSecTrustSettingsPolicyString); |
5c19dc3a | 241 | CFNumberRef certKeyUsage = (CFNumberRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 242 | kSecTrustSettingsKeyUsage); |
5c19dc3a | 243 | CFNumberRef certResultType = (CFNumberRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 244 | kSecTrustSettingsResult); |
5c19dc3a | 245 | |
b1ab9ed8 A |
246 | if(!tsCheckPolicy(policyOID, certPolicy)) { |
247 | continue; | |
248 | } | |
249 | if(!tsCheckApp(certApp)) { | |
250 | continue; | |
251 | } | |
252 | if(!tsCheckKeyUse(keyUsage, certKeyUsage)) { | |
253 | continue; | |
254 | } | |
255 | if(!tsCheckPolicyStr(policyStr, certPolicyStr)) { | |
256 | continue; | |
257 | } | |
258 | ||
5c19dc3a | 259 | /* |
b1ab9ed8 | 260 | * This is a match, take whatever SecTrustSettingsResult is here, |
5c19dc3a | 261 | * including the default if not specified. |
b1ab9ed8 A |
262 | */ |
263 | SecTrustSettingsResult resultType = kSecTrustSettingsResultTrustRoot; | |
264 | if(certResultType) { | |
265 | SInt32 s; | |
266 | CFNumberGetValue(certResultType, kCFNumberSInt32Type, &s); | |
267 | resultType = (SecTrustSettingsResult)s; | |
268 | } | |
269 | switch(resultType) { | |
270 | case kSecTrustSettingsResultTrustRoot: | |
271 | trustSettingsEvalDbg("qualifyUsageWithCertDict: TrustRoot MATCH"); | |
272 | return true; | |
273 | case kSecTrustSettingsResultTrustAsRoot: | |
274 | if(onlyRoots) { | |
275 | trustSettingsEvalDbg("qualifyUsageWithCertDict: TrustAsRoot but not root"); | |
276 | return false; | |
277 | } | |
278 | trustSettingsEvalDbg("qualifyUsageWithCertDict: TrustAsRoot MATCH"); | |
279 | return true; | |
280 | default: | |
281 | trustSettingsEvalDbg("qualifyUsageWithCertDict: bad resultType " | |
282 | "(%lu)", (unsigned long)resultType); | |
283 | return false; | |
284 | } | |
285 | } | |
286 | trustSettingsEvalDbg("qualifyUsageWithCertDict: NO MATCH"); | |
287 | return false; | |
288 | } | |
289 | ||
5c19dc3a | 290 | /* |
b1ab9ed8 A |
291 | * Create initial top-level dictionary when constructing a new TrustSettings. |
292 | */ | |
293 | static CFMutableDictionaryRef tsInitialDict() | |
294 | { | |
5c19dc3a | 295 | CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, |
b1ab9ed8 A |
296 | kSecTrustRecordNumTopDictKeys, |
297 | &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
298 | ||
299 | /* the dictionary of per-cert entries */ | |
5c19dc3a | 300 | CFMutableDictionaryRef trustDict = CFDictionaryCreateMutable(NULL, 0, |
b1ab9ed8 A |
301 | &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
302 | CFDictionaryAddValue(dict, kTrustRecordTrustList, trustDict); | |
303 | CFRelease(trustDict); | |
304 | ||
305 | SInt32 vers = kSecTrustRecordVersionCurrent; | |
306 | CFNumberRef cfVers = CFNumberCreate(NULL, kCFNumberSInt32Type, &vers); | |
307 | CFDictionaryAddValue(dict, kTrustRecordVersion, cfVers); | |
308 | CFRelease(cfVers); | |
309 | return dict; | |
310 | } | |
311 | ||
312 | /* | |
313 | * Set the modification date of a per-cert dictionary to current time. | |
314 | */ | |
315 | static void tsSetModDate( | |
316 | CFMutableDictionaryRef dict) | |
317 | { | |
318 | CFDateRef modDate; | |
319 | ||
320 | modDate = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); | |
321 | CFDictionarySetValue(dict, kTrustRecordModDate, modDate); | |
322 | CFRelease(modDate); | |
323 | } | |
324 | ||
325 | /* make sure a presumed CFNumber can be converted to a 32-bit number */ | |
427c49bc | 326 | static |
b1ab9ed8 A |
327 | bool tsIsGoodCfNum(CFNumberRef cfn, SInt32 *num = NULL) |
328 | { | |
329 | if(cfn == NULL) { | |
330 | /* by convention */ | |
331 | if(num) { | |
332 | *num = 0; | |
333 | } | |
334 | return true; | |
335 | } | |
336 | if(CFGetTypeID(cfn) != CFNumberGetTypeID()) { | |
337 | return false; | |
338 | } | |
5c19dc3a | 339 | |
b1ab9ed8 A |
340 | SInt32 s; |
341 | if(!CFNumberGetValue(cfn, kCFNumberSInt32Type, &s)) { | |
342 | return false; | |
343 | } | |
344 | else { | |
345 | if(num) { | |
346 | *num = s; | |
347 | } | |
348 | return true; | |
349 | } | |
350 | } | |
351 | ||
352 | TrustSettings::TrustSettings(SecTrustSettingsDomain domain) | |
5c19dc3a | 353 | : mPropList(NULL), |
b1ab9ed8 A |
354 | mTrustDict(NULL), |
355 | mDictVersion(0), | |
356 | mDomain(domain), | |
357 | mDirty(false) | |
358 | { | |
359 | } | |
360 | ||
361 | ||
362 | ||
363 | #pragma mark --- Public methods --- | |
364 | ||
5c19dc3a | 365 | /* |
b1ab9ed8 A |
366 | * Normal constructor, from disk. |
367 | * If create is true, the absence of an on-disk TrustSettings file | |
5c19dc3a | 368 | * results in the creation of a new empty TrustSettings. If create is |
b1ab9ed8 A |
369 | * false and no on-disk TrustSettings exists, errSecNoTrustSettings is |
370 | * thrown. | |
371 | * If trim is true, the components of the on-disk TrustSettings not | |
372 | * needed for cert evaluation are discarded. This is for TrustSettings | |
5c19dc3a | 373 | * that will be cached in memory long-term. |
b1ab9ed8 A |
374 | */ |
375 | OSStatus TrustSettings::CreateTrustSettings( | |
376 | SecTrustSettingsDomain domain, | |
377 | bool create, | |
378 | bool trim, | |
379 | TrustSettings*& ts) | |
380 | { | |
381 | TrustSettings* t = new TrustSettings(domain); | |
382 | ||
383 | Allocator &alloc = Allocator::standard(); | |
384 | CSSM_DATA fileData = {0, NULL}; | |
427c49bc | 385 | OSStatus ortn = errSecSuccess; |
b1ab9ed8 A |
386 | struct stat sb; |
387 | const char *path; | |
388 | ||
389 | /* get trust settings from file, one way or another */ | |
390 | switch(domain) { | |
391 | case kSecTrustSettingsDomainAdmin: | |
5c19dc3a A |
392 | /* |
393 | * Quickie optimization: if it's not there, don't try to | |
b1ab9ed8 | 394 | * get it from ocspd. This is possible because the name of the |
5c19dc3a | 395 | * admin file is hard coded, but the per-user files aren't. |
b1ab9ed8 A |
396 | */ |
397 | path = TRUST_SETTINGS_PATH "/" ADMIN_TRUST_SETTINGS; | |
398 | if(stat(path, &sb)) { | |
399 | trustSettingsDbg("TrustSettings: no admin record; skipping"); | |
400 | ortn = errSecNoTrustSettings; | |
401 | break; | |
402 | } | |
403 | /* else drop thru, get it from ocspd */ | |
404 | case kSecTrustSettingsDomainUser: | |
405 | /* get settings from ocspd */ | |
406 | ortn = ocspdTrustSettingsRead(alloc, domain, fileData); | |
407 | break; | |
408 | case kSecTrustSettingsDomainSystem: | |
409 | /* immutable; it's safe for us to read this directly */ | |
410 | if(tsReadFile(SYSTEM_TRUST_SETTINGS_PATH, alloc, fileData)) { | |
411 | ortn = errSecNoTrustSettings; | |
412 | } | |
413 | break; | |
414 | default: | |
415 | delete t; | |
427c49bc | 416 | return errSecParam; |
b1ab9ed8 A |
417 | } |
418 | if(ortn) { | |
419 | if(create) { | |
420 | trustSettingsDbg("TrustSettings: creating new record for domain %d", | |
421 | (int)domain); | |
422 | t->mPropList = tsInitialDict(); | |
423 | t->mDirty = true; | |
424 | } | |
425 | else { | |
426 | trustSettingsDbg("TrustSettings: record not found for domain %d", | |
427 | (int)domain); | |
428 | delete t; | |
429 | return ortn; | |
430 | } | |
431 | } | |
432 | else { | |
433 | CFRef<CFDataRef> propList(CFDataCreate(NULL, fileData.Data, fileData.Length)); | |
434 | t->initFromData(propList); | |
435 | alloc.free(fileData.Data); | |
436 | } | |
437 | t->validatePropList(trim); | |
5c19dc3a | 438 | |
b1ab9ed8 | 439 | ts = t; |
427c49bc | 440 | return errSecSuccess; |
b1ab9ed8 A |
441 | } |
442 | ||
5c19dc3a | 443 | /* |
b1ab9ed8 A |
444 | * Create from external data, obtained by createExternal(). |
445 | * If externalData is NULL, we'll create an empty mTrustDict. | |
446 | */ | |
447 | OSStatus TrustSettings::CreateTrustSettings( | |
448 | SecTrustSettingsDomain domain, | |
449 | CFDataRef externalData, | |
450 | TrustSettings*& ts) | |
451 | { | |
452 | switch(domain) { | |
453 | case kSecTrustSettingsDomainUser: | |
454 | case kSecTrustSettingsDomainAdmin: | |
455 | case kSecTrustSettingsDomainMemory: | |
456 | break; | |
457 | case kSecTrustSettingsDomainSystem: /* no can do, that implies writing to it */ | |
458 | default: | |
427c49bc | 459 | return errSecParam; |
b1ab9ed8 A |
460 | } |
461 | ||
462 | TrustSettings* t = new TrustSettings(domain); | |
463 | ||
464 | if(externalData != NULL) { | |
465 | t->initFromData(externalData); | |
466 | } | |
467 | else { | |
468 | t->mPropList = tsInitialDict(); | |
469 | } | |
470 | t->validatePropList(TRIM_NO); /* never trim this */ | |
471 | t->mDirty = true; | |
5c19dc3a | 472 | |
b1ab9ed8 | 473 | ts = t; |
427c49bc | 474 | return errSecSuccess; |
b1ab9ed8 A |
475 | } |
476 | ||
477 | ||
478 | TrustSettings::~TrustSettings() | |
479 | { | |
480 | trustSettingsDbg("TrustSettings(domain %d) destructor", (int)mDomain); | |
481 | CFRELEASE(mPropList); /* may be null if trimmed */ | |
482 | CFRELEASE(mTrustDict); /* normally always non-NULL */ | |
483 | ||
484 | } | |
485 | ||
486 | /* common code to init mPropList from raw data */ | |
487 | void TrustSettings::initFromData( | |
488 | CFDataRef trustSettingsData) | |
489 | { | |
490 | CFStringRef errStr = NULL; | |
491 | ||
492 | mPropList = (CFMutableDictionaryRef)CFPropertyListCreateFromXMLData( | |
493 | NULL, | |
494 | trustSettingsData, | |
495 | kCFPropertyListMutableContainersAndLeaves, | |
496 | &errStr); | |
497 | if(mPropList == NULL) { | |
498 | trustSettingsDbg("TrustSettings::initFromData decode err (%s)", | |
499 | errStr ? CFStringGetCStringPtr(errStr, kCFStringEncodingUTF8) : "<no err>"); | |
500 | if(errStr != NULL) { | |
501 | CFRelease(errStr); | |
502 | } | |
503 | MacOSError::throwMe(errSecInvalidTrustSettings); | |
504 | } | |
505 | } | |
506 | ||
5c19dc3a | 507 | /* |
b1ab9ed8 A |
508 | * Flush property list data out to disk if dirty. |
509 | */ | |
510 | void TrustSettings::flushToDisk() | |
511 | { | |
512 | if(!mDirty) { | |
513 | trustSettingsDbg("flushToDisk, domain %d, !dirty!", (int)mDomain); | |
5c19dc3a | 514 | return; |
b1ab9ed8 A |
515 | } |
516 | if(mPropList == NULL) { | |
517 | trustSettingsDbg("flushToDisk, domain %d, trimmed!", (int)mDomain); | |
518 | assert(0); | |
427c49bc | 519 | MacOSError::throwMe(errSecInternalComponent); |
b1ab9ed8 A |
520 | } |
521 | switch(mDomain) { | |
522 | case kSecTrustSettingsDomainSystem: | |
523 | case kSecTrustSettingsDomainMemory: | |
524 | /* caller shouldn't even try this */ | |
525 | default: | |
526 | trustSettingsDbg("flushToDisk, bad domain (%d)", (int)mDomain); | |
427c49bc | 527 | MacOSError::throwMe(errSecInternalComponent); |
b1ab9ed8 A |
528 | case kSecTrustSettingsDomainUser: |
529 | case kSecTrustSettingsDomainAdmin: | |
530 | break; | |
531 | } | |
532 | ||
533 | /* | |
5c19dc3a A |
534 | * Optimization: if there are no certs in the mTrustDict dictionary, |
535 | * we tell ocspd to *remove* the settings for the specified domain. | |
536 | * Having *no* settings uses less memory and is faster than having | |
537 | * an empty settings file, especially for the admin domain, where we | |
b1ab9ed8 | 538 | * can avoid |
5c19dc3a | 539 | * an RPC if the settings file is simply not there. |
b1ab9ed8 A |
540 | */ |
541 | CFRef<CFDataRef> xmlData; | |
542 | CSSM_DATA cssmXmlData = {0, NULL}; | |
543 | CFIndex numCerts = CFDictionaryGetCount(mTrustDict); | |
544 | if(numCerts) { | |
545 | xmlData.take(CFPropertyListCreateXMLData(NULL, mPropList)); | |
546 | if(!xmlData) { | |
547 | /* we've been very careful; this should never happen */ | |
548 | trustSettingsDbg("flushToDisk, domain %d: error converting to XML", (int)mDomain); | |
427c49bc | 549 | MacOSError::throwMe(errSecInternalComponent); |
b1ab9ed8 A |
550 | } |
551 | cssmXmlData.Data = (uint8 *)CFDataGetBytePtr(xmlData); | |
552 | cssmXmlData.Length = CFDataGetLength(xmlData); | |
553 | } | |
554 | else { | |
555 | trustSettingsDbg("flushToDisk, domain %d: DELETING trust settings", (int)mDomain); | |
556 | } | |
5c19dc3a | 557 | |
b1ab9ed8 A |
558 | /* cook up auth stuff so ocspd can act on our behalf */ |
559 | AuthorizationRef authRef; | |
560 | OSStatus ortn; | |
5c19dc3a | 561 | ortn = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, |
b1ab9ed8 A |
562 | 0, &authRef); |
563 | if(ortn) { | |
5c19dc3a | 564 | trustSettingsDbg("flushToDisk, domain %d: AuthorizationCreate returned %ld", |
b1ab9ed8 | 565 | (int)mDomain, (long)ortn); |
427c49bc | 566 | MacOSError::throwMe(errSecInternalComponent); |
b1ab9ed8 A |
567 | } |
568 | AuthorizationExternalForm authExt; | |
569 | CSSM_DATA authBlob = {sizeof(authExt), (uint8 *)&authExt}; | |
570 | ortn = AuthorizationMakeExternalForm(authRef, &authExt); | |
571 | if(ortn) { | |
5c19dc3a | 572 | trustSettingsDbg("flushToDisk, domain %d: AuthorizationMakeExternalForm returned %ld", |
b1ab9ed8 | 573 | (int)mDomain, (long)ortn); |
427c49bc | 574 | ortn = errSecInternalComponent; |
b1ab9ed8 A |
575 | goto errOut; |
576 | } | |
5c19dc3a | 577 | |
b1ab9ed8 A |
578 | ortn = ocspdTrustSettingsWrite(mDomain, authBlob, cssmXmlData); |
579 | if(ortn) { | |
5c19dc3a | 580 | trustSettingsDbg("flushToDisk, domain %d: ocspdTrustSettingsWrite returned %ld", |
b1ab9ed8 A |
581 | (int)mDomain, (long)ortn); |
582 | goto errOut; | |
583 | } | |
584 | trustSettingsDbg("flushToDisk, domain %d: wrote to disk", (int)mDomain); | |
585 | mDirty = false; | |
586 | errOut: | |
587 | AuthorizationFree(authRef, 0); | |
588 | if(ortn) { | |
589 | MacOSError::throwMe(ortn); | |
590 | } | |
591 | } | |
592 | ||
593 | /* | |
594 | * Obtain external representation of TrustSettings data. | |
595 | */ | |
596 | CFDataRef TrustSettings::createExternal() | |
597 | { | |
598 | assert(mPropList); | |
599 | CFDataRef xmlData = CFPropertyListCreateXMLData(NULL, mPropList); | |
600 | if(xmlData == NULL) { | |
601 | trustSettingsDbg("createExternal, domain %d: error converting to XML", | |
602 | (int)mDomain); | |
427c49bc | 603 | MacOSError::throwMe(errSecInternalComponent); |
b1ab9ed8 A |
604 | } |
605 | return xmlData; | |
606 | } | |
607 | ||
5c19dc3a | 608 | /* |
b1ab9ed8 | 609 | * Evaluate specified cert. Returns true if we found a record for the cert |
5c19dc3a A |
610 | * matching specified constraints. |
611 | * Note that a true return with a value of kSecTrustSettingsResultUnspecified for | |
b1ab9ed8 | 612 | * the resultType means that a cert isn't to be trusted or untrusted |
5c19dc3a | 613 | * per se; it just means that we only found allowedErrors entries. |
b1ab9ed8 A |
614 | * |
615 | * Found "allows errors" values are added to the incoming allowedErrors | |
616 | * array which is reallocd as needed (and which may be NULL or non-NULL on | |
617 | * entry). | |
618 | */ | |
619 | bool TrustSettings::evaluateCert( | |
620 | CFStringRef certHashStr, | |
621 | const CSSM_OID *policyOID, /* optional */ | |
622 | const char *policyStr, /* optional */ | |
623 | SecTrustSettingsKeyUsage keyUsage, /* optional */ | |
624 | bool isRootCert, /* for checking default setting */ | |
625 | CSSM_RETURN **allowedErrors, /* IN/OUT; reallocd as needed */ | |
626 | uint32 *numAllowedErrors, /* IN/OUT */ | |
627 | SecTrustSettingsResult *resultType, /* RETURNED */ | |
628 | bool *foundAnyEntry) /* RETURNED */ | |
629 | { | |
630 | assert(mTrustDict != NULL); | |
631 | ||
632 | /* get trust settings dictionary for this cert */ | |
633 | CFDictionaryRef certDict = findDictionaryForCertHash(certHashStr); | |
b1ab9ed8 A |
634 | #if CERT_HASH_DEBUG |
635 | /* @@@ debug only @@@ */ | |
636 | /* print certificate hash and found dictionary reference */ | |
637 | const size_t maxHashStrLen = 512; | |
638 | char *buf = (char*)malloc(maxHashStrLen); | |
639 | if (buf) { | |
640 | if (!CFStringGetCString(certHashStr, buf, (CFIndex)maxHashStrLen, kCFStringEncodingUTF8)) { | |
641 | buf[0]='\0'; | |
642 | } | |
643 | trustSettingsEvalDbg("evaluateCert for \"%s\", found dict %p", buf, certDict); | |
644 | free(buf); | |
645 | } | |
646 | #endif | |
647 | ||
648 | if(certDict == NULL) { | |
649 | *foundAnyEntry = false; | |
650 | return false; | |
651 | } | |
652 | *foundAnyEntry = true; | |
653 | ||
654 | /* to-be-returned array of allowed errors */ | |
655 | CSSM_RETURN *allowedErrs = *allowedErrors; | |
656 | uint32 numAllowedErrs = *numAllowedErrors; | |
5c19dc3a | 657 | |
b1ab9ed8 A |
658 | /* this means "we found something other than allowedErrors" if true */ |
659 | bool foundSettings = false; | |
5c19dc3a | 660 | |
b1ab9ed8 A |
661 | /* to be returned in *resultType if it ends up something other than Invalid */ |
662 | SecTrustSettingsResult returnedResult = kSecTrustSettingsResultInvalid; | |
663 | ||
5c19dc3a | 664 | /* |
b1ab9ed8 A |
665 | * Note since we validated the entire mPropList in our constructor, and we're careful |
666 | * about what we put into it, we don't bother typechecking its contents here. | |
667 | * Also note that the kTrustRecordTrustSettings entry is optional. | |
668 | */ | |
669 | CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict, | |
670 | kTrustRecordTrustSettings); | |
671 | CFIndex numSpecs = 0; | |
672 | if(trustSettings != NULL) { | |
673 | numSpecs = CFArrayGetCount(trustSettings); | |
674 | } | |
675 | if(numSpecs == 0) { | |
676 | /* | |
677 | * Trivial case: cert has no trust settings, indicating that | |
5c19dc3a | 678 | * it's used for everything. |
b1ab9ed8 A |
679 | */ |
680 | trustSettingsEvalDbg("evaluateCert: no trust settings"); | |
681 | /* the default... */ | |
682 | *resultType = kSecTrustSettingsResultTrustRoot; | |
683 | return true; | |
684 | } | |
5c19dc3a | 685 | |
b1ab9ed8 A |
686 | /* |
687 | * The decidedly nontrivial part: grind thru all of the cert's trust | |
688 | * settings, see if the cert matches the caller's specified usage. | |
689 | */ | |
690 | for(CFIndex addDex=0; addDex<numSpecs; addDex++) { | |
691 | CFDictionaryRef tsDict = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, | |
692 | addDex); | |
5c19dc3a | 693 | |
b1ab9ed8 | 694 | /* per-cert specs: all optional */ |
5c19dc3a | 695 | CFDataRef certPolicy = (CFDataRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 696 | kSecTrustSettingsPolicy); |
5c19dc3a | 697 | CFDataRef certApp = (CFDataRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 698 | kSecTrustSettingsApplication); |
5c19dc3a | 699 | CFStringRef certPolicyStr = (CFStringRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 700 | kSecTrustSettingsPolicyString); |
5c19dc3a | 701 | CFNumberRef certKeyUsage = (CFNumberRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 702 | kSecTrustSettingsKeyUsage); |
5c19dc3a | 703 | CFNumberRef certResultType = (CFNumberRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 704 | kSecTrustSettingsResult); |
5c19dc3a | 705 | CFNumberRef certAllowedErr = (CFNumberRef)CFDictionaryGetValue(tsDict, |
b1ab9ed8 | 706 | kSecTrustSettingsAllowedError); |
5c19dc3a | 707 | |
b1ab9ed8 A |
708 | /* now, skip if we find a constraint that doesn't match intended use */ |
709 | if(!tsCheckPolicy(policyOID, certPolicy)) { | |
710 | continue; | |
711 | } | |
712 | if(!tsCheckApp(certApp)) { | |
713 | continue; | |
714 | } | |
715 | if(!tsCheckKeyUse(keyUsage, certKeyUsage)) { | |
716 | continue; | |
717 | } | |
718 | if(!tsCheckPolicyStr(policyStr, certPolicyStr)) { | |
719 | continue; | |
720 | } | |
5c19dc3a | 721 | |
b1ab9ed8 A |
722 | trustSettingsEvalDbg("evaluateCert: MATCH"); |
723 | foundSettings = true; | |
724 | ||
725 | if(certAllowedErr) { | |
726 | /* note we already validated this value */ | |
727 | SInt32 s; | |
728 | CFNumberGetValue(certAllowedErr, kCFNumberSInt32Type, &s); | |
5c19dc3a | 729 | allowedErrs = (CSSM_RETURN *)::realloc(allowedErrs, |
b1ab9ed8 A |
730 | ++numAllowedErrs * sizeof(CSSM_RETURN)); |
731 | allowedErrs[numAllowedErrs-1] = (CSSM_RETURN) s; | |
732 | } | |
5c19dc3a | 733 | |
b1ab9ed8 A |
734 | /* |
735 | * We found a match, but we only return the current result type | |
736 | * to caller if we haven't already returned something other than | |
737 | * kSecTrustSettingsResultUnspecified. Once we find a valid result type, | |
5c19dc3a | 738 | * we keep on searching, but only for additional allowed errors. |
b1ab9ed8 A |
739 | */ |
740 | switch(returnedResult) { | |
741 | /* found match but no valid resultType yet */ | |
742 | case kSecTrustSettingsResultUnspecified: | |
743 | /* haven't been thru here */ | |
744 | case kSecTrustSettingsResultInvalid: | |
745 | if(certResultType) { | |
746 | /* note we already validated this */ | |
747 | SInt32 s; | |
748 | CFNumberGetValue(certResultType, kCFNumberSInt32Type, &s); | |
749 | returnedResult = (SecTrustSettingsResult)s; | |
750 | } | |
751 | else { | |
752 | /* default is "copacetic" */ | |
753 | returnedResult = kSecTrustSettingsResultTrustRoot; | |
754 | } | |
5c19dc3a | 755 | break; |
b1ab9ed8 A |
756 | default: |
757 | /* we already have a definitive resultType, don't change it */ | |
758 | break; | |
759 | } | |
760 | } /* for each dictionary in trustSettings */ | |
761 | ||
762 | *allowedErrors = allowedErrs; | |
763 | *numAllowedErrors = numAllowedErrs; | |
764 | if(returnedResult != kSecTrustSettingsResultInvalid) { | |
765 | *resultType = returnedResult; | |
766 | } | |
767 | return foundSettings; | |
768 | } | |
769 | ||
770 | ||
771 | /* | |
772 | * Find all certs in specified keychain list which have entries in this trust record. | |
773 | * Certs already in the array are not added. | |
774 | */ | |
775 | void TrustSettings::findCerts( | |
776 | StorageManager::KeychainList &keychains, | |
777 | CFMutableArrayRef certArray) | |
778 | { | |
5c19dc3a | 779 | findQualifiedCerts(keychains, |
b1ab9ed8 A |
780 | true, /* findAll */ |
781 | false, /* onlyRoots */ | |
782 | NULL, NULL, kSecTrustSettingsKeyUseAny, | |
783 | certArray); | |
784 | } | |
785 | ||
786 | void TrustSettings::findQualifiedCerts( | |
787 | StorageManager::KeychainList &keychains, | |
5c19dc3a A |
788 | /* |
789 | * If findAll is true, all certs are returned and the subsequent | |
790 | * qualifiers are ignored | |
b1ab9ed8 A |
791 | */ |
792 | bool findAll, | |
793 | /* if true, only return root (self-signed) certs */ | |
794 | bool onlyRoots, | |
795 | const CSSM_OID *policyOID, /* optional */ | |
796 | const char *policyString, /* optional */ | |
797 | SecTrustSettingsKeyUsage keyUsage, /* optional */ | |
798 | CFMutableArrayRef certArray) /* certs appended here */ | |
799 | { | |
800 | StLock<Mutex> _(SecTrustKeychainsGetMutex()); | |
801 | ||
5c19dc3a | 802 | /* |
b1ab9ed8 | 803 | * a set, hopefully with a good hash function for CFData, to keep track of what's |
5c19dc3a | 804 | * been added to the outgoing array. |
b1ab9ed8 A |
805 | */ |
806 | CFRef<CFMutableSetRef> certSet(CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks)); | |
5c19dc3a | 807 | |
b1ab9ed8 | 808 | /* search: all certs, no attributes */ |
6b200bc3 | 809 | KCCursor cursor(keychains, (SecItemClass) CSSM_DL_DB_RECORD_X509_CERTIFICATE, NULL); |
b1ab9ed8 A |
810 | Item certItem; |
811 | bool found; | |
fa7225c8 | 812 | unsigned int total=0, entries=0, qualified=0; |
b1ab9ed8 A |
813 | do { |
814 | found = cursor->next(certItem); | |
815 | if(!found) { | |
816 | break; | |
817 | } | |
fa7225c8 | 818 | ++total; |
6b200bc3 | 819 | |
5c19dc3a A |
820 | /* must convert to unified SecCertificateRef */ |
821 | SecPointer<Certificate> certificate(static_cast<Certificate *>(&*certItem)); | |
fa7225c8 A |
822 | CssmData certCssmData; |
823 | try { | |
824 | certCssmData = certificate->data(); | |
825 | } | |
826 | catch (...) {} | |
5c19dc3a A |
827 | if (!(certCssmData.Data && certCssmData.Length)) { |
828 | continue; | |
829 | } | |
830 | CFRef<CFDataRef> cfDataRef(CFDataCreate(NULL, certCssmData.Data, certCssmData.Length)); | |
831 | CFRef<SecCertificateRef> certRef(SecCertificateCreateWithData(NULL, cfDataRef)); | |
5c19dc3a | 832 | |
b1ab9ed8 A |
833 | /* do we have an entry for this cert? */ |
834 | CFDictionaryRef certDict = findDictionaryForCert(certRef); | |
835 | if(certDict == NULL) { | |
836 | continue; | |
837 | } | |
fa7225c8 | 838 | ++entries; |
5c19dc3a | 839 | |
b1ab9ed8 A |
840 | if(!findAll) { |
841 | /* qualify */ | |
842 | if(!qualifyUsageWithCertDict(certDict, policyOID, | |
843 | policyString, keyUsage, onlyRoots)) { | |
844 | continue; | |
845 | } | |
846 | } | |
fa7225c8 | 847 | ++qualified; |
5c19dc3a | 848 | |
b1ab9ed8 A |
849 | /* see if we already have this one - get in CFData form */ |
850 | CSSM_DATA certData; | |
851 | OSStatus ortn = SecCertificateGetData(certRef, &certData); | |
852 | if(ortn) { | |
853 | trustSettingsEvalDbg("findQualifiedCerts: SecCertificateGetData error"); | |
854 | continue; | |
855 | } | |
856 | CFRef<CFDataRef> cfData(CFDataCreate(NULL, certData.Data, certData.Length)); | |
fa7225c8 | 857 | CFDataRef cfd = cfData.get(); |
b1ab9ed8 A |
858 | if(CFSetContainsValue(certSet, cfd)) { |
859 | trustSettingsEvalDbg("findQualifiedCerts: dup cert"); | |
860 | continue; | |
861 | } | |
862 | else { | |
863 | /* add to the tracking set, which owns the CFData now */ | |
5c19dc3a | 864 | CFSetAddValue(certSet, cfd); |
b1ab9ed8 A |
865 | /* and add the SecCert to caller's array, which owns that now */ |
866 | CFArrayAppendValue(certArray, certRef); | |
867 | } | |
868 | } while(found); | |
fa7225c8 A |
869 | |
870 | trustSettingsEvalDbg("findQualifiedCerts: examined %d certs, qualified %d of %d", | |
871 | total, qualified, entries); | |
b1ab9ed8 A |
872 | } |
873 | ||
874 | /* | |
875 | * Obtain trust settings for the specified cert. Returned settings array | |
876 | * is in the public API form; caller must release. Returns NULL | |
5c19dc3a | 877 | * (does not throw) if the cert is not present in this TrustRecord. |
b1ab9ed8 A |
878 | */ |
879 | CFArrayRef TrustSettings::copyTrustSettings( | |
880 | SecCertificateRef certRef) | |
881 | { | |
882 | CFDictionaryRef certDict = NULL; | |
5c19dc3a | 883 | |
b1ab9ed8 A |
884 | /* find the on-disk usage constraints for this cert */ |
885 | certDict = findDictionaryForCert(certRef); | |
886 | if(certDict == NULL) { | |
887 | trustSettingsDbg("copyTrustSettings: dictionary not found"); | |
888 | return NULL; | |
889 | } | |
890 | CFArrayRef diskTrustSettings = (CFArrayRef)CFDictionaryGetValue(certDict, | |
891 | kTrustRecordTrustSettings); | |
892 | CFIndex numSpecs = 0; | |
893 | if(diskTrustSettings != NULL) { | |
894 | /* this field is optional */ | |
895 | numSpecs = CFArrayGetCount(diskTrustSettings); | |
896 | } | |
897 | ||
5c19dc3a | 898 | /* |
b1ab9ed8 A |
899 | * Convert to API-style array of dictionaries. |
900 | * We give the caller an array even if it's empty. | |
901 | */ | |
5c19dc3a | 902 | CFRef<CFMutableArrayRef> outArray(CFArrayCreateMutable(NULL, numSpecs, |
b1ab9ed8 A |
903 | &kCFTypeArrayCallBacks)); |
904 | for(CFIndex dex=0; dex<numSpecs; dex++) { | |
5c19dc3a | 905 | CFDictionaryRef diskTsDict = |
b1ab9ed8 A |
906 | (CFDictionaryRef)CFArrayGetValueAtIndex(diskTrustSettings, dex); |
907 | /* already validated... */ | |
908 | assert(CFGetTypeID(diskTsDict) == CFDictionaryGetTypeID()); | |
5c19dc3a | 909 | |
fa7225c8 A |
910 | CFTypeRef certPolicy = (CFTypeRef) CFDictionaryGetValue(diskTsDict, kSecTrustSettingsPolicy); |
911 | CFStringRef policyName = (CFStringRef)CFDictionaryGetValue(diskTsDict, kSecTrustSettingsPolicyName); | |
b1ab9ed8 A |
912 | CFDataRef certApp = (CFDataRef) CFDictionaryGetValue(diskTsDict, kSecTrustSettingsApplication); |
913 | CFStringRef policyStr = (CFStringRef)CFDictionaryGetValue(diskTsDict, kSecTrustSettingsPolicyString); | |
914 | CFNumberRef allowedErr = (CFNumberRef)CFDictionaryGetValue(diskTsDict, kSecTrustSettingsAllowedError); | |
915 | CFNumberRef resultType = (CFNumberRef)CFDictionaryGetValue(diskTsDict, kSecTrustSettingsResult); | |
916 | CFNumberRef keyUsage = (CFNumberRef)CFDictionaryGetValue(diskTsDict, kSecTrustSettingsKeyUsage); | |
5c19dc3a A |
917 | |
918 | if((certPolicy == NULL) && | |
b1ab9ed8 A |
919 | (certApp == NULL) && |
920 | (policyStr == NULL) && | |
921 | (allowedErr == NULL) && | |
922 | (resultType == NULL) && | |
923 | (keyUsage == NULL)) { | |
924 | /* weird but legal */ | |
925 | continue; | |
926 | } | |
927 | CFRef<CFMutableDictionaryRef> outTsDict(CFDictionaryCreateMutable(NULL, | |
928 | 0, // capacity | |
5c19dc3a | 929 | &kCFTypeDictionaryKeyCallBacks, |
b1ab9ed8 | 930 | &kCFTypeDictionaryValueCallBacks)); |
5c19dc3a | 931 | |
b1ab9ed8 | 932 | if(certPolicy != NULL) { |
b1ab9ed8 | 933 | SecPolicyRef policyRef = NULL; |
fa7225c8 A |
934 | if (CFDataGetTypeID() == CFGetTypeID(certPolicy)) { |
935 | /* convert OID as CFDataRef to SecPolicyRef */ | |
6b200bc3 | 936 | CSSM_OID policyOid = { int_cast<CFIndex, CSSM_SIZE>(CFDataGetLength((CFDataRef)certPolicy)), |
fa7225c8 A |
937 | (uint8 *)CFDataGetBytePtr((CFDataRef)certPolicy) }; |
938 | OSStatus ortn = SecPolicyCopy(CSSM_CERT_X_509v3, &policyOid, &policyRef); | |
939 | if(ortn) { | |
940 | trustSettingsDbg("copyTrustSettings: OID conversion error"); | |
941 | abort("Bad Policy OID in trusted root list", errSecInvalidTrustedRootRecord); | |
942 | } | |
943 | } else if (CFStringGetTypeID() == CFGetTypeID(certPolicy)) { | |
944 | policyRef = SecPolicyCreateWithProperties(certPolicy, NULL); | |
b1ab9ed8 | 945 | } |
fa7225c8 A |
946 | if (policyRef) { |
947 | CFDictionaryAddValue(outTsDict, kSecTrustSettingsPolicy, policyRef); | |
948 | CFRelease(policyRef); // owned by dictionary | |
949 | } | |
950 | } | |
951 | ||
952 | if (policyName != NULL) { | |
953 | /* | |
954 | * copy, since policyName is in our mutable dictionary and could change out from | |
955 | * under the caller | |
956 | */ | |
957 | CFStringRef str = CFStringCreateCopy(NULL, policyName); | |
958 | CFDictionaryAddValue(outTsDict, kSecTrustSettingsPolicyName, str); | |
959 | CFRelease(str); // owned by dictionary | |
b1ab9ed8 | 960 | } |
5c19dc3a | 961 | |
b1ab9ed8 A |
962 | if(certApp != NULL) { |
963 | /* convert app as CFDataRef to SecTrustedApplicationRef */ | |
964 | SecTrustedApplicationRef appRef; | |
965 | OSStatus ortn = SecTrustedApplicationCreateWithExternalRepresentation(certApp, &appRef); | |
966 | if(ortn) { | |
967 | trustSettingsDbg("copyTrustSettings: App conversion error"); | |
968 | abort("Bad application data in trusted root list", errSecInvalidTrustedRootRecord); | |
969 | } | |
970 | CFDictionaryAddValue(outTsDict, kSecTrustSettingsApplication, appRef); | |
5c19dc3a | 971 | CFRelease(appRef); // owned by dictionary |
b1ab9ed8 | 972 | } |
5c19dc3a | 973 | |
b1ab9ed8 A |
974 | /* remaining 4 are trivial */ |
975 | if(policyStr != NULL) { | |
5c19dc3a | 976 | /* |
b1ab9ed8 A |
977 | * copy, since policyStr is in our mutable dictionary and could change out from |
978 | * under the caller | |
979 | */ | |
980 | CFStringRef str = CFStringCreateCopy(NULL, policyStr); | |
981 | CFDictionaryAddValue(outTsDict, kSecTrustSettingsPolicyString, str); | |
5c19dc3a | 982 | CFRelease(str); // owned by dictionary |
b1ab9ed8 A |
983 | } |
984 | if(allowedErr != NULL) { | |
985 | /* there is no mutable CFNumber, so.... */ | |
986 | CFDictionaryAddValue(outTsDict, kSecTrustSettingsAllowedError, allowedErr); | |
987 | } | |
988 | if(resultType != NULL) { | |
989 | CFDictionaryAddValue(outTsDict, kSecTrustSettingsResult, resultType); | |
990 | } | |
991 | if(keyUsage != NULL) { | |
992 | CFDictionaryAddValue(outTsDict, kSecTrustSettingsKeyUsage, keyUsage); | |
993 | } | |
994 | CFArrayAppendValue(outArray, outTsDict); | |
995 | /* outTsDict autoreleases; owned by outArray now */ | |
996 | } | |
997 | CFRetain(outArray); // now that it's good to go.... | |
998 | return outArray; | |
5c19dc3a | 999 | } |
b1ab9ed8 A |
1000 | |
1001 | CFDateRef TrustSettings::copyModDate( | |
1002 | SecCertificateRef certRef) | |
1003 | { | |
1004 | CFDictionaryRef certDict = NULL; | |
5c19dc3a | 1005 | |
b1ab9ed8 A |
1006 | /* find the on-disk usage constraints dictionary for this cert */ |
1007 | certDict = findDictionaryForCert(certRef); | |
1008 | if(certDict == NULL) { | |
1009 | trustSettingsDbg("copyModDate: dictionary not found"); | |
1010 | return NULL; | |
1011 | } | |
1012 | CFDateRef modDate = (CFDateRef)CFDictionaryGetValue(certDict, kTrustRecordModDate); | |
1013 | if(modDate == NULL) { | |
1014 | return NULL; | |
1015 | } | |
1016 | ||
1017 | /* this only works becuase there is no mutable CFDateRef */ | |
1018 | CFRetain(modDate); | |
1019 | return modDate; | |
1020 | } | |
1021 | ||
1022 | /* | |
5c19dc3a | 1023 | * Modify cert's trust settings, or add a new cert to the record. |
b1ab9ed8 A |
1024 | */ |
1025 | void TrustSettings::setTrustSettings( | |
1026 | SecCertificateRef certRef, | |
1027 | CFTypeRef trustSettingsDictOrArray) | |
1028 | { | |
1029 | /* to validate, we need to know if the cert is self-signed */ | |
1030 | OSStatus ortn; | |
1031 | Boolean isSelfSigned = false; | |
1032 | ||
1033 | if(certRef == kSecTrustSettingsDefaultRootCertSetting) { | |
5c19dc3a A |
1034 | /* |
1035 | * Validate settings as if this were root, specifically, | |
b1ab9ed8 A |
1036 | * kSecTrustSettingsResultTrustRoot (explicitly or by |
1037 | * default) is OK. | |
1038 | */ | |
1039 | isSelfSigned = true; | |
1040 | } | |
1041 | else { | |
1042 | ortn = SecCertificateIsSelfSigned(certRef, &isSelfSigned); | |
1043 | if(ortn) { | |
1044 | MacOSError::throwMe(ortn); | |
1045 | } | |
1046 | } | |
1047 | ||
1048 | /* caller's app/policy spec OK? */ | |
1049 | CFRef<CFArrayRef> trustSettings(validateApiTrustSettings( | |
1050 | trustSettingsDictOrArray, isSelfSigned)); | |
5c19dc3a | 1051 | |
b1ab9ed8 A |
1052 | /* caller is responsible for ensuring these */ |
1053 | assert(mPropList != NULL); | |
1054 | assert(mDomain != kSecTrustSettingsDomainSystem); | |
1055 | ||
1056 | /* extract issuer and serial number from the cert, if it's a cert */ | |
1057 | CFRef<CFDataRef> issuer; | |
1058 | CFRef<CFDataRef> serial; | |
1059 | if(certRef != kSecTrustSettingsDefaultRootCertSetting) { | |
1060 | copyIssuerAndSerial(certRef, issuer.take(), serial.take()); | |
1061 | } | |
1062 | else { | |
1063 | UInt8 dummy; | |
1064 | issuer = CFDataCreate(NULL, &dummy, 0); | |
1065 | serial = CFDataCreate(NULL, &dummy, 0); | |
1066 | } | |
5c19dc3a | 1067 | |
b1ab9ed8 A |
1068 | /* SHA1 digest as string */ |
1069 | CFRef<CFStringRef> certHashStr(SecTrustSettingsCertHashStrFromCert(certRef)); | |
1070 | if(!certHashStr) { | |
1071 | trustSettingsDbg("TrustSettings::setTrustSettings: CertHashStrFromCert error"); | |
1072 | MacOSError::throwMe(errSecItemNotFound); | |
1073 | } | |
1074 | ||
5c19dc3a | 1075 | /* |
b1ab9ed8 A |
1076 | * Find entry for this cert, if present. |
1077 | */ | |
5c19dc3a | 1078 | CFMutableDictionaryRef certDict = |
b1ab9ed8 A |
1079 | (CFMutableDictionaryRef)findDictionaryForCertHash(certHashStr); |
1080 | if(certDict == NULL) { | |
1081 | /* create new dictionary */ | |
1082 | certDict = CFDictionaryCreateMutable(NULL, kSecTrustRecordNumCertDictKeys, | |
1083 | &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
1084 | if(certDict == NULL) { | |
427c49bc | 1085 | MacOSError::throwMe(errSecAllocate); |
b1ab9ed8 A |
1086 | } |
1087 | CFDictionaryAddValue(certDict, kTrustRecordIssuer, issuer); | |
1088 | CFDictionaryAddValue(certDict, kTrustRecordSerialNumber, serial); | |
1089 | if(CFArrayGetCount(trustSettings) != 0) { | |
1090 | /* skip this if the settings array is empty */ | |
1091 | CFDictionaryAddValue(certDict, kTrustRecordTrustSettings, trustSettings); | |
1092 | } | |
1093 | tsSetModDate(certDict); | |
5c19dc3a | 1094 | |
b1ab9ed8 A |
1095 | /* add this new cert dictionary to top-level mTrustDict */ |
1096 | CFDictionaryAddValue(mTrustDict, static_cast<CFStringRef>(certHashStr), certDict); | |
5c19dc3a | 1097 | |
b1ab9ed8 A |
1098 | /* mTrustDict owns the dictionary now */ |
1099 | CFRelease(certDict); | |
1100 | } | |
1101 | else { | |
1102 | /* update */ | |
1103 | tsSetModDate(certDict); | |
1104 | if(CFArrayGetCount(trustSettings) != 0) { | |
1105 | CFDictionarySetValue(certDict, kTrustRecordTrustSettings, trustSettings); | |
1106 | } | |
1107 | else { | |
1108 | /* empty settings array: remove from dictionary */ | |
1109 | CFDictionaryRemoveValue(certDict, kTrustRecordTrustSettings); | |
1110 | } | |
1111 | } | |
1112 | mDirty = true; | |
1113 | } | |
5c19dc3a | 1114 | |
b1ab9ed8 | 1115 | /* |
5c19dc3a | 1116 | * Delete a certificate's trust settings. |
b1ab9ed8 A |
1117 | */ |
1118 | void TrustSettings::deleteTrustSettings( | |
1119 | SecCertificateRef certRef) | |
1120 | { | |
1121 | CFDictionaryRef certDict = NULL; | |
5c19dc3a | 1122 | |
b1ab9ed8 A |
1123 | /* caller is responsible for ensuring these */ |
1124 | assert(mPropList != NULL); | |
1125 | assert(mDomain != kSecTrustSettingsDomainSystem); | |
1126 | ||
1127 | /* SHA1 digest as string */ | |
1128 | CFRef<CFStringRef> certHashStr(SecTrustSettingsCertHashStrFromCert(certRef)); | |
1129 | if(!certHashStr) { | |
1130 | MacOSError::throwMe(errSecItemNotFound); | |
1131 | } | |
1132 | ||
1133 | /* present in top-level mTrustDict? */ | |
1134 | certDict = findDictionaryForCertHash(certHashStr); | |
1135 | if(certDict != NULL) { | |
1136 | CFDictionaryRemoveValue(mTrustDict, static_cast<CFStringRef>(certHashStr)); | |
1137 | mDirty = true; | |
1138 | } | |
1139 | else { | |
5c19dc3a A |
1140 | /* |
1141 | * Throwing this error is the only reason we don't blindly do | |
1142 | * a CFDictionaryRemoveValue() without first doing | |
b1ab9ed8 A |
1143 | * findDictionaryForCertHash(). |
1144 | */ | |
1145 | trustSettingsDbg("TrustSettings::deleteRoot: cert dictionary not found"); | |
1146 | MacOSError::throwMe(errSecItemNotFound); | |
1147 | } | |
1148 | } | |
1149 | ||
1150 | #pragma mark --- Private methods --- | |
1151 | ||
1152 | /* | |
5c19dc3a A |
1153 | * Find a given cert's entry in the top-level mTrustDict. Return the |
1154 | * entry as a dictionary. Returned dictionary is not refcounted. | |
1155 | * The mutability of the returned dictionary is the same as the mutability | |
1156 | * of the underlying StickRecord::mPropList, which the caller is just | |
1157 | * going to have to know (and cast accordingly if a mutable dictionary | |
b1ab9ed8 | 1158 | * is needed). |
5c19dc3a | 1159 | */ |
b1ab9ed8 A |
1160 | CFDictionaryRef TrustSettings::findDictionaryForCert( |
1161 | SecCertificateRef certRef) | |
1162 | { | |
1163 | CFRef<CFStringRef> certHashStr(SecTrustSettingsCertHashStrFromCert(certRef)); | |
1164 | if (certHashStr.get() == NULL) | |
1165 | { | |
1166 | return NULL; | |
1167 | } | |
5c19dc3a | 1168 | |
fa7225c8 | 1169 | return findDictionaryForCertHash(static_cast<CFStringRef>(certHashStr.get())); |
b1ab9ed8 A |
1170 | } |
1171 | ||
1172 | /* | |
5c19dc3a | 1173 | * Find entry in mTrustDict given cert hash string. |
b1ab9ed8 A |
1174 | */ |
1175 | CFDictionaryRef TrustSettings::findDictionaryForCertHash( | |
1176 | CFStringRef certHashStr) | |
1177 | { | |
1178 | assert(mTrustDict != NULL); | |
1179 | return (CFDictionaryRef)CFDictionaryGetValue(mTrustDict, certHashStr); | |
1180 | } | |
1181 | ||
1182 | /* | |
5c19dc3a A |
1183 | * Validate incoming trust settings, which may be NULL, a dictionary, or |
1184 | * an array of dictionaries. Convert from the API-style dictionaries | |
1185 | * to the internal style suitable for writing to disk as part of | |
b1ab9ed8 A |
1186 | * mPropList. |
1187 | * | |
1188 | * We return a refcounted CFArray in any case if the incoming parameter is good. | |
1189 | */ | |
1190 | CFArrayRef TrustSettings::validateApiTrustSettings( | |
1191 | CFTypeRef trustSettingsDictOrArray, | |
1192 | Boolean isSelfSigned) | |
1193 | { | |
1194 | CFArrayRef tmpInArray = NULL; | |
1195 | ||
1196 | if(trustSettingsDictOrArray == NULL) { | |
1197 | /* trivial case, only valid for roots */ | |
1198 | if(!isSelfSigned) { | |
1199 | trustSettingsDbg("validateApiUsageConstraints: !isSelfSigned, no settings"); | |
427c49bc | 1200 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
1201 | } |
1202 | return CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); | |
1203 | } | |
1204 | else if(CFGetTypeID(trustSettingsDictOrArray) == CFDictionaryGetTypeID()) { | |
1205 | /* array-ize it */ | |
1206 | tmpInArray = CFArrayCreate(NULL, &trustSettingsDictOrArray, 1, | |
1207 | &kCFTypeArrayCallBacks); | |
1208 | } | |
1209 | else if(CFGetTypeID(trustSettingsDictOrArray) == CFArrayGetTypeID()) { | |
1210 | /* as is, refcount - we'll release later */ | |
1211 | tmpInArray = (CFArrayRef)trustSettingsDictOrArray; | |
1212 | CFRetain(tmpInArray); | |
1213 | } | |
1214 | else { | |
1215 | trustSettingsDbg("validateApiUsageConstraints: bad trustSettingsDictOrArray"); | |
427c49bc | 1216 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 | 1217 | } |
5c19dc3a | 1218 | |
b1ab9ed8 A |
1219 | CFIndex numSpecs = CFArrayGetCount(tmpInArray); |
1220 | CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, numSpecs, &kCFTypeArrayCallBacks); | |
1221 | CSSM_OID oid; | |
427c49bc | 1222 | OSStatus ortn = errSecSuccess; |
b1ab9ed8 A |
1223 | SecPolicyRef certPolicy; |
1224 | SecTrustedApplicationRef certApp; | |
5c19dc3a | 1225 | |
b1ab9ed8 A |
1226 | /* convert */ |
1227 | for(CFIndex dex=0; dex<numSpecs; dex++) { | |
fa7225c8 A |
1228 | CFTypeRef oidData = NULL; |
1229 | CFStringRef policyName = NULL; | |
b1ab9ed8 A |
1230 | CFDataRef appData = NULL; |
1231 | CFStringRef policyStr = NULL; | |
1232 | CFNumberRef allowedErr = NULL; | |
1233 | CFNumberRef resultType = NULL; | |
1234 | CFNumberRef keyUsage = NULL; | |
1235 | SInt32 resultNum; | |
1236 | SecTrustSettingsResult result; | |
1237 | ||
1238 | /* each element is a dictionary */ | |
1239 | CFDictionaryRef ucDict = (CFDictionaryRef)CFArrayGetValueAtIndex(tmpInArray, dex); | |
1240 | if(CFGetTypeID(ucDict) != CFDictionaryGetTypeID()) { | |
1241 | trustSettingsDbg("validateAppPolicyArray: malformed usageConstraint dictionary"); | |
427c49bc | 1242 | ortn = errSecParam; |
b1ab9ed8 A |
1243 | break; |
1244 | } | |
5c19dc3a | 1245 | |
b1ab9ed8 A |
1246 | /* policy - optional */ |
1247 | certPolicy = (SecPolicyRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicy); | |
1248 | if(certPolicy != NULL) { | |
1249 | if(CFGetTypeID(certPolicy) != SecPolicyGetTypeID()) { | |
1250 | trustSettingsDbg("validateAppPolicyArray: malformed certPolicy"); | |
427c49bc | 1251 | ortn = errSecParam; |
b1ab9ed8 A |
1252 | break; |
1253 | } | |
1254 | ortn = SecPolicyGetOID(certPolicy, &oid); | |
fa7225c8 A |
1255 | if (ortn) { |
1256 | /* newer policies don't have CSSM OIDs but they do have string OIDs */ | |
1257 | oidData = CFRetain(SecPolicyGetOidString(certPolicy)); | |
1258 | } else { | |
1259 | oidData = CFDataCreate(NULL, oid.Data, oid.Length); | |
1260 | } | |
1261 | ||
1262 | if (!oidData) { | |
b1ab9ed8 A |
1263 | trustSettingsDbg("validateAppPolicyArray: SecPolicyGetOID error"); |
1264 | break; | |
1265 | } | |
fa7225c8 | 1266 | policyName = SecPolicyGetName(certPolicy); |
b1ab9ed8 A |
1267 | } |
1268 | ||
1269 | /* application - optional */ | |
1270 | certApp = (SecTrustedApplicationRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsApplication); | |
1271 | if(certApp != NULL) { | |
1272 | if(CFGetTypeID(certApp) != SecTrustedApplicationGetTypeID()) { | |
1273 | trustSettingsDbg("validateAppPolicyArray: malformed certApp"); | |
427c49bc | 1274 | ortn = errSecParam; |
b1ab9ed8 A |
1275 | break; |
1276 | } | |
1277 | ortn = SecTrustedApplicationCopyExternalRepresentation(certApp, &appData); | |
1278 | if(ortn) { | |
1279 | trustSettingsDbg("validateAppPolicyArray: " | |
1280 | "SecTrustedApplicationCopyExternalRepresentation error"); | |
1281 | break; | |
1282 | } | |
1283 | } | |
1284 | ||
1285 | policyStr = (CFStringRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicyString); | |
1286 | if(policyStr != NULL) { | |
1287 | if(CFGetTypeID(policyStr) != CFStringGetTypeID()) { | |
1288 | trustSettingsDbg("validateAppPolicyArray: malformed policyStr"); | |
427c49bc | 1289 | ortn = errSecParam; |
b1ab9ed8 A |
1290 | break; |
1291 | } | |
1292 | } | |
1293 | allowedErr = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsAllowedError); | |
1294 | if(!tsIsGoodCfNum(allowedErr)) { | |
1295 | trustSettingsDbg("validateAppPolicyArray: malformed allowedErr"); | |
427c49bc | 1296 | ortn = errSecParam; |
b1ab9ed8 A |
1297 | break; |
1298 | } | |
1299 | resultType = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsResult); | |
1300 | if(!tsIsGoodCfNum(resultType, &resultNum)) { | |
1301 | trustSettingsDbg("validateAppPolicyArray: malformed resultType"); | |
427c49bc | 1302 | ortn = errSecParam; |
b1ab9ed8 A |
1303 | break; |
1304 | } | |
6b200bc3 | 1305 | result = (SecTrustSettingsResult) resultNum; |
b1ab9ed8 A |
1306 | /* validate result later */ |
1307 | ||
1308 | keyUsage = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsKeyUsage); | |
1309 | if(!tsIsGoodCfNum(keyUsage)) { | |
1310 | trustSettingsDbg("validateAppPolicyArray: malformed keyUsage"); | |
427c49bc | 1311 | ortn = errSecParam; |
b1ab9ed8 A |
1312 | break; |
1313 | } | |
5c19dc3a A |
1314 | |
1315 | if(!oidData && !appData && !policyStr && | |
b1ab9ed8 A |
1316 | !allowedErr && !resultType && !keyUsage) { |
1317 | /* nothing here - weird, but legal - skip it */ | |
1318 | continue; | |
1319 | } | |
5c19dc3a | 1320 | |
b1ab9ed8 | 1321 | /* create dictionary for this usageConstraint */ |
5c19dc3a A |
1322 | CFMutableDictionaryRef outDict = CFDictionaryCreateMutable(NULL, |
1323 | 2, | |
1324 | &kCFTypeDictionaryKeyCallBacks, | |
b1ab9ed8 A |
1325 | &kCFTypeDictionaryValueCallBacks); |
1326 | if(oidData) { | |
1327 | CFDictionaryAddValue(outDict, kSecTrustSettingsPolicy, oidData); | |
5c19dc3a | 1328 | CFRelease(oidData); // owned by dictionary |
b1ab9ed8 | 1329 | } |
fa7225c8 A |
1330 | if(policyName) { |
1331 | CFDictionaryAddValue(outDict, kSecTrustSettingsPolicyName, policyName); | |
1332 | /* still owned by ucDict */ | |
1333 | } | |
b1ab9ed8 A |
1334 | if(appData) { |
1335 | CFDictionaryAddValue(outDict, kSecTrustSettingsApplication, appData); | |
5c19dc3a | 1336 | CFRelease(appData); // owned by dictionary |
b1ab9ed8 A |
1337 | } |
1338 | if(policyStr) { | |
1339 | CFDictionaryAddValue(outDict, kSecTrustSettingsPolicyString, policyStr); | |
1340 | /* still owned by ucDict */ | |
1341 | } | |
1342 | if(allowedErr) { | |
1343 | CFDictionaryAddValue(outDict, kSecTrustSettingsAllowedError, allowedErr); | |
1344 | } | |
fa7225c8 | 1345 | |
5c19dc3a | 1346 | ortn = errSecSuccess; |
fa7225c8 | 1347 | |
b1ab9ed8 A |
1348 | if(resultType) { |
1349 | /* let's be really picky on this one */ | |
1350 | switch(result) { | |
1351 | case kSecTrustSettingsResultInvalid: | |
427c49bc | 1352 | ortn = errSecParam; |
b1ab9ed8 A |
1353 | break; |
1354 | case kSecTrustSettingsResultTrustRoot: | |
1355 | if(!isSelfSigned) { | |
1356 | trustSettingsDbg("validateAppPolicyArray: TrustRoot, !isSelfSigned"); | |
427c49bc | 1357 | ortn = errSecParam; |
b1ab9ed8 A |
1358 | } |
1359 | break; | |
1360 | case kSecTrustSettingsResultTrustAsRoot: | |
1361 | if(isSelfSigned) { | |
1362 | trustSettingsDbg("validateAppPolicyArray: TrustAsRoot, isSelfSigned"); | |
427c49bc | 1363 | ortn = errSecParam; |
b1ab9ed8 A |
1364 | } |
1365 | break; | |
1366 | case kSecTrustSettingsResultDeny: | |
1367 | case kSecTrustSettingsResultUnspecified: | |
1368 | break; | |
1369 | default: | |
1370 | trustSettingsDbg("validateAppPolicyArray: bogus resultType"); | |
427c49bc | 1371 | ortn = errSecParam; |
b1ab9ed8 A |
1372 | break; |
1373 | } | |
1374 | if(ortn) { | |
1375 | break; | |
1376 | } | |
1377 | CFDictionaryAddValue(outDict, kSecTrustSettingsResult, resultType); | |
1378 | } | |
1379 | else { | |
1380 | /* no resultType; default of TrustRoot only valid for root */ | |
1381 | if(!isSelfSigned) { | |
1382 | trustSettingsDbg("validateAppPolicyArray: default result, !isSelfSigned"); | |
427c49bc | 1383 | ortn = errSecParam; |
b1ab9ed8 A |
1384 | break; |
1385 | } | |
1386 | } | |
fa7225c8 | 1387 | |
b1ab9ed8 A |
1388 | if(keyUsage) { |
1389 | CFDictionaryAddValue(outDict, kSecTrustSettingsKeyUsage, keyUsage); | |
1390 | } | |
5c19dc3a | 1391 | |
b1ab9ed8 A |
1392 | /* append dictionary to output */ |
1393 | CFArrayAppendValue(outArray, outDict); | |
1394 | /* array owns the dictionary now */ | |
1395 | CFRelease(outDict); | |
5c19dc3a | 1396 | |
b1ab9ed8 | 1397 | } /* for each usage constraint dictionary */ |
5c19dc3a | 1398 | |
b1ab9ed8 A |
1399 | CFRelease(tmpInArray); |
1400 | if(ortn) { | |
1401 | CFRelease(outArray); | |
1402 | MacOSError::throwMe(ortn); | |
1403 | } | |
1404 | return outArray; | |
1405 | } | |
1406 | ||
5c19dc3a | 1407 | /* |
b1ab9ed8 | 1408 | * Validate an trust settings array obtained from disk. |
5c19dc3a | 1409 | * Returns true if OK, else returns false. |
b1ab9ed8 A |
1410 | */ |
1411 | bool TrustSettings::validateTrustSettingsArray( | |
1412 | CFArrayRef trustSettings) | |
1413 | { | |
1414 | CFIndex numSpecs = CFArrayGetCount(trustSettings); | |
1415 | for(CFIndex dex=0; dex<numSpecs; dex++) { | |
5c19dc3a | 1416 | CFDictionaryRef ucDict = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, |
b1ab9ed8 A |
1417 | dex); |
1418 | if(CFGetTypeID(ucDict) != CFDictionaryGetTypeID()) { | |
1419 | trustSettingsDbg("validateAppPolicyArray: malformed app/policy dictionary"); | |
1420 | return false; | |
1421 | } | |
1422 | CFDataRef certPolicy = (CFDataRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicy); | |
1423 | if((certPolicy != NULL) && (CFGetTypeID(certPolicy) != CFDataGetTypeID())) { | |
1424 | trustSettingsDbg("validateAppPolicyArray: malformed certPolicy"); | |
1425 | return false; | |
1426 | } | |
1427 | CFDataRef certApp = (CFDataRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsApplication); | |
1428 | if((certApp != NULL) && (CFGetTypeID(certApp) != CFDataGetTypeID())) { | |
1429 | trustSettingsDbg("validateAppPolicyArray: malformed certApp"); | |
1430 | return false; | |
1431 | } | |
1432 | CFStringRef policyStr = (CFStringRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicyString); | |
1433 | if((policyStr != NULL) && (CFGetTypeID(policyStr) != CFStringGetTypeID())) { | |
1434 | trustSettingsDbg("validateAppPolicyArray: malformed policyStr"); | |
1435 | return false; | |
1436 | } | |
1437 | CFNumberRef cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsAllowedError); | |
1438 | if(!tsIsGoodCfNum(cfNum)) { | |
1439 | trustSettingsDbg("validateAppPolicyArray: malformed allowedErr"); | |
1440 | return false; | |
1441 | } | |
1442 | cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsResult); | |
1443 | if(!tsIsGoodCfNum(cfNum)) { | |
1444 | trustSettingsDbg("validateAppPolicyArray: malformed resultType"); | |
1445 | return false; | |
1446 | } | |
1447 | cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsKeyUsage); | |
1448 | if(!tsIsGoodCfNum(cfNum)) { | |
1449 | trustSettingsDbg("validateAppPolicyArray: malformed keyUsage"); | |
1450 | return false; | |
1451 | } | |
1452 | } /* for each usageConstraint dictionary */ | |
1453 | return true; | |
1454 | } | |
1455 | ||
1456 | /* | |
5c19dc3a A |
1457 | * Validate mPropList after it's read from disk or supplied as an external |
1458 | * representation. Allows subsequent use of mTrustDict to proceed with | |
1459 | * relative impunity. | |
b1ab9ed8 A |
1460 | */ |
1461 | void TrustSettings::validatePropList(bool trim) | |
1462 | { | |
1463 | /* top level dictionary */ | |
1464 | if(!mPropList) { | |
1465 | trustSettingsDbg("TrustSettings::validatePropList missing mPropList"); | |
1466 | abort("missing propList", errSecInvalidTrustedRootRecord); | |
1467 | } | |
1468 | ||
1469 | if(CFGetTypeID(mPropList) != CFDictionaryGetTypeID()) { | |
1470 | trustSettingsDbg("TrustSettings::validatePropList: malformed mPropList"); | |
1471 | abort("malformed propList", errSecInvalidTrustedRootRecord); | |
1472 | } | |
1473 | ||
1474 | /* That dictionary has two entries */ | |
1475 | CFNumberRef cfVers = (CFNumberRef)CFDictionaryGetValue(mPropList, kTrustRecordVersion); | |
1476 | if((cfVers == NULL) || (CFGetTypeID(cfVers) != CFNumberGetTypeID())) { | |
1477 | trustSettingsDbg("TrustSettings::validatePropList: malformed version"); | |
1478 | abort("malformed version", errSecInvalidTrustedRootRecord); | |
1479 | } | |
1480 | if(!CFNumberGetValue(cfVers, kCFNumberSInt32Type, &mDictVersion)) { | |
1481 | trustSettingsDbg("TrustSettings::validatePropList: malformed version"); | |
1482 | abort("malformed version", errSecInvalidTrustedRootRecord); | |
1483 | } | |
1484 | if((mDictVersion > kSecTrustRecordVersionCurrent) || | |
1485 | (mDictVersion == kSecTrustRecordVersionInvalid)) { | |
1486 | trustSettingsDbg("TrustSettings::validatePropList: incompatible version"); | |
1487 | abort("incompatible version", errSecInvalidTrustedRootRecord); | |
1488 | } | |
1489 | /* other backwards-compatibility handling done later, if needed, per mDictVersion */ | |
5c19dc3a | 1490 | |
b1ab9ed8 A |
1491 | mTrustDict = (CFMutableDictionaryRef)CFDictionaryGetValue(mPropList, kTrustRecordTrustList); |
1492 | if(mTrustDict != NULL) { | |
1493 | CFRetain(mTrustDict); | |
1494 | } | |
1495 | if((mTrustDict == NULL) || (CFGetTypeID(mTrustDict) != CFDictionaryGetTypeID())) { | |
1496 | trustSettingsDbg("TrustSettings::validatePropList: malformed mTrustDict"); | |
1497 | abort("malformed TrustArray", errSecInvalidTrustedRootRecord); | |
1498 | } | |
5c19dc3a | 1499 | |
b1ab9ed8 A |
1500 | /* grind through the per-cert entries */ |
1501 | CFIndex numCerts = CFDictionaryGetCount(mTrustDict); | |
1502 | const void *dictKeys[numCerts]; | |
1503 | const void *dictValues[numCerts]; | |
1504 | CFDictionaryGetKeysAndValues(mTrustDict, dictKeys, dictValues); | |
1505 | ||
1506 | for(CFIndex dex=0; dex<numCerts; dex++) { | |
1507 | /* get per-cert dictionary */ | |
1508 | CFMutableDictionaryRef certDict = (CFMutableDictionaryRef)dictValues[dex]; | |
1509 | if((certDict == NULL) || (CFGetTypeID(certDict) != CFDictionaryGetTypeID())) { | |
1510 | trustSettingsDbg("TrustSettings::validatePropList: malformed certDict"); | |
1511 | abort("malformed certDict", errSecInvalidTrustedRootRecord); | |
1512 | } | |
5c19dc3a A |
1513 | |
1514 | /* | |
b1ab9ed8 A |
1515 | * That dictionary has exactly four entries. |
1516 | * If we're trimming, all we need is the actual trust settings. | |
1517 | */ | |
1518 | ||
1519 | /* issuer */ | |
1520 | CFDataRef cfd = (CFDataRef)CFDictionaryGetValue(certDict, kTrustRecordIssuer); | |
1521 | if(cfd == NULL) { | |
1522 | trustSettingsDbg("TrustSettings::validatePropList: missing issuer"); | |
1523 | abort("missing issuer", errSecInvalidTrustedRootRecord); | |
1524 | } | |
1525 | if(CFGetTypeID(cfd) != CFDataGetTypeID()) { | |
1526 | trustSettingsDbg("TrustSettings::validatePropList: malformed issuer"); | |
1527 | abort("malformed issuer", errSecInvalidTrustedRootRecord); | |
1528 | } | |
1529 | if(trim) { | |
1530 | CFDictionaryRemoveValue(certDict, kTrustRecordIssuer); | |
1531 | } | |
1532 | ||
1533 | /* serial number */ | |
1534 | cfd = (CFDataRef)CFDictionaryGetValue(certDict, kTrustRecordSerialNumber); | |
1535 | if(cfd == NULL) { | |
1536 | trustSettingsDbg("TrustSettings::validatePropList: missing serial number"); | |
1537 | abort("missing serial number", errSecInvalidTrustedRootRecord); | |
1538 | } | |
1539 | if(CFGetTypeID(cfd) != CFDataGetTypeID()) { | |
1540 | trustSettingsDbg("TrustSettings::validatePropList: malformed serial number"); | |
1541 | abort("malformed serial number", errSecInvalidTrustedRootRecord); | |
1542 | } | |
1543 | if(trim) { | |
1544 | CFDictionaryRemoveValue(certDict, kTrustRecordSerialNumber); | |
1545 | } | |
5c19dc3a | 1546 | |
b1ab9ed8 A |
1547 | /* modification date */ |
1548 | CFDateRef modDate = (CFDateRef)CFDictionaryGetValue(certDict, kTrustRecordModDate); | |
1549 | if(modDate == NULL) { | |
1550 | trustSettingsDbg("TrustSettings::validatePropList: missing modDate"); | |
1551 | abort("missing modDate", errSecInvalidTrustedRootRecord); | |
1552 | } | |
1553 | if(CFGetTypeID(modDate) != CFDateGetTypeID()) { | |
1554 | trustSettingsDbg("TrustSettings::validatePropList: malformed modDate"); | |
1555 | abort("malformed modDate", errSecInvalidTrustedRootRecord); | |
1556 | } | |
1557 | if(trim) { | |
1558 | CFDictionaryRemoveValue(certDict, kTrustRecordModDate); | |
1559 | } | |
5c19dc3a | 1560 | |
b1ab9ed8 A |
1561 | /* the actual trust settings */ |
1562 | CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict, | |
1563 | kTrustRecordTrustSettings); | |
1564 | if(trustSettings == NULL) { | |
1565 | /* optional; this cert's entry is good */ | |
1566 | continue; | |
1567 | } | |
1568 | if(CFGetTypeID(trustSettings) != CFArrayGetTypeID()) { | |
1569 | trustSettingsDbg("TrustSettings::validatePropList: malformed useConstraint" | |
1570 | "array"); | |
1571 | abort("malformed useConstraint array", errSecInvalidTrustedRootRecord); | |
1572 | } | |
5c19dc3a | 1573 | |
b1ab9ed8 A |
1574 | /* Now validate the usageConstraint array contents */ |
1575 | if(!validateTrustSettingsArray(trustSettings)) { | |
1576 | abort("malformed useConstraint array", errSecInvalidTrustedRootRecord); | |
1577 | } | |
1578 | } /* for each cert dictionary in top-level array */ | |
1579 | ||
1580 | if(trim) { | |
1581 | /* we don't need the top-level dictionary any more */ | |
1582 | CFRelease(mPropList); | |
1583 | mPropList = NULL; | |
1584 | } | |
1585 | } | |
1586 | ||
5c19dc3a A |
1587 | /* |
1588 | * Obtain non-normalized issuer and serial number for specified cert, both | |
1589 | * returned as CFDataRefs owned by caller. | |
b1ab9ed8 A |
1590 | */ |
1591 | void TrustSettings::copyIssuerAndSerial( | |
1592 | SecCertificateRef certRef, | |
1593 | CFDataRef *issuer, /* optional, RETURNED */ | |
1594 | CFDataRef *serial) /* RETURNED */ | |
1595 | { | |
5c19dc3a | 1596 | CFRef<SecCertificateRef> certificate = SecCertificateCreateItemImplInstance(certRef); |
5c19dc3a A |
1597 | |
1598 | SecPointer<Certificate> cert = Certificate::required(certificate); | |
b1ab9ed8 | 1599 | CSSM_DATA_PTR fieldVal; |
5c19dc3a A |
1600 | |
1601 | if(issuer != NULL) { | |
b1ab9ed8 A |
1602 | fieldVal = cert->copyFirstFieldValue(CSSMOID_X509V1IssuerNameStd); |
1603 | *issuer = CFDataCreate(NULL, fieldVal->Data, fieldVal->Length); | |
1604 | cert->releaseFieldValue(CSSMOID_X509V1IssuerNameStd, fieldVal); | |
1605 | } | |
5c19dc3a | 1606 | |
b1ab9ed8 A |
1607 | fieldVal = cert->copyFirstFieldValue(CSSMOID_X509V1SerialNumber); |
1608 | *serial = CFDataCreate(NULL, fieldVal->Data, fieldVal->Length); | |
1609 | cert->releaseFieldValue(CSSMOID_X509V1SerialNumber, fieldVal); | |
1610 | } | |
1611 | ||
1612 | void TrustSettings::abort( | |
1613 | const char *why, | |
1614 | OSStatus err) | |
1615 | { | |
1616 | Syslog::error("TrustSettings: %s", why); | |
1617 | MacOSError::throwMe(err); | |
1618 | } | |
1619 |