xref: /freebsd/contrib/wpa/src/tls/tlsv1_cred.c (revision 9a14aa017b21c292740c00ee098195cd46642730)
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 
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 static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
72 static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
73 static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
74 static const char *pem_key2_end = "-----END PRIVATE KEY-----";
75 static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
76 static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
77 
78 
79 static 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 
96 static 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 
146 static 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  */
183 int 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  */
210 int 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 
218 static 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 
250 static 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 
278 static 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  */
304 int 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 
339 static 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 
413 static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
414 static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
415 
416 
417 static 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  */
469 int 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