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 --- src/conf.c | 499 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 499 insertions(+) create mode 100644 src/conf.c (limited to 'src/conf.c') 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); + } +} -- cgit v1.1-32-gdbae