/ OSX / libsecurity_codesigning / lib / policydb.cpp
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