1/*
2 * Copyright (c) 2009-2013 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 <sys/sysctl.h>
33
34#include "SecCode.h"
35#include "SecCodePriv.h"
36#include "SecRequirement.h"
37
38#include "SecTask.h"
39#include "SecTaskPriv.h"
40
41
42struct __SecTask {
43	CFRuntimeBase base;
44
45	pid_t pid;
46
47	audit_token_t *token;
48	audit_token_t token_storage;
49
50	/* Track whether we've loaded entitlements independently since after the
51	 * load, entitlements may legitimately be NULL */
52	Boolean entitlementsLoaded;
53	CFDictionaryRef entitlements;
54};
55
56enum {
57	kSecCodeMagicEntitlement = 0xfade7171,		/* entitlement blob */
58};
59
60
61CFTypeID _kSecTaskTypeID = _kCFRuntimeNotATypeID;
62
63static void SecTaskFinalize(CFTypeRef cfTask)
64{
65	SecTaskRef task = (SecTaskRef) cfTask;
66
67	if (task->entitlements != NULL) {
68		CFRelease(task->entitlements);
69		task->entitlements = NULL;
70	}
71}
72
73
74// Define PRIdPID (proper printf format string for pid_t)
75#define PRIdPID PRId32
76
77static CFStringRef SecTaskCopyDebugDescription(CFTypeRef cfTask)
78{
79    SecTaskRef task = (SecTaskRef) cfTask;
80    const char *task_name;
81    int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, task->pid};
82    struct kinfo_proc kp;
83    size_t len = sizeof(kp);
84    if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1 || len == 0)
85        task_name = strerror(errno);
86    else
87        task_name = kp.kp_proc.p_comm;
88
89    return CFStringCreateWithFormat(CFGetAllocator(task), NULL, CFSTR("%s[%" PRIdPID "]"), task_name, task->pid);
90}
91
92static void SecTaskRegisterClass(void)
93{
94	static const CFRuntimeClass SecTaskClass = {
95		.version = 0,
96		.className = "SecTask",
97		.init = NULL,
98		.copy = NULL,
99		.finalize = SecTaskFinalize,
100		.equal = NULL,
101		.hash = NULL,
102		.copyFormattingDesc = NULL,
103		.copyDebugDesc = SecTaskCopyDebugDescription,
104	};
105
106	_kSecTaskTypeID = _CFRuntimeRegisterClass(&SecTaskClass);
107}
108
109CFTypeID SecTaskGetTypeID(void)
110{
111	static pthread_once_t secTaskRegisterClassOnce = PTHREAD_ONCE_INIT;
112
113	/* Register the class with the CF runtime the first time through */
114	pthread_once(&secTaskRegisterClassOnce, SecTaskRegisterClass);
115
116	return _kSecTaskTypeID;
117}
118
119static SecTaskRef SecTaskCreateWithPID(CFAllocatorRef allocator, pid_t pid)
120{
121	CFIndex extra = sizeof(struct __SecTask) - sizeof(CFRuntimeBase);
122	SecTaskRef task = (SecTaskRef) _CFRuntimeCreateInstance(allocator, SecTaskGetTypeID(), extra, NULL);
123	if (task != NULL) {
124		task->pid = pid;
125		task->entitlementsLoaded = false;
126		task->entitlements = NULL;
127	}
128
129	return task;
130}
131
132SecTaskRef SecTaskCreateWithAuditToken(CFAllocatorRef allocator, audit_token_t token)
133{
134	SecTaskRef task;
135
136	task = SecTaskCreateWithPID(allocator, audit_token_to_pid(token));
137	if (task != NULL) {
138#if 0
139		task->token_storage = token;
140		task->token = &task->token_storage;
141#endif
142	}
143
144	return task;
145}
146
147SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator)
148{
149	return SecTaskCreateWithPID(allocator, getpid());
150}
151
152/*
153 * Determine if the given task meets a specified requirement.
154 */
155OSStatus
156SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement)
157{
158    OSStatus status;
159    SecCodeRef code = NULL;
160    SecRequirementRef req = NULL;
161    pid_t pid = task->pid;
162    if (pid <= 0) {
163        return errSecParam;
164    }
165    status = SecCodeCreateWithPID(pid, kSecCSDefaultFlags, &code);
166    //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCreateWithPID=%d", status);
167    if (!status) {
168        status = SecRequirementCreateWithString(requirement,
169                                                kSecCSDefaultFlags, &req);
170        //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecRequirementCreateWithString=%d", status);
171    }
172    if (!status) {
173        status = SecCodeCheckValidity(code, kSecCSDefaultFlags, req);
174        //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCheckValidity=%d", status);
175    }
176    if (req)
177        CFRelease(req);
178    if (code)
179        CFRelease(code);
180
181    return status;
182}
183
184static CFRange myMakeRange(CFIndex loc, CFIndex len) {
185	CFRange r = {.location = loc, .length = len };
186	return r;
187}
188struct csheader {
189	uint32_t magic;
190	uint32_t length;
191};
192
193static int
194csops_task(SecTaskRef task, int ops, void *blob, size_t size)
195{
196	if (task->token)
197		return csops_audittoken(task->pid, ops, blob, size, task->token);
198	else
199		return csops(task->pid, ops, blob, size);
200}
201
202static int SecTaskLoadEntitlements(SecTaskRef task, CFErrorRef *error)
203{
204	CFMutableDataRef data = NULL;
205	struct csheader header;
206	uint32_t bufferlen;
207	int ret;
208
209	ret = csops_task(task, CS_OPS_ENTITLEMENTS_BLOB, &header, sizeof(header));
210	if (ret != -1 || errno != ERANGE) {
211		/* no entitlements */
212		task->entitlementsLoaded = true;
213		return 0;
214	}
215
216	bufferlen = ntohl(header.length);
217	/* check for insane values */
218	if (bufferlen > 1024 * 1024 || bufferlen < 8) {
219		ret = EINVAL;
220		goto out;
221	}
222	data = CFDataCreateMutable(NULL, bufferlen);
223	if (data == NULL) {
224		ret = ENOMEM;
225		goto out;
226	}
227	CFDataSetLength(data, bufferlen);
228	ret = csops_task(task, CS_OPS_ENTITLEMENTS_BLOB, CFDataGetMutableBytePtr(data), bufferlen);
229	if (ret) {
230		ret = errno;
231		goto out;
232	}
233	CFDataDeleteBytes(data, myMakeRange(0, 8));
234	task->entitlements = CFPropertyListCreateWithData(NULL, data, 0, NULL, error);
235	task->entitlementsLoaded = true;
236 out:
237	if (data)
238		CFRelease(data);
239	if (ret && error)
240		*error = CFErrorCreate(NULL, kCFErrorDomainMach, ret, NULL);
241
242	return ret;
243}
244
245CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error)
246{
247	/* Load entitlements if necessary */
248	if (task->entitlementsLoaded == false) {
249		SecTaskLoadEntitlements(task, error);
250	}
251
252	CFTypeRef value = NULL;
253	if (task->entitlements != NULL) {
254		value = CFDictionaryGetValue(task->entitlements, entitlement);
255
256		/* Return something the caller must release */
257		if (value != NULL) {
258			CFRetain(value);
259		}
260	}
261
262	return value;
263}
264
265CFDictionaryRef SecTaskCopyValuesForEntitlements(SecTaskRef task, CFArrayRef entitlements, CFErrorRef *error)
266{
267	/* Load entitlements if necessary */
268	if (task->entitlementsLoaded == false) {
269		SecTaskLoadEntitlements(task, error);
270	}
271
272	/* Iterate over the passed in entitlements, populating the dictionary
273	 * If entitlements were loaded but none were present, return an empty
274	 * dictionary */
275	CFMutableDictionaryRef values = NULL;
276	if (task->entitlementsLoaded == true) {
277
278		CFIndex i, count = CFArrayGetCount(entitlements);
279		values = CFDictionaryCreateMutable(CFGetAllocator(task), count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
280		if (task->entitlements != NULL) {
281			for (i = 0; i < count; i++) {
282				CFStringRef entitlement = CFArrayGetValueAtIndex(entitlements, i);
283				CFTypeRef value = CFDictionaryGetValue(task->entitlements, entitlement);
284				if (value != NULL) {
285					CFDictionarySetValue(values, entitlement, value);
286				}
287			}
288		}
289	}
290
291	return values;
292}
293