]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SharedWebCredential/swcagent_client.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / SharedWebCredential / swcagent_client.c
1 /*
2 * Copyright (c) 2014 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 <stdbool.h>
25 #include <sys/queue.h>
26 #include <syslog.h>
27
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>
33
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"
41
42 #include <xpc/private.h>
43
44
45 CFStringRef sSWCAXPCErrorDomain = CFSTR("com.apple.security.swcagent");
46 CFStringRef sSWCASecAttrServer = CFSTR("srvr");
47
48 //
49 // MARK: XPC IPC.
50 //
51
52 static xpc_connection_t swca_create_connection(const char *name) {
53 if (!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);
60 });
61 xpc_connection_resume(connection);
62 return connection;
63 }
64
65 static xpc_connection_t sSWCAConnection;
66
67 static xpc_connection_t swca_connection(void) {
68 static dispatch_once_t once;
69 dispatch_once(&once, ^{
70 sSWCAConnection = swca_create_connection(NULL);
71 });
72 return sSWCAConnection;
73 }
74
75 xpc_object_t
76 swca_message_with_reply_sync(xpc_object_t message, CFErrorRef *error)
77 {
78 xpc_object_t reply = NULL;
79 xpc_connection_t connection = swca_connection();
80
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.
82
83 unsigned int tries_left = max_tries;
84 do {
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);
88
89 if (xpc_get_type(reply) == XPC_TYPE_ERROR) {
90 CFIndex code = 0;
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;
95 else
96 code = kSecXPCErrorUnknown;
97
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);
101 free(conn_desc);
102 xpc_release(reply);
103 reply = NULL;
104 }
105
106 return reply;
107 }
108
109 xpc_object_t swca_create_message(enum SWCAXPCOperation op, CFErrorRef* error)
110 {
111 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
112 if (message) {
113 xpc_dictionary_set_uint64(message, kSecXPCKeyOperation, op);
114 } else {
115 SecCFCreateError(kSecXPCErrorConnectionFailed, sSWCAXPCErrorDomain,
116 CFSTR("xpc_dictionary_create returned NULL"), NULL, error);
117 }
118 return message;
119 }
120
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)
125 return true;
126
127 if (error) {
128 *error = SecCreateCFErrorWithXPCObject(xpc_error);
129 }
130 return false;
131 }
132
133 // Return int value of message reply (or -1 if error)
134 long swca_message_response(xpc_object_t replyMessage, CFErrorRef *error) {
135 int32_t value = -1;
136 CFTypeRef result = NULL;
137 if (!swca_message_no_error(replyMessage, error) ||
138 !SecXPCDictionaryCopyPListOptional(replyMessage, kSecXPCKeyResult, &result, error) ||
139 !result) {
140 return value;
141 }
142 CFTypeID typeID = CFGetTypeID(result);
143 if (typeID == CFBooleanGetTypeID()) {
144 value = (CFEqual((CFBooleanRef)result, kCFBooleanTrue)) ? 1 : 0;
145 }
146 else if (typeID == CFNumberGetTypeID()) {
147 if (!CFNumberGetValue((CFNumberRef)result, kCFNumberSInt32Type, &value)) {
148 value = -1;
149 }
150 }
151 CFReleaseSafe(result);
152 return value;
153 }
154
155 bool swca_autofill_enabled(const audit_token_t *auditToken)
156 {
157 bool result = false;
158 CFErrorRef error = NULL;
159 xpc_object_t message = swca_create_message(swca_enabled_request_id, &error);
160 if (message) {
161 xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t));
162 xpc_object_t reply = swca_message_with_reply_sync(message, &error);
163 if (reply) {
164 long value = swca_message_response(reply, &error);
165 result = (value > 0);
166 xpc_release(reply);
167 }
168 xpc_release(message);
169 }
170 CFReleaseSafe(error);
171 return result;
172 }
173
174 bool swca_confirm_operation(enum SWCAXPCOperation op,
175 const audit_token_t *auditToken,
176 CFTypeRef query,
177 CFErrorRef *error,
178 void (^add_negative_entry)(CFStringRef fqdn))
179 {
180 bool result = false;
181 xpc_object_t message = swca_create_message(op, error);
182 if (message) {
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);
186 if (reply) {
187 long value = swca_message_response(reply, error);
188 //
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)
194 //
195 result = (value == 1);
196 if (value == 0 && add_negative_entry) {
197 CFStringRef fqdn = CFDictionaryGetValue(query, sSWCASecAttrServer);
198 add_negative_entry(fqdn);
199 }
200 xpc_release(reply);
201 }
202 }
203 xpc_release(message);
204 }
205 return result;
206 }
207
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);
214 }
215 return result;
216 }
217
218 CFDictionaryRef swca_copy_selected_dictionary(enum SWCAXPCOperation op,
219 const audit_token_t *auditToken,
220 CFTypeRef items, // array of dictionaries
221 CFErrorRef *error)
222 {
223 CFDictionaryRef result = NULL;
224 xpc_object_t message = swca_create_message(op, error);
225 if (message) {
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);
229 if (reply) {
230 result = (CFDictionaryRef) swca_message_copy_response(reply, error);
231 if (!(result && CFGetTypeID(result) == CFDictionaryGetTypeID())) {
232 CFReleaseNull(result);
233 }
234 xpc_release(reply);
235 }
236 }
237 xpc_release(message);
238 }
239 return result;
240 }
241
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,
245 CFErrorRef *error)
246 {
247 CFArrayRef result = NULL;
248 xpc_object_t message = swca_create_message(op, error);
249 if (message) {
250 xpc_dictionary_set_data(message, kSecXPCKeyClientToken, auditToken, sizeof(audit_token_t));
251 xpc_object_t reply = swca_message_with_reply_sync(message, error);
252 if (reply) {
253 result = (CFArrayRef) swca_message_copy_response(reply, error);
254 if (!(result && CFGetTypeID(result) == CFArrayGetTypeID())) {
255 CFReleaseNull(result);
256 }
257 xpc_release(reply);
258 }
259 xpc_release(message);
260 }
261 return result;
262 }
263
264 bool swca_set_selection(enum SWCAXPCOperation op,
265 const audit_token_t *auditToken,
266 CFTypeRef dictionary,
267 CFErrorRef *error)
268 {
269 bool result = false;
270 xpc_object_t message = swca_create_message(op, error);
271 if (message) {
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);
275 if (reply) {
276 long value = swca_message_response(reply, error);
277 if (value != 0) {
278 result = true;
279 };
280 xpc_release(reply);
281 }
282 }
283 xpc_release(message);
284 }
285 return result;
286 }
287
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);
292 bool ok = false;
293 if (message) {
294 if (!add_to_message || add_to_message(message, error)) {
295 xpc_object_t response = swca_message_with_reply_sync(message, error);
296 if (response) {
297 if (swca_message_no_error(response, error)) {
298 ok = (!handle_response || handle_response(response, error));
299 }
300 xpc_release(response);
301 }
302 }
303 xpc_release(message);
304 }
305
306 return ok;
307 }
308
309
310 /* vi:set ts=4 sw=4 et: */