From f631e8e055dfcdae440631902ed8a38eb5109cb8 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Thu, 19 Sep 2013 07:45:02 +0200 Subject: added preflight worker implementation to handle initial device pairing --- configure.ac | 30 ++++++- src/Makefile.am | 5 +- src/client.c | 1 + src/device.c | 19 +++- src/device.h | 2 + src/main.c | 20 ++++- src/preflight.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/preflight.h | 28 ++++++ 8 files changed, 365 insertions(+), 11 deletions(-) create mode 100644 src/preflight.c create mode 100644 src/preflight.h diff --git a/configure.ac b/configure.ac index a874692..b7aa54b 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,8 @@ AC_PROG_LIBTOOL # Checks for libraries. PKG_CHECK_MODULES(libusb, libusb-1.0 >= 1.0.3) PKG_CHECK_MODULES(libplist, libplist >= 1.9, have_plist=yes, have_plist=no) +PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= 1.1.6, have_limd=yes, have_limd=no) +AC_CHECK_LIB(pthread, [pthread_create, pthread_mutex_lock], [AC_SUBST(libpthread_LIBS,[-lpthread])], [AC_MSG_ERROR([libpthread is required to build usbmuxd])]) AC_ARG_WITH([protov1], [AS_HELP_STRING([--without-protov1], @@ -40,6 +42,27 @@ else fi fi +AC_ARG_WITH([preflight], + [AS_HELP_STRING([--without-preflight], + [do not build with preflight worker support (default is yes)])], + [with_preflight=no], + [with_preflight=yes]) + +if test "x$have_limd" = "xyes"; then + if test "x$with_preflight" != "xyes"; then + have_limd=no + echo "*** Note: preflight worker support has been disabled ***" + else + AC_DEFINE(HAVE_LIBIMOBILEDEVICE, 1, [Define if you have libimobiledevice support]) + AC_SUBST(libimobiledevice_CFLAGS) + AC_SUBST(libimobiledevice_LIBS) + fi +else + if test "x$with_preflight" == "xyes"; then + AC_MSG_ERROR([preflight worker support requested but libimobiledevice could not befound]) + fi +fi + # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdint.h stdlib.h string.h]) @@ -73,7 +96,7 @@ case ${host_os} in esac AM_CONDITIONAL(WIN32, test x$win32 = xtrue) -AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter") +AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-g -Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter") AC_SUBST(GLOBAL_CFLAGS) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) @@ -88,8 +111,9 @@ echo " Configuration for $PACKAGE $VERSION: ------------------------------------------- - Install prefix: .........: $prefix - Protocol v1 support: ....: $have_plist + Install prefix: ...........: $prefix + Protocol v1 support: ......: $have_plist + Preflight worker support ..: $have_limd Now type 'make' to build $PACKAGE $VERSION, and then 'make install' for installation. diff --git a/src/Makefile.am b/src/Makefile.am index ae8fee7..883f1d8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,10 +1,11 @@ -AM_CFLAGS = $(GLOBAL_CFLAGS) -I$(top_srcdir)/src $(libplist_CFLAGS) $(libusb_CFLAGS) -AM_LDFLAGS = $(libplist_LIBS) $(libusb_LIBS) +AM_CFLAGS = $(GLOBAL_CFLAGS) -I$(top_srcdir)/src $(libplist_CFLAGS) $(libusb_CFLAGS) $(libimobildevice_CFLAGS) +AM_LDFLAGS = $(libplist_LIBS) $(libusb_LIBS) $(libimobiledevice_LIBS) $(libpthread_LIBS) sbin_PROGRAMS = usbmuxd usbmuxd_SOURCES = client.c client.h \ device.c device.h \ + preflight.c preflight.h \ log.c log.h \ usb-linux.c usb.h \ utils.c utils.h \ diff --git a/src/client.c b/src/client.c index d4a4a10..b2e3644 100644 --- a/src/client.c +++ b/src/client.c @@ -584,6 +584,7 @@ void client_process(int fd, short events) void client_device_add(struct device_info *dev) { usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial); + device_set_visible(dev->id); FOREACH(struct mux_client *client, &client_list) { if(client->state == CLIENT_LISTEN) notify_device_add(client, dev); diff --git a/src/device.c b/src/device.c index 91712be..27e25d5 100644 --- a/src/device.c +++ b/src/device.c @@ -33,6 +33,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include #include "device.h" #include "client.h" +#include "preflight.h" #include "usb.h" #include "log.h" @@ -106,6 +107,7 @@ struct mux_device struct usb_device *usbdev; int id; enum mux_dev_state state; + int visible; struct collection connections; uint16_t next_sport; unsigned char *pktbuf; @@ -461,7 +463,7 @@ static void device_version_input(struct mux_device *dev, struct version_header * info.location = usb_get_location(dev->usbdev); info.serial = usb_get_serial(dev->usbdev); info.pid = usb_get_pid(dev->usbdev); - client_device_add(&info); + preflight_worker_device_add(&info); } static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, uint32_t payload_length) @@ -642,6 +644,7 @@ int device_add(struct usb_device *usbdev) dev->id = id; dev->usbdev = usbdev; dev->state = MUXDEV_INIT; + dev->visible = 0; dev->next_sport = 1; dev->pktbuf = malloc(DEV_MRU); dev->pktlen = 0; @@ -680,11 +683,21 @@ void device_remove(struct usb_device *usbdev) usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev)); } +void device_set_visible(int device_id) +{ + FOREACH(struct mux_device *dev, &device_list) { + if(dev->id == device_id) { + dev->visible = 1; + break; + } + } ENDFOREACH +} + int device_get_count(void) { int count = 0; FOREACH(struct mux_device *dev, &device_list) { - if(dev->state == MUXDEV_ACTIVE) + if((dev->state == MUXDEV_ACTIVE) && dev->visible) count++; } ENDFOREACH return count; @@ -694,7 +707,7 @@ int device_get_list(struct device_info *p) { int count = 0; FOREACH(struct mux_device *dev, &device_list) { - if(dev->state == MUXDEV_ACTIVE) { + if((dev->state == MUXDEV_ACTIVE) && dev->visible) { p->id = dev->id; p->serial = usb_get_serial(dev->usbdev); p->location = usb_get_location(dev->usbdev); diff --git a/src/device.h b/src/device.h index ea77069..4b1a581 100644 --- a/src/device.h +++ b/src/device.h @@ -40,6 +40,8 @@ int device_start_connect(int device_id, uint16_t port, struct mux_client *client void device_client_process(int device_id, struct mux_client *client, short events); void device_abort_connect(int device_id, struct mux_client *client); +void device_set_visible(int device_id); + int device_get_count(void); int device_get_list(struct device_info *p); diff --git a/src/main.c b/src/main.c index 32c6a2b..1804c30 100644 --- a/src/main.c +++ b/src/main.c @@ -49,6 +49,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA static const char *socket_path = "/var/run/usbmuxd"; static const char *lockfile = "/var/run/usbmuxd.pid"; +static const char *userprefdir = "/var/lib/lockdown"; int should_exit; int should_discover; @@ -285,9 +286,6 @@ static int daemonize(void) close(pfd[0]); report_to_parent = 1; - // Change the file mode mask - umask(0); - // Create a new SID for the child process sid = setsid(); if (sid < 0) { @@ -531,6 +529,13 @@ int main(int argc, char *argv[]) if(listenfd < 0) goto terminate; + struct stat fst; + int userprefdir_created = 0; + if (stat(userprefdir, &fst) < 0) { + mkdir(userprefdir, 0775); + userprefdir_created = 1; + } + // drop elevated privileges if (drop_privileges && (getuid() == 0 || geteuid() == 0)) { struct passwd *pw; @@ -548,6 +553,15 @@ int main(int argc, char *argv[]) if (pw->pw_uid == 0) { usbmuxd_log(LL_INFO, "Not dropping privileges to root"); } else { + if (userprefdir_created) { + if (chown(userprefdir, pw->pw_uid, pw->pw_gid) < 0) { + usbmuxd_log(LL_WARNING, "chown(%s, %d, %d) failed", userprefdir, pw->pw_uid, pw->pw_gid); + } + if (chmod(userprefdir, 02775) < 0) { + usbmuxd_log(LL_WARNING, "chmod %s failed", userprefdir); + } + } + if ((res = initgroups(drop_user, pw->pw_gid)) < 0) { usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)"); goto terminate; diff --git a/src/preflight.c b/src/preflight.c new file mode 100644 index 0000000..0041b21 --- /dev/null +++ b/src/preflight.c @@ -0,0 +1,271 @@ +/* + usbmuxd - iPhone/iPod Touch USB multiplex server daemon + +Copyright (C) 2013 Nikias Bassen + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 or version 3. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include "preflight.h" +#include "client.h" +#include "log.h" + +#ifdef HAVE_LIBIMOBILEDEVICE +enum connection_type { + CONNECTION_USBMUXD = 1 +}; + +struct idevice_private { + char *udid; + enum connection_type conn_type; + void *conn_data; +}; + +extern void userpref_get_system_buid(char **systembuid); + +struct np_cb_data { + idevice_t dev; + np_client_t np; +}; + +static void set_untrusted_host_buid(lockdownd_client_t lockdown) +{ + char* system_buid = NULL; + userpref_get_system_buid(&system_buid); + usbmuxd_log(LL_DEBUG, "%s: Setting UntrustedHostBUID to %s", __func__, system_buid); + lockdownd_set_value(lockdown, NULL, "UntrustedHostBUID", plist_new_string(system_buid)); + free(system_buid); +} + +static void np_callback(const char* notification, void* userdata) +{ + struct np_cb_data *cbdata = (struct np_cb_data*)userdata; + idevice_t dev = cbdata->dev; + struct idevice_private *_dev = (struct idevice_private*)dev; + + lockdownd_client_t lockdown = NULL; + lockdownd_error_t lerr; + + if (strlen(notification) == 0) { + cbdata->np = NULL; + return; + } + + if (strcmp(notification, "com.apple.mobile.lockdown.request_pair") == 0) { + usbmuxd_log(LL_INFO, "%s: user trusted this computer on device %s, pairing now", __func__, _dev->udid); + lerr = lockdownd_client_new(dev, &lockdown, "usbmuxd"); + if (lerr != LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr); + return; + } + + lerr = lockdownd_pair(lockdown, NULL); + if (lerr != LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_ERROR, "%s: ERROR: Pair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr); + lockdownd_client_free(lockdown); + return; + } + lockdownd_client_free(lockdown); + // device will reconnect by itself at this point. + + } else if (strcmp(notification, "com.apple.mobile.lockdown.request_host_buid") == 0) { + lerr = lockdownd_client_new(cbdata->dev, &lockdown, "usbmuxd"); + if (lerr != LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr); + } else { + set_untrusted_host_buid(lockdown); + lockdownd_client_free(lockdown); + } + } +} + +static void* preflight_worker_handle_device_add(void* userdata) +{ + struct device_info *info = (struct device_info*)userdata; + struct idevice_private *_dev = (struct idevice_private*)malloc(sizeof(struct idevice_private)); + _dev->udid = strdup(info->serial); + _dev->conn_type = CONNECTION_USBMUXD; + _dev->conn_data = (void*)(long)info->id; + + idevice_t dev = (idevice_t)_dev; + + lockdownd_client_t lockdown; + lockdownd_error_t lerr; + + lerr = lockdownd_client_new(dev, &lockdown, "usbmuxd"); + if (lerr != LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr); + goto leave; + } + + char *type = NULL; + lerr = lockdownd_query_type(lockdown, &type); + if (!type) { + usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get lockdownd type from device %s, lockdown error %d", __func__, _dev->udid, lerr); + goto leave; + } + + if (strcmp(type, "com.apple.mobile.lockdown") != 0) { + // make restore mode devices visible + client_device_add(info); + goto leave; + } + + char *host_id = NULL; + userpref_device_record_get_host_id(dev->udid, &host_id); + lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL); + free(host_id); + if (lerr == LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_INFO, "%s: StartSession success for device %s", __func__, _dev->udid); + client_device_add(info); + goto leave; + } + + usbmuxd_log(LL_INFO, "%s: StartSession failed on device %s, lockdown error %d", __func__, _dev->udid, lerr); + if (lerr == LOCKDOWN_E_INVALID_HOST_ID) { + usbmuxd_log(LL_INFO, "%s: Device %s is not paired with this host.", __func__, _dev->udid); + } + + plist_t value = NULL; + lerr = lockdownd_get_value(lockdown, NULL, "ProductVersion", &value); + if (lerr != LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get ProductVersion from device %s, lockdown error %d", __func__, _dev->udid, lerr); + goto leave; + } + + char* version_str = NULL; + plist_get_string_val(value, &version_str); + if (!version_str) { + usbmuxd_log(LL_ERROR, "%s: Could not get ProductVersion string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data); + goto leave; + } + + int version_major = strtol(version_str, NULL, 10); + if (version_major >= 7) { + // ============== iOS 7.0 and beyond ============= + usbmuxd_log(LL_INFO, "%s: Found ProductVersion %s device %s", __func__, version_str, _dev->udid); + + set_untrusted_host_buid(lockdown); + + lockdownd_service_descriptor_t service = NULL; + lerr = lockdownd_start_service(lockdown, "com.apple.mobile.insecure_notification_proxy", &service); + if (lerr != LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_ERROR, "%s: ERROR: Could not start insecure_notification_proxy on %s, lockdown error %d", __func__, _dev->udid, lerr); + goto leave; + } + + np_client_t np = NULL; + np_client_new(dev, service, &np); + + lockdownd_service_descriptor_free(service); + service = NULL; + + lockdownd_client_free(lockdown); + lockdown = NULL; + + struct np_cb_data cbdata; + cbdata.dev = dev; + cbdata.np = np; + + np_set_notify_callback(np, np_callback, (void*)&cbdata); + + const char* spec[] = { + "com.apple.mobile.lockdown.request_pair", + "com.apple.mobile.lockdown.request_host_buid", + NULL + }; + np_observe_notifications(np, spec); + + usbmuxd_log(LL_INFO, "%s: Waiting for user to trust this computer on device %s", __func__, _dev->udid); + // TODO send notification to user's desktop + while (cbdata.np) { + sleep(1); + } + + if (cbdata.np) { + np_client_free(cbdata.np); + } + } else { + // ============== iOS 6.x and below ============== + lerr = lockdownd_pair(lockdown, NULL); + if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) { + usbmuxd_log(LL_INFO, "%s: Device %s is locked with a passcode. Cannot pair.", __func__, _dev->udid); + // TODO send notification to user's desktop + goto leave; + } else if (lerr != LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_ERROR, "%s: ERROR: Pair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr); + goto leave; + } + + host_id = NULL; + userpref_device_record_get_host_id(dev->udid, &host_id); + lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL); + free(host_id); + if (lerr != LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_ERROR, "%s: ERROR StartSession failed on device %s, lockdown error %d", __func__, _dev->udid, lerr); + goto leave; + } + + lerr = lockdownd_validate_pair(lockdown, NULL); + if (lerr != LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_ERROR, "%s: ERROR: ValidatePair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr); + goto leave; + } + + // make device visible + client_device_add(info); + } + +leave: + if (lockdown) + lockdownd_client_free(lockdown); + if (dev) + idevice_free(dev); + + free(info); + + return NULL; +} +#endif + +void preflight_worker_device_add(struct device_info* info) +{ +#ifdef HAVE_LIBIMOBILEDEVICE + struct device_info *infocopy = (struct device_info*)malloc(sizeof(struct device_info)); + + memcpy(infocopy, info, sizeof(struct device_info)); + + pthread_t th; + pthread_create(&th, NULL, preflight_worker_handle_device_add, infocopy); +#else + client_device_add(info); +#endif +} diff --git a/src/preflight.h b/src/preflight.h new file mode 100644 index 0000000..dce3356 --- /dev/null +++ b/src/preflight.h @@ -0,0 +1,28 @@ +/* + usbmuxd - iPhone/iPod Touch USB multiplex server daemon + +Copyright (C) 2013 Nikias Bassen + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 or version 3. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef __PREFLIGHT_H__ +#define __PREFLIGHT_H__ + +#include "device.h" + +void preflight_worker_device_add(struct device_info* info); + +#endif -- cgit v1.1-32-gdbae