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 <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// MARK: - 42// MARK: Static Functions 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); 107 return CFDataCopyHexString(digest); 108} 109#endif 110 111// MARK: - 112// MARK: SecTrustSettings 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 = { 324 ts->_trustDict, ts->_dictVersion, trim, errSecSuccess 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 349 CFErrorRef error = NULL; 350 CFPropertyListRef plist = CFPropertyListCreateWithData(kCFAllocatorDefault, 351 external, kCFPropertyListImmutable, NULL, &error); 352 if (!plist) { 353 secwarning("SecTrustSettingsCreateFromExternal: %@", error); 354 CFReleaseSafe(error); 355 CFReleaseSafe(result); 356 goto errOut; 357 } 358 359 result->_propList = plist; 360 result->_trustDict = NULL; 361 SecTrustSettingsValidate(result, false); 362 363 *ts = result; 364 365 return errSecSuccess; 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 402// MARK: - 403// MARK: SPI functions 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; 508 return errSecSuccess; 509 } 510 foundAny |= foundAnyHere; 511 } 512 trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND"); 513 *foundAnyEntry = foundAny; 514 *foundMatchingEntry = false; 515 return errSecSuccess; 516 END_RCSAPI 517#endif 518 return errSecSuccess; 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} 544