]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * TLSv1 credentials | |
6b7bb429 | 3 | * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> |
6fc6879b | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
12 | #include "base64.h" | |
03da66bd | 13 | #include "crypto/crypto.h" |
6b7bb429 JM |
14 | #include "crypto/sha1.h" |
15 | #include "pkcs5.h" | |
16 | #include "pkcs8.h" | |
6fc6879b JM |
17 | #include "x509v3.h" |
18 | #include "tlsv1_cred.h" | |
19 | ||
20 | ||
21 | struct tlsv1_credentials * tlsv1_cred_alloc(void) | |
22 | { | |
23 | struct tlsv1_credentials *cred; | |
24 | cred = os_zalloc(sizeof(*cred)); | |
25 | return cred; | |
26 | } | |
27 | ||
28 | ||
29 | void tlsv1_cred_free(struct tlsv1_credentials *cred) | |
30 | { | |
31 | if (cred == NULL) | |
32 | return; | |
33 | ||
34 | x509_certificate_chain_free(cred->trusted_certs); | |
35 | x509_certificate_chain_free(cred->cert); | |
36 | crypto_private_key_free(cred->key); | |
37 | os_free(cred->dh_p); | |
38 | os_free(cred->dh_g); | |
bca0872d | 39 | os_free(cred->ocsp_stapling_response); |
8ea6a270 | 40 | os_free(cred->ocsp_stapling_response_multi); |
6fc6879b JM |
41 | os_free(cred); |
42 | } | |
43 | ||
44 | ||
45 | static int tlsv1_add_cert_der(struct x509_certificate **chain, | |
46 | const u8 *buf, size_t len) | |
47 | { | |
6921f1f3 | 48 | struct x509_certificate *cert, *p; |
6fc6879b JM |
49 | char name[128]; |
50 | ||
51 | cert = x509_certificate_parse(buf, len); | |
52 | if (cert == NULL) { | |
53 | wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate", | |
54 | __func__); | |
55 | return -1; | |
56 | } | |
57 | ||
6921f1f3 JM |
58 | p = *chain; |
59 | while (p && p->next) | |
60 | p = p->next; | |
61 | if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) { | |
62 | /* | |
63 | * The new certificate is the issuer of the last certificate in | |
64 | * the chain - add the new certificate to the end. | |
65 | */ | |
66 | p->next = cert; | |
67 | } else { | |
68 | /* Add to the beginning of the chain */ | |
69 | cert->next = *chain; | |
70 | *chain = cert; | |
71 | } | |
6fc6879b JM |
72 | |
73 | x509_name_string(&cert->subject, name, sizeof(name)); | |
74 | wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name); | |
75 | ||
76 | return 0; | |
77 | } | |
78 | ||
79 | ||
80 | static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----"; | |
81 | static const char *pem_cert_end = "-----END CERTIFICATE-----"; | |
1b8409a0 JM |
82 | static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----"; |
83 | static const char *pem_key_end = "-----END RSA PRIVATE KEY-----"; | |
8ef74414 JM |
84 | static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----"; |
85 | static const char *pem_key2_end = "-----END PRIVATE KEY-----"; | |
3f4ed97a JM |
86 | static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; |
87 | static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----"; | |
6fc6879b JM |
88 | |
89 | ||
90 | static const u8 * search_tag(const char *tag, const u8 *buf, size_t len) | |
91 | { | |
92 | size_t i, plen; | |
93 | ||
94 | plen = os_strlen(tag); | |
95 | if (len < plen) | |
96 | return NULL; | |
97 | ||
98 | for (i = 0; i < len - plen; i++) { | |
99 | if (os_memcmp(buf + i, tag, plen) == 0) | |
100 | return buf + i; | |
101 | } | |
102 | ||
103 | return NULL; | |
104 | } | |
105 | ||
106 | ||
107 | static int tlsv1_add_cert(struct x509_certificate **chain, | |
108 | const u8 *buf, size_t len) | |
109 | { | |
110 | const u8 *pos, *end; | |
111 | unsigned char *der; | |
112 | size_t der_len; | |
113 | ||
114 | pos = search_tag(pem_cert_begin, buf, len); | |
115 | if (!pos) { | |
116 | wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - " | |
117 | "assume DER format"); | |
118 | return tlsv1_add_cert_der(chain, buf, len); | |
119 | } | |
120 | ||
121 | wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into " | |
122 | "DER format"); | |
123 | ||
124 | while (pos) { | |
125 | pos += os_strlen(pem_cert_begin); | |
126 | end = search_tag(pem_cert_end, pos, buf + len - pos); | |
127 | if (end == NULL) { | |
128 | wpa_printf(MSG_INFO, "TLSv1: Could not find PEM " | |
129 | "certificate end tag (%s)", pem_cert_end); | |
130 | return -1; | |
131 | } | |
132 | ||
8e5e36a1 | 133 | der = base64_decode((const char *) pos, end - pos, &der_len); |
6fc6879b JM |
134 | if (der == NULL) { |
135 | wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM " | |
136 | "certificate"); | |
137 | return -1; | |
138 | } | |
139 | ||
140 | if (tlsv1_add_cert_der(chain, der, der_len) < 0) { | |
141 | wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM " | |
142 | "certificate after DER conversion"); | |
143 | os_free(der); | |
144 | return -1; | |
145 | } | |
146 | ||
147 | os_free(der); | |
148 | ||
149 | end += os_strlen(pem_cert_end); | |
150 | pos = search_tag(pem_cert_begin, end, buf + len - end); | |
151 | } | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | ||
157 | static int tlsv1_set_cert_chain(struct x509_certificate **chain, | |
158 | const char *cert, const u8 *cert_blob, | |
159 | size_t cert_blob_len) | |
160 | { | |
161 | if (cert_blob) | |
162 | return tlsv1_add_cert(chain, cert_blob, cert_blob_len); | |
163 | ||
164 | if (cert) { | |
165 | u8 *buf; | |
166 | size_t len; | |
167 | int ret; | |
168 | ||
169 | buf = (u8 *) os_readfile(cert, &len); | |
170 | if (buf == NULL) { | |
171 | wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", | |
172 | cert); | |
173 | return -1; | |
174 | } | |
175 | ||
176 | ret = tlsv1_add_cert(chain, buf, len); | |
177 | os_free(buf); | |
178 | return ret; | |
179 | } | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
184 | ||
185 | /** | |
186 | * tlsv1_set_ca_cert - Set trusted CA certificate(s) | |
187 | * @cred: TLSv1 credentials from tlsv1_cred_alloc() | |
188 | * @cert: File or reference name for X.509 certificate in PEM or DER format | |
189 | * @cert_blob: cert as inlined data or %NULL if not used | |
190 | * @cert_blob_len: ca_cert_blob length | |
191 | * @path: Path to CA certificates (not yet supported) | |
192 | * Returns: 0 on success, -1 on failure | |
193 | */ | |
194 | int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, | |
195 | const u8 *cert_blob, size_t cert_blob_len, | |
196 | const char *path) | |
197 | { | |
fdc16142 PR |
198 | if (cert && os_strncmp(cert, "hash://", 7) == 0) { |
199 | const char *pos = cert + 7; | |
200 | if (os_strncmp(pos, "server/sha256/", 14) != 0) { | |
201 | wpa_printf(MSG_DEBUG, | |
202 | "TLSv1: Unsupported ca_cert hash value '%s'", | |
203 | cert); | |
204 | return -1; | |
205 | } | |
206 | pos += 14; | |
207 | if (os_strlen(pos) != 32 * 2) { | |
208 | wpa_printf(MSG_DEBUG, | |
209 | "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'", | |
210 | cert); | |
211 | return -1; | |
212 | } | |
213 | if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) { | |
214 | wpa_printf(MSG_DEBUG, | |
215 | "TLSv1: Invalid SHA256 hash value in ca_cert '%s'", | |
216 | cert); | |
217 | return -1; | |
218 | } | |
219 | cred->server_cert_only = 1; | |
220 | cred->ca_cert_verify = 0; | |
221 | wpa_printf(MSG_DEBUG, | |
222 | "TLSv1: Checking only server certificate match"); | |
223 | return 0; | |
224 | } | |
225 | ||
f2a6ad01 JM |
226 | if (cert && os_strncmp(cert, "probe://", 8) == 0) { |
227 | cred->cert_probe = 1; | |
228 | cred->ca_cert_verify = 0; | |
229 | wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate"); | |
230 | return 0; | |
231 | } | |
232 | ||
3665776e PR |
233 | cred->ca_cert_verify = cert || cert_blob || path; |
234 | ||
6fc6879b JM |
235 | if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, |
236 | cert_blob, cert_blob_len) < 0) | |
237 | return -1; | |
238 | ||
239 | if (path) { | |
240 | /* TODO: add support for reading number of certificate files */ | |
241 | wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory " | |
242 | "not yet supported"); | |
243 | return -1; | |
244 | } | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
249 | ||
250 | /** | |
251 | * tlsv1_set_cert - Set certificate | |
252 | * @cred: TLSv1 credentials from tlsv1_cred_alloc() | |
253 | * @cert: File or reference name for X.509 certificate in PEM or DER format | |
254 | * @cert_blob: cert as inlined data or %NULL if not used | |
255 | * @cert_blob_len: cert_blob length | |
256 | * Returns: 0 on success, -1 on failure | |
257 | */ | |
258 | int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, | |
259 | const u8 *cert_blob, size_t cert_blob_len) | |
260 | { | |
261 | return tlsv1_set_cert_chain(&cred->cert, cert, | |
262 | cert_blob, cert_blob_len); | |
263 | } | |
264 | ||
265 | ||
3af9f298 | 266 | static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len) |
1b8409a0 JM |
267 | { |
268 | const u8 *pos, *end; | |
269 | unsigned char *der; | |
270 | size_t der_len; | |
3af9f298 | 271 | struct crypto_private_key *pkey; |
1b8409a0 JM |
272 | |
273 | pos = search_tag(pem_key_begin, key, len); | |
8ef74414 JM |
274 | if (!pos) { |
275 | pos = search_tag(pem_key2_begin, key, len); | |
276 | if (!pos) | |
3af9f298 | 277 | return NULL; |
8ef74414 JM |
278 | pos += os_strlen(pem_key2_begin); |
279 | end = search_tag(pem_key2_end, pos, key + len - pos); | |
280 | if (!end) | |
3af9f298 | 281 | return NULL; |
8ef74414 | 282 | } else { |
e770c497 | 283 | const u8 *pos2; |
8ef74414 JM |
284 | pos += os_strlen(pem_key_begin); |
285 | end = search_tag(pem_key_end, pos, key + len - pos); | |
286 | if (!end) | |
3af9f298 | 287 | return NULL; |
e770c497 JM |
288 | pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos); |
289 | if (pos2) { | |
290 | wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key " | |
291 | "format (Proc-Type/DEK-Info)"); | |
292 | return NULL; | |
293 | } | |
8ef74414 | 294 | } |
1b8409a0 | 295 | |
8e5e36a1 | 296 | der = base64_decode((const char *) pos, end - pos, &der_len); |
1b8409a0 | 297 | if (!der) |
3af9f298 JM |
298 | return NULL; |
299 | pkey = crypto_private_key_import(der, der_len, NULL); | |
3f4ed97a | 300 | os_free(der); |
3af9f298 | 301 | return pkey; |
3f4ed97a JM |
302 | } |
303 | ||
304 | ||
3af9f298 JM |
305 | static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, |
306 | size_t len, | |
307 | const char *passwd) | |
3f4ed97a JM |
308 | { |
309 | const u8 *pos, *end; | |
310 | unsigned char *der; | |
311 | size_t der_len; | |
3af9f298 | 312 | struct crypto_private_key *pkey; |
3f4ed97a JM |
313 | |
314 | if (passwd == NULL) | |
3af9f298 | 315 | return NULL; |
3f4ed97a JM |
316 | pos = search_tag(pem_key_enc_begin, key, len); |
317 | if (!pos) | |
3af9f298 | 318 | return NULL; |
3f4ed97a JM |
319 | pos += os_strlen(pem_key_enc_begin); |
320 | end = search_tag(pem_key_enc_end, pos, key + len - pos); | |
321 | if (!end) | |
3af9f298 | 322 | return NULL; |
3f4ed97a | 323 | |
8e5e36a1 | 324 | der = base64_decode((const char *) pos, end - pos, &der_len); |
3f4ed97a | 325 | if (!der) |
3af9f298 JM |
326 | return NULL; |
327 | pkey = crypto_private_key_import(der, der_len, passwd); | |
1b8409a0 | 328 | os_free(der); |
3af9f298 | 329 | return pkey; |
1b8409a0 JM |
330 | } |
331 | ||
332 | ||
6b7bb429 JM |
333 | #ifdef PKCS12_FUNCS |
334 | ||
335 | static int oid_is_rsadsi(struct asn1_oid *oid) | |
336 | { | |
337 | return oid->len >= 4 && | |
338 | oid->oid[0] == 1 /* iso */ && | |
339 | oid->oid[1] == 2 /* member-body */ && | |
340 | oid->oid[2] == 840 /* us */ && | |
341 | oid->oid[3] == 113549 /* rsadsi */; | |
342 | } | |
343 | ||
344 | ||
345 | static int pkcs12_is_bagtype_oid(struct asn1_oid *oid, unsigned long type) | |
346 | { | |
347 | return oid->len == 9 && | |
348 | oid_is_rsadsi(oid) && | |
349 | oid->oid[4] == 1 /* pkcs */ && | |
350 | oid->oid[5] == 12 /* pkcs-12 */ && | |
351 | oid->oid[6] == 10 && | |
352 | oid->oid[7] == 1 /* bagtypes */ && | |
353 | oid->oid[8] == type; | |
354 | } | |
355 | ||
356 | ||
357 | static int is_oid_pkcs7(struct asn1_oid *oid) | |
358 | { | |
359 | return oid->len == 7 && | |
360 | oid->oid[0] == 1 /* iso */ && | |
361 | oid->oid[1] == 2 /* member-body */ && | |
362 | oid->oid[2] == 840 /* us */ && | |
363 | oid->oid[3] == 113549 /* rsadsi */ && | |
364 | oid->oid[4] == 1 /* pkcs */ && | |
365 | oid->oid[5] == 7 /* pkcs-7 */; | |
366 | } | |
367 | ||
368 | ||
369 | static int is_oid_pkcs7_data(struct asn1_oid *oid) | |
370 | { | |
371 | return is_oid_pkcs7(oid) && oid->oid[6] == 1 /* data */; | |
372 | } | |
373 | ||
374 | ||
375 | static int is_oid_pkcs7_enc_data(struct asn1_oid *oid) | |
376 | { | |
377 | return is_oid_pkcs7(oid) && oid->oid[6] == 6 /* encryptedData */; | |
378 | } | |
379 | ||
380 | ||
381 | static int is_oid_pkcs9(struct asn1_oid *oid) | |
382 | { | |
383 | return oid->len >= 6 && | |
384 | oid->oid[0] == 1 /* iso */ && | |
385 | oid->oid[1] == 2 /* member-body */ && | |
386 | oid->oid[2] == 840 /* us */ && | |
387 | oid->oid[3] == 113549 /* rsadsi */ && | |
388 | oid->oid[4] == 1 /* pkcs */ && | |
389 | oid->oid[5] == 9 /* pkcs-9 */; | |
390 | } | |
391 | ||
392 | ||
393 | static int is_oid_pkcs9_friendly_name(struct asn1_oid *oid) | |
394 | { | |
395 | return oid->len == 7 && is_oid_pkcs9(oid) && | |
396 | oid->oid[6] == 20; | |
397 | } | |
398 | ||
399 | ||
400 | static int is_oid_pkcs9_local_key_id(struct asn1_oid *oid) | |
401 | { | |
402 | return oid->len == 7 && is_oid_pkcs9(oid) && | |
403 | oid->oid[6] == 21; | |
404 | } | |
405 | ||
406 | ||
407 | static int is_oid_pkcs9_x509_cert(struct asn1_oid *oid) | |
408 | { | |
409 | return oid->len == 8 && is_oid_pkcs9(oid) && | |
410 | oid->oid[6] == 22 /* certTypes */ && | |
411 | oid->oid[7] == 1 /* x509Certificate */; | |
412 | } | |
413 | ||
414 | ||
415 | static int pkcs12_keybag(struct tlsv1_credentials *cred, | |
416 | const u8 *buf, size_t len) | |
417 | { | |
418 | /* TODO */ | |
419 | return 0; | |
420 | } | |
421 | ||
422 | ||
423 | static int pkcs12_pkcs8_keybag(struct tlsv1_credentials *cred, | |
424 | const u8 *buf, size_t len, | |
425 | const char *passwd) | |
426 | { | |
427 | struct crypto_private_key *key; | |
428 | ||
429 | /* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo */ | |
430 | key = pkcs8_enc_key_import(buf, len, passwd); | |
431 | if (!key) | |
432 | return -1; | |
433 | ||
434 | wpa_printf(MSG_DEBUG, | |
435 | "PKCS #12: Successfully decrypted PKCS8ShroudedKeyBag"); | |
436 | crypto_private_key_free(cred->key); | |
437 | cred->key = key; | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
442 | ||
443 | static int pkcs12_certbag(struct tlsv1_credentials *cred, | |
444 | const u8 *buf, size_t len) | |
445 | { | |
446 | struct asn1_hdr hdr; | |
447 | struct asn1_oid oid; | |
448 | char obuf[80]; | |
449 | const u8 *pos, *end; | |
450 | ||
451 | /* | |
452 | * CertBag ::= SEQUENCE { | |
453 | * certId BAG-TYPE.&id ({CertTypes}), | |
454 | * certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId}) | |
455 | * } | |
456 | */ | |
457 | ||
458 | if (asn1_get_next(buf, len, &hdr) < 0 || | |
459 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
460 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
461 | wpa_printf(MSG_DEBUG, | |
462 | "PKCS #12: Expected SEQUENCE (CertBag) - found class %d tag 0x%x", | |
463 | hdr.class, hdr.tag); | |
464 | return -1; | |
465 | } | |
466 | ||
467 | pos = hdr.payload; | |
468 | end = hdr.payload + hdr.length; | |
469 | ||
470 | if (asn1_get_oid(pos, end - pos, &oid, &pos)) { | |
471 | wpa_printf(MSG_DEBUG, | |
472 | "PKCS #12: Failed to parse OID (certId)"); | |
473 | return -1; | |
474 | } | |
475 | ||
476 | asn1_oid_to_str(&oid, obuf, sizeof(obuf)); | |
477 | wpa_printf(MSG_DEBUG, "PKCS #12: certId %s", obuf); | |
478 | ||
479 | if (!is_oid_pkcs9_x509_cert(&oid)) { | |
480 | wpa_printf(MSG_DEBUG, | |
481 | "PKCS #12: Ignored unsupported certificate type (certId %s)", | |
482 | obuf); | |
483 | } | |
484 | ||
485 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
486 | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || | |
487 | hdr.tag != 0) { | |
488 | wpa_printf(MSG_DEBUG, | |
489 | "PKCS #12: Expected [0] EXPLICIT (certValue) - found class %d tag 0x%x", | |
490 | hdr.class, hdr.tag); | |
491 | return -1; | |
492 | } | |
493 | ||
494 | if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || | |
495 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
496 | hdr.tag != ASN1_TAG_OCTETSTRING) { | |
497 | wpa_printf(MSG_DEBUG, | |
498 | "PKCS #12: Expected OCTET STRING (x509Certificate) - found class %d tag 0x%x", | |
499 | hdr.class, hdr.tag); | |
500 | return -1; | |
501 | } | |
502 | ||
503 | wpa_hexdump(MSG_DEBUG, "PKCS #12: x509Certificate", | |
504 | hdr.payload, hdr.length); | |
505 | if (cred->cert) { | |
506 | struct x509_certificate *cert; | |
507 | ||
508 | wpa_printf(MSG_DEBUG, "PKCS #12: Ignore extra certificate"); | |
509 | cert = x509_certificate_parse(hdr.payload, hdr.length); | |
510 | if (!cert) { | |
511 | wpa_printf(MSG_DEBUG, | |
512 | "PKCS #12: Failed to parse x509Certificate"); | |
513 | return 0; | |
514 | } | |
515 | x509_certificate_chain_free(cert); | |
516 | ||
517 | return 0; | |
518 | } | |
519 | return tlsv1_set_cert(cred, NULL, hdr.payload, hdr.length); | |
520 | } | |
521 | ||
522 | ||
523 | static int pkcs12_parse_attr_friendly_name(const u8 *pos, const u8 *end) | |
524 | { | |
525 | struct asn1_hdr hdr; | |
526 | ||
527 | /* | |
528 | * RFC 2985, 5.5.1: | |
529 | * friendlyName ATTRIBUTE ::= { | |
530 | * WITH SYNTAX BMPString (SIZE(1..pkcs-9-ub-friendlyName)) | |
531 | * EQUALITY MATCHING RULE caseIgnoreMatch | |
532 | * SINGLE VALUE TRUE | |
533 | * ID pkcs-9-at-friendlyName | |
534 | * } | |
535 | */ | |
536 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
537 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
538 | hdr.tag != ASN1_TAG_BMPSTRING) { | |
539 | wpa_printf(MSG_DEBUG, | |
540 | "PKCS #12: Expected BMPSTRING (friendlyName) - found class %d tag 0x%x", | |
541 | hdr.class, hdr.tag); | |
542 | return 0; | |
543 | } | |
544 | wpa_hexdump_ascii(MSG_DEBUG, "PKCS #12: friendlyName", | |
545 | hdr.payload, hdr.length); | |
546 | return 0; | |
547 | } | |
548 | ||
549 | ||
550 | static int pkcs12_parse_attr_local_key_id(const u8 *pos, const u8 *end) | |
551 | { | |
552 | struct asn1_hdr hdr; | |
553 | ||
554 | /* | |
555 | * RFC 2985, 5.5.2: | |
556 | * localKeyId ATTRIBUTE ::= { | |
557 | * WITH SYNTAX OCTET STRING | |
558 | * EQUALITY MATCHING RULE octetStringMatch | |
559 | * SINGLE VALUE TRUE | |
560 | * ID pkcs-9-at-localKeyId | |
561 | * } | |
562 | */ | |
563 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
564 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
565 | hdr.tag != ASN1_TAG_OCTETSTRING) { | |
566 | wpa_printf(MSG_DEBUG, | |
567 | "PKCS #12: Expected OCTET STRING (localKeyID) - found class %d tag 0x%x", | |
568 | hdr.class, hdr.tag); | |
569 | return -1; | |
570 | } | |
571 | wpa_hexdump_key(MSG_DEBUG, "PKCS #12: localKeyID", | |
572 | hdr.payload, hdr.length); | |
573 | return 0; | |
574 | } | |
575 | ||
576 | ||
577 | static int pkcs12_parse_attr(const u8 *pos, size_t len) | |
578 | { | |
579 | const u8 *end = pos + len; | |
580 | struct asn1_hdr hdr; | |
581 | struct asn1_oid a_oid; | |
582 | char obuf[80]; | |
583 | ||
584 | /* | |
585 | * PKCS12Attribute ::= SEQUENCE { | |
586 | * attrId ATTRIBUTE.&id ({PKCS12AttrSet}), | |
587 | * attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) | |
588 | * } | |
589 | */ | |
590 | ||
591 | if (asn1_get_oid(pos, end - pos, &a_oid, &pos)) { | |
592 | wpa_printf(MSG_DEBUG, "PKCS #12: Failed to parse OID (attrId)"); | |
593 | return -1; | |
594 | } | |
595 | ||
596 | asn1_oid_to_str(&a_oid, obuf, sizeof(obuf)); | |
597 | wpa_printf(MSG_DEBUG, "PKCS #12: attrId %s", obuf); | |
598 | ||
599 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
600 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
601 | hdr.tag != ASN1_TAG_SET) { | |
602 | wpa_printf(MSG_DEBUG, | |
603 | "PKCS #12: Expected SET (attrValues) - found class %d tag 0x%x", | |
604 | hdr.class, hdr.tag); | |
605 | return -1; | |
606 | } | |
607 | wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: attrValues", | |
608 | hdr.payload, hdr.length); | |
609 | pos = hdr.payload; | |
610 | end = hdr.payload + hdr.length; | |
611 | ||
612 | if (is_oid_pkcs9_friendly_name(&a_oid)) | |
613 | return pkcs12_parse_attr_friendly_name(pos, end); | |
614 | if (is_oid_pkcs9_local_key_id(&a_oid)) | |
615 | return pkcs12_parse_attr_local_key_id(pos, end); | |
616 | ||
617 | wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unknown attribute"); | |
618 | return 0; | |
619 | } | |
620 | ||
621 | ||
622 | static int pkcs12_safebag(struct tlsv1_credentials *cred, | |
623 | const u8 *buf, size_t len, const char *passwd) | |
624 | { | |
625 | struct asn1_hdr hdr; | |
626 | struct asn1_oid oid; | |
627 | char obuf[80]; | |
628 | const u8 *pos = buf, *end = buf + len; | |
629 | const u8 *value; | |
630 | size_t value_len; | |
631 | ||
632 | wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: SafeBag", buf, len); | |
633 | ||
634 | /* BAG-TYPE ::= TYPE-IDENTIFIER */ | |
635 | if (asn1_get_oid(pos, end - pos, &oid, &pos)) { | |
636 | wpa_printf(MSG_DEBUG, | |
637 | "PKCS #12: Failed to parse OID (BAG-TYPE)"); | |
638 | return -1; | |
639 | } | |
640 | ||
641 | asn1_oid_to_str(&oid, obuf, sizeof(obuf)); | |
642 | wpa_printf(MSG_DEBUG, "PKCS #12: BAG-TYPE %s", obuf); | |
643 | ||
644 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
645 | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || | |
646 | hdr.tag != 0) { | |
647 | wpa_printf(MSG_DEBUG, | |
648 | "PKCS #12: Expected [0] EXPLICIT (bagValue) - found class %d tag 0x%x", | |
649 | hdr.class, hdr.tag); | |
650 | return 0; | |
651 | } | |
652 | value = hdr.payload; | |
653 | value_len = hdr.length; | |
654 | wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagValue", value, value_len); | |
655 | pos = hdr.payload + hdr.length; | |
656 | ||
657 | if (pos < end) { | |
658 | /* bagAttributes SET OF PKCS12Attribute OPTIONAL */ | |
659 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
660 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
661 | hdr.tag != ASN1_TAG_SET) { | |
662 | wpa_printf(MSG_DEBUG, | |
663 | "PKCS #12: Expected SET (bagAttributes) - found class %d tag 0x%x", | |
664 | hdr.class, hdr.tag); | |
665 | return -1; | |
666 | } | |
667 | wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagAttributes", | |
668 | hdr.payload, hdr.length); | |
669 | ||
670 | pos = hdr.payload; | |
671 | end = hdr.payload + hdr.length; | |
672 | while (pos < end) { | |
673 | /* PKCS12Attribute ::= SEQUENCE */ | |
674 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
675 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
676 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
677 | wpa_printf(MSG_DEBUG, | |
678 | "PKCS #12: Expected SEQUENCE (PKCS12Attribute) - found class %d tag 0x%x", | |
679 | hdr.class, hdr.tag); | |
680 | return -1; | |
681 | } | |
682 | if (pkcs12_parse_attr(hdr.payload, hdr.length) < 0) | |
683 | return -1; | |
684 | pos = hdr.payload + hdr.length; | |
685 | } | |
686 | } | |
687 | ||
688 | if (pkcs12_is_bagtype_oid(&oid, 1)) | |
689 | return pkcs12_keybag(cred, value, value_len); | |
690 | if (pkcs12_is_bagtype_oid(&oid, 2)) | |
691 | return pkcs12_pkcs8_keybag(cred, value, value_len, passwd); | |
692 | if (pkcs12_is_bagtype_oid(&oid, 3)) | |
693 | return pkcs12_certbag(cred, value, value_len); | |
694 | ||
695 | wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unsupported BAG-TYPE"); | |
696 | return 0; | |
697 | } | |
698 | ||
699 | ||
700 | static int pkcs12_safecontents(struct tlsv1_credentials *cred, | |
701 | const u8 *buf, size_t len, | |
702 | const char *passwd) | |
703 | { | |
704 | struct asn1_hdr hdr; | |
705 | const u8 *pos, *end; | |
706 | ||
707 | /* SafeContents ::= SEQUENCE OF SafeBag */ | |
708 | if (asn1_get_next(buf, len, &hdr) < 0 || | |
709 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
710 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
711 | wpa_printf(MSG_DEBUG, | |
712 | "PKCS #12: Expected SEQUENCE (SafeContents) - found class %d tag 0x%x", | |
713 | hdr.class, hdr.tag); | |
714 | return -1; | |
715 | } | |
716 | pos = hdr.payload; | |
717 | end = hdr.payload + hdr.length; | |
718 | ||
719 | /* | |
720 | * SafeBag ::= SEQUENCE { | |
721 | * bagId BAG-TYPE.&id ({PKCS12BagSet}) | |
722 | * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), | |
723 | * bagAttributes SET OF PKCS12Attribute OPTIONAL | |
724 | * } | |
725 | */ | |
726 | ||
727 | while (pos < end) { | |
728 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
729 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
730 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
731 | wpa_printf(MSG_DEBUG, | |
732 | "PKCS #12: Expected SEQUENCE (SafeBag) - found class %d tag 0x%x", | |
733 | hdr.class, hdr.tag); | |
734 | return -1; | |
735 | } | |
736 | if (pkcs12_safebag(cred, hdr.payload, hdr.length, passwd) < 0) | |
737 | return -1; | |
738 | pos = hdr.payload + hdr.length; | |
739 | } | |
740 | ||
741 | return 0; | |
742 | } | |
743 | ||
744 | ||
745 | static int pkcs12_parse_content_data(struct tlsv1_credentials *cred, | |
746 | const u8 *pos, const u8 *end, | |
747 | const char *passwd) | |
748 | { | |
749 | struct asn1_hdr hdr; | |
750 | ||
751 | /* Data ::= OCTET STRING */ | |
752 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
753 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
754 | hdr.tag != ASN1_TAG_OCTETSTRING) { | |
755 | wpa_printf(MSG_DEBUG, | |
756 | "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x", | |
757 | hdr.class, hdr.tag); | |
758 | return -1; | |
759 | } | |
760 | ||
761 | wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data", hdr.payload, hdr.length); | |
762 | ||
763 | return pkcs12_safecontents(cred, hdr.payload, hdr.length, passwd); | |
764 | } | |
765 | ||
766 | ||
767 | static int pkcs12_parse_content_enc_data(struct tlsv1_credentials *cred, | |
768 | const u8 *pos, const u8 *end, | |
769 | const char *passwd) | |
770 | { | |
771 | struct asn1_hdr hdr; | |
772 | struct asn1_oid oid; | |
773 | char buf[80]; | |
774 | const u8 *enc_alg; | |
775 | u8 *data; | |
776 | size_t enc_alg_len, data_len; | |
777 | int res = -1; | |
778 | ||
779 | /* | |
780 | * EncryptedData ::= SEQUENCE { | |
781 | * version Version, | |
782 | * encryptedContentInfo EncryptedContentInfo } | |
783 | */ | |
784 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
785 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
786 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
787 | wpa_printf(MSG_DEBUG, | |
788 | "PKCS #12: Expected SEQUENCE (EncryptedData) - found class %d tag 0x%x", | |
789 | hdr.class, hdr.tag); | |
790 | return 0; | |
791 | } | |
792 | pos = hdr.payload; | |
793 | ||
794 | /* Version ::= INTEGER */ | |
795 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
796 | hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { | |
797 | wpa_printf(MSG_DEBUG, | |
798 | "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x", | |
799 | hdr.class, hdr.tag); | |
800 | return -1; | |
801 | } | |
802 | if (hdr.length != 1 || hdr.payload[0] != 0) { | |
803 | wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized PKCS #7 version"); | |
804 | return -1; | |
805 | } | |
806 | pos = hdr.payload + hdr.length; | |
807 | ||
808 | wpa_hexdump(MSG_MSGDUMP, "PKCS #12: EncryptedContentInfo", | |
809 | pos, end - pos); | |
810 | ||
811 | /* | |
812 | * EncryptedContentInfo ::= SEQUENCE { | |
813 | * contentType ContentType, | |
814 | * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, | |
815 | * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL } | |
816 | */ | |
817 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
818 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
819 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
820 | wpa_printf(MSG_DEBUG, | |
821 | "PKCS #12: Expected SEQUENCE (EncryptedContentInfo) - found class %d tag 0x%x", | |
822 | hdr.class, hdr.tag); | |
823 | return -1; | |
824 | } | |
825 | ||
826 | pos = hdr.payload; | |
827 | end = pos + hdr.length; | |
828 | ||
829 | /* ContentType ::= OBJECT IDENTIFIER */ | |
830 | if (asn1_get_oid(pos, end - pos, &oid, &pos)) { | |
831 | wpa_printf(MSG_DEBUG, | |
832 | "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); | |
833 | return -1; | |
834 | } | |
835 | asn1_oid_to_str(&oid, buf, sizeof(buf)); | |
836 | wpa_printf(MSG_DEBUG, "PKCS #12: EncryptedContentInfo::contentType %s", | |
837 | buf); | |
838 | ||
839 | if (!is_oid_pkcs7_data(&oid)) { | |
840 | wpa_printf(MSG_DEBUG, | |
841 | "PKCS #12: Unsupported EncryptedContentInfo::contentType %s", | |
842 | buf); | |
843 | return 0; | |
844 | } | |
845 | ||
846 | /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */ | |
847 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
848 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
849 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
850 | wpa_printf(MSG_DEBUG, "PKCS #12: Expected SEQUENCE (ContentEncryptionAlgorithmIdentifier) - found class %d tag 0x%x", | |
851 | hdr.class, hdr.tag); | |
852 | return -1; | |
853 | } | |
854 | enc_alg = hdr.payload; | |
855 | enc_alg_len = hdr.length; | |
856 | pos = hdr.payload + hdr.length; | |
857 | ||
858 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
859 | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || | |
860 | hdr.tag != 0) { | |
861 | wpa_printf(MSG_DEBUG, | |
862 | "PKCS #12: Expected [0] IMPLICIT (encryptedContent) - found class %d tag 0x%x", | |
863 | hdr.class, hdr.tag); | |
864 | return -1; | |
865 | } | |
866 | ||
867 | /* EncryptedContent ::= OCTET STRING */ | |
868 | data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length, | |
869 | passwd, &data_len); | |
870 | if (data) { | |
871 | wpa_hexdump_key(MSG_MSGDUMP, | |
872 | "PKCS #12: Decrypted encryptedContent", | |
873 | data, data_len); | |
874 | res = pkcs12_safecontents(cred, data, data_len, passwd); | |
875 | os_free(data); | |
876 | } | |
877 | ||
878 | return res; | |
879 | } | |
880 | ||
881 | ||
882 | static int pkcs12_parse_content(struct tlsv1_credentials *cred, | |
883 | const u8 *buf, size_t len, | |
884 | const char *passwd) | |
885 | { | |
886 | const u8 *pos = buf; | |
887 | const u8 *end = buf + len; | |
888 | struct asn1_oid oid; | |
889 | char txt[80]; | |
890 | struct asn1_hdr hdr; | |
891 | ||
892 | wpa_hexdump(MSG_MSGDUMP, "PKCS #12: ContentInfo", buf, len); | |
893 | ||
894 | if (asn1_get_oid(pos, end - pos, &oid, &pos)) { | |
895 | wpa_printf(MSG_DEBUG, | |
896 | "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); | |
897 | return 0; | |
898 | } | |
899 | ||
900 | asn1_oid_to_str(&oid, txt, sizeof(txt)); | |
901 | wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", txt); | |
902 | ||
903 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
904 | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || | |
905 | hdr.tag != 0) { | |
906 | wpa_printf(MSG_DEBUG, | |
907 | "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x", | |
908 | hdr.class, hdr.tag); | |
909 | return 0; | |
910 | } | |
911 | pos = hdr.payload; | |
912 | ||
913 | if (is_oid_pkcs7_data(&oid)) | |
914 | return pkcs12_parse_content_data(cred, pos, end, passwd); | |
915 | if (is_oid_pkcs7_enc_data(&oid)) | |
916 | return pkcs12_parse_content_enc_data(cred, pos, end, passwd); | |
917 | ||
918 | wpa_printf(MSG_DEBUG, "PKCS #12: Ignored unsupported contentType %s", | |
919 | txt); | |
920 | ||
921 | return 0; | |
922 | } | |
923 | ||
924 | ||
925 | static int pkcs12_parse(struct tlsv1_credentials *cred, | |
926 | const u8 *key, size_t len, const char *passwd) | |
927 | { | |
928 | struct asn1_hdr hdr; | |
929 | const u8 *pos, *end; | |
930 | struct asn1_oid oid; | |
931 | char buf[80]; | |
932 | ||
933 | /* | |
934 | * PFX ::= SEQUENCE { | |
935 | * version INTEGER {v3(3)}(v3,...), | |
936 | * authSafe ContentInfo, | |
937 | * macData MacData OPTIONAL | |
938 | * } | |
939 | */ | |
940 | ||
941 | if (asn1_get_next(key, len, &hdr) < 0 || | |
942 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
943 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
944 | wpa_printf(MSG_DEBUG, | |
945 | "PKCS #12: Expected SEQUENCE (PFX) - found class %d tag 0x%x; assume PKCS #12 not used", | |
946 | hdr.class, hdr.tag); | |
947 | return -1; | |
948 | } | |
949 | ||
950 | pos = hdr.payload; | |
951 | end = pos + hdr.length; | |
952 | ||
953 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
954 | hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { | |
955 | wpa_printf(MSG_DEBUG, | |
956 | "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x", | |
957 | hdr.class, hdr.tag); | |
958 | return -1; | |
959 | } | |
960 | if (hdr.length != 1 || hdr.payload[0] != 3) { | |
961 | wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized version"); | |
962 | return -1; | |
963 | } | |
964 | pos = hdr.payload + hdr.length; | |
965 | ||
966 | /* | |
967 | * ContentInfo ::= SEQUENCE { | |
968 | * contentType ContentType, | |
969 | * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } | |
970 | */ | |
971 | ||
972 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
973 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
974 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
975 | wpa_printf(MSG_DEBUG, | |
976 | "PKCS #12: Expected SEQUENCE (authSafe) - found class %d tag 0x%x; assume PKCS #12 not used", | |
977 | hdr.class, hdr.tag); | |
978 | return -1; | |
979 | } | |
980 | ||
981 | pos = hdr.payload; | |
982 | end = pos + hdr.length; | |
983 | ||
984 | /* ContentType ::= OBJECT IDENTIFIER */ | |
985 | if (asn1_get_oid(pos, end - pos, &oid, &pos)) { | |
986 | wpa_printf(MSG_DEBUG, | |
987 | "PKCS #12: Could not find OBJECT IDENTIFIER (contentType); assume PKCS #12 not used"); | |
988 | return -1; | |
989 | } | |
990 | asn1_oid_to_str(&oid, buf, sizeof(buf)); | |
991 | wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", buf); | |
992 | if (!is_oid_pkcs7_data(&oid)) { | |
993 | wpa_printf(MSG_DEBUG, "PKCS #12: Unsupported contentType %s", | |
994 | buf); | |
995 | return -1; | |
996 | } | |
997 | ||
998 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
999 | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || | |
1000 | hdr.tag != 0) { | |
1001 | wpa_printf(MSG_DEBUG, | |
1002 | "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x; assume PKCS #12 not used", | |
1003 | hdr.class, hdr.tag); | |
1004 | return -1; | |
1005 | } | |
1006 | ||
1007 | pos = hdr.payload; | |
1008 | ||
1009 | /* Data ::= OCTET STRING */ | |
1010 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
1011 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1012 | hdr.tag != ASN1_TAG_OCTETSTRING) { | |
1013 | wpa_printf(MSG_DEBUG, | |
1014 | "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x; assume PKCS #12 not used", | |
1015 | hdr.class, hdr.tag); | |
1016 | return -1; | |
1017 | } | |
1018 | ||
1019 | /* | |
1020 | * AuthenticatedSafe ::= SEQUENCE OF ContentInfo | |
1021 | * -- Data if unencrypted | |
1022 | * -- EncryptedData if password-encrypted | |
1023 | * -- EnvelopedData if public key-encrypted | |
1024 | */ | |
1025 | wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data content", | |
1026 | hdr.payload, hdr.length); | |
1027 | ||
1028 | if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || | |
1029 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1030 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1031 | wpa_printf(MSG_DEBUG, | |
1032 | "PKCS #12: Expected SEQUENCE within Data content - found class %d tag 0x%x; assume PKCS #12 not used", | |
1033 | hdr.class, hdr.tag); | |
1034 | return -1; | |
1035 | } | |
1036 | ||
1037 | pos = hdr.payload; | |
1038 | end = pos + hdr.length; | |
1039 | ||
1040 | while (end > pos) { | |
1041 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
1042 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1043 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1044 | wpa_printf(MSG_DEBUG, | |
1045 | "PKCS #12: Expected SEQUENCE (ContentInfo) - found class %d tag 0x%x; assume PKCS #12 not used", | |
1046 | hdr.class, hdr.tag); | |
1047 | return -1; | |
1048 | } | |
1049 | if (pkcs12_parse_content(cred, hdr.payload, hdr.length, | |
1050 | passwd) < 0) | |
1051 | return -1; | |
1052 | ||
1053 | pos = hdr.payload + hdr.length; | |
1054 | } | |
1055 | ||
1056 | return 0; | |
1057 | } | |
1058 | ||
1059 | #endif /* PKCS12_FUNCS */ | |
1060 | ||
1061 | ||
6fc6879b | 1062 | static int tlsv1_set_key(struct tlsv1_credentials *cred, |
3f4ed97a | 1063 | const u8 *key, size_t len, const char *passwd) |
6fc6879b | 1064 | { |
3f4ed97a | 1065 | cred->key = crypto_private_key_import(key, len, passwd); |
1b8409a0 | 1066 | if (cred->key == NULL) |
3af9f298 | 1067 | cred->key = tlsv1_set_key_pem(key, len); |
3f4ed97a | 1068 | if (cred->key == NULL) |
3af9f298 | 1069 | cred->key = tlsv1_set_key_enc_pem(key, len, passwd); |
6b7bb429 JM |
1070 | #ifdef PKCS12_FUNCS |
1071 | if (!cred->key) | |
1072 | pkcs12_parse(cred, key, len, passwd); | |
1073 | #endif /* PKCS12_FUNCS */ | |
6fc6879b JM |
1074 | if (cred->key == NULL) { |
1075 | wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); | |
1076 | return -1; | |
1077 | } | |
1078 | return 0; | |
1079 | } | |
1080 | ||
1081 | ||
1082 | /** | |
1083 | * tlsv1_set_private_key - Set private key | |
1084 | * @cred: TLSv1 credentials from tlsv1_cred_alloc() | |
1085 | * @private_key: File or reference name for the key in PEM or DER format | |
1086 | * @private_key_passwd: Passphrase for decrypted private key, %NULL if no | |
1087 | * passphrase is used. | |
1088 | * @private_key_blob: private_key as inlined data or %NULL if not used | |
1089 | * @private_key_blob_len: private_key_blob length | |
1090 | * Returns: 0 on success, -1 on failure | |
1091 | */ | |
1092 | int tlsv1_set_private_key(struct tlsv1_credentials *cred, | |
1093 | const char *private_key, | |
1094 | const char *private_key_passwd, | |
1095 | const u8 *private_key_blob, | |
1096 | size_t private_key_blob_len) | |
1097 | { | |
1098 | crypto_private_key_free(cred->key); | |
1099 | cred->key = NULL; | |
1100 | ||
1101 | if (private_key_blob) | |
1102 | return tlsv1_set_key(cred, private_key_blob, | |
3f4ed97a JM |
1103 | private_key_blob_len, |
1104 | private_key_passwd); | |
6fc6879b JM |
1105 | |
1106 | if (private_key) { | |
1107 | u8 *buf; | |
1108 | size_t len; | |
1109 | int ret; | |
1110 | ||
1111 | buf = (u8 *) os_readfile(private_key, &len); | |
1112 | if (buf == NULL) { | |
1113 | wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", | |
1114 | private_key); | |
1115 | return -1; | |
1116 | } | |
1117 | ||
3f4ed97a | 1118 | ret = tlsv1_set_key(cred, buf, len, private_key_passwd); |
6fc6879b JM |
1119 | os_free(buf); |
1120 | return ret; | |
1121 | } | |
1122 | ||
1123 | return 0; | |
1124 | } | |
1125 | ||
1126 | ||
1127 | static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, | |
1128 | const u8 *dh, size_t len) | |
1129 | { | |
1130 | struct asn1_hdr hdr; | |
1131 | const u8 *pos, *end; | |
1132 | ||
1133 | pos = dh; | |
1134 | end = dh + len; | |
1135 | ||
1136 | /* | |
1137 | * DHParameter ::= SEQUENCE { | |
1138 | * prime INTEGER, -- p | |
1139 | * base INTEGER, -- g | |
1140 | * privateValueLength INTEGER OPTIONAL } | |
1141 | */ | |
1142 | ||
1143 | /* DHParamer ::= SEQUENCE */ | |
1144 | if (asn1_get_next(pos, len, &hdr) < 0 || | |
1145 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1146 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1147 | wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a " | |
1148 | "valid SEQUENCE - found class %d tag 0x%x", | |
1149 | hdr.class, hdr.tag); | |
1150 | return -1; | |
1151 | } | |
1152 | pos = hdr.payload; | |
1153 | ||
1154 | /* prime INTEGER */ | |
1155 | if (asn1_get_next(pos, end - pos, &hdr) < 0) | |
1156 | return -1; | |
1157 | ||
1158 | if (hdr.class != ASN1_CLASS_UNIVERSAL || | |
1159 | hdr.tag != ASN1_TAG_INTEGER) { | |
1160 | wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; " | |
1161 | "class=%d tag=0x%x", hdr.class, hdr.tag); | |
1162 | return -1; | |
1163 | } | |
1164 | ||
1165 | wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length); | |
1166 | if (hdr.length == 0) | |
1167 | return -1; | |
1168 | os_free(cred->dh_p); | |
a1f11e34 | 1169 | cred->dh_p = os_memdup(hdr.payload, hdr.length); |
6fc6879b JM |
1170 | if (cred->dh_p == NULL) |
1171 | return -1; | |
6fc6879b JM |
1172 | cred->dh_p_len = hdr.length; |
1173 | pos = hdr.payload + hdr.length; | |
1174 | ||
1175 | /* base INTEGER */ | |
1176 | if (asn1_get_next(pos, end - pos, &hdr) < 0) | |
1177 | return -1; | |
1178 | ||
1179 | if (hdr.class != ASN1_CLASS_UNIVERSAL || | |
1180 | hdr.tag != ASN1_TAG_INTEGER) { | |
1181 | wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; " | |
1182 | "class=%d tag=0x%x", hdr.class, hdr.tag); | |
1183 | return -1; | |
1184 | } | |
1185 | ||
1186 | wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length); | |
1187 | if (hdr.length == 0) | |
1188 | return -1; | |
1189 | os_free(cred->dh_g); | |
a1f11e34 | 1190 | cred->dh_g = os_memdup(hdr.payload, hdr.length); |
6fc6879b JM |
1191 | if (cred->dh_g == NULL) |
1192 | return -1; | |
6fc6879b JM |
1193 | cred->dh_g_len = hdr.length; |
1194 | ||
1195 | return 0; | |
1196 | } | |
1197 | ||
1198 | ||
1199 | static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----"; | |
1200 | static const char *pem_dhparams_end = "-----END DH PARAMETERS-----"; | |
1201 | ||
1202 | ||
1203 | static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred, | |
1204 | const u8 *buf, size_t len) | |
1205 | { | |
1206 | const u8 *pos, *end; | |
1207 | unsigned char *der; | |
1208 | size_t der_len; | |
1209 | ||
1210 | pos = search_tag(pem_dhparams_begin, buf, len); | |
1211 | if (!pos) { | |
1212 | wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - " | |
1213 | "assume DER format"); | |
1214 | return tlsv1_set_dhparams_der(cred, buf, len); | |
1215 | } | |
1216 | ||
1217 | wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER " | |
1218 | "format"); | |
1219 | ||
1220 | pos += os_strlen(pem_dhparams_begin); | |
1221 | end = search_tag(pem_dhparams_end, pos, buf + len - pos); | |
1222 | if (end == NULL) { | |
1223 | wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end " | |
1224 | "tag (%s)", pem_dhparams_end); | |
1225 | return -1; | |
1226 | } | |
1227 | ||
8e5e36a1 | 1228 | der = base64_decode((const char *) pos, end - pos, &der_len); |
6fc6879b JM |
1229 | if (der == NULL) { |
1230 | wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams"); | |
1231 | return -1; | |
1232 | } | |
1233 | ||
1234 | if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) { | |
1235 | wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams " | |
1236 | "DER conversion"); | |
1237 | os_free(der); | |
1238 | return -1; | |
1239 | } | |
1240 | ||
1241 | os_free(der); | |
1242 | ||
1243 | return 0; | |
1244 | } | |
1245 | ||
1246 | ||
1247 | /** | |
1248 | * tlsv1_set_dhparams - Set Diffie-Hellman parameters | |
1249 | * @cred: TLSv1 credentials from tlsv1_cred_alloc() | |
1250 | * @dh_file: File or reference name for the DH params in PEM or DER format | |
1251 | * @dh_blob: DH params as inlined data or %NULL if not used | |
1252 | * @dh_blob_len: dh_blob length | |
1253 | * Returns: 0 on success, -1 on failure | |
1254 | */ | |
1255 | int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, | |
1256 | const u8 *dh_blob, size_t dh_blob_len) | |
1257 | { | |
1258 | if (dh_blob) | |
1259 | return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len); | |
1260 | ||
1261 | if (dh_file) { | |
1262 | u8 *buf; | |
1263 | size_t len; | |
1264 | int ret; | |
1265 | ||
1266 | buf = (u8 *) os_readfile(dh_file, &len); | |
1267 | if (buf == NULL) { | |
1268 | wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", | |
1269 | dh_file); | |
1270 | return -1; | |
1271 | } | |
1272 | ||
1273 | ret = tlsv1_set_dhparams_blob(cred, buf, len); | |
1274 | os_free(buf); | |
1275 | return ret; | |
1276 | } | |
1277 | ||
1278 | return 0; | |
1279 | } |