]>
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" | |
b54c578e | 31 | #include <Security/SecTrustSettings.h> |
b1ab9ed8 A |
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> | |
d64be36e | 53 | #include <security_utilities/simulatecrash_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 | ||
d64be36e A |
1022 | bool TrustSettings::contains(SecCertificateRef certRef) |
1023 | { | |
1024 | if(findDictionaryForCert(certRef) != NULL) { | |
1025 | return true; | |
1026 | } | |
1027 | return false; | |
1028 | } | |
1029 | ||
b1ab9ed8 | 1030 | /* |
5c19dc3a | 1031 | * Modify cert's trust settings, or add a new cert to the record. |
b1ab9ed8 A |
1032 | */ |
1033 | void TrustSettings::setTrustSettings( | |
1034 | SecCertificateRef certRef, | |
1035 | CFTypeRef trustSettingsDictOrArray) | |
1036 | { | |
1037 | /* to validate, we need to know if the cert is self-signed */ | |
1038 | OSStatus ortn; | |
1039 | Boolean isSelfSigned = false; | |
1040 | ||
1041 | if(certRef == kSecTrustSettingsDefaultRootCertSetting) { | |
5c19dc3a A |
1042 | /* |
1043 | * Validate settings as if this were root, specifically, | |
b1ab9ed8 A |
1044 | * kSecTrustSettingsResultTrustRoot (explicitly or by |
1045 | * default) is OK. | |
1046 | */ | |
1047 | isSelfSigned = true; | |
1048 | } | |
1049 | else { | |
1050 | ortn = SecCertificateIsSelfSigned(certRef, &isSelfSigned); | |
1051 | if(ortn) { | |
1052 | MacOSError::throwMe(ortn); | |
1053 | } | |
1054 | } | |
1055 | ||
1056 | /* caller's app/policy spec OK? */ | |
1057 | CFRef<CFArrayRef> trustSettings(validateApiTrustSettings( | |
1058 | trustSettingsDictOrArray, isSelfSigned)); | |
5c19dc3a | 1059 | |
b1ab9ed8 A |
1060 | /* caller is responsible for ensuring these */ |
1061 | assert(mPropList != NULL); | |
1062 | assert(mDomain != kSecTrustSettingsDomainSystem); | |
1063 | ||
1064 | /* extract issuer and serial number from the cert, if it's a cert */ | |
1065 | CFRef<CFDataRef> issuer; | |
1066 | CFRef<CFDataRef> serial; | |
1067 | if(certRef != kSecTrustSettingsDefaultRootCertSetting) { | |
1068 | copyIssuerAndSerial(certRef, issuer.take(), serial.take()); | |
1069 | } | |
1070 | else { | |
1071 | UInt8 dummy; | |
1072 | issuer = CFDataCreate(NULL, &dummy, 0); | |
1073 | serial = CFDataCreate(NULL, &dummy, 0); | |
1074 | } | |
5c19dc3a | 1075 | |
b1ab9ed8 A |
1076 | /* SHA1 digest as string */ |
1077 | CFRef<CFStringRef> certHashStr(SecTrustSettingsCertHashStrFromCert(certRef)); | |
1078 | if(!certHashStr) { | |
1079 | trustSettingsDbg("TrustSettings::setTrustSettings: CertHashStrFromCert error"); | |
1080 | MacOSError::throwMe(errSecItemNotFound); | |
1081 | } | |
1082 | ||
5c19dc3a | 1083 | /* |
b1ab9ed8 A |
1084 | * Find entry for this cert, if present. |
1085 | */ | |
5c19dc3a | 1086 | CFMutableDictionaryRef certDict = |
b1ab9ed8 A |
1087 | (CFMutableDictionaryRef)findDictionaryForCertHash(certHashStr); |
1088 | if(certDict == NULL) { | |
1089 | /* create new dictionary */ | |
1090 | certDict = CFDictionaryCreateMutable(NULL, kSecTrustRecordNumCertDictKeys, | |
1091 | &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
1092 | if(certDict == NULL) { | |
427c49bc | 1093 | MacOSError::throwMe(errSecAllocate); |
b1ab9ed8 A |
1094 | } |
1095 | CFDictionaryAddValue(certDict, kTrustRecordIssuer, issuer); | |
1096 | CFDictionaryAddValue(certDict, kTrustRecordSerialNumber, serial); | |
1097 | if(CFArrayGetCount(trustSettings) != 0) { | |
1098 | /* skip this if the settings array is empty */ | |
1099 | CFDictionaryAddValue(certDict, kTrustRecordTrustSettings, trustSettings); | |
1100 | } | |
1101 | tsSetModDate(certDict); | |
5c19dc3a | 1102 | |
b1ab9ed8 A |
1103 | /* add this new cert dictionary to top-level mTrustDict */ |
1104 | CFDictionaryAddValue(mTrustDict, static_cast<CFStringRef>(certHashStr), certDict); | |
5c19dc3a | 1105 | |
b1ab9ed8 A |
1106 | /* mTrustDict owns the dictionary now */ |
1107 | CFRelease(certDict); | |
1108 | } | |
1109 | else { | |
1110 | /* update */ | |
1111 | tsSetModDate(certDict); | |
1112 | if(CFArrayGetCount(trustSettings) != 0) { | |
1113 | CFDictionarySetValue(certDict, kTrustRecordTrustSettings, trustSettings); | |
1114 | } | |
1115 | else { | |
1116 | /* empty settings array: remove from dictionary */ | |
1117 | CFDictionaryRemoveValue(certDict, kTrustRecordTrustSettings); | |
1118 | } | |
1119 | } | |
1120 | mDirty = true; | |
1121 | } | |
5c19dc3a | 1122 | |
b1ab9ed8 | 1123 | /* |
5c19dc3a | 1124 | * Delete a certificate's trust settings. |
b1ab9ed8 A |
1125 | */ |
1126 | void TrustSettings::deleteTrustSettings( | |
1127 | SecCertificateRef certRef) | |
1128 | { | |
1129 | CFDictionaryRef certDict = NULL; | |
5c19dc3a | 1130 | |
b1ab9ed8 A |
1131 | /* caller is responsible for ensuring these */ |
1132 | assert(mPropList != NULL); | |
1133 | assert(mDomain != kSecTrustSettingsDomainSystem); | |
1134 | ||
1135 | /* SHA1 digest as string */ | |
1136 | CFRef<CFStringRef> certHashStr(SecTrustSettingsCertHashStrFromCert(certRef)); | |
1137 | if(!certHashStr) { | |
1138 | MacOSError::throwMe(errSecItemNotFound); | |
1139 | } | |
1140 | ||
1141 | /* present in top-level mTrustDict? */ | |
1142 | certDict = findDictionaryForCertHash(certHashStr); | |
1143 | if(certDict != NULL) { | |
1144 | CFDictionaryRemoveValue(mTrustDict, static_cast<CFStringRef>(certHashStr)); | |
1145 | mDirty = true; | |
1146 | } | |
1147 | else { | |
5c19dc3a A |
1148 | /* |
1149 | * Throwing this error is the only reason we don't blindly do | |
1150 | * a CFDictionaryRemoveValue() without first doing | |
b1ab9ed8 A |
1151 | * findDictionaryForCertHash(). |
1152 | */ | |
1153 | trustSettingsDbg("TrustSettings::deleteRoot: cert dictionary not found"); | |
1154 | MacOSError::throwMe(errSecItemNotFound); | |
1155 | } | |
1156 | } | |
1157 | ||
1158 | #pragma mark --- Private methods --- | |
1159 | ||
1160 | /* | |
5c19dc3a A |
1161 | * Find a given cert's entry in the top-level mTrustDict. Return the |
1162 | * entry as a dictionary. Returned dictionary is not refcounted. | |
1163 | * The mutability of the returned dictionary is the same as the mutability | |
1164 | * of the underlying StickRecord::mPropList, which the caller is just | |
1165 | * going to have to know (and cast accordingly if a mutable dictionary | |
b1ab9ed8 | 1166 | * is needed). |
5c19dc3a | 1167 | */ |
b1ab9ed8 A |
1168 | CFDictionaryRef TrustSettings::findDictionaryForCert( |
1169 | SecCertificateRef certRef) | |
1170 | { | |
1171 | CFRef<CFStringRef> certHashStr(SecTrustSettingsCertHashStrFromCert(certRef)); | |
1172 | if (certHashStr.get() == NULL) | |
1173 | { | |
1174 | return NULL; | |
1175 | } | |
5c19dc3a | 1176 | |
fa7225c8 | 1177 | return findDictionaryForCertHash(static_cast<CFStringRef>(certHashStr.get())); |
b1ab9ed8 A |
1178 | } |
1179 | ||
1180 | /* | |
5c19dc3a | 1181 | * Find entry in mTrustDict given cert hash string. |
b1ab9ed8 A |
1182 | */ |
1183 | CFDictionaryRef TrustSettings::findDictionaryForCertHash( | |
1184 | CFStringRef certHashStr) | |
1185 | { | |
1186 | assert(mTrustDict != NULL); | |
1187 | return (CFDictionaryRef)CFDictionaryGetValue(mTrustDict, certHashStr); | |
1188 | } | |
1189 | ||
1190 | /* | |
5c19dc3a A |
1191 | * Validate incoming trust settings, which may be NULL, a dictionary, or |
1192 | * an array of dictionaries. Convert from the API-style dictionaries | |
1193 | * to the internal style suitable for writing to disk as part of | |
b1ab9ed8 A |
1194 | * mPropList. |
1195 | * | |
1196 | * We return a refcounted CFArray in any case if the incoming parameter is good. | |
1197 | */ | |
1198 | CFArrayRef TrustSettings::validateApiTrustSettings( | |
1199 | CFTypeRef trustSettingsDictOrArray, | |
1200 | Boolean isSelfSigned) | |
1201 | { | |
1202 | CFArrayRef tmpInArray = NULL; | |
1203 | ||
1204 | if(trustSettingsDictOrArray == NULL) { | |
1205 | /* trivial case, only valid for roots */ | |
1206 | if(!isSelfSigned) { | |
1207 | trustSettingsDbg("validateApiUsageConstraints: !isSelfSigned, no settings"); | |
427c49bc | 1208 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
1209 | } |
1210 | return CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); | |
1211 | } | |
1212 | else if(CFGetTypeID(trustSettingsDictOrArray) == CFDictionaryGetTypeID()) { | |
1213 | /* array-ize it */ | |
1214 | tmpInArray = CFArrayCreate(NULL, &trustSettingsDictOrArray, 1, | |
1215 | &kCFTypeArrayCallBacks); | |
1216 | } | |
1217 | else if(CFGetTypeID(trustSettingsDictOrArray) == CFArrayGetTypeID()) { | |
1218 | /* as is, refcount - we'll release later */ | |
1219 | tmpInArray = (CFArrayRef)trustSettingsDictOrArray; | |
1220 | CFRetain(tmpInArray); | |
1221 | } | |
1222 | else { | |
1223 | trustSettingsDbg("validateApiUsageConstraints: bad trustSettingsDictOrArray"); | |
427c49bc | 1224 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 | 1225 | } |
5c19dc3a | 1226 | |
b1ab9ed8 A |
1227 | CFIndex numSpecs = CFArrayGetCount(tmpInArray); |
1228 | CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, numSpecs, &kCFTypeArrayCallBacks); | |
1229 | CSSM_OID oid; | |
427c49bc | 1230 | OSStatus ortn = errSecSuccess; |
b1ab9ed8 A |
1231 | SecPolicyRef certPolicy; |
1232 | SecTrustedApplicationRef certApp; | |
b54c578e | 1233 | CFTypeRef oidData = NULL; |
5c19dc3a | 1234 | |
b1ab9ed8 A |
1235 | /* convert */ |
1236 | for(CFIndex dex=0; dex<numSpecs; dex++) { | |
fa7225c8 | 1237 | CFStringRef policyName = NULL; |
b1ab9ed8 A |
1238 | CFDataRef appData = NULL; |
1239 | CFStringRef policyStr = NULL; | |
1240 | CFNumberRef allowedErr = NULL; | |
1241 | CFNumberRef resultType = NULL; | |
1242 | CFNumberRef keyUsage = NULL; | |
1243 | SInt32 resultNum; | |
1244 | SecTrustSettingsResult result; | |
1245 | ||
1246 | /* each element is a dictionary */ | |
1247 | CFDictionaryRef ucDict = (CFDictionaryRef)CFArrayGetValueAtIndex(tmpInArray, dex); | |
1248 | if(CFGetTypeID(ucDict) != CFDictionaryGetTypeID()) { | |
1249 | trustSettingsDbg("validateAppPolicyArray: malformed usageConstraint dictionary"); | |
427c49bc | 1250 | ortn = errSecParam; |
b1ab9ed8 A |
1251 | break; |
1252 | } | |
5c19dc3a | 1253 | |
b1ab9ed8 A |
1254 | /* policy - optional */ |
1255 | certPolicy = (SecPolicyRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicy); | |
1256 | if(certPolicy != NULL) { | |
1257 | if(CFGetTypeID(certPolicy) != SecPolicyGetTypeID()) { | |
1258 | trustSettingsDbg("validateAppPolicyArray: malformed certPolicy"); | |
427c49bc | 1259 | ortn = errSecParam; |
b1ab9ed8 A |
1260 | break; |
1261 | } | |
1262 | ortn = SecPolicyGetOID(certPolicy, &oid); | |
fa7225c8 A |
1263 | if (ortn) { |
1264 | /* newer policies don't have CSSM OIDs but they do have string OIDs */ | |
1265 | oidData = CFRetain(SecPolicyGetOidString(certPolicy)); | |
1266 | } else { | |
1267 | oidData = CFDataCreate(NULL, oid.Data, oid.Length); | |
1268 | } | |
1269 | ||
1270 | if (!oidData) { | |
b1ab9ed8 A |
1271 | trustSettingsDbg("validateAppPolicyArray: SecPolicyGetOID error"); |
1272 | break; | |
1273 | } | |
fa7225c8 | 1274 | policyName = SecPolicyGetName(certPolicy); |
b1ab9ed8 A |
1275 | } |
1276 | ||
1277 | /* application - optional */ | |
1278 | certApp = (SecTrustedApplicationRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsApplication); | |
1279 | if(certApp != NULL) { | |
1280 | if(CFGetTypeID(certApp) != SecTrustedApplicationGetTypeID()) { | |
1281 | trustSettingsDbg("validateAppPolicyArray: malformed certApp"); | |
427c49bc | 1282 | ortn = errSecParam; |
b1ab9ed8 A |
1283 | break; |
1284 | } | |
1285 | ortn = SecTrustedApplicationCopyExternalRepresentation(certApp, &appData); | |
1286 | if(ortn) { | |
1287 | trustSettingsDbg("validateAppPolicyArray: " | |
1288 | "SecTrustedApplicationCopyExternalRepresentation error"); | |
1289 | break; | |
1290 | } | |
1291 | } | |
1292 | ||
1293 | policyStr = (CFStringRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicyString); | |
1294 | if(policyStr != NULL) { | |
1295 | if(CFGetTypeID(policyStr) != CFStringGetTypeID()) { | |
1296 | trustSettingsDbg("validateAppPolicyArray: malformed policyStr"); | |
427c49bc | 1297 | ortn = errSecParam; |
b1ab9ed8 A |
1298 | break; |
1299 | } | |
1300 | } | |
1301 | allowedErr = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsAllowedError); | |
1302 | if(!tsIsGoodCfNum(allowedErr)) { | |
1303 | trustSettingsDbg("validateAppPolicyArray: malformed allowedErr"); | |
427c49bc | 1304 | ortn = errSecParam; |
b1ab9ed8 A |
1305 | break; |
1306 | } | |
1307 | resultType = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsResult); | |
1308 | if(!tsIsGoodCfNum(resultType, &resultNum)) { | |
1309 | trustSettingsDbg("validateAppPolicyArray: malformed resultType"); | |
427c49bc | 1310 | ortn = errSecParam; |
b1ab9ed8 A |
1311 | break; |
1312 | } | |
6b200bc3 | 1313 | result = (SecTrustSettingsResult) resultNum; |
b1ab9ed8 A |
1314 | /* validate result later */ |
1315 | ||
1316 | keyUsage = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsKeyUsage); | |
1317 | if(!tsIsGoodCfNum(keyUsage)) { | |
1318 | trustSettingsDbg("validateAppPolicyArray: malformed keyUsage"); | |
427c49bc | 1319 | ortn = errSecParam; |
b1ab9ed8 A |
1320 | break; |
1321 | } | |
5c19dc3a A |
1322 | |
1323 | if(!oidData && !appData && !policyStr && | |
b1ab9ed8 A |
1324 | !allowedErr && !resultType && !keyUsage) { |
1325 | /* nothing here - weird, but legal - skip it */ | |
1326 | continue; | |
1327 | } | |
5c19dc3a | 1328 | |
b1ab9ed8 | 1329 | /* create dictionary for this usageConstraint */ |
5c19dc3a A |
1330 | CFMutableDictionaryRef outDict = CFDictionaryCreateMutable(NULL, |
1331 | 2, | |
1332 | &kCFTypeDictionaryKeyCallBacks, | |
b1ab9ed8 A |
1333 | &kCFTypeDictionaryValueCallBacks); |
1334 | if(oidData) { | |
1335 | CFDictionaryAddValue(outDict, kSecTrustSettingsPolicy, oidData); | |
b54c578e | 1336 | CFReleaseNull(oidData); // owned by dictionary |
b1ab9ed8 | 1337 | } |
fa7225c8 A |
1338 | if(policyName) { |
1339 | CFDictionaryAddValue(outDict, kSecTrustSettingsPolicyName, policyName); | |
1340 | /* still owned by ucDict */ | |
1341 | } | |
b1ab9ed8 A |
1342 | if(appData) { |
1343 | CFDictionaryAddValue(outDict, kSecTrustSettingsApplication, appData); | |
5c19dc3a | 1344 | CFRelease(appData); // owned by dictionary |
b1ab9ed8 A |
1345 | } |
1346 | if(policyStr) { | |
1347 | CFDictionaryAddValue(outDict, kSecTrustSettingsPolicyString, policyStr); | |
1348 | /* still owned by ucDict */ | |
1349 | } | |
1350 | if(allowedErr) { | |
1351 | CFDictionaryAddValue(outDict, kSecTrustSettingsAllowedError, allowedErr); | |
1352 | } | |
fa7225c8 | 1353 | |
5c19dc3a | 1354 | ortn = errSecSuccess; |
fa7225c8 | 1355 | |
b1ab9ed8 A |
1356 | if(resultType) { |
1357 | /* let's be really picky on this one */ | |
1358 | switch(result) { | |
1359 | case kSecTrustSettingsResultInvalid: | |
427c49bc | 1360 | ortn = errSecParam; |
b1ab9ed8 A |
1361 | break; |
1362 | case kSecTrustSettingsResultTrustRoot: | |
1363 | if(!isSelfSigned) { | |
1364 | trustSettingsDbg("validateAppPolicyArray: TrustRoot, !isSelfSigned"); | |
427c49bc | 1365 | ortn = errSecParam; |
b1ab9ed8 A |
1366 | } |
1367 | break; | |
1368 | case kSecTrustSettingsResultTrustAsRoot: | |
1369 | if(isSelfSigned) { | |
1370 | trustSettingsDbg("validateAppPolicyArray: TrustAsRoot, isSelfSigned"); | |
427c49bc | 1371 | ortn = errSecParam; |
b1ab9ed8 A |
1372 | } |
1373 | break; | |
1374 | case kSecTrustSettingsResultDeny: | |
1375 | case kSecTrustSettingsResultUnspecified: | |
1376 | break; | |
1377 | default: | |
1378 | trustSettingsDbg("validateAppPolicyArray: bogus resultType"); | |
427c49bc | 1379 | ortn = errSecParam; |
b1ab9ed8 A |
1380 | break; |
1381 | } | |
1382 | if(ortn) { | |
1383 | break; | |
1384 | } | |
1385 | CFDictionaryAddValue(outDict, kSecTrustSettingsResult, resultType); | |
1386 | } | |
1387 | else { | |
1388 | /* no resultType; default of TrustRoot only valid for root */ | |
1389 | if(!isSelfSigned) { | |
1390 | trustSettingsDbg("validateAppPolicyArray: default result, !isSelfSigned"); | |
427c49bc | 1391 | ortn = errSecParam; |
b1ab9ed8 A |
1392 | break; |
1393 | } | |
1394 | } | |
fa7225c8 | 1395 | |
b1ab9ed8 A |
1396 | if(keyUsage) { |
1397 | CFDictionaryAddValue(outDict, kSecTrustSettingsKeyUsage, keyUsage); | |
1398 | } | |
5c19dc3a | 1399 | |
b1ab9ed8 A |
1400 | /* append dictionary to output */ |
1401 | CFArrayAppendValue(outArray, outDict); | |
1402 | /* array owns the dictionary now */ | |
1403 | CFRelease(outDict); | |
5c19dc3a | 1404 | |
b1ab9ed8 | 1405 | } /* for each usage constraint dictionary */ |
5c19dc3a | 1406 | |
b54c578e | 1407 | CFReleaseNull(oidData); |
b1ab9ed8 A |
1408 | CFRelease(tmpInArray); |
1409 | if(ortn) { | |
1410 | CFRelease(outArray); | |
1411 | MacOSError::throwMe(ortn); | |
1412 | } | |
1413 | return outArray; | |
1414 | } | |
1415 | ||
5c19dc3a | 1416 | /* |
b1ab9ed8 | 1417 | * Validate an trust settings array obtained from disk. |
5c19dc3a | 1418 | * Returns true if OK, else returns false. |
b1ab9ed8 A |
1419 | */ |
1420 | bool TrustSettings::validateTrustSettingsArray( | |
1421 | CFArrayRef trustSettings) | |
1422 | { | |
1423 | CFIndex numSpecs = CFArrayGetCount(trustSettings); | |
1424 | for(CFIndex dex=0; dex<numSpecs; dex++) { | |
5c19dc3a | 1425 | CFDictionaryRef ucDict = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, |
b1ab9ed8 A |
1426 | dex); |
1427 | if(CFGetTypeID(ucDict) != CFDictionaryGetTypeID()) { | |
1428 | trustSettingsDbg("validateAppPolicyArray: malformed app/policy dictionary"); | |
1429 | return false; | |
1430 | } | |
1431 | CFDataRef certPolicy = (CFDataRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicy); | |
1432 | if((certPolicy != NULL) && (CFGetTypeID(certPolicy) != CFDataGetTypeID())) { | |
1433 | trustSettingsDbg("validateAppPolicyArray: malformed certPolicy"); | |
1434 | return false; | |
1435 | } | |
1436 | CFDataRef certApp = (CFDataRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsApplication); | |
1437 | if((certApp != NULL) && (CFGetTypeID(certApp) != CFDataGetTypeID())) { | |
1438 | trustSettingsDbg("validateAppPolicyArray: malformed certApp"); | |
1439 | return false; | |
1440 | } | |
1441 | CFStringRef policyStr = (CFStringRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicyString); | |
1442 | if((policyStr != NULL) && (CFGetTypeID(policyStr) != CFStringGetTypeID())) { | |
1443 | trustSettingsDbg("validateAppPolicyArray: malformed policyStr"); | |
1444 | return false; | |
1445 | } | |
1446 | CFNumberRef cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsAllowedError); | |
1447 | if(!tsIsGoodCfNum(cfNum)) { | |
1448 | trustSettingsDbg("validateAppPolicyArray: malformed allowedErr"); | |
1449 | return false; | |
1450 | } | |
1451 | cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsResult); | |
1452 | if(!tsIsGoodCfNum(cfNum)) { | |
1453 | trustSettingsDbg("validateAppPolicyArray: malformed resultType"); | |
1454 | return false; | |
1455 | } | |
1456 | cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsKeyUsage); | |
1457 | if(!tsIsGoodCfNum(cfNum)) { | |
1458 | trustSettingsDbg("validateAppPolicyArray: malformed keyUsage"); | |
1459 | return false; | |
1460 | } | |
1461 | } /* for each usageConstraint dictionary */ | |
1462 | return true; | |
1463 | } | |
1464 | ||
1465 | /* | |
5c19dc3a A |
1466 | * Validate mPropList after it's read from disk or supplied as an external |
1467 | * representation. Allows subsequent use of mTrustDict to proceed with | |
1468 | * relative impunity. | |
b1ab9ed8 A |
1469 | */ |
1470 | void TrustSettings::validatePropList(bool trim) | |
1471 | { | |
1472 | /* top level dictionary */ | |
1473 | if(!mPropList) { | |
1474 | trustSettingsDbg("TrustSettings::validatePropList missing mPropList"); | |
1475 | abort("missing propList", errSecInvalidTrustedRootRecord); | |
1476 | } | |
1477 | ||
1478 | if(CFGetTypeID(mPropList) != CFDictionaryGetTypeID()) { | |
1479 | trustSettingsDbg("TrustSettings::validatePropList: malformed mPropList"); | |
1480 | abort("malformed propList", errSecInvalidTrustedRootRecord); | |
1481 | } | |
1482 | ||
1483 | /* That dictionary has two entries */ | |
1484 | CFNumberRef cfVers = (CFNumberRef)CFDictionaryGetValue(mPropList, kTrustRecordVersion); | |
1485 | if((cfVers == NULL) || (CFGetTypeID(cfVers) != CFNumberGetTypeID())) { | |
1486 | trustSettingsDbg("TrustSettings::validatePropList: malformed version"); | |
1487 | abort("malformed version", errSecInvalidTrustedRootRecord); | |
1488 | } | |
1489 | if(!CFNumberGetValue(cfVers, kCFNumberSInt32Type, &mDictVersion)) { | |
1490 | trustSettingsDbg("TrustSettings::validatePropList: malformed version"); | |
1491 | abort("malformed version", errSecInvalidTrustedRootRecord); | |
1492 | } | |
1493 | if((mDictVersion > kSecTrustRecordVersionCurrent) || | |
1494 | (mDictVersion == kSecTrustRecordVersionInvalid)) { | |
1495 | trustSettingsDbg("TrustSettings::validatePropList: incompatible version"); | |
1496 | abort("incompatible version", errSecInvalidTrustedRootRecord); | |
1497 | } | |
1498 | /* other backwards-compatibility handling done later, if needed, per mDictVersion */ | |
5c19dc3a | 1499 | |
b1ab9ed8 A |
1500 | mTrustDict = (CFMutableDictionaryRef)CFDictionaryGetValue(mPropList, kTrustRecordTrustList); |
1501 | if(mTrustDict != NULL) { | |
1502 | CFRetain(mTrustDict); | |
1503 | } | |
1504 | if((mTrustDict == NULL) || (CFGetTypeID(mTrustDict) != CFDictionaryGetTypeID())) { | |
1505 | trustSettingsDbg("TrustSettings::validatePropList: malformed mTrustDict"); | |
1506 | abort("malformed TrustArray", errSecInvalidTrustedRootRecord); | |
1507 | } | |
5c19dc3a | 1508 | |
b1ab9ed8 A |
1509 | /* grind through the per-cert entries */ |
1510 | CFIndex numCerts = CFDictionaryGetCount(mTrustDict); | |
1511 | const void *dictKeys[numCerts]; | |
1512 | const void *dictValues[numCerts]; | |
1513 | CFDictionaryGetKeysAndValues(mTrustDict, dictKeys, dictValues); | |
1514 | ||
1515 | for(CFIndex dex=0; dex<numCerts; dex++) { | |
1516 | /* get per-cert dictionary */ | |
1517 | CFMutableDictionaryRef certDict = (CFMutableDictionaryRef)dictValues[dex]; | |
1518 | if((certDict == NULL) || (CFGetTypeID(certDict) != CFDictionaryGetTypeID())) { | |
1519 | trustSettingsDbg("TrustSettings::validatePropList: malformed certDict"); | |
1520 | abort("malformed certDict", errSecInvalidTrustedRootRecord); | |
1521 | } | |
5c19dc3a A |
1522 | |
1523 | /* | |
b1ab9ed8 A |
1524 | * That dictionary has exactly four entries. |
1525 | * If we're trimming, all we need is the actual trust settings. | |
1526 | */ | |
1527 | ||
1528 | /* issuer */ | |
1529 | CFDataRef cfd = (CFDataRef)CFDictionaryGetValue(certDict, kTrustRecordIssuer); | |
1530 | if(cfd == NULL) { | |
1531 | trustSettingsDbg("TrustSettings::validatePropList: missing issuer"); | |
1532 | abort("missing issuer", errSecInvalidTrustedRootRecord); | |
1533 | } | |
1534 | if(CFGetTypeID(cfd) != CFDataGetTypeID()) { | |
1535 | trustSettingsDbg("TrustSettings::validatePropList: malformed issuer"); | |
1536 | abort("malformed issuer", errSecInvalidTrustedRootRecord); | |
1537 | } | |
1538 | if(trim) { | |
1539 | CFDictionaryRemoveValue(certDict, kTrustRecordIssuer); | |
1540 | } | |
1541 | ||
1542 | /* serial number */ | |
1543 | cfd = (CFDataRef)CFDictionaryGetValue(certDict, kTrustRecordSerialNumber); | |
1544 | if(cfd == NULL) { | |
1545 | trustSettingsDbg("TrustSettings::validatePropList: missing serial number"); | |
1546 | abort("missing serial number", errSecInvalidTrustedRootRecord); | |
1547 | } | |
1548 | if(CFGetTypeID(cfd) != CFDataGetTypeID()) { | |
1549 | trustSettingsDbg("TrustSettings::validatePropList: malformed serial number"); | |
1550 | abort("malformed serial number", errSecInvalidTrustedRootRecord); | |
1551 | } | |
1552 | if(trim) { | |
1553 | CFDictionaryRemoveValue(certDict, kTrustRecordSerialNumber); | |
1554 | } | |
5c19dc3a | 1555 | |
b1ab9ed8 A |
1556 | /* modification date */ |
1557 | CFDateRef modDate = (CFDateRef)CFDictionaryGetValue(certDict, kTrustRecordModDate); | |
1558 | if(modDate == NULL) { | |
1559 | trustSettingsDbg("TrustSettings::validatePropList: missing modDate"); | |
1560 | abort("missing modDate", errSecInvalidTrustedRootRecord); | |
1561 | } | |
1562 | if(CFGetTypeID(modDate) != CFDateGetTypeID()) { | |
1563 | trustSettingsDbg("TrustSettings::validatePropList: malformed modDate"); | |
1564 | abort("malformed modDate", errSecInvalidTrustedRootRecord); | |
1565 | } | |
1566 | if(trim) { | |
1567 | CFDictionaryRemoveValue(certDict, kTrustRecordModDate); | |
1568 | } | |
5c19dc3a | 1569 | |
b1ab9ed8 A |
1570 | /* the actual trust settings */ |
1571 | CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict, | |
1572 | kTrustRecordTrustSettings); | |
1573 | if(trustSettings == NULL) { | |
1574 | /* optional; this cert's entry is good */ | |
1575 | continue; | |
1576 | } | |
1577 | if(CFGetTypeID(trustSettings) != CFArrayGetTypeID()) { | |
1578 | trustSettingsDbg("TrustSettings::validatePropList: malformed useConstraint" | |
1579 | "array"); | |
1580 | abort("malformed useConstraint array", errSecInvalidTrustedRootRecord); | |
1581 | } | |
5c19dc3a | 1582 | |
b1ab9ed8 A |
1583 | /* Now validate the usageConstraint array contents */ |
1584 | if(!validateTrustSettingsArray(trustSettings)) { | |
1585 | abort("malformed useConstraint array", errSecInvalidTrustedRootRecord); | |
1586 | } | |
1587 | } /* for each cert dictionary in top-level array */ | |
1588 | ||
1589 | if(trim) { | |
1590 | /* we don't need the top-level dictionary any more */ | |
1591 | CFRelease(mPropList); | |
1592 | mPropList = NULL; | |
1593 | } | |
1594 | } | |
1595 | ||
5c19dc3a A |
1596 | /* |
1597 | * Obtain non-normalized issuer and serial number for specified cert, both | |
1598 | * returned as CFDataRefs owned by caller. | |
b1ab9ed8 A |
1599 | */ |
1600 | void TrustSettings::copyIssuerAndSerial( | |
1601 | SecCertificateRef certRef, | |
1602 | CFDataRef *issuer, /* optional, RETURNED */ | |
1603 | CFDataRef *serial) /* RETURNED */ | |
1604 | { | |
5c19dc3a | 1605 | CFRef<SecCertificateRef> certificate = SecCertificateCreateItemImplInstance(certRef); |
5c19dc3a A |
1606 | |
1607 | SecPointer<Certificate> cert = Certificate::required(certificate); | |
b1ab9ed8 | 1608 | CSSM_DATA_PTR fieldVal; |
5c19dc3a A |
1609 | |
1610 | if(issuer != NULL) { | |
b1ab9ed8 A |
1611 | fieldVal = cert->copyFirstFieldValue(CSSMOID_X509V1IssuerNameStd); |
1612 | *issuer = CFDataCreate(NULL, fieldVal->Data, fieldVal->Length); | |
1613 | cert->releaseFieldValue(CSSMOID_X509V1IssuerNameStd, fieldVal); | |
1614 | } | |
5c19dc3a | 1615 | |
b1ab9ed8 A |
1616 | fieldVal = cert->copyFirstFieldValue(CSSMOID_X509V1SerialNumber); |
1617 | *serial = CFDataCreate(NULL, fieldVal->Data, fieldVal->Length); | |
1618 | cert->releaseFieldValue(CSSMOID_X509V1SerialNumber, fieldVal); | |
1619 | } | |
1620 | ||
1621 | void TrustSettings::abort( | |
1622 | const char *why, | |
1623 | OSStatus err) | |
1624 | { | |
1625 | Syslog::error("TrustSettings: %s", why); | |
1626 | MacOSError::throwMe(err); | |
1627 | } | |
1628 |