]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/policydb.cpp
Security-59306.140.5.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / policydb.cpp
index af49aa9e95e0da668c791e7a811442c547a4f83f..92d9cfa2a0be1d23c1696bf0c5291dc44c00df33 100644 (file)
@@ -219,12 +219,12 @@ void PolicyDatabase::addFeature(const char *name, const char *value, const char
 
 void PolicyDatabase::simpleFeature(const char *feature, void (^perform)())
 {
+       SQLite::Transaction update(*this);
        if (!hasFeature(feature)) {
-               SQLite::Transaction update(*this);
                perform();
                addFeature(feature, "upgraded", "upgraded");
-               update.commit();
        }
+       update.commit();
 }
 
 void PolicyDatabase::simpleFeature(const char *feature, const char *sql)
@@ -234,6 +234,14 @@ void PolicyDatabase::simpleFeature(const char *feature, const char *sql)
                perform.execute();
        });
 }
+       
+void PolicyDatabase::simpleFeatureNoTransaction(const char *feature, void (^perform)())
+{
+       if (!hasFeature(feature)) {
+               perform();
+               addFeature(feature, "upgraded", "upgraded");
+       }
+}
 
 
 void PolicyDatabase::upgradeDatabase()
@@ -282,6 +290,146 @@ void PolicyDatabase::upgradeDatabase()
                        "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')");
                addDevID.execute();
        });
