]> git.saurik.com Git - apple/security.git/blob - libsecurity_codesigning/lib/SecAssessment.cpp
Security-55179.11.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / SecAssessment.cpp
1 /*
2 * Copyright (c) 2011 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 #include "cs.h"
24 #include "SecAssessment.h"
25 #include "policydb.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>
33 #include <notify.h>
34
35 using namespace CodeSigning;
36
37
38 //
39 // CF Objects
40 //
41 struct _SecAssessment : private CFRuntimeBase {
42 public:
43 _SecAssessment(CFURLRef p, AuthorityType typ, CFDictionaryRef r) : path(p), type(typ), result(r) { }
44
45 CFCopyRef<CFURLRef> path;
46 AuthorityType type;
47 CFRef<CFDictionaryRef> result;
48
49 public:
50 static _SecAssessment &ref(SecAssessmentRef r)
51 { return *(_SecAssessment *)r; }
52
53 // CF Boiler-plate
54 void *operator new (size_t size)
55 {
56 return (void *)_CFRuntimeCreateInstance(NULL, SecAssessmentGetTypeID(),
57 sizeof(_SecAssessment) - sizeof(CFRuntimeBase), NULL);
58 }
59
60 static void finalize(CFTypeRef obj)
61 { ((_SecAssessment *)obj)->~_SecAssessment(); }
62 };
63
64 typedef _SecAssessment SecAssessment;
65
66
67 static const CFRuntimeClass assessmentClass = {
68 0, // version
69 "SecAssessment", // name
70 NULL, // init
71 NULL, // copy
72 SecAssessment::finalize, // finalize
73 NULL, // equal
74 NULL, // hash
75 NULL, // formatting
76 NULL // debug string
77 };
78
79
80 static dispatch_once_t assessmentOnce;
81 CFTypeID assessmentType = _kCFRuntimeNotATypeID;
82
83 CFTypeID SecAssessmentGetTypeID()
84 {
85
86 dispatch_once(&assessmentOnce, ^void() {
87 if ((assessmentType = _CFRuntimeRegisterClass(&assessmentClass)) == _kCFRuntimeNotATypeID)
88 abort();
89 });
90 return assessmentType;
91 }
92
93
94 //
95 // Common dictionary constants
96 //
97 CFStringRef kSecAssessmentContextKeyOperation = CFSTR("operation");
98 CFStringRef kSecAssessmentOperationTypeExecute = CFSTR("operation:execute");
99 CFStringRef kSecAssessmentOperationTypeInstall = CFSTR("operation:install");
100 CFStringRef kSecAssessmentOperationTypeOpenDocument = CFSTR("operation:lsopen");
101
102
103 //
104 // Read-only in-process access to the policy database
105 //
106 class ReadPolicy : public PolicyDatabase {
107 public:
108 ReadPolicy() : PolicyDatabase(defaultDatabase) { }
109 };
110 ModuleNexus<ReadPolicy> gDatabase;
111
112
113 //
114 // An on-demand instance of the policy engine
115 //
116 ModuleNexus<PolicyEngine> gEngine;
117
118
119 //
120 // Policy evaluation ("assessment") operations
121 //
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");
129
130 CFStringRef kDisabledOverride = CFSTR("security disabled");
131
132 CFStringRef kSecAssessmentContextKeyCertificates = CFSTR("context:certificates"); // obsolete
133
134 SecAssessmentRef SecAssessmentCreate(CFURLRef path,
135 SecAssessmentFlags flags,
136 CFDictionaryRef context,
137 CFErrorRef *errors)
138 {
139 BEGIN_CSAPI
140
141 if (flags & kSecAssessmentFlagAsynchronous)
142 MacOSError::throwMe(errSecCSUnimplemented);
143
144 AuthorityType type = typeFor(context, kAuthorityExecute);
145 CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
146
147 SYSPOLICY_ASSESS_API(cfString(path).c_str(), int(type), flags);
148
149 try {
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());
154 }
155
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);
160 } else {
161 // relay the question to our daemon for consideration
162 SYSPOLICY_ASSESS_REMOTE();
163 xpcEngineAssess(path, flags, context, result);
164 }
165 } catch (CommonError &error) {
166 switch (error.osStatus()) {
167 case CSSMERR_TP_CERT_REVOKED:
168 throw;
169 default:
170 if (!overrideAssessment())
171 throw; // let it go as an error
172 break;
173 }
174 // record the error we would have returned
175 cfadd(result, "{%O=#F,'assessment:error'=%d}}", kSecAssessmentAssessmentVerdict, error.osStatus());
176 } catch (...) {
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);
181 }
182 return new SecAssessment(path, type, result.yield());
183
184 END_CSAPI_ERRORS1(NULL)
185 }
186
187
188 static void traceResult(SecAssessment &assessment, AuthorityType type, CFDictionaryRef result)
189 {
190 if (CFDictionaryGetValue(result, CFSTR("assessment:remote")))
191 return; // just traced in syspolicyd
192
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);
201 else
202 sanitized = "(none)";
203
204 string identifier = "UNBUNDLED";
205 if (CFRef<CFBundleRef> bundle = CFBundleCreate(NULL, assessment.path))
206 if (CFStringRef ident = CFBundleGetIdentifier(bundle))
207 identifier = cfString(ident);
208
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);
214 else
215 authority = "no authority";
216 if (CFTypeRef override = CFDictionaryGetValue(authdict, kSecAssessmentAssessmentAuthorityOverride))
217 if (CFEqual(override, kDisabledOverride))
218 overridden = true;
219 }
220
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());
232 } else {
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());
236 }
237 }
238
239
240 //
241 // At present, CopyResult simply retrieves the result already formed by Create.
242 // In the future, this will be more lazy.
243 //
244 CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessmentRef,
245 SecAssessmentFlags flags,
246 CFErrorRef *errors)
247 {
248 BEGIN_CSAPI
249
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);
261 } else {
262 cfadd(adulterated, "{%O={%O=%O}}",
263 kSecAssessmentAssessmentAuthority, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride);
264 }
265 result = adulterated.get();
266 }
267 }
268 traceResult(assessment, assessment.type, result);
269 return result.yield();
270
271 END_CSAPI_ERRORS1(NULL)
272 }
273
274
275 //
276 // Policy editing operations.
277 // These all make permanent changes to the system-wide authority records.
278 //
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");
285
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");
292
293 CFStringRef kSecAssessmentUpdateKeyRow = CFSTR("update:row");
294 CFStringRef kSecAssessmentUpdateKeyCount = CFSTR("update:count");
295 CFStringRef kSecAssessmentUpdateKeyFound = CFSTR("update:found");
296
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");
307
308
309 Boolean SecAssessmentUpdate(CFTypeRef target,
310 SecAssessmentFlags flags,
311 CFDictionaryRef context,
312 CFErrorRef *errors)
313 {
314 if (CFDictionaryRef outcome = SecAssessmentCopyUpdate(target, flags, context, errors)) {
315 CFRelease(outcome);
316 return true;
317 } else {
318 return false;
319 }
320 }
321
322 CFDictionaryRef SecAssessmentCopyUpdate(CFTypeRef target,
323 SecAssessmentFlags flags,
324 CFDictionaryRef context,
325 CFErrorRef *errors)
326 {
327 BEGIN_CSAPI
328
329 CFDictionary ctx(context, errSecCSInvalidAttributeValues);
330
331 if (flags & kSecAssessmentFlagDirect) {
332 // ask the engine right here to do its thing
333 return gEngine().update(target, flags, ctx);
334 } else {
335 // relay the question to our daemon for consideration
336 return xpcEngineUpdate(target, flags, ctx);
337 }
338
339 END_CSAPI_ERRORS1(false)
340 }
341
342
343 //
344 // The fcntl of System Policies.
345 // For those very special requests.
346 //
347 Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *errors)
348 {
349 BEGIN_CSAPI
350
351 if (CFEqual(control, CFSTR("ui-enable"))) {
352 setAssessment(true);
353 MessageTrace trace("com.apple.security.assessment.state", "enable");
354 trace.send("enable assessment outcomes");
355 return true;
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");
360 return true;
361 } else if (CFEqual(control, CFSTR("ui-status"))) {
362 CFBooleanRef &result = *(CFBooleanRef*)(arguments);
363 if (overrideAssessment())
364 result = kCFBooleanFalse;
365 else
366 result = kCFBooleanTrue;
367 return true;
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))
371 CFRelease(result);
372 MessageTrace trace("com.apple.security.assessment.state", "enable-devid");
373 trace.send("enable Developer ID approval");
374 return true;
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))
378 CFRelease(result);
379 MessageTrace trace("com.apple.security.assessment.state", "disable-devid");
380 trace.send("disable Developer ID approval");
381 return true;
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;
386 else
387 result = kCFBooleanTrue;
388 return true;
389 } else
390 MacOSError::throwMe(errSecCSInvalidAttributeValues);
391
392 END_CSAPI_ERRORS1(false)
393 }