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