+//
+// Perform an action on existing authority rule(s)
+//
+bool PolicyEngine::manipulateRules(const std::string &stanza,
+ CFTypeRef inTarget, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context)
+{
+ authorizeUpdate(flags, context);
+ CFDictionary ctx(context, errSecCSInvalidAttributeValues);
+ CFCopyRef<CFTypeRef> target = inTarget;
+ normalizeTarget(target, ctx);
+
+ string label;
+
+ if (CFStringRef lab = ctx.get<CFStringRef>(kSecAssessmentUpdateKeyLabel))
+ label = cfString(CFStringRef(lab));
+
+ SQLite::Transaction xact(*this, SQLite3::Transaction::deferred, "rule_change");
+ SQLite::Statement action(*this);
+ if (!target) {
+ if (label.empty()) // underspecified
+ MacOSError::throwMe(errSecCSInvalidObjectRef);
+ if (type == kAuthorityInvalid) {
+ action.query(stanza + " WHERE label = :label");
+ } else {
+ action.query(stanza + " WHERE type = :type AND label = :label");
+ action.bind(":type").integer(type);
+ }
+ action.bind(":label") = label.c_str();
+ } else if (CFGetTypeID(target) == CFNumberGetTypeID()) {
+ action.query(stanza + " WHERE id = :id");
+ action.bind(":id").integer(cfNumber<uint64_t>(target.as<CFNumberRef>()));
+ } else if (CFGetTypeID(target) == SecRequirementGetTypeID()) {
+ if (type == kAuthorityInvalid)
+ type = kAuthorityExecute;
+ CFRef<CFStringRef> requirementText;
+ MacOSError::check(SecRequirementCopyString(target.as<SecRequirementRef>(), kSecCSDefaultFlags, &requirementText.aref()));
+ action.query(stanza + " WHERE type = :type AND requirement = :requirement");
+ action.bind(":type").integer(type);
+ action.bind(":requirement") = requirementText.get();
+ } else
+ MacOSError::throwMe(errSecCSInvalidObjectRef);
+
+ action.execute();
+ unsigned int changes = this->changes(); // latch change count
+ // We MUST purge objects with priority <= MAX(priority of any changed rules);
+ // but for now we just get lazy and purge them ALL.
+ if (changes) {
+ this->purgeObjects(1.0E100);
+ xact.commit();
+ notify_post(kNotifySecAssessmentUpdate);
+ return true;
+ }
+ // no change; return an error
+ MacOSError::throwMe(errSecCSNoMatches);
+}
+
+
+bool PolicyEngine::remove(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context)
+{
+ if (type == kAuthorityOpenDoc) {
+ // handle document-open differently: use quarantine flags for whitelisting
+ authorizeUpdate(flags, context);
+ if (!target || CFGetTypeID(target) != CFURLGetTypeID())
+ MacOSError::throwMe(errSecCSInvalidObjectRef);
+ std::string spath = cfString(CFURLRef(target)).c_str();
+ FileQuarantine qtn(spath.c_str());
+ qtn.clearFlag(QTN_FLAG_ASSESSMENT_OK);
+ qtn.applyTo(spath.c_str());
+ return true;
+ }
+ return manipulateRules("DELETE FROM authority", target, type, flags, context);
+}
+
+bool PolicyEngine::enable(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context)
+{
+ return manipulateRules("UPDATE authority SET disabled = 0", target, type, flags, context);
+}
+
+bool PolicyEngine::disable(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context)
+{
+ return manipulateRules("UPDATE authority SET disabled = 1", target, type, flags, context);
+}
+
+
+bool PolicyEngine::update(CFTypeRef target, SecAssessmentFlags flags, CFDictionaryRef context)
+{
+ AuthorityType type = typeFor(context, kAuthorityInvalid);
+ CFStringRef edit = CFStringRef(CFDictionaryGetValue(context, kSecAssessmentContextKeyUpdate));
+ if (CFEqual(edit, kSecAssessmentUpdateOperationAdd))
+ return this->add(target, type, flags, context);
+ else if (CFEqual(edit, kSecAssessmentUpdateOperationRemove))
+ return this->remove(target, type, flags, context);
+ else if (CFEqual(edit, kSecAssessmentUpdateOperationEnable))
+ return this->enable(target, type, flags, context);
+ else if (CFEqual(edit, kSecAssessmentUpdateOperationDisable))
+ return this->disable(target, type, flags, context);
+ else
+ MacOSError::throwMe(errSecCSInvalidAttributeValues);
+}
+
+