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