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 
24 struct tlsv1_credentials * tlsv1_cred_alloc(void)
25 {
26 	struct tlsv1_credentials *cred;
27 	cred = os_zalloc(sizeof(*cred));
28 	return cred;
29 }
30 
31 
32 void 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 
46 static 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 
69 static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
70 static const char *pem_cert_end = "-----END CERTIFICATE-----";
71 
72 
73 static 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 
90 static 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 
140 static 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  */
177 int 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  */
204 int 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 
212 static 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  */
234 int 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 
268 static 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 
342 static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
343 static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
344 
345 
346 static 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  */
398 int 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