policydb.cpp
1 /* 2 * Copyright (c) 2011-2013 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 #include "cs.h" 24 #include "policydb.h" 25 #include "policyengine.h" 26 #include <Security/CodeSigning.h> 27 #include <security_utilities/cfutilities.h> 28 #include <security_utilities/cfmunge.h> 29 #include <security_utilities/blob.h> 30 #include <security_utilities/logging.h> 31 #include <security_utilities/simpleprefs.h> 32 #include <security_utilities/logging.h> 33 #include "csdatabase.h" 34 35 #include <dispatch/dispatch.h> 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 #include <notify.h> 39 40 namespace Security { 41 namespace CodeSigning { 42 43 44 using namespace SQLite; 45 46 47 // 48 // Determine the database path 49 // 50 static const char *dbPath() 51 { 52 if (const char *s = getenv("SYSPOLICYDATABASE")) 53 return s; 54 return defaultDatabase; 55 } 56 57 58 // 59 // Help mapping API-ish CFString keys to more convenient internal enumerations 60 // 61 typedef struct { 62 const CFStringRef &cstring; 63 uint enumeration; 64 } StringMap; 65 66 static uint mapEnum(CFDictionaryRef context, CFStringRef attr, const StringMap *map, uint value = 0) 67 { 68 if (context) 69 if (CFTypeRef value = CFDictionaryGetValue(context, attr)) 70 for (const StringMap *mp = map; mp->cstring; ++mp) 71 if (CFEqual(mp->cstring, value)) 72 return mp->enumeration; 73 return value; 74 } 75 76 static const StringMap mapType[] = { 77 { kSecAssessmentOperationTypeExecute, kAuthorityExecute }, 78 { kSecAssessmentOperationTypeInstall, kAuthorityInstall }, 79 { kSecAssessmentOperationTypeOpenDocument, kAuthorityOpenDoc }, 80 { NULL } 81 }; 82 83 AuthorityType typeFor(CFDictionaryRef context, AuthorityType type /* = kAuthorityInvalid */) 84 { 85 return mapEnum(context, kSecAssessmentContextKeyOperation, mapType, type); 86 } 87 88 CFStringRef typeNameFor(AuthorityType type) 89 { 90 for (const StringMap *mp = mapType; mp->cstring; ++mp) 91 if (type == mp->enumeration) 92 return mp->cstring; 93 return CFStringCreateWithFormat(NULL, NULL, CFSTR("type %d"), type); 94 } 95 96 97 // 98 // Open the database 99 // 100 PolicyDatabase::PolicyDatabase(const char *path, int flags) 101 : SQLite::Database(path ? path : dbPath(), flags), 102 mLastExplicitCheck(0) 103 { 104 // sqlite3 doesn't do foreign key support by default, have to turn this on per connection 105 SQLite::Statement foreign(*this, "PRAGMA foreign_keys = true"); 106 foreign.execute(); 107 108 // Try upgrade processing if we may be open for write. 109 // Ignore any errors (we may have been downgraded to read-only) 110 // and try again later. 111 if (openFlags() & SQLITE_OPEN_READWRITE) 112 try { 113 upgradeDatabase(); 114 installExplicitSet(gkeAuthFile, gkeSigsFile); 115 } catch(...) { 116 } 117 } 118 119 PolicyDatabase::~PolicyDatabase() 120 { /* virtual */ } 121 122 123 // 124 // Quick-check the cache for a match. 125 // Return true on a cache hit, false on failure to confirm a hit for any reason. 126 // 127 bool PolicyDatabase::checkCache(CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFMutableDictionaryRef result) 128 { 129 // we currently don't use the cache for anything but execution rules 130 if (type != kAuthorityExecute) 131 return false; 132 133 CFRef<SecStaticCodeRef> code; 134 MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref())); 135 if (SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly, NULL) != errSecSuccess) 136 return false; // quick pass - any error is a cache miss 137 CFRef<CFDictionaryRef> info; 138 MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref())); 139 CFDataRef cdHash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique)); 140 141 // check the cache table for a fast match 142 SQLite::Statement cached(*this, "SELECT object.allow, authority.label, authority FROM object, authority" 143 " WHERE object.authority = authority.id AND object.type = :type AND object.hash = :hash AND authority.disabled = 0" 144 " AND JULIANDAY('now') < object.expires;"); 145 cached.bind(":type").integer(type); 146 cached.bind(":hash") = cdHash; 147 if (cached.nextRow()) { 148 bool allow = int(cached[0]); 149 const char *label = cached[1]; 150 SQLite::int64 auth = cached[2]; 151 SYSPOLICY_ASSESS_CACHE_HIT(); 152 153 // If its allowed, lets do a full validation unless if 154 // we are overriding the assessement, since that force 155 // the verdict to 'pass' at the end 156 157 if (allow && !overrideAssessment(flags)) 158 MacOSError::check(SecStaticCodeCheckValidity(code, kSecCSDefaultFlags, NULL)); 159 160 cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow); 161 PolicyEngine::addAuthority(flags, result, label, auth, kCFBooleanTrue); 162 return true; 163 } 164 return false; 165 } 166 167 168 // 169 // Purge the object cache of all expired entries. 170 // These are meant to run within the caller's transaction. 171 // 172 void PolicyDatabase::purgeAuthority() 173 { 174 SQLite::Statement cleaner(*this, 175 "DELETE FROM authority WHERE expires <= JULIANDAY('now');"); 176 cleaner.execute(); 177 } 178 179 void PolicyDatabase::purgeObjects() 180 { 181 SQLite::Statement cleaner(*this, 182 "DELETE FROM object WHERE expires <= JULIANDAY('now');"); 183 cleaner.execute(); 184 } 185 186 void PolicyDatabase::purgeObjects(double priority) 187 { 188 SQLite::Statement cleaner(*this, 189 "DELETE FROM object WHERE expires <= JULIANDAY('now') OR (SELECT priority FROM authority WHERE id = object.authority) <= :priority;"); 190 cleaner.bind(":priority") = priority; 191 cleaner.execute(); 192 } 193 194 195 // 196 // Database migration 197 // 198 std::string PolicyDatabase::featureLevel(const char *name) 199 { 200 SQLite::Statement feature(*this, "SELECT value FROM feature WHERE name=:name"); 201 feature.bind(":name") = name; 202 if (feature.nextRow()) { 203 if (const char *value = feature[0]) 204 return value; 205 else 206 return "default"; // old engineering versions may have NULL values; tolerate this 207 } 208 return ""; // new feature (no level) 209 } 210 211 void PolicyDatabase::addFeature(const char *name, const char *value, const char *remarks) 212 { 213 SQLite::Statement feature(*this, "INSERT OR REPLACE INTO feature (name,value,remarks) VALUES(:name, :value, :remarks)"); 214 feature.bind(":name") = name; 215 feature.bind(":value") = value; 216 feature.bind(":remarks") = remarks; 217 feature.execute(); 218 } 219 220 void PolicyDatabase::simpleFeature(const char *feature, void (^perform)()) 221 { 222 SQLite::Transaction update(*this); 223 if (!hasFeature(feature)) { 224 perform(); 225 addFeature(feature, "upgraded", "upgraded"); 226 } 227 update.commit(); 228 } 229 230 void PolicyDatabase::simpleFeature(const char *feature, const char *sql) 231 { 232 simpleFeature(feature, ^{ 233 SQLite::Statement perform(*this, sql); 234 perform.execute(); 235 }); 236 } 237 238 void PolicyDatabase::simpleFeatureNoTransaction(const char *feature, void (^perform)()) 239 { 240 if (!hasFeature(feature)) { 241 perform(); 242 addFeature(feature, "upgraded", "upgraded"); 243 } 244 } 245 246 247 void PolicyDatabase::upgradeDatabase() 248 { 249 simpleFeature("bookmarkhints", 250 "CREATE TABLE bookmarkhints (" 251 " id INTEGER PRIMARY KEY AUTOINCREMENT, " 252 " bookmark BLOB," 253 " authority INTEGER NOT NULL" 254 " REFERENCES authority(id) ON DELETE CASCADE" 255 ")"); 256 257 simpleFeature("codesignedpackages", ^{ 258 SQLite::Statement update(*this, 259 "UPDATE authority" 260 " SET requirement = 'anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and " 261 "(certificate leaf[field.1.2.840.113635.100.6.1.14] or certificate leaf[field.1.2.840.113635.100.6.1.13])'" 262 " WHERE type = 2 and label = 'Developer ID' and flags & :flag"); 263 update.bind(":flag") = kAuthorityFlagDefault; 264 update.execute(); 265 }); 266 267 simpleFeature("filter_unsigned", 268 "ALTER TABLE authority ADD COLUMN filter_unsigned TEXT NULL" 269 ); 270 271 simpleFeature("strict_apple_installer", ^{ 272 SQLite::Statement update(*this, 273 "UPDATE authority" 274 " SET requirement = 'anchor apple generic and certificate 1[subject.CN] = \"Apple Software Update Certification Authority\"'" 275 " WHERE flags & :flag AND label = 'Apple Installer'"); 276 update.bind(":flag") = kAuthorityFlagDefault; 277 update.execute(); 278 SQLite::Statement add(*this, 279 "INSERT INTO authority (type, label, flags, requirement)" 280 " VALUES (2, 'Mac App Store', :flags, 'anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.10] exists')"); 281 add.bind(":flags") = kAuthorityFlagDefault; 282 add.execute(); 283 }); 284 285 simpleFeature("document rules", ^{ 286 SQLite::Statement addApple(*this, 287 "INSERT INTO authority (type, allow, flags, label, requirement) VALUES (3, 1, 2, 'Apple System', 'anchor apple')"); 288 addApple.execute(); 289 SQLite::Statement addDevID(*this, 290 "INSERT INTO authority (type, allow, flags, label, requirement) VALUES (3, 1, 2, 'Developer ID', 'anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists')"); 291 addDevID.execute(); 292 }); 293 294 simpleFeature("root_only", ^{ 295 UnixError::check(::chmod(dbPath(), S_IRUSR | S_IWUSR)); 296 }); 297 298 simpleFeature("notarized_apps", ^{ 299 300 // Insert a set of notarization requirements for notarized applications and installers, with a priority that will be higher than developer id priorities 301 // so they are guaranteed to match first. 302 SQLite::Statement addNotarizedExecutables(*this, 303 "INSERT INTO authority (type, allow, flags, priority, label, requirement) VALUES (1, 1, 2, 5.0, 'Notarized Developer ID', 'anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists and notarized')"); 304 addNotarizedExecutables.execute(); 305 306 SQLite::Statement addNotarizedInstallers(*this, 307 "INSERT INTO authority (type, allow, flags, priority, label, requirement) VALUES (2, 1, 2, 5.0, 'Notarized Developer ID', 'anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and (certificate leaf[field.1.2.840.113635.100.6.1.14] or certificate leaf[field.1.2.840.113635.100.6.1.13]) and notarized')"); 308 addNotarizedInstallers.execute(); 309 310 // Bump the priority on apple system, apple installer, and mac app store entries so they are evaluated before Developer ID variants. 311 // This is important because notarized variants meet the requirement of the Developer ID variant and would could match that too. 312 SQLite::Statement bumpAppleSystemPriority(*this, 313 "UPDATE authority SET priority = 20.0 WHERE label = 'Apple System'"); 314 bumpAppleSystemPriority.execute(); 315 316 SQLite::Statement bumpAppleInstallerPriority(*this, 317 "UPDATE authority SET priority = 20.0 WHERE label = 'Apple Installer'"); 318 bumpAppleInstallerPriority.execute(); 319 320 SQLite::Statement bumpMacAppStorePriority(*this, 321 "UPDATE authority SET priority = 10.0 WHERE label = 'Mac App Store'"); 322 bumpMacAppStorePriority.execute(); 323 }); 324 325 { 326 SQLite::Transaction devIdRequirementUpgrades(*this); 327 328 simpleFeatureNoTransaction("legacy_devid", ^{ 329 auto migrateReq = [](auto db, int type, string req) { 330 const string legacy = 331 " and (certificate leaf[timestamp.1.2.840.113635.100.6.1.33] absent or " 332 "certificate leaf[timestamp.1.2.840.113635.100.6.1.33] < timestamp \"20190408000000Z\")"; 333 334 const string unnotarized = 335 " and (certificate leaf[timestamp.1.2.840.113635.100.6.1.33] exists and " 336 "certificate leaf[timestamp.1.2.840.113635.100.6.1.33] >= timestamp \"20190408000000Z\")"; 337 338 SQLite::Statement update(*db, "UPDATE OR IGNORE authority " 339 "SET requirement = :newreq " 340 "WHERE requirement = :oldreq " 341 " AND type = :type " 342 " AND label = 'Developer ID'"); 343 update.bind(":oldreq") = req; 344 update.bind(":type") = type; 345 update.bind(":newreq") = req + legacy; 346 update.execute(); 347 348 SQLite::Statement insert(*db, "INSERT OR IGNORE INTO authority " 349 "(type, requirement, allow, priority, label) " 350 "VALUES " 351 "(:type, :req, 0, 4.0, " 352 "'Unnotarized Developer ID')"); 353 insert.bind(":type") = type; 354 insert.bind(":req") = req + unnotarized; 355 insert.execute(); 356 }; 357 358 migrateReq(this, 1, "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists"); 359 migrateReq(this, 2, "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and (certificate leaf[field.1.2.840.113635.100.6.1.14] or certificate leaf[field.1.2.840.113635.100.6.1.13])"); 360 migrateReq(this, 3, "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists"); 361 }); 362 363 simpleFeatureNoTransaction("legacy_devid_v2", ^{ 364 auto migrateReq = [](auto db, int type, string oldreq, string newreq) { 365 const string legacy = 366 " and legacy"; 367 368 SQLite::Statement update(*db, "UPDATE OR IGNORE authority " 369 "SET requirement = :newreq " 370 "WHERE requirement = :oldreq " 371 " AND type = :type " 372 " AND label = 'Developer ID'"); 373 update.bind(":oldreq") = oldreq; 374 update.bind(":type") = type; 375 update.bind(":newreq") = newreq; 376 update.execute(); 377 }; 378 379 // App handling has moved to the sunfish path. The legacy keyword won't work well for apps because we don't collect nested code hashes to whitelist them. 380 migrateReq(this, 2, 381 "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and (certificate leaf[field.1.2.840.113635.100.6.1.14] or certificate leaf[field.1.2.840.113635.100.6.1.13]) and (certificate leaf[timestamp.1.2.840.113635.100.6.1.33] absent or certificate leaf[timestamp.1.2.840.113635.100.6.1.33] < timestamp \"20190408000000Z\")", 382 "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and (certificate leaf[field.1.2.840.113635.100.6.1.14] or certificate leaf[field.1.2.840.113635.100.6.1.13]) and legacy"); 383 migrateReq(this, 3, 384 "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists and (certificate leaf[timestamp.1.2.840.113635.100.6.1.33] absent or certificate leaf[timestamp.1.2.840.113635.100.6.1.33] < timestamp \"20190408000000Z\")", 385 "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists and legacy"); 386 }); 387 388 simpleFeatureNoTransaction("unnotarized_without_timestamp", ^{ 389 auto migrateReq = [](auto db, int type, string req) { 390 const string to_remove = 391 " and (certificate leaf[timestamp.1.2.840.113635.100.6.1.33] exists and " 392 "certificate leaf[timestamp.1.2.840.113635.100.6.1.33] >= timestamp \"20190408000000Z\")"; 393 394 SQLite::Statement update(*db, "UPDATE OR IGNORE authority " 395 "SET requirement = :newreq " 396 "WHERE requirement = :oldreq " 397 " AND type = :type " 398 " AND label = 'Unnotarized Developer ID'"); 399 update.bind(":oldreq") = req + to_remove; 400 update.bind(":type") = type; 401 update.bind(":newreq") = req; 402 update.execute(); 403 }; 404 405 migrateReq(this, kAuthorityInstall, "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and (certificate leaf[field.1.2.840.113635.100.6.1.14] or certificate leaf[field.1.2.840.113635.100.6.1.13])"); 406 migrateReq(this, kAuthorityOpenDoc, "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists"); 407 }); 408 409 devIdRequirementUpgrades.commit(); 410 } 411 412 simpleFeature("notarized_documents", ^{ 413 SQLite::Statement addNotarizedDocs(*this, 414 "INSERT INTO authority (type, allow, flags, priority, label, requirement) " 415 " VALUES (3, 1, 2, 5.0, 'Notarized Developer ID', " 416 " 'anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists and notarized')"); 417 addNotarizedDocs.execute(); 418 }); 419 420 simpleFeature("notarization_priority_fix", ^{ 421 auto migrateReq = [](auto db, string label, float priority) { 422 SQLite::Statement update(*db, 423 "UPDATE OR IGNORE authority " 424 "SET priority = :newpriority " 425 "WHERE label = :label"); 426 update.bind(":newpriority") = priority; 427 update.bind(":label") = label; 428 update.execute(); 429 }; 430 migrateReq(this, "Developer ID", 4.0); 431 migrateReq(this, "Unnotarized Developer ID", 0.0); 432 }); 433 } 434 435 436 // 437 // Install Gatekeeper override (GKE) data. 438 // The arguments are paths to the authority and signature files. 439 // 440 void PolicyDatabase::installExplicitSet(const char *authfile, const char *sigfile) 441 { 442 // only try this every gkeCheckInterval seconds 443 time_t now = time(NULL); 444 if (mLastExplicitCheck + gkeCheckInterval > now) 445 return; 446 mLastExplicitCheck = now; 447 448 try { 449 if (CFRef<CFDataRef> authData = cfLoadFile(authfile)) { 450 CFDictionary auth(CFRef<CFDictionaryRef>(makeCFDictionaryFrom(authData)), errSecCSDbCorrupt); 451 CFDictionaryRef content = auth.get<CFDictionaryRef>(CFSTR("authority")); 452 std::string authUUID = cfString(auth.get<CFStringRef>(CFSTR("uuid"))); 453 if (authUUID.empty()) { 454 secinfo("gkupgrade", "no uuid in auth file; ignoring gke.auth"); 455 return; 456 } 457 std::string dbUUID; 458 SQLite::Statement uuidQuery(*this, "SELECT value FROM feature WHERE name='gke'"); 459 if (uuidQuery.nextRow()) 460 dbUUID = (const char *)uuidQuery[0]; 461 if (dbUUID == authUUID) { 462 secinfo("gkupgrade", "gke.auth already present, ignoring"); 463 return; 464 } 465 Syslog::notice("loading GKE %s (replacing %s)", authUUID.c_str(), dbUUID.empty() ? "nothing" : dbUUID.c_str()); 466 467 // first, load code signatures. This is pretty much idempotent 468 if (sigfile) 469 if (FILE *sigs = fopen(sigfile, "r")) { 470 unsigned count = 0; 471 SignatureDatabaseWriter db; 472 while (const BlobCore *blob = BlobCore::readBlob(sigs)) { 473 db.storeCode(blob, "<remote>"); 474 count++; 475 } 476 secinfo("gkupgrade", "%d detached signature(s) loaded from override data", count); 477 fclose(sigs); 478 } 479 480 // start transaction (atomic from here on out) 481 SQLite::Transaction loadAuth(*this, SQLite::Transaction::exclusive, "GKE_Upgrade"); 482 483 // purge prior authority data 484 SQLite::Statement purge(*this, "DELETE FROM authority WHERE flags & :flag"); 485 purge.bind(":flag") = kAuthorityFlagWhitelist; 486 purge(); 487 488 // load new data 489 CFIndex count = CFDictionaryGetCount(content); 490 vector<CFStringRef> keys_vector(count, NULL); 491 vector<CFDictionaryRef> values_vector(count, NULL); 492 CFDictionaryGetKeysAndValues(content, (const void **)keys_vector.data(), (const void **)values_vector.data()); 493 494 SQLite::Statement insert(*this, "INSERT INTO authority (type, allow, requirement, label, filter_unsigned, flags, remarks)" 495 " VALUES (:type, 1, :requirement, 'GKE', :filter, :flags, :path)"); 496 for (CFIndex n = 0; n < count; n++) { 497 CFDictionary info(values_vector[n], errSecCSDbCorrupt); 498 uint32_t flags = kAuthorityFlagWhitelist; 499 if (CFNumberRef versionRef = info.get<CFNumberRef>("version")) { 500 int version = cfNumber<int>(versionRef); 501 if (version >= 2) { 502 flags |= kAuthorityFlagWhitelistV2; 503 if (version >= 3) { 504 flags |= kAuthorityFlagWhitelistSHA256; 505 } 506 } 507 } 508 insert.reset(); 509 insert.bind(":type") = cfString(info.get<CFStringRef>(CFSTR("type"))); 510 insert.bind(":path") = cfString(info.get<CFStringRef>(CFSTR("path"))); 511 insert.bind(":requirement") = "cdhash H\"" + cfString(info.get<CFStringRef>(CFSTR("cdhash"))) + "\""; 512 insert.bind(":filter") = cfString(info.get<CFStringRef>(CFSTR("screen"))); 513 insert.bind(":flags").integer(flags); 514 insert(); 515 } 516 517 // we just changed the authority configuration at priority zero 518 this->purgeObjects(0); 519 520 // update version and commit 521 addFeature("gke", authUUID.c_str(), "gke loaded"); 522 loadAuth.commit(); 523 /* now that we have moved to a bundle for gke files, delete any old style files we find 524 This is really just a best effort cleanup, so we don't care about errors. */ 525 if (access(gkeAuthFile_old, F_OK) == 0) 526 { 527 if (unlink(gkeAuthFile_old) == 0) 528 { 529 Syslog::notice("Deleted old style gke file (%s)", gkeAuthFile_old); 530 } 531 } 532 if (access(gkeSigsFile_old, F_OK) == 0) 533 { 534 if (unlink(gkeSigsFile_old) == 0) 535 { 536 Syslog::notice("Deleted old style gke file (%s)", gkeSigsFile_old); 537 } 538 } 539 } 540 } catch (...) { 541 secinfo("gkupgrade", "exception during GKE upgrade"); 542 } 543 } 544 545 546 // 547 // Check the override-enable master flag 548 // 549 #define SP_ENABLE_KEY CFSTR("enabled") 550 #define SP_ENABLED CFSTR("yes") 551 #define SP_DISABLED CFSTR("no") 552 553 bool overrideAssessment(SecAssessmentFlags flags /* = 0 */) 554 { 555 static bool enabled = true; 556 static dispatch_once_t once; 557 static int token = -1; 558 static int have_token = 0; 559 static dispatch_queue_t queue; 560 int check; 561 562 if (flags & kSecAssessmentFlagEnforce) // explicitly disregard disables (force on) 563 return false; 564 565 if (have_token && notify_check(token, &check) == NOTIFY_STATUS_OK && !check) 566 return !enabled; 567 568 dispatch_once(&once, ^{ 569 if (notify_register_check(kNotifySecAssessmentMasterSwitch, &token) == NOTIFY_STATUS_OK) 570 have_token = 1; 571 queue = dispatch_queue_create("com.apple.SecAssessment.assessment", NULL); 572 }); 573 574 dispatch_sync(queue, ^{ 575 /* upgrade configuration from emir, ignore all error since we might not be able to write to */ 576 if (::access(visibleSecurityFlagFile, F_OK) == 0) { 577 try { 578 setAssessment(true); 579 ::unlink(visibleSecurityFlagFile); 580 } catch (...) { 581 } 582 enabled = true; 583 return; 584 } 585 586 try { 587 Dictionary * prefsDict = Dictionary::CreateDictionary(prefsFile); 588 if (prefsDict == NULL) 589 return; 590 591 CFStringRef value = prefsDict->getStringValue(SP_ENABLE_KEY); 592 if (value && CFStringCompare(value, SP_DISABLED, 0) == 0) 593 enabled = false; 594 else 595 enabled = true; 596 delete prefsDict; 597 } catch(...) { 598 } 599 }); 600 601 return !enabled; 602 } 603 604 void setAssessment(bool masterSwitch) 605 { 606 MutableDictionary *prefsDict = MutableDictionary::CreateMutableDictionary(prefsFile); 607 if (prefsDict == NULL) 608 prefsDict = new MutableDictionary(); 609 prefsDict->setValue(SP_ENABLE_KEY, masterSwitch ? SP_ENABLED : SP_DISABLED); 610 prefsDict->writePlistToFile(prefsFile); 611 delete prefsDict; 612 613 /* make sure permissions is right */ 614 ::chmod(prefsFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 615 616 notify_post(kNotifySecAssessmentMasterSwitch); 617 618 /* reset the automatic rearm timer */ 619 resetRearmTimer("masterswitch"); 620 } 621 622 623 // 624 // Reset or query the automatic rearm timer 625 // 626 void resetRearmTimer(const char *event) 627 { 628 CFRef<CFDateRef> now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); 629 CFTemp<CFDictionaryRef> info("{event=%s, timestamp=%O}", event, now.get()); 630 CFRef<CFDataRef> infoData = makeCFData(info.get()); 631 UnixPlusPlus::AutoFileDesc fd(rearmTimerFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 632 fd.write(CFDataGetBytePtr(infoData), CFDataGetLength(infoData)); 633 } 634 635 bool queryRearmTimer(CFTimeInterval &delta) 636 { 637 if (CFRef<CFDataRef> infoData = cfLoadFile(rearmTimerFile)) { 638 if (CFRef<CFDictionaryRef> info = makeCFDictionaryFrom(infoData)) { 639 CFDateRef timestamp = (CFDateRef)CFDictionaryGetValue(info, CFSTR("timestamp")); 640 if (timestamp && CFGetTypeID(timestamp) == CFDateGetTypeID()) { 641 delta = CFAbsoluteTimeGetCurrent() - CFDateGetAbsoluteTime(timestamp); 642 return true; 643 } 644 } 645 MacOSError::throwMe(errSecCSDbCorrupt); 646 } 647 return false; 648 } 649 650 651 } // end namespace CodeSigning 652 } // end namespace Security