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>
34 using namespace CodeSigning
;
40 struct _SecAssessment
: private CFRuntimeBase
{
42 _SecAssessment(CFURLRef p
, CFDictionaryRef r
) : path(p
), result(r
) { }
44 CFCopyRef
<CFURLRef
> path
;
45 CFRef
<CFDictionaryRef
> result
;
48 static _SecAssessment
&ref(SecAssessmentRef r
)
49 { return *(_SecAssessment
*)r
; }
52 void *operator new (size_t size
)
54 return (void *)_CFRuntimeCreateInstance(NULL
, SecAssessmentGetTypeID(),
55 sizeof(_SecAssessment
) - sizeof(CFRuntimeBase
), NULL
);
58 static void finalize(CFTypeRef obj
)
59 { ((_SecAssessment
*)obj
)->~_SecAssessment(); }
62 typedef _SecAssessment SecAssessment
;
65 static const CFRuntimeClass assessmentClass
= {
67 "SecAssessment", // name
70 SecAssessment::finalize
, // finalize
78 static dispatch_once_t assessmentOnce
;
79 CFTypeID assessmentType
= _kCFRuntimeNotATypeID
;
81 CFTypeID
SecAssessmentGetTypeID()
84 dispatch_once(&assessmentOnce
, ^void() {
85 if ((assessmentType
= _CFRuntimeRegisterClass(&assessmentClass
)) == _kCFRuntimeNotATypeID
)
88 return assessmentType
;
93 // Common dictionary constants
95 const CFStringRef kSecAssessmentContextKeyOperation
= CFSTR("operation");
96 const CFStringRef kSecAssessmentOperationTypeExecute
= CFSTR("operation:execute");
97 const CFStringRef kSecAssessmentOperationTypeInstall
= CFSTR("operation:install");
98 const CFStringRef kSecAssessmentOperationTypeOpenDocument
= CFSTR("operation:lsopen");
102 // Read-only in-process access to the policy database
104 class ReadPolicy
: public PolicyDatabase
{
106 ReadPolicy() : PolicyDatabase(defaultDatabase
) { }
108 ModuleNexus
<ReadPolicy
> gDatabase
;
112 // An on-demand instance of the policy engine
114 ModuleNexus
<PolicyEngine
> gEngine
;
118 // Help mapping API-ish CFString keys to more convenient internal enumerations
125 static uint
mapEnum(CFDictionaryRef context
, CFStringRef attr
, const StringMap
*map
, uint value
= 0)
128 if (CFTypeRef value
= CFDictionaryGetValue(context
, attr
))
129 for (const StringMap
*mp
= map
; mp
->cstring
; ++mp
)
130 if (CFEqual(mp
->cstring
, value
))
131 return mp
->enumeration
;
134 MacOSError::throwMe(errSecCSInvalidAttributeValues
);
137 static const StringMap mapType
[] = {
138 { kSecAssessmentOperationTypeExecute
, kAuthorityExecute
},
139 { kSecAssessmentOperationTypeInstall
, kAuthorityInstall
},
140 { kSecAssessmentOperationTypeOpenDocument
, kAuthorityOpenDoc
},
144 static AuthorityType
typeFor(CFDictionaryRef context
, AuthorityType type
= kAuthorityInvalid
)
145 { return mapEnum(context
, kSecAssessmentContextKeyOperation
, mapType
, type
); }
149 // Policy evaluation ("assessment") operations
151 const CFStringRef kSecAssessmentContextKeyCertificates
= CFSTR("context:certificates");
153 const CFStringRef kSecAssessmentAssessmentVerdict
= CFSTR("assessment:verdict");
154 const CFStringRef kSecAssessmentAssessmentOriginator
= CFSTR("assessment:originator");
155 const CFStringRef kSecAssessmentAssessmentAuthority
= CFSTR("assessment:authority");
156 const CFStringRef kSecAssessmentAssessmentSource
= CFSTR("assessment:authority:source");
157 const CFStringRef kSecAssessmentAssessmentAuthorityRow
= CFSTR("assessment:authority:row");
158 const CFStringRef kSecAssessmentAssessmentAuthorityOverride
= CFSTR("assessment:authority:override");
159 const CFStringRef kSecAssessmentAssessmentFromCache
= CFSTR("assessment:authority:cached");
161 SecAssessmentRef
SecAssessmentCreate(CFURLRef path
,
162 SecAssessmentFlags flags
,
163 CFDictionaryRef context
,
167 SYSPOLICY_ASSESS_API(cfString(path
).c_str(), flags
);
169 if (flags
& kSecAssessmentFlagAsynchronous
)
170 MacOSError::throwMe(errSecCSUnimplemented
);
172 AuthorityType type
= typeFor(context
, kAuthorityExecute
);
173 CFRef
<CFMutableDictionaryRef
> result
= makeCFMutableDictionary();
176 // check the object cache first unless caller denied that or we need extended processing
177 if (!(flags
& (kSecAssessmentFlagRequestOrigin
| kSecAssessmentFlagIgnoreCache
))) {
178 if (gDatabase().checkCache(path
, type
, result
))
179 return new SecAssessment(path
, result
.yield());
182 if (flags
& kSecAssessmentFlagDirect
) {
183 // ask the engine right here to do its thing
184 SYSPOLICY_ASSESS_LOCAL();
185 gEngine().evaluate(path
, type
, flags
, context
, result
);
187 // relay the question to our daemon for consideration
188 SYSPOLICY_ASSESS_REMOTE();
189 xpcEngineAssess(path
, flags
, context
, result
);
191 } catch (CommonError
&error
) {
192 if (!overrideAssessment())
193 throw; // let it go as an error
194 cfadd(result
, "{%O=#F,'assessment:error'=%d}}", kSecAssessmentAssessmentVerdict
, error
.osStatus());
196 if (!overrideAssessment())
197 throw; // let it go as an error
198 cfadd(result
, "{%O=#F}", kSecAssessmentAssessmentVerdict
);
200 return new SecAssessment(path
, result
.yield());
202 END_CSAPI_ERRORS1(NULL
)
206 static void traceResult(SecAssessment
&assessment
, CFDictionaryRef result
)
208 if (CFDictionaryGetValue(result
, CFSTR("assessment:remote")))
209 return; // just traced in syspolicyd
211 CFRef
<CFURLRef
> url
= CFURLCopyAbsoluteURL(assessment
.path
);
212 string sanitized
= cfString(url
);
213 string::size_type rslash
= sanitized
.rfind('/');
214 if (rslash
!= string::npos
)
215 sanitized
= sanitized
.substr(rslash
+1);
216 string::size_type dot
= sanitized
.rfind('.');
217 if (dot
!= string::npos
)
218 sanitized
= sanitized
.substr(dot
+1);
220 sanitized
= "(none)";
222 string identifier
= "UNBUNDLED";
223 if (CFRef
<CFBundleRef
> bundle
= CFBundleCreate(NULL
, assessment
.path
))
224 if (CFStringRef ident
= CFBundleGetIdentifier(bundle
))
225 identifier
= cfString(ident
);
227 string authority
= "UNSPECIFIED";
228 bool overridden
= false;
229 if (CFDictionaryRef authdict
= CFDictionaryRef(CFDictionaryGetValue(result
, kSecAssessmentAssessmentAuthority
))) {
230 if (CFStringRef auth
= CFStringRef(CFDictionaryGetValue(authdict
, kSecAssessmentAssessmentSource
)))
231 authority
= cfString(auth
);
233 authority
= "no authority";
234 if (CFDictionaryGetValue(authdict
, kSecAssessmentAssessmentAuthorityOverride
))
238 MessageTrace
trace("com.apple.security.assessment.outcome", NULL
);
239 trace
.add("signature2", "bundle:%s", identifier
.c_str());
240 if (CFDictionaryGetValue(result
, kSecAssessmentAssessmentVerdict
) == kCFBooleanFalse
) {
241 trace
.add("signature", "denied:%s", authority
.c_str());
242 trace
.add("signature3", sanitized
.c_str());
243 trace
.send("assessment denied for %s", sanitized
.c_str());
244 } else if (overridden
) {
245 trace
.add("signature", "override:%s", authority
.c_str());
246 trace
.add("signature3", sanitized
.c_str());
247 trace
.send("assessment denied for %s but overridden", sanitized
.c_str());
249 trace
.add("signature", "granted:%s", authority
.c_str());
250 trace
.add("signature3", sanitized
.c_str());
251 trace
.send("assessment granted for %s by %s", sanitized
.c_str(), authority
.c_str());
257 // At present, CopyResult simply retrieves the result already formed by Create.
258 // In the future, this will be more lazy.
260 CFDictionaryRef
SecAssessmentCopyResult(SecAssessmentRef assessmentRef
,
261 SecAssessmentFlags flags
,
266 SecAssessment
&assessment
= SecAssessment::ref(assessmentRef
);
267 CFCopyRef
<CFDictionaryRef
> result
= assessment
.result
;
268 if (!(flags
& kSecAssessmentFlagEnforce
) && overrideAssessment()) {
269 // turn rejections into approvals, but note that we did that
270 if (CFDictionaryGetValue(result
, kSecAssessmentAssessmentVerdict
) == kCFBooleanFalse
) {
271 CFRef
<CFMutableDictionaryRef
> adulterated
= makeCFMutableDictionary(result
.get());
272 CFDictionarySetValue(adulterated
, kSecAssessmentAssessmentVerdict
, kCFBooleanTrue
);
273 if (CFDictionaryRef authority
= CFDictionaryRef(CFDictionaryGetValue(adulterated
, kSecAssessmentAssessmentAuthority
))) {
274 CFRef
<CFMutableDictionaryRef
> authority2
= makeCFMutableDictionary(authority
);
275 CFDictionarySetValue(authority2
, kSecAssessmentAssessmentAuthorityOverride
, CFSTR("security disabled"));
276 CFDictionarySetValue(adulterated
, kSecAssessmentAssessmentAuthority
, authority2
);
278 cfadd(adulterated
, "{%O={%O='security disabled'}}",
279 kSecAssessmentAssessmentAuthority
, kSecAssessmentAssessmentAuthorityOverride
);
281 result
= adulterated
.get();
284 traceResult(assessment
, result
);
285 return result
.yield();
287 END_CSAPI_ERRORS1(NULL
)
292 // Policy editing operations
294 const CFStringRef kSecAssessmentContextKeyUpdate
= CFSTR("update");
295 const CFStringRef kSecAssessmentUpdateOperationAddFile
= CFSTR("update:addfile");
296 const CFStringRef kSecAssessmentUpdateOperationRemoveFile
= CFSTR("update:removefile");
298 const CFStringRef kSecAssessmentUpdateKeyPriority
= CFSTR("update:priority");
299 const CFStringRef kSecAssessmentUpdateKeyLabel
= CFSTR("update:label");
301 Boolean
SecAssessmentUpdate(CFURLRef path
,
302 SecAssessmentFlags flags
,
303 CFDictionaryRef context
,
308 CFDictionary
ctx(context
, errSecCSInvalidAttributeValues
);
309 CFStringRef edit
= ctx
.get
<CFStringRef
>(kSecAssessmentContextKeyUpdate
);
311 AuthorityType type
= typeFor(context
);
312 if (edit
== kSecAssessmentUpdateOperationAddFile
)
313 return gEngine().add(path
, type
, flags
, context
);
314 else if (edit
== kSecAssessmentUpdateOperationRemoveFile
)
315 MacOSError::throwMe(errSecCSUnimplemented
);
317 MacOSError::throwMe(errSecCSInvalidAttributeValues
);
319 END_CSAPI_ERRORS1(false)
324 // The fcntl of System Policies.
325 // For those very special requests.
327 Boolean
SecAssessmentControl(CFStringRef control
, void *arguments
, CFErrorRef
*errors
)
331 if (CFEqual(control
, CFSTR("ui-enable"))) {
332 UnixPlusPlus::AutoFileDesc
flagFile(visibleSecurityFlagFile
, O_CREAT
| O_WRONLY
, 0644);
333 MessageTrace
trace("com.apple.security.assessment.state", "enable");
334 trace
.send("enable assessment outcomes");
336 } else if (CFEqual(control
, CFSTR("ui-disable"))) {
337 if (::remove(visibleSecurityFlagFile
) == 0 || errno
== ENOENT
) {
338 MessageTrace
trace("com.apple.security.assessment.state", "disable");
339 trace
.send("disable assessment outcomes");
342 UnixError::throwMe();
343 } else if (CFEqual(control
, CFSTR("ui-status"))) {
344 CFBooleanRef
&result
= *(CFBooleanRef
*)(arguments
);
345 if (overrideAssessment())
346 result
= kCFBooleanFalse
;
348 result
= kCFBooleanTrue
;
351 MacOSError::throwMe(errSecCSInvalidAttributeValues
);
353 END_CSAPI_ERRORS1(false)