1 /* 2 * TLSv1 credentials 3 * Copyright (c) 2006-2007, Jouni Malinen <j@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