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