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