2  * Copyright (c) 2011-2013 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  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" 
  35 #include <dispatch/dispatch.h> 
  36 #include <sys/types.h> 
  41 namespace CodeSigning 
{ 
  44 using namespace SQLite
; 
  48 // Determine the database path 
  50 static const char *dbPath() 
  52         if (const char *s 
= getenv("SYSPOLICYDATABASE")) 
  54         return defaultDatabase
; 
  59 // Help mapping API-ish CFString keys to more convenient internal enumerations 
  62         const CFStringRef 
&cstring
; 
  66 static uint 
mapEnum(CFDictionaryRef context
, CFStringRef attr
, const StringMap 
*map
, uint value 
= 0) 
  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
; 
  76 static const StringMap mapType
[] = { 
  77         { kSecAssessmentOperationTypeExecute
, kAuthorityExecute 
}, 
  78         { kSecAssessmentOperationTypeInstall
, kAuthorityInstall 
}, 
  79         { kSecAssessmentOperationTypeOpenDocument
, kAuthorityOpenDoc 
}, 
  83 AuthorityType 
typeFor(CFDictionaryRef context
, AuthorityType type 
/* = kAuthorityInvalid */) 
  85         return mapEnum(context
, kSecAssessmentContextKeyOperation
, mapType
, type
); 
  88 CFStringRef 
typeNameFor(AuthorityType type
) 
  90         for (const StringMap 
*mp 
= mapType
; mp
->cstring
; ++mp
) 
  91                 if (type 
== mp
->enumeration
) 
  93         return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("type %d"), type
); 
 100 PolicyDatabase::PolicyDatabase(const char *path
, int flags
) 
 101         : SQLite::Database(path 
? path 
: dbPath(), flags
), 
 102           mLastExplicitCheck(0) 
 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"); 
 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
) 
 114                         installExplicitSet(gkeAuthFile
, gkeSigsFile
); 
 119 PolicyDatabase::~PolicyDatabase() 
 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. 
 127 bool PolicyDatabase::checkCache(CFURLRef path
, AuthorityType type
, SecAssessmentFlags flags
, CFMutableDictionaryRef result
) 
 129         // we currently don't use the cache for anything but execution rules 
 130         if (type 
!= kAuthorityExecute
) 
 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
)); 
 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(); 
 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 
 157                 if (allow 
&& !overrideAssessment(flags
)) 
 158                     MacOSError::check(SecStaticCodeCheckValidity(code
, kSecCSDefaultFlags
, NULL
)); 
 160                 cfadd(result
, "{%O=%B}", kSecAssessmentAssessmentVerdict
, allow
); 
 161                 PolicyEngine::addAuthority(flags
, result
, label
, auth
, kCFBooleanTrue
); 
 169 // Purge the object cache of all expired entries. 
 170 // These are meant to run within the caller's transaction. 
 172 void PolicyDatabase::purgeAuthority() 
 174         SQLite::Statement 
cleaner(*this, 
 175                 "DELETE FROM authority WHERE expires <= JULIANDAY('now');"); 
 179 void PolicyDatabase::purgeObjects() 
 181         SQLite::Statement 
cleaner(*this, 
 182                 "DELETE FROM object WHERE expires <= JULIANDAY('now');"); 
 186 void PolicyDatabase::purgeObjects(double priority
) 
 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
; 
 196 // Database migration 
 198 std::string 
PolicyDatabase::featureLevel(const char *name
) 
 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]) 
 206                         return "default";       // old engineering versions may have NULL values; tolerate this 
 208         return "";              // new feature (no level) 
 211 void PolicyDatabase::addFeature(const char *name
, const char *value
, const char *remarks
) 
 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
; 
 220 void PolicyDatabase::simpleFeature(const char *feature
, void (^perform
)()) 
 222         SQLite::Transaction 
