summaryrefslogtreecommitdiffstats
path: root/src/preflight.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/preflight.c')
-rw-r--r--src/preflight.c406
1 files changed, 406 insertions, 0 deletions
diff --git a/src/preflight.c b/src/preflight.c
new file mode 100644
index 0000000..9c57e98
--- /dev/null
+++ b/src/preflight.c
@@ -0,0 +1,406 @@
1/*
2 * preflight.c
3 *
4 * Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27#include <errno.h>
28
29#include <sys/time.h>
30
31#ifdef HAVE_LIBIMOBILEDEVICE
32#include <libimobiledevice/libimobiledevice.h>
33#include <libimobiledevice/lockdown.h>
34#include <libimobiledevice/notification_proxy.h>
35#endif
36
37#include <libimobiledevice-glue/thread.h>
38
39#include "preflight.h"
40#include "device.h"
41#include "client.h"
42#include "conf.h"
43#include "log.h"
44#include "usb.h"
45
46extern int no_preflight;
47
48#ifdef HAVE_LIBIMOBILEDEVICE
49#ifndef HAVE_ENUM_IDEVICE_CONNECTION_TYPE
50enum idevice_connection_type {
51 CONNECTION_USBMUXD = 1,
52 CONNECTION_NETWORK
53};
54#endif
55
56struct idevice_private {
57 char *udid;
58 uint32_t mux_id;
59 enum idevice_connection_type conn_type;
60 void *conn_data;
61 int version;
62 int device_class;
63};
64
65struct cb_data {
66 idevice_t dev;
67 np_client_t np;
68 int is_device_connected;
69 int is_finished;
70};
71
72static void lockdownd_set_untrusted_host_buid(lockdownd_client_t lockdown)
73{
74 char* system_buid = NULL;
75 config_get_system_buid(&system_buid);
76 usbmuxd_log(LL_DEBUG, "%s: Setting UntrustedHostBUID to %s", __func__, system_buid);
77 lockdownd_set_value(lockdown, NULL, "UntrustedHostBUID", plist_new_string(system_buid));
78 free(system_buid);
79}
80
81void preflight_device_remove_cb(void *data)
82{
83 if (!data)
84 return;
85 struct cb_data *cbdata = (struct cb_data*)data;
86 cbdata->is_device_connected = 0;
87}
88
89static void np_callback(const char* notification, void* userdata)
90{
91 struct cb_data *cbdata = (struct cb_data*)userdata;
92 idevice_t dev = cbdata->dev;
93 struct idevice_private *_dev = (struct idevice_private*)dev;
94
95 lockdownd_client_t lockdown = NULL;
96 lockdownd_error_t lerr;
97
98 if (strlen(notification) == 0) {
99 cbdata->np = NULL;
100 return;
101 }
102
103 if (strcmp(notification, "com.apple.mobile.lockdown.request_pair") == 0) {
104 usbmuxd_log(LL_INFO, "%s: user trusted this computer on device %s, pairing now", __func__, _dev->udid);
105 lerr = lockdownd_client_new(dev, &lockdown, "usbmuxd");
106 if (lerr != LOCKDOWN_E_SUCCESS) {
107 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr);
108 cbdata->is_finished = 1;
109 return;
110 }
111
112 lerr = lockdownd_pair(lockdown, NULL);
113 if (lerr != LOCKDOWN_E_SUCCESS) {
114 usbmuxd_log(LL_ERROR, "%s: ERROR: Pair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr);
115 lockdownd_client_free(lockdown);
116 cbdata->is_finished = 1;
117 return;
118 }
119 lockdownd_client_free(lockdown);
120 cbdata->is_finished = 1;
121
122 } else if (strcmp(notification, "com.apple.mobile.lockdown.request_host_buid") == 0) {
123 lerr = lockdownd_client_new(cbdata->dev, &lockdown, "usbmuxd");
124 if (lerr != LOCKDOWN_E_SUCCESS) {
125 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr);
126 } else {
127 lockdownd_set_untrusted_host_buid(lockdown);
128 lockdownd_client_free(lockdown);
129 }
130 }
131}
132
133static void* preflight_worker_handle_device_add(void* userdata)
134{
135 struct device_info *info = (struct device_info*)userdata;
136 struct idevice_private *_dev = (struct idevice_private*)malloc(sizeof(struct idevice_private));
137 _dev->udid = strdup(info->serial);
138 _dev->mux_id = info->id;
139 _dev->conn_type = CONNECTION_USBMUXD;
140 _dev->conn_data = NULL;
141 _dev->version = 0;
142 _dev->device_class = 0;
143
144 idevice_t dev = (idevice_t)_dev;
145
146 lockdownd_client_t lockdown = NULL;
147 lockdownd_error_t lerr;
148
149 plist_t value = NULL;
150 char* version_str = NULL;
151 char* deviceclass_str = NULL;
152
153 usbmuxd_log(LL_INFO, "%s: Starting preflight on device %s...", __func__, _dev->udid);
154
155retry:
156 lerr = lockdownd_client_new(dev, &lockdown, "usbmuxd");
157 if (lerr != LOCKDOWN_E_SUCCESS) {
158 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr);
159 goto leave;
160 }
161
162 char *type = NULL;
163 lerr = lockdownd_query_type(lockdown, &type);
164 if (!type) {
165 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get lockdownd type from device %s, lockdown error %d", __func__, _dev->udid, lerr);
166 goto leave;
167 }
168
169 if (strcmp(type, "com.apple.mobile.lockdown") != 0) {
170 // make restore mode devices visible
171 free(type);
172 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
173 client_device_add(info);
174 goto leave;
175 }
176 free(type);
177
178 int is_device_paired = 0;
179 char *host_id = NULL;
180 if (config_has_device_record(dev->udid)) {
181 config_device_record_get_host_id(dev->udid, &host_id);
182 lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL);
183 if (host_id)
184 free(host_id);
185 if (lerr == LOCKDOWN_E_SUCCESS) {
186 usbmuxd_log(LL_INFO, "%s: StartSession success for device %s", __func__, _dev->udid);
187 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
188 client_device_add(info);
189 goto leave;
190 }
191
192 usbmuxd_log(LL_INFO, "%s: StartSession failed on device %s, lockdown error %d", __func__, _dev->udid, lerr);
193 } else {
194 lerr = LOCKDOWN_E_INVALID_HOST_ID;
195 }
196 switch (lerr) {
197 case LOCKDOWN_E_INVALID_HOST_ID:
198 usbmuxd_log(LL_INFO, "%s: Device %s is not paired with this host.", __func__, _dev->udid);
199 break;
200 case LOCKDOWN_E_SSL_ERROR:
201 usbmuxd_log(LL_ERROR, "%s: The stored pair record for device %s is invalid. Removing.", __func__, _dev->udid);
202 if (config_remove_device_record(_dev->udid) == 0) {
203 lockdownd_client_free(lockdown);
204 lockdown = NULL;
205 goto retry;
206 } else {
207 usbmuxd_log(LL_ERROR, "%s: Could not remove pair record for device %s", __func__, _dev->udid);
208 }
209 break;
210 default:
211 is_device_paired = 1;
212 break;
213 }
214
215 lerr = lockdownd_get_value(lockdown, NULL, "ProductVersion", &value);
216 if (lerr != LOCKDOWN_E_SUCCESS) {
217 usbmuxd_log(LL_WARNING, "%s: Could not get ProductVersion from device %s, lockdown error %d", __func__, _dev->udid, lerr);
218 /* assume old iOS version */
219 version_str = strdup("1.0");
220 } else {
221 if (value && plist_get_node_type(value) == PLIST_STRING) {
222 plist_get_string_val(value, &version_str);
223 }
224 plist_free(value);
225
226 if (!version_str) {
227 usbmuxd_log(LL_ERROR, "%s: Could not get ProductVersion string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data);
228 goto leave;
229 }
230 }
231
232 lerr = lockdownd_get_value(lockdown, NULL, "DeviceClass", &value);
233 if (lerr != LOCKDOWN_E_SUCCESS) {
234 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get DeviceClass from device %s, lockdown error %d", __func__, _dev->udid, lerr);
235 goto leave;
236 }
237 if (value && plist_get_node_type(value) == PLIST_STRING) {
238 plist_get_string_val(value, &deviceclass_str);
239 }
240 plist_free(value);
241
242 if (!deviceclass_str) {
243 usbmuxd_log(LL_ERROR, "%s: Could not get DeviceClass string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data);
244 goto leave;
245 }
246
247 int version_major = strtol(version_str, NULL, 10);
248 if (((!strcmp(deviceclass_str, "iPhone") || !strcmp(deviceclass_str, "iPad")) && version_major >= 7)
249 || (!strcmp(deviceclass_str, "Watch") && version_major >= 2)
250 || (!strcmp(deviceclass_str, "AppleTV") && version_major >= 9)
251 ) {
252 /* iOS 7.0 / watchOS 2.0 / tvOS 9.0 and later */
253 usbmuxd_log(LL_INFO, "%s: Found %s %s device %s", __func__, deviceclass_str, version_str, _dev->udid);
254
255 lockdownd_set_untrusted_host_buid(lockdown);
256
257 /* if not paired, trigger the trust dialog to make sure it appears */
258 if (!is_device_paired) {
259 if (lockdownd_pair(lockdown, NULL) == LOCKDOWN_E_SUCCESS) {
260 /* if device is still showing the setup screen it will pair even without trust dialog */
261 usbmuxd_log(LL_INFO, "%s: Pair success for device %s", __func__, _dev->udid);
262 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
263 client_device_add(info);
264 goto leave;
265 }
266 }
267
268 lockdownd_service_descriptor_t service = NULL;
269 lerr = lockdownd_start_service(lockdown, "com.apple.mobile.insecure_notification_proxy", &service);
270 if (lerr != LOCKDOWN_E_SUCCESS) {
271 /* even though we failed, simple mode should still work, so only warn of an error */
272 usbmuxd_log(LL_INFO, "%s: ERROR: Could not start insecure_notification_proxy on %s, lockdown error %d", __func__, _dev->udid, lerr);
273 client_device_add(info);
274 goto leave;
275 }
276
277 np_client_t np = NULL;
278 np_client_new(dev, service, &np);
279
280 lockdownd_service_descriptor_free(service);
281 service = NULL;
282
283 lockdownd_client_free(lockdown);
284 lockdown = NULL;
285
286 struct cb_data cbdata;
287 cbdata.dev = dev;
288 cbdata.np = np;
289 cbdata.is_device_connected = 1;
290 cbdata.is_finished = 0;
291
292 np_set_notify_callback(np, np_callback, (void*)&cbdata);
293 device_set_preflight_cb_data(info->id, (void*)&cbdata);
294
295 const char* spec[] = {
296 "com.apple.mobile.lockdown.request_pair",
297 "com.apple.mobile.lockdown.request_host_buid",
298 NULL
299 };
300 np_observe_notifications(np, spec);
301
302 /* TODO send notification to user's desktop */
303
304 usbmuxd_log(LL_INFO, "%s: Waiting for user to trust this computer on device %s", __func__, _dev->udid);
305
306 /* make device visible anyways */
307 client_device_add(info);
308
309 while (cbdata.np && cbdata.is_device_connected && !cbdata.is_finished) {
310 sleep(1);
311 }
312 device_set_preflight_cb_data(info->id, NULL);
313
314 usbmuxd_log(LL_INFO, "%s: Finished waiting for notification from device %s, is_device_connected %d", __func__, _dev->udid, cbdata.is_device_connected);
315
316 if (cbdata.np) {
317 np_client_free(cbdata.np);
318 }
319 } else {
320 /* iOS 6.x and earlier */
321 lerr = lockdownd_pair(lockdown, NULL);
322 if (lerr != LOCKDOWN_E_SUCCESS) {
323 if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) {
324 usbmuxd_log(LL_INFO, "%s: Device %s is locked with a passcode. Cannot pair.", __func__, _dev->udid);
325 /* TODO send notification to user's desktop */
326 } else {
327 usbmuxd_log(LL_ERROR, "%s: ERROR: Pair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr);
328 }
329
330 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
331
332 /* make device visible anyways */
333 client_device_add(info);
334
335 goto leave;
336 }
337
338 host_id = NULL;
339 config_device_record_get_host_id(dev->udid, &host_id);
340 lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL);
341 free(host_id);
342 if (lerr != LOCKDOWN_E_SUCCESS) {
343 usbmuxd_log(LL_ERROR, "%s: ERROR StartSession failed on device %s, lockdown error %d", __func__, _dev->udid, lerr);
344 goto leave;
345 }
346
347 lerr = lockdownd_validate_pair(lockdown, NULL);
348 if (lerr != LOCKDOWN_E_SUCCESS) {
349 usbmuxd_log(LL_ERROR, "%s: ERROR: ValidatePair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr);
350 goto leave;
351 }
352
353 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
354
355 /* emit device added event and thus make device visible to clients */
356 client_device_add(info);
357 }
358
359leave:
360 free(deviceclass_str);
361 free(version_str);
362 if (lockdown)
363 lockdownd_client_free(lockdown);
364 if (dev)
365 idevice_free(dev);
366
367 free((char*)info->serial);
368 free(info);
369
370 return NULL;
371}
372#else
373void preflight_device_remove_cb(void *data)
374{
375}
376#endif
377
378void preflight_worker_device_add(struct device_info* info)
379{
380 if (info->pid == PID_APPLE_T2_COPROCESSOR || no_preflight == 1) {
381 client_device_add(info);
382 return;
383 }
384
385#ifdef HAVE_LIBIMOBILEDEVICE
386 struct device_info *infocopy = (struct device_info*)malloc(sizeof(struct device_info));
387
388 memcpy(infocopy, info, sizeof(struct device_info));
389 if (info->serial) {
390 infocopy->serial = strdup(info->serial);
391 }
392
393 THREAD_T th;
394 int perr = thread_new(&th, preflight_worker_handle_device_add, infocopy);
395 if (perr != 0) {
396 free((char*)infocopy->serial);
397 free(infocopy);
398 usbmuxd_log(LL_ERROR, "ERROR: failed to start preflight worker thread for device %s: %s (%d). Invoking client_device_add() directly but things might not work as expected.", info->serial, strerror(perr), perr);
399 client_device_add(info);
400 } else {
401 thread_detach(th);
402 }
403#else
404 client_device_add(info);
405#endif
406}