From c168c3c7746e51fef4ec748a3982553833bb0c4e Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Fri, 13 Dec 2013 04:35:44 +0100 Subject: add support for reading and writing config and pair record files --- configure.ac | 2 +- src/Makefile.am | 1 + src/conf.c | 499 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/conf.h | 40 +++++ src/main.c | 7 +- src/preflight.c | 11 +- src/utils.c | 144 ++++++++++++++++ src/utils.h | 17 ++ 8 files changed, 709 insertions(+), 12 deletions(-) create mode 100644 src/conf.c create mode 100644 src/conf.h diff --git a/configure.ac b/configure.ac index 9e113d7..6737f6f 100644 --- a/configure.ac +++ b/configure.ac @@ -57,7 +57,7 @@ AC_TYPE_UINT8_T # Checks for library functions. AC_FUNC_MALLOC AC_FUNC_REALLOC -AC_CHECK_FUNCS([strcasecmp strdup strerror strndup]) +AC_CHECK_FUNCS([strcasecmp strdup strerror strndup stpcpy]) # Check for operating system AC_MSG_CHECKING([whether to enable WIN32 build settings]) diff --git a/src/Makefile.am b/src/Makefile.am index 883f1d8..38858c4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,6 +9,7 @@ usbmuxd_SOURCES = client.c client.h \ log.c log.h \ usb-linux.c usb.h \ utils.c utils.h \ + conf.c conf.h \ main.c usbmuxd_CFLAGS = $(AM_CFLAGS) usbmuxd_LDFLAGS = $(AM_LDFLAGS) diff --git a/src/conf.c b/src/conf.c new file mode 100644 index 0000000..780a7c4 --- /dev/null +++ b/src/conf.c @@ -0,0 +1,499 @@ +/* + 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 +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include +#include +#include +#include + +#ifdef WIN32 +#include +#endif + +#include "conf.h" +#include "utils.h" +#include "log.h" + +#ifdef WIN32 +#define DIR_SEP '\\' +#define DIR_SEP_S "\\" +#else +#define DIR_SEP '/' +#define DIR_SEP_S "/" +#endif + +#define CONFIG_SYSTEM_BUID_KEY "SystemBUID" +#define CONFIG_HOST_ID_KEY "HostID" + +#define CONFIG_EXT ".plist" + +#ifdef WIN32 +#define CONFIG_DIR "Apple"DIR_SEP_S"Lockdown" +#else +#define CONFIG_DIR "lockdown" +#endif + +#define CONFIG_FILE "SystemConfiguration"CONFIG_EXT + +static char *__config_dir = NULL; + +#ifdef WIN32 +static char *config_utf16_to_utf8(wchar_t *unistr, long len, long *items_read, long *items_written) +{ + if (!unistr || (len <= 0)) return NULL; + char *outbuf = (char*)malloc(3*(len+1)); + int p = 0; + int i = 0; + + wchar_t wc; + + while (i < len) { + wc = unistr[i++]; + if (wc >= 0x800) { + outbuf[p++] = (char)(0xE0 + ((wc >> 12) & 0xF)); + outbuf[p++] = (char)(0x80 + ((wc >> 6) & 0x3F)); + outbuf[p++] = (char)(0x80 + (wc & 0x3F)); + } else if (wc >= 0x80) { + outbuf[p++] = (char)(0xC0 + ((wc >> 6) & 0x1F)); + outbuf[p++] = (char)(0x80 + (wc & 0x3F)); + } else { + outbuf[p++] = (char)(wc & 0x7F); + } + } + if (items_read) { + *items_read = i; + } + if (items_written) { + *items_written = p; + } + outbuf[p] = 0; + + return outbuf; +} +#endif + +const char *config_get_config_dir() +{ + char *base_config_dir = NULL; + + if (__config_dir) + return __config_dir; + +#ifdef WIN32 + wchar_t path[MAX_PATH+1]; + HRESULT hr; + LPITEMIDLIST pidl = NULL; + BOOL b = FALSE; + + hr = SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_APPDATA, &pidl); + if (hr == S_OK) { + b = SHGetPathFromIDListW (pidl, path); + if (b) { + base_config_dir = config_utf16_to_utf8 (path, wcslen(path), NULL, NULL); + CoTaskMemFree (pidl); + } + } +#else +#ifdef __APPLE__ + base_config_dir = strdup("/var/db"); +#else + base_config_dir = strdup("/var/lib"); +#endif +#endif + __config_dir = string_concat(base_config_dir, DIR_SEP_S, CONFIG_DIR, NULL); + + if (__config_dir) { + int i = strlen(__config_dir)-1; + while ((i > 0) && (__config_dir[i] == DIR_SEP)) { + __config_dir[i--] = '\0'; + } + } + + free(base_config_dir); + + usbmuxd_log(LL_DEBUG, "initialized config_dir to %s", __config_dir); + + return __config_dir; +} + +static int __mkdir(const char *dir, int mode) +{ +#ifdef WIN32 + return mkdir(dir); +#else + return mkdir(dir, mode); +#endif +} + +static int mkdir_with_parents(const char *dir, int mode) +{ + if (!dir) return -1; + if (__mkdir(dir, mode) == 0) { + return 0; + } else { + if (errno == EEXIST) return 0; + } + int res; + char *parent = strdup(dir); + char* parentdir = dirname(parent); + if (parentdir) { + res = mkdir_with_parents(parentdir, mode); + } else { + res = -1; + } + free(parent); + return res; +} + +/** + * Creates a freedesktop compatible configuration directory. + */ +static void config_create_config_dir(void) +{ + const char *config_path = config_get_config_dir(); + struct stat st; + if (stat(config_path, &st) != 0) { + mkdir_with_parents(config_path, 0755); + } +} + +static int get_rand(int min, int max) +{ + int retval = (rand() % (max - min)) + min; + return retval; +} + +static char *config_generate_uuid(int idx) +{ + char *uuid = (char *) malloc(sizeof(char) * 37); + const char *chars = "ABCDEF0123456789"; + srand(time(NULL) - idx); + int i = 0; + + for (i = 0; i < 36; i++) { + if (i == 8 || i == 13 || i == 18 || i == 23) { + uuid[i] = '-'; + continue; + } else { + uuid[i] = chars[get_rand(0, 16)]; + } + } + /* make it a real string */ + uuid[36] = '\0'; + return uuid; +} + +/** + * Generates a valid BUID for this system (which is actually a UUID). + * + * @return A null terminated string containing a valid BUID. + */ +static char *config_generate_system_buid() +{ + return config_generate_uuid(1); +} + +static int internal_set_value(const char *config_file, const char *key, plist_t value) +{ + if (!config_file) + return 0; + + /* read file into plist */ + plist_t config = NULL; + + plist_read_from_filename(&config, config_file); + if (!config) { + config = plist_new_dict(); + plist_dict_insert_item(config, key, value); + } else { + plist_t n = plist_dict_get_item(config, key); + if (n) { + plist_dict_remove_item(config, key); + } + plist_dict_insert_item(config, key, value); + remove(config_file); + } + + /* store in config file */ + char *value_string = NULL; + if (plist_get_node_type(value) == PLIST_STRING) { + plist_get_string_val(value, &value_string); + usbmuxd_log(LL_DEBUG, "setting key %s to %s in config_file %s", key, value_string, config_file); + if (value_string) + free(value_string); + } else { + usbmuxd_log(LL_DEBUG, "setting key %s in config_file %s", key, config_file); + } + + plist_write_to_filename(config, config_file, PLIST_FORMAT_XML); + + plist_free(config); + + return 1; +} + +static int config_set_value(const char *key, plist_t value) +{ + const char *config_path = NULL; + char *config_file = NULL; + + /* Make sure config directory exists */ + config_create_config_dir(); + + config_path = config_get_config_dir(); + config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL); + + int result = internal_set_value(config_file, key, value); + + free(config_file); + + return result; +} + +static int internal_get_value(const char* config_file, const char *key, plist_t *value) +{ + *value = NULL; + + /* now parse file to get the SystemBUID */ + plist_t config = NULL; + if (plist_read_from_filename(&config, config_file)) { + usbmuxd_log(LL_DEBUG, "reading key %s from config_file %s", key, config_file); + plist_t n = plist_dict_get_item(config, key); + if (n) { + *value = plist_copy(n); + plist_free(n); + n = NULL; + } + } + plist_free(config); + + return 1; +} + +static int config_get_value(const char *key, plist_t *value) +{ + const char *config_path = NULL; + char *config_file = NULL; + + config_path = config_get_config_dir(); + config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL); + + int result = internal_get_value(config_file, key, value); + + free(config_file); + + return result; +} + +/** + * Store SystemBUID in config file. + * + * @param system_buid A null terminated string containing a valid SystemBUID. + */ +static int config_set_system_buid(const char *system_buid) +{ + return config_set_value(CONFIG_SYSTEM_BUID_KEY, plist_new_string(system_buid)); +} + +/** + * Reads the BUID from a previously generated configuration file. + * + * @param system_buid pointer to a variable that will be set to point to a + * newly allocated string containing the BUID. + * + * @note It is the responsibility of the calling function to free the returned system_buid + */ +void config_get_system_buid(char **system_buid) +{ + plist_t value = NULL; + + config_get_value(CONFIG_SYSTEM_BUID_KEY, &value); + + if (value && (plist_get_node_type(value) == PLIST_STRING)) { + plist_get_string_val(value, system_buid); + usbmuxd_log(LL_DEBUG, "got %s %s", CONFIG_SYSTEM_BUID_KEY, *system_buid); + } + + if (value) + plist_free(value); + + if (!*system_buid) { + /* no config, generate system_buid */ + usbmuxd_log(LL_DEBUG, "no previous %s found", CONFIG_SYSTEM_BUID_KEY); + *system_buid = config_generate_system_buid(); + config_set_system_buid(*system_buid); + } + + usbmuxd_log(LL_DEBUG, "using %s as %s", *system_buid, CONFIG_SYSTEM_BUID_KEY); +} + +/** + * Store a pairing record for the given device identifier. + * + * @param udid device identifier + * @param record_data buffer containing a pairing record + * @param record_size size of buffer passed in record_data + * + * @return 0 on success or a negative errno otherwise. + */ +int config_set_device_record(const char *udid, char* record_data, uint64_t record_size) +{ + int res = 0; + + if (!udid || record_data || record_size < 8) + return -EINVAL; + + plist_t plist = NULL; + if (memcmp(record_data, "bplist00", 8) == 0) { + plist_from_bin(record_data, record_size, &plist); + } else { + plist_from_xml(record_data, record_size, &plist); + } + + if (!plist || plist_get_node_type(plist) != PLIST_DICT) { + if (plist) + plist_free(plist); + return -EINVAL; + } + + /* ensure config directory exists */ + config_create_config_dir(); + + /* build file path */ + const char *config_path = config_get_config_dir(); + char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL); + + remove(device_record_file); + + /* store file */ + if (!plist_write_to_filename(plist, device_record_file, PLIST_FORMAT_XML)) { + usbmuxd_log(LL_DEBUG, "could not open '%s' for writing: %s", device_record_file, strerror(errno)); + res = -ENOENT; + } + free(device_record_file); + if (plist) + plist_free(plist); + + return res; +} + +/** + * Retrieve a pairing record for the given device identifier + * + * @param udid device identifier + * @param record_data pointer to a variable that will be set to point to a + * newly allocated buffer holding the pairing record + * @param record_size pointer to a variable that will be set to the size + * of the buffer given in record_data. + * + * @return 0 on success or a negative errno otherwise. + */ +int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size) +{ + int res = 0; + + /* ensure config directory exists */ + config_create_config_dir(); + + /* build file path */ + const char *config_path = config_get_config_dir(); + char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL); + + /* read file */ + buffer_read_from_filename(device_record_file, record_data, record_size); if (!*record_data) { + usbmuxd_log(LL_ERROR, "%s: failed to read '%s': %s", __func__, device_record_file, strerror(errno)); + res = -ENOENT; + } + free(device_record_file); + + return res; +} + +/** + * Remove the pairing record stored for a device from this host. + * + * @param udid The udid of the device + * + * @return 0 on success or a negative errno otherwise. + */ +int config_remove_device_record(const char *udid) +{ + int res = 0; + + /* build file path */ + const char *config_path = config_get_config_dir(); + char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL); + + /* remove file */ + if (remove(device_record_file) != 0) { + res = -errno; + usbmuxd_log(LL_DEBUG, "could not remove %s: %s", device_record_file, strerror(errno)); + } + + free(device_record_file); + + return res; +} + +static int config_device_record_get_value(const char *udid, const char *key, plist_t *value) +{ + const char *config_path = NULL; + char *config_file = NULL; + + config_path = config_get_config_dir(); + config_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL); + + int result = internal_get_value(config_file, key, value); + + free(config_file); + + return result; +} + +void config_device_record_get_host_id(const char *udid, char **host_id) +{ + plist_t value = NULL; + + config_device_record_get_value(udid, CONFIG_HOST_ID_KEY, &value); + + if (value && (plist_get_node_type(value) == PLIST_STRING)) { + plist_get_string_val(value, host_id); + } + + if (value) + plist_free(value); + + if (!*host_id) { + usbmuxd_log(LL_ERROR, "%s: ERROR couldn't get HostID from pairing record for udid %s\n", __func__, udid); + } +} diff --git a/src/conf.h b/src/conf.h new file mode 100644 index 0000000..a104185 --- /dev/null +++ b/src/conf.h @@ -0,0 +1,40 @@ +/* + 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 __CONF_H__ +#define __CONF_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +const char *config_get_config_dir(); + +void config_get_system_buid(char **system_buid); + +int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size); +int config_set_device_record(const char *udid, char* record_data, uint64_t record_size); +int config_remove_device_record(const char *udid); + +void config_device_record_get_host_id(const char *udid, char **host_id); + +#endif diff --git a/src/main.c b/src/main.c index 15cb5a1..9e2db60 100644 --- a/src/main.c +++ b/src/main.c @@ -46,10 +46,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "usb.h" #include "device.h" #include "client.h" - -#ifdef HAVE_LIBIMOBILEDEVICE -extern const char* userpref_get_config_dir(); -#endif +#include "conf.h" static const char *socket_path = "/var/run/usbmuxd"; static const char *lockfile = "/var/run/usbmuxd.pid"; @@ -534,7 +531,7 @@ int main(int argc, char *argv[]) goto terminate; #ifdef HAVE_LIBIMOBILEDEVICE - const char* userprefdir = userpref_get_config_dir(); + const char* userprefdir = config_get_config_dir(); struct stat fst; memset(&fst, '\0', sizeof(struct stat)); if (stat(userprefdir, &fst) < 0) { diff --git a/src/preflight.c b/src/preflight.c index def6a82..283c6d9 100644 --- a/src/preflight.c +++ b/src/preflight.c @@ -36,6 +36,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "preflight.h" #include "client.h" +#include "conf.h" #include "log.h" #ifdef HAVE_LIBIMOBILEDEVICE @@ -55,12 +56,10 @@ struct cb_data { int is_device_connected; }; -extern uint16_t userpref_remove_device_record(const char* udid); - static void lockdownd_set_untrusted_host_buid(lockdownd_client_t lockdown) { char* system_buid = NULL; - userpref_get_system_buid(&system_buid); + config_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); @@ -162,7 +161,7 @@ retry: int is_device_paired = 0; char *host_id = NULL; - userpref_device_record_get_host_id(dev->udid, &host_id); + 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) { @@ -179,7 +178,7 @@ retry: 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 (userpref_remove_device_record(_dev->udid) == 0) { + if (config_remove_device_record(_dev->udid) == 0) { lockdownd_client_free(lockdown); lockdown = NULL; goto retry; @@ -293,7 +292,7 @@ retry: } host_id = NULL; - userpref_device_record_get_host_id(dev->udid, &host_id); + 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) { diff --git a/src/utils.c b/src/utils.c index ceef535..475a921 100644 --- a/src/utils.c +++ b/src/utils.c @@ -3,6 +3,7 @@ Copyright (C) 2009 Hector Martin "marcan" Copyright (C) 2009 Nikias Bassen +Copyright (c) 2013 Federico Mena Quintero This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -27,6 +28,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include #include #include +#include + #include "utils.h" #include "log.h" @@ -118,3 +121,144 @@ int collection_count(struct collection *col) } return cnt; } + +#ifndef HAVE_STPCPY +/** + * Copy characters from one string into another + * + * @note: The strings should not overlap, as the behavior is undefined. + * + * @s1: The source string. + * @s2: The destination string. + * + * @return a pointer to the terminating `\0' character of @s1, + * or NULL if @s1 or @s2 is NULL. + */ +char *stpcpy(char * s1, const char * s2) +{ + if (s1 == NULL || s2 == NULL) + return NULL; + + strcpy(s1, s2); + + return s1 + strlen(s2); +} +#endif + +/** + * Concatenate strings into a newly allocated string + * + * @note: Specify NULL for the last string in the varargs list + * + * @str: The first string in the list + * @...: Subsequent strings. Use NULL for the last item. + * + * @return a newly allocated string, or NULL if @str is NULL. This will also + * return NULL and set errno to ENOMEM if memory is exhausted. + */ +char *string_concat(const char *str, ...) +{ + size_t len; + va_list args; + char *s; + char *result; + char *dest; + + if (!str) + return NULL; + + /* Compute final length */ + + len = strlen(str) + 1; /* plus 1 for the null terminator */ + + va_start(args, str); + s = va_arg(args, char *); + while (s) { + len += strlen(s); + s = va_arg(args, char*); + } + va_end(args); + + /* Concat each string */ + + result = malloc(len); + if (!result) + return NULL; /* errno remains set */ + + dest = result; + + dest = stpcpy(dest, str); + + va_start(args, str); + s = va_arg(args, char *); + while (s) { + dest = stpcpy(dest, s); + s = va_arg(args, char *); + } + va_end(args); + + return result; +} + +void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length) +{ + FILE *f; + uint64_t size; + + *length = 0; + + f = fopen(filename, "rb"); + if (!f) { + return; + } + + fseek(f, 0, SEEK_END); + size = ftell(f); + rewind(f); + + if (size == 0) { + fclose(f); + return; + } + + *buffer = (char*)malloc(sizeof(char)*(size+1)); + if (fread(*buffer, sizeof(char), size, f) != size) { + usbmuxd_log(LL_ERROR, "%s: ERROR: couldn't read %d bytes from %s\n", __func__, (int)size, filename); + } + fclose(f); + + *length = size; +} + +void buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length) +{ + FILE *f; + + f = fopen(filename, "wb"); + if (f) { + fwrite(buffer, sizeof(char), length, f); + fclose(f); + } +} + +int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format) +{ + char *buffer = NULL; + uint32_t length; + + if (!plist || !filename) + return 0; + + if (format == PLIST_FORMAT_XML) + plist_to_xml(plist, &buffer, &length); + else if (format == PLIST_FORMAT_BINARY) + plist_to_bin(plist, &buffer, &length); + else + return 0; + + buffer_write_to_filename(filename, buffer, length); + + free(buffer); + + return 1; +} diff --git a/src/utils.h b/src/utils.h index 69a4259..730fbad 100644 --- a/src/utils.h +++ b/src/utils.h @@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #define __UTILS_H__ #include +#include enum fdowner { FD_LISTEN, @@ -69,4 +70,20 @@ void collection_free(struct collection *col); } \ } while(0); +#ifndef HAVE_STPCPY +char *stpcpy(char * s1, const char * s2); +#endif +char *string_concat(const char *str, ...); + +void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length); +void buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length); + +enum plist_format_t { + PLIST_FORMAT_XML, + PLIST_FORMAT_BINARY +}; + +int plist_read_from_filename(plist_t *plist, const char *filename); +int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format); + #endif -- cgit v1.1-32-gdbae