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)