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 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