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