diff options
Diffstat (limited to 'src/userpref.c')
-rw-r--r-- | src/userpref.c | 492 |
1 files changed, 404 insertions, 88 deletions
diff --git a/src/userpref.c b/src/userpref.c index 4aab67b..d44d5aa 100644 --- a/src/userpref.c +++ b/src/userpref.c @@ -19,9 +19,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <glib.h> -#include <glib/gstdio.h> -#include <glib/gprintf.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> @@ -30,6 +27,15 @@ #include <gnutls/x509.h> #include <gcrypt.h> +#include <dirent.h> +#include <libgen.h> +#include <sys/stat.h> +#include <errno.h> + +#ifdef WIN32 +#include <shlobj.h> +#endif + #include "userpref.h" #include "debug.h" @@ -41,18 +47,239 @@ #define LIBIMOBILEDEVICE_ROOT_CERTIF "RootCertificate.pem" #define LIBIMOBILEDEVICE_HOST_CERTIF "HostCertificate.pem" +#ifdef WIN32 +#define DIR_SEP '\\' +#define DIR_SEP_S "\\" +#else +#define DIR_SEP '/' +#define DIR_SEP_S "/" +#endif + +static char __config_dir[512] = {0, }; + +#ifdef WIN32 +static char *userpref_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 + +static const char *userpref_get_config_dir() +{ + if (__config_dir[0]) return __config_dir; +#ifdef WIN32 + wchar_t path[MAX_PATH+1]; + HRESULT hr; + LPITEMIDLIST pidl = NULL; + BOOL b = FALSE; + + hr = SHGetSpecialFolderLocation (NULL, CSIDL_LOCAL_APPDATA, &pidl); + if (hr == S_OK) { + b = SHGetPathFromIDListW (pidl, path); + if (b) { + char *cdir = userpref_utf16_to_utf8 (path, wcslen(path), NULL, NULL); + strcpy(__config_dir, cdir); + free(cdir); + CoTaskMemFree (pidl); + } + } +#else + const char *cdir = getenv("XDG_CONFIG_HOME"); + if (!cdir) { + cdir = getenv("HOME"); + strcpy(__config_dir, cdir); + strcat(__config_dir, DIR_SEP_S); + strcat(__config_dir, ".config"); + } else { + strcpy(__config_dir, cdir); + } +#endif + strcat(__config_dir, DIR_SEP_S); + strcat(__config_dir, LIBIMOBILEDEVICE_CONF_DIR); + + int i = strlen(__config_dir)-1; + while ((i > 0) && (__config_dir[i] == DIR_SEP)) { + __config_dir[i--] = '\0'; + } + + return __config_dir; +} + +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); + parent = dirname(parent); + if (parent) { + res = mkdir_with_parents(parent, mode); + } else { + res = -1; + } + free(parent); + if (res == 0) { + mkdir_with_parents(dir, mode); + } + return res; +} + +static int config_write(const char *cfgfile, plist_t dict) +{ + if (!cfgfile || !dict || (plist_get_node_type(dict) != PLIST_DICT)) { + return -1; + } + int res = -1; + +#if 1 // old style config + plist_t hostid = plist_dict_get_item(dict, "HostID"); + if (hostid && (plist_get_node_type(hostid) == PLIST_STRING)) { + char *hostidstr = NULL; + plist_get_string_val(hostid, &hostidstr); + if (hostidstr) { + FILE *fd = fopen(cfgfile, "w"); + if (fd) { + fprintf(fd, "\n[Global]\nHostID=%s\n", hostidstr); + fclose(fd); + res = 0; + } + free(hostidstr); + } + } +#endif +#if 0 + char *xml = NULL; + uint32_t length = 0; + + plist_to_xml(dict, &xml, &length); + if (!xml) { + return res; + } + + FILE *fd = fopen(cfgfile, "w"); + if (!fd) { + free(xml); + return res; + } + + if (fwrite(xml, 1, length, fd) == length) { + res = 0; + } else { + fprintf(stderr, "%s: ERROR: failed to write configuration to '%s'\n", __func__, cfgfile); + } + fclose(fd); + + free(xml); +#endif + return res; +} + +static int config_read(const char *cfgfile, plist_t *dict) +{ + if (!cfgfile || !dict) { + return -1; + } + + int res = -1; + FILE *fd = fopen(cfgfile, "r+"); + if (!fd) { + return -1; + } + + fseek(fd, 0, SEEK_END); + unsigned long int size = ftell(fd); + fseek(fd, 0, SEEK_SET); + unsigned char *contents = NULL; + + contents = malloc(size); + if (fread(contents, 1, size, fd) != size) { + free(contents); + fclose(fd); + return -1; + } + plist_t plist = NULL; + + if (!memcmp(contents, "bplist00", 8)) { + plist_from_bin((const char*)contents, (uint32_t)size, &plist); + fclose(fd); + } else { + if (memchr(contents, '<', size)) { + plist_from_xml((const char*)contents, (uint32_t)size, &plist); + } + if (plist) { + fclose(fd); + } else { + // try parsing old format config file + char line[256]; + fseek(fd, 0, SEEK_SET); + while (fgets(line, 256, fd)) { + size_t llen = strlen(line)-1; + while ((llen > 0) && ((line[llen] == '\n') || (line[llen] == '\r'))) { + line[llen] = '\0'; + } + if (!strncmp(line, "HostID=", 7)) { + plist = plist_new_dict(); + plist_dict_insert_item(plist, "HostID", plist_new_string(line+7)); + break; + } + } + fclose(fd); + if (plist) { + // write new format config + config_write(cfgfile, plist); + } + } + } + free(contents); + if (plist) { + *dict = plist; + res = 0; + } + return res; +} /** * Creates a freedesktop compatible configuration directory. */ static void userpref_create_config_dir(void) { - gchar *config_dir = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, NULL); - - if (!g_file_test(config_dir, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) - g_mkdir_with_parents(config_dir, 0755); - - g_free(config_dir); + const char *config_path = userpref_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) @@ -94,10 +321,8 @@ static char *userpref_generate_host_id() */ static int userpref_set_host_id(const char *host_id) { - GKeyFile *key_file; - gsize length; - gchar *buf, *config_file; - GIOChannel *file; + const char *config_path; + char *config_file; if (!host_id) return 0; @@ -105,24 +330,34 @@ static int userpref_set_host_id(const char *host_id) /* Make sure config directory exists */ userpref_create_config_dir(); + config_path = userpref_get_config_dir(); + config_file = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_CONF_FILE)+1); + strcpy(config_file, config_path); + strcat(config_file, DIR_SEP_S); + strcat(config_file, LIBIMOBILEDEVICE_CONF_FILE); + /* Now parse file to get the HostID */ - key_file = g_key_file_new(); + plist_t config = NULL; + config_read(config_file, &config); + if (!config) { + config = plist_new_dict(); + plist_dict_insert_item(config, "HostID", plist_new_string(host_id)); + } else { + plist_t n = plist_dict_get_item(config, "HostID"); + if (n) { + plist_set_string_val(n, host_id); + } else { + plist_dict_insert_item(config, "HostID", plist_new_string(host_id)); + } + } /* Store in config file */ debug_info("setting hostID to %s", host_id); - g_key_file_set_value(key_file, "Global", "HostID", host_id); - - /* Write config file on disk */ - buf = g_key_file_to_data(key_file, &length, NULL); - config_file = - g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_CONF_FILE, NULL); - file = g_io_channel_new_file(config_file, "w", NULL); - g_free(config_file); - g_io_channel_write_chars(file, buf, length, NULL, NULL); - g_io_channel_shutdown(file, TRUE, NULL); - g_io_channel_unref(file); - - g_key_file_free(key_file); + + config_write(config_file, config); + plist_free(config); + + free(config_file); return 1; } @@ -135,23 +370,25 @@ static int userpref_set_host_id(const char *host_id) */ void userpref_get_host_id(char **host_id) { - gchar *config_file; - GKeyFile *key_file; - gchar *loc_host_id; + const char *config_path; + char *config_file; - config_file = - g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_CONF_FILE, NULL); + config_path = userpref_get_config_dir(); + config_file = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_CONF_FILE)+1); + strcpy(config_file, config_path); + strcat(config_file, DIR_SEP_S); + strcat(config_file, LIBIMOBILEDEVICE_CONF_FILE); /* now parse file to get the HostID */ - key_file = g_key_file_new(); - if (g_key_file_load_from_file(key_file, config_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) { - loc_host_id = g_key_file_get_value(key_file, "Global", "HostID", NULL); - if (loc_host_id) - *host_id = strdup((char *) loc_host_id); - g_free(loc_host_id); + plist_t config = NULL; + if (config_read(config_file, &config) == 0) { + plist_t n_host_id = plist_dict_get_item(config, "HostID"); + if (n_host_id && (plist_get_node_type(n_host_id) == PLIST_STRING)) { + plist_get_string_val(n_host_id, host_id); + } } - g_key_file_free(key_file); - g_free(config_file); + plist_free(config); + free(config_file); if (!*host_id) { /* no config, generate host_id */ @@ -173,15 +410,23 @@ void userpref_get_host_id(char **host_id) int userpref_has_device_public_key(const char *uuid) { int ret = 0; - gchar *config_file; + const char *config_path; + char *config_file; + struct stat st; + + if (!uuid) return 0; /* first get config file */ - gchar *device_file = g_strconcat(uuid, ".pem", NULL); - config_file = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, device_file, NULL); - if (g_file_test(config_file, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) + config_path = userpref_get_config_dir(); + config_file = (char*)malloc(strlen(config_path)+1+strlen(uuid)+4+1); + strcpy(config_file, config_path); + strcat(config_file, DIR_SEP_S); + strcat(config_file, uuid); + strcat(config_file, ".pem"); + + if ((stat(config_file, &st) == 0) && S_ISREG(st.st_mode)) ret = 1; - g_free(config_file); - g_free(device_file); + free(config_file); return ret; } @@ -202,10 +447,13 @@ int userpref_has_device_public_key(const char *uuid) */ userpref_error_t userpref_get_paired_uuids(char ***list, unsigned int *count) { - GDir *config_dir; - gchar *config_path; - const gchar *dir_file; - GList *uuids = NULL; + struct slist_t { + char *name; + void *next; + }; + DIR *config_dir; + const char *config_path; + struct slist_t *uuids = NULL; unsigned int i; unsigned int found = 0; @@ -218,29 +466,44 @@ userpref_error_t userpref_get_paired_uuids(char ***list, unsigned int *count) *count = 0; } - config_path = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, NULL); - - config_dir = g_dir_open(config_path,0,NULL); + config_path = userpref_get_config_dir(); + config_dir = opendir(config_path); if (config_dir) { - while ((dir_file = g_dir_read_name(config_dir))) { - if (g_str_has_suffix(dir_file, ".pem") && (strlen(dir_file) == 44)) { - uuids = g_list_append(uuids, g_strndup(dir_file, strlen(dir_file)-4)); + struct dirent *entry; + struct slist_t *listp = uuids; + while ((entry = readdir(config_dir))) { + char *ext = strstr(entry->d_name, ".pem"); + if (ext && ((ext - entry->d_name) == 40) && (strlen(entry->d_name) == 44)) { + struct slist_t *ne = (struct slist_t*)malloc(sizeof(struct slist_t)); + ne->name = (char*)malloc(41); + strncpy(ne->name, entry->d_name, 40); + ne->name[40] = 0; + ne->next = NULL; + if (!listp) { + listp = ne; + uuids = listp; + } else { + listp->next = ne; + listp = listp->next; + } found++; } } - g_dir_close(config_dir); + closedir(config_dir); } *list = (char**)malloc(sizeof(char*) * (found+1)); - for (i = 0; i < found; i++) { - (*list)[i] = g_list_nth_data(uuids, i); + i = 0; + while (uuids) { + (*list)[i++] = uuids->name; + struct slist_t *old = uuids; + uuids = uuids->next; + free(old); } (*list)[i] = NULL; if (count) { *count = found; } - g_list_free(uuids); - g_free(config_path); return USERPREF_E_SUCCESS; } @@ -266,15 +529,18 @@ userpref_error_t userpref_set_device_public_key(const char *uuid, gnutls_datum_t userpref_create_config_dir(); /* build file path */ - gchar *device_file = g_strconcat(uuid, ".pem", NULL); - gchar *pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, device_file, NULL); + const char *config_path = userpref_get_config_dir(); + char *pem = (char*)malloc(strlen(config_path)+1+strlen(uuid)+4+1); + strcpy(pem, config_path); + strcat(pem, DIR_SEP_S); + strcat(pem, uuid); + strcat(pem, ".pem"); /* store file */ FILE *pFile = fopen(pem, "wb"); fwrite(public_key.data, 1, public_key.size, pFile); fclose(pFile); - g_free(pem); - g_free(device_file); + free(pem); return USERPREF_E_SUCCESS; } @@ -292,14 +558,17 @@ userpref_error_t userpref_remove_device_public_key(const char *uuid) return USERPREF_E_SUCCESS; /* build file path */ - gchar *device_file = g_strconcat(uuid, ".pem", NULL); - gchar *pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, device_file, NULL); + const char *config_path = userpref_get_config_dir(); + char *pem = (char*)malloc(strlen(config_path)+1+strlen(uuid)+4+1); + strcpy(pem, config_path); + strcat(pem, DIR_SEP_S); + strcat(pem, uuid); + strcat(pem, ".pem"); /* remove file */ - g_remove(pem); + remove(pem); - g_free(pem); - g_free(device_file); + free(pem); return USERPREF_E_SUCCESS; } @@ -314,18 +583,50 @@ userpref_error_t userpref_remove_device_public_key(const char *uuid) */ static int userpref_get_file_contents(const char *file, gnutls_datum_t * data) { - gboolean success; - gsize size; - char *content; - gchar *filepath; + int success; + unsigned long int size = 0; + unsigned char *content = NULL; + const char *config_path; + char *filepath; + FILE *fd; if (NULL == file || NULL == data) return 0; /* Read file */ - filepath = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, file, NULL); - success = g_file_get_contents(filepath, &content, &size, NULL); - g_free(filepath); + config_path = userpref_get_config_dir(); + filepath = (char*)malloc(strlen(config_path)+1+strlen(file)+1); + strcpy(filepath, config_path); + strcat(filepath, DIR_SEP_S); + strcat(filepath, file); + + fd = fopen(filepath, "rb"); + if (fd) { + fseek(fd, 0, SEEK_END); + size = ftell(fd); + fseek(fd, 0, SEEK_SET); + + // prevent huge files + if (size > 0xFFFFFF) { + fprintf(stderr, "%s: file is too big (> 16MB). Refusing to read the contents to memory!", __func__); + } else { + size_t p = 0; + content = (unsigned char*)malloc(size); + while (!feof(fd)) { + p += fread(content+p, 1, size-p, fd); + if (ferror(fd) != 0) { + break; + } + if (p >= size) { + success = 1; + break; + } + } + } + fclose(fd); + } + + free(filepath); /* Add it to the gnutls_datnum_t structure */ if (success) { @@ -549,8 +850,8 @@ userpref_error_t userpref_get_certs_as_pem(gnutls_datum_t *pem_root_cert, gnutls if (userpref_get_file_contents(LIBIMOBILEDEVICE_ROOT_CERTIF, pem_root_cert) && userpref_get_file_contents(LIBIMOBILEDEVICE_HOST_CERTIF, pem_host_cert)) return USERPREF_E_SUCCESS; else { - g_free(pem_root_cert->data); - g_free(pem_host_cert->data); + gnutls_free(pem_root_cert->data); + gnutls_free(pem_host_cert->data); } return USERPREF_E_INVALID_CONF; } @@ -570,7 +871,8 @@ userpref_error_t userpref_get_certs_as_pem(gnutls_datum_t *pem_root_cert, gnutls userpref_error_t userpref_set_keys_and_certs(gnutls_datum_t * root_key, gnutls_datum_t * root_cert, gnutls_datum_t * host_key, gnutls_datum_t * host_cert) { FILE *pFile; - gchar *pem; + char *pem; + const char *config_path; if (!root_key || !host_key || !root_cert || !host_cert) return USERPREF_E_INVALID_ARG; @@ -578,30 +880,44 @@ userpref_error_t userpref_set_keys_and_certs(gnutls_datum_t * root_key, gnutls_d /* Make sure config directory exists */ userpref_create_config_dir(); + config_path = userpref_get_config_dir(); + /* Now write keys and certificates to disk */ - pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_ROOT_PRIVKEY, NULL); + pem = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_ROOT_PRIVKEY)+1); + strcpy(pem, config_path); + strcat(pem, DIR_SEP_S); + strcat(pem, LIBIMOBILEDEVICE_ROOT_PRIVKEY); pFile = fopen(pem, "wb"); fwrite(root_key->data, 1, root_key->size, pFile); fclose(pFile); - g_free(pem); + free(pem); - pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_HOST_PRIVKEY, NULL); + pem = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_HOST_PRIVKEY)+1); + strcpy(pem, config_path); + strcat(pem, DIR_SEP_S); + strcat(pem, LIBIMOBILEDEVICE_HOST_PRIVKEY); pFile = fopen(pem, "wb"); fwrite(host_key->data, 1, host_key->size, pFile); fclose(pFile); - g_free(pem); + free(pem); - pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_ROOT_CERTIF, NULL); + pem = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_ROOT_CERTIF)+1); + strcpy(pem, config_path); + strcat(pem, DIR_SEP_S); + strcat(pem, LIBIMOBILEDEVICE_ROOT_CERTIF); pFile = fopen(pem, "wb"); fwrite(root_cert->data, 1, root_cert->size, pFile); fclose(pFile); - g_free(pem); + free(pem); - pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_HOST_CERTIF, NULL); + pem = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_HOST_CERTIF)+1); + strcpy(pem, config_path); + strcat(pem, DIR_SEP_S); + strcat(pem, LIBIMOBILEDEVICE_HOST_CERTIF); pFile = fopen(pem, "wb"); fwrite(host_cert->data, 1, host_cert->size, pFile); fclose(pFile); - g_free(pem); + free(pem); return USERPREF_E_SUCCESS; } |