]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/policydb.cpp
Security-59306.61.1.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / 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