blob: 067562b6b3d0c0f2d44ea9312a763354a9af8287 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * TLSv1 credentials
3 * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
4 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "base64.h"
13#include "crypto/crypto.h"
14#include "x509v3.h"
15#include "tlsv1_cred.h"
16
17
18struct tlsv1_credentials * tlsv1_cred_alloc(void)
19{
20 struct tlsv1_credentials *cred;
21 cred = os_zalloc(sizeof(*cred));
22 return cred;
23}
24
25
26void tlsv1_cred_free(struct tlsv1_credentials *cred)
27{
28 if (cred == NULL)
29 return;
30
31 x509_certificate_chain_free(cred->trusted_certs);
32 x509_certificate_chain_free(cred->cert);
33 crypto_private_key_free(cred->key);
34 os_free(cred->dh_p);
35 os_free(cred->dh_g);
36 os_free(cred);
37}
38
39
40static int tlsv1_add_cert_der(struct x509_certificate **chain,
41 const u8 *buf, size_t len)
42{
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080043 struct x509_certificate *cert, *p;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070044 char name[128];
45
46 cert = x509_certificate_parse(buf, len);
47 if (cert == NULL) {
48 wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
49 __func__);
50 return -1;
51 }
52
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080053 p = *chain;
54 while (p && p->next)
55 p = p->next;
56 if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
57 /*
58 * The new certificate is the issuer of the last certificate in
59 * the chain - add the new certificate to the end.
60 */
61 p->next = cert;
62 } else {
63 /* Add to the beginning of the chain */
64 cert->next = *chain;
65 *chain = cert;
66 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070067
68 x509_name_string(&cert->subject, name, sizeof(name));
69 wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
70
71 return 0;
72}
73
74
75static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
76static const char *pem_cert_end = "-----END CERTIFICATE-----";
77static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
78static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
79static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
80static const char *pem_key2_end = "-----END PRIVATE KEY-----";
81static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
82static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
83
84
85static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
86{
87 size_t i, plen;
88
89 plen = os_strlen(tag);
90 if (len < plen)
91 return NULL;
92
93 for (i = 0; i < len - plen; i++) {
94 if (os_memcmp(buf + i, tag, plen) == 0)
95 return buf + i;
96 }
97
98 return NULL;
99}
100
101
102static int tlsv1_add_cert(struct x509_certificate **chain,
103 const u8 *buf, size_t len)
104{
105 const u8 *pos, *end;
106 unsigned char *der;
107 size_t der_len;
108
109 pos = search_tag(pem_cert_begin, buf, len);
110 if (!pos) {
111 wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
112 "assume DER format");
113 return tlsv1_add_cert_der(chain, buf, len);
114 }
115
116 wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
117 "DER format");
118
119 while (pos) {
120 pos += os_strlen(pem_cert_begin);
121 end = search_tag(pem_cert_end, pos, buf + len - pos);
122 if (end == NULL) {
123 wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
124 "certificate end tag (%s)", pem_cert_end);
125 return -1;
126 }
127
128 der = base64_decode(pos, end - pos, &der_len);
129 if (der == NULL) {
130 wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
131 "certificate");
132 return -1;
133 }
134
135 if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
136 wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
137 "certificate after DER conversion");
138 os_free(der);
139 return -1;
140 }
141
142 os_free(der);
143
144 end += os_strlen(pem_cert_end);
145 pos = search_tag(pem_cert_begin, end, buf + len - end);
146 }
147
148 return 0;
149}
150
151
152static int tlsv1_set_cert_chain(struct x509_certificate **chain,
153 const char *cert, const u8 *cert_blob,
154 size_t cert_blob_len)
155{
156 if (cert_blob)
157 return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
158
159 if (cert) {
160 u8 *buf;
161 size_t len;
162 int ret;
163
164 buf = (u8 *) os_readfile(cert, &len);
165 if (buf == NULL) {
166 wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
167 cert);
168 return -1;
169 }
170
171 ret = tlsv1_add_cert(chain, buf, len);
172 os_free(buf);
173 return ret;
174 }
175
176 return 0;
177}
178
179
180/**
181 * tlsv1_set_ca_cert - Set trusted CA certificate(s)
182 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
183 * @cert: File or reference name for X.509 certificate in PEM or DER format
184 * @cert_blob: cert as inlined data or %NULL if not used
185 * @cert_blob_len: ca_cert_blob length
186 * @path: Path to CA certificates (not yet supported)
187 * Returns: 0 on success, -1 on failure
188 */
189int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
190 const u8 *cert_blob, size_t cert_blob_len,
191 const char *path)
192{
Dmitry Shmidtd7ff03d2015-12-04 14:49:35 -0800193 if (cert && os_strncmp(cert, "hash://", 7) == 0) {
194 const char *pos = cert + 7;
195 if (os_strncmp(pos, "server/sha256/", 14) != 0) {
196 wpa_printf(MSG_DEBUG,
197 "TLSv1: Unsupported ca_cert hash value '%s'",
198 cert);
199 return -1;
200 }
201 pos += 14;
202 if (os_strlen(pos) != 32 * 2) {
203 wpa_printf(MSG_DEBUG,
204 "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'",
205 cert);
206 return -1;
207 }
208 if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) {
209 wpa_printf(MSG_DEBUG,
210 "TLSv1: Invalid SHA256 hash value in ca_cert '%s'",
211 cert);
212 return -1;
213 }
214 cred->server_cert_only = 1;
215 cred->ca_cert_verify = 0;
216 wpa_printf(MSG_DEBUG,
217 "TLSv1: Checking only server certificate match");
218 return 0;
219 }
220
221 if (cert && os_strncmp(cert, "probe://", 8) == 0) {
222 cred->cert_probe = 1;
223 cred->ca_cert_verify = 0;
224 wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate");
225 return 0;
226 }
227
228 cred->ca_cert_verify = cert || cert_blob || path;
229
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700230 if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
231 cert_blob, cert_blob_len) < 0)
232 return -1;
233
234 if (path) {
235 /* TODO: add support for reading number of certificate files */
236 wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
237 "not yet supported");
238 return -1;
239 }
240
241 return 0;
242}
243
244
245/**
246 * tlsv1_set_cert - Set certificate
247 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
248 * @cert: File or reference name for X.509 certificate in PEM or DER format
249 * @cert_blob: cert as inlined data or %NULL if not used
250 * @cert_blob_len: cert_blob length
251 * Returns: 0 on success, -1 on failure
252 */
253int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
254 const u8 *cert_blob, size_t cert_blob_len)
255{
256 return tlsv1_set_cert_chain(&cred->cert, cert,
257 cert_blob, cert_blob_len);
258}
259
260
261static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
262{
263 const u8 *pos, *end;
264 unsigned char *der;
265 size_t der_len;
266 struct crypto_private_key *pkey;
267
268 pos = search_tag(pem_key_begin, key, len);
269 if (!pos) {
270 pos = search_tag(pem_key2_begin, key, len);
271 if (!pos)
272 return NULL;
273 pos += os_strlen(pem_key2_begin);
274 end = search_tag(pem_key2_end, pos, key + len - pos);
275 if (!end)
276 return NULL;
277 } else {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800278 const u8 *pos2;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700279 pos += os_strlen(pem_key_begin);
280 end = search_tag(pem_key_end, pos, key + len - pos);
281 if (!end)
282 return NULL;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800283 pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
284 if (pos2) {
285 wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
286 "format (Proc-Type/DEK-Info)");
287 return NULL;
288 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700289 }
290
291 der = base64_decode(pos, end - pos, &der_len);
292 if (!der)
293 return NULL;
294 pkey = crypto_private_key_import(der, der_len, NULL);
295 os_free(der);
296 return pkey;
297}
298
299
300static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
301 size_t len,
302 const char *passwd)
303{
304 const u8 *pos, *end;
305 unsigned char *der;
306 size_t der_len;
307 struct crypto_private_key *pkey;
308
309 if (passwd == NULL)
310 return NULL;
311 pos = search_tag(pem_key_enc_begin, key, len);
312 if (!pos)
313 return NULL;
314 pos += os_strlen(pem_key_enc_begin);
315 end = search_tag(pem_key_enc_end, pos, key + len - pos);
316 if (!end)
317 return NULL;
318
319 der = base64_decode(pos, end - pos, &der_len);
320 if (!der)
321 return NULL;
322 pkey = crypto_private_key_import(der, der_len, passwd);
323 os_free(der);
324 return pkey;
325}
326
327
328static int tlsv1_set_key(struct tlsv1_credentials *cred,
329 const u8 *key, size_t len, const char *passwd)
330{
331 cred->key = crypto_private_key_import(key, len, passwd);
332 if (cred->key == NULL)
333 cred->key = tlsv1_set_key_pem(key, len);
334 if (cred->key == NULL)
335 cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
336 if (cred->key == NULL) {
337 wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
338 return -1;
339 }
340 return 0;
341}
342
343
344/**
345 * tlsv1_set_private_key - Set private key
346 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
347 * @private_key: File or reference name for the key in PEM or DER format
348 * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
349 * passphrase is used.
350 * @private_key_blob: private_key as inlined data or %NULL if not used
351 * @private_key_blob_len: private_key_blob length
352 * Returns: 0 on success, -1 on failure
353 */
354int tlsv1_set_private_key(struct tlsv1_credentials *cred,
355 const char *private_key,
356 const char *private_key_passwd,
357 const u8 *private_key_blob,
358 size_t private_key_blob_len)
359{
360 crypto_private_key_free(cred->key);
361 cred->key = NULL;
362
363 if (private_key_blob)
364 return tlsv1_set_key(cred, private_key_blob,
365 private_key_blob_len,
366 private_key_passwd);
367
368 if (private_key) {
369 u8 *buf;
370 size_t len;
371 int ret;
372
373 buf = (u8 *) os_readfile(private_key, &len);
374 if (buf == NULL) {
375 wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
376 private_key);
377 return -1;
378 }
379
380 ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
381 os_free(buf);
382 return ret;
383 }
384
385 return 0;
386}
387
388
389static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
390 const u8 *dh, size_t len)
391{
392 struct asn1_hdr hdr;
393 const u8 *pos, *end;
394
395 pos = dh;
396 end = dh + len;
397
398 /*
399 * DHParameter ::= SEQUENCE {
400 * prime INTEGER, -- p
401 * base INTEGER, -- g
402 * privateValueLength INTEGER OPTIONAL }
403 */
404
405 /* DHParamer ::= SEQUENCE */
406 if (asn1_get_next(pos, len, &hdr) < 0 ||
407 hdr.class != ASN1_CLASS_UNIVERSAL ||
408 hdr.tag != ASN1_TAG_SEQUENCE) {
409 wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
410 "valid SEQUENCE - found class %d tag 0x%x",
411 hdr.class, hdr.tag);
412 return -1;
413 }
414 pos = hdr.payload;
415
416 /* prime INTEGER */
417 if (asn1_get_next(pos, end - pos, &hdr) < 0)
418 return -1;
419
420 if (hdr.class != ASN1_CLASS_UNIVERSAL ||
421 hdr.tag != ASN1_TAG_INTEGER) {
422 wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
423 "class=%d tag=0x%x", hdr.class, hdr.tag);
424 return -1;
425 }
426
427 wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
428 if (hdr.length == 0)
429 return -1;
430 os_free(cred->dh_p);
431 cred->dh_p = os_malloc(hdr.length);
432 if (cred->dh_p == NULL)
433 return -1;
434 os_memcpy(cred->dh_p, hdr.payload, hdr.length);
435 cred->dh_p_len = hdr.length;
436 pos = hdr.payload + hdr.length;
437
438 /* base INTEGER */
439 if (asn1_get_next(pos, end - pos, &hdr) < 0)
440 return -1;
441
442 if (hdr.class != ASN1_CLASS_UNIVERSAL ||
443 hdr.tag != ASN1_TAG_INTEGER) {
444 wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
445 "class=%d tag=0x%x", hdr.class, hdr.tag);
446 return -1;
447 }
448
449 wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
450 if (hdr.length == 0)
451 return -1;
452 os_free(cred->dh_g);
453 cred->dh_g = os_malloc(hdr.length);
454 if (cred->dh_g == NULL)
455 return -1;
456 os_memcpy(cred->dh_g, hdr.payload, hdr.length);
457 cred->dh_g_len = hdr.length;
458
459 return 0;
460}
461
462
463static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
464static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
465
466
467static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
468 const u8 *buf, size_t len)
469{
470 const u8 *pos, *end;
471 unsigned char *der;
472 size_t der_len;
473
474 pos = search_tag(pem_dhparams_begin, buf, len);
475 if (!pos) {
476 wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
477 "assume DER format");
478 return tlsv1_set_dhparams_der(cred, buf, len);
479 }
480
481 wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
482 "format");
483
484 pos += os_strlen(pem_dhparams_begin);
485 end = search_tag(pem_dhparams_end, pos, buf + len - pos);
486 if (end == NULL) {
487 wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
488 "tag (%s)", pem_dhparams_end);
489 return -1;
490 }
491
492 der = base64_decode(pos, end - pos, &der_len);
493 if (der == NULL) {
494 wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
495 return -1;
496 }
497
498 if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
499 wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
500 "DER conversion");
501 os_free(der);
502 return -1;
503 }
504
505 os_free(der);
506
507 return 0;
508}
509
510
511/**
512 * tlsv1_set_dhparams - Set Diffie-Hellman parameters
513 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
514 * @dh_file: File or reference name for the DH params in PEM or DER format
515 * @dh_blob: DH params as inlined data or %NULL if not used
516 * @dh_blob_len: dh_blob length
517 * Returns: 0 on success, -1 on failure
518 */
519int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
520 const u8 *dh_blob, size_t dh_blob_len)
521{
522 if (dh_blob)
523 return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
524
525 if (dh_file) {
526 u8 *buf;
527 size_t len;
528 int ret;
529
530 buf = (u8 *) os_readfile(dh_file, &len);
531 if (buf == NULL) {
532 wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
533 dh_file);
534 return -1;
535 }
536
537 ret = tlsv1_set_dhparams_blob(cred, buf, len);
538 os_free(buf);
539 return ret;
540 }
541
542 return 0;
543}