update(*this); 
 223         if (!hasFeature(feature
)) { 
 225                 addFeature(feature
, "upgraded", "upgraded"); 
 230 void PolicyDatabase::simpleFeature(const char *feature
, const char *sql
) 
 232         simpleFeature(feature
, ^{ 
 233                 SQLite::Statement 
perform(*this, sql
); 
 238 void PolicyDatabase::simpleFeatureNoTransaction(const char *feature
, void (^perform
)()) 
 240         if (!hasFeature(feature
)) { 
 242                 addFeature(feature
, "upgraded", "upgraded"); 
 247 void PolicyDatabase::upgradeDatabase() 
 249         simpleFeature("bookmarkhints", 
 250                 "CREATE TABLE bookmarkhints (" 
 251                         "  id INTEGER PRIMARY KEY AUTOINCREMENT, " 
 253                         "  authority INTEGER NOT NULL" 
 254                         "     REFERENCES authority(id) ON DELETE CASCADE" 
 257         simpleFeature("codesignedpackages", ^{ 
 258                 SQLite::Statement 
update(*this, 
 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
; 
 267         simpleFeature("filter_unsigned", 
 268                 "ALTER TABLE authority ADD COLUMN filter_unsigned TEXT NULL" 
 271         simpleFeature("strict_apple_installer", ^{ 
 272                 SQLite::Statement 
update(*this, 
 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
; 
 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
; 
 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')"); 
 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')"); 
 294     simpleFeature("root_only", ^{ 
 295         UnixError::check(::chmod(dbPath(), S_IRUSR 
| S_IWUSR
)); 
 298         simpleFeature("notarized_apps", ^{ 
 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(); 
 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(); 
 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(); 
 316                 SQLite::Statement 
bumpAppleInstallerPriority(*this, 
 317                           "UPDATE authority SET priority = 20.0 WHERE label = 'Apple Installer'"); 
 318                 bumpAppleInstallerPriority
.execute(); 
 320                 SQLite::Statement 
bumpMacAppStorePriority(*this, 
 321                           "UPDATE authority SET priority = 10.0 WHERE label = 'Mac App Store'"); 
 322                 bumpMacAppStorePriority
.execute(); 
 326                 SQLite::Transaction 
devIdRequirementUpgrades(*this); 
 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\")"; 
 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\")"; 
 338                                 SQLite::Statement 
update(*db
, "UPDATE OR IGNORE authority " 
 339                                                                                  "SET requirement = :newreq " 
 340                                                                                  "WHERE requirement = :oldreq " 
 342                                                                                  "      AND label = 'Developer ID'"); 
 343                                 update
.bind(":oldreq") = req
; 
 344                                 update
.bind(":type") = type
; 
 345                                 update
.bind(":newreq") = req 
+ legacy
; 
 348                                 SQLite::Statement 
insert(*db
, "INSERT OR IGNORE INTO authority " 
 349                                                                                  "(type, requirement, allow, priority, label) " 
 351                                                                                  "(:type, :req, 0, 4.0, " 
 352                                                                                  "'Unnotarized Developer ID')"); 
 353                                 insert
.bind(":type") = type
; 
 354                                 insert
.bind(":req") = req 
+ unnotarized
; 
 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"); 
 363                 // Add simpleFeatureNoTransaction for going from the requirements create above, to add secure timestamps in requirements, here before the commit 
 365                 devIdRequirementUpgrades
.commit(); 
 368         simpleFeature("notarized_documents", ^{ 
 369                 SQLite::Statement 
addNotarizedDocs(*this, 
 370                                                                                    "INSERT INTO authority (type, allow, flags, priority, label, requirement) " 
 371                                                                                    "  VALUES (3, 1, 2, 5.0, 'Notarized Developer ID', " 
 372                                                                                    "          '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')"); 
 373                 addNotarizedDocs
.execute(); 
 379 // Install Gatekeeper override (GKE) data. 
 380 // The arguments are paths to the authority and signature files. 
 382 void PolicyDatabase::installExplicitSet(const char *authfile
, const char *sigfile
) 
 384         // only try this every gkeCheckInterval seconds 
 385         time_t now 
= time(NULL
); 
 386         if (mLastExplicitCheck 
+ gkeCheckInterval 
> now
) 
 388         mLastExplicitCheck 
= now
; 
 391                 if (CFRef
<CFDataRef
> authData 
= cfLoadFile(authfile
)) { 
 392                         CFDictionary 
auth(CFRef
<CFDictionaryRef
>(makeCFDictionaryFrom(authData
)), errSecCSDbCorrupt
); 
 393                         CFDictionaryRef content 
= auth
.get
<CFDictionaryRef
>(CFSTR("authority")); 
 394                         std::string authUUID 
= cfString(auth
.get
<CFStringRef
>(CFSTR("uuid"))); 
 395                         if (authUUID
.empty()) { 
 396                                 secinfo("gkupgrade", "no uuid in auth file; ignoring gke.auth"); 
 400                         SQLite::Statement 
uuidQuery(*this, "SELECT value FROM feature WHERE name='gke'"); 
 401                         if (uuidQuery
.nextRow()) 
 402                                 dbUUID 
= (const char *)uuidQuery
[0]; 
 403                         if (dbUUID 
== authUUID
) { 
 404                                 secinfo("gkupgrade", "gke.auth already present, ignoring"); 
 407                         Syslog::notice("loading GKE %s (replacing %s)", authUUID
.c_str(), dbUUID
.empty() ? "nothing" : dbUUID
.c_str()); 
 409                         // first, load code signatures. This is pretty much idempotent 
 411                                 if (FILE *sigs 
= fopen(sigfile
, "r")) { 
 413                                     SignatureDatabaseWriter db
; 
 414                                         while (const BlobCore 
*blob 
= BlobCore::readBlob(sigs
)) { 
 415                                                 db
.storeCode(blob
, "<remote>"); 
 418                                         secinfo("gkupgrade", "%d detached signature(s) loaded from override data", count
); 
 422                         // start transaction (atomic from here on out) 
 423                         SQLite::Transaction 
loadAuth(*this, SQLite::Transaction::exclusive
, "GKE_Upgrade"); 
 425                         // purge prior authority data 
 426                         SQLite::Statement 
purge(*this, "DELETE FROM authority WHERE flags & :flag"); 
 427                         purge
.bind(":flag") = kAuthorityFlagWhitelist
; 
 431                         CFIndex count 
= CFDictionaryGetCount(content
); 
 432                         vector
<CFStringRef
> keys_vector(count
, NULL
); 
 433                         vector
<CFDictionaryRef
> values_vector(count
, NULL
); 
 434                         CFDictionaryGetKeysAndValues(content
, (const void **)keys_vector
.data(), (const void **)values_vector
.data()); 
 436                         SQLite::Statement 
insert(*this, "INSERT INTO authority (type, allow, requirement, label, filter_unsigned, flags, remarks)" 
 437                                 " VALUES (:type, 1, :requirement, 'GKE', :filter, :flags, :path)"); 
 438                         for (CFIndex n 
= 0; n 
< count
; n
++) { 
 439                                 CFDictionary 
info(values_vector
[n
], errSecCSDbCorrupt
); 
 440                                 uint32_t flags 
= kAuthorityFlagWhitelist
; 
 441                                 if (CFNumberRef versionRef 
= info
.get
<CFNumberRef
>("version")) { 
 442                                         int version 
= cfNumber
<int>(versionRef
); 
 444                                                 flags 
|= kAuthorityFlagWhitelistV2
; 
 446                                                         flags 
|= kAuthorityFlagWhitelistSHA256
; 
 451                                 insert
.bind(":type") = cfString(info
.get
<CFStringRef
>(CFSTR("type"))); 
 452                                 insert
.bind(":path") = cfString(info
.get
<CFStringRef
>(CFSTR("path"))); 
 453                                 insert
.bind(":requirement") = "cdhash H\"" + cfString(info
.get
<CFStringRef
>(CFSTR("cdhash"))) + "\""; 
 454                                 insert
.bind(":filter") = cfString(info
.get
<CFStringRef
>(CFSTR("screen"))); 
 455                                 insert
.bind(":flags").integer(flags
); 
 459                         // we just changed the authority configuration at priority zero 
 460                         this->purgeObjects(0); 
 462                         // update version and commit 
 463                         addFeature("gke", authUUID
.c_str(), "gke loaded"); 
 465             /* now that we have moved to a bundle for gke files, delete any old style files we find 
 466                This is really just a best effort cleanup, so we don't care about errors. */ 
 467             if (access(gkeAuthFile_old
, F_OK
) == 0) 
 469                 if (unlink(gkeAuthFile_old
) == 0) 
 471                     Syslog::notice("Deleted old style gke file (%s)", gkeAuthFile_old
); 
 474             if (access(gkeSigsFile_old
, F_OK
) == 0) 
 476                 if (unlink(gkeSigsFile_old
) == 0) 
 478                     Syslog::notice("Deleted old style gke file (%s)", gkeSigsFile_old
); 
 483                 secinfo("gkupgrade", "exception during GKE upgrade"); 
 489 // Check the override-enable master flag 
 491 #define SP_ENABLE_KEY CFSTR("enabled") 
 492 #define SP_ENABLED CFSTR("yes") 
 493 #define SP_DISABLED CFSTR("no") 
 495 bool overrideAssessment(SecAssessmentFlags flags 
/* = 0 */) 
 497         static bool enabled 
= true; 
 498         static dispatch_once_t once
; 
 499         static int token 
= -1; 
 500         static int have_token 
= 0; 
 501         static dispatch_queue_t queue
; 
 504         if (flags 
& kSecAssessmentFlagEnforce
)  // explicitly disregard disables (force on) 
 507         if (have_token 
&& notify_check(token
, &check
) == NOTIFY_STATUS_OK 
&& !check
) 
 510         dispatch_once(&once
, ^{ 
 511                 if (notify_register_check(kNotifySecAssessmentMasterSwitch
, &token
) == NOTIFY_STATUS_OK
) 
 513                 queue 
= dispatch_queue_create("com.apple.SecAssessment.assessment", NULL
); 
 516         dispatch_sync(queue
, ^{ 
 517                 /* upgrade configuration from emir, ignore all error since we might not be able to write to */ 
 518                 if (::access(visibleSecurityFlagFile
, F_OK
) == 0) { 
 521                                 ::unlink(visibleSecurityFlagFile
); 
 529                         Dictionary 
* prefsDict 
= Dictionary::CreateDictionary(prefsFile
); 
 530                         if (prefsDict 
== NULL
) 
 533                         CFStringRef value 
= prefsDict
->getStringValue(SP_ENABLE_KEY
); 
 534                         if (value 
&& CFStringCompare(value
, SP_DISABLED
, 0) == 0) 
 546 void setAssessment(bool masterSwitch
) 
 548         MutableDictionary 
*prefsDict 
= MutableDictionary::CreateMutableDictionary(prefsFile
); 
 549         if (prefsDict 
== NULL
) 
 550                 prefsDict 
= new MutableDictionary(); 
 551         prefsDict
->setValue(SP_ENABLE_KEY
, masterSwitch 
? SP_ENABLED 
: SP_DISABLED
); 
 552         prefsDict
->writePlistToFile(prefsFile
); 
 555         /* make sure permissions is right */ 
 556         ::chmod(prefsFile
, S_IRUSR 
| S_IWUSR 
| S_IRGRP 
| S_IROTH
); 
 558         notify_post(kNotifySecAssessmentMasterSwitch
); 
 560         /* reset the automatic rearm timer */ 
 561         resetRearmTimer("masterswitch"); 
 566 // Reset or query the automatic rearm timer 
 568 void resetRearmTimer(const char *event
) 
 570         CFRef
<CFDateRef
> now 
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent()); 
 571         CFTemp
<CFDictionaryRef
> info("{event=%s, timestamp=%O}", event
, now
.get()); 
 572         CFRef
<CFDataRef
> infoData 
= makeCFData(info
.get()); 
 573         UnixPlusPlus::AutoFileDesc 
fd(rearmTimerFile
, O_WRONLY 
| O_CREAT 
| O_TRUNC
, 0644); 
 574         fd
.write(CFDataGetBytePtr(infoData
), CFDataGetLength(infoData
)); 
 577 bool queryRearmTimer(CFTimeInterval 
&delta
) 
 579         if (CFRef
<CFDataRef
> infoData 
= cfLoadFile(rearmTimerFile
)) { 
 580                 if (CFRef
<CFDictionaryRef
> info 
= makeCFDictionaryFrom(infoData
)) { 
 581                         CFDateRef timestamp 
= (CFDateRef
)CFDictionaryGetValue(info
, CFSTR("timestamp")); 
 582                         if (timestamp 
&& CFGetTypeID(timestamp
) == CFDateGetTypeID()) { 
 583                                 delta 
= CFAbsoluteTimeGetCurrent() - CFDateGetAbsoluteTime(timestamp
); 
 587                 MacOSError::throwMe(errSecCSDbCorrupt
); 
 593 } // end namespace CodeSigning 
 594 } // end namespace Security