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
45CFStringRef sSWCAXPCErrorDomain = CFSTR("com.apple.security.swcagent");
46CFStringRef sSWCASecAttrServer = CFSTR("srvr");
47
48//
49// MARK: XPC IPC.
50//
51
52static 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
65static xpc_connection_t sSWCAConnection;
66
67static 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
75xpc_object_t
76swca_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
109xpc_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.
122bool 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)
134long 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
155bool 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
174bool 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)
209CFTypeRef 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
218CFDictionaryRef 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)
243CFArrayRef 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
264bool 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
288bool 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: */
311