+    
+    simpleFeature("root_only", ^{
+        UnixError::check(::chmod(dbPath(), S_IRUSR | S_IWUSR));
+    });
+
+       simpleFeature("notarized_apps", ^{
+
+               // Insert a set of notarization requirements for notarized applications and installers, with a priority that will be higher than developer id priorities
+               // so they are guaranteed to match first.
+               SQLite::Statement addNotarizedExecutables(*this,
+                       "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')");
+               addNotarizedExecutables.execute();
+
+               SQLite::Statement addNotarizedInstallers(*this,
+                       "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')");
+               addNotarizedInstallers.execute();
+
+               // Bump the priority on apple system, apple installer, and mac app store entries so they are evaluated before Developer ID variants.
+               // This is important because notarized variants meet the requirement of the Developer ID variant and would could match that too.
+               SQLite::Statement bumpAppleSystemPriority(*this,
+                         "UPDATE authority SET priority = 20.0 WHERE label = 'Apple System'");
+               bumpAppleSystemPriority.execute();
+
+               SQLite::Statement bumpAppleInstallerPriority(*this,
+                         "UPDATE authority SET priority = 20.0 WHERE label = 'Apple Installer'");
+               bumpAppleInstallerPriority.execute();
+
+               SQLite::Statement bumpMacAppStorePriority(*this,
+                         "UPDATE authority SET priority = 10.0 WHERE label = 'Mac App Store'");
+               bumpMacAppStorePriority.execute();
+       });
+       
+       {
+               SQLite::Transaction devIdRequirementUpgrades(*this);
+               
+               simpleFeatureNoTransaction("legacy_devid", ^{
+                       auto migrateReq = [](auto db, int type, string req) {
+                               const string legacy =
+                               " 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\")";
+                               
+                               const string unnotarized =
+                               " and (certificate leaf[timestamp.1.2.840.113635.100.6.1.33] exists and "
+                               "certificate leaf[timestamp.1.2.840.113635.100.6.1.33] >= timestamp \"20190408000000Z\")";
+                               
+                               SQLite::Statement update(*db, "UPDATE OR IGNORE authority "
+                                                                                "SET requirement = :newreq "
+                                                                                "WHERE requirement = :oldreq "
+                                                                                "      AND type = :type "
+                                                                                "      AND label = 'Developer ID'");
+                               update.bind(":oldreq") = req;
+                               update.bind(":type") = type;
+                               update.bind(":newreq") = req + legacy;
+                               update.execute();
+                               
+                               SQLite::Statement insert(*db, "INSERT OR IGNORE INTO authority "
+                                                                                "(type, requirement, allow, priority, label) "
+                                                                                "VALUES "
+                                                                                "(:type, :req, 0, 4.0, "
+                                                                                "'Unnotarized Developer ID')");
+                               insert.bind(":type") = type;
+                               insert.bind(":req") = req + unnotarized;
+                               insert.execute();
+                       };
+                       
+                       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");
+                       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])");
+                       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");
+               });
+       
+               simpleFeatureNoTransaction("legacy_devid_v2", ^{
+                       auto migrateReq = [](auto db, int type, string oldreq, string newreq) {
+                               const string legacy =
+                               " and legacy";
+
+                               SQLite::Statement update(*db, "UPDATE OR IGNORE authority "
+                                                                                "SET requirement = :newreq "
+                                                                                "WHERE requirement = :oldreq "
+                                                                                "      AND type = :type "
+                                                                                "      AND label = 'Developer ID'");
+                               update.bind(":oldreq") = oldreq;
+                               update.bind(":type") = type;
+                               update.bind(":newreq") = newreq;
+                               update.execute();
+                       };
+
+                       // 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.
+                       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]) 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\")",
+                                          "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");
+                       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 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\")",
+                                          "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");
+               });
+               
+               simpleFeatureNoTransaction("unnotarized_without_timestamp", ^{
+                       auto migrateReq = [](auto db, int type, string req) {
+                               const string to_remove =
+                               " and (certificate leaf[timestamp.1.2.840.113635.100.6.1.33] exists and "
+                               "certificate leaf[timestamp.1.2.840.113635.100.6.1.33] >= timestamp \"20190408000000Z\")";
+                               
+                               SQLite::Statement update(*db, "UPDATE OR IGNORE authority "
+                                                                                "SET requirement = :newreq "
+                                                                                "WHERE requirement = :oldreq "
+                                                                                "      AND type = :type "
+                                                                                "      AND label = 'Unnotarized Developer ID'");
+                               update.bind(":oldreq") = req + to_remove;
+                               update.bind(":type") = type;
+                               update.bind(":newreq") = req;
+                               update.execute();
+                       };
+                       
+                       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])");
+                       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");
+               });
+               
+               devIdRequirementUpgrades.commit();
+       }
+       
+       simpleFeature("notarized_documents", ^{
+               SQLite::Statement addNotarizedDocs(*this,
+                                                                                  "INSERT INTO authority (type, allow, flags, priority, label, requirement) "
+                                                                                  "  VALUES (3, 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')");
+               addNotarizedDocs.execute();
+       });
+
+       simpleFeature("notarization_priority_fix", ^{
+               auto migrateReq = [](auto db, string label, float priority) {
+                       SQLite::Statement update(*db,
+                                                                        "UPDATE OR IGNORE authority "
+                                                                        "SET priority = :newpriority "
+                                                                        "WHERE label = :label");
+                       update.bind(":newpriority") = priority;
+                       update.bind(":label") = label;
+                       update.execute();
+               };
+               migrateReq(this, "Developer ID", 4.0);
+               migrateReq(this, "Unnotarized Developer ID", 0.0);
+       });
 }
 
 
