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