2 * Copyright (c) 2007-2009 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <mach/mach.h>
25 #include <mach/message.h>
26 #include <mach/mach_error.h>
27 #include <servers/bootstrap.h>
31 #include <sys/queue.h>
33 #include <CoreFoundation/CoreFoundation.h>
34 #include <Security/SecItem.h>
35 #include <Security/SecBasePriv.h>
36 #include <Security/SecInternal.h>
38 #include <security_utilities/debugging.h>
39 #include "securityd_client.h"
40 #include "securityd_req.h"
43 #define CHECK_ENTITLEMENTS 0
45 #define CHECK_ENTITLEMENTS 1
48 struct securityd
*gSecurityd
;
50 /* XXX Not thread safe right now. */
51 static CFArrayRef gAccessGroups
= NULL
;
53 CFArrayRef
SecAccessGroupsGetCurrent(void) {
54 #if !CHECK_ENTITLEMENTS
56 /* Initialize gAccessGroups for tests. */
57 const void *agrps
[] = {
61 CFSTR("lockdown-identities"),
66 gAccessGroups
= CFArrayCreate(NULL
, agrps
,
67 sizeof(agrps
) / sizeof(*agrps
), &kCFTypeArrayCallBacks
);
73 void SecAccessGroupsSetCurrent(CFArrayRef accessGroups
) {
75 CFRetain(accessGroups
);
76 CFReleaseSafe(gAccessGroups
);
77 gAccessGroups
= accessGroups
;
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
;
84 static CFStringRef kSecRunLoopModeRef
= CFSTR("com.apple.securityd.runloop");
89 SLIST_ENTRY(request
) next
;
96 typedef struct per_thread
{
97 SLIST_HEAD(request_head
, request
) head
;;
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 */
106 static void destroy_per_thread(void *ex
)
108 per_thread_t pt
= (per_thread_t
)ex
;
109 CFMachPortInvalidate(pt
->port
);
114 static void securityd_port_reset(void)
116 pthread_once_t temp
= PTHREAD_ONCE_INIT
;
118 securityd_port
= MACH_PORT_NULL
;
121 static void securityd_port_lookup(void)
124 mach_port_t bootstrap_lookup_port
= MACH_PORT_NULL
;
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
));
132 ret
= bootstrap_look_up(bootstrap_lookup_port
,
133 SECURITYSERVER_BOOTSTRAP_NAME
,
135 if (ret
!= KERN_SUCCESS
) {
136 secdebug("client", "bootstrap_look_up(): 0x%x: %s\n", ret
, mach_error_string(ret
));
139 pthread_key_create(&port_key
, destroy_per_thread
);
141 int err
= pthread_atfork(NULL
, NULL
, securityd_port_reset
);
143 secdebug("client", "pthread_atfork(): %d: %s\n", errno
, strerror(errno
));
147 union max_msg_size_union
{
148 union __RequestUnion__securityd_client_securityd_request_subsystem request
;
151 static uint8_t reply_buffer
[sizeof(union max_msg_size_union
) + MAX_TRAILER_SIZE
];
153 static boolean_t
maybe_notification(mach_msg_header_t
*request
)
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 */
160 per_thread_t pt
= (per_thread_t
)pthread_getspecific(port_key
);
162 mach_msg_id_t notification_id
= notify
->not_header
.msgh_id
;
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());
171 secdebug("client", "unexpected notification %d", pt
->notification
);
177 extern boolean_t securityd_reply_server
178 (mach_msg_header_t
*InHeadP
, mach_msg_header_t
*OutHeadP
);
180 static void cfmachport_callback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
182 if (!maybe_notification((mach_msg_header_t
*)msg
))
183 securityd_reply_server(msg
, (mach_msg_header_t
*)reply_buffer
);
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
)
189 pthread_once(&init_once
, securityd_port_lookup
);
191 CFRunLoopRef rl
= CFRunLoopGetCurrent();
193 per_thread_t pt
= (per_thread_t
)pthread_getspecific(port_key
);
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
);
201 pthread_setspecific(port_key
, pt
);
204 struct request req
= { pt
->seq_no
++, msg_id
, };
205 SLIST_INSERT_HEAD(&pt
->head
, &req
, next
);
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
);
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 " : "");
228 secdebug("client", "failed to send request to securityd (err=%d).", ret
);
231 } while ((ret
|| pt
->notification
) && retries
--);
233 SLIST_REMOVE_HEAD(&pt
->head
, next
);
235 struct request
*next_head
= SLIST_FIRST(&pt
->head
);
237 /* stop runloop if "new head" is also already done */
238 if (pt
->notification
|| next_head
->done
)
244 *data_out
= req
.data
;
246 *length_out
= req
.length
;
250 return errSecNotAvailable
;
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
);
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
)
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
);
266 SLIST_FOREACH(req
, &pt
->head
, next
) {
267 if (req
->seq_no
== seq_no
) {
269 if (msg_length
&& msg_data
) {
270 req
->data
= malloc(msg_length
);
272 req
->length
= msg_length
;
273 memcpy(req
->data
, msg_data
, msg_length
);
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());
289 OSStatus
ServerCommandSendReceive(uint32_t id
, CFTypeRef in
, CFTypeRef
*out
)
291 CFDataRef data_in
= NULL
, data_out
= NULL
;
292 void *bytes_out
= NULL
; size_t length_out
= 0;
296 CFDataRef query_debug
= CFPropertyListCreateXMLData(kCFAllocatorDefault
, in
);
298 secdebug("client", "securityd query: %.*s\n",
299 CFDataGetLength(query_debug
), CFDataGetBytePtr(query_debug
));
300 CFReleaseSafe(query_debug
);
303 CFErrorRef error
= NULL
;
304 data_in
= CFPropertyListCreateData(kCFAllocatorDefault
, in
,
305 kCFPropertyListBinaryFormat_v1_0
,
308 secdebug("client", "failed to encode query: %@", error
);
309 CFReleaseSafe(error
);
310 return errSecItemIllegalQuery
;
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
);
318 if (bytes_out
&& length_out
) {
319 data_out
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, bytes_out
, length_out
, kCFAllocatorMalloc
);
321 *out
= CFPropertyListCreateFromXMLData(kCFAllocatorDefault
, data_out
, kCFPropertyListImmutable
, NULL
);
331 /* vi:set ts=4 sw=4 et: */