2 * Copyright (c) 2014 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@
25 #include <sys/queue.h>
28 #include <CoreFoundation/CoreFoundation.h>
29 #include <Security/SecItem.h>
30 #include <Security/SecBasePriv.h>
31 #include <Security/SecInternal.h>
32 #include <Security/SecuritydXPC.h>
34 #include <utilities/debugging.h>
35 #include <utilities/SecCFError.h>
36 #include <utilities/SecXPCError.h>
37 #include <utilities/SecCFWrappers.h>
38 #include <utilities/SecDispatchRelease.h>
39 #include <utilities/SecDb.h> // TODO Fixme this gets us SecError().
40 #include "swcagent_client.h"
42 #include <xpc/private.h>
45 CFStringRef sSWCAXPCErrorDomain
= CFSTR("com.apple.security.swcagent");
46 CFStringRef sSWCASecAttrServer
= CFSTR("srvr");
52 static xpc_connection_t
swca_create_connection(const char *name
) {
54 name
= kSWCAXPCServiceName
;
55 xpc_connection_t connection
;
56 connection
= xpc_connection_create_mach_service(name
, NULL
, 0);
57 xpc_connection_set_event_handler(connection
, ^(xpc_object_t event
) {
58 const char *description
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
59 secnotice("xpc", "got event: %s", description
);
61 xpc_connection_resume(connection
);
65 static xpc_connection_t sSWCAConnection
;
67 static xpc_connection_t
swca_connection(void) {
68 static dispatch_once_t once
;
69 dispatch_once(&once
, ^{
70 sSWCAConnection
= swca_create_connection(NULL
);
72 return sSWCAConnection
;
76 swca_message_with_reply_sync(xpc_object_t message
, CFErrorRef
*error
)
78 xpc_object_t reply
= NULL
;
79 xpc_connection_t connection
= swca_connection();
81 const int max_tries
= 4; // Per <rdar://problem/17829836> N61/12A342: Audio Playback... for why this needs to be at least 3, so we made it 4.
83 unsigned int tries_left
= max_tries
;
85 if (reply
) xpc_release(reply
);
86 reply
= xpc_connection_send_message_with_reply_sync(connection
, message
);
87 } while (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
&& --tries_left
> 0);
89 if (xpc_get_type(reply
) == XPC_TYPE_ERROR
) {
91 if (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
|| reply
== XPC_ERROR_CONNECTION_INVALID
)
92 code
= kSecXPCErrorConnectionFailed
;
93 else if (reply
== XPC_ERROR_TERMINATION_IMMINENT
)
94 code
= kSecXPCErrorUnknown
;
96 code
= kSecXPCErrorUnknown
;
98 char *conn_desc
= xpc_copy_description(connection
);
99 const char *description
= xpc_dictionary_get_string(reply
, XPC_ERROR_KEY_DESCRIPTION
);
100 SecCFCreateErrorWithFormat(code
, sSWCAXPCErrorDomain
, NULL
, error
, NULL
, CFSTR("%s: %s"), conn_desc
, description
);
109 xpc_object_t
swca_create_message(enum SWCAXPCOperation op
, CFErrorRef
* error
)
111 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
113 xpc_dictionary_set_uint64(message
, kSecXPCKeyOperation
, op
);
115 SecCFCreateError(kSecXPCErrorConnectionFailed
, sSWCAXPCErrorDomain
,
116 CFSTR("xpc_dictionary_create returned NULL"), NULL
, error
);
121 // Return true if there is no error in message, return false and set *error if there is.
122 bool swca_message_no_error(xpc_object_t message
, CFErrorRef
*error
) {
123 xpc_object_t xpc_error
= xpc_dictionary_get_value(message
, kSecXPCKeyError
);
124 if (xpc_error
== NULL
)
128 *error
= SecCreateCFErrorWithXPCObject(xpc_error
);
133 // Return int value of message reply (or -1 if error)
134 long swca_message_response(xpc_object_t replyMessage
, CFErrorRef
*error
) {
136 CFTypeRef result
= NULL
;
137 if (!swca_message_no_error(replyMessage
, error
) ||
138 !SecXPCDictionaryCopyPListOptional(replyMessage
, kSecXPCKeyResult
, &result
, error
) ||
142 CFTypeID typeID
= CFGetTypeID(result
);
143 if (typeID
== CFBooleanGetTypeID()) {
144 value
= (CFEqual((CFBooleanRef
)result
, kCFBooleanTrue
)) ? 1 : 0;
146 else if (typeID
== CFNumberGetTypeID()) {
147 if (!CFNumberGetValue((CFNumberRef
)result
, kCFNumberSInt32Type
, &value
)) {
151 CFReleaseSafe(result
);
155 bool swca_autofill_enabled(const audit_token_t
*auditToken
)
158 CFErrorRef error
= NULL
;
159 xpc_object_t message
= swca_create_message(swca_enabled_request_id
, &error
);
161 xpc_dictionary_set_data(message
, kSecXPCKeyClientToken
, auditToken
, sizeof(audit_token_t
));
162 xpc_object_t reply
= swca_message_with_reply_sync(message
, &error
);
164 long value
= swca_message_response(reply
, &error
);
165 result
= (value
> 0);
168 xpc_release(message
);
170 CFReleaseSafe(error
);
174 bool swca_confirm_operation(enum SWCAXPCOperation op
,
175 const audit_token_t
*auditToken
,
178 void (^add_negative_entry
)(CFStringRef fqdn
))
181 xpc_object_t message
= swca_create_message(op
, error
);
183 xpc_dictionary_set_data(message
, kSecXPCKeyClientToken
, auditToken
, sizeof(audit_token_t
));
184 if (SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
)) {
185 xpc_object_t reply
= swca_message_with_reply_sync(message
, error
);
187 long value
= swca_message_response(reply
, error
);
189 // possible values (see CFUserNotification.h):
190 // unable to get message response = -1
191 // kCFUserNotificationDefaultResponse = 0 "Don't Allow" (i.e. permanently)
192 // kCFUserNotificationAlternateResponse = 1 "OK"
193 // kCFUserNotificationOtherResponse = 2 (no longer used)
195 result
= (value
== 1);
196 if (value
== 0 && add_negative_entry
) {
197 CFStringRef fqdn
= CFDictionaryGetValue(query
, sSWCASecAttrServer
);
198 add_negative_entry(fqdn
);
203 xpc_release(message
);
208 // Return retained dictionary value of message reply (or NULL if error)
209 CFTypeRef
swca_message_copy_response(xpc_object_t replyMessage
, CFErrorRef
*error
) {
210 CFTypeRef result
= NULL
;
211 if (!swca_message_no_error(replyMessage
, error
) ||
212 !SecXPCDictionaryCopyPListOptional(replyMessage
, kSecXPCKeyResult
, &result
, error
)) {
213 CFReleaseNull(result
);
218 CFDictionaryRef
swca_copy_selected_dictionary(enum SWCAXPCOperation op
,
219 const audit_token_t
*auditToken
,
220 CFTypeRef items
, // array of dictionaries
223 CFDictionaryRef result
= NULL
;
224 xpc_object_t message
= swca_create_message(op
, error
);
226 xpc_dictionary_set_data(message
, kSecXPCKeyClientToken
, auditToken
, sizeof(audit_token_t
));
227 if (SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, items
, error
)) {
228 xpc_object_t reply
= swca_message_with_reply_sync(message
, error
);
230 result
= (CFDictionaryRef
) swca_message_copy_response(reply
, error
);
231 if (!(result
&& CFGetTypeID(result
) == CFDictionaryGetTypeID())) {
232 CFReleaseNull(result
);
237 xpc_release(message
);
242 // Return retained array value of message reply (or NULL if error)
243 CFArrayRef
swca_copy_pairs(enum SWCAXPCOperation op
,
244 const audit_token_t
*auditToken
,
247 CFArrayRef result
= NULL
;
248 xpc_object_t message
= swca_create_message(op
, error
);
250 xpc_dictionary_set_data(message
, kSecXPCKeyClientToken
, auditToken
, sizeof(audit_token_t
));
251 xpc_object_t reply
= swca_message_with_reply_sync(message
, error
);
253 result
= (CFArrayRef
) swca_message_copy_response(reply
, error
);
254 if (!(result
&& CFGetTypeID(result
) == CFArrayGetTypeID())) {
255 CFReleaseNull(result
);
259 xpc_release(message
);
264 bool swca_set_selection(enum SWCAXPCOperation op
,
265 const audit_token_t
*auditToken
,
266 CFTypeRef dictionary
,
270 xpc_object_t message
= swca_create_message(op
, error
);
272 xpc_dictionary_set_data(message
, kSecXPCKeyClientToken
, auditToken
, sizeof(audit_token_t
));
273 if (SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, dictionary
, error
)) {
274 xpc_object_t reply
= swca_message_with_reply_sync(message
, error
);
276 long value
= swca_message_response(reply
, error
);
283 xpc_release(message
);
288 bool swca_send_sync_and_do(enum SWCAXPCOperation op
, CFErrorRef
*error
,
289 bool (^add_to_message
)(xpc_object_t message
, CFErrorRef
* error
),
290 bool (^handle_response
)(xpc_object_t response
, CFErrorRef
* error
)) {
291 xpc_object_t message
= swca_create_message(op
, error
);
294 if (!add_to_message
|| add_to_message(message
, error
)) {
295 xpc_object_t response
= swca_message_with_reply_sync(message
, error
);
297 if (swca_message_no_error(response
, error
)) {
298 ok
= (!handle_response
|| handle_response(response
, error
));
300 xpc_release(response
);
303 xpc_release(message
);
310 /* vi:set ts=4 sw=4 et: */