summaryrefslogtreecommitdiffstats
path: root/src/userpref.c
diff options
context:
space:
mode:
authorGravatar Matt Colyer2009-04-13 08:48:00 -0700
committerGravatar Matt Colyer2009-04-13 08:48:00 -0700
commit6671ca3d6de6a1fd27853e3b1ce7a81d568703f0 (patch)
tree735c5ace7ed57cd4e19f2fde423b22e6104eaa98 /src/userpref.c
parentbd31783d7fde0b5bd101f4a3f97ca1aca2aa6fab (diff)
parent288929f45cb2641690879b52ec514097995cd41a (diff)
downloadlibimobiledevice-6671ca3d6de6a1fd27853e3b1ce7a81d568703f0.tar.gz
libimobiledevice-6671ca3d6de6a1fd27853e3b1ce7a81d568703f0.tar.bz2
Merged in Jonathan's libplist libiphone. [#2 state:resolved]
Diffstat (limited to 'src/userpref.c')
-rw-r--r--src/userpref.c334
1 files changed, 273 insertions, 61 deletions
diff --git a/src/userpref.c b/src/userpref.c
index 5f227b0..0e83133 100644
--- a/src/userpref.c
+++ b/src/userpref.c
@@ -8,25 +8,28 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#include <glib.h> 22#include <glib.h>
23#include <glib/gprintf.h> 23#include <glib/gprintf.h>
24#include <stdio.h> 24#include <stdio.h>
25#include <stdlib.h>
25#include <string.h> 26#include <string.h>
27#include <gnutls/gnutls.h>
28#include <gnutls/x509.h>
29#include <gcrypt.h>
30
26#include "userpref.h" 31#include "userpref.h"
27#include "utils.h" 32#include "utils.h"
28#include <string.h>
29#include <stdlib.h>
30 33
31#define LIBIPHONE_CONF_DIR "libiphone" 34#define LIBIPHONE_CONF_DIR "libiphone"
32#define LIBIPHONE_CONF_FILE "libiphonerc" 35#define LIBIPHONE_CONF_FILE "libiphonerc"
@@ -49,9 +52,75 @@ static void create_config_dir(void)
49 g_free(config_dir); 52 g_free(config_dir);
50} 53}
51 54
55static int get_rand(int min, int max)
56{
57 int retval = (rand() % (max - min)) + min;
58 return retval;
59}
60
61/** Generates a valid HostID (which is actually a UUID).
62 *
63 * @return A null terminated string containing a valid HostID.
64 */
65static char *lockdownd_generate_hostid()
66{
67 char *hostid = (char *) malloc(sizeof(char) * 37); // HostID's are just UUID's, and UUID's are 36 characters long
68 const char *chars = "ABCDEF0123456789";
69 srand(time(NULL));
70 int i = 0;
71
72 for (i = 0; i < 36; i++) {
73 if (i == 8 || i == 13 || i == 18 || i == 23) {
74 hostid[i] = '-';
75 continue;
76 } else {
77 hostid[i] = chars[get_rand(0, 16)];
78 }
79 }
80 hostid[36] = '\0'; // make it a real string
81 return hostid;
82}
83
84/** Store HostID in config file.
85 *
86 * @param host_id A null terminated string containing a valid HostID.
87 */
88static int write_host_id(char *host_id)
89{
90 GKeyFile *key_file;
91 gsize length;
92 gchar *buf, *config_file;
93 GIOChannel *file;
94
95 if (!host_id)
96 return 0;
97
98 /* Make sure config directory exists */
99 create_config_dir();
100
101 /* Now parse file to get the HostID */
102 key_file = g_key_file_new();
52 103
53/** Reads the HostID from a previously generated configuration file. 104 /* Store in config file */
54 * 105 log_debug_msg("init_config_file(): setting hostID to %s\n", host_id);
106 g_key_file_set_value(key_file, "Global", "HostID", host_id);
107
108 /* Write config file on disk */
109 buf = g_key_file_to_data(key_file, &length, NULL);
110 config_file =
111 g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_CONF_FILE, NULL);
112 file = g_io_channel_new_file(config_file, "w", NULL);
113 g_free(config_file);
114 g_io_channel_write_chars(file, buf, length, NULL, NULL);
115 g_io_channel_shutdown(file, TRUE, NULL);
116 g_io_channel_unref(file);
117
118 g_key_file_free(key_file);
119 return 1;
120}
121
122/** Reads the HostID from a previously generated configuration file.
123 *
55 * @note It is the responsibility of the calling function to free the returned host_id 124 * @note It is the responsibility of the calling function to free the returned host_id
56 * 125 *
57 * @return The string containing the HostID or NULL 126 * @return The string containing the HostID or NULL
@@ -77,6 +146,12 @@ char *get_host_id(void)
77 g_key_file_free(key_file); 146 g_key_file_free(key_file);
78 g_free(config_file); 147 g_free(config_file);
79 148
149 if (!host_id) {
150 //no config, generate host_id
151 host_id = lockdownd_generate_hostid();
152 write_host_id(host_id);
153 }
154
80 log_debug_msg("get_host_id(): Using %s as HostID\n", host_id); 155 log_debug_msg("get_host_id(): Using %s as HostID\n", host_id);
81 return host_id; 156 return host_id;
82} 157}
@@ -111,10 +186,10 @@ int is_device_known(char *uid)
111 * @return 1 on success and 0 if no public key is given or if it has already 186 * @return 1 on success and 0 if no public key is given or if it has already
112 * been marked as connected previously. 187 * been marked as connected previously.
113 */ 188 */
114int store_device_public_key(char *uid, char *public_key) 189int store_device_public_key(char *uid, gnutls_datum_t public_key)
115{ 190{
116 191
117 if (NULL == public_key || is_device_known(uid)) 192 if (NULL == public_key.data || is_device_known(uid))
118 return 0; 193 return 0;
119 194
120 /* ensure config directory exists */ 195 /* ensure config directory exists */
@@ -124,15 +199,11 @@ int store_device_public_key(char *uid, char *public_key)
124 gchar *device_file = g_strconcat(uid, ".pem", NULL); 199 gchar *device_file = g_strconcat(uid, ".pem", NULL);
125 gchar *pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, device_file, NULL); 200 gchar *pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, device_file, NULL);
126 201
127 /* decode public key for storing */
128 gsize decoded_size;
129 gchar *data = g_base64_decode(public_key, &decoded_size);
130 /* store file */ 202 /* store file */
131 FILE *pFile = fopen(pem, "wb"); 203 FILE *pFile = fopen(pem, "wb");
132 fwrite(data, 1, decoded_size, pFile); 204 fwrite(public_key.data, 1, public_key.size, pFile);
133 fclose(pFile); 205 fclose(pFile);
134 g_free(pem); 206 g_free(pem);
135 g_free(data);
136 g_free(device_file); 207 g_free(device_file);
137 return 1; 208 return 1;
138} 209}
@@ -160,56 +231,220 @@ static int read_file_in_confdir(const char *file, gnutls_datum_t * data)
160 g_free(filepath); 231 g_free(filepath);
161 232
162 /* Add it to the gnutls_datnum_t structure */ 233 /* Add it to the gnutls_datnum_t structure */
163 data->data = content; 234 data->data = (uint8_t*) content;
164 data->size = size; 235 data->size = size;
165 236
166 return success; 237 return success;
167} 238}
168 239
169/** Read the root private key 240
170 * 241/** Private function which generate private keys and certificates.
171 * @param root_privkey A pointer to the appropriate gnutls structure
172 * 242 *
173 * @return 1 if the file was successfully read and 0 otherwise. 243 * @return IPHONE_E_SUCCESS if keys were successfully generated.
174 */ 244 */
175int get_root_private_key(gnutls_datum_t * root_privkey) 245static iphone_error_t gen_keys_and_cert(void)
176{ 246{
177 return read_file_in_confdir(LIBIPHONE_ROOT_PRIVKEY, root_privkey); 247 iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;
248 gnutls_x509_privkey_t root_privkey;
249 gnutls_x509_privkey_t host_privkey;
250 gnutls_x509_crt_t root_cert;
251 gnutls_x509_crt_t host_cert;
252
253 gnutls_global_deinit();
254 gnutls_global_init();
255
256 //use less secure random to speed up key generation
257 gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM);
258
259 gnutls_x509_privkey_init(&root_privkey);
260 gnutls_x509_privkey_init(&host_privkey);
261
262 gnutls_x509_crt_init(&root_cert);
263 gnutls_x509_crt_init(&host_cert);
264
265 /* generate root key */
266 gnutls_x509_privkey_generate(root_privkey, GNUTLS_PK_RSA, 2048, 0);
267 gnutls_x509_privkey_generate(host_privkey, GNUTLS_PK_RSA, 2048, 0);
268
269 /* generate certificates */
270 gnutls_x509_crt_set_key(root_cert, root_privkey);
271 gnutls_x509_crt_set_serial(root_cert, "\x00", 1);
272 gnutls_x509_crt_set_version(root_cert, 3);
273 gnutls_x509_crt_set_ca_status(root_cert, 1);
274 gnutls_x509_crt_set_activation_time(root_cert, time(NULL));
275 gnutls_x509_crt_set_expiration_time(root_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
276 gnutls_x509_crt_sign(root_cert, root_cert, root_privkey);
277
278
279 gnutls_x509_crt_set_key(host_cert, host_privkey);
280 gnutls_x509_crt_set_serial(host_cert, "\x00", 1);
281 gnutls_x509_crt_set_version(host_cert, 3);
282 gnutls_x509_crt_set_ca_status(host_cert, 0);
283 gnutls_x509_crt_set_key_usage(host_cert, GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_DIGITAL_SIGNATURE);
284 gnutls_x509_crt_set_activation_time(host_cert, time(NULL));
285 gnutls_x509_crt_set_expiration_time(host_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
286 gnutls_x509_crt_sign(host_cert, root_cert, root_privkey);
287
288 /* export to PEM format */
289 gnutls_datum_t root_key_pem = { NULL, 0 };
290 gnutls_datum_t host_key_pem = { NULL, 0 };
291
292 gnutls_x509_privkey_export(root_privkey, GNUTLS_X509_FMT_PEM, NULL, &root_key_pem.size);
293 gnutls_x509_privkey_export(host_privkey, GNUTLS_X509_FMT_PEM, NULL, &host_key_pem.size);
294
295 root_key_pem.data = gnutls_malloc(root_key_pem.size);
296 host_key_pem.data = gnutls_malloc(host_key_pem.size);
297
298 gnutls_x509_privkey_export(root_privkey, GNUTLS_X509_FMT_PEM, root_key_pem.data, &root_key_pem.size);
299 gnutls_x509_privkey_export(host_privkey, GNUTLS_X509_FMT_PEM, host_key_pem.data, &host_key_pem.size);
300
301 gnutls_datum_t root_cert_pem = { NULL, 0 };
302 gnutls_datum_t host_cert_pem = { NULL, 0 };
303
304 gnutls_x509_crt_export(root_cert, GNUTLS_X509_FMT_PEM, NULL, &root_cert_pem.size);
305 gnutls_x509_crt_export(host_cert, GNUTLS_X509_FMT_PEM, NULL, &host_cert_pem.size);
306
307 root_cert_pem.data = gnutls_malloc(root_cert_pem.size);
308 host_cert_pem.data = gnutls_malloc(host_cert_pem.size);
309
310 gnutls_x509_crt_export(root_cert, GNUTLS_X509_FMT_PEM, root_cert_pem.data, &root_cert_pem.size);
311 gnutls_x509_crt_export(host_cert, GNUTLS_X509_FMT_PEM, host_cert_pem.data, &host_cert_pem.size);
312
313 if (NULL != root_cert_pem.data && 0 != root_cert_pem.size &&
314 NULL != host_cert_pem.data && 0 != host_cert_pem.size)
315 ret = IPHONE_E_SUCCESS;
316
317 /* store values in config file */
318 init_config_file( &root_key_pem, &host_key_pem, &root_cert_pem, &host_cert_pem);
319
320 gnutls_free(root_key_pem.data);
321 gnutls_free(host_key_pem.data);
322 gnutls_free(root_cert_pem.data);
323 gnutls_free(host_cert_pem.data);
324
325 //restore gnutls env
326 gnutls_global_deinit();
327 gnutls_global_init();
328
329 return ret;
178} 330}
179 331
180/** Read the host private key 332/** Private function which import the given key into a gnutls structure.
181 * 333 *
182 * @param host_privkey A pointer to the appropriate gnutls structure 334 * @param key_name The filename of the private key to import.
335 * @param key the gnutls key structure.
183 * 336 *
184 * @return 1 if the file was successfully read and 0 otherwise. 337 * @return IPHONE_E_SUCCESS if the key was successfully imported.
185 */ 338 */
186int get_host_private_key(gnutls_datum_t * host_privkey) 339static iphone_error_t import_key(const char* key_name, gnutls_x509_privkey_t key)
187{ 340{
188 return read_file_in_confdir(LIBIPHONE_HOST_PRIVKEY, host_privkey); 341 iphone_error_t ret = IPHONE_E_INVALID_CONF;
342 gnutls_datum_t pem_key = { NULL, 0 };
343
344 if ( read_file_in_confdir(key_name, &pem_key) ) {
345 if (GNUTLS_E_SUCCESS == gnutls_x509_privkey_import(key, &pem_key, GNUTLS_X509_FMT_PEM))
346 ret = IPHONE_E_SUCCESS;
347 else
348 ret = IPHONE_E_SSL_ERROR;
349 }
350 gnutls_free(pem_key.data);
351 return ret;
189} 352}
190 353
191/** Read the root certificate 354/** Private function which import the given certificate into a gnutls structure.
192 * 355 *
193 * @param root_privkey A pointer to the appropriate gnutls structure 356 * @param crt_name The filename of the certificate to import.
357 * @param cert the gnutls certificate structure.
194 * 358 *
195 * @return 1 if the file was successfully read and 0 otherwise. 359 * @return IPHONE_E_SUCCESS if the certificate was successfully imported.
196 */ 360 */
197int get_root_certificate(gnutls_datum_t * root_cert) 361static iphone_error_t import_crt(const char* crt_name, gnutls_x509_crt_t cert)
198{ 362{
199 return read_file_in_confdir(LIBIPHONE_ROOT_CERTIF, root_cert); 363 iphone_error_t ret = IPHONE_E_INVALID_CONF;
364 gnutls_datum_t pem_cert = { NULL, 0 };
365
366 if ( read_file_in_confdir(crt_name, &pem_cert) ) {
367 if (GNUTLS_E_SUCCESS == gnutls_x509_crt_import(cert, &pem_cert, GNUTLS_X509_FMT_PEM))
368 ret = IPHONE_E_SUCCESS;
369 else
370 ret = IPHONE_E_SSL_ERROR;
371 }
372 gnutls_free(pem_cert.data);
373 return ret;
200} 374}
201 375
202/** Read the host certificate 376/** Function to retrieve host keys and certificates.
377 * This function trigger key generation if they do not exists yet or are invalid.
203 * 378 *
204 * @param root_privkey A pointer to the appropriate gnutls structure 379 * @note This function can take few seconds to complete (typically 5 seconds)
205 * 380 *
206 * @return 1 if the file was successfully read and 0 otherwise. 381 * @param root_privkey The root private key.
382 * @param root_crt The root certificate.
383 * @param host_privkey The host private key.
384 * @param host_crt The host certificate.
385 *
386 * @return IPHONE_E_SUCCESS if the keys and certificates were successfully retrieved.
207 */ 387 */
208int get_host_certificate(gnutls_datum_t * host_cert) 388iphone_error_t get_keys_and_certs(gnutls_x509_privkey_t root_privkey, gnutls_x509_crt_t root_crt, gnutls_x509_privkey_t host_privkey, gnutls_x509_crt_t host_crt)
209{ 389{
210 return read_file_in_confdir(LIBIPHONE_HOST_CERTIF, host_cert); 390 iphone_error_t ret = IPHONE_E_SUCCESS;
391
392 if (ret == IPHONE_E_SUCCESS)
393 ret = import_key(LIBIPHONE_ROOT_PRIVKEY, root_privkey);
394
395 if (ret == IPHONE_E_SUCCESS)
396 ret = import_key(LIBIPHONE_HOST_PRIVKEY, host_privkey);
397
398 if (ret == IPHONE_E_SUCCESS)
399 ret = import_crt(LIBIPHONE_ROOT_CERTIF, root_crt);
400
401 if (ret == IPHONE_E_SUCCESS)
402 ret = import_crt(LIBIPHONE_HOST_CERTIF, host_crt);
403
404
405 if (IPHONE_E_SUCCESS != ret) {
406 //we had problem reading or importing root cert
407 //try with a new ones.
408 ret = gen_keys_and_cert();
409
410 if (ret == IPHONE_E_SUCCESS)
411 ret = import_key(LIBIPHONE_ROOT_PRIVKEY, root_privkey);
412
413 if (ret == IPHONE_E_SUCCESS)
414 ret = import_key(LIBIPHONE_HOST_PRIVKEY, host_privkey);
415
416 if (ret == IPHONE_E_SUCCESS)
417 ret = import_crt(LIBIPHONE_ROOT_CERTIF, root_crt);
418
419 if (ret == IPHONE_E_SUCCESS)
420 ret = import_crt(LIBIPHONE_HOST_CERTIF, host_crt);
421 }
422
423 return ret;
211} 424}
212 425
426/** Function to retrieve certificates encoded in PEM format.
427 *
428 * @param pem_root_cert The root certificate.
429 * @param pem_host_cert The host certificate.
430 *
431 * @return IPHONE_E_SUCCESS if the certificates were successfully retrieved.
432 */
433iphone_error_t get_certs_as_pem(gnutls_datum_t *pem_root_cert, gnutls_datum_t *pem_host_cert)
434{
435 iphone_error_t ret = IPHONE_E_INVALID_CONF;
436
437 if ( !pem_root_cert || !pem_host_cert)
438 return IPHONE_E_INVALID_ARG;
439
440 if ( read_file_in_confdir(LIBIPHONE_ROOT_CERTIF, pem_root_cert) && read_file_in_confdir(LIBIPHONE_HOST_CERTIF, pem_host_cert))
441 ret = IPHONE_E_SUCCESS;
442 else {
443 g_free(pem_root_cert->data);
444 g_free(pem_host_cert->data);
445 }
446 return ret;
447}
213/** Create and save a configuration file containing the given data. 448/** Create and save a configuration file containing the given data.
214 * 449 *
215 * @note: All fields must specified and be non-null 450 * @note: All fields must specified and be non-null
@@ -222,41 +457,18 @@ int get_host_certificate(gnutls_datum_t * host_cert)
222 * 457 *
223 * @return 1 on success and 0 otherwise. 458 * @return 1 on success and 0 otherwise.
224 */ 459 */
225int init_config_file(char *host_id, gnutls_datum_t * root_key, gnutls_datum_t * host_key, gnutls_datum_t * root_cert, 460int init_config_file( gnutls_datum_t * root_key, gnutls_datum_t * host_key, gnutls_datum_t * root_cert,
226 gnutls_datum_t * host_cert) 461 gnutls_datum_t * host_cert)
227{ 462{
228 FILE *pFile; 463 FILE *pFile;
229 gchar *pem; 464 gchar *pem;
230 GKeyFile *key_file;
231 gsize length;
232 gchar *buf, *config_file;
233 GIOChannel *file;
234 465
235 if (!host_id || !root_key || !host_key || !root_cert || !host_cert) 466 if (!root_key || !host_key || !root_cert || !host_cert)
236 return 0; 467 return 0;
237 468
238 /* Make sure config directory exists */ 469 /* Make sure config directory exists */
239 create_config_dir(); 470 create_config_dir();
240 471
241 /* Now parse file to get the HostID */
242 key_file = g_key_file_new();
243
244 /* Store in config file */
245 log_debug_msg("init_config_file(): setting hostID to %s\n", host_id);
246 g_key_file_set_value(key_file, "Global", "HostID", host_id);
247
248 /* Write config file on disk */
249 buf = g_key_file_to_data(key_file, &length, NULL);
250 config_file =
251 g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_CONF_FILE, NULL);
252 file = g_io_channel_new_file(config_file, "w", NULL);
253 g_free(config_file);
254 g_io_channel_write_chars(file, buf, length, NULL, NULL);
255 g_io_channel_shutdown(file, TRUE, NULL);
256 g_io_channel_unref(file);
257
258 g_key_file_free(key_file);
259
260 /* Now write keys and certificates to disk */ 472 /* Now write keys and certificates to disk */
261 pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_ROOT_PRIVKEY, NULL); 473 pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_ROOT_PRIVKEY, NULL);
262 pFile = fopen(pem, "wb"); 474 pFile = fopen(pem, "wb");