diff options
Diffstat (limited to 'common/userpref.c')
-rw-r--r-- | common/userpref.c | 1171 |
1 files changed, 1171 insertions, 0 deletions
diff --git a/common/userpref.c b/common/userpref.c new file mode 100644 index 0000000..48bcfcb --- /dev/null +++ b/common/userpref.c | |||
@@ -0,0 +1,1171 @@ | |||
1 | /* | ||
2 | * userpref.c | ||
3 | * contains methods to access user specific certificates IDs and more. | ||
4 | * | ||
5 | * Copyright (c) 2013-2021 Nikias Bassen, All Rights Reserved. | ||
6 | * Copyright (c) 2013-2014 Martin Szulecki All Rights Reserved. | ||
7 | * Copyright (c) 2008 Jonathan Beck All Rights Reserved. | ||
8 | * | ||
9 | * This library is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU Lesser General Public | ||
11 | * License as published by the Free Software Foundation; either | ||
12 | * version 2.1 of the License, or (at your option) any later version. | ||
13 | * | ||
14 | * This library is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * Lesser General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU Lesser General Public | ||
20 | * License along with this library; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | */ | ||
23 | |||
24 | #ifdef HAVE_CONFIG_H | ||
25 | #include <config.h> | ||
26 | #endif | ||
27 | |||
28 | #include <stdio.h> | ||
29 | #include <stdint.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | #ifdef HAVE_SYS_TYPES_H | ||
33 | #include <sys/types.h> | ||
34 | #endif | ||
35 | #ifndef WIN32 | ||
36 | #include <pwd.h> | ||
37 | #endif | ||
38 | #include <unistd.h> | ||
39 | #include <usbmuxd.h> | ||
40 | #if defined(HAVE_OPENSSL) | ||
41 | #include <openssl/bn.h> | ||
42 | #include <openssl/pem.h> | ||
43 | #include <openssl/rsa.h> | ||
44 | #include <openssl/x509.h> | ||
45 | #include <openssl/x509v3.h> | ||
46 | #if OPENSSL_VERSION_NUMBER < 0x1010000fL || \ | ||
47 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x20700000L)) | ||
48 | #define X509_set1_notBefore X509_set_notBefore | ||
49 | #define X509_set1_notAfter X509_set_notAfter | ||
50 | #endif | ||
51 | #elif defined(HAVE_GNUTLS) | ||
52 | #include <gnutls/gnutls.h> | ||
53 | #include <gnutls/crypto.h> | ||
54 | #include <gnutls/x509.h> | ||
55 | #include <gcrypt.h> | ||
56 | #include <libtasn1.h> | ||
57 | #elif defined(HAVE_MBEDTLS) | ||
58 | #include <mbedtls/ssl.h> | ||
59 | #include <mbedtls/entropy.h> | ||
60 | #include <mbedtls/ctr_drbg.h> | ||
61 | #include <mbedtls/asn1write.h> | ||
62 | #include <mbedtls/oid.h> | ||
63 | #else | ||
64 | #error No supported TLS/SSL library enabled | ||
65 | #endif | ||
66 | |||
67 | #include <dirent.h> | ||
68 | #include <libgen.h> | ||
69 | #include <sys/stat.h> | ||
70 | #include <errno.h> | ||
71 | |||
72 | #ifdef WIN32 | ||
73 | #include <shlobj.h> | ||
74 | #endif | ||
75 | |||
76 | #ifndef ETIMEDOUT | ||
77 | #define ETIMEDOUT 138 | ||
78 | #endif | ||
79 | |||
80 | #include <libimobiledevice-glue/utils.h> | ||
81 | |||
82 | #include "userpref.h" | ||
83 | #include "debug.h" | ||
84 | |||
85 | #if defined(HAVE_GNUTLS) | ||
86 | const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { | ||
87 | {"PKCS1", 536872976, 0}, | ||
88 | {0, 1073741836, 0}, | ||
89 | {"RSAPublicKey", 536870917, 0}, | ||
90 | {"modulus", 1073741827, 0}, | ||
91 | {"publicExponent", 3, 0}, | ||
92 | {0, 0, 0} | ||
93 | }; | ||
94 | #endif | ||
95 | |||
96 | #ifdef WIN32 | ||
97 | #define DIR_SEP '\\' | ||
98 | #define DIR_SEP_S "\\" | ||
99 | #else | ||
100 | #define DIR_SEP '/' | ||
101 | #define DIR_SEP_S "/" | ||
102 | #endif | ||
103 | |||
104 | #define USERPREF_CONFIG_EXTENSION ".plist" | ||
105 | |||
106 | #ifdef WIN32 | ||
107 | #define USERPREF_CONFIG_DIR "Apple"DIR_SEP_S"Lockdown" | ||
108 | #else | ||
109 | #define USERPREF_CONFIG_DIR "lockdown" | ||
110 | #endif | ||
111 | |||
112 | #define USERPREF_CONFIG_FILE "SystemConfiguration"USERPREF_CONFIG_EXTENSION | ||
113 | |||
114 | static char *__config_dir = NULL; | ||
115 | |||
116 | #ifdef WIN32 | ||
117 | static char *userpref_utf16_to_utf8(wchar_t *unistr, long len, long *items_read, long *items_written) | ||
118 | { | ||
119 | if (!unistr || (len <= 0)) return NULL; | ||
120 | char *outbuf = (char*)malloc(3*(len+1)); | ||
121 | int p = 0; | ||
122 | int i = 0; | ||
123 | |||
124 | wchar_t wc; | ||
125 | |||
126 | while (i < len) { | ||
127 | wc = unistr[i++]; | ||
128 | if (wc >= 0x800) { | ||
129 | outbuf[p++] = (char)(0xE0 + ((wc >> 12) & 0xF)); | ||
130 | outbuf[p++] = (char)(0x80 + ((wc >> 6) & 0x3F)); | ||
131 | outbuf[p++] = (char)(0x80 + (wc & 0x3F)); | ||
132 | } else if (wc >= 0x80) { | ||
133 | outbuf[p++] = (char)(0xC0 + ((wc >> 6) & 0x1F)); | ||
134 | outbuf[p++] = (char)(0x80 + (wc & 0x3F)); | ||
135 | } else { | ||
136 | outbuf[p++] = (char)(wc & 0x7F); | ||
137 | } | ||
138 | } | ||
139 | if (items_read) { | ||
140 | *items_read = i; | ||
141 | } | ||
142 | if (items_written) { | ||
143 | *items_written = p; | ||
144 | } | ||
145 | outbuf[p] = 0; | ||
146 | |||
147 | return outbuf; | ||
148 | } | ||
149 | #endif | ||
150 | |||
151 | const char *userpref_get_config_dir() | ||
152 | { | ||
153 | char *base_config_dir = NULL; | ||
154 | |||
155 | if (__config_dir) | ||
156 | return __config_dir; | ||
157 | |||
158 | #ifdef WIN32 | ||
159 | wchar_t path[MAX_PATH+1]; | ||
160 | HRESULT hr; | ||
161 | LPITEMIDLIST pidl = NULL; | ||
162 | BOOL b = FALSE; | ||
163 | |||
164 | hr = SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_APPDATA, &pidl); | ||
165 | if (hr == S_OK) { | ||
166 | b = SHGetPathFromIDListW (pidl, path); | ||
167 | if (b) { | ||
168 | base_config_dir = userpref_utf16_to_utf8 (path, wcslen(path), NULL, NULL); | ||
169 | CoTaskMemFree (pidl); | ||
170 | } | ||
171 | } | ||
172 | #else | ||
173 | #ifdef __APPLE__ | ||
174 | base_config_dir = strdup("/var/db"); | ||
175 | #else | ||
176 | base_config_dir = strdup("/var/lib"); | ||
177 | #endif | ||
178 | #endif | ||
179 | __config_dir = string_concat(base_config_dir, DIR_SEP_S, USERPREF_CONFIG_DIR, NULL); | ||
180 | |||
181 | if (__config_dir) { | ||
182 | int i = strlen(__config_dir)-1; | ||
183 | while ((i > 0) && (__config_dir[i] == DIR_SEP)) { | ||
184 | __config_dir[i--] = '\0'; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | free(base_config_dir); | ||
189 | |||
190 | debug_info("initialized config_dir to %s", __config_dir); | ||
191 | |||
192 | return __config_dir; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * Reads the SystemBUID from a previously generated configuration file. | ||
197 | * | ||
198 | * @note It is the responsibility of the calling function to free the returned system_buid. | ||
199 | * @param system_buid A pointer that will be set to a newly allocated string containing the | ||
200 | * SystemBUID upon successful return. | ||
201 | * @return 0 if the SystemBUID has been successfully retrieved or < 0 otherwise. | ||
202 | */ | ||
203 | int userpref_read_system_buid(char **system_buid) | ||
204 | { | ||
205 | int res = usbmuxd_read_buid(system_buid); | ||
206 | if (res == 0) { | ||
207 | debug_info("using %s as %s", *system_buid, USERPREF_SYSTEM_BUID_KEY); | ||
208 | } else { | ||
209 | debug_info("could not read system buid, error %d", res); | ||
210 | } | ||
211 | return res; | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * Fills a list with UDIDs of devices that have been connected to this | ||
216 | * system before, i.e. for which a public key file exists. | ||
217 | * | ||
218 | * @param list A pointer to a char** initially pointing to NULL that will | ||
219 | * hold a newly allocated list of UDIDs upon successful return. | ||
220 | * The caller is responsible for freeing the memory. Note that if | ||
221 | * no public key file was found the list has to be freed too as it | ||
222 | * points to a terminating NULL element. | ||
223 | * @param count The number of UDIDs found. This parameter can be NULL if it | ||
224 | * is not required. | ||
225 | * | ||
226 | * @return USERPREF_E_SUCCESS on success, or USERPREF_E_INVALID_ARG if the | ||
227 | * list parameter is not pointing to NULL. | ||
228 | */ | ||
229 | userpref_error_t userpref_get_paired_udids(char ***list, unsigned int *count) | ||
230 | { | ||
231 | DIR *config_dir; | ||
232 | const char *config_path = NULL; | ||
233 | unsigned int found = 0; | ||
234 | |||
235 | if (!list || (list && *list)) { | ||
236 | debug_info("ERROR: The list parameter needs to point to NULL!"); | ||
237 | return USERPREF_E_INVALID_ARG; | ||
238 | } | ||
239 | |||
240 | if (count) { | ||
241 | *count = 0; | ||
242 | } | ||
243 | *list = (char**)malloc(sizeof(char*)); | ||
244 | |||
245 | config_path = userpref_get_config_dir(); | ||
246 | config_dir = opendir(config_path); | ||
247 | if (config_dir) { | ||
248 | struct dirent *entry; | ||
249 | while ((entry = readdir(config_dir))) { | ||
250 | if (strcmp(entry->d_name, USERPREF_CONFIG_FILE) == 0) { | ||
251 | /* ignore SystemConfiguration.plist */ | ||
252 | continue; | ||
253 | } | ||
254 | char *ext = strrchr(entry->d_name, '.'); | ||
255 | if (ext && (strcmp(ext, USERPREF_CONFIG_EXTENSION) == 0)) { | ||
256 | size_t len = strlen(entry->d_name) - strlen(USERPREF_CONFIG_EXTENSION); | ||
257 | char **newlist = (char**)realloc(*list, sizeof(char*) * (found+2)); | ||
258 | if (!newlist) { | ||
259 | fprintf(stderr, "ERROR: Out of memory\n"); | ||
260 | break; | ||
261 | } | ||
262 | *list = newlist; | ||
263 | char *tmp = (char*)malloc(len+1); | ||
264 | if (tmp) { | ||
265 | strncpy(tmp, entry->d_name, len); | ||
266 | tmp[len] = '\0'; | ||
267 | } | ||
268 | (*list)[found] = tmp; | ||
269 | if (!tmp) { | ||
270 | fprintf(stderr, "ERROR: Out of memory\n"); | ||
271 | break; | ||
272 | } | ||
273 | found++; | ||
274 | } | ||
275 | } | ||
276 | closedir(config_dir); | ||
277 | } | ||
278 | (*list)[found] = NULL; | ||
279 | |||
280 | if (count) { | ||
281 | *count = found; | ||
282 | } | ||
283 | |||
284 | return USERPREF_E_SUCCESS; | ||
285 | } | ||
286 | |||
287 | /** | ||
288 | * Save a pair record for a device. | ||
289 | * | ||
290 | * @param udid The device UDID as given by the device | ||
291 | * @param device_id The usbmux device id (handle) of the connected device, or 0 | ||
292 | * @param pair_record The pair record to save | ||
293 | * | ||
294 | * @return 1 on success and 0 if no device record is given or if it has already | ||
295 | * been saved previously. | ||
296 | */ | ||
297 | userpref_error_t userpref_save_pair_record(const char *udid, uint32_t device_id, plist_t pair_record) | ||
298 | { | ||
299 | char* record_data = NULL; | ||
300 | uint32_t record_size = 0; | ||
301 | |||
302 | plist_to_bin(pair_record, &record_data, &record_size); | ||
303 | |||
304 | int res = usbmuxd_save_pair_record_with_device_id(udid, device_id, record_data, record_size); | ||
305 | |||
306 | free(record_data); | ||
307 | |||
308 | return res == 0 ? USERPREF_E_SUCCESS: USERPREF_E_UNKNOWN_ERROR; | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * Read a pair record for a device. | ||
313 | * | ||
314 | * @param udid The device UDID as given by the device | ||
315 | * @param pair_record The pair record to read | ||
316 | * | ||
317 | * @return USERPREF_E_SUCCESS on success, | ||
318 | * USERPREF_E_NOENT if no pairing record was found, | ||
319 | * USERPREF_E_READ_ERROR if retrieving the pairing record from usbmuxd failed, | ||
320 | * or USERPREF_E_INVALID_CONF otherwise. | ||
321 | */ | ||
322 | userpref_error_t userpref_read_pair_record(const char *udid, plist_t *pair_record) | ||
323 | { | ||
324 | char* record_data = NULL; | ||
325 | uint32_t record_size = 0; | ||
326 | |||
327 | int res = usbmuxd_read_pair_record(udid, &record_data, &record_size); | ||
328 | if (res < 0) { | ||
329 | free(record_data); | ||
330 | switch (-res) { | ||
331 | case ENOENT: | ||
332 | return USERPREF_E_NOENT; | ||
333 | case ETIMEDOUT: | ||
334 | return USERPREF_E_READ_ERROR; | ||
335 | default: | ||
336 | return USERPREF_E_INVALID_CONF; | ||
337 | } | ||
338 | } | ||
339 | |||
340 | *pair_record = NULL; | ||
341 | plist_from_memory(record_data, record_size, pair_record, NULL); | ||
342 | free(record_data); | ||
343 | |||
344 | if (!*pair_record) { | ||
345 | debug_info("Failed to parse pairing record"); | ||
346 | return USERPREF_E_INVALID_CONF; | ||
347 | } | ||
348 | return USERPREF_E_SUCCESS; | ||
349 | } | ||
350 | |||
351 | /** | ||
352 | * Remove the pairing record stored for a device from this host. | ||
353 | * | ||
354 | * @param udid The udid of the device | ||
355 | * | ||
356 | * @return USERPREF_E_SUCCESS on success. | ||
357 | */ | ||
358 | userpref_error_t userpref_delete_pair_record(const char *udid) | ||
359 | { | ||
360 | int res = usbmuxd_delete_pair_record(udid); | ||
361 | |||
362 | return res == 0 ? USERPREF_E_SUCCESS: USERPREF_E_UNKNOWN_ERROR; | ||
363 | } | ||
364 | |||
365 | #if defined(HAVE_OPENSSL) | ||
366 | static int X509_add_ext_helper(X509 *cert, int nid, char *value) | ||
367 | { | ||
368 | X509_EXTENSION *ex; | ||
369 | X509V3_CTX ctx; | ||
370 | |||
371 | /* No configuration database */ | ||
372 | X509V3_set_ctx_nodb(&ctx); | ||
373 | |||
374 | X509V3_set_ctx(&ctx, NULL, cert, NULL, NULL, 0); | ||
375 | ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); | ||
376 | if (!ex) { | ||
377 | debug_info("ERROR: X509V3_EXT_conf_nid(%d, %s) failed", nid, value); | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | X509_add_ext(cert, ex, -1); | ||
382 | X509_EXTENSION_free(ex); | ||
383 | |||
384 | return 1; | ||
385 | } | ||
386 | #elif defined(HAVE_MBEDTLS) | ||
387 | static int _mbedtls_x509write_crt_set_basic_constraints_critical(mbedtls_x509write_cert *ctx, int is_ca, int max_pathlen) | ||
388 | { | ||
389 | int ret; | ||
390 | unsigned char buf[9]; | ||
391 | unsigned char *c = buf + sizeof(buf); | ||
392 | size_t len = 0; | ||
393 | |||
394 | memset( buf, 0, sizeof(buf) ); | ||
395 | |||
396 | if (is_ca && max_pathlen > 127) | ||
397 | return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); | ||
398 | |||
399 | if (is_ca) { | ||
400 | if (max_pathlen >= 0) { | ||
401 | MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, buf, max_pathlen ) ); | ||
402 | } | ||
403 | MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_bool( &c, buf, 1 ) ); | ||
404 | } | ||
405 | |||
406 | MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) ); | ||
407 | MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ); | ||
408 | |||
409 | return mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_BASIC_CONSTRAINTS, MBEDTLS_OID_SIZE( MBEDTLS_OID_BASIC_CONSTRAINTS ), 1, buf + sizeof(buf) - len, len ); | ||
410 | } | ||
411 | #endif | ||
412 | |||
413 | /** | ||
414 | * Private function to generate required private keys and certificates. | ||
415 | * | ||
416 | * @param pair_record a #PLIST_DICT that will be filled with the keys | ||
417 | * and certificates | ||
418 | * @param public_key the public key to use (device public key) | ||
419 | * | ||
420 | * @return 1 if keys were successfully generated, 0 otherwise | ||
421 | */ | ||
422 | userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_data_t public_key) | ||
423 | { | ||
424 | userpref_error_t ret = USERPREF_E_SSL_ERROR; | ||
425 | |||
426 | key_data_t dev_cert_pem = { NULL, 0 }; | ||
427 | key_data_t root_key_pem = { NULL, 0 }; | ||
428 | key_data_t root_cert_pem = { NULL, 0 }; | ||
429 | key_data_t host_key_pem = { NULL, 0 }; | ||
430 | key_data_t host_cert_pem = { NULL, 0 }; | ||
431 | |||
432 | if (!pair_record || !public_key.data) | ||
433 | return USERPREF_E_INVALID_ARG; | ||
434 | |||
435 | debug_info("Generating keys and certificates..."); | ||
436 | |||
437 | #if defined(HAVE_OPENSSL) | ||
438 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
439 | EVP_PKEY* root_pkey = EVP_RSA_gen(2048); | ||
440 | EVP_PKEY* host_pkey = EVP_RSA_gen(2048); | ||
441 | #else | ||
442 | BIGNUM *e = BN_new(); | ||
443 | RSA* root_keypair = RSA_new(); | ||
444 | RSA* host_keypair = RSA_new(); | ||
445 | |||
446 | BN_set_word(e, 65537); | ||
447 | |||
448 | RSA_generate_key_ex(root_keypair, 2048, e, NULL); | ||
449 | RSA_generate_key_ex(host_keypair, 2048, e, NULL); | ||
450 | |||
451 | BN_free(e); | ||
452 | |||
453 | EVP_PKEY* root_pkey = EVP_PKEY_new(); | ||
454 | EVP_PKEY_assign_RSA(root_pkey, root_keypair); | ||
455 | |||
456 | EVP_PKEY* host_pkey = EVP_PKEY_new(); | ||
457 | EVP_PKEY_assign_RSA(host_pkey, host_keypair); | ||
458 | #endif | ||
459 | |||
460 | /* generate root certificate */ | ||
461 | X509* root_cert = X509_new(); | ||
462 | { | ||
463 | /* set serial number */ | ||
464 | ASN1_INTEGER* sn = ASN1_INTEGER_new(); | ||
465 | ASN1_INTEGER_set(sn, 0); | ||
466 | X509_set_serialNumber(root_cert, sn); | ||
467 | ASN1_INTEGER_free(sn); | ||
468 | |||
469 | /* set version */ | ||
470 | X509_set_version(root_cert, 2); | ||
471 | |||
472 | /* set x509v3 basic constraints */ | ||
473 | X509_add_ext_helper(root_cert, NID_basic_constraints, (char*)"critical,CA:TRUE"); | ||
474 | |||
475 | /* set key validity */ | ||
476 | ASN1_TIME* asn1time = ASN1_TIME_new(); | ||
477 | ASN1_TIME_set(asn1time, time(NULL)); | ||
478 | X509_set1_notBefore(root_cert, asn1time); | ||
479 | ASN1_TIME_set(asn1time, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
480 | X509_set1_notAfter(root_cert, asn1time); | ||
481 | ASN1_TIME_free(asn1time); | ||
482 | |||
483 | /* use root public key for root cert */ | ||
484 | X509_set_pubkey(root_cert, root_pkey); | ||
485 | |||
486 | /* sign root cert with root private key */ | ||
487 | X509_sign(root_cert, root_pkey, EVP_sha1()); | ||
488 | } | ||
489 | |||
490 | /* create host certificate */ | ||
491 | X509* host_cert = X509_new(); | ||
492 | { | ||
493 | /* set serial number */ | ||
494 | ASN1_INTEGER* sn = ASN1_INTEGER_new(); | ||
495 | ASN1_INTEGER_set(sn, 0); | ||
496 | X509_set_serialNumber(host_cert, sn); | ||
497 | ASN1_INTEGER_free(sn); | ||
498 | |||
499 | /* set version */ | ||
500 | X509_set_version(host_cert, 2); | ||
501 | |||
502 | /* set x509v3 basic constraints */ | ||
503 | X509_add_ext_helper(host_cert, NID_basic_constraints, (char*)"critical,CA:FALSE"); | ||
504 | |||
505 | /* set x509v3 key usage */ | ||
506 | X509_add_ext_helper(host_cert, NID_key_usage, (char*)"critical,digitalSignature,keyEncipherment"); | ||
507 | |||
508 | /* set key validity */ | ||
509 | ASN1_TIME* asn1time = ASN1_TIME_new(); | ||
510 | ASN1_TIME_set(asn1time, time(NULL)); | ||
511 | X509_set1_notBefore(host_cert, asn1time); | ||
512 | ASN1_TIME_set(asn1time, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
513 | X509_set1_notAfter(host_cert, asn1time); | ||
514 | ASN1_TIME_free(asn1time); | ||
515 | |||
516 | /* use host public key for host cert */ | ||
517 | X509_set_pubkey(host_cert, host_pkey); | ||
518 | |||
519 | /* sign host cert with root private key */ | ||
520 | X509_sign(host_cert, root_pkey, EVP_sha1()); | ||
521 | } | ||
522 | |||
523 | if (root_cert && root_pkey && host_cert && host_pkey) { | ||
524 | BIO* membp; | ||
525 | char *bdata; | ||
526 | |||
527 | membp = BIO_new(BIO_s_mem()); | ||
528 | if (PEM_write_bio_X509(membp, root_cert) > 0) { | ||
529 | root_cert_pem.size = BIO_get_mem_data(membp, &bdata); | ||
530 | root_cert_pem.data = (unsigned char*)malloc(root_cert_pem.size); | ||
531 | if (root_cert_pem.data) { | ||
532 | memcpy(root_cert_pem.data, bdata, root_cert_pem.size); | ||
533 | } | ||
534 | BIO_free(membp); | ||
535 | membp = NULL; | ||
536 | } | ||
537 | membp = BIO_new(BIO_s_mem()); | ||
538 | if (PEM_write_bio_PrivateKey(membp, root_pkey, NULL, NULL, 0, 0, NULL) > 0) { | ||
539 | root_key_pem.size = BIO_get_mem_data(membp, &bdata); | ||
540 | root_key_pem.data = (unsigned char*)malloc(root_key_pem.size); | ||
541 | if (root_key_pem.data) { | ||
542 | memcpy(root_key_pem.data, bdata, root_key_pem.size); | ||
543 | } | ||
544 | BIO_free(membp); | ||
545 | membp = NULL; | ||
546 | } | ||
547 | membp = BIO_new(BIO_s_mem()); | ||
548 | if (PEM_write_bio_X509(membp, host_cert) > 0) { | ||
549 | host_cert_pem.size = BIO_get_mem_data(membp, &bdata); | ||
550 | host_cert_pem.data = (unsigned char*)malloc(host_cert_pem.size); | ||
551 | if (host_cert_pem.data) { | ||
552 | memcpy(host_cert_pem.data, bdata, host_cert_pem.size); | ||
553 | } | ||
554 | BIO_free(membp); | ||
555 | membp = NULL; | ||
556 | } | ||
557 | membp = BIO_new(BIO_s_mem()); | ||
558 | if (PEM_write_bio_PrivateKey(membp, host_pkey, NULL, NULL, 0, 0, NULL) > 0) { | ||
559 | host_key_pem.size = BIO_get_mem_data(membp, &bdata); | ||
560 | host_key_pem.data = (unsigned char*)malloc(host_key_pem.size); | ||
561 | if (host_key_pem.data) { | ||
562 | memcpy(host_key_pem.data, bdata, host_key_pem.size); | ||
563 | } | ||
564 | BIO_free(membp); | ||
565 | membp = NULL; | ||
566 | } | ||
567 | } | ||
568 | |||
569 | EVP_PKEY *pubkey = NULL; | ||
570 | { | ||
571 | BIO *membp = BIO_new_mem_buf(public_key.data, public_key.size); | ||
572 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
573 | if (!PEM_read_bio_PUBKEY(membp, &pubkey, NULL, NULL)) { | ||
574 | debug_info("WARNING: Could not read public key"); | ||
575 | } | ||
576 | #else | ||
577 | RSA *rsa_pubkey = NULL; | ||
578 | if (!PEM_read_bio_RSAPublicKey(membp, &rsa_pubkey, NULL, NULL)) { | ||
579 | debug_info("WARNING: Could not read public key"); | ||
580 | } else { | ||
581 | pubkey = EVP_PKEY_new(); | ||
582 | EVP_PKEY_assign_RSA(pubkey, rsa_pubkey); | ||
583 | } | ||
584 | #endif | ||
585 | BIO_free(membp); | ||
586 | } | ||
587 | |||
588 | X509* dev_cert = X509_new(); | ||
589 | if (pubkey && dev_cert) { | ||
590 | /* generate device certificate */ | ||
591 | ASN1_INTEGER* sn = ASN1_INTEGER_new(); | ||
592 | ASN1_INTEGER_set(sn, 0); | ||
593 | X509_set_serialNumber(dev_cert, sn); | ||
594 | ASN1_INTEGER_free(sn); | ||
595 | X509_set_version(dev_cert, 2); | ||
596 | |||
597 | X509_add_ext_helper(dev_cert, NID_basic_constraints, (char*)"critical,CA:FALSE"); | ||
598 | |||
599 | ASN1_TIME* asn1time = ASN1_TIME_new(); | ||
600 | ASN1_TIME_set(asn1time, time(NULL)); | ||
601 | X509_set1_notBefore(dev_cert, asn1time); | ||
602 | ASN1_TIME_set(asn1time, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
603 | X509_set1_notAfter(dev_cert, asn1time); | ||
604 | ASN1_TIME_free(asn1time); | ||
605 | |||
606 | X509_set_pubkey(dev_cert, pubkey); | ||
607 | |||
608 | X509_add_ext_helper(dev_cert, NID_subject_key_identifier, (char*)"hash"); | ||
609 | X509_add_ext_helper(dev_cert, NID_key_usage, (char*)"critical,digitalSignature,keyEncipherment"); | ||
610 | |||
611 | /* sign device certificate with root private key */ | ||
612 | if (X509_sign(dev_cert, root_pkey, EVP_sha1())) { | ||
613 | /* if signing succeeded, export in PEM format */ | ||
614 | BIO* membp = BIO_new(BIO_s_mem()); | ||
615 | if (PEM_write_bio_X509(membp, dev_cert) > 0) { | ||
616 | char *bdata = NULL; | ||
617 | dev_cert_pem.size = BIO_get_mem_data(membp, &bdata); | ||
618 | dev_cert_pem.data = (unsigned char*)malloc(dev_cert_pem.size); | ||
619 | if (dev_cert_pem.data) { | ||
620 | memcpy(dev_cert_pem.data, bdata, dev_cert_pem.size); | ||
621 | } | ||
622 | BIO_free(membp); | ||
623 | membp = NULL; | ||
624 | } | ||
625 | } else { | ||
626 | debug_info("ERROR: Signing device certificate with root private key failed!"); | ||
627 | } | ||
628 | } | ||
629 | |||
630 | X509_free(dev_cert); | ||
631 | |||
632 | EVP_PKEY_free(pubkey); | ||
633 | EVP_PKEY_free(root_pkey); | ||
634 | EVP_PKEY_free(host_pkey); | ||
635 | |||
636 | X509_free(host_cert); | ||
637 | X509_free(root_cert); | ||
638 | #elif defined(HAVE_GNUTLS) | ||
639 | gnutls_x509_privkey_t root_privkey; | ||
640 | gnutls_x509_crt_t root_cert; | ||
641 | gnutls_x509_privkey_t host_privkey; | ||
642 | gnutls_x509_crt_t host_cert; | ||
643 | |||
644 | /* use less secure random to speed up key generation */ | ||
645 | gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM); | ||
646 | |||
647 | gnutls_x509_privkey_init(&root_privkey); | ||
648 | gnutls_x509_privkey_init(&host_privkey); | ||
649 | |||
650 | gnutls_x509_crt_init(&root_cert); | ||
651 | gnutls_x509_crt_init(&host_cert); | ||
652 | |||
653 | /* generate root key */ | ||
654 | gnutls_x509_privkey_generate(root_privkey, GNUTLS_PK_RSA, 2048, 0); | ||
655 | gnutls_x509_privkey_generate(host_privkey, GNUTLS_PK_RSA, 2048, 0); | ||
656 | |||
657 | /* generate certificates */ | ||
658 | gnutls_x509_crt_set_key(root_cert, root_privkey); | ||
659 | gnutls_x509_crt_set_serial(root_cert, "\x01", 1); | ||
660 | gnutls_x509_crt_set_version(root_cert, 3); | ||
661 | gnutls_x509_crt_set_ca_status(root_cert, 1); | ||
662 | gnutls_x509_crt_set_activation_time(root_cert, time(NULL)); | ||
663 | gnutls_x509_crt_set_expiration_time(root_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
664 | gnutls_x509_crt_sign2(root_cert, root_cert, root_privkey, GNUTLS_DIG_SHA1, 0); | ||
665 | |||
666 | gnutls_x509_crt_set_key(host_cert, host_privkey); | ||
667 | gnutls_x509_crt_set_serial(host_cert, "\x01", 1); | ||
668 | gnutls_x509_crt_set_version(host_cert, 3); | ||
669 | gnutls_x509_crt_set_ca_status(host_cert, 0); | ||
670 | gnutls_x509_crt_set_key_usage(host_cert, GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_DIGITAL_SIGNATURE); | ||
671 | gnutls_x509_crt_set_activation_time(host_cert, time(NULL)); | ||
672 | gnutls_x509_crt_set_expiration_time(host_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
673 | gnutls_x509_crt_sign2(host_cert, root_cert, root_privkey, GNUTLS_DIG_SHA1, 0); | ||
674 | |||
675 | /* export to PEM format */ | ||
676 | size_t root_key_export_size = 0; | ||
677 | size_t host_key_export_size = 0; | ||
678 | |||
679 | gnutls_x509_privkey_export(root_privkey, GNUTLS_X509_FMT_PEM, NULL, &root_key_export_size); | ||
680 | gnutls_x509_privkey_export(host_privkey, GNUTLS_X509_FMT_PEM, NULL, &host_key_export_size); | ||
681 | |||
682 | root_key_pem.data = gnutls_malloc(root_key_export_size); | ||
683 | host_key_pem.data = gnutls_malloc(host_key_export_size); | ||
684 | |||
685 | gnutls_x509_privkey_export(root_privkey, GNUTLS_X509_FMT_PEM, root_key_pem.data, &root_key_export_size); | ||
686 | root_key_pem.size = root_key_export_size; | ||
687 | gnutls_x509_privkey_export(host_privkey, GNUTLS_X509_FMT_PEM, host_key_pem.data, &host_key_export_size); | ||
688 | host_key_pem.size = host_key_export_size; | ||
689 | |||
690 | size_t root_cert_export_size = 0; | ||
691 | size_t host_cert_export_size = 0; | ||
692 | |||
693 | gnutls_x509_crt_export(root_cert, GNUTLS_X509_FMT_PEM, NULL, &root_cert_export_size); | ||
694 | gnutls_x509_crt_export(host_cert, GNUTLS_X509_FMT_PEM, NULL, &host_cert_export_size); | ||
695 | |||
696 | root_cert_pem.data = gnutls_malloc(root_cert_export_size); | ||
697 | host_cert_pem.data = gnutls_malloc(host_cert_export_size); | ||
698 | |||
699 | gnutls_x509_crt_export(root_cert, GNUTLS_X509_FMT_PEM, root_cert_pem.data, &root_cert_export_size); | ||
700 | root_cert_pem.size = root_cert_export_size; | ||
701 | gnutls_x509_crt_export(host_cert, GNUTLS_X509_FMT_PEM, host_cert_pem.data, &host_cert_export_size); | ||
702 | host_cert_pem.size = host_cert_export_size; | ||
703 | |||
704 | gnutls_datum_t modulus = { NULL, 0 }; | ||
705 | gnutls_datum_t exponent = { NULL, 0 }; | ||
706 | |||
707 | /* now decode the PEM encoded key */ | ||
708 | gnutls_datum_t der_pub_key = { NULL, 0 }; | ||
709 | int gnutls_error = gnutls_pem_base64_decode_alloc("RSA PUBLIC KEY", &public_key, &der_pub_key); | ||
710 | if (GNUTLS_E_SUCCESS == gnutls_error) { | ||
711 | /* initalize asn.1 parser */ | ||
712 | ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY; | ||
713 | if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) { | ||
714 | |||
715 | ASN1_TYPE asn1_pub_key = ASN1_TYPE_EMPTY; | ||
716 | asn1_create_element(pkcs1, "PKCS1.RSAPublicKey", &asn1_pub_key); | ||
717 | |||
718 | if (ASN1_SUCCESS == asn1_der_decoding(&asn1_pub_key, der_pub_key.data, der_pub_key.size, NULL)) { | ||
719 | |||
720 | /* get size to read */ | ||
721 | int ret1 = asn1_read_value(asn1_pub_key, "modulus", NULL, (int*)&modulus.size); | ||
722 | int ret2 = asn1_read_value(asn1_pub_key, "publicExponent", NULL, (int*)&exponent.size); | ||
723 | |||
724 | modulus.data = gnutls_malloc(modulus.size); | ||
725 | exponent.data = gnutls_malloc(exponent.size); | ||
726 | |||
727 | ret1 = asn1_read_value(asn1_pub_key, "modulus", modulus.data, (int*)&modulus.size); | ||
728 | ret2 = asn1_read_value(asn1_pub_key, "publicExponent", exponent.data, (int*)&exponent.size); | ||
729 | if (ret1 != ASN1_SUCCESS || ret2 != ASN1_SUCCESS) { | ||
730 | gnutls_free(modulus.data); | ||
731 | modulus.data = NULL; | ||
732 | modulus.size = 0; | ||
733 | gnutls_free(exponent.data); | ||
734 | exponent.data = NULL; | ||
735 | exponent.size = 0; | ||
736 | } | ||
737 | } | ||
738 | if (asn1_pub_key) | ||
739 | asn1_delete_structure(&asn1_pub_key); | ||
740 | } | ||
741 | if (pkcs1) | ||
742 | asn1_delete_structure(&pkcs1); | ||
743 | } else { | ||
744 | debug_info("ERROR: Could not parse public key: %s", gnutls_strerror(gnutls_error)); | ||
745 | } | ||
746 | |||
747 | /* generate device certificate */ | ||
748 | if (modulus.data && 0 != modulus.size && exponent.data && 0 != exponent.size) { | ||
749 | |||
750 | gnutls_datum_t prime_p = { (unsigned char*)"\x00\xca\x4a\x03\x13\xdf\x9d\x7a\xfd", 9 }; | ||
751 | gnutls_datum_t prime_q = { (unsigned char*)"\x00\xf2\xff\xe0\x15\xd1\x60\x37\x63", 9 }; | ||
752 | gnutls_datum_t coeff = { (unsigned char*)"\x32\x07\xf1\x68\x57\xdf\x9a\xf4", 8 }; | ||
753 | |||
754 | gnutls_x509_privkey_t fake_privkey; | ||
755 | gnutls_x509_crt_t dev_cert; | ||
756 | |||
757 | gnutls_x509_privkey_init(&fake_privkey); | ||
758 | gnutls_x509_crt_init(&dev_cert); | ||
759 | |||
760 | gnutls_error = gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &exponent, &prime_p, &prime_q, &coeff); | ||
761 | if (GNUTLS_E_SUCCESS == gnutls_error) { | ||
762 | /* now generate device certificate */ | ||
763 | gnutls_x509_crt_set_key(dev_cert, fake_privkey); | ||
764 | gnutls_x509_crt_set_serial(dev_cert, "\x01", 1); | ||
765 | gnutls_x509_crt_set_version(dev_cert, 3); | ||
766 | gnutls_x509_crt_set_ca_status(dev_cert, 0); | ||
767 | gnutls_x509_crt_set_activation_time(dev_cert, time(NULL)); | ||
768 | gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
769 | |||
770 | /* use custom hash generation for compatibility with the "Apple ecosystem" */ | ||
771 | const gnutls_digest_algorithm_t dig_sha1 = GNUTLS_DIG_SHA1; | ||
772 | size_t hash_size = gnutls_hash_get_len(dig_sha1); | ||
773 | unsigned char hash[hash_size]; | ||
774 | if (gnutls_hash_fast(dig_sha1, der_pub_key.data, der_pub_key.size, (unsigned char*)&hash) < 0) { | ||
775 | debug_info("ERROR: Failed to generate SHA1 for public key"); | ||
776 | } else { | ||
777 | gnutls_x509_crt_set_subject_key_id(dev_cert, hash, hash_size); | ||
778 | } | ||
779 | |||
780 | gnutls_x509_crt_set_key_usage(dev_cert, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT); | ||
781 | gnutls_error = gnutls_x509_crt_sign2(dev_cert, root_cert, root_privkey, GNUTLS_DIG_SHA1, 0); | ||
782 | if (GNUTLS_E_SUCCESS == gnutls_error) { | ||
783 | /* if everything went well, export in PEM format */ | ||
784 | size_t export_size = 0; | ||
785 | gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &export_size); | ||
786 | dev_cert_pem.data = gnutls_malloc(export_size); | ||
787 | gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_cert_pem.data, &export_size); | ||
788 | dev_cert_pem.size = export_size; | ||
789 | } else { | ||
790 | debug_info("ERROR: Signing device certificate with root private key failed: %s", gnutls_strerror(gnutls_error)); | ||
791 | } | ||
792 | } else { | ||
793 | debug_info("ERROR: Failed to import RSA key data: %s", gnutls_strerror(gnutls_error)); | ||
794 | } | ||
795 | gnutls_x509_crt_deinit(dev_cert); | ||
796 | gnutls_x509_privkey_deinit(fake_privkey); | ||
797 | } | ||
798 | |||
799 | gnutls_x509_crt_deinit(root_cert); | ||
800 | gnutls_x509_crt_deinit(host_cert); | ||
801 | gnutls_x509_privkey_deinit(root_privkey); | ||
802 | gnutls_x509_privkey_deinit(host_privkey); | ||
803 | |||
804 | gnutls_free(modulus.data); | ||
805 | gnutls_free(exponent.data); | ||
806 | |||
807 | gnutls_free(der_pub_key.data); | ||
808 | #elif defined(HAVE_MBEDTLS) | ||
809 | time_t now = time(NULL); | ||
810 | struct tm* timestamp = gmtime(&now); | ||
811 | char notbefore[16]; | ||
812 | strftime(notbefore, sizeof(notbefore), "%Y%m%d%H%M%S", timestamp); | ||
813 | time_t then = now + 60 * 60 * 24 * 365 * 10; | ||
814 | char notafter[16]; | ||
815 | timestamp = gmtime(&then); | ||
816 | strftime(notafter, sizeof(notafter), "%Y%m%d%H%M%S", timestamp); | ||
817 | |||
818 | mbedtls_mpi sn; | ||
819 | mbedtls_mpi_init(&sn); | ||
820 | mbedtls_mpi_lset(&sn, 1); /* 0 doesn't work, so we have to use 1 (like GnuTLS) */ | ||
821 | |||
822 | mbedtls_ctr_drbg_context ctr_drbg; | ||
823 | mbedtls_ctr_drbg_init(&ctr_drbg); | ||
824 | |||
825 | mbedtls_pk_context root_pkey; | ||
826 | mbedtls_pk_init(&root_pkey); | ||
827 | |||
828 | mbedtls_pk_context host_pkey; | ||
829 | mbedtls_pk_init(&host_pkey); | ||
830 | |||
831 | mbedtls_pk_context dev_public_key; | ||
832 | mbedtls_pk_init(&dev_public_key); | ||
833 | |||
834 | mbedtls_entropy_context entropy; | ||
835 | mbedtls_entropy_init(&entropy); | ||
836 | |||
837 | mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)"limd", 4); | ||
838 | |||
839 | /* ----- root key & cert ----- */ | ||
840 | ret = mbedtls_pk_setup(&root_pkey, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); | ||
841 | if (ret != 0) { | ||
842 | debug_info("mbedtls_pk_setup returned -0x%04x", -ret); | ||
843 | goto cleanup; | ||
844 | } | ||
845 | |||
846 | ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(root_pkey), mbedtls_ctr_drbg_random, &ctr_drbg, 2048, 65537); | ||
847 | if (ret != 0) { | ||
848 | debug_info("mbedtls_rsa_gen_key returned -0x%04x", -ret); | ||
849 | goto cleanup; | ||
850 | } | ||
851 | |||
852 | mbedtls_x509write_cert cert; | ||
853 | mbedtls_x509write_crt_init(&cert); | ||
854 | |||
855 | /* set serial number */ | ||
856 | mbedtls_x509write_crt_set_serial(&cert, &sn); | ||
857 | |||
858 | /* set version */ | ||
859 | mbedtls_x509write_crt_set_version(&cert, 2); | ||
860 | |||
861 | /* set x509v3 basic constraints */ | ||
862 | _mbedtls_x509write_crt_set_basic_constraints_critical(&cert, 1, -1); | ||
863 | |||
864 | /* use root public key for root cert */ | ||
865 | mbedtls_x509write_crt_set_subject_key(&cert, &root_pkey); | ||
866 | |||
867 | /* set x509v3 subject key identifier */ | ||
868 | mbedtls_x509write_crt_set_subject_key_identifier(&cert); | ||
869 | |||
870 | /* set key validity */ | ||
871 | mbedtls_x509write_crt_set_validity(&cert, notbefore, notafter); | ||
872 | |||
873 | /* sign root cert with root private key */ | ||
874 | mbedtls_x509write_crt_set_issuer_key(&cert, &root_pkey); | ||
875 | mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA1); | ||
876 | |||
877 | unsigned char outbuf[16384]; | ||
878 | |||
879 | /* write root private key */ | ||
880 | mbedtls_pk_write_key_pem(&root_pkey, outbuf, sizeof(outbuf)); | ||
881 | root_key_pem.size = strlen((const char*)outbuf); | ||
882 | root_key_pem.data = malloc(root_key_pem.size+1); | ||
883 | memcpy(root_key_pem.data, outbuf, root_key_pem.size); | ||
884 | root_key_pem.data[root_key_pem.size] = '\0'; | ||
885 | |||
886 | /* write root certificate */ | ||
887 | mbedtls_x509write_crt_pem(&cert, outbuf, sizeof(outbuf), mbedtls_ctr_drbg_random, &ctr_drbg); | ||
888 | root_cert_pem.size = strlen((const char*)outbuf); | ||
889 | root_cert_pem.data = malloc(root_cert_pem.size+1); | ||
890 | memcpy(root_cert_pem.data, outbuf, root_cert_pem.size); | ||
891 | root_cert_pem.data[root_cert_pem.size] = '\0'; | ||
892 | |||
893 | mbedtls_x509write_crt_free(&cert); | ||
894 | |||
895 | |||
896 | /* ----- host key & cert ----- */ | ||
897 | ret = mbedtls_pk_setup(&host_pkey, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); | ||
898 | if (ret != 0) { | ||
899 | debug_info("mbedtls_pk_setup returned -0x%04x", -ret); | ||
900 | goto cleanup; | ||
901 | } | ||
902 | |||
903 | ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(host_pkey), mbedtls_ctr_drbg_random, &ctr_drbg, 2048, 65537); | ||
904 | if (ret != 0) { | ||
905 | debug_info("mbedtls_rsa_gen_key returned -0x%04x", -ret); | ||
906 | goto cleanup; | ||
907 | } | ||
908 | |||
909 | mbedtls_x509write_crt_init(&cert); | ||
910 | |||
911 | /* set serial number */ | ||
912 | mbedtls_x509write_crt_set_serial(&cert, &sn); | ||
913 | |||
914 | /* set version */ | ||
915 | mbedtls_x509write_crt_set_version(&cert, 2); | ||
916 | |||
917 | /* set x509v3 basic constraints */ | ||
918 | _mbedtls_x509write_crt_set_basic_constraints_critical(&cert, 0, -1); | ||
919 | |||
920 | /* use host public key for host cert */ | ||
921 | mbedtls_x509write_crt_set_subject_key(&cert, &host_pkey); | ||
922 | |||
923 | /* set x509v3 subject key identifier */ | ||
924 | mbedtls_x509write_crt_set_subject_key_identifier(&cert); | ||
925 | |||
926 | /* set x509v3 key usage */ | ||
927 | mbedtls_x509write_crt_set_key_usage(&cert, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_ENCIPHERMENT); | ||
928 | |||
929 | /* set key validity */ | ||
930 | mbedtls_x509write_crt_set_validity(&cert, notbefore, notafter); | ||
931 | |||
932 | /* sign host cert with root private key */ | ||
933 | mbedtls_x509write_crt_set_issuer_key(&cert, &root_pkey); | ||
934 | mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA1); | ||
935 | |||
936 | /* write host private key */ | ||
937 | mbedtls_pk_write_key_pem(&host_pkey, outbuf, sizeof(outbuf)); | ||
938 | host_key_pem.size = strlen((const char*)outbuf); | ||
939 | host_key_pem.data = malloc(host_key_pem.size+1); | ||
940 | memcpy(host_key_pem.data, outbuf, host_key_pem.size); | ||
941 | host_key_pem.data[host_key_pem.size] = '\0'; | ||
942 | |||
943 | /* write host certificate */ | ||
944 | mbedtls_x509write_crt_pem(&cert, outbuf, sizeof(outbuf), mbedtls_ctr_drbg_random, &ctr_drbg); | ||
945 | host_cert_pem.size = strlen((const char*)outbuf); | ||
946 | host_cert_pem.data = malloc(host_cert_pem.size+1); | ||
947 | memcpy(host_cert_pem.data, outbuf, host_cert_pem.size); | ||
948 | host_cert_pem.data[host_cert_pem.size] = '\0'; | ||
949 | |||
950 | mbedtls_x509write_crt_free(&cert); | ||
951 | |||
952 | |||
953 | /* ----- device certificate ----- */ | ||
954 | unsigned char* pubkey_data = malloc(public_key.size+1); | ||
955 | if (!pubkey_data) { | ||
956 | debug_info("malloc() failed\n"); | ||
957 | goto cleanup; | ||
958 | } | ||
959 | memcpy(pubkey_data, public_key.data, public_key.size); | ||
960 | pubkey_data[public_key.size] = '\0'; | ||
961 | |||
962 | int pr = mbedtls_pk_parse_public_key(&dev_public_key, pubkey_data, public_key.size+1); | ||
963 | free(pubkey_data); | ||
964 | if (pr != 0) { | ||
965 | debug_info("Failed to read device public key: -0x%x\n", -pr); | ||
966 | goto cleanup; | ||
967 | } | ||
968 | |||
969 | mbedtls_x509write_crt_init(&cert); | ||
970 | |||
971 | /* set serial number */ | ||
972 | mbedtls_x509write_crt_set_serial(&cert, &sn); | ||
973 | |||
974 | /* set version */ | ||
975 | mbedtls_x509write_crt_set_version(&cert, 2); | ||
976 | |||
977 | /* set x509v3 basic constraints */ | ||
978 | _mbedtls_x509write_crt_set_basic_constraints_critical(&cert, 0, -1); | ||
979 | |||
980 | /* use root public key for dev cert subject key */ | ||
981 | mbedtls_x509write_crt_set_subject_key(&cert, &dev_public_key); | ||
982 | |||
983 | /* set x509v3 subject key identifier */ | ||
984 | mbedtls_x509write_crt_set_subject_key_identifier(&cert); | ||
985 | |||
986 | /* set x509v3 key usage */ | ||
987 | mbedtls_x509write_crt_set_key_usage(&cert, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_ENCIPHERMENT); | ||
988 | |||
989 | /* set key validity */ | ||
990 | mbedtls_x509write_crt_set_validity(&cert, notbefore, notafter); | ||
991 | |||
992 | /* sign device certificate with root private key */ | ||
993 | mbedtls_x509write_crt_set_issuer_key(&cert, &root_pkey); | ||
994 | mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA1); | ||
995 | |||
996 | /* write device certificate */ | ||
997 | mbedtls_x509write_crt_pem(&cert, outbuf, sizeof(outbuf), mbedtls_ctr_drbg_random, &ctr_drbg); | ||
998 | dev_cert_pem.size = strlen((const char*)outbuf); | ||
999 | dev_cert_pem.data = malloc(dev_cert_pem.size+1); | ||
1000 | memcpy(dev_cert_pem.data, outbuf, dev_cert_pem.size); | ||
1001 | dev_cert_pem.data[dev_cert_pem.size] = '\0'; | ||
1002 | |||
1003 | mbedtls_x509write_crt_free(&cert); | ||
1004 | |||
1005 | /* cleanup */ | ||
1006 | cleanup: | ||
1007 | mbedtls_mpi_free(&sn); | ||
1008 | mbedtls_pk_free(&dev_public_key); | ||
1009 | mbedtls_entropy_free(&entropy); | ||
1010 | mbedtls_pk_free(&host_pkey); | ||
1011 | mbedtls_pk_free(&root_pkey); | ||
1012 | mbedtls_ctr_drbg_free(&ctr_drbg); | ||
1013 | #endif | ||
1014 | |||
1015 | /* make sure that we have all we need */ | ||
1016 | if (root_cert_pem.data && 0 != root_cert_pem.size | ||
1017 | && root_key_pem.data && 0 != root_key_pem.size | ||
1018 | && host_cert_pem.data && 0 != host_cert_pem.size | ||
1019 | && host_key_pem.data && 0 != host_key_pem.size | ||
1020 | && dev_cert_pem.data && 0 != dev_cert_pem.size) { | ||
1021 | /* now set keys and certificates */ | ||
1022 | pair_record_set_item_from_key_data(pair_record, USERPREF_DEVICE_CERTIFICATE_KEY, &dev_cert_pem); | ||
1023 | pair_record_set_item_from_key_data(pair_record, USERPREF_HOST_PRIVATE_KEY_KEY, &host_key_pem); | ||
1024 | pair_record_set_item_from_key_data(pair_record, USERPREF_HOST_CERTIFICATE_KEY, &host_cert_pem); | ||
1025 | pair_record_set_item_from_key_data(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_key_pem); | ||
1026 | pair_record_set_item_from_key_data(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert_pem); | ||
1027 | ret = USERPREF_E_SUCCESS; | ||
1028 | } | ||
1029 | |||
1030 | free(dev_cert_pem.data); | ||
1031 | free(root_key_pem.data); | ||
1032 | free(root_cert_pem.data); | ||
1033 | free(host_key_pem.data); | ||
1034 | free(host_cert_pem.data); | ||
1035 | |||
1036 | return ret; | ||
1037 | } | ||
1038 | |||
1039 | /** | ||
1040 | * Private function which import the given key into a gnutls structure. | ||
1041 | * | ||
1042 | * @param name The name of the private key to import. | ||
1043 | * @param key the gnutls key structure. | ||
1044 | * | ||
1045 | * @return 1 if the key was successfully imported. | ||
1046 | */ | ||
1047 | #if defined(HAVE_OPENSSL) || defined(HAVE_MBEDTLS) | ||
1048 | userpref_error_t pair_record_import_key_with_name(plist_t pair_record, const char* name, key_data_t* key) | ||
1049 | #elif defined(HAVE_GNUTLS) | ||
1050 | userpref_error_t pair_record_import_key_with_name(plist_t pair_record, const char* name, gnutls_x509_privkey_t key) | ||
1051 | #endif | ||
1052 | { | ||
1053 | #if defined(HAVE_OPENSSL) || defined(HAVE_MBEDTLS) | ||
1054 | if (!key) | ||
1055 | return USERPREF_E_SUCCESS; | ||
1056 | #endif | ||
1057 | userpref_error_t ret = USERPREF_E_INVALID_CONF; | ||
1058 | |||
1059 | #if defined(HAVE_OPENSSL) || defined(HAVE_MBEDTLS) | ||
1060 | ret = pair_record_get_item_as_key_data(pair_record, name, key); | ||
1061 | #elif defined(HAVE_GNUTLS) | ||
1062 | key_data_t pem = { NULL, 0 }; | ||
1063 | ret = pair_record_get_item_as_key_data(pair_record, name, &pem); | ||
1064 | if (ret == USERPREF_E_SUCCESS && GNUTLS_E_SUCCESS == gnutls_x509_privkey_import(key, &pem, GNUTLS_X509_FMT_PEM)) | ||
1065 | ret = USERPREF_E_SUCCESS; | ||
1066 | else | ||
1067 | ret = USERPREF_E_SSL_ERROR; | ||
1068 | |||
1069 | if (pem.data) | ||
1070 | free(pem.data); | ||
1071 | #endif | ||
1072 | return ret; | ||
1073 | } | ||
1074 | |||
1075 | /** | ||
1076 | * Private function which import the given certificate into a gnutls structure. | ||
1077 | * | ||
1078 | * @param name The name of the certificate to import. | ||
1079 | * @param cert the gnutls certificate structure. | ||
1080 | * | ||
1081 | * @return IDEVICE_E_SUCCESS if the certificate was successfully imported. | ||
1082 | */ | ||
1083 | #if defined(HAVE_OPENSSL) || defined(HAVE_MBEDTLS) | ||
1084 | userpref_error_t pair_record_import_crt_with_name(plist_t pair_record, const char* name, key_data_t* cert) | ||
1085 | #else | ||
1086 | userpref_error_t pair_record_import_crt_with_name(plist_t pair_record, const char* name, gnutls_x509_crt_t cert) | ||
1087 | #endif | ||
1088 | { | ||
1089 | #if defined(HAVE_OPENSSL) || defined(HAVE_MBEDTLS) | ||
1090 | if (!cert) | ||
1091 | return USERPREF_E_SUCCESS; | ||
1092 | #endif | ||
1093 | userpref_error_t ret = USERPREF_E_INVALID_CONF; | ||
1094 | |||
1095 | #if defined(HAVE_OPENSSL) || defined(HAVE_MBEDTLS) | ||
1096 | ret = pair_record_get_item_as_key_data(pair_record, name, cert); | ||
1097 | #elif defined(HAVE_GNUTLS) | ||
1098 | key_data_t pem = { NULL, 0 }; | ||
1099 | ret = pair_record_get_item_as_key_data(pair_record, name, &pem); | ||
1100 | if (ret == USERPREF_E_SUCCESS && GNUTLS_E_SUCCESS == gnutls_x509_crt_import(cert, &pem, GNUTLS_X509_FMT_PEM)) | ||
1101 | ret = USERPREF_E_SUCCESS; | ||
1102 | else | ||
1103 | ret = USERPREF_E_SSL_ERROR; | ||
1104 | |||
1105 | if (pem.data) | ||
1106 | free(pem.data); | ||
1107 | #endif | ||
1108 | return ret; | ||
1109 | } | ||
1110 | |||
1111 | userpref_error_t pair_record_get_host_id(plist_t pair_record, char** host_id) | ||
1112 | { | ||
1113 | plist_t node = plist_dict_get_item(pair_record, USERPREF_HOST_ID_KEY); | ||
1114 | |||
1115 | if (node && plist_get_node_type(node) == PLIST_STRING) { | ||
1116 | plist_get_string_val(node, host_id); | ||
1117 | } | ||
1118 | |||
1119 | return USERPREF_E_SUCCESS; | ||
1120 | } | ||
1121 | |||
1122 | userpref_error_t pair_record_set_host_id(plist_t pair_record, const char* host_id) | ||
1123 | { | ||
1124 | plist_dict_set_item(pair_record, USERPREF_HOST_ID_KEY, plist_new_string(host_id)); | ||
1125 | |||
1126 | return USERPREF_E_SUCCESS; | ||
1127 | } | ||
1128 | |||
1129 | userpref_error_t pair_record_get_item_as_key_data(plist_t pair_record, const char* name, key_data_t *value) | ||
1130 | { | ||
1131 | if (!pair_record || !value) | ||
1132 | return USERPREF_E_INVALID_ARG; | ||
1133 | |||
1134 | userpref_error_t ret = USERPREF_E_SUCCESS; | ||
1135 | char* buffer = NULL; | ||
1136 | uint64_t length = 0; | ||
1137 | |||
1138 | plist_t node = plist_dict_get_item(pair_record, name); | ||
1139 | |||
1140 | if (node && plist_get_node_type(node) == PLIST_DATA) { | ||
1141 | plist_get_data_val(node, &buffer, &length); | ||
1142 | value->data = (unsigned char*)malloc(length+1); | ||
1143 | memcpy(value->data, buffer, length); | ||
1144 | value->data[length] = '\0'; | ||
1145 | value->size = length+1; | ||
1146 | free(buffer); | ||
1147 | buffer = NULL; | ||
1148 | } else { | ||
1149 | ret = USERPREF_E_INVALID_CONF; | ||
1150 | } | ||
1151 | |||
1152 | if (buffer) | ||
1153 | free(buffer); | ||
1154 | |||
1155 | return ret; | ||
1156 | } | ||
1157 | |||
1158 | userpref_error_t pair_record_set_item_from_key_data(plist_t pair_record, const char* name, key_data_t *value) | ||
1159 | { | ||
1160 | userpref_error_t ret = USERPREF_E_SUCCESS; | ||
1161 | |||
1162 | if (!pair_record || !value) { | ||
1163 | return USERPREF_E_INVALID_ARG; | ||
1164 | } | ||
1165 | |||
1166 | /* set new item */ | ||
1167 | plist_dict_set_item(pair_record, name, plist_new_data((char*)value->data, value->size)); | ||
1168 | |||
1169 | return ret; | ||
1170 | } | ||
1171 | |||