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