/ OSX / libsecurity_codesigning / lib / SecAssessment.cpp
SecAssessment.cpp
  1  /*
  2   * Copyright (c) 2011-2014 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 "SecAssessment.h"
 25  #include "policydb.h"
 26  #include "policyengine.h"
 27  #include "xpcengine.h"
 28  #include "csutilities.h"
 29  #include "xar++.h"
 30  #include <CoreFoundation/CFRuntime.h>
 31  #include <CoreFoundation/CFBundlePriv.h>
 32  #include <security_utilities/globalizer.h>
 33  #include <security_utilities/unix++.h>
 34  #include <security_utilities/cfmunge.h>
 35  #include <notify.h>
 36  
 37  using namespace CodeSigning;
 38  
 39  //
 40  // CF Objects
 41  //
 42  struct _SecAssessment : private CFRuntimeBase {
 43  public:
 44  	_SecAssessment(CFURLRef p, AuthorityType typ, CFDictionaryRef r) : path(p), type(typ), result(r) { }
 45  	
 46  	CFCopyRef<CFURLRef> path;
 47  	AuthorityType type;
 48  	CFRef<CFDictionaryRef> result;
 49  
 50  public:
 51  	static _SecAssessment &ref(SecAssessmentRef r)
 52  		{ return *(_SecAssessment *)r; }
 53  
 54  	// CF Boiler-plate
 55  	void *operator new (size_t size)
 56  	{
 57  		return (void *)_CFRuntimeCreateInstance(NULL, SecAssessmentGetTypeID(),
 58  			sizeof(_SecAssessment) - sizeof(CFRuntimeBase), NULL);
 59  	}
 60  	
 61  	static void finalize(CFTypeRef obj)
 62  	{ ((_SecAssessment *)obj)->~_SecAssessment(); }
 63  };
 64  
 65  typedef _SecAssessment SecAssessment;
 66  
 67  
 68  static const CFRuntimeClass assessmentClass = {
 69  	0,								// version
 70  	"SecAssessment",				// name
 71  	NULL,							// init
 72  	NULL,							// copy
 73  	SecAssessment::finalize,		// finalize
 74  	NULL,							// equal
 75  	NULL,							// hash
 76  	NULL,							// formatting
 77  	NULL							// debug string
 78  };
 79  
 80  
 81  static dispatch_once_t assessmentOnce;
 82  CFTypeID assessmentType = _kCFRuntimeNotATypeID;
 83  	
 84  CFTypeID SecAssessmentGetTypeID()
 85  {
 86  	dispatch_once(&assessmentOnce, ^void() {
 87  		if ((assessmentType = _CFRuntimeRegisterClass(&assessmentClass)) == _kCFRuntimeNotATypeID)
 88  			abort();
 89  	});
 90  	return assessmentType;
 91  }
 92  
 93  
 94  //
 95  // Common dictionary constants
 96  //
 97  CFStringRef kSecAssessmentContextKeyOperation = CFSTR("operation");
 98  CFStringRef kSecAssessmentOperationTypeExecute = CFSTR("operation:execute");
 99  CFStringRef kSecAssessmentOperationTypeInstall = CFSTR("operation:install");
100  CFStringRef kSecAssessmentOperationTypeOpenDocument = CFSTR("operation:lsopen");
101  
102  
103  //
104  // Read-only in-process access to the policy database
105  //
106  class ReadPolicy : public PolicyDatabase {
107  public:
108  	ReadPolicy() : PolicyDatabase(defaultDatabase) { }
109  };
110  ModuleNexus<ReadPolicy> gDatabase;
111  
112  
113  //
114  // An on-demand instance of the policy engine
115  //
116  ModuleNexus<PolicyEngine> gEngine;
117  
118  
119  //
120  // Policy evaluation ("assessment") operations
121  //
122  CFStringRef kSecAssessmentContextKeyUTI = CFSTR("context:uti");
123  
124  CFStringRef kSecAssessmentContextKeyFeedback = CFSTR("context:feedback");
125  CFStringRef kSecAssessmentFeedbackProgress = CFSTR("feedback:progress");
126  CFStringRef kSecAssessmentFeedbackInfoCurrent = CFSTR("current");
127  CFStringRef kSecAssessmentFeedbackInfoTotal = CFSTR("total");
128  
129  CFStringRef kSecAssessmentContextKeyPrimarySignature = CFSTR("context:primary-signature");
130  
131  CFStringRef kSecAssessmentAssessmentVerdict = CFSTR("assessment:verdict");
132  CFStringRef kSecAssessmentAssessmentOriginator = CFSTR("assessment:originator");
133  CFStringRef kSecAssessmentAssessmentAuthority = CFSTR("assessment:authority");
134  CFStringRef kSecAssessmentAssessmentSource = CFSTR("assessment:authority:source");
135  CFStringRef kSecAssessmentAssessmentAuthorityRow = CFSTR("assessment:authority:row");
136  CFStringRef kSecAssessmentAssessmentAuthorityOverride = CFSTR("assessment:authority:override");
137  CFStringRef kSecAssessmentAssessmentAuthorityOriginalVerdict = CFSTR("assessment:authority:verdict");
138  CFStringRef kSecAssessmentAssessmentAuthorityFlags = CFSTR("assessment:authority:flags");
139  CFStringRef kSecAssessmentAssessmentFromCache = CFSTR("assessment:authority:cached");
140  CFStringRef kSecAssessmentAssessmentWeakSignature = CFSTR("assessment:authority:weak");
141  CFStringRef kSecAssessmentAssessmentCodeSigningError = CFSTR("assessment:cserror");
142  CFStringRef kSecAssessmentAssessmentNotarizationDate = CFSTR("assessment:notarization-date");
143  
144  CFStringRef kDisabledOverride = CFSTR("security disabled");
145  
146  SecAssessmentRef SecAssessmentCreate(CFURLRef path,
147  	SecAssessmentFlags flags,
148  	CFDictionaryRef context,
149  	CFErrorRef *errors)
150  {
151  	BEGIN_CSAPI
152  	
153  	if (flags & kSecAssessmentFlagAsynchronous)
154  		MacOSError::throwMe(errSecCSUnimplemented);
155  	
156  	AuthorityType type = typeFor(context, kAuthorityExecute);
157  	CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
158  
159  	SYSPOLICY_ASSESS_API(cfString(path).c_str(), int(type), flags);
160  
161  	try {
162  		if (flags & kSecAssessmentFlagDirect) {
163  			// ask the engine right here to do its thing
164  			SYSPOLICY_ASSESS_LOCAL();
165  			gEngine().evaluate(path, type, flags, context, result);
166  		} else {
167  			// relay the question to our daemon for consideration
168  			SYSPOLICY_ASSESS_REMOTE();
169  			xpcEngineAssess(path, flags, context, result);
170  		}
171  	} catch (CommonError &error) {
172  		switch (error.osStatus()) {
173  		case CSSMERR_TP_CERT_REVOKED:
174  			throw;
175  		default:
176  			if (!overrideAssessment(flags))
177  				throw;		// let it go as an error
178  			break;
179  		}
180  		// record the error we would have returned
181  		cfadd(result, "{%O=#F,'assessment:error'=%d}}", kSecAssessmentAssessmentVerdict, error.osStatus());
182  	} catch (...) {
183  		// catch stray errors not conforming to the CommonError scheme
184  		if (!overrideAssessment(flags))
185  			throw;		// let it go as an error
186  		cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
187  	}
188  
189  	return new SecAssessment(path, type, result.yield());
190  
191  	END_CSAPI_ERRORS1(NULL)
192  }
193  
194  
195  static void traceResult(CFURLRef target, MessageTrace &trace, std::string &sanitized)
196  {
197  	static const char *interestingBundles[] = {
198  		"UNBUNDLED",
199  		"com.apple.",
200  		"com.install4j.",
201  		"com.MindVision.",
202  		"com.yourcompany.",
203  
204  		"com.adobe.flashplayer.installmanager",
205  		"com.adobe.Installers.Setup",
206  		"com.adobe.PDApp.setup",
207  		"com.bittorrent.uTorrent",
208  		"com.divx.divx6formacinstaller",
209  		"com.getdropbox.dropbox",
210  		"com.google.Chrome",
211  		"com.Google.GoogleEarthPlugin.plugin",
212  		"com.Google.GoogleEarthPlus",
213  		"com.hp.Installer",
214  		"com.macpaw.CleanMyMac",
215  		"com.microsoft.SilverlightInstaller",
216  		"com.paragon-software.filesystems.NTFS.pkg",
217  		"com.RealNetworks.RealPlayer",
218  		"com.skype.skype",
219  		"it.alfanet.squared5.MPEGStreamclip",
220  		"org.mozilla.firefox",
221  		"org.videolan.vlc",
222  		
223  		NULL	// sentinel
224  	};
225  
226  	string identifier = "UNBUNDLED";
227  	string version = "UNKNOWN";
228  	if (CFRef<CFBundleRef> bundle = _CFBundleCreateUnique(NULL, target)) {
229  		if (CFStringRef ident = CFBundleGetIdentifier(bundle))
230  			identifier = cfString(ident);
231  		if (CFStringRef vers = CFStringRef(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString"))))
232  			version = cfString(vers);
233  	}
234  	
235  	CFRef<CFURLRef> url = CFURLCopyAbsoluteURL(target);
236  	sanitized = cfString(url);
237  	string::size_type rslash = sanitized.rfind('/');
238  	if (rslash != string::npos)
239  		sanitized = sanitized.substr(rslash+1);
240  	bool keepFilename = false;
241  	for (const char **pfx = interestingBundles; *pfx; pfx++) {
242  		size_t pfxlen = strlen(*pfx);
243  		if (identifier.compare(0, pfxlen, *pfx, pfxlen) == 0)
244  			if (pfxlen == identifier.size() || (*pfx)[pfxlen-1] == '.') {
245  				keepFilename = true;
246  				break;
247  			}
248  	}
249  	if (!keepFilename) {
250  		string::size_type dot = sanitized.rfind('.');
251  		if (dot != string::npos)
252  			sanitized = sanitized.substr(dot);
253  		else
254  			sanitized = "(none)";
255  	}
256  	
257  	trace.add("signature2", "bundle:%s", identifier.c_str());
258  	trace.add("signature3", "%s", sanitized.c_str());
259  	trace.add("signature5", "%s", version.c_str());
260  }
261  	
262  static void traceAssessment(SecAssessment &assessment, AuthorityType type, CFDictionaryRef result)
263  {
264  	if (CFDictionaryGetValue(result, CFSTR("assessment:remote")))
265  		return;		// just traced in syspolicyd
266  	
267  	string authority = "UNSPECIFIED";
268  	bool overridden = false;
269  	bool old_overridden = false;
270  	if (CFDictionaryRef authdict = CFDictionaryRef(CFDictionaryGetValue(result, kSecAssessmentAssessmentAuthority))) {
271  		if (CFStringRef auth = CFStringRef(CFDictionaryGetValue(authdict, kSecAssessmentAssessmentSource)))
272  			authority = cfString(auth);
273  		else
274  			authority = "no authority";
275  		if (CFTypeRef override = CFDictionaryGetValue(authdict, kSecAssessmentAssessmentAuthorityOverride))
276  			if (CFEqual(override, kDisabledOverride)) {
277  				old_overridden = true;
278  				if (CFDictionaryGetValue(authdict, kSecAssessmentAssessmentAuthorityOriginalVerdict) == kCFBooleanFalse)
279  					overridden = true;
280  			}
281  	}
282  
283  	MessageTrace trace("com.apple.security.assessment.outcome2", NULL);
284  	std::string sanitized;
285  	traceResult(assessment.path, trace, sanitized);
286  	trace.add("signature4", "%d", type);
287  
288  	if (CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict) == kCFBooleanFalse) {
289  		trace.add("signature", "denied:%s", authority.c_str());
290  		trace.send("assessment denied for %s", sanitized.c_str());
291  	} else if (overridden) {		// would have failed except for override
292  		trace.add("signature", "defeated:%s", authority.c_str());
293  		trace.send("assessment denied for %s but overridden", sanitized.c_str());
294  	} else if (old_overridden) {	// would have succeeded even without override
295  		trace.add("signature", "override:%s", authority.c_str());
296  		trace.send("assessment granted for %s and overridden", sanitized.c_str());
297  	} else {
298  		trace.add("signature", "granted:%s", authority.c_str());
299  		trace.send("assessment granted for %s by %s", sanitized.c_str(), authority.c_str());
300  	}
301  }
302  
303  static void traceUpdate(CFTypeRef target, CFDictionaryRef context, CFDictionaryRef result)
304  {
305  	// only trace add operations on URL targets
306  	if (target == NULL || CFGetTypeID(target) != CFURLGetTypeID())
307  		return;
308  	CFStringRef edit = CFStringRef(CFDictionaryGetValue(context, kSecAssessmentContextKeyUpdate));
309  	if (!CFEqual(edit, kSecAssessmentUpdateOperationAdd))
310  		return;
311  	MessageTrace trace("com.apple.security.assessment.update", NULL);
312  	std::string sanitized;
313  	traceResult(CFURLRef(target), trace, sanitized);
314  	trace.send("added rule for %s", sanitized.c_str());
315  }
316  
317  
318  //
319  // At present, CopyResult simply retrieves the result already formed by Create.
320  // In the future, this will be more lazy.
321  //
322  CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessmentRef,
323  	SecAssessmentFlags flags,
324  	CFErrorRef *errors)
325  {
326  	BEGIN_CSAPI
327  
328  	SecAssessment &assessment = SecAssessment::ref(assessmentRef);
329  	CFCopyRef<CFDictionaryRef> result = assessment.result;
330  	if (overrideAssessment(flags)) {
331  		// turn rejections into approvals, but note that we did that
332  		CFTypeRef verdict = CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict);
333  		if (verdict == kCFBooleanFalse) {
334  			CFRef<CFMutableDictionaryRef> adulterated = makeCFMutableDictionary(result.get());
335  			CFDictionarySetValue(adulterated, kSecAssessmentAssessmentVerdict, kCFBooleanTrue);
336  			if (CFDictionaryRef authority = CFDictionaryRef(CFDictionaryGetValue(adulterated, kSecAssessmentAssessmentAuthority))) {
337  				CFRef<CFMutableDictionaryRef> authority2 = makeCFMutableDictionary(authority);
338  				CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride);
339  				CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOriginalVerdict, verdict);
340  				CFDictionarySetValue(adulterated, kSecAssessmentAssessmentAuthority, authority2);
341  			} else {
342  				cfadd(adulterated, "{%O={%O=%O}}",
343  					kSecAssessmentAssessmentAuthority, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride);
344  			}
345  			result = adulterated.get();
346  		}
347  	}
348  	traceAssessment(assessment, assessment.type, result);
349  	return result.yield();
350  
351  	END_CSAPI_ERRORS1(NULL)
352  }
353  
354  
355  //
356  // Policy editing operations.
357  // These all make permanent changes to the system-wide authority records.
358  //
359  CFStringRef kSecAssessmentContextKeyUpdate = CFSTR("update");
360  CFStringRef kSecAssessmentUpdateOperationAdd = CFSTR("update:add");
361  CFStringRef kSecAssessmentUpdateOperationRemove = CFSTR("update:remove");
362  CFStringRef kSecAssessmentUpdateOperationEnable = CFSTR("update:enable");
363  CFStringRef kSecAssessmentUpdateOperationDisable = CFSTR("update:disable");
364  CFStringRef kSecAssessmentUpdateOperationFind = CFSTR("update:find");
365  
366  CFStringRef kSecAssessmentUpdateKeyAuthorization = CFSTR("update:authorization");
367  CFStringRef kSecAssessmentUpdateKeyPriority = CFSTR("update:priority");
368  CFStringRef kSecAssessmentUpdateKeyLabel = CFSTR("update:label");
369  CFStringRef kSecAssessmentUpdateKeyExpires = CFSTR("update:expires");
370  CFStringRef kSecAssessmentUpdateKeyAllow = CFSTR("update:allow");
371  CFStringRef kSecAssessmentUpdateKeyRemarks = CFSTR("update:remarks");
372  
373  CFStringRef kSecAssessmentUpdateKeyRow = CFSTR("update:row");
374  CFStringRef kSecAssessmentUpdateKeyCount = CFSTR("update:count");
375  CFStringRef kSecAssessmentUpdateKeyFound = CFSTR("update:found");
376  
377  CFStringRef kSecAssessmentRuleKeyID = CFSTR("rule:id");
378  CFStringRef kSecAssessmentRuleKeyPriority = CFSTR("rule:priority");
379  CFStringRef kSecAssessmentRuleKeyAllow = CFSTR("rule:allow");
380  CFStringRef kSecAssessmentRuleKeyLabel = CFSTR("rule:label");
381  CFStringRef kSecAssessmentRuleKeyRemarks = CFSTR("rule:remarks");
382  CFStringRef kSecAssessmentRuleKeyRequirement = CFSTR("rule:requirement");
383  CFStringRef kSecAssessmentRuleKeyType = CFSTR("rule:type");
384  CFStringRef kSecAssessmentRuleKeyExpires = CFSTR("rule:expires");
385  CFStringRef kSecAssessmentRuleKeyDisabled = CFSTR("rule:disabled");
386  CFStringRef kSecAssessmentRuleKeyBookmark = CFSTR("rule:bookmark");
387  
388  
389  Boolean SecAssessmentUpdate(CFTypeRef target,
390  	SecAssessmentFlags flags,
391  	CFDictionaryRef context,
392  	CFErrorRef *errors)
393  {
394  	if (CFDictionaryRef outcome = SecAssessmentCopyUpdate(target, flags, context, errors)) {
395  		CFRelease(outcome);
396  		return true;
397  	} else {
398  		return false;
399  	}
400  }
401  
402  CFDictionaryRef SecAssessmentCopyUpdate(CFTypeRef target,
403  	SecAssessmentFlags flags,
404  	CFDictionaryRef context,
405  	CFErrorRef *errors)
406  {
407  	BEGIN_CSAPI
408  
409  	CFDictionary ctx(context, errSecCSInvalidAttributeValues);
410  	CFRef<CFDictionaryRef> result;
411  
412  	// make context exist and writable
413  	CFRef<CFMutableDictionaryRef> mcontext = context ? makeCFMutableDictionary(context) : makeCFMutableDictionary();
414  	
415  	if (CFDictionaryGetValue(mcontext, kSecAssessmentUpdateKeyAuthorization) == NULL) {
416  		// no authorization passed in. Make an empty one in this context
417  		AuthorizationRef authorization;
418  		MacOSError::check(AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authorization));
419  		AuthorizationExternalForm extform;
420  		MacOSError::check(AuthorizationMakeExternalForm(authorization, &extform));
421  		CFDictionaryAddValue(mcontext, kSecAssessmentUpdateKeyAuthorization, CFTempData(&extform, sizeof(extform)));
422  		if (!(flags & kSecAssessmentFlagDirect))
423  			AuthorizationFree(authorization, kAuthorizationFlagDefaults);
424  	}
425  
426  	if (flags & kSecAssessmentFlagDirect) {
427  		// ask the engine right here to do its thing
428  		result.take(gEngine().update(target, flags, ctx));
429  	} else {
430  		// relay the question to our daemon for consideration
431  		result.take(xpcEngineUpdate(target, flags, ctx));
432  	}
433  
434  	traceUpdate(target, context, result);
435  	return result.yield();
436  
437  	END_CSAPI_ERRORS1(NULL)
438  }
439  
440  static Boolean
441  updateAuthority(const char *authority, bool enable, CFErrorRef *errors)
442  {
443  	CFStringRef updateValue = enable ? kSecAssessmentUpdateOperationEnable : kSecAssessmentUpdateOperationDisable;
444  	CFTemp<CFDictionaryRef> ctx("{%O=%s, %O=%O}", kSecAssessmentUpdateKeyLabel, authority, kSecAssessmentContextKeyUpdate, updateValue);
445  	return SecAssessmentUpdate(NULL, kSecCSDefaultFlags, ctx, errors);
446  }
447  
448  
449  //
450  // The fcntl of System Policies.
451  // For those very special requests.
452  //
453  Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *errors)
454  {
455  	BEGIN_CSAPI
456  	
457  	if (CFEqual(control, CFSTR("ui-enable"))) {
458  		setAssessment(true);
459  		MessageTrace trace("com.apple.security.assessment.state", "enable");
460  		trace.send("enable assessment outcomes");
461  		return true;
462  	} else if (CFEqual(control, CFSTR("ui-disable"))) {
463  		setAssessment(false);
464  		MessageTrace trace("com.apple.security.assessment.state", "disable");
465  		trace.send("disable assessment outcomes");
466  		return true;
467  	} else if (CFEqual(control, CFSTR("ui-status"))) {
468  		CFBooleanRef &result = *(CFBooleanRef*)(arguments);
469  		if (overrideAssessment())
470  			result = kCFBooleanFalse;
471  		else
472  			result = kCFBooleanTrue;
473  		return true;
474  	} else if (CFEqual(control, CFSTR("ui-enable-devid"))) {
475  		updateAuthority("Developer ID", true, errors);
476  		updateAuthority("Notarized Developer ID", true, errors);
477  		MessageTrace trace("com.apple.security.assessment.state", "enable-devid");
478  		trace.send("enable Developer ID approval");
479  		return true;
480  	} else if (CFEqual(control, CFSTR("ui-disable-devid"))) {
481  		updateAuthority("Developer ID", false, errors);
482  		MessageTrace trace("com.apple.security.assessment.state", "disable-devid");
483  		trace.send("disable Developer ID approval");
484  		return true;
485      } else if (CFEqual(control, CFSTR("ui-get-devid"))) {
486          xpcEngineCheckDevID((CFBooleanRef*)(arguments));
487          return true;
488      } else if (CFEqual(control, CFSTR("ui-get-devid-local"))) {
489  		CFBooleanRef &result = *(CFBooleanRef*)(arguments);
490  		if (gEngine().value<int>("SELECT disabled FROM authority WHERE label = 'Developer ID';", true))
491  			result = kCFBooleanFalse;
492  		else
493  			result = kCFBooleanTrue;
494  		return true;
495  	} else if (CFEqual(control, CFSTR("ui-enable-notarized"))) {
496  		updateAuthority("Notarized Developer ID", true, errors);
497  		updateAuthority("Unnotarized Developer ID", true, errors);
498  		MessageTrace trace("com.apple.security.assessment.state", "enable-notarized");
499  		trace.send("enable Notarized Developer ID approval");
500  		return true;
501  	} else if (CFEqual(control, CFSTR("ui-disable-notarized"))) {
502  		updateAuthority("Notarized Developer ID", false, errors);
503  		updateAuthority("Unnotarized Developer ID", false, errors);
504  		MessageTrace trace("com.apple.security.assessment.state", "disable-notarized");
505  		trace.send("disable Notarized Developer ID approval");
506  		return true;
507  	} else if (CFEqual(control, CFSTR("ui-get-notarized"))) {
508  		xpcEngineCheckNotarized((CFBooleanRef*)(arguments));
509  		return true;
510  	} else if (CFEqual(control, CFSTR("ui-get-notarized-local"))) {
511  		CFBooleanRef &result = *(CFBooleanRef*)(arguments);
512  		if (gEngine().value<int>("SELECT disabled FROM authority WHERE label = 'Notarized Developer ID';", true))
513  			result = kCFBooleanFalse;
514  		else
515  			result = kCFBooleanTrue;
516  		return true;
517  	} else if (CFEqual(control, CFSTR("ui-record-reject"))) {
518  		// send this through syspolicyd for update validation
519  		xpcEngineRecord(CFDictionaryRef(arguments));
520  		return true;
521  	} else if (CFEqual(control, CFSTR("ui-record-reject-local"))) {
522  		// perform the local operation (requires root)
523  		gEngine().recordFailure(CFDictionaryRef(arguments));
524  		return true;
525  	} else if (CFEqual(control, CFSTR("ui-recall-reject"))) {
526  		// no special privileges required for this, so read directly
527  		CFDictionaryRef &result = *(CFDictionaryRef*)(arguments);
528  		CFRef<CFDataRef> infoData = cfLoadFile(lastRejectFile);
529  		if (infoData)
530  			result = makeCFDictionaryFrom(infoData);
531  		else
532  			result = NULL;
533  		return true;
534  	} else if (CFEqual(control, CFSTR("rearm-status"))) {
535  		CFTimeInterval &result = *(CFTimeInterval*)(arguments);
536  		if (!queryRearmTimer(result))
537  			result = 0;
538  		return true;
539  	} else
540  		MacOSError::throwMe(errSecCSInvalidAttributeValues);
541  
542  	END_CSAPI_ERRORS1(false)
543  }
544  
545  Boolean SecAssessmentTicketRegister(CFDataRef ticketData, CFErrorRef *errors)
546  {
547  	BEGIN_CSAPI
548  
549  	xpcEngineTicketRegister(ticketData);
550  	return true;
551  
552  	END_CSAPI_ERRORS1(false)
553  }
554  
555  Boolean SecAssessmentRegisterPackageTicket(CFURLRef packageURL, CFErrorRef* errors)
556  {
557  	BEGIN_CSAPI
558  	
559  	string path = cfString(packageURL);
560  	Xar xar(path.c_str());
561  	
562  	if (!xar) {
563  		MacOSError::throwMe(errSecParam);
564  	}
565  	
566  	xar.registerStapledNotarization();
567  	return true;
568  	
569  	END_CSAPI_ERRORS1(false)
570  }
571  
572  Boolean SecAssessmentTicketLookup(CFDataRef hash, SecCSDigestAlgorithm hashType, SecAssessmentTicketFlags flags, double *date, CFErrorRef *errors)
573  {
574  	BEGIN_CSAPI
575  
576  	xpcEngineTicketLookup(hash, hashType, flags, date);
577  	return true;
578  
579  	END_CSAPI_ERRORS1(false)
580  }
581  
582  Boolean SecAssessmentLegacyCheck(CFDataRef hash, SecCSDigestAlgorithm hashType, CFStringRef teamID, CFErrorRef *errors)
583  {
584  	BEGIN_CSAPI
585  
586  	xpcEngineLegacyCheck(hash, hashType, teamID);
587  	return true;
588  
589  	END_CSAPI_ERRORS1(false)
590  }
591