]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
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> | |
fa7225c8 | 30 | #include <CoreFoundation/CFBundlePriv.h> |
b1ab9ed8 A |
31 | #include <security_utilities/globalizer.h> |
32 | #include <security_utilities/unix++.h> | |
33 | #include <security_utilities/cfmunge.h> | |
34 | #include <notify.h> | |
427c49bc | 35 | #include <esp.h> |
b1ab9ed8 A |
36 | |
37 | using namespace CodeSigning; | |
38 | ||
39 | ||
427c49bc A |
40 | static void esp_do_check(const char *op, CFDictionaryRef dict) |
41 | { | |
42 | OSStatus result = __esp_check_ns(op, (void *)(CFDictionaryRef)dict); | |
43 | if (result != noErr) | |
44 | MacOSError::throwMe(result); | |
45 | } | |
46 | ||
b1ab9ed8 A |
47 | // |
48 | // CF Objects | |
49 | // | |
50 | struct _SecAssessment : private CFRuntimeBase { | |
51 | public: | |
d8f41ccd | 52 | _SecAssessment(CFURLRef p, AuthorityType typ, CFDictionaryRef r) : path(p), type(typ), result(r) { } |
b1ab9ed8 A |
53 | |
54 | CFCopyRef<CFURLRef> path; | |
313fa17b | 55 | AuthorityType type; |
b1ab9ed8 A |
56 | CFRef<CFDictionaryRef> result; |
57 | ||
58 | public: | |
59 | static _SecAssessment &ref(SecAssessmentRef r) | |
60 | { return *(_SecAssessment *)r; } | |
61 | ||
62 | // CF Boiler-plate | |
63 | void *operator new (size_t size) | |
64 | { | |
65 | return (void *)_CFRuntimeCreateInstance(NULL, SecAssessmentGetTypeID(), | |
66 | sizeof(_SecAssessment) - sizeof(CFRuntimeBase), NULL); | |
67 | } | |
68 | ||
69 | static void finalize(CFTypeRef obj) | |
70 | { ((_SecAssessment *)obj)->~_SecAssessment(); } | |
71 | }; | |
72 | ||
73 | typedef _SecAssessment SecAssessment; | |
74 | ||
75 | ||
76 | static const CFRuntimeClass assessmentClass = { | |
77 | 0, // version | |
78 | "SecAssessment", // name | |
79 | NULL, // init | |
80 | NULL, // copy | |
81 | SecAssessment::finalize, // finalize | |
82 | NULL, // equal | |
83 | NULL, // hash | |
84 | NULL, // formatting | |
85 | NULL // debug string | |
86 | }; | |
87 | ||
88 | ||
89 | static dispatch_once_t assessmentOnce; | |
90 | CFTypeID assessmentType = _kCFRuntimeNotATypeID; | |
91 | ||
92 | CFTypeID SecAssessmentGetTypeID() | |
93 | { | |
b1ab9ed8 A |
94 | dispatch_once(&assessmentOnce, ^void() { |
95 | if ((assessmentType = _CFRuntimeRegisterClass(&assessmentClass)) == _kCFRuntimeNotATypeID) | |
96 | abort(); | |
97 | }); | |
98 | return assessmentType; | |
99 | } | |
100 | ||
101 | ||
102 | // | |
103 | // Common dictionary constants | |
104 | // | |
105 | CFStringRef kSecAssessmentContextKeyOperation = CFSTR("operation"); | |
106 | CFStringRef kSecAssessmentOperationTypeExecute = CFSTR("operation:execute"); | |
107 | CFStringRef kSecAssessmentOperationTypeInstall = CFSTR("operation:install"); | |
108 | CFStringRef kSecAssessmentOperationTypeOpenDocument = CFSTR("operation:lsopen"); | |
109 | ||
110 | ||
111 | // | |
112 | // Read-only in-process access to the policy database | |
113 | // | |
114 | class ReadPolicy : public PolicyDatabase { | |
115 | public: | |
116 | ReadPolicy() : PolicyDatabase(defaultDatabase) { } | |
117 | }; | |
118 | ModuleNexus<ReadPolicy> gDatabase; | |
119 | ||
120 | ||
121 | // | |
122 | // An on-demand instance of the policy engine | |
123 | // | |
124 | ModuleNexus<PolicyEngine> gEngine; | |
125 | ||
126 | ||
127 | // | |
128 | // Policy evaluation ("assessment") operations | |
129 | // | |
641423b6 A |
130 | CFStringRef kSecAssessmentContextKeyUTI = CFSTR("context:uti"); |
131 | ||
d8f41ccd A |
132 | CFStringRef kSecAssessmentContextKeyFeedback = CFSTR("context:feedback"); |
133 | CFStringRef kSecAssessmentFeedbackProgress = CFSTR("feedback:progress"); | |
134 | CFStringRef kSecAssessmentFeedbackInfoCurrent = CFSTR("current"); | |
135 | CFStringRef kSecAssessmentFeedbackInfoTotal = CFSTR("total"); | |
136 | ||
fa7225c8 A |
137 | CFStringRef kSecAssessmentContextKeyPrimarySignature = CFSTR("context:primary-signature"); |
138 | ||
b1ab9ed8 A |
139 | CFStringRef kSecAssessmentAssessmentVerdict = CFSTR("assessment:verdict"); |
140 | CFStringRef kSecAssessmentAssessmentOriginator = CFSTR("assessment:originator"); | |
141 | CFStringRef kSecAssessmentAssessmentAuthority = CFSTR("assessment:authority"); | |
142 | CFStringRef kSecAssessmentAssessmentSource = CFSTR("assessment:authority:source"); | |
143 | CFStringRef kSecAssessmentAssessmentAuthorityRow = CFSTR("assessment:authority:row"); | |
144 | CFStringRef kSecAssessmentAssessmentAuthorityOverride = CFSTR("assessment:authority:override"); | |
427c49bc | 145 | CFStringRef kSecAssessmentAssessmentAuthorityOriginalVerdict = CFSTR("assessment:authority:verdict"); |
fa7225c8 | 146 | CFStringRef kSecAssessmentAssessmentAuthorityFlags = CFSTR("assessment:authority:flags"); |
b1ab9ed8 | 147 | CFStringRef kSecAssessmentAssessmentFromCache = CFSTR("assessment:authority:cached"); |
80e23899 A |
148 | CFStringRef kSecAssessmentAssessmentWeakSignature = CFSTR("assessment:authority:weak"); |
149 | CFStringRef kSecAssessmentAssessmentCodeSigningError = CFSTR("assessment:cserror"); | |
79b9da22 | 150 | CFStringRef kSecAssessmentAssessmentNotarizationDate = CFSTR("assessment:notarization-date"); |
b1ab9ed8 A |
151 | |
152 | CFStringRef kDisabledOverride = CFSTR("security disabled"); | |
153 | ||
b1ab9ed8 A |
154 | SecAssessmentRef SecAssessmentCreate(CFURLRef path, |
155 | SecAssessmentFlags flags, | |
156 | CFDictionaryRef context, | |
157 | CFErrorRef *errors) | |
158 | { | |
159 | BEGIN_CSAPI | |
160 | ||
161 | if (flags & kSecAssessmentFlagAsynchronous) | |
162 | MacOSError::throwMe(errSecCSUnimplemented); | |
163 | ||
164 | AuthorityType type = typeFor(context, kAuthorityExecute); | |
165 | CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary(); | |
166 | ||
167 | SYSPOLICY_ASSESS_API(cfString(path).c_str(), int(type), flags); | |
168 | ||
169 | try { | |
427c49bc A |
170 | if (__esp_enabled() && (flags & kSecAssessmentFlagDirect)) { |
171 | CFTemp<CFDictionaryRef> dict("{path=%O, flags=%d, context=%O, override=%d}", path, flags, context, overrideAssessment()); | |
172 | esp_do_check("cs-assessment-evaluate", dict); | |
173 | } | |
174 | ||
b1ab9ed8 A |
175 | if (flags & kSecAssessmentFlagDirect) { |
176 | // ask the engine right here to do its thing | |
177 | SYSPOLICY_ASSESS_LOCAL(); | |
178 | gEngine().evaluate(path, type, flags, context, result); | |
179 | } else { | |
180 | // relay the question to our daemon for consideration | |
181 | SYSPOLICY_ASSESS_REMOTE(); | |
182 | xpcEngineAssess(path, flags, context, result); | |
183 | } | |
184 | } catch (CommonError &error) { | |
185 | switch (error.osStatus()) { | |
186 | case CSSMERR_TP_CERT_REVOKED: | |
187 | throw; | |
188 | default: | |
427c49bc | 189 | if (!overrideAssessment(flags)) |
b1ab9ed8 A |
190 | throw; // let it go as an error |
191 | break; | |
192 | } | |
193 | // record the error we would have returned | |
194 | cfadd(result, "{%O=#F,'assessment:error'=%d}}", kSecAssessmentAssessmentVerdict, error.osStatus()); | |
195 | } catch (...) { | |
196 | // catch stray errors not conforming to the CommonError scheme | |
427c49bc | 197 | if (!overrideAssessment(flags)) |
b1ab9ed8 A |
198 | throw; // let it go as an error |
199 | cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict); | |
200 | } | |
427c49bc A |
201 | |
202 | if (__esp_enabled() && (flags & kSecAssessmentFlagDirect)) { | |
203 | CFTemp<CFDictionaryRef> dict("{path=%O, flags=%d, context=%O, override=%d, result=%O}", path, flags, context, overrideAssessment(), (CFDictionaryRef)result); | |
204 | __esp_notify_ns("cs-assessment-evaluate", (void *)(CFDictionaryRef)dict); | |
205 | } | |
206 | ||
d8f41ccd | 207 | return new SecAssessment(path, type, result.yield()); |
b1ab9ed8 A |
208 | |
209 | END_CSAPI_ERRORS1(NULL) | |
210 | } | |
211 | ||
212 | ||
427c49bc | 213 | static void traceResult(CFURLRef target, MessageTrace &trace, std::string &sanitized) |
b1ab9ed8 | 214 | { |
427c49bc A |
215 | static const char *interestingBundles[] = { |
216 | "UNBUNDLED", | |
217 | "com.apple.", | |
218 | "com.install4j.", | |
219 | "com.MindVision.", | |
220 | "com.yourcompany.", | |
221 | ||
222 | "com.adobe.flashplayer.installmanager", | |
223 | "com.adobe.Installers.Setup", | |
224 | "com.adobe.PDApp.setup", | |
225 | "com.bittorrent.uTorrent", | |
226 | "com.divx.divx6formacinstaller", | |
227 | "com.getdropbox.dropbox", | |
228 | "com.google.Chrome", | |
229 | "com.Google.GoogleEarthPlugin.plugin", | |
230 | "com.Google.GoogleEarthPlus", | |
231 | "com.hp.Installer", | |
232 | "com.macpaw.CleanMyMac", | |
233 | "com.microsoft.SilverlightInstaller", | |
234 | "com.paragon-software.filesystems.NTFS.pkg", | |
235 | "com.RealNetworks.RealPlayer", | |
236 | "com.skype.skype", | |
237 | "it.alfanet.squared5.MPEGStreamclip", | |
238 | "org.mozilla.firefox", | |
239 | "org.videolan.vlc", | |
240 | ||
241 | NULL // sentinel | |
242 | }; | |
b1ab9ed8 A |
243 | |
244 | string identifier = "UNBUNDLED"; | |
427c49bc | 245 | string version = "UNKNOWN"; |
fa7225c8 | 246 | if (CFRef<CFBundleRef> bundle = _CFBundleCreateUnique(NULL, target)) { |
b1ab9ed8 A |
247 | if (CFStringRef ident = CFBundleGetIdentifier(bundle)) |
248 | identifier = cfString(ident); | |
427c49bc A |
249 | if (CFStringRef vers = CFStringRef(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString")))) |
250 | version = cfString(vers); | |
251 | } | |
252 | ||
253 | CFRef<CFURLRef> url = CFURLCopyAbsoluteURL(target); | |
254 | sanitized = cfString(url); | |
255 | string::size_type rslash = sanitized.rfind('/'); | |
256 | if (rslash != string::npos) | |
257 | sanitized = sanitized.substr(rslash+1); | |
258 | bool keepFilename = false; | |
259 | for (const char **pfx = interestingBundles; *pfx; pfx++) { | |
260 | size_t pfxlen = strlen(*pfx); | |
261 | if (identifier.compare(0, pfxlen, *pfx, pfxlen) == 0) | |
262 | if (pfxlen == identifier.size() || (*pfx)[pfxlen-1] == '.') { | |
263 | keepFilename = true; | |
264 | break; | |
265 | } | |
266 | } | |
267 | if (!keepFilename) { | |
268 | string::size_type dot = sanitized.rfind('.'); | |
269 | if (dot != string::npos) | |
270 | sanitized = sanitized.substr(dot); | |
271 | else | |
272 | sanitized = "(none)"; | |
273 | } | |
274 | ||
275 | trace.add("signature2", "bundle:%s", identifier.c_str()); | |
276 | trace.add("signature3", "%s", sanitized.c_str()); | |
277 | trace.add("signature5", "%s", version.c_str()); | |
278 | } | |
d8f41ccd | 279 | |
427c49bc A |
280 | static void traceAssessment(SecAssessment &assessment, AuthorityType type, CFDictionaryRef result) |
281 | { | |
282 | if (CFDictionaryGetValue(result, CFSTR("assessment:remote"))) | |
283 | return; // just traced in syspolicyd | |
b1ab9ed8 A |
284 | |
285 | string authority = "UNSPECIFIED"; | |
286 | bool overridden = false; | |
427c49bc | 287 | bool old_overridden = false; |
b1ab9ed8 A |
288 | if (CFDictionaryRef authdict = CFDictionaryRef(CFDictionaryGetValue(result, kSecAssessmentAssessmentAuthority))) { |
289 | if (CFStringRef auth = CFStringRef(CFDictionaryGetValue(authdict, kSecAssessmentAssessmentSource))) | |
290 | authority = cfString(auth); | |
291 | else | |
292 | authority = "no authority"; | |
293 | if (CFTypeRef override = CFDictionaryGetValue(authdict, kSecAssessmentAssessmentAuthorityOverride)) | |
427c49bc A |
294 | if (CFEqual(override, kDisabledOverride)) { |
295 | old_overridden = true; | |
296 | if (CFDictionaryGetValue(authdict, kSecAssessmentAssessmentAuthorityOriginalVerdict) == kCFBooleanFalse) | |
297 | overridden = true; | |
298 | } | |
b1ab9ed8 | 299 | } |
427c49bc A |
300 | |
301 | MessageTrace trace("com.apple.security.assessment.outcome2", NULL); | |
302 | std::string sanitized; | |
303 | traceResult(assessment.path, trace, sanitized); | |
313fa17b | 304 | trace.add("signature4", "%d", type); |
427c49bc | 305 | |
b1ab9ed8 A |
306 | if (CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict) == kCFBooleanFalse) { |
307 | trace.add("signature", "denied:%s", authority.c_str()); | |
b1ab9ed8 | 308 | trace.send("assessment denied for %s", sanitized.c_str()); |
427c49bc A |
309 | } else if (overridden) { // would have failed except for override |
310 | trace.add("signature", "defeated:%s", authority.c_str()); | |
b1ab9ed8 | 311 | trace.send("assessment denied for %s but overridden", sanitized.c_str()); |
427c49bc A |
312 | } else if (old_overridden) { // would have succeeded even without override |
313 | trace.add("signature", "override:%s", authority.c_str()); | |
314 | trace.send("assessment granted for %s and overridden", sanitized.c_str()); | |
b1ab9ed8 A |
315 | } else { |
316 | trace.add("signature", "granted:%s", authority.c_str()); | |
b1ab9ed8 A |
317 | trace.send("assessment granted for %s by %s", sanitized.c_str(), authority.c_str()); |
318 | } | |
319 | } | |
320 | ||
427c49bc A |
321 | static void traceUpdate(CFTypeRef target, CFDictionaryRef context, CFDictionaryRef result) |
322 | { | |
323 | // only trace add operations on URL targets | |
324 | if (target == NULL || CFGetTypeID(target) != CFURLGetTypeID()) | |
325 | return; | |
326 | CFStringRef edit = CFStringRef(CFDictionaryGetValue(context, kSecAssessmentContextKeyUpdate)); | |
327 | if (!CFEqual(edit, kSecAssessmentUpdateOperationAdd)) | |
328 | return; | |
329 | MessageTrace trace("com.apple.security.assessment.update", NULL); | |
330 | std::string sanitized; | |
331 | traceResult(CFURLRef(target), trace, sanitized); | |
332 | trace.send("added rule for %s", sanitized.c_str()); | |
333 | } | |
334 | ||
b1ab9ed8 A |
335 | |
336 | // | |
337 | // At present, CopyResult simply retrieves the result already formed by Create. | |
338 | // In the future, this will be more lazy. | |
339 | // | |
340 | CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessmentRef, | |
341 | SecAssessmentFlags flags, | |
342 | CFErrorRef *errors) | |
343 | { | |
344 | BEGIN_CSAPI | |
345 | ||
346 | SecAssessment &assessment = SecAssessment::ref(assessmentRef); | |
347 | CFCopyRef<CFDictionaryRef> result = assessment.result; | |
427c49bc | 348 | if (overrideAssessment(flags)) { |
b1ab9ed8 | 349 | // turn rejections into approvals, but note that we did that |
427c49bc A |
350 | CFTypeRef verdict = CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict); |
351 | if (verdict == kCFBooleanFalse) { | |
b1ab9ed8 A |
352 | CFRef<CFMutableDictionaryRef> adulterated = makeCFMutableDictionary(result.get()); |
353 | CFDictionarySetValue(adulterated, kSecAssessmentAssessmentVerdict, kCFBooleanTrue); | |
354 | if (CFDictionaryRef authority = CFDictionaryRef(CFDictionaryGetValue(adulterated, kSecAssessmentAssessmentAuthority))) { | |
355 | CFRef<CFMutableDictionaryRef> authority2 = makeCFMutableDictionary(authority); | |
356 | CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride); | |
427c49bc | 357 | CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOriginalVerdict, verdict); |
b1ab9ed8 A |
358 | CFDictionarySetValue(adulterated, kSecAssessmentAssessmentAuthority, authority2); |
359 | } else { | |
360 | cfadd(adulterated, "{%O={%O=%O}}", | |
361 | kSecAssessmentAssessmentAuthority, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride); | |
362 | } | |
363 | result = adulterated.get(); | |
364 | } | |
365 | } | |
d8f41ccd | 366 | traceAssessment(assessment, assessment.type, result); |
b1ab9ed8 A |
367 | return result.yield(); |
368 | ||
369 | END_CSAPI_ERRORS1(NULL) | |
370 | } | |
371 | ||
372 | ||
373 | // | |
374 | // Policy editing operations. | |
375 | // These all make permanent changes to the system-wide authority records. | |
376 | // | |
377 | CFStringRef kSecAssessmentContextKeyUpdate = CFSTR("update"); | |
378 | CFStringRef kSecAssessmentUpdateOperationAdd = CFSTR("update:add"); | |
379 | CFStringRef kSecAssessmentUpdateOperationRemove = CFSTR("update:remove"); | |
380 | CFStringRef kSecAssessmentUpdateOperationEnable = CFSTR("update:enable"); | |
381 | CFStringRef kSecAssessmentUpdateOperationDisable = CFSTR("update:disable"); | |
382 | CFStringRef kSecAssessmentUpdateOperationFind = CFSTR("update:find"); | |
383 | ||
384 | CFStringRef kSecAssessmentUpdateKeyAuthorization = CFSTR("update:authorization"); | |
385 | CFStringRef kSecAssessmentUpdateKeyPriority = CFSTR("update:priority"); | |
386 | CFStringRef kSecAssessmentUpdateKeyLabel = CFSTR("update:label"); | |
387 | CFStringRef kSecAssessmentUpdateKeyExpires = CFSTR("update:expires"); | |
388 | CFStringRef kSecAssessmentUpdateKeyAllow = CFSTR("update:allow"); | |
389 | CFStringRef kSecAssessmentUpdateKeyRemarks = CFSTR("update:remarks"); | |
390 | ||
391 | CFStringRef kSecAssessmentUpdateKeyRow = CFSTR("update:row"); | |
392 | CFStringRef kSecAssessmentUpdateKeyCount = CFSTR("update:count"); | |
393 | CFStringRef kSecAssessmentUpdateKeyFound = CFSTR("update:found"); | |
394 | ||
395 | CFStringRef kSecAssessmentRuleKeyID = CFSTR("rule:id"); | |
396 | CFStringRef kSecAssessmentRuleKeyPriority = CFSTR("rule:priority"); | |
397 | CFStringRef kSecAssessmentRuleKeyAllow = CFSTR("rule:allow"); | |
398 | CFStringRef kSecAssessmentRuleKeyLabel = CFSTR("rule:label"); | |
399 | CFStringRef kSecAssessmentRuleKeyRemarks = CFSTR("rule:remarks"); | |
400 | CFStringRef kSecAssessmentRuleKeyRequirement = CFSTR("rule:requirement"); | |
401 | CFStringRef kSecAssessmentRuleKeyType = CFSTR("rule:type"); | |
402 | CFStringRef kSecAssessmentRuleKeyExpires = CFSTR("rule:expires"); | |
403 | CFStringRef kSecAssessmentRuleKeyDisabled = CFSTR("rule:disabled"); | |
404 | CFStringRef kSecAssessmentRuleKeyBookmark = CFSTR("rule:bookmark"); | |
405 | ||
406 | ||
407 | Boolean SecAssessmentUpdate(CFTypeRef target, | |
408 | SecAssessmentFlags flags, | |
409 | CFDictionaryRef context, | |
410 | CFErrorRef *errors) | |
411 | { | |
412 | if (CFDictionaryRef outcome = SecAssessmentCopyUpdate(target, flags, context, errors)) { | |
413 | CFRelease(outcome); | |
414 | return true; | |
415 | } else { | |
416 | return false; | |
417 | } | |
418 | } | |
419 | ||
420 | CFDictionaryRef SecAssessmentCopyUpdate(CFTypeRef target, | |
421 | SecAssessmentFlags flags, | |
422 | CFDictionaryRef context, | |
423 | CFErrorRef *errors) | |
424 | { | |
425 | BEGIN_CSAPI | |
426 | ||
427 | CFDictionary ctx(context, errSecCSInvalidAttributeValues); | |
427c49bc | 428 | CFRef<CFDictionaryRef> result; |
b1ab9ed8 | 429 | |
5c19dc3a | 430 | // make context exist and writable |
866f8763 | 431 | CFRef<CFMutableDictionaryRef> mcontext = context ? makeCFMutableDictionary(context) : makeCFMutableDictionary(); |
5c19dc3a A |
432 | |
433 | if (CFDictionaryGetValue(mcontext, kSecAssessmentUpdateKeyAuthorization) == NULL) { | |
434 | // no authorization passed in. Make an empty one in this context | |
435 | AuthorizationRef authorization; | |
436 | MacOSError::check(AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authorization)); | |
437 | AuthorizationExternalForm extform; | |
438 | MacOSError::check(AuthorizationMakeExternalForm(authorization, &extform)); | |
439 | CFDictionaryAddValue(mcontext, kSecAssessmentUpdateKeyAuthorization, CFTempData(&extform, sizeof(extform))); | |
440 | if (!(flags & kSecAssessmentFlagDirect)) | |
441 | AuthorizationFree(authorization, kAuthorizationFlagDefaults); | |
442 | } | |
443 | ||
b1ab9ed8 | 444 | if (flags & kSecAssessmentFlagDirect) { |
427c49bc A |
445 | if (__esp_enabled()) { |
446 | CFTemp<CFDictionaryRef> dict("{target=%O, flags=%d, context=%O}", target, flags, context); | |
447 | OSStatus esp_result = __esp_check_ns("cs-assessment-update", (void *)(CFDictionaryRef)dict); | |
448 | if (esp_result != noErr) | |
449 | return NULL; | |
450 | } | |
451 | ||
b1ab9ed8 | 452 | // ask the engine right here to do its thing |
427c49bc | 453 | result = gEngine().update(target, flags, ctx); |
b1ab9ed8 A |
454 | } else { |
455 | // relay the question to our daemon for consideration | |
427c49bc | 456 | result = xpcEngineUpdate(target, flags, ctx); |
b1ab9ed8 A |
457 | } |
458 | ||
427c49bc A |
459 | if (__esp_enabled() && (flags & kSecAssessmentFlagDirect)) { |
460 | CFTemp<CFDictionaryRef> dict("{target=%O, flags=%d, context=%O, outcome=%O}", target, flags, context, (CFDictionaryRef)result); | |
461 | __esp_notify_ns("cs-assessment-update", (void *)(CFDictionaryRef)dict); | |
462 | } | |
463 | ||
464 | traceUpdate(target, context, result); | |
465 | return result.yield(); | |
466 | ||
6b200bc3 | 467 | END_CSAPI_ERRORS1(NULL) |
b1ab9ed8 A |
468 | } |
469 | ||
79b9da22 A |
470 | static void |
471 | updateAuthority(const char *authority, bool enable, CFErrorRef *errors) | |
472 | { | |
473 | CFStringRef updateValue = enable ? kSecAssessmentUpdateOperationEnable : kSecAssessmentUpdateOperationDisable; | |
474 | CFTemp<CFDictionaryRef> ctx("{%O=%s, %O=%O}", kSecAssessmentUpdateKeyLabel, authority, kSecAssessmentContextKeyUpdate, updateValue); | |
475 | SecAssessmentUpdate(NULL, kSecCSDefaultFlags, ctx, errors); | |
476 | } | |
477 | ||
b1ab9ed8 A |
478 | |
479 | // | |
480 | // The fcntl of System Policies. | |
481 | // For those very special requests. | |
482 | // | |
483 | Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *errors) | |
484 | { | |
485 | BEGIN_CSAPI | |
486 | ||
427c49bc A |
487 | CFTemp<CFDictionaryRef> dict("{control=%O}", control); |
488 | esp_do_check("cs-assessment-control", dict); | |
489 | ||
b1ab9ed8 A |
490 | if (CFEqual(control, CFSTR("ui-enable"))) { |
491 | setAssessment(true); | |
492 | MessageTrace trace("com.apple.security.assessment.state", "enable"); | |
493 | trace.send("enable assessment outcomes"); | |
494 | return true; | |
495 | } else if (CFEqual(control, CFSTR("ui-disable"))) { | |
496 | setAssessment(false); | |
497 | MessageTrace trace("com.apple.security.assessment.state", "disable"); | |
498 | trace.send("disable assessment outcomes"); | |
499 | return true; | |
500 | } else if (CFEqual(control, CFSTR("ui-status"))) { | |
501 | CFBooleanRef &result = *(CFBooleanRef*)(arguments); | |
502 | if (overrideAssessment()) | |
503 | result = kCFBooleanFalse; | |
504 | else | |
505 | result = kCFBooleanTrue; | |
506 | return true; | |
507 | } else if (CFEqual(control, CFSTR("ui-enable-devid"))) { | |
79b9da22 A |
508 | updateAuthority("Developer ID", true, errors); |
509 | updateAuthority("Notarized Developer ID", true, errors); | |
313fa17b A |
510 | MessageTrace trace("com.apple.security.assessment.state", "enable-devid"); |
511 | trace.send("enable Developer ID approval"); | |
b1ab9ed8 A |
512 | return true; |
513 | } else if (CFEqual(control, CFSTR("ui-disable-devid"))) { | |
79b9da22 | 514 | updateAuthority("Developer ID", false, errors); |
313fa17b A |
515 | MessageTrace trace("com.apple.security.assessment.state", "disable-devid"); |
516 | trace.send("disable Developer ID approval"); | |
b1ab9ed8 | 517 | return true; |
fa7225c8 A |
518 | } else if (CFEqual(control, CFSTR("ui-get-devid"))) { |
519 | xpcEngineCheckDevID((CFBooleanRef*)(arguments)); | |
520 | return true; | |
521 | } else if (CFEqual(control, CFSTR("ui-get-devid-local"))) { | |
b1ab9ed8 A |
522 | CFBooleanRef &result = *(CFBooleanRef*)(arguments); |
523 | if (gEngine().value<int>("SELECT disabled FROM authority WHERE label = 'Developer ID';", true)) | |
524 | result = kCFBooleanFalse; | |
525 | else | |
526 | result = kCFBooleanTrue; | |
527 | return true; | |
79b9da22 A |
528 | } else if (CFEqual(control, CFSTR("ui-enable-notarized"))) { |
529 | updateAuthority("Notarized Developer ID", true, errors); | |
530 | MessageTrace trace("com.apple.security.assessment.state", "enable-notarized"); | |
531 | trace.send("enable Notarized Developer ID approval"); | |
532 | return true; | |
533 | } else if (CFEqual(control, CFSTR("ui-disable-notarized"))) { | |
534 | updateAuthority("Notarized Developer ID", false, errors); | |
535 | MessageTrace trace("com.apple.security.assessment.state", "disable-notarized"); | |
536 | trace.send("disable Notarized Developer ID approval"); | |
537 | return true; | |
538 | } else if (CFEqual(control, CFSTR("ui-get-notarized"))) { | |
539 | xpcEngineCheckNotarized((CFBooleanRef*)(arguments)); | |
540 | return true; | |
541 | } else if (CFEqual(control, CFSTR("ui-get-notarized-local"))) { | |
542 | CFBooleanRef &result = *(CFBooleanRef*)(arguments); | |
543 | if (gEngine().value<int>("SELECT disabled FROM authority WHERE label = 'Notarized Developer ID';", true)) | |
544 | result = kCFBooleanFalse; | |
545 | else | |
546 | result = kCFBooleanTrue; | |
547 | return true; | |
427c49bc A |
548 | } else if (CFEqual(control, CFSTR("ui-record-reject"))) { |
549 | // send this through syspolicyd for update validation | |
550 | xpcEngineRecord(CFDictionaryRef(arguments)); | |
551 | return true; | |
552 | } else if (CFEqual(control, CFSTR("ui-record-reject-local"))) { | |
553 | // perform the local operation (requires root) | |
554 | gEngine().recordFailure(CFDictionaryRef(arguments)); | |
555 | return true; | |
556 | } else if (CFEqual(control, CFSTR("ui-recall-reject"))) { | |
557 | // no special privileges required for this, so read directly | |
558 | CFDictionaryRef &result = *(CFDictionaryRef*)(arguments); | |
559 | CFRef<CFDataRef> infoData = cfLoadFile(lastRejectFile); | |
560 | if (infoData) | |
561 | result = makeCFDictionaryFrom(infoData); | |
562 | else | |
563 | result = NULL; | |
564 | return true; | |
d8f41ccd A |
565 | } else if (CFEqual(control, CFSTR("rearm-status"))) { |
566 | CFTimeInterval &result = *(CFTimeInterval*)(arguments); | |
567 | if (!queryRearmTimer(result)) | |
568 | result = 0; | |
569 | return true; | |
b1ab9ed8 A |
570 | } else |
571 | MacOSError::throwMe(errSecCSInvalidAttributeValues); | |
572 | ||
573 | END_CSAPI_ERRORS1(false) | |
574 | } | |
79b9da22 A |
575 | |
576 | Boolean SecAssessmentTicketRegister(CFDataRef ticketData, CFErrorRef *errors) | |
577 | { | |
578 | BEGIN_CSAPI | |
579 | ||
580 | xpcEngineTicketRegister(ticketData); | |
581 | return true; | |
582 | ||
583 | END_CSAPI_ERRORS1(false) | |
584 | } | |
585 | ||
586 | Boolean SecAssessmentTicketLookup(CFDataRef hash, SecCSDigestAlgorithm hashType, SecAssessmentTicketFlags flags, double *date, CFErrorRef *errors) | |
587 | { | |
588 | BEGIN_CSAPI | |
589 | ||
590 | xpcEngineTicketLookup(hash, hashType, flags, date); | |
591 | return true; | |
592 | ||
593 | END_CSAPI_ERRORS1(false) | |
594 | } | |
595 |