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