]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/SecTask.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / SecTask.c
1 /*
2 * Copyright (c) 2009-2014 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
24 #include <CoreFoundation/CoreFoundation.h>
25 #include <CoreFoundation/CFRuntime.h>
26 #include <IOKit/IOKitLib.h>
27 #include <IOKit/IOCFUnserialize.h>
28 #include <System/sys/codesign.h>
29 #include <bsm/libbsm.h>
30 #include <inttypes.h>
31 #include <pthread.h>
32 #include <syslog.h>
33 #include <sys/sysctl.h>
34
35 #include "SecCode.h"
36 #include "SecCodePriv.h"
37 #include "SecRequirement.h"
38
39 #include "SecTask.h"
40 #include "SecTaskPriv.h"
41
42
43 struct __SecTask {
44 CFRuntimeBase base;
45
46 pid_t pid;
47
48 audit_token_t *token;
49 audit_token_t token_storage;
50
51 /* Track whether we've loaded entitlements independently since after the
52 * load, entitlements may legitimately be NULL */
53 Boolean entitlementsLoaded;
54 CFDictionaryRef entitlements;
55 };
56
57 enum {
58 kSecCodeMagicEntitlement = 0xfade7171, /* entitlement blob */
59 };
60
61
62 CFTypeID _kSecTaskTypeID = _kCFRuntimeNotATypeID;
63
64 static void SecTaskFinalize(CFTypeRef cfTask)
65 {
66 SecTaskRef task = (SecTaskRef) cfTask;
67
68 if (task->entitlements != NULL) {
69 CFRelease(task->entitlements);
70 task->entitlements = NULL;
71 }
72 }
73
74
75 // Define PRIdPID (proper printf format string for pid_t)
76 #define PRIdPID PRId32
77
78 static CFStringRef SecTaskCopyDebugDescription(CFTypeRef cfTask)
79 {
80 SecTaskRef task = (SecTaskRef) cfTask;
81 const char *task_name;
82 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, task->pid};
83 struct kinfo_proc kp;
84 size_t len = sizeof(kp);
85 if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1 || len == 0)
86 task_name = strerror(errno);
87 else
88 task_name = kp.kp_proc.p_comm;
89
90 return CFStringCreateWithFormat(CFGetAllocator(task), NULL, CFSTR("%s[%" PRIdPID "]"), task_name, task->pid);
91 }
92
93 static void SecTaskRegisterClass(void)
94 {
95 static const CFRuntimeClass SecTaskClass = {
96 .version = 0,
97 .className = "SecTask",
98 .init = NULL,
99 .copy = NULL,
100 .finalize = SecTaskFinalize,
101 .equal = NULL,
102 .hash = NULL,
103 .copyFormattingDesc = NULL,
104 .copyDebugDesc = SecTaskCopyDebugDescription,
105 };
106
107 _kSecTaskTypeID = _CFRuntimeRegisterClass(&SecTaskClass);
108 }
109
110 CFTypeID SecTaskGetTypeID(void)
111 {
112 static pthread_once_t secTaskRegisterClassOnce = PTHREAD_ONCE_INIT;
113
114 /* Register the class with the CF runtime the first time through */
115 pthread_once(&secTaskRegisterClassOnce, SecTaskRegisterClass);
116
117 return _kSecTaskTypeID;
118 }
119
120 static SecTaskRef SecTaskCreateWithPID(CFAllocatorRef allocator, pid_t pid)
121 {
122 CFIndex extra = sizeof(struct __SecTask) - sizeof(CFRuntimeBase);
123 SecTaskRef task = (SecTaskRef) _CFRuntimeCreateInstance(allocator, SecTaskGetTypeID(), extra, NULL);
124 if (task != NULL) {
125 task->pid = pid;
126 task->entitlementsLoaded = false;
127 task->entitlements = NULL;
128 }
129
130 return task;
131 }
132
133 SecTaskRef SecTaskCreateWithAuditToken(CFAllocatorRef allocator, audit_token_t token)
134 {
135 SecTaskRef task;
136
137 task = SecTaskCreateWithPID(allocator, audit_token_to_pid(token));
138 if (task != NULL) {
139 #if 0
140 task->token_storage = token;
141 task->token = &task->token_storage;
142 #endif
143 }
144
145 return task;
146 }
147
148 SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator)
149 {
150 return SecTaskCreateWithPID(allocator, getpid());
151 }
152
153 /*
154 * Determine if the given task meets a specified requirement.
155 */
156 OSStatus
157 SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement)
158 {
159 OSStatus status;
160 SecCodeRef code = NULL;
161 SecRequirementRef req = NULL;
162 pid_t pid = task->pid;
163 if (pid <= 0) {
164 return errSecParam;
165 }
166 status = SecCodeCreateWithPID(pid, kSecCSDefaultFlags, &code);
167 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCreateWithPID=%d", status);
168 if (!status) {
169 status = SecRequirementCreateWithString(requirement,
170 kSecCSDefaultFlags, &req);
171 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecRequirementCreateWithString=%d", status);
172 }
173 if (!status) {
174 status = SecCodeCheckValidity(code, kSecCSDefaultFlags, req);
175 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCheckValidity=%d", status);
176 }
177 if (req)
178 CFRelease(req);
179 if (code)
180 CFRelease(code);
181
182 return status;
183 }
184
185 static CFRange myMakeRange(CFIndex loc, CFIndex len) {
186 CFRange r = {.location = loc, .length = len };
187 return r;
188 }
189 struct csheader {
190 uint32_t magic;
191 uint32_t length;
192 };
193
194 static int
195 csops_task(SecTaskRef task, int ops, void *blob, size_t size)
196 {
197 #if 0
198 if (task->token)
199 return csops_audittoken(task->pid, ops, blob, size, task->token);
200 else
201 #endif
202 return csops(task->pid, ops, blob, size);
203 }
204
205 static int SecTaskLoadEntitlements(SecTaskRef task, CFErrorRef *error)
206 {
207 CFMutableDataRef data = NULL;
208 struct csheader header;
209 uint32_t bufferlen;
210 int ret;
211
212 ret = csops_task(task, CS_OPS_ENTITLEMENTS_BLOB, &header, sizeof(header));
213 if (ret == 0) {
214 // we only gave a header's worth of buffer. If this succeeded, we have no entitlements
215 task->entitlementsLoaded = true;
216 return 0;
217 }
218 if (errno != ERANGE) {
219 // ERANGE means "your buffer is too small, it now tells you how much you need
220 // EINVAL is what the kernel says for unsigned code AND broken code, so we'll have to let that pass
221 if (errno == EINVAL) {
222 task->entitlementsLoaded = true;
223 return 0;
224 }
225 ret = errno;
226 goto out;
227 }
228 // kernel told us the needed buffer size in header.length; proceed
229
230 bufferlen = ntohl(header.length);
231 /* check for insane values */
232 if (bufferlen > 1024 * 1024 || bufferlen < 8) {
233 ret = EINVAL;
234 goto out;
235 }
236 data = CFDataCreateMutable(NULL, bufferlen);
237 if (data == NULL) {
238 ret = ENOMEM;
239 goto out;
240 }
241 CFDataSetLength(data, bufferlen);
242 ret = csops_task(task, CS_OPS_ENTITLEMENTS_BLOB, CFDataGetMutableBytePtr(data), bufferlen);
243 if (ret) {
244 ret = errno;
245 goto out;
246 }
247 CFDataDeleteBytes(data, myMakeRange(0, 8));
248 task->entitlements = CFPropertyListCreateWithData(NULL, data, 0, NULL, error);
249 task->entitlementsLoaded = true;
250 out:
251 if (data)
252 CFRelease(data);
253 if (ret && error)
254 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, ret, NULL);
255
256 return ret;
257 }
258
259 CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error)
260 {
261 /* Load entitlements if necessary */
262 if (task->entitlementsLoaded == false) {
263 SecTaskLoadEntitlements(task, error);
264 }
265
266 CFTypeRef value = NULL;
267 if (task->entitlements != NULL) {
268 value = CFDictionaryGetValue(task->entitlements, entitlement);
269
270 /* Return something the caller must release */
271 if (value != NULL) {
272 CFRetain(value);
273 }
274 }
275
276 return value;
277 }
278
279 CFDictionaryRef SecTaskCopyValuesForEntitlements(SecTaskRef task, CFArrayRef entitlements, CFErrorRef *error)
280 {
281 /* Load entitlements if necessary */
282 if (task->entitlementsLoaded == false) {
283 SecTaskLoadEntitlements(task, error);
284 }
285
286 /* Iterate over the passed in entitlements, populating the dictionary
287 * If entitlements were loaded but none were present, return an empty
288 * dictionary */
289 CFMutableDictionaryRef values = NULL;
290 if (task->entitlementsLoaded == true) {
291
292 CFIndex i, count = CFArrayGetCount(entitlements);
293 values = CFDictionaryCreateMutable(CFGetAllocator(task), count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
294 if (task->entitlements != NULL) {
295 for (i = 0; i < count; i++) {
296 CFStringRef entitlement = CFArrayGetValueAtIndex(entitlements, i);
297 CFTypeRef value = CFDictionaryGetValue(task->entitlements, entitlement);
298 if (value != NULL) {
299 CFDictionarySetValue(values, entitlement, value);
300 }
301 }
302 }
303 }
304
305 return values;
306 }
307
308 Boolean SecTaskEntitlementsValidated(SecTaskRef task) {
309 // TODO: Cache the result
310 uint32_t csflags = 0;
311 const uint32_t mask = CS_VALID | CS_KILL | CS_ENTITLEMENTS_VALIDATED;
312 int rc = csops_task(task, CS_OPS_STATUS, &csflags, sizeof(csflags));
313 return rc != -1 && ((csflags & mask) == mask);
314 }
315
316 CFStringRef
317 SecTaskCopySigningIdentifier(SecTaskRef task, CFErrorRef *error)
318 {
319 CFStringRef signingId = NULL;
320 char *data = NULL;
321 struct csheader header;
322 uint32_t bufferlen;
323 int ret;
324
325 ret = csops_task(task, CS_OPS_IDENTITY, &header, sizeof(header));
326 if (ret != -1 || errno != ERANGE)
327 return NULL;
328
329 bufferlen = ntohl(header.length);
330 /* check for insane values */
331 if (bufferlen > 1024 * 1024 || bufferlen < 8) {
332 ret = EINVAL;
333 goto out;
334 }
335 data = malloc(bufferlen + 1);
336 if (data == NULL) {
337 ret = ENOMEM;
338 goto out;
339 }
340 ret = csops_task(task, CS_OPS_IDENTITY, data, bufferlen);
341 if (ret) {
342 ret = errno;
343 goto out;
344 }
345 data[bufferlen] = '\0';
346
347 signingId = CFStringCreateWithCString(NULL, data + 8, kCFStringEncodingUTF8);
348
349 out:
350 if (data)
351 free(data);
352 if (ret && error)
353 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, ret, NULL);
354
355 return signingId;
356 }
357
358 uint32_t
359 SecTaskGetCodeSignStatus(SecTaskRef task)
360 {
361 uint32_t flags = 0;
362 if (csops_task(task, CS_OPS_STATUS, &flags, sizeof(flags)) != 0)
363 return 0;
364 return flags;
365 }
366