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 simpleFeatureNoTransaction("legacy_devid_v2", ^{
364 auto migrateReq
= [](auto db
, int type
, string oldreq
, string newreq
) {
365 const string legacy
=
368 SQLite::Statement
update(*db
, "UPDATE OR IGNORE authority "
369 "SET requirement = :newreq "
370 "WHERE requirement = :oldreq "
372 " AND label = 'Developer ID'");
373 update
.bind(":oldreq") = oldreq
;
374 update
.bind(":type") = type
;
375 update
.bind(":newreq") = newreq
;
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.
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");
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");
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\")";
394 SQLite::Statement
update(*db
, "UPDATE OR IGNORE authority "
395 "SET requirement = :newreq "
396 "WHERE requirement = :oldreq "
398 " AND label = 'Unnotarized Developer ID'");
399 update
.bind(":oldreq") = req
+ to_remove
;
400 update
.bind(":type") = type
;
401 update
.bind(":newreq") = req
;
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");
409 devIdRequirementUpgrades
.commit();
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();
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
;
430 migrateReq(this, "Developer ID", 4.0);
431 migrateReq(this, "Unnotarized Developer ID", 0.0);
437 // Install Gatekeeper override (GKE) data.
438 // The arguments are paths to the authority and signature files.
440 void PolicyDatabase::installExplicitSet(const char *authfile
, const char *sigfile
)
442 // only try this every gkeCheckInterval seconds
443 time_t now
= time(NULL
);
444 if (mLastExplicitCheck
+ gkeCheckInterval
> now
)
446 mLastExplicitCheck
= now
;
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");
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");
465 Syslog::notice("loading GKE %s (replacing %s)", authUUID
.c_str(), dbUUID
.empty() ? "nothing" : dbUUID
.c_str());
467 // first, load code signatures. This is pretty much idempotent
469 if (FILE *sigs
= fopen(sigfile
, "r")) {
471 SignatureDatabaseWriter db
;
472 while (const BlobCore
*blob
= BlobCore::readBlob(sigs
)) {
473 db
.storeCode(blob
, "<remote>");
476 secinfo("gkupgrade", "%d detached signature(s) loaded from override data", count
);
480 // start transaction (atomic from here on out)
481 SQLite::Transaction
loadAuth(*this, SQLite::Transaction::exclusive
, "GKE_Upgrade");
483 // purge prior authority data
484 SQLite::Statement
purge(*this, "DELETE FROM authority WHERE flags & :flag");
485 purge
.bind(":flag") = kAuthorityFlagWhitelist
;
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());
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
);
502 flags
|= kAuthorityFlagWhitelistV2
;
504 flags
|= kAuthorityFlagWhitelistSHA256
;
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
);
517 // we just changed the authority configuration at priority zero
518 this->purgeObjects(0);
520 // update version and commit
521 addFeature("gke", authUUID
.c_str(), "gke loaded");
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)
527 if (unlink(gkeAuthFile_old
) == 0)
529 Syslog::notice("Deleted old style gke file (%s)", gkeAuthFile_old
);
532 if (access(gkeSigsFile_old
, F_OK
) == 0)
534 if (unlink(gkeSigsFile_old
) == 0)
536 Syslog::notice("Deleted old style gke file (%s)", gkeSigsFile_old
);
541 secinfo("gkupgrade", "exception during GKE upgrade");
547 // Check the override-enable master flag
549 #define SP_ENABLE_KEY CFSTR("enabled")
550 #define SP_ENABLED CFSTR("yes")
551 #define SP_DISABLED CFSTR("no")
553 bool overrideAssessment(SecAssessmentFlags flags
/* = 0 */)
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
;
562 if (flags
& kSecAssessmentFlagEnforce
) // explicitly disregard disables (force on)
565 if (have_token
&& notify_check(token
, &check
) == NOTIFY_STATUS_OK
&& !check
)
568 dispatch_once(&once
, ^{
569 if (notify_register_check(kNotifySecAssessmentMasterSwitch
, &token
) == NOTIFY_STATUS_OK
)
571 queue
= dispatch_queue_create("com.apple.SecAssessment.assessment", NULL
);
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) {
579 ::unlink(visibleSecurityFlagFile
);
587 Dictionary
* prefsDict
= Dictionary::CreateDictionary(prefsFile
);
588 if (prefsDict
== NULL
)
591 CFStringRef value
= prefsDict
->getStringValue(SP_ENABLE_KEY
);
592 if (value
&& CFStringCompare(value
, SP_DISABLED
, 0) == 0)
604 void setAssessment(bool masterSwitch
)
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
);
613 /* make sure permissions is right */
614 ::chmod(prefsFile
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
616 notify_post(kNotifySecAssessmentMasterSwitch
);
618 /* reset the automatic rearm timer */
619 resetRearmTimer("masterswitch");
624 // Reset or query the automatic rearm timer
626 void resetRearmTimer(const char *event
)
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
));
635 bool queryRearmTimer(CFTimeInterval
&delta
)
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
);
645 MacOSError::throwMe(errSecCSDbCorrupt
);
651 } // end namespace CodeSigning
652 } // end namespace Security