]> git.saurik.com Git - apple/security.git/blob - sec/ipc/client.c
Security-55163.44.tar.gz
[apple/security.git] / sec / ipc / client.c
1 /*
2 * Copyright (c) 2007-2009 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 <mach/mach.h>
25 #include <mach/message.h>
26 #include <mach/mach_error.h>
27 #include <servers/bootstrap.h>
28
29 #include <pthread.h>
30 #include <stdbool.h>
31 #include <sys/queue.h>
32
33 #include <CoreFoundation/CoreFoundation.h>
34 #include <Security/SecItem.h>
35 #include <Security/SecBasePriv.h>
36 #include <Security/SecInternal.h>
37
38 #include <security_utilities/debugging.h>
39 #include "securityd_client.h"
40 #include "securityd_req.h"
41
42 #if NO_SERVER
43 #define CHECK_ENTITLEMENTS 0
44 #else
45 #define CHECK_ENTITLEMENTS 1
46 #endif
47
48 struct securityd *gSecurityd;
49
50 /* XXX Not thread safe right now. */
51 static CFArrayRef gAccessGroups = NULL;
52
53 CFArrayRef SecAccessGroupsGetCurrent(void) {
54 #if !CHECK_ENTITLEMENTS
55 if (!gAccessGroups) {
56 /* Initialize gAccessGroups for tests. */
57 const void *agrps[] = {
58 CFSTR("test"),
59 #if 1
60 CFSTR("apple"),
61 CFSTR("lockdown-identities"),
62 #else
63 CFSTR("*"),
64 #endif
65 };
66 gAccessGroups = CFArrayCreate(NULL, agrps,
67 sizeof(agrps) / sizeof(*agrps), &kCFTypeArrayCallBacks);
68 }
69 #endif
70 return gAccessGroups;
71 }
72
73 void SecAccessGroupsSetCurrent(CFArrayRef accessGroups) {
74 if (accessGroups)
75 CFRetain(accessGroups);
76 CFReleaseSafe(gAccessGroups);
77 gAccessGroups = accessGroups;
78 }
79
80 static mach_port_t securityd_port = MACH_PORT_NULL;
81 static pthread_once_t init_once = PTHREAD_ONCE_INIT;
82 static pthread_key_t port_key;
83
84 static CFStringRef kSecRunLoopModeRef = CFSTR("com.apple.securityd.runloop");
85
86 struct request {
87 uint32_t seq_no;
88 uint32_t msgid;
89 SLIST_ENTRY(request) next;
90 int32_t rcode;
91 void *data;
92 size_t length;
93 volatile bool done;
94 };
95
96 typedef struct per_thread {
97 SLIST_HEAD(request_head, request) head;;
98 CFMachPortRef port;
99 uint32_t seq_no;
100 mach_msg_id_t notification; /* XXX for stacked requests, notifications
101 need to be matched up with seq_nos such
102 that only failed requests error out/retry */
103 } *per_thread_t;
104
105
106 static void destroy_per_thread(void *ex)
107 {
108 per_thread_t pt = (per_thread_t)ex;
109 CFMachPortInvalidate(pt->port);
110 CFRelease(pt->port);
111 free(pt);
112 }
113
114 static void securityd_port_reset(void)
115 {
116 pthread_once_t temp = PTHREAD_ONCE_INIT;
117 init_once = temp;
118 securityd_port = MACH_PORT_NULL;
119 }
120
121 static void securityd_port_lookup(void)
122 {
123 kern_return_t ret;
124 mach_port_t bootstrap_lookup_port = MACH_PORT_NULL;
125
126 /* bootstrap_port is not initialized on embedded */
127 ret = task_get_bootstrap_port(mach_task_self(), &bootstrap_lookup_port);
128 if (ret != KERN_SUCCESS) {
129 secdebug("client", "task_get_bootstrap_port(): 0x%x: %s\n", ret, mach_error_string(ret));
130 }
131
132 ret = bootstrap_look_up(bootstrap_lookup_port,
133 SECURITYSERVER_BOOTSTRAP_NAME,
134 &securityd_port);
135 if (ret != KERN_SUCCESS) {
136 secdebug("client", "bootstrap_look_up(): 0x%x: %s\n", ret, mach_error_string(ret));
137 }
138
139 pthread_key_create(&port_key, destroy_per_thread);
140
141 int err = pthread_atfork(NULL, NULL, securityd_port_reset);
142 if (err) {
143 secdebug("client", "pthread_atfork(): %d: %s\n", errno, strerror(errno));
144 }
145 }
146
147 union max_msg_size_union {
148 union __RequestUnion__securityd_client_securityd_request_subsystem request;
149 };
150
151 static uint8_t reply_buffer[sizeof(union max_msg_size_union) + MAX_TRAILER_SIZE];
152
153 static boolean_t maybe_notification(mach_msg_header_t *request)
154 {
155 mach_no_senders_notification_t * notify = (mach_no_senders_notification_t *)request;
156 if ((notify->not_header.msgh_id > MACH_NOTIFY_LAST) ||
157 (notify->not_header.msgh_id < MACH_NOTIFY_FIRST))
158 return false; /* if this is not a notification message */
159
160 per_thread_t pt = (per_thread_t)pthread_getspecific(port_key);
161 assert(pt);
162 mach_msg_id_t notification_id = notify->not_header.msgh_id;
163
164 switch(notification_id) {
165 case MACH_NOTIFY_SEND_ONCE:
166 /* our send-once right for a reply died in the hands of another */
167 pt->notification = notification_id;
168 CFRunLoopStop(CFRunLoopGetCurrent());
169 break;
170 default:
171 secdebug("client", "unexpected notification %d", pt->notification);
172 break;
173 }
174 return true;
175 }
176
177 extern boolean_t securityd_reply_server
178 (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
179
180 static void cfmachport_callback(CFMachPortRef port, void *msg, CFIndex size, void *info)
181 {
182 if (!maybe_notification((mach_msg_header_t *)msg))
183 securityd_reply_server(msg, (mach_msg_header_t *)reply_buffer);
184 }
185
186 static int32_t send_receive(uint32_t msg_id, const void *data_in, size_t length_in,
187 void **data_out, size_t *length_out)
188 {
189 pthread_once(&init_once, securityd_port_lookup);
190
191 CFRunLoopRef rl = CFRunLoopGetCurrent();
192
193 per_thread_t pt = (per_thread_t)pthread_getspecific(port_key);
194 if (!pt) {
195 pt = calloc(1, sizeof(*pt));
196 SLIST_INIT(&pt->head);
197 pt->port = CFMachPortCreate (NULL, cfmachport_callback, NULL, false);
198 CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(NULL, pt->port, 0/*order*/);
199 CFRunLoopAddSource(rl, source, kSecRunLoopModeRef);
200 CFRelease(source);
201 pthread_setspecific(port_key, pt);
202 }
203
204 struct request req = { pt->seq_no++, msg_id, };
205 SLIST_INSERT_HEAD(&pt->head, &req, next);
206
207 int retries = 1;
208 kern_return_t ret;
209 do {
210 /* 64 bits cast: worst case here is the client send a truncated query, which the server will reject */
211 /* Debug check: The size of the request is of type natural_t.
212 The following is correct as long as natural_t is unsigned int */
213 assert(length_in<=UINT_MAX);
214 ret = securityd_client_request(securityd_port,
215 CFMachPortGetPort(pt->port), req.seq_no, msg_id,
216 (void *)data_in, (unsigned int) length_in);
217 secdebug("client", "securityd_client_request %d sent, retry %d, result %d\n",
218 req.seq_no, retries, ret);
219
220 if (!ret) {
221 pt->notification = 0;
222 while (!pt->notification && !req.done) {
223 CFRunLoopRunInMode(kSecRunLoopModeRef, 10000, true);
224 secdebug("client", "return from runloop, notification %d, request %d %sdone\n",
225 pt->notification, req.seq_no, !req.done ? "not " : "");
226 }
227 } else {
228 secdebug("client", "failed to send request to securityd (err=%d).", ret);
229 }
230
231 } while ((ret || pt->notification) && retries--);
232
233 SLIST_REMOVE_HEAD(&pt->head, next);
234
235 struct request *next_head = SLIST_FIRST(&pt->head);
236 if (next_head) {
237 /* stop runloop if "new head" is also already done */
238 if (pt->notification || next_head->done)
239 CFRunLoopStop(rl);
240 }
241
242 if (req.done) {
243 if (data_out)
244 *data_out = req.data;
245 if (length_out)
246 *length_out = req.length;
247 return req.rcode;
248 }
249
250 return errSecNotAvailable;
251 }
252
253 kern_return_t securityd_server_reply(mach_port_t receiver,
254 uint32_t seq_no, int32_t rcode,
255 uint8_t *msg_data, mach_msg_type_number_t msg_length);
256
257 kern_return_t securityd_server_reply(mach_port_t receiver,
258 uint32_t seq_no, int32_t rcode,
259 uint8_t *msg_data, mach_msg_type_number_t msg_length)
260 {
261 secdebug("client", "reply from port %d request_id %d data(%d,%p)\n",
262 receiver, seq_no, msg_length, msg_data);
263 per_thread_t pt = (per_thread_t)pthread_getspecific(port_key);
264 assert(pt);
265 struct request *req;
266 SLIST_FOREACH(req, &pt->head, next) {
267 if (req->seq_no == seq_no) {
268 req->rcode = rcode;
269 if (msg_length && msg_data) {
270 req->data = malloc(msg_length);
271 if (req->data) {
272 req->length = msg_length;
273 memcpy(req->data, msg_data, msg_length);
274 } else
275 req->length = 0;
276 }
277 req->done = true;
278 /* if multiple requests were queued during nested invocations
279 we wait until the last one inserted which is the deepest
280 nested one is done */
281 if (req == SLIST_FIRST(&pt->head))
282 CFRunLoopStop(CFRunLoopGetCurrent());
283 break;
284 }
285 }
286 return 0;
287 }
288
289 OSStatus ServerCommandSendReceive(uint32_t id, CFTypeRef in, CFTypeRef *out)
290 {
291 CFDataRef data_in = NULL, data_out = NULL;
292 void *bytes_out = NULL; size_t length_out = 0;
293
294 if (in) {
295 #ifndef NDEBUG
296 CFDataRef query_debug = CFPropertyListCreateXMLData(kCFAllocatorDefault, in);
297 if (query_debug) {
298 secdebug("client", "securityd query: %.*s\n",
299 CFDataGetLength(query_debug), CFDataGetBytePtr(query_debug));
300 CFReleaseSafe(query_debug);
301 }
302 #endif
303 CFErrorRef error = NULL;
304 data_in = CFPropertyListCreateData(kCFAllocatorDefault, in,
305 kCFPropertyListBinaryFormat_v1_0,
306 0, &error);
307 if (!data_in) {
308 secdebug("client", "failed to encode query: %@", error);
309 CFReleaseSafe(error);
310 return errSecItemIllegalQuery;
311 }
312 }
313
314 OSStatus status = send_receive(id, data_in ? CFDataGetBytePtr(data_in) : NULL,
315 data_in ? CFDataGetLength(data_in) : 0, &bytes_out, &length_out);
316 if (data_in) CFRelease(data_in);
317
318 if (bytes_out && length_out) {
319 data_out = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bytes_out, length_out, kCFAllocatorMalloc);
320 if (out)
321 *out = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data_out, kCFPropertyListImmutable, NULL);
322 CFRelease(data_out);
323 }
324 else
325 if (!status && out)
326 *out = NULL;
327
328 return status;
329 }
330
331 /* vi:set ts=4 sw=4 et: */