]> git.saurik.com Git - apple/security.git/blame - libsecurity_codesigning/lib/SecAssessment.cpp
Security-55178.0.1.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / SecAssessment.cpp
CommitLineData
b1ab9ed8
A
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
35using namespace CodeSigning;
36
37
38//
39// CF Objects
40//
41struct _SecAssessment : private CFRuntimeBase {
42public:
43 _SecAssessment(CFURLRef p, CFDictionaryRef r) : path(p), result(r) { }
44
45 CFCopyRef<CFURLRef> path;
46 CFRef<CFDictionaryRef> result;
47
48public:
49 static _SecAssessment &ref(SecAssessmentRef r)
50 { return *(_SecAssessment *)r; }
51
52 // CF Boiler-plate
53 void *operator new (size_t size)
54 {
55 return (void *)_CFRuntimeCreateInstance(NULL, SecAssessmentGetTypeID(),
56 sizeof(_SecAssessment) - sizeof(CFRuntimeBase), NULL);
57 }
58
59 static void finalize(CFTypeRef obj)
60 { ((_SecAssessment *)obj)->~_SecAssessment(); }
61};
62
63typedef _SecAssessment SecAssessment;
64
65
66static const CFRuntimeClass assessmentClass = {
67 0, // version
68 "SecAssessment", // name
69 NULL, // init
70 NULL, // copy
71 SecAssessment::finalize, // finalize
72 NULL, // equal
73 NULL, // hash
74 NULL, // formatting
75 NULL // debug string
76};
77
78
79static dispatch_once_t assessmentOnce;
80CFTypeID assessmentType = _kCFRuntimeNotATypeID;
81
82CFTypeID SecAssessmentGetTypeID()
83{
84
85 dispatch_once(&assessmentOnce, ^void() {
86 if ((assessmentType = _CFRuntimeRegisterClass(&assessmentClass)) == _kCFRuntimeNotATypeID)
87 abort();
88 });
89 return assessmentType;
90}
91
92
93//
94// Common dictionary constants
95//
96CFStringRef kSecAssessmentContextKeyOperation = CFSTR("operation");
97CFStringRef kSecAssessmentOperationTypeExecute = CFSTR("operation:execute");
98CFStringRef kSecAssessmentOperationTypeInstall = CFSTR("operation:install");
99CFStringRef kSecAssessmentOperationTypeOpenDocument = CFSTR("operation:lsopen");
100
101
102//
103// Read-only in-process access to the policy database
104//
105class ReadPolicy : public PolicyDatabase {
106public:
107 ReadPolicy() : PolicyDatabase(defaultDatabase) { }
108};
109ModuleNexus<ReadPolicy> gDatabase;
110
111
112//
113// An on-demand instance of the policy engine
114//
115ModuleNexus<PolicyEngine> gEngine;
116
117
118//
119// Policy evaluation ("assessment") operations
120//
121CFStringRef kSecAssessmentAssessmentVerdict = CFSTR("assessment:verdict");
122CFStringRef kSecAssessmentAssessmentOriginator = CFSTR("assessment:originator");
123CFStringRef kSecAssessmentAssessmentAuthority = CFSTR("assessment:authority");
124CFStringRef kSecAssessmentAssessmentSource = CFSTR("assessment:authority:source");
125CFStringRef kSecAssessmentAssessmentAuthorityRow = CFSTR("assessment:authority:row");
126CFStringRef kSecAssessmentAssessmentAuthorityOverride = CFSTR("assessment:authority:override");
127CFStringRef kSecAssessmentAssessmentFromCache = CFSTR("assessment:authority:cached");
128
129CFStringRef kDisabledOverride = CFSTR("security disabled");
130
131CFStringRef kSecAssessmentContextKeyCertificates = CFSTR("context:certificates"); // obsolete
132
133SecAssessmentRef SecAssessmentCreate(CFURLRef path,
134 SecAssessmentFlags flags,
135 CFDictionaryRef context,
136 CFErrorRef *errors)
137{
138 BEGIN_CSAPI
139
140 if (flags & kSecAssessmentFlagAsynchronous)
141 MacOSError::throwMe(errSecCSUnimplemented);
142
143 AuthorityType type = typeFor(context, kAuthorityExecute);
144 CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
145
146 SYSPOLICY_ASSESS_API(cfString(path).c_str(), int(type), flags);
147
148 try {
149 // check the object cache first unless caller denied that or we need extended processing
150 if (!(flags & (kSecAssessmentFlagRequestOrigin | kSecAssessmentFlagIgnoreCache))) {
151 if (gDatabase().checkCache(path, type, result))
152 return new SecAssessment(path, result.yield());
153 }
154
155 if (flags & kSecAssessmentFlagDirect) {
156 // ask the engine right here to do its thing
157 SYSPOLICY_ASSESS_LOCAL();
158 gEngine().evaluate(path, type, flags, context, result);
159 } else {
160 // relay the question to our daemon for consideration
161 SYSPOLICY_ASSESS_REMOTE();
162 xpcEngineAssess(path, flags, context, result);
163 }
164 } catch (CommonError &error) {
165 switch (error.osStatus()) {
166 case CSSMERR_TP_CERT_REVOKED:
167 throw;
168 default:
169 if (!overrideAssessment())
170 throw; // let it go as an error
171 break;
172 }
173 // record the error we would have returned
174 cfadd(result, "{%O=#F,'assessment:error'=%d}}", kSecAssessmentAssessmentVerdict, error.osStatus());
175 } catch (...) {
176 // catch stray errors not conforming to the CommonError scheme
177 if (!overrideAssessment())
178 throw; // let it go as an error
179 cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
180 }
181 return new SecAssessment(path, result.yield());
182
183 END_CSAPI_ERRORS1(NULL)
184}
185
186
187static void traceResult(SecAssessment &assessment, CFDictionaryRef result)
188{
189 if (CFDictionaryGetValue(result, CFSTR("assessment:remote")))
190 return; // just traced in syspolicyd
191
192 CFRef<CFURLRef> url = CFURLCopyAbsoluteURL(assessment.path);
193 string sanitized = cfString(url);
194 string::size_type rslash = sanitized.rfind('/');
195 if (rslash != string::npos)
196 sanitized = sanitized.substr(rslash+1);
197 string::size_type dot = sanitized.rfind('.');
198 if (dot != string::npos)
199 sanitized = sanitized.substr(dot+1);
200 else
201 sanitized = "(none)";
202
203 string identifier = "UNBUNDLED";
204 if (CFRef<CFBundleRef> bundle = CFBundleCreate(NULL, assessment.path))
205 if (CFStringRef ident = CFBundleGetIdentifier(bundle))
206 identifier = cfString(ident);
207
208 string authority = "UNSPECIFIED";
209 bool overridden = false;
210 if (CFDictionaryRef authdict = CFDictionaryRef(CFDictionaryGetValue(result, kSecAssessmentAssessmentAuthority))) {
211 if (CFStringRef auth = CFStringRef(CFDictionaryGetValue(authdict, kSecAssessmentAssessmentSource)))
212 authority = cfString(auth);
213 else
214 authority = "no authority";
215 if (CFTypeRef override = CFDictionaryGetValue(authdict, kSecAssessmentAssessmentAuthorityOverride))
216 if (CFEqual(override, kDisabledOverride))
217 overridden = true;
218 }
219
220 MessageTrace trace("com.apple.security.assessment.outcome", NULL);
221 trace.add("signature2", "bundle:%s", identifier.c_str());
222 if (CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict) == kCFBooleanFalse) {
223 trace.add("signature", "denied:%s", authority.c_str());
224 trace.add("signature3", sanitized.c_str());
225 trace.send("assessment denied for %s", sanitized.c_str());
226 } else if (overridden) {
227 trace.add("signature", "override:%s", authority.c_str());
228 trace.add("signature3", sanitized.c_str());
229 trace.send("assessment denied for %s but overridden", sanitized.c_str());
230 } else {
231 trace.add("signature", "granted:%s", authority.c_str());
232 trace.add("signature3", sanitized.c_str());
233 trace.send("assessment granted for %s by %s", sanitized.c_str(), authority.c_str());
234 }
235}
236
237
238//
239// At present, CopyResult simply retrieves the result already formed by Create.
240// In the future, this will be more lazy.
241//
242CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessmentRef,
243 SecAssessmentFlags flags,
244 CFErrorRef *errors)
245{
246 BEGIN_CSAPI
247
248 SecAssessment &assessment = SecAssessment::ref(assessmentRef);
249 CFCopyRef<CFDictionaryRef> result = assessment.result;
250 if (!(flags & kSecAssessmentFlagEnforce) && overrideAssessment()) {
251 // turn rejections into approvals, but note that we did that
252 if (CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict) == kCFBooleanFalse) {
253 CFRef<CFMutableDictionaryRef> adulterated = makeCFMutableDictionary(result.get());
254 CFDictionarySetValue(adulterated, kSecAssessmentAssessmentVerdict, kCFBooleanTrue);
255 if (CFDictionaryRef authority = CFDictionaryRef(CFDictionaryGetValue(adulterated, kSecAssessmentAssessmentAuthority))) {
256 CFRef<CFMutableDictionaryRef> authority2 = makeCFMutableDictionary(authority);
257 CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride);
258 CFDictionarySetValue(adulterated, kSecAssessmentAssessmentAuthority, authority2);
259 } else {
260 cfadd(adulterated, "{%O={%O=%O}}",
261 kSecAssessmentAssessmentAuthority, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride);
262 }
263 result = adulterated.get();
264 }
265 }
266 traceResult(assessment, result);
267 return result.yield();
268
269 END_CSAPI_ERRORS1(NULL)
270}
271
272
273//
274// Policy editing operations.
275// These all make permanent changes to the system-wide authority records.
276//
277CFStringRef kSecAssessmentContextKeyUpdate = CFSTR("update");
278CFStringRef kSecAssessmentUpdateOperationAdd = CFSTR("update:add");
279CFStringRef kSecAssessmentUpdateOperationRemove = CFSTR("update:remove");
280CFStringRef kSecAssessmentUpdateOperationEnable = CFSTR("update:enable");
281CFStringRef kSecAssessmentUpdateOperationDisable = CFSTR("update:disable");
282CFStringRef kSecAssessmentUpdateOperationFind = CFSTR("update:find");
283
284CFStringRef kSecAssessmentUpdateKeyAuthorization = CFSTR("update:authorization");
285CFStringRef kSecAssessmentUpdateKeyPriority = CFSTR("update:priority");
286CFStringRef kSecAssessmentUpdateKeyLabel = CFSTR("update:label");
287CFStringRef kSecAssessmentUpdateKeyExpires = CFSTR("update:expires");
288CFStringRef kSecAssessmentUpdateKeyAllow = CFSTR("update:allow");
289CFStringRef kSecAssessmentUpdateKeyRemarks = CFSTR("update:remarks");
290
291CFStringRef kSecAssessmentUpdateKeyRow = CFSTR("update:row");
292CFStringRef kSecAssessmentUpdateKeyCount = CFSTR("update:count");
293CFStringRef kSecAssessmentUpdateKeyFound = CFSTR("update:found");
294
295CFStringRef kSecAssessmentRuleKeyID = CFSTR("rule:id");
296CFStringRef kSecAssessmentRuleKeyPriority = CFSTR("rule:priority");
297CFStringRef kSecAssessmentRuleKeyAllow = CFSTR("rule:allow");
298CFStringRef kSecAssessmentRuleKeyLabel = CFSTR("rule:label");
299CFStringRef kSecAssessmentRuleKeyRemarks = CFSTR("rule:remarks");
300CFStringRef kSecAssessmentRuleKeyRequirement = CFSTR("rule:requirement");
301CFStringRef kSecAssessmentRuleKeyType = CFSTR("rule:type");
302CFStringRef kSecAssessmentRuleKeyExpires = CFSTR("rule:expires");
303CFStringRef kSecAssessmentRuleKeyDisabled = CFSTR("rule:disabled");
304CFStringRef kSecAssessmentRuleKeyBookmark = CFSTR("rule:bookmark");
305
306
307Boolean SecAssessmentUpdate(CFTypeRef target,
308 SecAssessmentFlags flags,
309 CFDictionaryRef context,
310 CFErrorRef *errors)
311{
312 if (CFDictionaryRef outcome = SecAssessmentCopyUpdate(target, flags, context, errors)) {
313 CFRelease(outcome);
314 return true;
315 } else {
316 return false;
317 }
318}
319
320CFDictionaryRef SecAssessmentCopyUpdate(CFTypeRef target,
321 SecAssessmentFlags flags,
322 CFDictionaryRef context,
323 CFErrorRef *errors)
324{
325 BEGIN_CSAPI
326
327 CFDictionary ctx(context, errSecCSInvalidAttributeValues);
328
329 if (flags & kSecAssessmentFlagDirect) {
330 // ask the engine right here to do its thing
331 return gEngine().update(target, flags, ctx);
332 } else {
333 // relay the question to our daemon for consideration
334 return xpcEngineUpdate(target, flags, ctx);
335 }
336
337 END_CSAPI_ERRORS1(false)
338}
339
340
341//
342// The fcntl of System Policies.
343// For those very special requests.
344//
345Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *errors)
346{
347 BEGIN_CSAPI
348
349 if (CFEqual(control, CFSTR("ui-enable"))) {
350 setAssessment(true);
351 MessageTrace trace("com.apple.security.assessment.state", "enable");
352 trace.send("enable assessment outcomes");
353 return true;
354 } else if (CFEqual(control, CFSTR("ui-disable"))) {
355 setAssessment(false);
356 MessageTrace trace("com.apple.security.assessment.state", "disable");
357 trace.send("disable assessment outcomes");
358 return true;
359 } else if (CFEqual(control, CFSTR("ui-status"))) {
360 CFBooleanRef &result = *(CFBooleanRef*)(arguments);
361 if (overrideAssessment())
362 result = kCFBooleanFalse;
363 else
364 result = kCFBooleanTrue;
365 return true;
366 } else if (CFEqual(control, CFSTR("ui-enable-devid"))) {
367 CFTemp<CFDictionaryRef> ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel, "Developer ID");
368 if (CFDictionaryRef result = gEngine().enable(NULL, kAuthorityInvalid, kSecCSDefaultFlags, ctx))
369 CFRelease(result);
370 return true;
371 } else if (CFEqual(control, CFSTR("ui-disable-devid"))) {
372 CFTemp<CFDictionaryRef> ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel, "Developer ID");
373 if (CFDictionaryRef result = gEngine().disable(NULL, kAuthorityInvalid, kSecCSDefaultFlags, ctx))
374 CFRelease(result);
375 return true;
376 } else if (CFEqual(control, CFSTR("ui-get-devid"))) {
377 CFBooleanRef &result = *(CFBooleanRef*)(arguments);
378 if (gEngine().value<int>("SELECT disabled FROM authority WHERE label = 'Developer ID';", true))
379 result = kCFBooleanFalse;
380 else
381 result = kCFBooleanTrue;
382 return true;
383 } else
384 MacOSError::throwMe(errSecCSInvalidAttributeValues);
385
386 END_CSAPI_ERRORS1(false)
387}