/** * Wait for an inbound connection on the usbmuxd socket * and create a new mux_client instance for it, and store * the client in the client list. * * @param listenfd the socket fd to accept() on. * @return The connection fd for the client, or < 0 for error * in which case errno will be set. */ int client_accept(int listenfd) { struct sockaddr_un addr; int cfd; socklen_t len = sizeof(struct sockaddr_un); cfd = accept(listenfd, (struct sockaddr *)&addr, &len); if (cfd < 0) { usbmuxd_log(LL_ERROR, "accept() failed (%s)", strerror(errno)); return cfd; } int flags = fcntl(cfd, F_GETFL, 0); if (flags < 0) { usbmuxd_log(LL_ERROR, "ERROR: Could not get socket flags!"); } else { if (fcntl(cfd, F_SETFL, flags | O_NONBLOCK) < 0) { usbmuxd_log(LL_ERROR, "ERROR: Could not set socket to non-blocking mode"); } } struct mux_client *client; client = malloc(sizeof(struct mux_client)); memset(client, 0, sizeof(struct mux_client)); client->fd = cfd; client->ob_buf = malloc(REPLY_BUF_SIZE); client->ob_size = 0; client->ob_capacity = REPLY_BUF_SIZE; client->ib_buf = malloc(CMD_BUF_SIZE); client->ib_size = 0; client->ib_capacity = CMD_BUF_SIZE; client->state = CLIENT_COMMAND; client->events = POLLIN; pthread_mutex_lock(&client_list_mutex); collection_add(&client_list, client); pthread_mutex_unlock(&client_list_mutex); #ifdef SO_PEERCRED if (log_level >= LL_INFO) { struct ucred cr; len = sizeof(struct ucred); getsockopt(cfd, SOL_SOCKET, SO_PEERCRED, &cr, &len); if (getpid() == cr.pid) { usbmuxd_log(LL_INFO, "New client on fd %d (self)", client->fd); } else { usbmuxd_log(LL_INFO, "New client on fd %d (pid %d)", client->fd, cr.pid); } } #else usbmuxd_log(LL_INFO, "New client on fd %d", client->fd); #endif return client->fd; }
static void process_recv(struct mux_client *client) { int res; int did_read = 0; if(client->ib_size < sizeof(struct usbmuxd_header)) { res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct usbmuxd_header) - client->ib_size, 0); if(res <= 0) { if(res < 0) usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno)); else usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd); client_close(client); return; } client->ib_size += res; if(client->ib_size < sizeof(struct usbmuxd_header)) return; did_read = 1; } struct usbmuxd_header *hdr = (void*)client->ib_buf; if(hdr->length > client->ib_capacity) { usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length); client_close(client); return; } if(hdr->length < sizeof(struct usbmuxd_header)) { usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length); client_close(client); return; } if(client->ib_size < hdr->length) { if(did_read) return; //maybe we would block, so defer to next loop res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0); if(res < 0) { usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno)); client_close(client); return; } else if(res == 0) { usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd); client_close(client); return; } client->ib_size += res; if(client->ib_size < hdr->length) return; } client_command(client, hdr); client->ib_size = 0; }
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_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); int perr = pthread_create(&th, &attr, preflight_worker_handle_device_add, infocopy); if (perr != 0) { free(infocopy); 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); client_device_add(info); } #else client_device_add(info); #endif }
static int create_socket(void) { struct sockaddr_un bind_addr; int listenfd; if(unlink(socket_path) == -1 && errno != ENOENT) { usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno)); return -1; } listenfd = socket(AF_UNIX, SOCK_STREAM, 0); if (listenfd == -1) { usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno)); return -1; } int flags = fcntl(listenfd, F_GETFL, 0); if (flags < 0) { usbmuxd_log(LL_FATAL, "ERROR: Could not get flags for socket"); } else { if (fcntl(listenfd, F_SETFL, flags | O_NONBLOCK) < 0) { usbmuxd_log(LL_FATAL, "ERROR: Could not set socket to non-blocking"); } } bzero(&bind_addr, sizeof(bind_addr)); bind_addr.sun_family = AF_UNIX; strcpy(bind_addr.sun_path, socket_path); if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) { usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno)); return -1; } // Start listening if (listen(listenfd, 5) != 0) { usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno)); return -1; } chmod(socket_path, 0666); return listenfd; }
int main(int argc, char *argv[]) { int listenfd; int res = 0; int lfd; struct flock lock; char pids[10]; parse_opts(argc, argv); argc -= optind; argv += optind; if (!foreground) { verbose += LL_WARNING; log_enable_syslog(); } else { verbose += LL_NOTICE; } /* set log level to specified verbosity */ log_level = verbose; usbmuxd_log(LL_NOTICE, "usbmuxd v%s starting up", PACKAGE_VERSION); should_exit = 0; should_discover = 0; set_signal_handlers(); signal(SIGPIPE, SIG_IGN); res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644); if(res == -1) { usbmuxd_log(LL_FATAL, "Could not open lockfile"); goto terminate; } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; lock.l_pid = 0; fcntl(lfd, F_GETLK, &lock); close(lfd); if (lock.l_type != F_UNLCK) { if (opt_exit) { if (lock.l_pid && !kill(lock.l_pid, 0)) { usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid); res = 0; if (kill(lock.l_pid, exit_signal) < 0) { usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid); res = -1; } goto terminate; } else { usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!"); res = -1; goto terminate; } } else { if (!opt_disable_hotplug) { usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid); res = -1; } else { usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). Telling it to check for devices.", lock.l_pid); if (lock.l_pid && !kill(lock.l_pid, 0)) { usbmuxd_log(LL_NOTICE, "Sending signal SIGUSR2 to instance with pid %d", lock.l_pid); res = 0; if (kill(lock.l_pid, SIGUSR2) < 0) { usbmuxd_log(LL_FATAL, "Could not deliver SIGUSR2 to pid %d", lock.l_pid); res = -1; } } else { usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!"); res = -1; } } goto terminate; } } unlink(lockfile); if (opt_exit) { usbmuxd_log(LL_NOTICE, "No running instance found, none killed. Exiting."); goto terminate; } if (!foreground) { if ((res = daemonize()) < 0) { fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n"); usbmuxd_log(LL_FATAL, "Could not daemonize!"); goto terminate; } } // now open the lockfile and place the lock res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644); if(res < 0) { usbmuxd_log(LL_FATAL, "Could not open lockfile"); goto terminate; } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) { usbmuxd_log(LL_FATAL, "Lockfile locking failed!"); goto terminate; } sprintf(pids, "%d", getpid()); if ((size_t)(res = write(lfd, pids, strlen(pids))) != strlen(pids)) { usbmuxd_log(LL_FATAL, "Could not write pidfile!"); if(res >= 0) res = -2; goto terminate; } // set number of file descriptors to higher value struct rlimit rlim; getrlimit(RLIMIT_NOFILE, &rlim); rlim.rlim_max = 65536; setrlimit(RLIMIT_NOFILE, (const struct rlimit*)&rlim); usbmuxd_log(LL_INFO, "Creating socket"); res = listenfd = create_socket(); if(listenfd < 0) goto terminate; #ifdef HAVE_LIBIMOBILEDEVICE const char* userprefdir = config_get_config_dir(); struct stat fst; memset(&fst, '\0', sizeof(struct stat)); if (stat(userprefdir, &fst) < 0) { if (mkdir(userprefdir, 0775) < 0) { usbmuxd_log(LL_FATAL, "Failed to create required directory '%s': %s", userprefdir, strerror(errno)); res = -1; goto terminate; } if (stat(userprefdir, &fst) < 0) { usbmuxd_log(LL_FATAL, "stat() failed after creating directory '%s': %s", userprefdir, strerror(errno)); res = -1; goto terminate; } } // make sure permission bits are set correctly if (fst.st_mode != 02775) { if (chmod(userprefdir, 02775) < 0) { usbmuxd_log(LL_WARNING, "chmod(%s, 02775) failed: %s", userprefdir, strerror(errno)); } } #endif // drop elevated privileges if (drop_privileges && (getuid() == 0 || geteuid() == 0)) { struct passwd *pw; if (!drop_user) { usbmuxd_log(LL_FATAL, "No user to drop privileges to?"); res = -1; goto terminate; } pw = getpwnam(drop_user); if (!pw) { usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user); res = -1; goto terminate; } if (pw->pw_uid == 0) { usbmuxd_log(LL_INFO, "Not dropping privileges to root"); } else { #ifdef HAVE_LIBIMOBILEDEVICE /* make sure the non-privileged user has proper access to the config directory */ if ((fst.st_uid != pw->pw_uid) || (fst.st_gid != pw->pw_gid)) { if (chown(userprefdir, pw->pw_uid, pw->pw_gid) < 0) { usbmuxd_log(LL_WARNING, "chown(%s, %d, %d) failed: %s", userprefdir, pw->pw_uid, pw->pw_gid, strerror(errno)); } } #endif if ((res = initgroups(drop_user, pw->pw_gid)) < 0) { usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)"); goto terminate; } if ((res = setgid(pw->pw_gid)) < 0) { usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid); goto terminate; } if ((res = setuid(pw->pw_uid)) < 0) { usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid); goto terminate; } // security check if (setuid(0) != -1) { usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!"); res = -1; goto terminate; } if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) { usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!"); res = -1; goto terminate; } usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user); } } client_init(); device_init(); usbmuxd_log(LL_INFO, "Initializing USB"); if((res = usb_init()) < 0) goto terminate; usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s"); usbmuxd_log(LL_NOTICE, "Initialization complete"); if (report_to_parent) if((res = notify_parent(0)) < 0) goto terminate; if(opt_disable_hotplug) { usbmuxd_log(LL_NOTICE, "Automatic device discovery on hotplug disabled."); usb_autodiscover(0); // discovery to be triggered by new instance } if (opt_enable_exit) { usbmuxd_log(LL_NOTICE, "Enabled exit on SIGUSR1 if no devices are attached. Start a new instance with \"--exit\" to trigger."); } res = main_loop(listenfd); if(res < 0) usbmuxd_log(LL_FATAL, "main_loop failed"); usbmuxd_log(LL_NOTICE, "usbmuxd shutting down"); device_kill_connections(); usb_shutdown(); device_shutdown(); client_shutdown(); usbmuxd_log(LL_NOTICE, "Shutdown complete"); terminate: log_disable_syslog(); if (res < 0) res = -res; else res = 0; if (report_to_parent) notify_parent(res); return res; }
/** * make this program run detached from the current console */ static int daemonize(void) { pid_t pid; pid_t sid; int pfd[2]; int res; // already a daemon if (getppid() == 1) return 0; if((res = pipe(pfd)) < 0) { usbmuxd_log(LL_FATAL, "pipe() failed."); return res; } pid = fork(); if (pid < 0) { usbmuxd_log(LL_FATAL, "fork() failed."); return pid; } if (pid > 0) { // exit parent process int status; close(pfd[1]); if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) { fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n"); exit(1); } if(status != 0) fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status); exit(status); } // At this point we are executing as the child process // but we need to do one more fork daemon_pipe = pfd[1]; close(pfd[0]); report_to_parent = 1; // Create a new SID for the child process sid = setsid(); if (sid < 0) { usbmuxd_log(LL_FATAL, "setsid() failed."); return -1; } pid = fork(); if (pid < 0) { usbmuxd_log(LL_FATAL, "fork() failed (second)."); return pid; } if (pid > 0) { // exit parent process close(daemon_pipe); exit(0); } // Change the current working directory. if ((chdir("/")) < 0) { usbmuxd_log(LL_FATAL, "chdir() failed"); return -2; } // Redirect standard files to /dev/null if (!freopen("/dev/null", "r", stdin)) { usbmuxd_log(LL_FATAL, "Redirection of stdin failed."); return -3; } if (!freopen("/dev/null", "w", stdout)) { usbmuxd_log(LL_FATAL, "Redirection of stdout failed."); return -3; } return 0; }
static int main_loop(int listenfd) { int to, cnt, i, dto; struct fdlist pollfds; struct timespec tspec; sigset_t empty_sigset; sigemptyset(&empty_sigset); // unmask all signals fdlist_create(&pollfds); while(!should_exit) { usbmuxd_log(LL_FLOOD, "main_loop iteration"); to = usb_get_timeout(); usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to); dto = device_get_timeout(); usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", dto); if(dto < to) to = dto; fdlist_reset(&pollfds); fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN); usb_get_fds(&pollfds); client_get_fds(&pollfds); usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count); tspec.tv_sec = to / 1000; tspec.tv_nsec = (to % 1000) * 1000000; cnt = ppoll(pollfds.fds, pollfds.count, &tspec, &empty_sigset); usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt); if(cnt == -1) { if(errno == EINTR) { if(should_exit) { usbmuxd_log(LL_INFO, "Event processing interrupted"); break; } if(should_discover) { should_discover = 0; usbmuxd_log(LL_INFO, "Device discovery triggered"); usb_discover(); } } } else if(cnt == 0) { if(usb_process() < 0) { usbmuxd_log(LL_FATAL, "usb_process() failed"); fdlist_free(&pollfds); return -1; } device_check_timeouts(); } else { int done_usb = 0; for(i=0; i<pollfds.count; i++) { if(pollfds.fds[i].revents) { if(!done_usb && pollfds.owners[i] == FD_USB) { if(usb_process() < 0) { usbmuxd_log(LL_FATAL, "usb_process() failed"); fdlist_free(&pollfds); return -1; } done_usb = 1; } if(pollfds.owners[i] == FD_LISTEN) { if(client_accept(listenfd) < 0) { usbmuxd_log(LL_FATAL, "client_accept() failed"); fdlist_free(&pollfds); return -1; } } if(pollfds.owners[i] == FD_CLIENT) { client_process(pollfds.fds[i].fd, pollfds.fds[i].revents); } } } } } fdlist_free(&pollfds); return 0; }
void client_init(void) { usbmuxd_log(LL_DEBUG, "client_init"); collection_init(&client_list); pthread_mutex_init(&client_list_mutex, NULL); }
static int client_command(struct mux_client *client, struct usbmuxd_header *hdr) { int res; usbmuxd_log(LL_DEBUG, "Client command in fd %d len %d ver %d msg %d tag %d", client->fd, hdr->length, hdr->version, hdr->message, hdr->tag); if(client->state != CLIENT_COMMAND) { usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state", client->fd); if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0) return -1; client_close(client); return -1; } if((hdr->version != 0) && (hdr->version != 1)) { usbmuxd_log(LL_INFO, "Client %d version mismatch: expected 0 or 1, got %d", client->fd, hdr->version); send_result(client, hdr->tag, RESULT_BADVERSION); return 0; } struct usbmuxd_connect_request *ch; char *payload; uint32_t payload_size; switch(hdr->message) { case MESSAGE_PLIST: client->proto_version = 1; payload = (char*)(hdr) + sizeof(struct usbmuxd_header); payload_size = hdr->length - sizeof(struct usbmuxd_header); plist_t dict = NULL; plist_from_xml(payload, payload_size, &dict); if (!dict) { usbmuxd_log(LL_ERROR, "Could not parse plist from payload!"); return -1; } else { char *message = NULL; plist_t node = plist_dict_get_item(dict, "MessageType"); plist_get_string_val(node, &message); if (!message) { usbmuxd_log(LL_ERROR, "Could not extract MessageType from plist!"); plist_free(dict); return -1; } if (!strcmp(message, "Listen")) { free(message); plist_free(dict); if (send_result(client, hdr->tag, 0) < 0) return -1; usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd); return start_listen(client); } else if (!strcmp(message, "Connect")) { uint64_t val; uint16_t portnum = 0; uint32_t device_id = 0; free(message); // get device id node = plist_dict_get_item(dict, "DeviceID"); if (!node) { usbmuxd_log(LL_ERROR, "Received connect request without device_id!"); plist_free(dict); if (send_result(client, hdr->tag, RESULT_BADDEV) < 0) return -1; return 0; } val = 0; plist_get_uint_val(node, &val); device_id = (uint32_t)val; // get port number node = plist_dict_get_item(dict, "PortNumber"); if (!node) { usbmuxd_log(LL_ERROR, "Received connect request without port number!"); plist_free(dict); if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0) return -1; return 0; } val = 0; plist_get_uint_val(node, &val); portnum = (uint16_t)val; plist_free(dict); usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, device_id, ntohs(portnum)); res = device_start_connect(device_id, ntohs(portnum), client); if(res < 0) { if (send_result(client, hdr->tag, -res) < 0) return -1; } else { client->connect_tag = hdr->tag; client->connect_device = device_id; client->state = CLIENT_CONNECTING1; } return 0; } else if (!strcmp(message, "ListDevices")) { free(message); plist_free(dict); if (send_device_list(client, hdr->tag) < 0) return -1; return 0; } else if (!strcmp(message, "ReadBUID")) { free(message); plist_free(dict); if (send_system_buid(client, hdr->tag) < 0) return -1; return 0; } else if (!strcmp(message, "ReadPairRecord")) { free(message); char* record_id = plist_dict_get_string_val(dict, "PairRecordID"); plist_free(dict); res = send_pair_record(client, hdr->tag, record_id); if (record_id) free(record_id); if (res < 0) return -1; return 0; } else if (!strcmp(message, "SavePairRecord")) { uint32_t rval = RESULT_OK; free(message); char* record_id = plist_dict_get_string_val(dict, "PairRecordID"); char* record_data = NULL; uint64_t record_size = 0; plist_t rdata = plist_dict_get_item(dict, "PairRecordData"); if (rdata && plist_get_node_type(rdata) == PLIST_DATA) { plist_get_data_val(rdata, &record_data, &record_size); } plist_free(dict); if (record_id && record_data) { res = config_set_device_record(record_id, record_data, record_size); if (res < 0) { rval = -res; } free(record_id); } else { rval = EINVAL; } if (send_result(client, hdr->tag, rval) < 0) return -1; return 0; } else if (!strcmp(message, "DeletePairRecord")) { uint32_t rval = RESULT_OK; free(message); char* record_id = plist_dict_get_string_val(dict, "PairRecordID"); plist_free(dict); if (record_id) { res = config_remove_device_record(record_id); if (res < 0) { rval = -res; } free(record_id); } else { rval = EINVAL; } if (send_result(client, hdr->tag, rval) < 0) return -1; return 0; } else { usbmuxd_log(LL_ERROR, "Unexpected command '%s' received!", message); free(message); plist_free(dict); if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0) return -1; return 0; } } // should not be reached?! return -1; case MESSAGE_LISTEN: if(send_result(client, hdr->tag, 0) < 0) return -1; usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd); return start_listen(client); case MESSAGE_CONNECT: ch = (void*)hdr; usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port)); res = device_start_connect(ch->device_id, ntohs(ch->port), client); if(res < 0) { if(send_result(client, hdr->tag, -res) < 0) return -1; } else { client->connect_tag = hdr->tag; client->connect_device = ch->device_id; client->state = CLIENT_CONNECTING1; } return 0; default: usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message); if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0) return -1; return 0; } return -1; }
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 = NULL; lockdownd_error_t lerr; plist_t value = NULL; char* version_str = NULL; usbmuxd_log(LL_INFO, "%s: Starting preflight on device %s...", __func__, _dev->udid); retry: 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 free(type); usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid); client_device_add(info); goto leave; } free(type); int is_device_paired = 0; char *host_id = NULL; if (config_has_device_record(dev->udid)) { config_device_record_get_host_id(dev->udid, &host_id); lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL); if (host_id) free(host_id); if (lerr == LOCKDOWN_E_SUCCESS) { usbmuxd_log(LL_INFO, "%s: StartSession success for device %s", __func__, _dev->udid); usbmuxd_log(LL_INFO, "%s: Finished preflight on 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); } else { lerr = LOCKDOWN_E_INVALID_HOST_ID; } switch (lerr) { case LOCKDOWN_E_INVALID_HOST_ID: usbmuxd_log(LL_INFO, "%s: Device %s is not paired with this host.", __func__, _dev->udid); break; case LOCKDOWN_E_SSL_ERROR: usbmuxd_log(LL_ERROR, "%s: The stored pair record for device %s is invalid. Removing.", __func__, _dev->udid); if (config_remove_device_record(_dev->udid) == 0) { lockdownd_client_free(lockdown); lockdown = NULL; goto retry; } else { usbmuxd_log(LL_ERROR, "%s: Could not remove pair record for device %s", __func__, _dev->udid); } break; default: is_device_paired = 1; break; } 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; } if (value && plist_get_node_type(value) == PLIST_STRING) { 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 later */ usbmuxd_log(LL_INFO, "%s: Found ProductVersion %s device %s", __func__, version_str, _dev->udid); lockdownd_set_untrusted_host_buid(lockdown); /* if not paired, trigger the trust dialog to make sure it appears */ if (!is_device_paired) { if (lockdownd_pair(lockdown, NULL) == LOCKDOWN_E_SUCCESS) { /* if device is still showing the setup screen it will pair even without trust dialog */ usbmuxd_log(LL_INFO, "%s: Pair success for device %s", __func__, _dev->udid); usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid); client_device_add(info); goto leave; } } 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 cb_data cbdata; cbdata.dev = dev; cbdata.np = np; cbdata.is_device_connected = 1; np_set_notify_callback(np, np_callback, (void*)&cbdata); device_set_preflight_cb_data(info->id, (void*)&cbdata); const char* spec[] = { "com.apple.mobile.lockdown.request_pair", "com.apple.mobile.lockdown.request_host_buid", NULL }; np_observe_notifications(np, spec); /* TODO send notification to user's desktop */ usbmuxd_log(LL_INFO, "%s: Waiting for user to trust this computer on device %s", __func__, _dev->udid); /* make device visible anyways */ client_device_add(info); while (cbdata.np && cbdata.is_device_connected == 1) { sleep(1); } device_set_preflight_cb_data(info->id, NULL); usbmuxd_log(LL_INFO, "%s: Finished waiting for notification from device %s, is_device_connected %d", __func__, _dev->udid, cbdata.is_device_connected); if (cbdata.np) { np_client_free(cbdata.np); } } else { /* iOS 6.x and earlier */ lerr = lockdownd_pair(lockdown, NULL); if (lerr != LOCKDOWN_E_SUCCESS) { 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 */ } else { usbmuxd_log(LL_ERROR, "%s: ERROR: Pair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr); } usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid); /* make device visible anyways */ client_device_add(info); goto leave; } host_id = NULL; config_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; } usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid); /* emit device added event and thus make device visible to clients */ client_device_add(info); } leave: if (value) plist_free(value); if (version_str) free(version_str); if (lockdown) lockdownd_client_free(lockdown); if (dev) idevice_free(dev); free(info); return NULL; }