tlsv1_cred.c revision c5ec7f57ead87efa365800228aa0b09a12d9e6c4
1/*
2 * TLSv1 credentials
3 * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
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{
43	struct x509_certificate *cert, *p;
44	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
53	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	}
67
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{
193	if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
194				 cert_blob, cert_blob_len) < 0)
195		return -1;
196
197	if (path) {
198		/* TODO: add support for reading number of certificate files */
199		wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
200			   "not yet supported");
201		return -1;
202	}
203
204	return 0;
205}
206
207
208/**
209 * tlsv1_set_cert - Set certificate
210 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
211 * @cert: File or reference name for X.509 certificate in PEM or DER format
212 * @cert_blob: cert as inlined data or %NULL if not used
213 * @cert_blob_len: cert_blob length
214 * Returns: 0 on success, -1 on failure
215 */
216int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
217		   const u8 *cert_blob, size_t cert_blob_len)
218{
219	return tlsv1_set_cert_chain(&cred->cert, cert,
220				    cert_blob, cert_blob_len);
221}
222
223
224static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
225{
226	const u8 *pos, *end;
227	unsigned char *der;
228	size_t der_len;
229	struct crypto_private_key *pkey;
230
231	pos = search_tag(pem_key_begin, key, len);
232	if (!pos) {
233		pos = search_tag(pem_key2_begin, key, len);
234		if (!pos)
235			return NULL;
236		pos += os_strlen(pem_key2_begin);
237		end = search_tag(pem_key2_end, pos, key + len - pos);
238		if (!end)
239			return NULL;
240	} else {
241		const u8 *pos2;
242		pos += os_strlen(pem_key_begin);
243		end = search_tag(pem_key_end, pos, key + len - pos);
244		if (!end)
245			return NULL;
246		pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
247		if (pos2) {
248			wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
249				   "format (Proc-Type/DEK-Info)");
250			return NULL;
251		}
252	}
253
254	der = base64_decode(pos, end - pos, &der_len);
255	if (!der)
256		return NULL;
257	pkey = crypto_private_key_import(der, der_len, NULL);
258	os_free(der);
259	return pkey;
260}
261
262
263static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
264							 size_t len,
265							 const char *passwd)
266{
267	const u8 *pos, *end;
268	unsigned char *der;
269	size_t der_len;
270	struct crypto_private_key *pkey;
271
272	if (passwd == NULL)
273		return NULL;
274	pos = search_tag(pem_key_enc_begin, key, len);
275	if (!pos)
276		return NULL;
277	pos += os_strlen(pem_key_enc_begin);
278	end = search_tag(pem_key_enc_end, pos, key + len - pos);
279	if (!end)
280		return NULL;
281
282	der = base64_decode(pos, end - pos, &der_len);
283	if (!der)
284		return NULL;
285	pkey = crypto_private_key_import(der, der_len, passwd);
286	os_free(der);
287	return pkey;
288}
289
290
291static int tlsv1_set_key(struct tlsv1_credentials *cred,
292			 const u8 *key, size_t len, const char *passwd)
293{
294	cred->key = crypto_private_key_import(key, len, passwd);
295	if (cred->key == NULL)
296		cred->key = tlsv1_set_key_pem(key, len);
297	if (cred->key == NULL)
298		cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
299	if (cred->key == NULL) {
300		wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
301		return -1;
302	}
303	return 0;
304}
305
306
307/**
308 * tlsv1_set_private_key - Set private key
309 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
310 * @private_key: File or reference name for the key in PEM or DER format
311 * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
312 * passphrase is used.
313 * @private_key_blob: private_key as inlined data or %NULL if not used
314 * @private_key_blob_len: private_key_blob length
315 * Returns: 0 on success, -1 on failure
316 */
317int tlsv1_set_private_key(struct tlsv1_credentials *cred,
318			  const char *private_key,
319			  const char *private_key_passwd,
320			  const u8 *private_key_blob,
321			  size_t private_key_blob_len)
322{
323	crypto_private_key_free(cred->key);
324	cred->key = NULL;
325
326	if (private_key_blob)
327		return tlsv1_set_key(cred, private_key_blob,
328				     private_key_blob_len,
329				     private_key_passwd);
330
331	if (private_key) {
332		u8 *buf;
333		size_t len;
334		int ret;
335
336		buf = (u8 *) os_readfile(private_key, &len);
337		if (buf == NULL) {
338			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
339				   private_key);
340			return -1;
341		}
342
343		ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
344		os_free(buf);
345		return ret;
346	}
347
348	return 0;
349}
350
351
352static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
353				  const u8 *dh, size_t len)
354{
355	struct asn1_hdr hdr;
356	const u8 *pos, *end;
357
358	pos = dh;
359	end = dh + len;
360
361	/*
362	 * DHParameter ::= SEQUENCE {
363	 *   prime INTEGER, -- p
364	 *   base INTEGER, -- g
365	 *   privateValueLength INTEGER OPTIONAL }
366	 */
367
368	/* DHParamer ::= SEQUENCE */
369	if (asn1_get_next(pos, len, &hdr) < 0 ||
370	    hdr.class != ASN1_CLASS_UNIVERSAL ||
371	    hdr.tag != ASN1_TAG_SEQUENCE) {
372		wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
373			   "valid SEQUENCE - found class %d tag 0x%x",
374			   hdr.class, hdr.tag);
375		return -1;
376	}
377	pos = hdr.payload;
378
379	/* prime INTEGER */
380	if (asn1_get_next(pos, end - pos, &hdr) < 0)
381		return -1;
382
383	if (hdr.class != ASN1_CLASS_UNIVERSAL ||
384	    hdr.tag != ASN1_TAG_INTEGER) {
385		wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
386			   "class=%d tag=0x%x", hdr.class, hdr.tag);
387		return -1;
388	}
389
390	wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
391	if (hdr.length == 0)
392		return -1;
393	os_free(cred->dh_p);
394	cred->dh_p = os_malloc(hdr.length);
395	if (cred->dh_p == NULL)
396		return -1;
397	os_memcpy(cred->dh_p, hdr.payload, hdr.length);
398	cred->dh_p_len = hdr.length;
399	pos = hdr.payload + hdr.length;
400
401	/* base INTEGER */
402	if (asn1_get_next(pos, end - pos, &hdr) < 0)
403		return -1;
404
405	if (hdr.class != ASN1_CLASS_UNIVERSAL ||
406	    hdr.tag != ASN1_TAG_INTEGER) {
407		wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
408			   "class=%d tag=0x%x", hdr.class, hdr.tag);
409		return -1;
410	}
411
412	wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
413	if (hdr.length == 0)
414		return -1;
415	os_free(cred->dh_g);
416	cred->dh_g = os_malloc(hdr.length);
417	if (cred->dh_g == NULL)
418		return -1;
419	os_memcpy(cred->dh_g, hdr.payload, hdr.length);
420	cred->dh_g_len = hdr.length;
421
422	return 0;
423}
424
425
426static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
427static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
428
429
430static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
431				   const u8 *buf, size_t len)
432{
433	const u8 *pos, *end;
434	unsigned char *der;
435	size_t der_len;
436
437	pos = search_tag(pem_dhparams_begin, buf, len);
438	if (!pos) {
439		wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
440			   "assume DER format");
441		return tlsv1_set_dhparams_der(cred, buf, len);
442	}
443
444	wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
445		   "format");
446
447	pos += os_strlen(pem_dhparams_begin);
448	end = search_tag(pem_dhparams_end, pos, buf + len - pos);
449	if (end == NULL) {
450		wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
451			   "tag (%s)", pem_dhparams_end);
452		return -1;
453	}
454
455	der = base64_decode(pos, end - pos, &der_len);
456	if (der == NULL) {
457		wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
458		return -1;
459	}
460
461	if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
462		wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
463			   "DER conversion");
464		os_free(der);
465		return -1;
466	}
467
468	os_free(der);
469
470	return 0;
471}
472
473
474/**
475 * tlsv1_set_dhparams - Set Diffie-Hellman parameters
476 * @cred: TLSv1 credentials from tlsv1_cred_alloc()
477 * @dh_file: File or reference name for the DH params in PEM or DER format
478 * @dh_blob: DH params as inlined data or %NULL if not used
479 * @dh_blob_len: dh_blob length
480 * Returns: 0 on success, -1 on failure
481 */
482int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
483		       const u8 *dh_blob, size_t dh_blob_len)
484{
485	if (dh_blob)
486		return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
487
488	if (dh_file) {
489		u8 *buf;
490		size_t len;
491		int ret;
492
493		buf = (u8 *) os_readfile(dh_file, &len);
494		if (buf == NULL) {
495			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
496				   dh_file);
497			return -1;
498		}
499
500		ret = tlsv1_set_dhparams_blob(cred, buf, len);
501		os_free(buf);
502		return ret;
503	}
504
505	return 0;
506}
507