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 <servers/bootstrap.h>
29 #include <sys/queue.h>
31 #include <Security/SecInternal.h>
32 #include <Security/SecBasePriv.h>
33 #include <Security/SecItemPriv.h> /* For SecItemDeleteAll */
34 #include <CoreFoundation/CoreFoundation.h>
37 #include <bsm/libbsm.h>
38 #include <security_utilities/debugging.h>
39 #include <sys/sysctl.h>
41 #include "securityd_client.h"
42 #include "securityd_rep.h"
43 #include "securityd_server.h"
45 #include <securityd/spi.h>
47 #ifndef SECITEM_SHIM_OSX
48 #include <Security/SecTask.h>
49 #include <Security/SecEntitlements.h>
52 #if INDIGO || SECITEM_SHIM_OSX
53 #define CHECK_ENTITLEMENTS 0
55 #define CHECK_ENTITLEMENTS 1
58 /* Time after which securityd exits. */
60 #define TIMEOUT_IN_SECONDS 10.0
62 #define TIMEOUT_IN_SECONDS 10000.0
66 /* defines from <Security/SecTask.h> */
67 typedef struct __SecTask
*SecTaskRef
;
68 /* defines from <Security/SecEntitlements.h> */
69 #define kSecEntitlementGetTaskAllow CFSTR("get-task-allow")
70 #define kSecEntitlementApplicationIdentifier CFSTR("application-identifier")
71 #define kSecEntitlementKeychainAccessGroups CFSTR("keychain-access-groups")
72 #define kSecEntitlementModifyAnchorCertificates CFSTR("modify-anchor-certificates")
73 #define kSecEntitlementDebugApplications CFSTR("com.apple.springboard.debugapplications")
74 #define kSecEntitlementOpenSensitiveURL CFSTR("com.apple.springboard.opensensitiveurl")
75 #define kSecEntitlementWipeDevice CFSTR("com.apple.springboard.wipedevice")
76 #define kSecEntitlementRemoteNotificationConfigure CFSTR("com.apple.remotenotification.configure")
77 #define kSecEntitlementMigrateKeychain CFSTR("migrate-keychain")
78 #define kSecEntitlementRestoreKeychain CFSTR("restore-keychain")
82 static mach_port_t _server_port
= MACH_PORT_NULL
;
83 static unsigned int active_requests
= 0;
84 static CFRunLoopTimerRef idle_timer
= NULL
;
86 static mach_port_t
server_port(void *info
)
91 ret
= bootstrap_check_in(bootstrap_port
, SECURITYSERVER_BOOTSTRAP_NAME
, &_server_port
);
92 if (ret
== KERN_SUCCESS
) {
93 secdebug("server", "bootstrap_check_in() succeeded, return checked in port: 0x%x\n", _server_port
);
98 ret
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &_server_port
);
99 if (ret
!= KERN_SUCCESS
) {
100 secdebug("server", "mach_port_allocate(): 0x%x: %s\n", ret
, mach_error_string(ret
));
104 ret
= mach_port_insert_right(mach_task_self(), _server_port
, _server_port
, MACH_MSG_TYPE_MAKE_SEND
);
105 if (ret
!= KERN_SUCCESS
) {
106 secdebug("server", "mach_port_insert_right(): 0x%x: %s\n", ret
, mach_error_string(ret
));
110 ret
= bootstrap_register(bootstrap_port
, SECURITYSERVER_BOOTSTRAP_NAME
, _server_port
);
111 if (ret
!= BOOTSTRAP_SUCCESS
) {
112 secdebug("server", "bootstrap_register(): 0x%x: %s\n", ret
, mach_error_string(ret
));
124 #if CHECK_ENTITLEMENTS
125 static CFStringRef
SecTaskCopyStringForEntitlement(SecTaskRef task
,
126 CFStringRef entitlement
)
128 CFStringRef value
= (CFStringRef
)SecTaskCopyValueForEntitlement(task
,
130 if (value
&& CFGetTypeID(value
) != CFStringGetTypeID()) {
138 static CFArrayRef
SecTaskCopyArrayOfStringsForEntitlement(SecTaskRef task
,
139 CFStringRef entitlement
)
141 CFArrayRef value
= (CFArrayRef
)SecTaskCopyValueForEntitlement(task
,
144 if (CFGetTypeID(value
) == CFArrayGetTypeID()) {
145 CFIndex ix
, count
= CFArrayGetCount(value
);
146 for (ix
= 0; ix
< count
; ++ix
) {
147 CFStringRef string
= (CFStringRef
)CFArrayGetValueAtIndex(value
, ix
);
148 if (CFGetTypeID(string
) != CFStringGetTypeID()) {
162 #endif /* CHECK_ENTITLEMENTS */
164 static CFArrayRef
SecTaskCopyAccessGroups(SecTaskRef task
) {
165 #if CHECK_ENTITLEMENTS
166 CFStringRef appID
= SecTaskCopyStringForEntitlement(task
,
167 kSecEntitlementApplicationIdentifier
);
168 CFArrayRef groups
= SecTaskCopyArrayOfStringsForEntitlement(task
,
169 kSecEntitlementKeychainAccessGroups
);
172 CFMutableArrayRef nGroups
= CFArrayCreateMutableCopy(
173 CFGetAllocator(groups
), CFArrayGetCount(groups
) + 1, groups
);
174 CFArrayAppendValue(nGroups
, appID
);
178 groups
= CFArrayCreate(CFGetAllocator(task
),
179 (const void **)&appID
, 1, &kCFTypeArrayCallBacks
);
184 CFArrayRef groups
= SecAccessGroupsGetCurrent();
191 static bool SecTaskGetBooleanValueForEntitlement(SecTaskRef task
,
192 CFStringRef entitlement
) {
193 #if CHECK_ENTITLEMENTS
194 CFStringRef canModify
= (CFStringRef
)SecTaskCopyValueForEntitlement(task
,
198 CFTypeID canModifyType
= CFGetTypeID(canModify
);
199 if (CFBooleanGetTypeID() == canModifyType
) {
200 return CFBooleanGetValue((CFBooleanRef
)canModify
);
205 #endif /* !CHECK_ENTITLEMENTS */
209 static CFDataRef
CFArrayGetCheckedDataAtIndex() {
213 static bool isDictionary(CFTypeRef cfType
) {
214 return cfType
&& CFGetTypeID(cfType
) == CFDictionaryGetTypeID();
217 static bool isData(CFTypeRef cfType
) {
218 return cfType
&& CFGetTypeID(cfType
) == CFDataGetTypeID();
221 static bool isString(CFTypeRef cfType
) {
222 return cfType
&& CFGetTypeID(cfType
) == CFStringGetTypeID();
225 static bool isArray(CFTypeRef cfType
) {
226 return cfType
&& CFGetTypeID(cfType
) == CFArrayGetTypeID();
229 static bool isArrayOfLength(CFTypeRef cfType
, CFIndex length
) {
230 return isArray(cfType
) && CFArrayGetCount(cfType
) == length
;
233 static void idle_timer_proc(CFRunLoopTimerRef timer
, void *info
)
235 if (active_requests
== 0) {
236 /* If the idle timer fired and we have no active requests we exit. */
241 static void cancel_timeout()
244 CFRunLoopTimerInvalidate(idle_timer
);
245 CFRelease(idle_timer
);
250 static void register_timeout(void)
252 if (idle_timer
== NULL
) {
253 idle_timer
= CFRunLoopTimerCreate(kCFAllocatorDefault
,
254 CFAbsoluteTimeGetCurrent() + TIMEOUT_IN_SECONDS
,
255 TIMEOUT_IN_SECONDS
, 0, 0, idle_timer_proc
, NULL
);
256 if (idle_timer
== NULL
) {
257 asl_log(NULL
, NULL
, ASL_LEVEL_CRIT
,
258 "FATAL: failed to create idle timer, exiting.");
261 CFRunLoopAddTimer(CFRunLoopGetCurrent(), idle_timer
,
262 kCFRunLoopDefaultMode
);
266 static void request_begin(void)
268 if (active_requests
++ == 0) {
269 /* First request, cancel timer. */
274 static void request_end(void)
276 if (--active_requests
== 0) {
277 /* Last request, set timer. */
282 /* AUDIT[securityd](done):
283 reply (checked by mig) is a caller provided mach_port.
284 request_id (checked by mig) is caller provided value, that matches the
285 mig entry for the server function.
287 kern_return_t
securityd_server_send_reply(mach_port_t reply
,
288 uint32_t request_id
, OSStatus status
, CFTypeRef args_out
) {
289 CFDataRef data_out
= NULL
;
292 CFDataRef query_debug
= CFPropertyListCreateXMLData(kCFAllocatorDefault
,
294 secdebug("client", "securityd response: %.*s\n",
295 CFDataGetLength(query_debug
), CFDataGetBytePtr(query_debug
));
296 CFReleaseSafe(query_debug
);
298 CFErrorRef error
= NULL
;
299 data_out
= CFPropertyListCreateData(kCFAllocatorDefault
, args_out
,
300 kCFPropertyListBinaryFormat_v1_0
,
304 secdebug("server", "failed to encode return data: %@", error
);
305 CFReleaseSafe(error
);
309 void *p
= (data_out
? (void *)CFDataGetBytePtr(data_out
) : NULL
);
310 CFIndex l
= (data_out
? CFDataGetLength(data_out
) : 0);
311 /* 64 bits cast: securityd should never generate replies bigger than 2^32 bytes.
312 Worst case is we are truncating the reply we send to the client. This would only
313 cause the client side to not be able to decode the response. */
314 assert((unsigned long)l
<UINT_MAX
); /* Debug check */
315 kern_return_t err
= securityd_client_reply(reply
, request_id
, status
,
318 CFReleaseSafe(data_out
);
325 struct securityd_server_trust_evaluation_context
{
331 securityd_server_trust_evaluate_done(const void *userData
,
332 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
333 SecTrustResultType result
) {
334 struct securityd_server_trust_evaluation_context
*tec
=
335 (struct securityd_server_trust_evaluation_context
*)userData
;
337 /* @@@ This code snippit is also in SecTrustServer.c. I'd factor it,
338 but a better fix would be to change the interfaces here to not use
339 single in/out args and do all the argument munging in server.c
341 CFDictionaryRef args_out
;
342 CFNumberRef resultNumber
= NULL
;
343 CFArrayRef chain_certs
= NULL
;
344 /* Proccess outgoing results. */
345 resultNumber
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &result
);
346 chain_certs
= SecCertificatePathCopyArray(chain
);
347 const void *out_keys
[] = { kSecTrustChainKey
, kSecTrustDetailsKey
,
348 kSecTrustInfoKey
, kSecTrustResultKey
};
349 const void *out_values
[] = { chain_certs
, details
, info
, resultNumber
};
350 args_out
= (CFTypeRef
)CFDictionaryCreate(kCFAllocatorDefault
, out_keys
,
351 out_values
, sizeof(out_keys
) / sizeof(*out_keys
),
352 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
353 CFReleaseSafe(chain_certs
);
354 CFReleaseSafe(resultNumber
);
356 /* Send back the response to the client. */
357 securityd_server_send_reply(tec
->reply
, tec
->request_id
, noErr
, args_out
);
362 kern_return_t
securityd_server_request(mach_port_t receiver
, mach_port_t reply
,
363 audit_token_t auditToken
,
364 uint32_t request_id
, uint32_t msg_id
, uint8_t *msg_data
,
365 mach_msg_type_number_t msg_length
);
367 /* AUDIT[securityd](done):
368 receiver (unused) is a mach_port owned by this process.
369 reply (checked by mig) is a caller provided mach_port.
370 auditToken (ok) is a kernel provided audit token.
371 request_id (checked by mig) is caller provided value, that matches the
372 mig entry for this function.
373 msg_id (ok) is caller provided value.
374 msg_data (ok) is a caller provided data of length:
377 kern_return_t
securityd_server_request(mach_port_t receiver
, mach_port_t reply
,
378 audit_token_t auditToken
,
379 uint32_t request_id
, uint32_t msg_id
, uint8_t *msg_data
,
380 mach_msg_type_number_t msg_length
)
382 CFTypeRef args_in
= NULL
;
383 CFTypeRef args_out
= NULL
;
384 bool sendResponse
= true;
390 CFDataRef data_in
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
391 msg_data
, msg_length
, kCFAllocatorNull
);
393 args_in
= CFPropertyListCreateFromXMLData(kCFAllocatorDefault
,
394 data_in
, kCFPropertyListImmutable
, NULL
);
400 static int crash_counter
= 0;
401 if (crash_counter
++) {
402 secdebug("server", "crash test");
407 SecTaskRef clientTask
=
408 #if CHECK_ENTITLEMENTS
409 SecTaskCreateWithAuditToken(kCFAllocatorDefault
, auditToken
);
413 CFArrayRef groups
= SecTaskCopyAccessGroups(clientTask
);
414 SecAccessGroupsSetCurrent(groups
);
415 OSStatus status
= errSecParam
;
417 case sec_item_add_id
:
418 op_name
= "SecItemAdd";
419 if (isDictionary(args_in
))
420 status
= _SecItemAdd(args_in
, &args_out
, groups
);
422 case sec_item_copy_matching_id
:
423 op_name
= "SecItemCopyMatching";
424 if (isDictionary(args_in
))
425 status
= _SecItemCopyMatching(args_in
, &args_out
, groups
);
427 case sec_item_delete_id
:
428 op_name
= "SecItemDelete";
429 if (isDictionary(args_in
))
430 status
= _SecItemDelete(args_in
, groups
);
432 case sec_item_update_id
:
433 op_name
= "SecItemUpdate";
434 if (isArrayOfLength(args_in
, 2)) {
436 in0
= (CFDictionaryRef
)CFArrayGetValueAtIndex(args_in
, 0),
437 in1
= (CFDictionaryRef
)CFArrayGetValueAtIndex(args_in
, 1);
438 if (isDictionary(in0
) && isDictionary(in1
))
439 status
= _SecItemUpdate(in0
, in1
, groups
);
442 case sec_trust_store_contains_id
:
444 op_name
= "SecTrustStoreContains";
445 if (!isArray(args_in
))
447 CFIndex argc_in
= CFArrayGetCount(args_in
);
450 CFStringRef domainName
= CFArrayGetValueAtIndex(args_in
, 0);
451 CFDataRef digest
= CFArrayGetValueAtIndex(args_in
, 1);
452 if (!isString(domainName
) || !isData(digest
))
455 SecTrustStoreRef ts
= SecTrustStoreForDomainName(domainName
);
456 status
= !SecTrustStoreContainsCertificateWithDigest(ts
, digest
);
459 case sec_trust_store_set_trust_settings_id
:
461 op_name
= "SecTrustStoreSetTrustSettings";
462 /* Open the trust store unconditially so we can abuse this method
463 even in clients that just want to read from the truststore,
464 and this call will force it to be created. */
465 SecTrustStoreRef ts
= SecTrustStoreForDomain(kSecTrustStoreDomainUser
);
466 if (!isArray(args_in
))
468 CFIndex argc_in
= CFArrayGetCount(args_in
);
469 if (argc_in
!= 1 && argc_in
!= 2)
471 if (!SecTaskGetBooleanValueForEntitlement(clientTask
,
472 kSecEntitlementModifyAnchorCertificates
)) {
473 status
= errSecMissingEntitlement
;
476 CFDataRef certificateData
= (CFDataRef
)CFArrayGetValueAtIndex(args_in
, 0);
477 if (!isData(certificateData
))
479 SecCertificateRef certificate
= SecCertificateCreateWithData(NULL
, certificateData
);
481 CFTypeRef trustSettingsDictOrArray
;
483 trustSettingsDictOrArray
= NULL
;
485 trustSettingsDictOrArray
= CFArrayGetValueAtIndex(args_in
, 1);
486 if (trustSettingsDictOrArray
) {
487 CFTypeID tst
= CFGetTypeID(trustSettingsDictOrArray
);
488 if (tst
!= CFArrayGetTypeID() && tst
!= CFDictionaryGetTypeID()) {
489 CFRelease(certificate
);
494 status
= _SecTrustStoreSetTrustSettings(ts
, certificate
, trustSettingsDictOrArray
);
495 CFRelease(certificate
);
499 case sec_trust_store_remove_certificate_id
:
500 op_name
= "SecTrustStoreRemoveCertificate";
501 if (SecTaskGetBooleanValueForEntitlement(clientTask
,
502 kSecEntitlementModifyAnchorCertificates
)) {
503 SecTrustStoreRef ts
= SecTrustStoreForDomain(kSecTrustStoreDomainUser
);
504 if (isData(args_in
)) {
505 status
= SecTrustStoreRemoveCertificateWithDigest(ts
, args_in
);
508 status
= errSecMissingEntitlement
;
511 case sec_delete_all_id
:
512 op_name
= "SecDeleteAll";
513 if (SecTaskGetBooleanValueForEntitlement(clientTask
,
514 kSecEntitlementWipeDevice
)) {
515 status
= SecItemDeleteAll();
517 status
= errSecMissingEntitlement
;
520 case sec_trust_evaluate_id
:
521 op_name
= "SecTrustEvaluate";
522 if (isDictionary(args_in
)) {
523 struct securityd_server_trust_evaluation_context
*tec
= malloc(sizeof(*tec
));
525 tec
->request_id
= request_id
;
526 status
= SecTrustServerEvaluateAsync(args_in
,
527 securityd_server_trust_evaluate_done
, tec
);
528 if (status
== noErr
|| status
== errSecWaitForCallback
) {
529 sendResponse
= false;
535 case sec_restore_keychain_id
:
536 op_name
= "SecRestoreKeychain";
537 if (SecTaskGetBooleanValueForEntitlement(clientTask
,
538 kSecEntitlementRestoreKeychain
)) {
539 status
= _SecServerRestoreKeychain();
541 status
= errSecMissingEntitlement
;
544 case sec_migrate_keychain_id
:
545 op_name
= "SecMigrateKeychain";
546 if (isArray(args_in
)) {
547 if (SecTaskGetBooleanValueForEntitlement(clientTask
,
548 kSecEntitlementMigrateKeychain
)) {
549 status
= _SecServerMigrateKeychain(args_in
, &args_out
);
551 status
= errSecMissingEntitlement
;
555 case sec_keychain_backup_id
:
556 op_name
= "SecKeychainBackup";
557 if (!args_in
|| isArray(args_in
)) {
558 if (SecTaskGetBooleanValueForEntitlement(clientTask
,
559 kSecEntitlementRestoreKeychain
)) {
560 status
= _SecServerKeychainBackup(args_in
, &args_out
);
562 status
= errSecMissingEntitlement
;
566 case sec_keychain_restore_id
:
567 op_name
= "SecKeychainRestore";
568 if (isArray(args_in
)) {
569 if (SecTaskGetBooleanValueForEntitlement(clientTask
,
570 kSecEntitlementRestoreKeychain
)) {
571 status
= _SecServerKeychainRestore(args_in
, &args_out
);
573 status
= errSecMissingEntitlement
;
578 op_name
= "invalid_operation";
579 status
= errSecParam
;
583 const char *proc_name
;
585 if (status
== errSecMissingEntitlement
) {
588 audit_token_to_au32(auditToken
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
);
589 int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
};
590 struct kinfo_proc kp
;
591 size_t len
= sizeof(kp
);
592 if (sysctl(mib
, 4, &kp
, &len
, NULL
, 0) == -1 || len
== 0)
593 proc_name
= strerror(errno
);
595 proc_name
= kp
.kp_proc
.p_comm
;
598 if (status
== errSecMissingEntitlement
) {
600 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
601 "%s[%u] %s: missing entitlement", proc_name
, pid
, op_name
);
602 /* Remap errSecMissingEntitlement -> errSecInteractionNotAllowed. */
603 status
= errSecInteractionNotAllowed
;
606 secdebug("ipc", "%s[%u] %s: returning: %d", proc_name
, pid
, op_name
,
609 CFReleaseSafe(groups
);
610 CFReleaseSafe(clientTask
);
611 SecAccessGroupsSetCurrent(NULL
);
613 kern_return_t err
= 0;
615 err
= securityd_server_send_reply(reply
, request_id
, status
, args_out
);
617 CFReleaseSafe(args_in
);
622 extern boolean_t
securityd_request_server(mach_msg_header_t
*InHeadP
, mach_msg_header_t
*OutHeadP
);
624 union max_msg_size_union
{
625 union __RequestUnion__securityd_client_securityd_reply_subsystem reply
;
628 static uint8_t reply_buffer
[sizeof(union max_msg_size_union
) + MAX_TRAILER_SIZE
];
630 static void *handle_message(void *msg
, CFIndex size
,
631 CFAllocatorRef allocator
, void *info
)
633 mach_msg_header_t
*message
= (mach_msg_header_t
*)msg
;
634 mach_msg_header_t
*reply
= (mach_msg_header_t
*)reply_buffer
;
636 securityd_request_server(message
, reply
);
642 static void register_server(void)
644 CFRunLoopSourceContext1 context
= { 1, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
645 server_port
, handle_message
};
646 CFRunLoopSourceRef source
= CFRunLoopSourceCreate(NULL
, 0,
647 (CFRunLoopSourceContext
*)&context
);
650 secdebug("server", "failed to create source for port, exiting.");
653 CFRunLoopAddSource(CFRunLoopGetCurrent(), source
, kCFRunLoopDefaultMode
);
658 int main(int argc
, char *argv
[])
667 /* vi:set ts=4 sw=4 et: */