2  * Copyright (c) 2011-2012 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@ 
  23 #include "policyengine.h" 
  25 #include "quarantine++.h" 
  26 #include "codesigning_dtrace.h" 
  27 #include <security_utilities/cfmunge.h> 
  28 #include <Security/Security.h> 
  29 #include <Security/SecCodePriv.h> 
  30 #include <Security/SecRequirementPriv.h> 
  31 #include <Security/SecPolicyPriv.h> 
  32 #include <Security/SecTrustPriv.h> 
  33 #include <Security/SecCodeSigner.h> 
  34 #include <Security/cssmapplePriv.h> 
  35 #include <security_utilities/unix++.h> 
  39 #include "codedirectory.h" 
  40 #include "csutilities.h" 
  41 #include "StaticCode.h" 
  43 #include <CoreServices/CoreServicesPriv.h> 
  44 #include "SecCodePriv.h" 
  45 #undef check // Macro! Yech. 
  48 #include <OpenScriptingUtilPriv.h> 
  53 namespace CodeSigning 
{ 
  55 static const double NEGATIVE_HOLD 
= 60.0/86400; // 60 seconds to cache negative outcomes 
  57 static const char RECORDER_DIR
[] = "/tmp/gke-";         // recorder mode destination for detached signatures 
  59         recorder_code_untrusted 
= 0,            // signed but untrusted 
  60         recorder_code_adhoc 
= 1,                        // unsigned; signature recorded 
  61         recorder_code_unable 
= 2,                       // unsigned; unable to record signature 
  65 static void authorizeUpdate(SecAssessmentFlags flags
, CFDictionaryRef context
); 
  66 static void normalizeTarget(CFRef
<CFTypeRef
> &target
, CFDictionary 
&context
, std::string 
*signUnsigned 
= NULL
); 
  67 static bool codeInvalidityExceptions(SecStaticCodeRef code
, CFMutableDictionaryRef result
); 
  68 static CFTypeRef 
installerPolicy() CF_RETURNS_RETAINED
; 
  74 PolicyEngine::PolicyEngine() 
  75         : PolicyDatabase(NULL
, SQLITE_OPEN_READWRITE 
| SQLITE_OPEN_CREATE
) 
  79 PolicyEngine::~PolicyEngine() 
  84 // Top-level evaluation driver 
  86 void PolicyEngine::evaluate(CFURLRef path
, AuthorityType type
, SecAssessmentFlags flags
, CFDictionaryRef context
, CFMutableDictionaryRef result
) 
  89         installExplicitSet(gkeAuthFile
, gkeSigsFile
); 
  92         case kAuthorityExecute
: 
  93                 evaluateCode(path
, kAuthorityExecute
, flags
, context
, result
); 
  95         case kAuthorityInstall
: 
  96                 evaluateInstall(path
, flags
, context
, result
); 
  98         case kAuthorityOpenDoc
: 
  99                 evaluateDocOpen(path
, flags
, context
, result
); 
 102                 MacOSError::throwMe(errSecCSInvalidAttributeValues
); 
 109 // Whitelist pre-screen processing. 
 110 // Whitelist-matching unsigned code is expensive since we have to generate a full code signature 
 111 // just to see if we match a whitelist authority entry. This class generates a light(er)-weight 
 112 // prescreen and matches it against a hint in an authority record generated from the (detached recorded) 
 113 // code signature at time of whitelist recording. 
 114 // This is just a heuristic to cheaply rule out guaranteed mismatches. When in doubt, we go ahead 
 115 // and do the full work. 
 117 class WhitelistPrescreen 
{ 
 119         WhitelistPrescreen(SecStaticCodeRef code
) 
 120                 : mRep(SecStaticCode::requiredStatic(code
)->diskRep()) { } 
 122         bool reject(const char *screen
, const char *remarks
); 
 125         std::string 
create(char type
, SHA1 
&hash
); 
 127         RefPointer
<DiskRep
> mRep
;       // DiskRep representing the code 
 128         std::string mScreen
;    // calculated screen (on demand) 
 131 bool WhitelistPrescreen::reject(const char *screen
, const char *remarks
) 
 133         if (!screen
) {  // authority record has no screen to match - apply heuristic 
 134                 if (remarks 
&& mRep
->mainExecutablePath() != remarks
)   // not an allow record (or moved) 
 137                         return false;   // can't rule out; proceed 
 140         if (mScreen
.empty()) { 
 141                 if (CFRef
<CFDataRef
> info 
= mRep
->component(cdInfoSlot
)) { 
 143                         hash
.update(CFDataGetBytePtr(info
), CFDataGetLength(info
)); 
 144                         mScreen 
= create('I', hash
); 
 145                 } else if (mRep
->mainExecutableImage()) { 
 149                         hashFileData(mRep
->mainExecutablePath().c_str(), &hash
); 
 150                         mScreen 
= create('M', hash
); 
 154         return screen 
!= mScreen
; 
 157 std::string 
WhitelistPrescreen::create(char type
, SHA1 
&hash
) 
 161         char buffer
[2*SHA1::digestLength 
+ 2] = { type 
}; 
 162         for (size_t n 
= 0; n 
< SHA1::digestLength
; n
++) 
 163                 sprintf(buffer 
+ 1 + 2*n
, "%02.2x", digest
[n
]); 
 170 // Read from disk, evaluate properly, cache as indicated. The whole thing, so far. 
 172 void PolicyEngine::evaluateCode(CFURLRef path
, AuthorityType type
, SecAssessmentFlags flags
, CFDictionaryRef context
, CFMutableDictionaryRef result
, 
 173         bool handleUnsignedCode 
/* = true */) 
 175         FileQuarantine 
qtn(cfString(path
).c_str()); 
 176         if (qtn
.flag(QTN_FLAG_HARD
)) 
 177                 MacOSError::throwMe(errSecCSFileHardQuarantined
); 
 179         CFRef
<SecStaticCodeRef
> code
; 
 180         MacOSError::check(SecStaticCodeCreateWithPath(path
, kSecCSDefaultFlags
, &code
.aref())); 
 181         OSStatus rc 
= noErr
;    // last validation error 
 183         const SecCSFlags validationFlags 
= kSecCSEnforceRevocationChecks
; 
 185         WhitelistPrescreen 
whitelistScreen(code
); // pre-screening filter for whitelist pre-screening (only) 
 187         SQLite::Statement 
query(*this, 
 188                 "SELECT allow, requirement, id, label, expires, flags, disabled, filter_unsigned, remarks FROM scan_authority" 
 189                 " WHERE type = :type" 
 190                 " ORDER BY priority DESC;"); 
 191         query
.bind(":type").integer(type
); 
 192         SQLite3::int64 latentID 
= 0;            // first (highest priority) disabled matching ID 
 193         std::string latentLabel
;                        // ... and associated label, if any 
 194         while (query
.nextRow()) { 
 195                 bool allow 
= int(query
[0]); 
 196                 const char *reqString 
= query
[1]; 
 197                 SQLite3::int64 id 
= query
[2]; 
 198                 const char *label 
= query
[3]; 
 199                 double expires 
= query
[4]; 
 200                 sqlite3_int64 ruleFlags 
= query
[5]; 
 201                 SQLite3::int64 disabled 
= query
[6]; 
 202                 const char *filter 
= query
[7]; 
 203                 const char *remarks 
= query
[8]; 
 205                 CFRef
<SecRequirementRef
> requirement
; 
 206                 MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString
), kSecCSDefaultFlags
, &requirement
.aref())); 
 207                 rc 
= SecStaticCodeCheckValidity(code
, validationFlags
, requirement
); 
 209                 // ad-hoc sign unsigned code, skip of Gatekeeper is off or the rule is disabled; but always do it for whitelist recording 
 210                 if (rc 
== errSecCSUnsigned 
&& handleUnsignedCode 
&& (!(disabled 
|| overrideAssessment()) || SYSPOLICY_RECORDER_MODE_ENABLED())) { 
 211                         if (!SYSPOLICY_RECORDER_MODE_ENABLED()) { 
 212                                 // apply whitelist pre-screening to speed things up for non-matches 
 213                                 if (ruleFlags 
& kAuthorityFlagDefault
)  // can't ever match standard rules with unsigned code 
 215                                 if (whitelistScreen
.reject(filter
, remarks
))    // apply whitelist pre-filter 
 219                                 // ad-hoc sign the code and attach the signature 
 220                                 CFRef
<CFDataRef
> signature 
= CFDataCreateMutable(NULL
, 0); 
 221                                 CFTemp
<CFDictionaryRef
> arguments("{%O=%O, %O=#N}", kSecCodeSignerDetached
, signature
.get(), kSecCodeSignerIdentity
); 
 222                                 CFRef
<SecCodeSignerRef
> signer
; 
 223                                 MacOSError::check(SecCodeSignerCreate(arguments
, kSecCSDefaultFlags
, &signer
.aref())); 
 224                                 MacOSError::check(SecCodeSignerAddSignature(signer
, code
, kSecCSDefaultFlags
)); 
 225                                 MacOSError::check(SecCodeSetDetachedSignature(code
, signature
, kSecCSDefaultFlags
)); 
 227                                 // if we're in GKE recording mode, save that signature and report its location 
 228                                 if (SYSPOLICY_RECORDER_MODE_ENABLED()) { 
 229                                         int status 
= recorder_code_unable
;      // ephemeral signature (not recorded) 
 230                                         if (geteuid() == 0) { 
 231                                                 CFRef
<CFUUIDRef
> uuid 
= CFUUIDCreate(NULL
); 
 232                                                 std::string sigfile 
= RECORDER_DIR 
+ cfStringRelease(CFUUIDCreateString(NULL
, uuid
)) + ".tsig"; 
 234                                                         UnixPlusPlus::AutoFileDesc 
fd(sigfile
, O_WRONLY 
| O_CREAT
); 
 235                                                         fd
.write(CFDataGetBytePtr(signature
), CFDataGetLength(signature
)); 
 236                                                         status 
= recorder_code_adhoc
;   // recorded signature 
 237                                                         SYSPOLICY_RECORDER_MODE_ADHOC_PATH(cfString(path
).c_str(), type
, sigfile
.c_str()); 
 241                                         // now report the D probe itself 
 242                                         CFRef
<CFDictionaryRef
> info
; 
 243                                         MacOSError::check(SecCodeCopySigningInformation(code
, kSecCSDefaultFlags
, &info
.aref())); 
 244                                         CFDataRef cdhash 
= CFDataRef(CFDictionaryGetValue(info
, kSecCodeInfoUnique
)); 
 245                                         SYSPOLICY_RECORDER_MODE(cfString(path
).c_str(), type
, "", 
 246                                                 cdhash 
? CFDataGetBytePtr(cdhash
) : NULL
, status
); 
 249                                 // rerun the validation to update state 
 250                                 rc 
= SecStaticCodeCheckValidity(code
, validationFlags 
| kSecCSBasicValidateOnly
, requirement
); 
 255                 case noErr
: // well signed and satisfies requirement... 
 256                         break;  // ... continue below 
 257                 case errSecCSSignatureFailed
: 
 258                         if (!codeInvalidityExceptions(code
, result
)) { 
 259                                 if (SYSPOLICY_ASSESS_OUTCOME_BROKEN_ENABLED()) 
 260                                         SYSPOLICY_ASSESS_OUTCOME_BROKEN(cfString(path
).c_str(), type
, false); 
 261                                 MacOSError::throwMe(rc
); 
 263                         if (SYSPOLICY_ASSESS_OUTCOME_BROKEN_ENABLED()) 
 264                                 SYSPOLICY_ASSESS_OUTCOME_BROKEN(cfString(path
).c_str(), type
, true); 
 265                         // treat as unsigned to fix problems in the field 
 266                 case errSecCSUnsigned
: 
 267                         if (handleUnsignedCode
) { 
 268                                 cfadd(result
, "{%O=#F}", kSecAssessmentAssessmentVerdict
); 
 269                                 addAuthority(result
, "no usable signature"); 
 272                 case errSecCSReqFailed
: // requirement missed, but otherwise okay 
 274                 default: // broken in some way; all tests will fail like this so bail out 
 275                         MacOSError::throwMe(rc
); 
 283                         continue;       // the loop 
 286                 CFRef
<CFDictionaryRef
> info
;    // as needed 
 287                 if (flags 
& kSecAssessmentFlagRequestOrigin
) { 
 289                                 MacOSError::check(SecCodeCopySigningInformation(code
, kSecCSSigningInformation
, &info
.aref())); 
 290                         if (CFArrayRef chain 
= CFArrayRef(CFDictionaryGetValue(info
, kSecCodeInfoCertificates
))) 
 291                                 setOrigin(chain
, result
); 
 293                 if (!(ruleFlags 
& kAuthorityFlagInhibitCache
) && !(flags 
& kSecAssessmentFlagNoCache
)) {        // cache inhibit 
 295                                 MacOSError::check(SecCodeCopySigningInformation(code
, kSecCSSigningInformation
, &info
.aref())); 
 296                         if (SecTrustRef trust 
= SecTrustRef(CFDictionaryGetValue(info
, kSecCodeInfoTrust
))) { 
 297                                 CFRef
<CFDictionaryRef
> xinfo
; 
 298                                 MacOSError::check(SecTrustCopyExtendedResult(trust
, &xinfo
.aref())); 
 299                                 if (CFDateRef limit 
= CFDateRef(CFDictionaryGetValue(xinfo
, kSecTrustExpirationDate
))) { 
 300                                         this->recordOutcome(code
, allow
, type
, min(expires
, dateToJulian(limit
)), id
); 
 305                         if (SYSPOLICY_ASSESS_OUTCOME_ACCEPT_ENABLED()) { 
 307                                         MacOSError::check(SecCodeCopySigningInformation(code
, kSecCSSigningInformation
, &info
.aref())); 
 308                                 CFDataRef cdhash 
= CFDataRef(CFDictionaryGetValue(info
, kSecCodeInfoUnique
)); 
 309                                 SYSPOLICY_ASSESS_OUTCOME_ACCEPT(cfString(path
).c_str(), type
, label
, cdhash 
? CFDataGetBytePtr(cdhash
) : NULL
); 
 312                         if (SYSPOLICY_ASSESS_OUTCOME_DENY_ENABLED() || SYSPOLICY_RECORDER_MODE_ENABLED()) { 
 314                                         MacOSError::check(SecCodeCopySigningInformation(code
, kSecCSSigningInformation
, &info
.aref())); 
 315                                 CFDataRef cdhash 
= CFDataRef(CFDictionaryGetValue(info
, kSecCodeInfoUnique
)); 
 316                                 std::string cpath 
= cfString(path
); 
 317                                 const void *hashp 
= cdhash 
? CFDataGetBytePtr(cdhash
) : NULL
; 
 318                                 SYSPOLICY_ASSESS_OUTCOME_DENY(cpath
.c_str(), type
, label
, hashp
); 
 319                                 SYSPOLICY_RECORDER_MODE(cpath
.c_str(), type
, label
, hashp
, recorder_code_untrusted
); 
 322                 cfadd(result
, "{%O=%B}", kSecAssessmentAssessmentVerdict
, allow
); 
 323                 addAuthority(result
, label
, id
); 
 327         if (rc 
== errSecCSUnsigned
) {   // skipped all applicable rules due to pre-screening 
 328                 cfadd(result
, "{%O=#F}", kSecAssessmentAssessmentVerdict
); 
 329                 addAuthority(result
, "no usable signature"); 
 333         // no applicable authority (but signed, perhaps temporarily). Deny by default 
 334         CFRef
<CFDictionaryRef
> info
; 
 335         MacOSError::check(SecCodeCopySigningInformation(code
, kSecCSSigningInformation
, &info
.aref())); 
 336         if (flags 
& kSecAssessmentFlagRequestOrigin
) { 
 337                 if (CFArrayRef chain 
= CFArrayRef(CFDictionaryGetValue(info
, kSecCodeInfoCertificates
))) 
 338                         setOrigin(chain
, result
); 
 340         if (SYSPOLICY_ASSESS_OUTCOME_DEFAULT_ENABLED() || SYSPOLICY_RECORDER_MODE_ENABLED()) { 
 341                 CFDataRef cdhash 
= CFDataRef(CFDictionaryGetValue(info
, kSecCodeInfoUnique
)); 
 342                 const void *hashp 
= cdhash 
? CFDataGetBytePtr(cdhash
) : NULL
; 
 343                 std::string cpath 
= cfString(path
); 
 344                 SYSPOLICY_ASSESS_OUTCOME_DEFAULT(cpath
.c_str(), type
, latentLabel
.c_str(), hashp
); 
 345                 SYSPOLICY_RECORDER_MODE(cpath
.c_str(), type
, latentLabel
.c_str(), hashp
, 0); 
 347         if (!(flags 
& kSecAssessmentFlagNoCache
)) 
 348                 this->recordOutcome(code
, false, type
, this->julianNow() + NEGATIVE_HOLD
, latentID
); 
 349         cfadd(result
, "{%O=%B}", kSecAssessmentAssessmentVerdict
, false); 
 350         addAuthority(result
, latentLabel
.c_str(), latentID
); 
 355 // Installer archive. 
 356 // Hybrid policy: If we detect an installer signature, use and validate that. 
 357 // If we don't, check for a code signature instead. 
 359 void PolicyEngine::evaluateInstall(CFURLRef path
, SecAssessmentFlags flags
, CFDictionaryRef context
, CFMutableDictionaryRef result
) 
 361         const AuthorityType type 
= kAuthorityInstall
; 
 363         Xar 
xar(cfString(path
).c_str()); 
 365                 // follow the code signing path 
 366                 evaluateCode(path
, type
, flags
, context
, result
); 
 370         SQLite3::int64 latentID 
= 0;            // first (highest priority) disabled matching ID 
 371         std::string latentLabel
;                        // ... and associated label, if any 
 372         if (!xar
.isSigned()) { 
 374                 if (SYSPOLICY_ASSESS_OUTCOME_UNSIGNED_ENABLED()) 
 375                         SYSPOLICY_ASSESS_OUTCOME_UNSIGNED(cfString(path
).c_str(), type
); 
 376                 cfadd(result
, "{%O=%B}", kSecAssessmentAssessmentVerdict
, false); 
 377                 addAuthority(result
, "no usable signature"); 
 380         if (CFRef
<CFArrayRef
> certs 
= xar
.copyCertChain()) { 
 381                 CFRef
<CFTypeRef
> policy 
= installerPolicy(); 
 382                 CFRef
<SecTrustRef
> trust
; 
 383                 MacOSError::check(SecTrustCreateWithCertificates(certs
, policy
, &trust
.aref())); 
 384 //              MacOSError::check(SecTrustSetAnchorCertificates(trust, cfEmptyArray())); // no anchors 
 385                 MacOSError::check(SecTrustSetOptions(trust
, kSecTrustOptionAllowExpired 
| kSecTrustOptionImplicitAnchors
)); 
 387                 SecTrustResultType trustResult
; 
 388                 MacOSError::check(SecTrustEvaluate(trust
, &trustResult
)); 
 389                 CFRef
<CFArrayRef
> chain
; 
 390                 CSSM_TP_APPLE_EVIDENCE_INFO 
*info
; 
 391                 MacOSError::check(SecTrustGetResult(trust
, &trustResult
, &chain
.aref(), &info
)); 
 393                 if (flags 
& kSecAssessmentFlagRequestOrigin
) 
 394                         setOrigin(chain
, result
); 
 396                 switch (trustResult
) { 
 397                 case kSecTrustResultProceed
: 
 398                 case kSecTrustResultUnspecified
: 
 403                                 MacOSError::check(SecTrustGetCssmResultCode(trust
, &rc
)); 
 404                                 MacOSError::throwMe(rc
); 
 408                 SQLite::Statement 
query(*this, 
 409                         "SELECT allow, requirement, id, label, flags, disabled FROM scan_authority" 
 410                         " WHERE type = :type" 
 411                         " ORDER BY priority DESC;"); 
 412                 query
.bind(":type").integer(type
); 
 413                 while (query
.nextRow()) { 
 414                         bool allow 
= int(query
[0]); 
 415                         const char *reqString 
= query
[1]; 
 416                         SQLite3::int64 id 
= query
[2]; 
 417                         const char *label 
= query
[3]; 
 418                         //sqlite_uint64 ruleFlags = query[4]; 
 419                         SQLite3::int64 disabled 
= query
[5]; 
 421                         CFRef
<SecRequirementRef
> requirement
; 
 422                         MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString
), kSecCSDefaultFlags
, &requirement
.aref())); 
 423                         switch (OSStatus rc 
= SecRequirementEvaluate(requirement
, chain
, NULL
, kSecCSDefaultFlags
)) { 
 424                         case noErr
: // success 
 426                         case errSecCSReqFailed
: // requirement missed, but otherwise okay 
 428                         default: // broken in some way; all tests will fail like this so bail out 
 429                                 MacOSError::throwMe(rc
); 
 437                                 continue;       // the loop 
 440                         if (SYSPOLICY_ASSESS_OUTCOME_ACCEPT_ENABLED() || SYSPOLICY_ASSESS_OUTCOME_DENY_ENABLED()) { 
 442                                         SYSPOLICY_ASSESS_OUTCOME_ACCEPT(cfString(path
).c_str(), type
, label
, NULL
); 
 444                                         SYSPOLICY_ASSESS_OUTCOME_DENY(cfString(path
).c_str(), type
, label
, NULL
); 
 447                         // not adding to the object cache - we could, but it's not likely to be worth it 
 448                         cfadd(result
, "{%O=%B}", kSecAssessmentAssessmentVerdict
, allow
); 
 449                         addAuthority(result
, label
, id
); 
 453         if (SYSPOLICY_ASSESS_OUTCOME_DEFAULT_ENABLED()) 
 454                 SYSPOLICY_ASSESS_OUTCOME_DEFAULT(cfString(path
).c_str(), type
, latentLabel
.c_str(), NULL
); 
 456         // no applicable authority. Deny by default 
 457         cfadd(result
, "{%O=#F}", kSecAssessmentAssessmentVerdict
); 
 458         addAuthority(result
, latentLabel
.c_str(), latentID
); 
 463 // Create a suitable policy array for verification of installer signatures. 
 465 static SecPolicyRef 
makeCRLPolicy() 
 467         CFRef
<SecPolicyRef
> policy
; 
 468         MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
, &CSSMOID_APPLE_TP_REVOCATION_CRL
, &policy
.aref())); 
 469         CSSM_APPLE_TP_CRL_OPTIONS options
; 
 470         memset(&options
, 0, sizeof(options
)); 
 471         options
.Version 
= CSSM_APPLE_TP_CRL_OPTS_VERSION
; 
 472         options
.CrlFlags 
= CSSM_TP_ACTION_FETCH_CRL_FROM_NET 
| CSSM_TP_ACTION_CRL_SUFFICIENT
; 
 473         CSSM_DATA optData 
= { sizeof(options
), (uint8 
*)&options 
}; 
 474         MacOSError::check(SecPolicySetValue(policy
, &optData
)); 
 475         return policy
.yield(); 
 478 static SecPolicyRef 
makeOCSPPolicy() 
 480         CFRef
<SecPolicyRef
> policy
; 
 481         MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
, &CSSMOID_APPLE_TP_REVOCATION_OCSP
, &policy
.aref())); 
 482         CSSM_APPLE_TP_OCSP_OPTIONS options
; 
 483         memset(&options
, 0, sizeof(options
)); 
 484         options
.Version 
= CSSM_APPLE_TP_OCSP_OPTS_VERSION
; 
 485         options
.Flags 
= CSSM_TP_ACTION_OCSP_SUFFICIENT
; 
 486         CSSM_DATA optData 
= { sizeof(options
), (uint8 
*)&options 
}; 
 487         MacOSError::check(SecPolicySetValue(policy
, &optData
)); 
 488         return policy
.yield(); 
 491 static CFTypeRef 
installerPolicy() 
 493         CFRef
<SecPolicyRef
> base 
= SecPolicyCreateBasicX509(); 
 494         CFRef
<SecPolicyRef
> crl 
= makeCRLPolicy(); 
 495         CFRef
<SecPolicyRef
> ocsp 
= makeOCSPPolicy(); 
 496         return makeCFArray(3, base
.get(), crl
.get(), ocsp
.get()); 
 501 // LaunchServices-layer document open. 
 502 // We don't cache those at present. If we ever do, we need to authenticate CoreServicesUIAgent as the source of its risk assessment. 
 504 void PolicyEngine::evaluateDocOpen(CFURLRef path
, SecAssessmentFlags flags
, CFDictionaryRef context
, CFMutableDictionaryRef result
) 
 507                 if (CFStringRef riskCategory 
= CFStringRef(CFDictionaryGetValue(context
, kLSDownloadRiskCategoryKey
))) { 
 508                         FileQuarantine 
qtn(cfString(path
).c_str()); 
 510                         if (CFEqual(riskCategory
, kLSRiskCategorySafe
) 
 511                                 || CFEqual(riskCategory
, kLSRiskCategoryNeutral
) 
 512                                 || CFEqual(riskCategory
, kLSRiskCategoryUnknown
) 
 513                                 || CFEqual(riskCategory
, kLSRiskCategoryMayContainUnsafeExecutable
)) { 
 514                                 cfadd(result
, "{%O=#T}", kSecAssessmentAssessmentVerdict
); 
 515                                 addAuthority(result
, "_XProtect"); 
 516                         } else if (qtn
.flag(QTN_FLAG_HARD
)) { 
 517                                 MacOSError::throwMe(errSecCSFileHardQuarantined
); 
 518                         } else if (qtn
.flag(QTN_FLAG_ASSESSMENT_OK
)) { 
 519                                 cfadd(result
, "{%O=#T}", kSecAssessmentAssessmentVerdict
); 
 520                                 addAuthority(result
, "Prior Assessment"); 
 521                         } else if (!overrideAssessment()) {             // no need to do more work if we're off 
 523                                         evaluateCode(path
, kAuthorityExecute
, flags
, context
, result
, false); 
 525                                         // some documents can't be code signed, so this may be quite benign 
 528                         if (CFDictionaryGetValue(result
, kSecAssessmentAssessmentVerdict
) == NULL
) {    // no code signature to help us out 
 529                            cfadd(result
, "{%O=#F}", kSecAssessmentAssessmentVerdict
); 
 530                            addAuthority(result
, "_XProtect"); 
 532                         addToAuthority(result
, kLSDownloadRiskCategoryKey
, riskCategory
); 
 536         // insufficient information from LS - deny by default 
 537         cfadd(result
, "{%O=#F}", kSecAssessmentAssessmentVerdict
); 
 538         addAuthority(result
, "Insufficient Context"); 
 543 // Result-creation helpers 
 545 void PolicyEngine::addAuthority(CFMutableDictionaryRef parent
, const char *label
, SQLite::int64 row
, CFTypeRef cacheInfo
) 
 547         CFRef
<CFMutableDictionaryRef
> auth 
= makeCFMutableDictionary(); 
 548         if (label 
&& label
[0]) 
 549                 cfadd(auth
, "{%O=%s}", kSecAssessmentAssessmentSource
, label
); 
 551                 CFDictionaryAddValue(auth
, kSecAssessmentAssessmentAuthorityRow
, CFTempNumber(row
)); 
 552         if (overrideAssessment()) 
 553                 CFDictionaryAddValue(auth
, kSecAssessmentAssessmentAuthorityOverride
, kDisabledOverride
); 
 555                 CFDictionaryAddValue(auth
, kSecAssessmentAssessmentFromCache
, cacheInfo
); 
 556         CFDictionaryAddValue(parent
, kSecAssessmentAssessmentAuthority
, auth
); 
 559 void PolicyEngine::addToAuthority(CFMutableDictionaryRef parent
, CFStringRef key
, CFTypeRef value
) 
 561         CFMutableDictionaryRef authority 
= CFMutableDictionaryRef(CFDictionaryGetValue(parent
, kSecAssessmentAssessmentAuthority
)); 
 563         CFDictionaryAddValue(authority
, key
, value
); 
 568 // Add a rule to the policy database 
 570 CFDictionaryRef 
PolicyEngine::add(CFTypeRef inTarget
, AuthorityType type
, SecAssessmentFlags flags
, CFDictionaryRef context
) 
 572         // default type to execution 
 573         if (type 
== kAuthorityInvalid
) 
 574                 type 
= kAuthorityExecute
; 
 576         authorizeUpdate(flags
, context
); 
 577         CFDictionary 
ctx(context
, errSecCSInvalidAttributeValues
); 
 578         CFCopyRef
<CFTypeRef
> target 
= inTarget
; 
 579         CFRef
<CFDataRef
> bookmark 
= NULL
; 
 580         std::string filter_unsigned
; 
 583         case kAuthorityExecute
: 
 584                 normalizeTarget(target
, ctx
, &filter_unsigned
); 
 585                 // bookmarks are untrusted and just a hint to callers 
 586                 bookmark 
= ctx
.get
<CFDataRef
>(kSecAssessmentRuleKeyBookmark
); 
 588         case kAuthorityInstall
: 
 589                 if (inTarget 
&& CFGetTypeID(inTarget
) == CFURLGetTypeID()) { 
 590                         // no good way to turn an installer file into a requirement. Pretend to succeeed so caller proceeds 
 591                         return cfmake
<CFDictionaryRef
>("{%O=%O}", kSecAssessmentAssessmentAuthorityOverride
, CFSTR("virtual install")); 
 594         case kAuthorityOpenDoc
: 
 595                 // handle document-open differently: use quarantine flags for whitelisting 
 596                 if (!target 
|| CFGetTypeID(target
) != CFURLGetTypeID()) // can only "add" file paths 
 597                         MacOSError::throwMe(errSecCSInvalidObjectRef
); 
 599                         std::string spath 
= cfString(target
.as
<CFURLRef
>()); 
 600                         FileQuarantine 
qtn(spath
.c_str()); 
 601                         qtn
.setFlag(QTN_FLAG_ASSESSMENT_OK
); 
 602                         qtn
.applyTo(spath
.c_str()); 
 603                 } catch (const CommonError 
&error
) { 
 604                         // could not set quarantine flag - report qualified success 
 605                         return cfmake
<CFDictionaryRef
>("{%O=%O,'assessment:error'=%d}", 
 606                                 kSecAssessmentAssessmentAuthorityOverride
, CFSTR("error setting quarantine"), error
.osStatus()); 
 608                         return cfmake
<CFDictionaryRef
>("{%O=%O}", kSecAssessmentAssessmentAuthorityOverride
, CFSTR("unable to set quarantine")); 
 613         // if we now have anything else, we're busted 
 614         if (!target 
|| CFGetTypeID(target
) != SecRequirementGetTypeID()) 
 615                 MacOSError::throwMe(errSecCSInvalidObjectRef
); 
 620         double expires 
= never
; 
 623         if (CFNumberRef pri 
= ctx
.get
<CFNumberRef
>(kSecAssessmentUpdateKeyPriority
)) 
 624                 CFNumberGetValue(pri
, kCFNumberDoubleType
, &priority
); 
 625         if (CFStringRef lab 
= ctx
.get
<CFStringRef
>(kSecAssessmentUpdateKeyLabel
)) 
 626                 label 
= cfString(lab
); 
 627         if (CFDateRef time 
= ctx
.get
<CFDateRef
>(kSecAssessmentUpdateKeyExpires
)) 
 628                 // we're using Julian dates here; convert from CFDate 
 629                 expires 
= dateToJulian(time
); 
 630         if (CFBooleanRef allowing 
= ctx
.get
<CFBooleanRef
>(kSecAssessmentUpdateKeyAllow
)) 
 631                 allow 
= allowing 
== kCFBooleanTrue
; 
 632         if (CFStringRef rem 
= ctx
.get
<CFStringRef
>(kSecAssessmentUpdateKeyRemarks
)) 
 633                 remarks 
= cfString(rem
); 
 635         CFRef
<CFStringRef
> requirementText
; 
 636         MacOSError::check(SecRequirementCopyString(target
.as
<SecRequirementRef
>(), kSecCSDefaultFlags
, &requirementText
.aref())); 
 637         SQLite::Transaction 
xact(*this, SQLite3::Transaction::deferred
, "add_rule"); 
 638         SQLite::Statement 
insert(*this, 
 639                 "INSERT INTO authority (type, allow, requirement, priority, label, expires, filter_unsigned, remarks)" 
 640                 "       VALUES (:type, :allow, :requirement, :priority, :label, :expires, :filter_unsigned, :remarks);"); 
 641         insert
.bind(":type").integer(type
); 
 642         insert
.bind(":allow").integer(allow
); 
 643         insert
.bind(":requirement") = requirementText
.get(); 
 644         insert
.bind(":priority") = priority
; 
 646                 insert
.bind(":label") = label
; 
 647         insert
.bind(":expires") = expires
; 
 648         insert
.bind(":filter_unsigned") = filter_unsigned
.empty() ? NULL 
: filter_unsigned
.c_str(); 
 649         if (!remarks
.empty()) 
 650                 insert
.bind(":remarks") = remarks
; 
 652         SQLite::int64 newRow 
= this->lastInsert(); 
 654                 SQLite::Statement 
bi(*this, "INSERT INTO bookmarkhints (bookmark, authority) VALUES (:bookmark, :authority)"); 
 655                 bi
.bind(":bookmark") = CFDataRef(bookmark
); 
 656                 bi
.bind(":authority").integer(newRow
); 
 659         this->purgeObjects(priority
); 
 661         notify_post(kNotifySecAssessmentUpdate
); 
 662         return cfmake
<CFDictionaryRef
>("{%O=%d}", kSecAssessmentUpdateKeyRow
, newRow
); 
 666 CFDictionaryRef 
PolicyEngine::remove(CFTypeRef target
, AuthorityType type
, SecAssessmentFlags flags
, CFDictionaryRef context
) 
 668         if (type 
== kAuthorityOpenDoc
) { 
 669                 // handle document-open differently: use quarantine flags for whitelisting 
 670                 authorizeUpdate(flags
, context
); 
 671                 if (!target 
|| CFGetTypeID(target
) != CFURLGetTypeID()) 
 672                         MacOSError::throwMe(errSecCSInvalidObjectRef
); 
 673                 std::string spath 
= cfString(CFURLRef(target
)).c_str(); 
 674                 FileQuarantine 
qtn(spath
.c_str()); 
 675                 qtn
.clearFlag(QTN_FLAG_ASSESSMENT_OK
); 
 676                 qtn
.applyTo(spath
.c_str()); 
 679         return manipulateRules("DELETE FROM authority", target
, type
, flags
, context
); 
 682 CFDictionaryRef 
PolicyEngine::enable(CFTypeRef target
, AuthorityType type
, SecAssessmentFlags flags
, CFDictionaryRef context
) 
 684         return manipulateRules("UPDATE authority SET disabled = 0", target
, type
, flags
, context
); 
 687 CFDictionaryRef 
PolicyEngine::disable(CFTypeRef target
, AuthorityType type
, SecAssessmentFlags flags
, CFDictionaryRef context
) 
 689         return manipulateRules("UPDATE authority SET disabled = 1", target
, type
, flags
, context
); 
 692 CFDictionaryRef 
PolicyEngine::find(CFTypeRef target
, AuthorityType type
, SecAssessmentFlags flags
, CFDictionaryRef context
) 
 694         SQLite::Statement 
query(*this); 
 695         selectRules(query
, "SELECT scan_authority.id, scan_authority.type, scan_authority.requirement, scan_authority.allow, scan_authority.label, scan_authority.priority, scan_authority.remarks, scan_authority.expires, scan_authority.disabled, bookmarkhints.bookmark FROM scan_authority LEFT OUTER JOIN bookmarkhints ON scan_authority.id = bookmarkhints.authority", 
 696                 "scan_authority", target
, type
, flags
, context
, 
 697                 " ORDER BY priority DESC"); 
 698         CFRef
<CFMutableArrayRef
> found 
= makeCFMutableArray(0); 
 699         while (query
.nextRow()) { 
 700                 SQLite::int64 id 
= query
[0]; 
 701                 int type 
= int(query
[1]); 
 702                 const char *requirement 
= query
[2]; 
 703                 int allow 
= int(query
[3]); 
 704                 const char *label 
= query
[4]; 
 705                 double priority 
= query
[5]; 
 706                 const char *remarks 
= query
[6]; 
 707                 double expires 
= query
[7]; 
 708                 int disabled 
= int(query
[8]); 
 709                 CFRef
<CFDataRef
> bookmark 
= query
[9].data(); 
 710                 CFRef
<CFMutableDictionaryRef
> rule 
= makeCFMutableDictionary(5, 
 711                         kSecAssessmentRuleKeyID
, CFTempNumber(id
).get(), 
 712                         kSecAssessmentRuleKeyType
, CFRef
<CFStringRef
>(typeNameFor(type
)).get(), 
 713                         kSecAssessmentRuleKeyRequirement
, CFTempString(requirement
).get(), 
 714                         kSecAssessmentRuleKeyAllow
, allow 
? kCFBooleanTrue 
: kCFBooleanFalse
, 
 715                         kSecAssessmentRuleKeyPriority
, CFTempNumber(priority
).get() 
 718                         CFDictionaryAddValue(rule
, kSecAssessmentRuleKeyLabel
, CFTempString(label
)); 
 720                         CFDictionaryAddValue(rule
, kSecAssessmentRuleKeyRemarks
, CFTempString(remarks
)); 
 721                 if (expires 
!= never
) 
 722                         CFDictionaryAddValue(rule
, kSecAssessmentRuleKeyExpires
, CFRef
<CFDateRef
>(julianToDate(expires
))); 
 724                         CFDictionaryAddValue(rule
, kSecAssessmentRuleKeyDisabled
, CFTempNumber(disabled
)); 
 726                         CFDictionaryAddValue(rule
, kSecAssessmentRuleKeyBookmark
, bookmark
); 
 727                 CFArrayAppendValue(found
, rule
); 
 729         if (CFArrayGetCount(found
) == 0) 
 730                 MacOSError::throwMe(errSecCSNoMatches
); 
 731         return cfmake
<CFDictionaryRef
>("{%O=%O}", kSecAssessmentUpdateKeyFound
, found
.get()); 
 735 CFDictionaryRef 
PolicyEngine::update(CFTypeRef target
, SecAssessmentFlags flags
, CFDictionaryRef context
) 
 738         installExplicitSet(gkeAuthFile
, gkeSigsFile
); 
 740         AuthorityType type 
= typeFor(context
, kAuthorityInvalid
); 
 741         CFStringRef edit 
= CFStringRef(CFDictionaryGetValue(context
, kSecAssessmentContextKeyUpdate
)); 
 742         CFDictionaryRef result
; 
 743         if (CFEqual(edit
, kSecAssessmentUpdateOperationAdd
)) 
 744                 result 
= this->add(target
, type
, flags
, context
); 
 745         else if (CFEqual(edit
, kSecAssessmentUpdateOperationRemove
)) 
 746                 result 
= this->remove(target
, type
, flags
, context
); 
 747         else if (CFEqual(edit
, kSecAssessmentUpdateOperationEnable
)) 
 748                 result 
= this->enable(target
, type
, flags
, context
); 
 749         else if (CFEqual(edit
, kSecAssessmentUpdateOperationDisable
)) 
 750                 result 
= this->disable(target
, type
, flags
, context
); 
 751         else if (CFEqual(edit
, kSecAssessmentUpdateOperationFind
)) 
 752                 result 
= this->find(target
, type
, flags
, context
); 
 754                 MacOSError::throwMe(errSecCSInvalidAttributeValues
); 
 756                 result 
= makeCFDictionary(0);           // success, no details 
 762 // Construct and prepare an SQL query on the authority table, operating on some set of existing authority records. 
 763 // In essence, this appends a suitable WHERE clause to the stanza passed and prepares it on the statement given. 
 765 void PolicyEngine::selectRules(SQLite::Statement 
&action
, std::string phrase
, std::string table
, 
 766         CFTypeRef inTarget
, AuthorityType type
, SecAssessmentFlags flags
, CFDictionaryRef context
, std::string suffix 
/* = "" */) 
 768         CFDictionary 
ctx(context
, errSecCSInvalidAttributeValues
); 
 769         CFCopyRef
<CFTypeRef
> target 
= inTarget
; 
 770         std::string filter_unsigned
;    // ignored; used just to trigger ad-hoc signing 
 771         normalizeTarget(target
, ctx
, &filter_unsigned
); 
 774         if (CFStringRef lab 
= ctx
.get
<CFStringRef
>(kSecAssessmentUpdateKeyLabel
)) 
 775                 label 
= cfString(CFStringRef(lab
)); 
 779                         if (type 
== kAuthorityInvalid
) { 
 780                                 action
.query(phrase 
+ suffix
); 
 782                                 action
.query(phrase 
+ " WHERE " + table 
+ ".type = :type" + suffix
); 
 783                                 action
.bind(":type").integer(type
); 
 785                 } else {        // have label 
 786                         if (type 
== kAuthorityInvalid
) { 
 787                                 action
.query(phrase 
+ " WHERE " + table 
+ ".label = :label" + suffix
); 
 789                                 action
.query(phrase 
+ " WHERE " + table 
+ ".type = :type AND " + table 
+ ".label = :label" + suffix
); 
 790                                 action
.bind(":type").integer(type
); 
 792                         action
.bind(":label") = label
; 
 794         } else if (CFGetTypeID(target
) == CFNumberGetTypeID()) { 
 795                 action
.query(phrase 
+ " WHERE " + table 
+ ".id = :id" + suffix
); 
 796                 action
.bind(":id").integer(cfNumber
<uint64_t>(target
.as
<CFNumberRef
>())); 
 797         } else if (CFGetTypeID(target
) == SecRequirementGetTypeID()) { 
 798                 if (type 
== kAuthorityInvalid
) 
 799                         type 
= kAuthorityExecute
; 
 800                 CFRef
<CFStringRef
> requirementText
; 
 801                 MacOSError::check(SecRequirementCopyString(target
.as
<SecRequirementRef
>(), kSecCSDefaultFlags
, &requirementText
.aref())); 
 802                 action
.query(phrase 
+ " WHERE " + table 
+ ".type = :type AND " + table 
+ ".requirement = :requirement" + suffix
); 
 803                 action
.bind(":type").integer(type
); 
 804                 action
.bind(":requirement") = requirementText
.get(); 
 806                 MacOSError::throwMe(errSecCSInvalidObjectRef
); 
 811 // Execute an atomic change to existing records in the authority table. 
 813 CFDictionaryRef 
PolicyEngine::manipulateRules(const std::string 
&stanza
, 
 814         CFTypeRef inTarget
, AuthorityType type
, SecAssessmentFlags flags
, CFDictionaryRef context
) 
 816         SQLite::Transaction 
xact(*this, SQLite3::Transaction::deferred
, "rule_change"); 
 817         SQLite::Statement 
action(*this); 
 818         authorizeUpdate(flags
, context
); 
 819         selectRules(action
, stanza
, "authority", inTarget
, type
, flags
, context
); 
 821         unsigned int changes 
= this->changes(); // latch change count 
 822         // We MUST purge objects with priority <= MAX(priority of any changed rules); 
 823         // but for now we just get lazy and purge them ALL. 
 825                 this->purgeObjects(1.0E100
); 
 827                 notify_post(kNotifySecAssessmentUpdate
); 
 828                 return cfmake
<CFDictionaryRef
>("{%O=%d}", kSecAssessmentUpdateKeyCount
, changes
); 
 830         // no change; return an error 
 831         MacOSError::throwMe(errSecCSNoMatches
); 
 836 // Fill in extra information about the originator of cryptographic credentials found - if any 
 838 void PolicyEngine::setOrigin(CFArrayRef chain
, CFMutableDictionaryRef result
) 
 841                 if (CFArrayGetCount(chain
) > 0) 
 842                         if (SecCertificateRef leaf 
= SecCertificateRef(CFArrayGetValueAtIndex(chain
, 0))) 
 843                                 if (CFStringRef summary 
= SecCertificateCopyLongDescription(NULL
, leaf
, NULL
)) { 
 844                                         CFDictionarySetValue(result
, kSecAssessmentAssessmentOriginator
, summary
); 
 851 // Take an assessment outcome and record it in the object cache 
 853 void PolicyEngine::recordOutcome(SecStaticCodeRef code
, bool allow
, AuthorityType type
, double expires
, SQLite::int64 authority
) 
 855         CFRef
<CFDictionaryRef
> info
; 
 856         MacOSError::check(SecCodeCopySigningInformation(code
, kSecCSDefaultFlags
, &info
.aref())); 
 857         CFDataRef cdHash 
= CFDataRef(CFDictionaryGetValue(info
, kSecCodeInfoUnique
)); 
 858         assert(cdHash
);         // was signed 
 859         CFRef
<CFURLRef
> path
; 
 860         MacOSError::check(SecCodeCopyPath(code
, kSecCSDefaultFlags
, &path
.aref())); 
 862         SQLite::Transaction 
xact(*this, SQLite3::Transaction::deferred
, "caching"); 
 863         SQLite::Statement 
insert(*this, 
 864                 "INSERT OR REPLACE INTO object (type, allow, hash, expires, path, authority)" 
 865                 "       VALUES (:type, :allow, :hash, :expires, :path," 
 866                 "       CASE :authority WHEN 0 THEN (SELECT id FROM authority WHERE label = 'No Matching Rule') ELSE :authority END" 
 868         insert
.bind(":type").integer(type
); 
 869         insert
.bind(":allow").integer(allow
); 
 870         insert
.bind(":hash") = cdHash
; 
 871         insert
.bind(":expires") = expires
; 
 872         insert
.bind(":path") = cfString(path
); 
 873         insert
.bind(":authority").integer(authority
); 
 880 // Perform update authorization processing. 
 881 // Throws an exception if authorization is denied. 
 883 static void authorizeUpdate(SecAssessmentFlags flags
, CFDictionaryRef context
) 
 885         AuthorizationRef authorization 
= NULL
; 
 888                 if (CFTypeRef authkey 
= CFDictionaryGetValue(context
, kSecAssessmentUpdateKeyAuthorization
)) 
 889                         if (CFGetTypeID(authkey
) == CFDataGetTypeID()) { 
 890                                 CFDataRef authdata 
= CFDataRef(authkey
); 
 891                                 MacOSError::check(AuthorizationCreateFromExternalForm((AuthorizationExternalForm 
*)CFDataGetBytePtr(authdata
), &authorization
)); 
 893         if (authorization 
== NULL
) 
 894                 MacOSError::check(AuthorizationCreate(NULL
, NULL
, kAuthorizationFlagDefaults
, &authorization
)); 
 896         AuthorizationItem right
[] = { 
 897                 { "com.apple.security.assessment.update", 0, NULL
, 0 } 
 899         AuthorizationRights rights 
= { sizeof(right
) / sizeof(right
[0]), right 
}; 
 900         MacOSError::check(AuthorizationCopyRights(authorization
, &rights
, NULL
, 
 901                 kAuthorizationFlagExtendRights 
| kAuthorizationFlagInteractionAllowed
, NULL
)); 
 903         MacOSError::check(AuthorizationFree(authorization
, kAuthorizationFlagDefaults
)); 
 908 // Perform common argument normalizations for update operations 
 910 static void normalizeTarget(CFRef
<CFTypeRef
> &target
, CFDictionary 
&context
, std::string 
*signUnsigned
) 
 912         // turn CFURLs into (designated) SecRequirements 
 913         if (target 
&& CFGetTypeID(target
) == CFURLGetTypeID()) { 
 914                 CFRef
<SecStaticCodeRef
> code
; 
 915                 MacOSError::check(SecStaticCodeCreateWithPath(target
.as
<CFURLRef
>(), kSecCSDefaultFlags
, &code
.aref())); 
 916                 switch (OSStatus rc 
= SecCodeCopyDesignatedRequirement(code
, kSecCSDefaultFlags
, (SecRequirementRef 
*)&target
.aref())) { 
 918                         // use the *default* DR to avoid unreasonably wide DRs opening up Gatekeeper to attack 
 919                         CFRef
<CFDictionaryRef
> info
; 
 920                         MacOSError::check(SecCodeCopySigningInformation(code
, kSecCSRequirementInformation
, &info
.aref())); 
 921                         target 
= CFDictionaryGetValue(info
, kSecCodeInfoImplicitDesignatedRequirement
); 
 924                 case errSecCSUnsigned
: 
 926                                 // Ad-hoc sign the code temporarily so we can get its code requirement 
 927                                 CFRef
<CFDataRef
> signature 
= CFDataCreateMutable(NULL
, 0); 
 928                                 CFRef
<SecCodeSignerRef
> signer
; 
 929                                 CFTemp
<CFDictionaryRef
> arguments("{%O=%O, %O=#N}", kSecCodeSignerDetached
, signature
.get(), kSecCodeSignerIdentity
); 
 930                                 MacOSError::check(SecCodeSignerCreate(arguments
, kSecCSDefaultFlags
, &signer
.aref())); 
 931                                 MacOSError::check(SecCodeSignerAddSignature(signer
, code
, kSecCSDefaultFlags
)); 
 932                                 MacOSError::check(SecCodeSetDetachedSignature(code
, signature
, kSecCSDefaultFlags
)); 
 933                                 MacOSError::check(SecCodeCopyDesignatedRequirement(code
, kSecCSDefaultFlags
, (SecRequirementRef 
*)&target
.aref())); 
 934                                 CFRef
<CFDictionaryRef
> info
; 
 935                                 MacOSError::check(SecCodeCopySigningInformation(code
, kSecCSInternalInformation
, &info
.aref())); 
 936                                 if (CFDataRef cdData 
= CFDataRef(CFDictionaryGetValue(info
, kSecCodeInfoCodeDirectory
))) 
 937                                         *signUnsigned 
= ((const CodeDirectory 
*)CFDataGetBytePtr(cdData
))->screeningCode(); 
 940                         MacOSError::check(rc
); 
 941                 case errSecCSSignatureFailed
: 
 942                         // recover certain cases of broken signatures (well, try) 
 943                         if (codeInvalidityExceptions(code
, NULL
)) { 
 944                                 // Ad-hoc sign the code in place (requiring a writable subject). This requires root privileges. 
 945                                 CFRef
<SecCodeSignerRef
> signer
; 
 946                                 CFTemp
<CFDictionaryRef
> arguments("{%O=#N}", kSecCodeSignerIdentity
); 
 947                                 MacOSError::check(SecCodeSignerCreate(arguments
, kSecCSDefaultFlags
, &signer
.aref())); 
 948                                 MacOSError::check(SecCodeSignerAddSignature(signer
, code
, kSecCSDefaultFlags
)); 
 949                                 MacOSError::check(SecCodeCopyDesignatedRequirement(code
, kSecCSDefaultFlags
, (SecRequirementRef 
*)&target
.aref())); 
 952                         MacOSError::check(rc
); 
 954                         MacOSError::check(rc
); 
 956                 if (context
.get(kSecAssessmentUpdateKeyRemarks
) == NULL
)        { 
 957                         // no explicit remarks; add one with the path 
 958                         CFRef
<CFURLRef
> path
; 
 959                         MacOSError::check(SecCodeCopyPath(code
, kSecCSDefaultFlags
, &path
.aref())); 
 960                         CFMutableDictionaryRef dict 
= makeCFMutableDictionary(context
.get()); 
 961                         CFDictionaryAddValue(dict
, kSecAssessmentUpdateKeyRemarks
, CFTempString(cfString(path
))); 
 969 // Process special overrides for invalidly signed code. 
 970 // This is the (hopefully minimal) concessions we make to keep hurting our customers 
 971 // for our own prior mistakes... 
 973 static bool codeInvalidityExceptions(SecStaticCodeRef code
, CFMutableDictionaryRef result
) 
 975         if (OSAIsRecognizedExecutableURL
) { 
 976                 CFRef
<CFDictionaryRef
> info
; 
 977                 MacOSError::check(SecCodeCopySigningInformation(code
, kSecCSDefaultFlags
, &info
.aref())); 
 978                 if (CFURLRef executable 
= CFURLRef(CFDictionaryGetValue(info
, kSecCodeInfoMainExecutable
))) { 
 980                         if (OSAIsRecognizedExecutableURL(executable
, &error
)) { 
 982                                         CFDictionaryAddValue(result
, 
 983                                                 kSecAssessmentAssessmentAuthorityOverride
, CFSTR("ignoring known invalid applet signature")); 
 992 } // end namespace CodeSigning 
 993 } // end namespace Security