2 * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "SecAssessment.h"
26 #include "policyengine.h"
27 #include "xpcengine.h"
28 #include "csutilities.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>
37 using namespace CodeSigning
;
42 struct _SecAssessment
: private CFRuntimeBase
{
44 _SecAssessment(CFURLRef p
, AuthorityType typ
, CFDictionaryRef r
) : path(p
), type(typ
), result(r
) { }
46 CFCopyRef
<CFURLRef
> path
;
48 CFRef
<CFDictionaryRef
> result
;
51 static _SecAssessment
&ref(SecAssessmentRef r
)
52 { return *(_SecAssessment
*)r
; }
55 void *operator new (size_t size
)
57 return (void *)_CFRuntimeCreateInstance(NULL
, SecAssessmentGetTypeID(),
58 sizeof(_SecAssessment
) - sizeof(CFRuntimeBase
), NULL
);
61 static void finalize(CFTypeRef obj
)
62 { ((_SecAssessment
*)obj
)->~_SecAssessment(); }
65 typedef _SecAssessment SecAssessment
;
68 static const CFRuntimeClass assessmentClass
= {
70 "SecAssessment", // name
73 SecAssessment::finalize
, // finalize
81 static dispatch_once_t assessmentOnce
;
82 CFTypeID assessmentType
= _kCFRuntimeNotATypeID
;
84 CFTypeID
SecAssessmentGetTypeID()
86 dispatch_once(&assessmentOnce
, ^void() {
87 if ((assessmentType
= _CFRuntimeRegisterClass(&assessmentClass
)) == _kCFRuntimeNotATypeID
)
90 return assessmentType
;
95 // Common dictionary constants
97 CFStringRef kSecAssessmentContextKeyOperation
= CFSTR("operation");
98 CFStringRef kSecAssessmentOperationTypeExecute
= CFSTR("operation:execute");
99 CFStringRef kSecAssessmentOperationTypeInstall
= CFSTR("operation:install");
100 CFStringRef kSecAssessmentOperationTypeOpenDocument
= CFSTR("operation:lsopen");
104 // Read-only in-process access to the policy database
106 class ReadPolicy
: public PolicyDatabase
{
108 ReadPolicy() : PolicyDatabase(defaultDatabase
) { }
110 ModuleNexus
<ReadPolicy
> gDatabase
;
114 // An on-demand instance of the policy engine
116 ModuleNexus
<PolicyEngine
> gEngine
;
120 // Policy evaluation ("assessment") operations
122 CFStringRef kSecAssessmentContextKeyUTI
= CFSTR("context:uti");
124 CFStringRef kSecAssessmentContextKeyFeedback
= CFSTR("context:feedback");
125 CFStringRef kSecAssessmentFeedbackProgress
= CFSTR("feedback:progress");
126 CFStringRef kSecAssessmentFeedbackInfoCurrent
= CFSTR("current");
127 CFStringRef kSecAssessmentFeedbackInfoTotal
= CFSTR("total");
129 CFStringRef kSecAssessmentContextKeyPrimarySignature
= CFSTR("context:primary-signature");
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");
144 CFStringRef kDisabledOverride
= CFSTR("security disabled");
146 SecAssessmentRef
SecAssessmentCreate(CFURLRef path
,
147 SecAssessmentFlags flags
,
148 CFDictionaryRef context
,
153 if (flags
& kSecAssessmentFlagAsynchronous
)
154 MacOSError::throwMe(errSecCSUnimplemented
);
156 AuthorityType type
= typeFor(context
, kAuthorityExecute
);
157 CFRef
<CFMutableDictionaryRef
> result
= makeCFMutableDictionary();
159 SYSPOLICY_ASSESS_API(cfString(path
).c_str(), int(type
), flags
);
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
);
167 // relay the question to our daemon for consideration
168 SYSPOLICY_ASSESS_REMOTE();
169 xpcEngineAssess(path
, flags
, context
, result
);
171 } catch (CommonError
&error
) {
172 switch (error
.osStatus()) {
173 case CSSMERR_TP_CERT_REVOKED
:
176 if (!overrideAssessment(flags
))
177 throw; // let it go as an error
180 // record the error we would have returned
181 cfadd(result
, "{%O=#F,'assessment:error'=%d}}", kSecAssessmentAssessmentVerdict
, error
.osStatus());
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
);
189 return new SecAssessment(path
, type
, result
.yield());
191 END_CSAPI_ERRORS1(NULL
)
195 static void traceResult(CFURLRef target
, MessageTrace
&trace
, std::string
&sanitized
)
197 static const char *interestingBundles
[] = {
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",
211 "com.Google.GoogleEarthPlugin.plugin",
212 "com.Google.GoogleEarthPlus",
214 "com.macpaw.CleanMyMac",
215 "com.microsoft.SilverlightInstaller",
216 "com.paragon-software.filesystems.NTFS.pkg",
217 "com.RealNetworks.RealPlayer",
219 "it.alfanet.squared5.MPEGStreamclip",
220 "org.mozilla.firefox",
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
);
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] == '.') {
250 string::size_type dot
= sanitized
.rfind('.');
251 if (dot
!= string::npos
)
252 sanitized
= sanitized
.substr(dot
);
254 sanitized
= "(none)";
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());
262 static void traceAssessment(SecAssessment
&assessment
, AuthorityType type
, CFDictionaryRef result
)
264 if (CFDictionaryGetValue(result
, CFSTR("assessment:remote")))
265 return; // just traced in syspolicyd
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
);
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
)
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
);
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());
298 trace
.add("signature", "granted:%s", authority
.c_str());
299 trace
.send("assessment granted for %s by %s", sanitized
.c_str(), authority
.c_str());
303 static void traceUpdate(CFTypeRef target
, CFDictionaryRef context
, CFDictionaryRef result
)
305 // only trace add operations on URL targets
306 if (target
== NULL
|| CFGetTypeID(target
) != CFURLGetTypeID())
308 CFStringRef edit
= CFStringRef(CFDictionaryGetValue(context
, kSecAssessmentContextKeyUpdate
));
309 if (!CFEqual(edit
, kSecAssessmentUpdateOperationAdd
))
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());
319 // At present, CopyResult simply retrieves the result already formed by Create.
320 // In the future, this will be more lazy.
322 CFDictionaryRef
SecAssessmentCopyResult(SecAssessmentRef assessmentRef
,
323 SecAssessmentFlags flags
,
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
);
342 cfadd(adulterated
, "{%O={%O=%O}}",
343 kSecAssessmentAssessmentAuthority
, kSecAssessmentAssessmentAuthorityOverride
, kDisabledOverride
);
345 result
= adulterated
.get();
348 traceAssessment(assessment
, assessment
.type
, result
);
349 return result
.yield();
351 END_CSAPI_ERRORS1(NULL
)
356 // Policy editing operations.
357 // These all make permanent changes to the system-wide authority records.
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");
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");
373 CFStringRef kSecAssessmentUpdateKeyRow
= CFSTR("update:row");
374 CFStringRef kSecAssessmentUpdateKeyCount
= CFSTR("update:count");
375 CFStringRef kSecAssessmentUpdateKeyFound
= CFSTR("update:found");
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");
389 Boolean
SecAssessmentUpdate(CFTypeRef target
,
390 SecAssessmentFlags flags
,
391 CFDictionaryRef context
,
394 if (CFDictionaryRef outcome
= SecAssessmentCopyUpdate(target
, flags
, context
, errors
)) {
402 CFDictionaryRef
SecAssessmentCopyUpdate(CFTypeRef target
,
403 SecAssessmentFlags flags
,
404 CFDictionaryRef context
,
409 CFDictionary
ctx(context
, errSecCSInvalidAttributeValues
);
410 CFRef
<CFDictionaryRef
> result
;
412 // make context exist and writable
413 CFRef
<CFMutableDictionaryRef
> mcontext
= context
? makeCFMutableDictionary(context
) : makeCFMutableDictionary();
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
);
426 if (flags
& kSecAssessmentFlagDirect
) {
427 // ask the engine right here to do its thing
428 result
= gEngine().update(target
, flags
, ctx
);
430 // relay the question to our daemon for consideration
431 result
= xpcEngineUpdate(target
, flags
, ctx
);
434 traceUpdate(target
, context
, result
);
435 return result
.yield();
437 END_CSAPI_ERRORS1(NULL
)
441 updateAuthority(const char *authority
, bool enable
, CFErrorRef
*errors
)
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
);
450 // The fcntl of System Policies.
451 // For those very special requests.
453 Boolean
SecAssessmentControl(CFStringRef control
, void *arguments
, CFErrorRef
*errors
)
457 if (CFEqual(control
, CFSTR("ui-enable"))) {
459 MessageTrace
trace("com.apple.security.assessment.state", "enable");
460 trace
.send("enable assessment outcomes");
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");
467 } else if (CFEqual(control
, CFSTR("ui-status"))) {
468 CFBooleanRef
&result
= *(CFBooleanRef
*)(arguments
);
469 if (overrideAssessment())
470 result
= kCFBooleanFalse
;
472 result
= kCFBooleanTrue
;
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");
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");
485 } else if (CFEqual(control
, CFSTR("ui-get-devid"))) {
486 xpcEngineCheckDevID((CFBooleanRef
*)(arguments
));
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
;
493 result
= kCFBooleanTrue
;
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");
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");
507 } else if (CFEqual(control
, CFSTR("ui-get-notarized"))) {
508 xpcEngineCheckNotarized((CFBooleanRef
*)(arguments
));
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
;
515 result
= kCFBooleanTrue
;
517 } else if (CFEqual(control
, CFSTR("ui-record-reject"))) {
518 // send this through syspolicyd for update validation
519 xpcEngineRecord(CFDictionaryRef(arguments
));
521 } else if (CFEqual(control
, CFSTR("ui-record-reject-local"))) {
522 // perform the local operation (requires root)
523 gEngine().recordFailure(CFDictionaryRef(arguments
));
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
);
530 result
= makeCFDictionaryFrom(infoData
);
534 } else if (CFEqual(control
, CFSTR("rearm-status"))) {
535 CFTimeInterval
&result
= *(CFTimeInterval
*)(arguments
);
536 if (!queryRearmTimer(result
))
540 MacOSError::throwMe(errSecCSInvalidAttributeValues
);
542 END_CSAPI_ERRORS1(false)
545 Boolean
SecAssessmentTicketRegister(CFDataRef ticketData
, CFErrorRef
*errors
)
549 xpcEngineTicketRegister(ticketData
);
552 END_CSAPI_ERRORS1(false)
555 Boolean
SecAssessmentRegisterPackageTicket(CFURLRef packageURL
, CFErrorRef
* errors
)
559 string path
= cfString(packageURL
);
560 Xar
xar(path
.c_str());
563 MacOSError::throwMe(errSecParam
);
566 xar
.registerStapledNotarization();
569 END_CSAPI_ERRORS1(false)
572 Boolean
SecAssessmentTicketLookup(CFDataRef hash
, SecCSDigestAlgorithm hashType
, SecAssessmentTicketFlags flags
, double *date
, CFErrorRef
*errors
)
576 xpcEngineTicketLookup(hash
, hashType
, flags
, date
);
579 END_CSAPI_ERRORS1(false)
582 Boolean
SecAssessmentLegacyCheck(CFDataRef hash
, SecCSDigestAlgorithm hashType
, CFStringRef teamID
, CFErrorRef
*errors
)
586 xpcEngineLegacyCheck(hash
, hashType
, teamID
);
589 END_CSAPI_ERRORS1(false)