Home | History | Annotate | Download | only in tls
      1 /*
      2  * TLSv1 credentials
      3  * Copyright (c) 2006-2009, Jouni Malinen <j (at) 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