@@ -303,7 +451,7 @@ void PolicyDatabase::installExplicitSet(const char *authfile, const char *sigfil
                        CFDictionaryRef content = auth.get<CFDictionaryRef>(CFSTR("authority"));
                        std::string authUUID = cfString(auth.get<CFStringRef>(CFSTR("uuid")));
                        if (authUUID.empty()) {
-                               secdebug("gkupgrade", "no uuid in auth file; ignoring gke.auth");
+                               secinfo("gkupgrade", "no uuid in auth file; ignoring gke.auth");
                                return;
                        }
                        std::string dbUUID;
@@ -311,7 +459,7 @@ void PolicyDatabase::installExplicitSet(const char *authfile, const char *sigfil
                        if (uuidQuery.nextRow())
                                dbUUID = (const char *)uuidQuery[0];
                        if (dbUUID == authUUID) {
-                               secdebug("gkupgrade", "gke.auth already present, ignoring");
+                               secinfo("gkupgrade", "gke.auth already present, ignoring");
                                return;
                        }
                        Syslog::notice("loading GKE %s (replacing %s)", authUUID.c_str(), dbUUID.empty() ? "nothing" : dbUUID.c_str());
@@ -325,7 +473,7 @@ void PolicyDatabase::installExplicitSet(const char *authfile, const char *sigfil
                                                db.storeCode(blob, "<remote>");
                                                count++;
                                        }
-                                       secdebug("gkupgrade", "%d detached signature(s) loaded from override data", count);
+                                       secinfo("gkupgrade", "%d detached signature(s) loaded from override data", count);
                                        fclose(sigs);
                                }
                        
@@ -339,19 +487,23 @@ void PolicyDatabase::installExplicitSet(const char *authfile, const char *sigfil
                        
                        // load new data
                        CFIndex count = CFDictionaryGetCount(content);
-                       CFStringRef keys[count];
-                       CFDictionaryRef values[count];
-                       CFDictionaryGetKeysAndValues(content, (const void **)keys, (const void **)values);
+                       vector<CFStringRef> keys_vector(count, NULL);
+                       vector<CFDictionaryRef> values_vector(count, NULL);
+                       CFDictionaryGetKeysAndValues(content, (const void **)keys_vector.data(), (const void **)values_vector.data());
                        
                        SQLite::Statement insert(*this, "INSERT INTO authority (type, allow, requirement, label, filter_unsigned, flags, remarks)"
                                " VALUES (:type, 1, :requirement, 'GKE', :filter, :flags, :path)");
                        for (CFIndex n = 0; n < count; n++) {
-                               CFDictionary info(values[n], errSecCSDbCorrupt);
+                               CFDictionary info(values_vector[n], errSecCSDbCorrupt);
                                uint32_t flags = kAuthorityFlagWhitelist;
                                if (CFNumberRef versionRef = info.get<CFNumberRef>("version")) {
                                        int version = cfNumber<int>(versionRef);
-                                       if (version >= 2)
+                                       if (version >= 2) {
                                                flags |= kAuthorityFlagWhitelistV2;
+                                               if (version >= 3) {
+                                                       flags |= kAuthorityFlagWhitelistSHA256;
+                                               }
+                                       }
                                }
                                insert.reset();
                                insert.bind(":type") = cfString(info.get<CFStringRef>(CFSTR("type")));
@@ -368,9 +520,25 @@ void PolicyDatabase::installExplicitSet(const char *authfile, const char *sigfil
                        // update version and commit
                        addFeature("gke", authUUID.c_str(), "gke loaded");
                        loadAuth.commit();
+            /* now that we have moved to a bundle for gke files, delete any old style files we find
+               This is really just a best effort cleanup, so we don't care about errors. */
+            if (access(gkeAuthFile_old, F_OK) == 0)
+            {
+                if (unlink(gkeAuthFile_old) == 0)
+                {
+                    Syslog::notice("Deleted old style gke file (%s)", gkeAuthFile_old);
+                }
+            }
+            if (access(gkeSigsFile_old, F_OK) == 0)
+            {
+                if (unlink(gkeSigsFile_old) == 0)
+                {
+                    Syslog::notice("Deleted old style gke file (%s)", gkeSigsFile_old);
+                }
+            }
                }
        } catch (...) {
-               secdebug("gkupgrade", "exception during GKE upgrade");
+               secinfo("gkupgrade", "exception during GKE upgrade");
        }
 }
 
@@ -437,7 +605,7 @@ void setAssessment(bool masterSwitch)
 {
        MutableDictionary *prefsDict = MutableDictionary::CreateMutableDictionary(prefsFile);
        if (prefsDict == NULL)
-               prefsDict = new MutableDictionary::MutableDictionary();
+               prefsDict = new MutableDictionary();
        prefsDict->setValue(SP_ENABLE_KEY, masterSwitch ? SP_ENABLED : SP_DISABLED);
        prefsDict->writePlistToFile(prefsFile);
        delete prefsDict;