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