Home | History | Annotate | Download | only in tls
      1 /*
      2  * TLSv1 credentials
      3  * Copyright (c) 2006-2007, 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.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 
     72 
     73 static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
     74 {
     75 	size_t i, plen;
     76 
     77 	plen = os_strlen(tag);
     78 	if (len < plen)
     79 		return NULL;
     80 
     81 	for (i = 0; i < len - plen; i++) {
     82 		if (os_memcmp(buf + i, tag, plen) == 0)
     83 			return buf + i;
     84 	}
     85 
     86 	return NULL;
     87 }
     88 
     89 
     90 static int tlsv1_add_cert(struct x509_certificate **chain,
     91 			  const u8 *buf, size_t len)
     92 {
     93 	const u8 *pos, *end;
     94 	unsigned char *der;
     95 	size_t der_len;
     96 
     97 	pos = search_tag(pem_cert_begin, buf, len);
     98 	if (!pos) {
     99 		wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
    100 			   "assume DER format");
    101 		return tlsv1_add_cert_der(chain, buf, len);
    102 	}
    103 
    104 	wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
    105 		   "DER format");
    106 
    107 	while (pos) {
    108 		pos += os_strlen(pem_cert_begin);
    109 		end = search_tag(pem_cert_end, pos, buf + len - pos);
    110 		if (end == NULL) {
    111 			wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
    112 				   "certificate end tag (%s)", pem_cert_end);
    113 			return -1;
    114 		}
    115 
    116 		der = base64_decode(pos, end - pos, &der_len);
    117 		if (der == NULL) {
    118 			wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
    119 				   "certificate");
    120 			return -1;
    121 		}
    122 
    123 		if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
    124 			wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
    125 				   "certificate after DER conversion");
    126 			os_free(der);
    127 			return -1;
    128 		}
    129 
    130 		os_free(der);
    131 
    132 		end += os_strlen(pem_cert_end);
    133 		pos = search_tag(pem_cert_begin, end, buf + len - end);
    134 	}
    135 
    136 	return 0;
    137 }
    138 
    139 
    140 static int tlsv1_set_cert_chain(struct x509_certificate **chain,
    141 				const char *cert, const u8 *cert_blob,
    142 				size_t cert_blob_len)
    143 {
    144 	if (cert_blob)
    145 		return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
    146 
    147 	if (cert) {
    148 		u8 *buf;
    149 		size_t len;
    150 		int ret;
    151 
    152 		buf = (u8 *) os_readfile(cert, &len);
    153 		if (buf == NULL) {
    154 			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
    155 				   cert);
    156 			return -1;
    157 		}
    158 
    159 		ret = tlsv1_add_cert(chain, buf, len);
    160 		os_free(buf);
    161 		return ret;
    162 	}
    163 
    164 	return 0;
    165 }
    166 
    167 
    168 /**
    169  * tlsv1_set_ca_cert - Set trusted CA certificate(s)
    170  * @cred: TLSv1 credentials from tlsv1_cred_alloc()
    171  * @cert: File or reference name for X.509 certificate in PEM or DER format
    172  * @cert_blob: cert as inlined data or %NULL if not used
    173  * @cert_blob_len: ca_cert_blob length
    174  * @path: Path to CA certificates (not yet supported)
    175  * Returns: 0 on success, -1 on failure
    176  */
    177 int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
    178 		      const u8 *cert_blob, size_t cert_blob_len,
    179 		      const char *path)
    180 {
    181 	if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
    182 				 cert_blob, cert_blob_len) < 0)
    183 		return -1;
    184 
    185 	if (path) {
    186 		/* TODO: add support for reading number of certificate files */
    187 		wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
    188 			   "not yet supported");
    189 		return -1;
    190 	}
    191 
    192 	return 0;
    193 }
    194 
    195 
    196 /**
    197  * tlsv1_set_cert - Set certificate
    198  * @cred: TLSv1 credentials from tlsv1_cred_alloc()
    199  * @cert: File or reference name for X.509 certificate in PEM or DER format
    200  * @cert_blob: cert as inlined data or %NULL if not used
    201  * @cert_blob_len: cert_blob length
    202  * Returns: 0 on success, -1 on failure
    203  */
    204 int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
    205 		   const u8 *cert_blob, size_t cert_blob_len)
    206 {
    207 	return tlsv1_set_cert_chain(&cred->cert, cert,
    208 				    cert_blob, cert_blob_len);
    209 }
    210 
    211 
    212 static int tlsv1_set_key(struct tlsv1_credentials *cred,
    213 			 const u8 *key, size_t len)
    214 {
    215 	cred->key = crypto_private_key_import(key, len);
    216 	if (cred->key == NULL) {
    217 		wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
    218 		return -1;
    219 	}
    220 	return 0;
    221 }
    222 
    223 
    224 /**
    225  * tlsv1_set_private_key - Set private key
    226  * @cred: TLSv1 credentials from tlsv1_cred_alloc()
    227  * @private_key: File or reference name for the key in PEM or DER format
    228  * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
    229  * passphrase is used.
    230  * @private_key_blob: private_key as inlined data or %NULL if not used
    231  * @private_key_blob_len: private_key_blob length
    232  * Returns: 0 on success, -1 on failure
    233  */
    234 int tlsv1_set_private_key(struct tlsv1_credentials *cred,
    235 			  const char *private_key,
    236 			  const char *private_key_passwd,
    237 			  const u8 *private_key_blob,
    238 			  size_t private_key_blob_len)
    239 {
    240 	crypto_private_key_free(cred->key);
    241 	cred->key = NULL;
    242 
    243 	if (private_key_blob)
    244 		return tlsv1_set_key(cred, private_key_blob,
    245 				     private_key_blob_len);
    246 
    247 	if (private_key) {
    248 		u8 *buf;
    249 		size_t len;
    250 		int ret;
    251 
    252 		buf = (u8 *) os_readfile(private_key, &len);
    253 		if (buf == NULL) {
    254 			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
    255 				   private_key);
    256 			return -1;
    257 		}
    258 
    259 		ret = tlsv1_set_key(cred, buf, len);
    260 		os_free(buf);
    261 		return ret;
    262 	}
    263 
    264 	return 0;
    265 }
    266 
    267 
    268 static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
    269 				  const u8 *dh, size_t len)
    270 {
    271 	struct asn1_hdr hdr;
    272 	const u8 *pos, *end;
    273 
    274 	pos = dh;
    275 	end = dh + len;
    276 
    277 	/*
    278 	 * DHParameter ::= SEQUENCE {
    279 	 *   prime INTEGER, -- p
    280 	 *   base INTEGER, -- g
    281 	 *   privateValueLength INTEGER OPTIONAL }
    282 	 */
    283 
    284 	/* DHParamer ::= SEQUENCE */
    285 	if (asn1_get_next(pos, len, &hdr) < 0 ||
    286 	    hdr.class != ASN1_CLASS_UNIVERSAL ||
    287 	    hdr.tag != ASN1_TAG_SEQUENCE) {
    288 		wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
    289 			   "valid SEQUENCE - found class %d tag 0x%x",
    290 			   hdr.class, hdr.tag);
    291 		return -1;
    292 	}
    293 	pos = hdr.payload;
    294 
    295 	/* prime INTEGER */
    296 	if (asn1_get_next(pos, end - pos, &hdr) < 0)
    297 		return -1;
    298 
    299 	if (hdr.class != ASN1_CLASS_UNIVERSAL ||
    300 	    hdr.tag != ASN1_TAG_INTEGER) {
    301 		wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
    302 			   "class=%d tag=0x%x", hdr.class, hdr.tag);
    303 		return -1;
    304 	}
    305 
    306 	wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
    307 	if (hdr.length == 0)
    308 		return -1;
    309 	os_free(cred->dh_p);
    310 	cred->dh_p = os_malloc(hdr.length);
    311 	if (cred->dh_p == NULL)
    312 		return -1;
    313 	os_memcpy(cred->dh_p, hdr.payload, hdr.length);
    314 	cred->dh_p_len = hdr.length;
    315 	pos = hdr.payload + hdr.length;
    316 
    317 	/* base INTEGER */
    318 	if (asn1_get_next(pos, end - pos, &hdr) < 0)
    319 		return -1;
    320 
    321 	if (hdr.class != ASN1_CLASS_UNIVERSAL ||
    322 	    hdr.tag != ASN1_TAG_INTEGER) {
    323 		wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
    324 			   "class=%d tag=0x%x", hdr.class, hdr.tag);
    325 		return -1;
    326 	}
    327 
    328 	wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
    329 	if (hdr.length == 0)
    330 		return -1;
    331 	os_free(cred->dh_g);
    332 	cred->dh_g = os_malloc(hdr.length);
    333 	if (cred->dh_g == NULL)
    334 		return -1;
    335 	os_memcpy(cred->dh_g, hdr.payload, hdr.length);
    336 	cred->dh_g_len = hdr.length;
    337 
    338 	return 0;
    339 }
    340 
    341 
    342 static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
    343 static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
    344 
    345 
    346 static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
    347 				   const u8 *buf, size_t len)
    348 {
    349 	const u8 *pos, *end;
    350 	unsigned char *der;
    351 	size_t der_len;
    352 
    353 	pos = search_tag(pem_dhparams_begin, buf, len);
    354 	if (!pos) {
    355 		wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
    356 			   "assume DER format");
    357 		return tlsv1_set_dhparams_der(cred, buf, len);
    358 	}
    359 
    360 	wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
    361 		   "format");
    362 
    363 	pos += os_strlen(pem_dhparams_begin);
    364 	end = search_tag(pem_dhparams_end, pos, buf + len - pos);
    365 	if (end == NULL) {
    366 		wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
    367 			   "tag (%s)", pem_dhparams_end);
    368 		return -1;
    369 	}
    370 
    371 	der = base64_decode(pos, end - pos, &der_len);
    372 	if (der == NULL) {
    373 		wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
    374 		return -1;
    375 	}
    376 
    377 	if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
    378 		wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
    379 			   "DER conversion");
    380 		os_free(der);
    381 		return -1;
    382 	}
    383 
    384 	os_free(der);
    385 
    386 	return 0;
    387 }
    388 
    389 
    390 /**
    391  * tlsv1_set_dhparams - Set Diffie-Hellman parameters
    392  * @cred: TLSv1 credentials from tlsv1_cred_alloc()
    393  * @dh_file: File or reference name for the DH params in PEM or DER format
    394  * @dh_blob: DH params as inlined data or %NULL if not used
    395  * @dh_blob_len: dh_blob length
    396  * Returns: 0 on success, -1 on failure
    397  */
    398 int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
    399 		       const u8 *dh_blob, size_t dh_blob_len)
    400 {
    401 	if (dh_blob)
    402 		return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
    403 
    404 	if (dh_file) {
    405 		u8 *buf;
    406 		size_t len;
    407 		int ret;
    408 
    409 		buf = (u8 *) os_readfile(dh_file, &len);
    410 		if (buf == NULL) {
    411 			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
    412 				   dh_file);
    413 			return -1;
    414 		}
    415 
    416 		ret = tlsv1_set_dhparams_blob(cred, buf, len);
    417 		os_free(buf);
    418 		return ret;
    419 	}
    420 
    421 	return 0;
    422 }
    423