summaryrefslogtreecommitdiffstats
path: root/src/conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf.c')
-rw-r--r--src/conf.c499
1 files changed, 499 insertions, 0 deletions
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 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdio.h>
26#include <stdint.h>
27#include <stdlib.h>
28#include <string.h>
29#include <time.h>
30#ifdef HAVE_SYS_TYPES_H
31#include <sys/types.h>
32#endif
33
34#include <dirent.h>
35#include <libgen.h>
36#include <sys/stat.h>
37#include <errno.h>
38
39#ifdef WIN32
40#include <shlobj.h>
41#endif
42
43#include "conf.h"
44#include "utils.h"
45#include "log.h"
46
47#ifdef WIN32
48#define DIR_SEP '\\'
49#define DIR_SEP_S "\\"
50#else
51#define DIR_SEP '/'
52#define DIR_SEP_S "/"
53#endif
54
55#define CONFIG_SYSTEM_BUID_KEY "SystemBUID"
56#define CONFIG_HOST_ID_KEY "HostID"
57
58#define CONFIG_EXT ".plist"
59
60#ifdef WIN32
61#define CONFIG_DIR "Apple"DIR_SEP_S"Lockdown"
62#else
63#define CONFIG_DIR "lockdown"
64#endif
65
66#define CONFIG_FILE "SystemConfiguration"CONFIG_EXT
67
68static char *__config_dir = NULL;
69
70#ifdef WIN32
71static char *config_utf16_to_utf8(wchar_t *unistr, long len, long *items_read, long *items_written)
72{
73 if (!unistr || (len <= 0)) return NULL;
74 char *outbuf = (char*)malloc(3*(len+1));
75 int p = 0;
76 int i = 0;
77
78 wchar_t wc;
79
80 while (i < len) {
81 wc = unistr[i++];
82 if (wc >= 0x800) {
83 outbuf[p++] = (char)(0xE0 + ((wc >> 12) & 0xF));
84 outbuf[p++] = (char)(0x80 + ((wc >> 6) & 0x3F));
85 outbuf[p++] = (char)(0x80 + (wc & 0x3F));
86 } else if (wc >= 0x80) {
87 outbuf[p++] = (char)(0xC0 + ((wc >> 6) & 0x1F));
88 outbuf[p++] = (char)(0x80 + (wc & 0x3F));
89 } else {
90 outbuf[p++] = (char)(wc & 0x7F);
91 }
92 }
93 if (items_read) {
94 *items_read = i;
95 }
96 if (items_written) {
97 *items_written = p;
98 }
99 outbuf[p] = 0;
100
101 return outbuf;
102}
103#endif
104
105const char *config_get_config_dir()
106{
107 char *base_config_dir = NULL;
108
109 if (__config_dir)
110 return __config_dir;
111
112#ifdef WIN32
113 wchar_t path[MAX_PATH+1];
114 HRESULT hr;
115 LPITEMIDLIST pidl = NULL;
116 BOOL b = FALSE;
117
118 hr = SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_APPDATA, &pidl);
119 if (hr == S_OK) {
120 b = SHGetPathFromIDListW (pidl, path);
121 if (b) {
122 base_config_dir = config_utf16_to_utf8 (path, wcslen(path), NULL, NULL);
123 CoTaskMemFree (pidl);
124 }
125 }
126#else
127#ifdef __APPLE__
128 base_config_dir = strdup("/var/db");
129#else
130 base_config_dir = strdup("/var/lib");
131#endif
132#endif
133 __config_dir = string_concat(base_config_dir, DIR_SEP_S, CONFIG_DIR, NULL);
134
135 if (__config_dir) {
136 int i = strlen(__config_dir)-1;
137 while ((i > 0) && (__config_dir[i] == DIR_SEP)) {
138 __config_dir[i--] = '\0';
139 }
140 }
141
142 free(base_config_dir);
143
144 usbmuxd_log(LL_DEBUG, "initialized config_dir to %s", __config_dir);
145
146 return __config_dir;
147}
148
149static int __mkdir(const char *dir, int mode)
150{
151#ifdef WIN32
152 return mkdir(dir);
153#else
154 return mkdir(dir, mode);
155#endif
156}
157
158static int mkdir_with_parents(const char *dir, int mode)
159{
160 if (!dir) return -1;
161 if (__mkdir(dir, mode) == 0) {
162 return 0;
163 } else {
164 if (errno == EEXIST) return 0;
165 }
166 int res;
167 char *parent = strdup(dir);
168 char* parentdir = dirname(parent);
169 if (parentdir) {
170 res = mkdir_with_parents(parentdir, mode);
171 } else {
172 res = -1;
173 }
174 free(parent);
175 return res;
176}
177
178/**
179 * Creates a freedesktop compatible configuration directory.
180 */
181static void config_create_config_dir(void)
182{
183 const char *config_path = config_get_config_dir();
184 struct stat st;
185 if (stat(config_path, &st) != 0) {
186 mkdir_with_parents(config_path, 0755);
187 }
188}
189
190static int get_rand(int min, int max)
191{
192 int retval = (rand() % (max - min)) + min;
193 return retval;
194}
195
196static char *config_generate_uuid(int idx)
197{
198 char *uuid = (char *) malloc(sizeof(char) * 37);
199 const char *chars = "ABCDEF0123456789";
200 srand(time(NULL) - idx);
201 int i = 0;
202
203 for (i = 0; i < 36; i++) {
204 if (i == 8 || i == 13 || i == 18 || i == 23) {
205 uuid[i] = '-';
206 continue;
207 } else {
208 uuid[i] = chars[get_rand(0, 16)];
209 }
210 }
211 /* make it a real string */
212 uuid[36] = '\0';
213 return uuid;
214}
215
216/**
217 * Generates a valid BUID for this system (which is actually a UUID).
218 *
219 * @return A null terminated string containing a valid BUID.
220 */
221static char *config_generate_system_buid()
222{
223 return config_generate_uuid(1);
224}
225
226static int internal_set_value(const char *config_file, const char *key, plist_t value)
227{
228 if (!config_file)
229 return 0;
230
231 /* read file into plist */
232 plist_t config = NULL;
233
234 plist_read_from_filename(&config, config_file);
235 if (!config) {
236 config = plist_new_dict();
237 plist_dict_insert_item(config, key, value);
238 } else {
239 plist_t n = plist_dict_get_item(config, key);
240 if (n) {
241 plist_dict_remove_item(config, key);
242 }
243 plist_dict_insert_item(config, key, value);
244 remove(config_file);
245 }
246
247 /* store in config file */
248 char *value_string = NULL;
249 if (plist_get_node_type(value) == PLIST_STRING) {
250 plist_get_string_val(value, &value_string);
251 usbmuxd_log(LL_DEBUG, "setting key %s to %s in config_file %s", key, value_string, config_file);
252 if (value_string)
253 free(value_string);
254 } else {
255 usbmuxd_log(LL_DEBUG, "setting key %s in config_file %s", key, config_file);
256 }
257
258 plist_write_to_filename(config, config_file, PLIST_FORMAT_XML);
259
260 plist_free(config);
261
262 return 1;
263}
264
265static int config_set_value(const char *key, plist_t value)
266{
267 const char *config_path = NULL;
268 char *config_file = NULL;
269
270 /* Make sure config directory exists */
271 config_create_config_dir();
272
273 config_path = config_get_config_dir();
274 config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL);
275
276 int result = internal_set_value(config_file, key, value);
277
278 free(config_file);
279
280 return result;
281}
282
283static int internal_get_value(const char* config_file, const char *key, plist_t *value)
284{
285 *value = NULL;
286
287 /* now parse file to get the SystemBUID */
288 plist_t config = NULL;
289 if (plist_read_from_filename(&config, config_file)) {
290 usbmuxd_log(LL_DEBUG, "reading key %s from config_file %s", key, config_file);
291 plist_t n = plist_dict_get_item(config, key);
292 if (n) {
293 *value = plist_copy(n);
294 plist_free(n);
295 n = NULL;
296 }
297 }
298 plist_free(config);
299
300 return 1;
301}
302
303static int config_get_value(const char *key, plist_t *value)
304{
305 const char *config_path = NULL;
306 char *config_file = NULL;
307
308 config_path = config_get_config_dir();
309 config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL);
310
311 int result = internal_get_value(config_file, key, value);
312
313 free(config_file);
314
315 return result;
316}
317
318/**
319 * Store SystemBUID in config file.
320 *
321 * @param system_buid A null terminated string containing a valid SystemBUID.
322 */
323static int config_set_system_buid(const char *system_buid)
324{
325 return config_set_value(CONFIG_SYSTEM_BUID_KEY, plist_new_string(system_buid));
326}
327
328/**
329 * Reads the BUID from a previously generated configuration file.
330 *
331 * @param system_buid pointer to a variable that will be set to point to a
332 * newly allocated string containing the BUID.
333 *
334 * @note It is the responsibility of the calling function to free the returned system_buid
335 */
336void config_get_system_buid(char **system_buid)
337{
338 plist_t value = NULL;
339
340 config_get_value(CONFIG_SYSTEM_BUID_KEY, &value);
341
342 if (value && (plist_get_node_type(value) == PLIST_STRING)) {
343 plist_get_string_val(value, system_buid);
344 usbmuxd_log(LL_DEBUG, "got %s %s", CONFIG_SYSTEM_BUID_KEY, *system_buid);
345 }
346
347 if (value)
348 plist_free(value);
349
350 if (!*system_buid) {
351 /* no config, generate system_buid */
352 usbmuxd_log(LL_DEBUG, "no previous %s found", CONFIG_SYSTEM_BUID_KEY);
353 *system_buid = config_generate_system_buid();
354 config_set_system_buid(*system_buid);
355 }
356
357 usbmuxd_log(LL_DEBUG, "using %s as %s", *system_buid, CONFIG_SYSTEM_BUID_KEY);
358}
359
360/**
361 * Store a pairing record for the given device identifier.
362 *
363 * @param udid device identifier
364 * @param record_data buffer containing a pairing record
365 * @param record_size size of buffer passed in record_data
366 *
367 * @return 0 on success or a negative errno otherwise.
368 */
369int config_set_device_record(const char *udid, char* record_data, uint64_t record_size)
370{
371 int res = 0;
372
373 if (!udid || record_data || record_size < 8)
374 return -EINVAL;
375
376 plist_t plist = NULL;
377 if (memcmp(record_data, "bplist00", 8) == 0) {
378 plist_from_bin(record_data, record_size, &plist);
379 } else {
380 plist_from_xml(record_data, record_size, &plist);
381 }
382
383 if (!plist || plist_get_node_type(plist) != PLIST_DICT) {
384 if (plist)
385 plist_free(plist);
386 return -EINVAL;
387 }
388
389 /* ensure config directory exists */
390 config_create_config_dir();
391
392 /* build file path */
393 const char *config_path = config_get_config_dir();
394 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
395
396 remove(device_record_file);
397
398 /* store file */
399 if (!plist_write_to_filename(plist, device_record_file, PLIST_FORMAT_XML)) {
400 usbmuxd_log(LL_DEBUG, "could not open '%s' for writing: %s", device_record_file, strerror(errno));
401 res = -ENOENT;
402 }
403 free(device_record_file);
404 if (plist)
405 plist_free(plist);
406
407 return res;
408}
409
410/**
411 * Retrieve a pairing record for the given device identifier
412 *
413 * @param udid device identifier
414 * @param record_data pointer to a variable that will be set to point to a
415 * newly allocated buffer holding the pairing record
416 * @param record_size pointer to a variable that will be set to the size
417 * of the buffer given in record_data.
418 *
419 * @return 0 on success or a negative errno otherwise.
420 */
421int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size)
422{
423 int res = 0;
424
425 /* ensure config directory exists */
426 config_create_config_dir();
427
428 /* build file path */
429 const char *config_path = config_get_config_dir();
430 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
431
432 /* read file */
433 buffer_read_from_filename(device_record_file, record_data, record_size); if (!*record_data) {
434 usbmuxd_log(LL_ERROR, "%s: failed to read '%s': %s", __func__, device_record_file, strerror(errno));
435 res = -ENOENT;
436 }
437 free(device_record_file);
438
439 return res;
440}
441
442/**
443 * Remove the pairing record stored for a device from this host.
444 *
445 * @param udid The udid of the device
446 *
447 * @return 0 on success or a negative errno otherwise.
448 */
449int config_remove_device_record(const char *udid)
450{
451 int res = 0;
452
453 /* build file path */
454 const char *config_path = config_get_config_dir();
455 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
456
457 /* remove file */
458 if (remove(device_record_file) != 0) {
459 res = -errno;
460 usbmuxd_log(LL_DEBUG, "could not remove %s: %s", device_record_file, strerror(errno));
461 }
462
463 free(device_record_file);
464
465 return res;
466}
467
468static int config_device_record_get_value(const char *udid, const char *key, plist_t *value)
469{
470 const char *config_path = NULL;
471 char *config_file = NULL;
472
473 config_path = config_get_config_dir();
474 config_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
475
476 int result = internal_get_value(config_file, key, value);
477
478 free(config_file);
479
480 return result;
481}
482
483void config_device_record_get_host_id(const char *udid, char **host_id)
484{
485 plist_t value = NULL;
486
487 config_device_record_get_value(udid, CONFIG_HOST_ID_KEY, &value);
488
489 if (value && (plist_get_node_type(value) == PLIST_STRING)) {
490 plist_get_string_val(value, host_id);
491 }
492
493 if (value)
494 plist_free(value);
495
496 if (!*host_id) {
497 usbmuxd_log(LL_ERROR, "%s: ERROR couldn't get HostID from pairing record for udid %s\n", __func__, udid);
498 }
499}