2  * Copyright (c) 2011 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" 
  29 #include <CoreFoundation/CFRuntime.h> 
  30 #include <security_utilities/globalizer.h> 
  31 #include <security_utilities/unix++.h> 
  32 #include <security_utilities/cfmunge.h> 
  35 using namespace CodeSigning
; 
  41 struct _SecAssessment 
: private CFRuntimeBase 
{ 
  43         _SecAssessment(CFURLRef p
, AuthorityType typ
, CFDictionaryRef r
) : path(p
), type(typ
), result(r
) { } 
  45         CFCopyRef
<CFURLRef
> path
; 
  47         CFRef
<CFDictionaryRef
> result
; 
  50         static _SecAssessment 
&ref(SecAssessmentRef r
) 
  51                 { return *(_SecAssessment 
*)r
; } 
  54         void *operator new (size_t size
) 
  56                 return (void *)_CFRuntimeCreateInstance(NULL
, SecAssessmentGetTypeID(), 
  57                         sizeof(_SecAssessment
) - sizeof(CFRuntimeBase
), NULL
); 
  60         static void finalize(CFTypeRef obj
) 
  61         { ((_SecAssessment 
*)obj
)->~_SecAssessment(); } 
  64 typedef _SecAssessment SecAssessment
; 
  67 static const CFRuntimeClass assessmentClass 
= { 
  69         "SecAssessment",                                // name 
  72         SecAssessment::finalize
,                // finalize 
  80 static dispatch_once_t assessmentOnce
; 
  81 CFTypeID assessmentType 
= _kCFRuntimeNotATypeID
; 
  83 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 kSecAssessmentAssessmentVerdict 
= CFSTR("assessment:verdict"); 
 123 CFStringRef kSecAssessmentAssessmentOriginator 
= CFSTR("assessment:originator"); 
 124 CFStringRef kSecAssessmentAssessmentAuthority 
= CFSTR("assessment:authority"); 
 125 CFStringRef kSecAssessmentAssessmentSource 
= CFSTR("assessment:authority:source"); 
 126 CFStringRef kSecAssessmentAssessmentAuthorityRow 
= CFSTR("assessment:authority:row"); 
 127 CFStringRef kSecAssessmentAssessmentAuthorityOverride 
= CFSTR("assessment:authority:override"); 
 128 CFStringRef kSecAssessmentAssessmentFromCache 
= CFSTR("assessment:authority:cached"); 
 130 CFStringRef kDisabledOverride 
= CFSTR("security disabled"); 
 132 CFStringRef kSecAssessmentContextKeyCertificates 
= CFSTR("context:certificates");       // obsolete 
 134 SecAssessmentRef 
SecAssessmentCreate(CFURLRef path
, 
 135         SecAssessmentFlags flags
, 
 136         CFDictionaryRef context
, 
 141         if (flags 
& kSecAssessmentFlagAsynchronous
) 
 142                 MacOSError::throwMe(errSecCSUnimplemented
); 
 144         AuthorityType type 
= typeFor(context
, kAuthorityExecute
); 
 145         CFRef
<CFMutableDictionaryRef
> result 
= makeCFMutableDictionary(); 
 147         SYSPOLICY_ASSESS_API(cfString(path
).c_str(), int(type
), flags
); 
 150                 // check the object cache first unless caller denied that or we need extended processing 
 151                 if (!(flags 
& (kSecAssessmentFlagRequestOrigin 
| kSecAssessmentFlagIgnoreCache
))) { 
 152                         if (gDatabase().checkCache(path
, type
, result
)) 
 153                                 return new SecAssessment(path
, type
, result
.yield()); 
 156                 if (flags 
& kSecAssessmentFlagDirect
) { 
 157                         // ask the engine right here to do its thing 
 158                         SYSPOLICY_ASSESS_LOCAL(); 
 159                         gEngine().evaluate(path
, type
, flags
, context
, result
); 
 161                         // relay the question to our daemon for consideration 
 162                         SYSPOLICY_ASSESS_REMOTE(); 
 163                         xpcEngineAssess(path
, flags
, context
, result
); 
 165         } catch (CommonError 
&error
) { 
 166                 switch (error
.osStatus()) { 
 167                 case CSSMERR_TP_CERT_REVOKED
: 
 170                         if (!overrideAssessment()) 
 171                                 throw;          // let it go as an error 
 174                 // record the error we would have returned 
 175                 cfadd(result
, "{%O=#F,'assessment:error'=%d}}", kSecAssessmentAssessmentVerdict
, error
.osStatus()); 
 177                 // catch stray errors not conforming to the CommonError scheme 
 178                 if (!overrideAssessment()) 
 179                         throw;          // let it go as an error 
 180                 cfadd(result
, "{%O=#F}", kSecAssessmentAssessmentVerdict
); 
 182         return new SecAssessment(path
, type
, result
.yield()); 
 184         END_CSAPI_ERRORS1(NULL
) 
 188 static void traceResult(SecAssessment 
&assessment
, AuthorityType type
, CFDictionaryRef result
) 
 190         if (CFDictionaryGetValue(result
, CFSTR("assessment:remote"))) 
 191                 return;         // just traced in syspolicyd 
 193         CFRef
<CFURLRef
> url 
= CFURLCopyAbsoluteURL(assessment
.path
); 
 194         string sanitized 
= cfString(url
); 
 195         string::size_type rslash 
= sanitized
.rfind('/'); 
 196         if (rslash 
!= string::npos
) 
 197                 sanitized 
= sanitized
.substr(rslash
+1); 
 198         string::size_type dot 
= sanitized
.rfind('.'); 
 199         if (dot 
!= string::npos
) 
 200                 sanitized 
= sanitized
.substr(dot
+1); 
 202                 sanitized 
= "(none)"; 
 204         string identifier 
= "UNBUNDLED"; 
 205         if (CFRef
<CFBundleRef
> bundle 
= CFBundleCreate(NULL
, assessment
.path
)) 
 206                 if (CFStringRef ident 
= CFBundleGetIdentifier(bundle
)) 
 207                         identifier 
= cfString(ident
); 
 209         string authority 
= "UNSPECIFIED"; 
 210         bool overridden 
= false; 
 211         if (CFDictionaryRef authdict 
= CFDictionaryRef(CFDictionaryGetValue(result
, kSecAssessmentAssessmentAuthority
))) { 
 212                 if (CFStringRef auth 
= CFStringRef(CFDictionaryGetValue(authdict
, kSecAssessmentAssessmentSource
))) 
 213                         authority 
= cfString(auth
); 
 215                         authority 
= "no authority"; 
 216                 if (CFTypeRef override 
= CFDictionaryGetValue(authdict
, kSecAssessmentAssessmentAuthorityOverride
)) 
 217                         if (CFEqual(override
, kDisabledOverride
)) 
 221         MessageTrace 
trace("com.apple.security.assessment.outcome", NULL
); 
 222         trace
.add("signature2", "bundle:%s", identifier
.c_str()); 
 223         trace
.add("signature4", "%d", type
); 
 224         if (CFDictionaryGetValue(result
, kSecAssessmentAssessmentVerdict
) == kCFBooleanFalse
) { 
 225                 trace
.add("signature", "denied:%s", authority
.c_str()); 
 226                 trace
.add("signature3", sanitized
.c_str()); 
 227                 trace
.send("assessment denied for %s", sanitized
.c_str()); 
 228         } else if (overridden
) { 
 229                 trace
.add("signature", "override:%s", authority
.c_str()); 
 230                 trace
.add("signature3", sanitized
.c_str()); 
 231                 trace
.send("assessment denied for %s but overridden", sanitized
.c_str()); 
 233                 trace
.add("signature", "granted:%s", authority
.c_str()); 
 234                 trace
.add("signature3", sanitized
.c_str()); 
 235                 trace
.send("assessment granted for %s by %s", sanitized
.c_str(), authority
.c_str()); 
 241 // At present, CopyResult simply retrieves the result already formed by Create. 
 242 // In the future, this will be more lazy. 
 244 CFDictionaryRef 
SecAssessmentCopyResult(SecAssessmentRef assessmentRef
, 
 245         SecAssessmentFlags flags
, 
 250         SecAssessment 
&assessment 
= SecAssessment::ref(assessmentRef
); 
 251         CFCopyRef
<CFDictionaryRef
> result 
= assessment
.result
; 
 252         if (!(flags 
& kSecAssessmentFlagEnforce
) && overrideAssessment()) { 
 253                 // turn rejections into approvals, but note that we did that 
 254                 if (CFDictionaryGetValue(result
, kSecAssessmentAssessmentVerdict
) == kCFBooleanFalse
) { 
 255                         CFRef
<CFMutableDictionaryRef
> adulterated 
= makeCFMutableDictionary(result
.get()); 
 256                         CFDictionarySetValue(adulterated
, kSecAssessmentAssessmentVerdict
, kCFBooleanTrue
); 
 257                         if (CFDictionaryRef authority 
= CFDictionaryRef(CFDictionaryGetValue(adulterated
, kSecAssessmentAssessmentAuthority
))) { 
 258                                 CFRef
<CFMutableDictionaryRef
> authority2 
= makeCFMutableDictionary(authority
); 
 259                                 CFDictionarySetValue(authority2
, kSecAssessmentAssessmentAuthorityOverride
, kDisabledOverride
); 
 260                                 CFDictionarySetValue(adulterated
, kSecAssessmentAssessmentAuthority
, authority2
); 
 262                                 cfadd(adulterated
, "{%O={%O=%O}}", 
 263                                         kSecAssessmentAssessmentAuthority
, kSecAssessmentAssessmentAuthorityOverride
, kDisabledOverride
); 
 265                         result 
= adulterated
.get(); 
 268         traceResult(assessment
, assessment
.type
, result
); 
 269         return result
.yield(); 
 271         END_CSAPI_ERRORS1(NULL
) 
 276 // Policy editing operations. 
 277 // These all make permanent changes to the system-wide authority records. 
 279 CFStringRef kSecAssessmentContextKeyUpdate 
= CFSTR("update"); 
 280 CFStringRef kSecAssessmentUpdateOperationAdd 
= CFSTR("update:add"); 
 281 CFStringRef kSecAssessmentUpdateOperationRemove 
= CFSTR("update:remove"); 
 282 CFStringRef kSecAssessmentUpdateOperationEnable 
= CFSTR("update:enable"); 
 283 CFStringRef kSecAssessmentUpdateOperationDisable 
= CFSTR("update:disable"); 
 284 CFStringRef kSecAssessmentUpdateOperationFind 
= CFSTR("update:find"); 
 286 CFStringRef kSecAssessmentUpdateKeyAuthorization 
= CFSTR("update:authorization"); 
 287 CFStringRef kSecAssessmentUpdateKeyPriority 
= CFSTR("update:priority"); 
 288 CFStringRef kSecAssessmentUpdateKeyLabel 
= CFSTR("update:label"); 
 289 CFStringRef kSecAssessmentUpdateKeyExpires 
= CFSTR("update:expires"); 
 290 CFStringRef kSecAssessmentUpdateKeyAllow 
= CFSTR("update:allow"); 
 291 CFStringRef kSecAssessmentUpdateKeyRemarks 
= CFSTR("update:remarks"); 
 293 CFStringRef kSecAssessmentUpdateKeyRow 
= CFSTR("update:row"); 
 294 CFStringRef kSecAssessmentUpdateKeyCount 
= CFSTR("update:count"); 
 295 CFStringRef kSecAssessmentUpdateKeyFound 
= CFSTR("update:found"); 
 297 CFStringRef kSecAssessmentRuleKeyID 
= CFSTR("rule:id"); 
 298 CFStringRef kSecAssessmentRuleKeyPriority 
= CFSTR("rule:priority"); 
 299 CFStringRef kSecAssessmentRuleKeyAllow 
= CFSTR("rule:allow"); 
 300 CFStringRef kSecAssessmentRuleKeyLabel 
= CFSTR("rule:label"); 
 301 CFStringRef kSecAssessmentRuleKeyRemarks 
= CFSTR("rule:remarks"); 
 302 CFStringRef kSecAssessmentRuleKeyRequirement 
= CFSTR("rule:requirement"); 
 303 CFStringRef kSecAssessmentRuleKeyType 
= CFSTR("rule:type"); 
 304 CFStringRef kSecAssessmentRuleKeyExpires 
= CFSTR("rule:expires"); 
 305 CFStringRef kSecAssessmentRuleKeyDisabled 
= CFSTR("rule:disabled"); 
 306 CFStringRef kSecAssessmentRuleKeyBookmark 
= CFSTR("rule:bookmark"); 
 309 Boolean 
SecAssessmentUpdate(CFTypeRef target
, 
 310         SecAssessmentFlags flags
, 
 311         CFDictionaryRef context
, 
 314         if (CFDictionaryRef outcome 
= SecAssessmentCopyUpdate(target
, flags
, context
, errors
)) { 
 322 CFDictionaryRef 
SecAssessmentCopyUpdate(CFTypeRef target
, 
 323         SecAssessmentFlags flags
, 
 324         CFDictionaryRef context
, 
 329         CFDictionary 
ctx(context
, errSecCSInvalidAttributeValues
); 
 331         if (flags 
& kSecAssessmentFlagDirect
) { 
 332                 // ask the engine right here to do its thing 
 333                 return gEngine().update(target
, flags
, ctx
); 
 335                 // relay the question to our daemon for consideration 
 336                 return xpcEngineUpdate(target
, flags
, ctx
); 
 339         END_CSAPI_ERRORS1(false) 
 344 // The fcntl of System Policies. 
 345 // For those very special requests. 
 347 Boolean 
SecAssessmentControl(CFStringRef control
, void *arguments
, CFErrorRef 
*errors
) 
 351         if (CFEqual(control
, CFSTR("ui-enable"))) { 
 353                 MessageTrace 
trace("com.apple.security.assessment.state", "enable"); 
 354                 trace
.send("enable assessment outcomes"); 
 356         } else if (CFEqual(control
, CFSTR("ui-disable"))) { 
 357                 setAssessment(false); 
 358                 MessageTrace 
trace("com.apple.security.assessment.state", "disable"); 
 359                 trace
.send("disable assessment outcomes"); 
 361         } else if (CFEqual(control
, CFSTR("ui-status"))) { 
 362                 CFBooleanRef 
&result 
= *(CFBooleanRef
*)(arguments
); 
 363                 if (overrideAssessment()) 
 364                         result 
= kCFBooleanFalse
; 
 366                         result 
= kCFBooleanTrue
; 
 368         } else if (CFEqual(control
, CFSTR("ui-enable-devid"))) { 
 369                 CFTemp
<CFDictionaryRef
> ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel
, "Developer ID"); 
 370                 if (CFDictionaryRef result 
= gEngine().enable(NULL
, kAuthorityInvalid
, kSecCSDefaultFlags
, ctx
)) 
 372                 MessageTrace 
trace("com.apple.security.assessment.state", "enable-devid"); 
 373                 trace
.send("enable Developer ID approval"); 
 375         } else if (CFEqual(control
, CFSTR("ui-disable-devid"))) { 
 376                 CFTemp
<CFDictionaryRef
> ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel
, "Developer ID"); 
 377                 if (CFDictionaryRef result 
= gEngine().disable(NULL
, kAuthorityInvalid
, kSecCSDefaultFlags
, ctx
)) 
 379                 MessageTrace 
trace("com.apple.security.assessment.state", "disable-devid"); 
 380                 trace
.send("disable Developer ID approval"); 
 382         } else if (CFEqual(control
, CFSTR("ui-get-devid"))) { 
 383                 CFBooleanRef 
&result 
= *(CFBooleanRef
*)(arguments
); 
 384                 if (gEngine().value
<int>("SELECT disabled FROM authority WHERE label = 'Developer ID';", true)) 
 385                         result 
= kCFBooleanFalse
; 
 387                         result 
= kCFBooleanTrue
; 
 390                 MacOSError::throwMe(errSecCSInvalidAttributeValues
); 
 392         END_CSAPI_ERRORS1(false)