]> git.saurik.com Git - apple/security.git/blob - sec/Security/SecTrustSettings.c
Security-55163.44.tar.gz
[apple/security.git] / sec / Security / SecTrustSettings.c
1 /*
2 * Copyright (c) 2007-2008,2010 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecTrustSettings.c - Implement trust settings for certificates
26 */
27
28 #include "SecTrustSettings.h"
29 #include "SecTrustSettingsPriv.h"
30 #include <Security/SecCertificatePriv.h>
31 #include <AssertMacros.h>
32 #include <pthread.h>
33 #include <security_utilities/debugging.h>
34 #include "SecBasePriv.h"
35 #include <Security/SecInternal.h>
36 #include <CoreFoundation/CFRuntime.h>
37
38 #define trustSettingsDbg(args...) secdebug("trustSettings", ## args)
39 #define trustSettingsEvalDbg(args...) secdebug("trustSettingsEval", ## args)
40
41 #pragma mark -
42 #pragma mark Static Functions
43
44 #if 0
45 /* Return a (hex)string representation of a CFDataRef. */
46 static CFStringRef SecCopyHexStringFromData(CFDataRef data)
47 {
48 CFIndex ix, length;
49 const UInt8 *bytes;
50 CFMutableStringRef string;
51
52 if (data) {
53 length = CFDataGetLength(data);
54 bytes = CFDataGetBytePtr(data);
55 } else {
56 length = 0;
57 bytes = NULL;
58 }
59 string = CFStringCreateMutable(kCFAllocatorDefault, length * 2);
60 for (ix = 0; ix < length; ++ix)
61 CFStringAppendFormat(string, NULL, CFSTR("%02X"), bytes[ix]);
62
63 return string;
64 }
65 #endif
66
67 /* Return a CFDataRef representation of a (hex)string. */
68 static CFDataRef SecCopyDataFromHexString(CFStringRef string) {
69 CFMutableDataRef data;
70 CFIndex ix, length;
71 UInt8 *bytes;
72
73 length = string ? CFStringGetLength(string) : 0;
74 if (length & 1) {
75 secwarning("Odd length string: %@ returning NULL", string);
76 return NULL;
77 }
78
79 data = CFDataCreateMutable(kCFAllocatorDefault, length / 2);
80 CFDataSetLength(data, length / 2);
81 bytes = CFDataGetMutableBytePtr(data);
82
83 CFStringInlineBuffer buf;
84 CFRange range = { 0, length };
85 CFStringInitInlineBuffer(string, &buf, range);
86 UInt8 lastv = 0;
87 for (ix = 0; ix < length; ++ix) {
88 UniChar c = CFStringGetCharacterFromInlineBuffer(&buf, ix);
89 UInt8 v;
90 if ('0' <= c && c <= '9')
91 v = c - '0';
92 else if ('A' <= c && c <= 'F')
93 v = c = 'A' + 10;
94 else if ('a' <= c && c <= 'a')
95 v = c = 'a' + 10;
96 else {
97 secwarning("Non hex string: %@ returning NULL", string);
98 CFRelease(data);
99 return NULL;
100 }
101 if (ix & 1) {
102 *bytes++ = (lastv << 4) + v;
103 } else {
104 lastv = v;
105 }
106 }
107
108 return data;
109 }
110
111 #if 0
112 /*
113 * Obtain a string representing a cert's SHA1 digest. This string is
114 * the key used to look up per-cert trust settings in a TrustSettings record.
115 */
116 static CFStringRef SecTrustSettingsCertHashStrFromCert(
117 SecCertificateRef certRef)
118 {
119 if (certRef == NULL) {
120 return NULL;
121 }
122
123 if(certRef == kSecTrustSettingsDefaultRootCertSetting) {
124 /* use this string instead of the cert hash as the dictionary key */
125 secdebug("trustsettings","DefaultSetting");
126 return kSecTrustRecordDefaultRootCert;
127 }
128
129 CFDataRef digest = SecCertificateGetSHA1Digest(certRef);
130 return SecCopyHexStringFromData(digest);
131 }
132 #endif
133
134 #pragma mark -
135 #pragma mark SecTrustSettings
136 /********************************************************
137 ************** SecTrustSettings object *****************
138 ********************************************************/
139
140 struct __SecTrustSettings {
141 CFRuntimeBase _base;
142
143 /* the overall parsed TrustSettings - may be NULL if this is trimmed */
144 CFDictionaryRef _propList;
145
146 /* and the main thing we work with, the dictionary of per-cert trust settings */
147 CFMutableDictionaryRef _trustDict;
148
149 /* version number of mPropDict */
150 SInt32 _dictVersion;
151
152 SecTrustSettingsDomain _domain;
153 bool _dirty; /* we've changed _trustDict since creation */
154 };
155
156 /* CFRuntime regsitration data. */
157 static pthread_once_t kSecTrustSettingsRegisterClass = PTHREAD_ONCE_INIT;
158 static CFTypeID kSecTrustSettingsTypeID = _kCFRuntimeNotATypeID;
159
160 static void SecTrustSettingsDestroy(CFTypeRef cf) {
161 SecTrustSettingsRef ts = (SecTrustSettingsRef) cf;
162 CFReleaseSafe(ts->_propList);
163 CFReleaseSafe(ts->_trustDict);
164 }
165
166 static void SecTrustSettingsRegisterClass(void) {
167 static const CFRuntimeClass kSecTrustSettingsClass = {
168 0, /* version */
169 "SecTrustSettings", /* class name */
170 NULL, /* init */
171 NULL, /* copy */
172 SecTrustSettingsDestroy, /* dealloc */
173 NULL, /* equal */
174 NULL, /* hash */
175 NULL, /* copyFormattingDesc */
176 NULL /* copyDebugDesc */
177 };
178
179 kSecTrustSettingsTypeID = _CFRuntimeRegisterClass(&kSecTrustSettingsClass);
180 }
181
182 /* SecTrustSettings API functions. */
183 CFTypeID SecTrustSettingsGetTypeID(void) {
184 pthread_once(&kSecTrustSettingsRegisterClass, SecTrustSettingsRegisterClass);
185 return kSecTrustSettingsTypeID;
186 }
187
188 /* Make sure a presumed CFNumber can be converted to a 32-bit number */
189 static bool tsIsGoodCfNum(CFNumberRef cfn, SInt32 *num)
190 {
191 /* by convention */
192 if (cfn == NULL) {
193 *num = 0;
194 return true;
195 }
196 require(CFGetTypeID(cfn) == CFNumberGetTypeID(), errOut);
197 return CFNumberGetValue(cfn, kCFNumberSInt32Type, num);
198 errOut:
199 return false;
200 }
201
202 static bool validateUsageConstraint(const void *value) {
203 CFDictionaryRef ucDict = (CFDictionaryRef)value;
204 require(CFGetTypeID(ucDict) == CFDictionaryGetTypeID(), errOut);
205
206 CFDataRef certPolicy = (CFDataRef)CFDictionaryGetValue(ucDict,
207 kSecTrustSettingsPolicy);
208 require(certPolicy && CFGetTypeID(certPolicy) == CFDataGetTypeID(), errOut);
209
210 CFStringRef certApp = (CFStringRef)CFDictionaryGetValue(ucDict,
211 kSecTrustSettingsApplication);
212 require(certApp && CFGetTypeID(certApp) == CFStringGetTypeID(), errOut);
213
214 CFStringRef policyStr = (CFStringRef)CFDictionaryGetValue(ucDict,
215 kSecTrustSettingsPolicyString);
216 require(policyStr && CFGetTypeID(policyStr) == CFStringGetTypeID(), errOut);
217
218 SInt32 dummy;
219 CFNumberRef allowedError = (CFNumberRef)CFDictionaryGetValue(ucDict,
220 kSecTrustSettingsAllowedError);
221 require(tsIsGoodCfNum(allowedError, &dummy), errOut);
222
223 CFNumberRef trustSettingsResult = (CFNumberRef)CFDictionaryGetValue(ucDict,
224 kSecTrustSettingsResult);
225 require(tsIsGoodCfNum(trustSettingsResult, &dummy), errOut);
226
227 CFNumberRef keyUsage = (CFNumberRef)CFDictionaryGetValue(ucDict,
228 kSecTrustSettingsKeyUsage);
229 require(tsIsGoodCfNum(keyUsage, &dummy), errOut);
230
231 return true;
232 errOut:
233 return false;
234 }
235
236 static bool validateTrustSettingsArray(CFArrayRef usageConstraints) {
237 CFIndex ix, numConstraints = CFArrayGetCount(usageConstraints);
238 bool result = true;
239 for (ix = 0; ix < numConstraints; ++ix) {
240 if (!validateUsageConstraint(CFArrayGetValueAtIndex(usageConstraints, ix)))
241 result = false;
242 }
243 return result;
244 }
245
246 struct trustListContext {
247 CFMutableDictionaryRef dict;
248 SInt32 version;
249 bool trim;
250 OSStatus status;
251 };
252
253 static void trustListApplierFunction(const void *key, const void *value, void *context) {
254 CFStringRef digest = (CFStringRef)key;
255 CFDictionaryRef certDict = (CFDictionaryRef)value;
256 struct trustListContext *tlc = (struct trustListContext *)context;
257 CFDataRef newKey = NULL;
258 CFMutableDictionaryRef newDict = NULL;
259
260 /* Get the key. */
261 require(digest && CFGetTypeID(digest) == CFStringGetTypeID(), errOut);
262 /* Convert it to a CFDataRef. */
263 require(newKey = SecCopyDataFromHexString(digest), errOut);
264
265 /* get per-cert dictionary */
266 require(certDict && CFGetTypeID(certDict) == CFDictionaryGetTypeID(), errOut);
267
268 /* Create the to be inserted dictionary. */
269 require(newDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
270 tlc->trim ? 1 : 4, &kCFTypeDictionaryKeyCallBacks,
271 &kCFTypeDictionaryValueCallBacks), errOut);
272
273 /* The certDict dictionary should have exactly four entries.
274 If we're trimming, all we need is the actual trust settings. */
275
276 /* issuer */
277 CFDataRef issuer = (CFDataRef)CFDictionaryGetValue(certDict,
278 kTrustRecordIssuer);
279 require(issuer && CFGetTypeID(issuer) == CFDataGetTypeID(), errOut);
280
281 /* serial number */
282 CFDataRef serial = (CFDataRef)CFDictionaryGetValue(certDict,
283 kTrustRecordSerialNumber);
284 require(serial && CFGetTypeID(serial) == CFDataGetTypeID(), errOut);
285
286 /* modification date */
287 CFDateRef modDate = (CFDateRef)CFDictionaryGetValue(certDict,
288 kTrustRecordModDate);
289 require(modDate && CFGetTypeID(modDate) == CFDateGetTypeID(), errOut);
290
291 /* If we are not trimming we copy these extra values as well. */
292 if (!tlc->trim) {
293 CFDictionaryAddValue(newDict, kTrustRecordIssuer, issuer);
294 CFDictionaryAddValue(newDict, kTrustRecordSerialNumber, serial);
295 CFDictionaryAddValue(newDict, kTrustRecordModDate, modDate);
296 }
297
298 /* The actual trust settings */
299 CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict,
300 kTrustRecordTrustSettings);
301 /* optional; this cert's entry is good */
302 if(trustSettings) {
303 require(CFGetTypeID(trustSettings) == CFArrayGetTypeID(), errOut);
304
305 /* Now validate the usageConstraint array contents */
306 require(validateTrustSettingsArray(trustSettings), errOut);
307
308 /* Add the trustSettings to the dict. */
309 CFDictionaryAddValue(newDict, kTrustRecordTrustSettings, trustSettings);
310 }
311
312 CFDictionaryAddValue(tlc->dict, newKey, newDict);
313 CFRelease(newKey);
314 CFRelease(newDict);
315
316 return;
317 errOut:
318 CFReleaseSafe(newKey);
319 CFReleaseSafe(newDict);
320 tlc->status = errSecInvalidTrustSettings;
321 }
322
323 static OSStatus SecTrustSettingsValidate(SecTrustSettingsRef ts, bool trim) {
324 /* top level dictionary */
325 require(ts->_propList, errOut);
326 require(CFGetTypeID(ts->_propList) == CFDictionaryGetTypeID(), errOut);
327
328 /* That dictionary has two entries */
329 CFNumberRef cfVers = (CFNumberRef)CFDictionaryGetValue(ts->_propList, kTrustRecordVersion);
330 require(cfVers != NULL && CFGetTypeID(cfVers) == CFNumberGetTypeID(), errOut);
331 require(CFNumberGetValue(cfVers, kCFNumberSInt32Type, &ts->_dictVersion), errOut);
332 require((ts->_dictVersion <= kSecTrustRecordVersionCurrent) &&
333 (ts->_dictVersion != kSecTrustRecordVersionInvalid), errOut);
334
335 /* other backwards-compatibility handling done later, if needed, per _dictVersion */
336
337 require(ts->_trustDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
338 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), errOut);
339
340 CFDictionaryRef trustList = (CFDictionaryRef)CFDictionaryGetValue(
341 ts->_propList, kTrustRecordTrustList);
342 require(trustList != NULL &&
343 CFGetTypeID(trustList) == CFDictionaryGetTypeID(), errOut);
344
345 /* Convert the per-cert entries from on disk to in memory format. */
346 struct trustListContext context = {
347 ts->_trustDict, ts->_dictVersion, trim, noErr
348 };
349 CFDictionaryApplyFunction(trustList, trustListApplierFunction, &context);
350
351 if (trim) {
352 /* we don't need the top-level dictionary any more */
353 CFRelease(ts->_propList);
354 ts->_propList = NULL;
355 }
356
357 return context.status;
358
359 errOut:
360 return errSecInvalidTrustSettings;
361 }
362
363 OSStatus SecTrustSettingsCreateFromExternal(SecTrustSettingsDomain domain,
364 CFDataRef external, SecTrustSettingsRef *ts) {
365 CFAllocatorRef allocator = kCFAllocatorDefault;
366 CFIndex size = sizeof(struct __SecTrustSettings);
367 SecTrustSettingsRef result;
368
369 require(result = (SecTrustSettingsRef)_CFRuntimeCreateInstance(allocator,
370 SecTrustSettingsGetTypeID(), size - sizeof(CFRuntimeBase), 0), errOut);
371
372 CFStringRef errorString = NULL;
373 CFPropertyListRef plist = CFPropertyListCreateFromXMLData(
374 kCFAllocatorDefault, external, kCFPropertyListImmutable, &errorString);
375 if (!plist) {
376 secwarning("CFPropertyListCreateFromXMLData: %@", errorString);
377 CFReleaseSafe(errorString);
378 goto errOut;
379 }
380
381 result->_propList = plist;
382 result->_trustDict = NULL;
383 SecTrustSettingsValidate(result, false);
384
385 *ts = result;
386
387 return noErr;
388
389 errOut:
390 return errSecInvalidTrustSettings;
391 }
392
393 SecTrustSettingsRef SecTrustSettingsCreate(SecTrustSettingsDomain domain,
394 bool create, bool trim) {
395 CFAllocatorRef allocator = kCFAllocatorDefault;
396 CFIndex size = sizeof(struct __SecTrustSettings);
397 SecTrustSettingsRef result =
398 (SecTrustSettingsRef)_CFRuntimeCreateInstance(allocator,
399 SecTrustSettingsGetTypeID(), size - sizeof(CFRuntimeBase), 0);
400 if (!result)
401 return NULL;
402
403 //result->_data = NULL;
404
405 return result;
406 }
407
408 CFDataRef SecTrustSettingsCopyExternal(SecTrustSettingsRef ts) {
409 /* Transform the internal represantation of the trustSettings to an
410 external form. */
411 // @@@ SecTrustSettingsUpdatePropertyList(SecTrustSettingsRef ts);
412 CFDataRef xmlData;
413 verify(xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault,
414 ts->_propList));
415 return xmlData;
416 }
417
418 void SecTrustSettingsSet(SecCertificateRef certRef,
419 CFTypeRef trustSettingsDictOrArray) {
420 }
421
422
423
424 #pragma mark -
425 #pragma mark SPI functions
426
427
428 /*
429 * Fundamental routine used by TP to ascertain status of one cert.
430 *
431 * Returns true in *foundMatchingEntry if a trust setting matching
432 * specific constraints was found for the cert. Returns true in
433 * *foundAnyEntry if any entry was found for the cert, even if it
434 * did not match the specified constraints. The TP uses this to
435 * optimize for the case where a cert is being evaluated for
436 * one type of usage, and then later for another type. If
437 * foundAnyEntry is false, the second evaluation need not occur.
438 *
439 * Returns the domain in which a setting was found in *foundDomain.
440 *
441 * Allowed errors applying to the specified cert evaluation
442 * are returned in a mallocd array in *allowedErrors and must
443 * be freed by caller.
444 *
445 * The design of the entire TrustSettings module is centered around
446 * optimizing the performance of this routine (security concerns
447 * aside, that is). It's why the per-cert dictionaries are stored
448 * as a dictionary, keyed off of the cert hash. It's why TrustSettings
449 * are cached in memory by tsGetGlobalTrustSettings(), and why those
450 * cached TrustSettings objects are 'trimmed' of dictionary fields
451 * which are not needed to verify a cert.
452 *
453 * The API functions which are used to manipulate Trust Settings
454 * are called infrequently and need not be particularly fast since
455 * they result in user interaction for authentication. Thus they do
456 * not use cached TrustSettings as this function does.
457 */
458 OSStatus SecTrustSettingsEvaluateCertificate(
459 SecCertificateRef certificate,
460 SecPolicyRef policy,
461 SecTrustSettingsKeyUsage keyUsage, /* optional */
462 bool isSelfSignedCert, /* for checking default setting */
463 /* RETURNED values */
464 SecTrustSettingsDomain *foundDomain,
465 CFArrayRef *allowedErrors, /* RETURNED */
466 SecTrustSettingsResult *resultType, /* RETURNED */
467 bool *foundMatchingEntry,/* RETURNED */
468 bool *foundAnyEntry) /* RETURNED */
469 {
470 #if 0
471 BEGIN_RCSAPI
472
473 StLock<Mutex> _(sutCacheLock());
474
475 TS_REQUIRED(certHashStr)
476 TS_REQUIRED(foundDomain)
477 TS_REQUIRED(allowedErrors)
478 TS_REQUIRED(numAllowedErrors)
479 TS_REQUIRED(resultType)
480 TS_REQUIRED(foundMatchingEntry)
481 TS_REQUIRED(foundMatchingEntry)
482
483 /* ensure a NULL_terminated string */
484 auto_array<char> polStr;
485 if(policyString != NULL) {
486 polStr.allocate(policyStringLen + 1);
487 memmove(polStr.get(), policyString, policyStringLen);
488 if(policyString[policyStringLen - 1] != '\0') {
489 (polStr.get())[policyStringLen] = '\0';
490 }
491 }
492
493 /* initial condition - this can grow if we inspect multiple TrustSettings */
494 *allowedErrors = NULL;
495 *numAllowedErrors = 0;
496
497 /*
498 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
499 * search user first, then admin, then system.
500 */
501 assert(kSecTrustSettingsDomainAdmin == (kSecTrustSettingsDomainUser + 1));
502 assert(kSecTrustSettingsDomainSystem == (kSecTrustSettingsDomainAdmin + 1));
503 bool foundAny = false;
504 for(unsigned domain=kSecTrustSettingsDomainUser;
505 domain<=kSecTrustSettingsDomainSystem;
506 domain++) {
507 TrustSettings *ts = tsGetGlobalTrustSettings(domain);
508 if(ts == NULL) {
509 continue;
510 }
511
512 /* validate cert returns true if matching entry was found */
513 bool foundAnyHere = false;
514 bool found = ts->evaluateCert(certHashStr, policyOID,
515 polStr.get(), keyUsage, isRootCert,
516 allowedErrors, numAllowedErrors, resultType, &foundAnyHere);
517
518 if(found) {
519 /*
520 * Note this, even though we may overwrite it later if this
521 * is an Unspecified entry and we find a definitive entry
522 * later
523 */
524 *foundDomain = domain;
525 }
526 if(found && (*resultType != kSecTrustSettingsResultUnspecified)) {
527 trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain);
528 *foundAnyEntry = true;
529 *foundMatchingEntry = true;
530 return noErr;
531 }
532 foundAny |= foundAnyHere;
533 }
534 trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
535 *foundAnyEntry = foundAny;
536 *foundMatchingEntry = false;
537 return noErr;
538 END_RCSAPI
539 #endif
540 return noErr;
541 }
542
543 /*
544 * Add a cert's TrustSettings to a non-persistent TrustSettings record.
545 * No locking or cache flushing here; it's all local to the TrustSettings
546 * we construct here.
547 */
548 OSStatus SecTrustSettingsSetTrustSettingsExternal(
549 CFDataRef settingsIn, /* optional */
550 SecCertificateRef certRef, /* optional */
551 CFTypeRef trustSettingsDictOrArray, /* optional */
552 CFDataRef *settingsOut) /* RETURNED */
553 {
554 SecTrustSettingsRef ts = NULL;
555 OSStatus status;
556
557 require_noerr(status = SecTrustSettingsCreateFromExternal(
558 kSecTrustSettingsDomainMemory, settingsIn, &ts), errOut);
559 SecTrustSettingsSet(certRef, trustSettingsDictOrArray);
560 *settingsOut = SecTrustSettingsCopyExternal(ts);
561
562 errOut:
563 CFReleaseSafe(ts);
564 return status;
565 }