William Lallemand | 03c331c | 2020-05-13 10:10:01 +0200 | [diff] [blame^] | 1 | /* |
| 2 | * |
| 3 | * Copyright (C) 2020 HAProxy Technologies, William Lallemand <wlallemand@haproxy.com> |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or |
| 6 | * modify it under the terms of the GNU General Public License |
| 7 | * as published by the Free Software Foundation; either version |
| 8 | * 2 of the License, or (at your option) any later version. |
| 9 | * |
| 10 | */ |
| 11 | |
| 12 | #define _GNU_SOURCE |
| 13 | #include <ctype.h> |
| 14 | #include <errno.h> |
| 15 | #include <fcntl.h> |
| 16 | #include <stdio.h> |
| 17 | #include <stdlib.h> |
| 18 | #include <string.h> |
| 19 | #include <unistd.h> |
| 20 | |
| 21 | #include <sys/stat.h> |
| 22 | #include <sys/types.h> |
| 23 | |
| 24 | #include <common/base64.h> |
| 25 | #include <common/errors.h> |
| 26 | #include <common/standard.h> |
| 27 | |
| 28 | #include <ebsttree.h> |
| 29 | |
| 30 | #include <types/ssl_ckch.h> |
| 31 | #include <types/ssl_sock.h> |
| 32 | |
| 33 | #include <proto/ssl_ckch.h> |
| 34 | #include <proto/ssl_sock.h> |
| 35 | |
| 36 | /******************** cert_key_and_chain functions ************************* |
| 37 | * These are the functions that fills a cert_key_and_chain structure. For the |
| 38 | * functions filling a SSL_CTX from a cert_key_and_chain, see ssl_sock.c |
| 39 | */ |
| 40 | |
| 41 | /* |
| 42 | * Try to parse Signed Certificate Timestamp List structure. This function |
| 43 | * makes only basic test if the data seems like SCTL. No signature validation |
| 44 | * is performed. |
| 45 | */ |
| 46 | static int ssl_sock_parse_sctl(struct buffer *sctl) |
| 47 | { |
| 48 | int ret = 1; |
| 49 | int len, pos, sct_len; |
| 50 | unsigned char *data; |
| 51 | |
| 52 | if (sctl->data < 2) |
| 53 | goto out; |
| 54 | |
| 55 | data = (unsigned char *) sctl->area; |
| 56 | len = (data[0] << 8) | data[1]; |
| 57 | |
| 58 | if (len + 2 != sctl->data) |
| 59 | goto out; |
| 60 | |
| 61 | data = data + 2; |
| 62 | pos = 0; |
| 63 | while (pos < len) { |
| 64 | if (len - pos < 2) |
| 65 | goto out; |
| 66 | |
| 67 | sct_len = (data[pos] << 8) | data[pos + 1]; |
| 68 | if (pos + sct_len + 2 > len) |
| 69 | goto out; |
| 70 | |
| 71 | pos += sct_len + 2; |
| 72 | } |
| 73 | |
| 74 | ret = 0; |
| 75 | |
| 76 | out: |
| 77 | return ret; |
| 78 | } |
| 79 | |
| 80 | /* Try to load a sctl from a buffer <buf> if not NULL, or read the file <sctl_path> |
| 81 | * It fills the ckch->sctl buffer |
| 82 | * return 0 on success or != 0 on failure */ |
| 83 | int ssl_sock_load_sctl_from_file(const char *sctl_path, char *buf, struct cert_key_and_chain *ckch, char **err) |
| 84 | { |
| 85 | int fd = -1; |
| 86 | int r = 0; |
| 87 | int ret = 1; |
| 88 | struct buffer tmp; |
| 89 | struct buffer *src; |
| 90 | struct buffer *sctl; |
| 91 | |
| 92 | if (buf) { |
| 93 | tmp.area = buf; |
| 94 | tmp.data = strlen(buf); |
| 95 | tmp.size = tmp.data + 1; |
| 96 | src = &tmp; |
| 97 | } else { |
| 98 | fd = open(sctl_path, O_RDONLY); |
| 99 | if (fd == -1) |
| 100 | goto end; |
| 101 | |
| 102 | trash.data = 0; |
| 103 | while (trash.data < trash.size) { |
| 104 | r = read(fd, trash.area + trash.data, trash.size - trash.data); |
| 105 | if (r < 0) { |
| 106 | if (errno == EINTR) |
| 107 | continue; |
| 108 | goto end; |
| 109 | } |
| 110 | else if (r == 0) { |
| 111 | break; |
| 112 | } |
| 113 | trash.data += r; |
| 114 | } |
| 115 | src = &trash; |
| 116 | } |
| 117 | |
| 118 | ret = ssl_sock_parse_sctl(src); |
| 119 | if (ret) |
| 120 | goto end; |
| 121 | |
| 122 | sctl = calloc(1, sizeof(*sctl)); |
| 123 | if (!chunk_dup(sctl, src)) { |
| 124 | free(sctl); |
| 125 | sctl = NULL; |
| 126 | goto end; |
| 127 | } |
| 128 | /* no error, fill ckch with new context, old context must be free */ |
| 129 | if (ckch->sctl) { |
| 130 | free(ckch->sctl->area); |
| 131 | ckch->sctl->area = NULL; |
| 132 | free(ckch->sctl); |
| 133 | } |
| 134 | ckch->sctl = sctl; |
| 135 | ret = 0; |
| 136 | end: |
| 137 | if (fd != -1) |
| 138 | close(fd); |
| 139 | |
| 140 | return ret; |
| 141 | } |
| 142 | |
| 143 | #if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL) |
| 144 | /* |
| 145 | * This function load the OCSP Resonse in DER format contained in file at |
| 146 | * path 'ocsp_path' or base64 in a buffer <buf> |
| 147 | * |
| 148 | * Returns 0 on success, 1 in error case. |
| 149 | */ |
| 150 | int ssl_sock_load_ocsp_response_from_file(const char *ocsp_path, char *buf, struct cert_key_and_chain *ckch, char **err) |
| 151 | { |
| 152 | int fd = -1; |
| 153 | int r = 0; |
| 154 | int ret = 1; |
| 155 | struct buffer *ocsp_response; |
| 156 | struct buffer *src = NULL; |
| 157 | |
| 158 | if (buf) { |
| 159 | int i, j; |
| 160 | /* if it's from a buffer it will be base64 */ |
| 161 | |
| 162 | /* remove \r and \n from the payload */ |
| 163 | for (i = 0, j = 0; buf[i]; i++) { |
| 164 | if (buf[i] == '\r' || buf[i] == '\n') |
| 165 | continue; |
| 166 | buf[j++] = buf[i]; |
| 167 | } |
| 168 | buf[j] = 0; |
| 169 | |
| 170 | ret = base64dec(buf, j, trash.area, trash.size); |
| 171 | if (ret < 0) { |
| 172 | memprintf(err, "Error reading OCSP response in base64 format"); |
| 173 | goto end; |
| 174 | } |
| 175 | trash.data = ret; |
| 176 | src = &trash; |
| 177 | } else { |
| 178 | fd = open(ocsp_path, O_RDONLY); |
| 179 | if (fd == -1) { |
| 180 | memprintf(err, "Error opening OCSP response file"); |
| 181 | goto end; |
| 182 | } |
| 183 | |
| 184 | trash.data = 0; |
| 185 | while (trash.data < trash.size) { |
| 186 | r = read(fd, trash.area + trash.data, trash.size - trash.data); |
| 187 | if (r < 0) { |
| 188 | if (errno == EINTR) |
| 189 | continue; |
| 190 | |
| 191 | memprintf(err, "Error reading OCSP response from file"); |
| 192 | goto end; |
| 193 | } |
| 194 | else if (r == 0) { |
| 195 | break; |
| 196 | } |
| 197 | trash.data += r; |
| 198 | } |
| 199 | close(fd); |
| 200 | fd = -1; |
| 201 | src = &trash; |
| 202 | } |
| 203 | |
| 204 | ocsp_response = calloc(1, sizeof(*ocsp_response)); |
| 205 | if (!chunk_dup(ocsp_response, src)) { |
| 206 | free(ocsp_response); |
| 207 | ocsp_response = NULL; |
| 208 | goto end; |
| 209 | } |
| 210 | /* no error, fill ckch with new context, old context must be free */ |
| 211 | if (ckch->ocsp_response) { |
| 212 | free(ckch->ocsp_response->area); |
| 213 | ckch->ocsp_response->area = NULL; |
| 214 | free(ckch->ocsp_response); |
| 215 | } |
| 216 | ckch->ocsp_response = ocsp_response; |
| 217 | ret = 0; |
| 218 | end: |
| 219 | if (fd != -1) |
| 220 | close(fd); |
| 221 | |
| 222 | return ret; |
| 223 | } |
| 224 | #endif |
| 225 | |
| 226 | /* |
| 227 | * Try to load in a ckch every files related to a ckch. |
| 228 | * (PEM, sctl, ocsp, issuer etc.) |
| 229 | * |
| 230 | * This function is only used to load files during the configuration parsing, |
| 231 | * it is not used with the CLI. |
| 232 | * |
| 233 | * This allows us to carry the contents of the file without having to read the |
| 234 | * file multiple times. The caller must call |
| 235 | * ssl_sock_free_cert_key_and_chain_contents. |
| 236 | * |
| 237 | * returns: |
| 238 | * 0 on Success |
| 239 | * 1 on SSL Failure |
| 240 | */ |
| 241 | int ssl_sock_load_files_into_ckch(const char *path, struct cert_key_and_chain *ckch, char **err) |
| 242 | { |
| 243 | int ret = 1; |
| 244 | |
| 245 | /* try to load the PEM */ |
| 246 | if (ssl_sock_load_pem_into_ckch(path, NULL, ckch , err) != 0) { |
| 247 | goto end; |
| 248 | } |
| 249 | |
| 250 | /* try to load an external private key if it wasn't in the PEM */ |
| 251 | if ((ckch->key == NULL) && (global_ssl.extra_files & SSL_GF_KEY)) { |
| 252 | char fp[MAXPATHLEN+1]; |
| 253 | struct stat st; |
| 254 | |
| 255 | snprintf(fp, MAXPATHLEN+1, "%s.key", path); |
| 256 | if (stat(fp, &st) == 0) { |
| 257 | if (ssl_sock_load_key_into_ckch(fp, NULL, ckch, err)) { |
| 258 | memprintf(err, "%s '%s' is present but cannot be read or parsed'.\n", |
| 259 | err && *err ? *err : "", fp); |
| 260 | goto end; |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | if (ckch->key == NULL) { |
| 266 | memprintf(err, "%sNo Private Key found in '%s' or '%s.key'.\n", err && *err ? *err : "", path, path); |
| 267 | goto end; |
| 268 | } |
| 269 | |
| 270 | if (!X509_check_private_key(ckch->cert, ckch->key)) { |
| 271 | memprintf(err, "%sinconsistencies between private key and certificate loaded '%s'.\n", |
| 272 | err && *err ? *err : "", path); |
| 273 | goto end; |
| 274 | } |
| 275 | |
| 276 | #if (HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL) |
| 277 | /* try to load the sctl file */ |
| 278 | if (global_ssl.extra_files & SSL_GF_SCTL) { |
| 279 | char fp[MAXPATHLEN+1]; |
| 280 | struct stat st; |
| 281 | |
| 282 | snprintf(fp, MAXPATHLEN+1, "%s.sctl", path); |
| 283 | if (stat(fp, &st) == 0) { |
| 284 | if (ssl_sock_load_sctl_from_file(fp, NULL, ckch, err)) { |
| 285 | memprintf(err, "%s '%s.sctl' is present but cannot be read or parsed'.\n", |
| 286 | err && *err ? *err : "", fp); |
| 287 | ret = 1; |
| 288 | goto end; |
| 289 | } |
| 290 | } |
| 291 | } |
| 292 | #endif |
| 293 | |
| 294 | /* try to load an ocsp response file */ |
| 295 | if (global_ssl.extra_files & SSL_GF_OCSP) { |
| 296 | char fp[MAXPATHLEN+1]; |
| 297 | struct stat st; |
| 298 | |
| 299 | snprintf(fp, MAXPATHLEN+1, "%s.ocsp", path); |
| 300 | if (stat(fp, &st) == 0) { |
| 301 | if (ssl_sock_load_ocsp_response_from_file(fp, NULL, ckch, err)) { |
| 302 | ret = 1; |
| 303 | goto end; |
| 304 | } |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | #ifndef OPENSSL_IS_BORINGSSL /* Useless for BoringSSL */ |
| 309 | if (ckch->ocsp_response && (global_ssl.extra_files & SSL_GF_OCSP_ISSUER)) { |
| 310 | /* if no issuer was found, try to load an issuer from the .issuer */ |
| 311 | if (!ckch->ocsp_issuer) { |
| 312 | struct stat st; |
| 313 | char fp[MAXPATHLEN+1]; |
| 314 | |
| 315 | snprintf(fp, MAXPATHLEN+1, "%s.issuer", path); |
| 316 | if (stat(fp, &st) == 0) { |
| 317 | if (ssl_sock_load_issuer_file_into_ckch(fp, NULL, ckch, err)) { |
| 318 | ret = 1; |
| 319 | goto end; |
| 320 | } |
| 321 | |
| 322 | if (X509_check_issued(ckch->ocsp_issuer, ckch->cert) != X509_V_OK) { |
| 323 | memprintf(err, "%s '%s' is not an issuer'.\n", |
| 324 | err && *err ? *err : "", fp); |
| 325 | ret = 1; |
| 326 | goto end; |
| 327 | } |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | #endif |
| 332 | |
| 333 | ret = 0; |
| 334 | |
| 335 | end: |
| 336 | |
| 337 | ERR_clear_error(); |
| 338 | |
| 339 | /* Something went wrong in one of the reads */ |
| 340 | if (ret != 0) |
| 341 | ssl_sock_free_cert_key_and_chain_contents(ckch); |
| 342 | |
| 343 | return ret; |
| 344 | } |
| 345 | |
| 346 | /* |
| 347 | * Try to load a private key file from a <path> or a buffer <buf> |
| 348 | * |
| 349 | * If it failed you should not attempt to use the ckch but free it. |
| 350 | * |
| 351 | * Return 0 on success or != 0 on failure |
| 352 | */ |
| 353 | int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err) |
| 354 | { |
| 355 | BIO *in = NULL; |
| 356 | int ret = 1; |
| 357 | EVP_PKEY *key = NULL; |
| 358 | |
| 359 | if (buf) { |
| 360 | /* reading from a buffer */ |
| 361 | in = BIO_new_mem_buf(buf, -1); |
| 362 | if (in == NULL) { |
| 363 | memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : ""); |
| 364 | goto end; |
| 365 | } |
| 366 | |
| 367 | } else { |
| 368 | /* reading from a file */ |
| 369 | in = BIO_new(BIO_s_file()); |
| 370 | if (in == NULL) |
| 371 | goto end; |
| 372 | |
| 373 | if (BIO_read_filename(in, path) <= 0) |
| 374 | goto end; |
| 375 | } |
| 376 | |
| 377 | /* Read Private Key */ |
| 378 | key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL); |
| 379 | if (key == NULL) { |
| 380 | memprintf(err, "%sunable to load private key from file '%s'.\n", |
| 381 | err && *err ? *err : "", path); |
| 382 | goto end; |
| 383 | } |
| 384 | |
| 385 | ret = 0; |
| 386 | |
| 387 | SWAP(ckch->key, key); |
| 388 | |
| 389 | end: |
| 390 | |
| 391 | ERR_clear_error(); |
| 392 | if (in) |
| 393 | BIO_free(in); |
| 394 | if (key) |
| 395 | EVP_PKEY_free(key); |
| 396 | |
| 397 | return ret; |
| 398 | } |
| 399 | |
| 400 | /* |
| 401 | * Try to load a PEM file from a <path> or a buffer <buf> |
| 402 | * The PEM must contain at least a Certificate, |
| 403 | * It could contain a DH, a certificate chain and a PrivateKey. |
| 404 | * |
| 405 | * If it failed you should not attempt to use the ckch but free it. |
| 406 | * |
| 407 | * Return 0 on success or != 0 on failure |
| 408 | */ |
| 409 | int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err) |
| 410 | { |
| 411 | BIO *in = NULL; |
| 412 | int ret = 1; |
| 413 | X509 *ca; |
| 414 | X509 *cert = NULL; |
| 415 | EVP_PKEY *key = NULL; |
| 416 | DH *dh = NULL; |
| 417 | STACK_OF(X509) *chain = NULL; |
| 418 | |
| 419 | if (buf) { |
| 420 | /* reading from a buffer */ |
| 421 | in = BIO_new_mem_buf(buf, -1); |
| 422 | if (in == NULL) { |
| 423 | memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : ""); |
| 424 | goto end; |
| 425 | } |
| 426 | |
| 427 | } else { |
| 428 | /* reading from a file */ |
| 429 | in = BIO_new(BIO_s_file()); |
| 430 | if (in == NULL) { |
| 431 | memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : ""); |
| 432 | goto end; |
| 433 | } |
| 434 | |
| 435 | if (BIO_read_filename(in, path) <= 0) { |
| 436 | memprintf(err, "%scannot open the file '%s'.\n", |
| 437 | err && *err ? *err : "", path); |
| 438 | goto end; |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | /* Read Private Key */ |
| 443 | key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL); |
| 444 | /* no need to check for errors here, because the private key could be loaded later */ |
| 445 | |
| 446 | #ifndef OPENSSL_NO_DH |
| 447 | /* Seek back to beginning of file */ |
| 448 | if (BIO_reset(in) == -1) { |
| 449 | memprintf(err, "%san error occurred while reading the file '%s'.\n", |
| 450 | err && *err ? *err : "", path); |
| 451 | goto end; |
| 452 | } |
| 453 | |
| 454 | dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL); |
| 455 | /* no need to return an error there, dh is not mandatory */ |
| 456 | #endif |
| 457 | |
| 458 | /* Seek back to beginning of file */ |
| 459 | if (BIO_reset(in) == -1) { |
| 460 | memprintf(err, "%san error occurred while reading the file '%s'.\n", |
| 461 | err && *err ? *err : "", path); |
| 462 | goto end; |
| 463 | } |
| 464 | |
| 465 | /* Read Certificate */ |
| 466 | cert = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL); |
| 467 | if (cert == NULL) { |
| 468 | memprintf(err, "%sunable to load certificate from file '%s'.\n", |
| 469 | err && *err ? *err : "", path); |
| 470 | goto end; |
| 471 | } |
| 472 | |
| 473 | /* Look for a Certificate Chain */ |
| 474 | while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) { |
| 475 | if (chain == NULL) |
| 476 | chain = sk_X509_new_null(); |
| 477 | if (!sk_X509_push(chain, ca)) { |
| 478 | X509_free(ca); |
| 479 | goto end; |
| 480 | } |
| 481 | } |
| 482 | |
| 483 | ret = ERR_get_error(); |
| 484 | if (ret && (ERR_GET_LIB(ret) != ERR_LIB_PEM && ERR_GET_REASON(ret) != PEM_R_NO_START_LINE)) { |
| 485 | memprintf(err, "%sunable to load certificate chain from file '%s'.\n", |
| 486 | err && *err ? *err : "", path); |
| 487 | goto end; |
| 488 | } |
| 489 | |
| 490 | /* once it loaded the PEM, it should remove everything else in the ckch */ |
| 491 | if (ckch->ocsp_response) { |
| 492 | free(ckch->ocsp_response->area); |
| 493 | ckch->ocsp_response->area = NULL; |
| 494 | free(ckch->ocsp_response); |
| 495 | ckch->ocsp_response = NULL; |
| 496 | } |
| 497 | |
| 498 | if (ckch->sctl) { |
| 499 | free(ckch->sctl->area); |
| 500 | ckch->sctl->area = NULL; |
| 501 | free(ckch->sctl); |
| 502 | ckch->sctl = NULL; |
| 503 | } |
| 504 | |
| 505 | if (ckch->ocsp_issuer) { |
| 506 | X509_free(ckch->ocsp_issuer); |
| 507 | ckch->ocsp_issuer = NULL; |
| 508 | } |
| 509 | |
| 510 | /* no error, fill ckch with new context, old context will be free at end: */ |
| 511 | SWAP(ckch->key, key); |
| 512 | SWAP(ckch->dh, dh); |
| 513 | SWAP(ckch->cert, cert); |
| 514 | SWAP(ckch->chain, chain); |
| 515 | |
| 516 | ret = 0; |
| 517 | |
| 518 | end: |
| 519 | |
| 520 | ERR_clear_error(); |
| 521 | if (in) |
| 522 | BIO_free(in); |
| 523 | if (key) |
| 524 | EVP_PKEY_free(key); |
| 525 | if (dh) |
| 526 | DH_free(dh); |
| 527 | if (cert) |
| 528 | X509_free(cert); |
| 529 | if (chain) |
| 530 | sk_X509_pop_free(chain, X509_free); |
| 531 | |
| 532 | return ret; |
| 533 | } |
| 534 | |
| 535 | /* Frees the contents of a cert_key_and_chain |
| 536 | */ |
| 537 | void ssl_sock_free_cert_key_and_chain_contents(struct cert_key_and_chain *ckch) |
| 538 | { |
| 539 | if (!ckch) |
| 540 | return; |
| 541 | |
| 542 | /* Free the certificate and set pointer to NULL */ |
| 543 | if (ckch->cert) |
| 544 | X509_free(ckch->cert); |
| 545 | ckch->cert = NULL; |
| 546 | |
| 547 | /* Free the key and set pointer to NULL */ |
| 548 | if (ckch->key) |
| 549 | EVP_PKEY_free(ckch->key); |
| 550 | ckch->key = NULL; |
| 551 | |
| 552 | /* Free each certificate in the chain */ |
| 553 | if (ckch->chain) |
| 554 | sk_X509_pop_free(ckch->chain, X509_free); |
| 555 | ckch->chain = NULL; |
| 556 | |
| 557 | if (ckch->dh) |
| 558 | DH_free(ckch->dh); |
| 559 | ckch->dh = NULL; |
| 560 | |
| 561 | if (ckch->sctl) { |
| 562 | free(ckch->sctl->area); |
| 563 | ckch->sctl->area = NULL; |
| 564 | free(ckch->sctl); |
| 565 | ckch->sctl = NULL; |
| 566 | } |
| 567 | |
| 568 | if (ckch->ocsp_response) { |
| 569 | free(ckch->ocsp_response->area); |
| 570 | ckch->ocsp_response->area = NULL; |
| 571 | free(ckch->ocsp_response); |
| 572 | ckch->ocsp_response = NULL; |
| 573 | } |
| 574 | |
| 575 | if (ckch->ocsp_issuer) |
| 576 | X509_free(ckch->ocsp_issuer); |
| 577 | ckch->ocsp_issuer = NULL; |
| 578 | } |
| 579 | |
| 580 | /* |
| 581 | * |
| 582 | * This function copy a cert_key_and_chain in memory |
| 583 | * |
| 584 | * It's used to try to apply changes on a ckch before committing them, because |
| 585 | * most of the time it's not possible to revert those changes |
| 586 | * |
| 587 | * Return a the dst or NULL |
| 588 | */ |
| 589 | struct cert_key_and_chain *ssl_sock_copy_cert_key_and_chain(struct cert_key_and_chain *src, |
| 590 | struct cert_key_and_chain *dst) |
| 591 | { |
| 592 | if (src->cert) { |
| 593 | dst->cert = src->cert; |
| 594 | X509_up_ref(src->cert); |
| 595 | } |
| 596 | |
| 597 | if (src->key) { |
| 598 | dst->key = src->key; |
| 599 | EVP_PKEY_up_ref(src->key); |
| 600 | } |
| 601 | |
| 602 | if (src->chain) { |
| 603 | dst->chain = X509_chain_up_ref(src->chain); |
| 604 | } |
| 605 | |
| 606 | if (src->dh) { |
| 607 | DH_up_ref(src->dh); |
| 608 | dst->dh = src->dh; |
| 609 | } |
| 610 | |
| 611 | if (src->sctl) { |
| 612 | struct buffer *sctl; |
| 613 | |
| 614 | sctl = calloc(1, sizeof(*sctl)); |
| 615 | if (!chunk_dup(sctl, src->sctl)) { |
| 616 | free(sctl); |
| 617 | sctl = NULL; |
| 618 | goto error; |
| 619 | } |
| 620 | dst->sctl = sctl; |
| 621 | } |
| 622 | |
| 623 | if (src->ocsp_response) { |
| 624 | struct buffer *ocsp_response; |
| 625 | |
| 626 | ocsp_response = calloc(1, sizeof(*ocsp_response)); |
| 627 | if (!chunk_dup(ocsp_response, src->ocsp_response)) { |
| 628 | free(ocsp_response); |
| 629 | ocsp_response = NULL; |
| 630 | goto error; |
| 631 | } |
| 632 | dst->ocsp_response = ocsp_response; |
| 633 | } |
| 634 | |
| 635 | if (src->ocsp_issuer) { |
| 636 | X509_up_ref(src->ocsp_issuer); |
| 637 | dst->ocsp_issuer = src->ocsp_issuer; |
| 638 | } |
| 639 | |
| 640 | return dst; |
| 641 | |
| 642 | error: |
| 643 | |
| 644 | /* free everything */ |
| 645 | ssl_sock_free_cert_key_and_chain_contents(dst); |
| 646 | |
| 647 | return NULL; |
| 648 | } |
| 649 | |
| 650 | /* |
| 651 | * return 0 on success or != 0 on failure |
| 652 | */ |
| 653 | int ssl_sock_load_issuer_file_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch, char **err) |
| 654 | { |
| 655 | int ret = 1; |
| 656 | BIO *in = NULL; |
| 657 | X509 *issuer; |
| 658 | |
| 659 | if (buf) { |
| 660 | /* reading from a buffer */ |
| 661 | in = BIO_new_mem_buf(buf, -1); |
| 662 | if (in == NULL) { |
| 663 | memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : ""); |
| 664 | goto end; |
| 665 | } |
| 666 | |
| 667 | } else { |
| 668 | /* reading from a file */ |
| 669 | in = BIO_new(BIO_s_file()); |
| 670 | if (in == NULL) |
| 671 | goto end; |
| 672 | |
| 673 | if (BIO_read_filename(in, path) <= 0) |
| 674 | goto end; |
| 675 | } |
| 676 | |
| 677 | issuer = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL); |
| 678 | if (!issuer) { |
| 679 | memprintf(err, "%s'%s' cannot be read or parsed'.\n", |
| 680 | err && *err ? *err : "", path); |
| 681 | goto end; |
| 682 | } |
| 683 | /* no error, fill ckch with new context, old context must be free */ |
| 684 | if (ckch->ocsp_issuer) |
| 685 | X509_free(ckch->ocsp_issuer); |
| 686 | ckch->ocsp_issuer = issuer; |
| 687 | ret = 0; |
| 688 | |
| 689 | end: |
| 690 | |
| 691 | ERR_clear_error(); |
| 692 | if (in) |
| 693 | BIO_free(in); |
| 694 | |
| 695 | return ret; |
| 696 | } |
| 697 | |
| 698 | /******************** ckch_store functions *********************************** |
| 699 | * The ckch_store is a structure used to cache and index the SSL files used in |
| 700 | * configuration |
| 701 | */ |
| 702 | |
| 703 | /* |
| 704 | * Free a ckch_store, its ckch, its instances and remove it from the ebtree |
| 705 | */ |
| 706 | void ckch_store_free(struct ckch_store *store) |
| 707 | { |
| 708 | struct ckch_inst *inst, *inst_s; |
| 709 | |
| 710 | if (!store) |
| 711 | return; |
| 712 | |
| 713 | #if HA_OPENSSL_VERSION_NUMBER >= 0x1000200L |
| 714 | if (store->multi) { |
| 715 | int n; |
| 716 | |
| 717 | for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) |
| 718 | ssl_sock_free_cert_key_and_chain_contents(&store->ckch[n]); |
| 719 | } else |
| 720 | #endif |
| 721 | { |
| 722 | ssl_sock_free_cert_key_and_chain_contents(store->ckch); |
| 723 | } |
| 724 | |
| 725 | free(store->ckch); |
| 726 | store->ckch = NULL; |
| 727 | |
| 728 | list_for_each_entry_safe(inst, inst_s, &store->ckch_inst, by_ckchs) { |
| 729 | ckch_inst_free(inst); |
| 730 | } |
| 731 | ebmb_delete(&store->node); |
| 732 | free(store); |
| 733 | } |
| 734 | |
| 735 | /* |
| 736 | * create and initialize a ckch_store |
| 737 | * <path> is the key name |
| 738 | * <nmemb> is the number of store->ckch objects to allocate |
| 739 | * |
| 740 | * Return a ckch_store or NULL upon failure. |
| 741 | */ |
| 742 | struct ckch_store *ckch_store_new(const char *filename, int nmemb) |
| 743 | { |
| 744 | struct ckch_store *store; |
| 745 | int pathlen; |
| 746 | |
| 747 | pathlen = strlen(filename); |
| 748 | store = calloc(1, sizeof(*store) + pathlen + 1); |
| 749 | if (!store) |
| 750 | return NULL; |
| 751 | |
| 752 | if (nmemb > 1) |
| 753 | store->multi = 1; |
| 754 | else |
| 755 | store->multi = 0; |
| 756 | |
| 757 | memcpy(store->path, filename, pathlen + 1); |
| 758 | |
| 759 | LIST_INIT(&store->ckch_inst); |
| 760 | LIST_INIT(&store->crtlist_entry); |
| 761 | |
| 762 | store->ckch = calloc(nmemb, sizeof(*store->ckch)); |
| 763 | if (!store->ckch) |
| 764 | goto error; |
| 765 | |
| 766 | return store; |
| 767 | error: |
| 768 | ckch_store_free(store); |
| 769 | return NULL; |
| 770 | } |
| 771 | |
| 772 | /* allocate and duplicate a ckch_store |
| 773 | * Return a new ckch_store or NULL */ |
| 774 | struct ckch_store *ckchs_dup(const struct ckch_store *src) |
| 775 | { |
| 776 | struct ckch_store *dst; |
| 777 | |
| 778 | dst = ckch_store_new(src->path, src->multi ? SSL_SOCK_NUM_KEYTYPES : 1); |
| 779 | |
| 780 | #if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL |
| 781 | if (src->multi) { |
| 782 | int n; |
| 783 | |
| 784 | for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) { |
| 785 | if (&src->ckch[n]) { |
| 786 | if (!ssl_sock_copy_cert_key_and_chain(&src->ckch[n], &dst->ckch[n])) |
| 787 | goto error; |
| 788 | } |
| 789 | } |
| 790 | } else |
| 791 | #endif |
| 792 | { |
| 793 | if (!ssl_sock_copy_cert_key_and_chain(src->ckch, dst->ckch)) |
| 794 | goto error; |
| 795 | } |
| 796 | |
| 797 | return dst; |
| 798 | |
| 799 | error: |
| 800 | ckch_store_free(dst); |
| 801 | |
| 802 | return NULL; |
| 803 | } |
| 804 | |
| 805 | /* |
| 806 | * lookup a path into the ckchs tree. |
| 807 | */ |
| 808 | struct ckch_store *ckchs_lookup(char *path) |
| 809 | { |
| 810 | struct ebmb_node *eb; |
| 811 | |
| 812 | eb = ebst_lookup(&ckchs_tree, path); |
| 813 | if (!eb) |
| 814 | return NULL; |
| 815 | |
| 816 | return ebmb_entry(eb, struct ckch_store, node); |
| 817 | } |
| 818 | |
| 819 | /* |
| 820 | * This function allocate a ckch_store and populate it with certificates from files. |
| 821 | */ |
| 822 | struct ckch_store *ckchs_load_cert_file(char *path, int multi, char **err) |
| 823 | { |
| 824 | struct ckch_store *ckchs; |
| 825 | |
| 826 | ckchs = ckch_store_new(path, multi ? SSL_SOCK_NUM_KEYTYPES : 1); |
| 827 | if (!ckchs) { |
| 828 | memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : ""); |
| 829 | goto end; |
| 830 | } |
| 831 | if (!multi) { |
| 832 | |
| 833 | if (ssl_sock_load_files_into_ckch(path, ckchs->ckch, err) == 1) |
| 834 | goto end; |
| 835 | |
| 836 | /* insert into the ckchs tree */ |
| 837 | memcpy(ckchs->path, path, strlen(path) + 1); |
| 838 | ebst_insert(&ckchs_tree, &ckchs->node); |
| 839 | } else { |
| 840 | int found = 0; |
| 841 | #if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL |
| 842 | char fp[MAXPATHLEN+1] = {0}; |
| 843 | int n = 0; |
| 844 | |
| 845 | /* Load all possible certs and keys */ |
| 846 | for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) { |
| 847 | struct stat buf; |
| 848 | snprintf(fp, sizeof(fp), "%s.%s", path, SSL_SOCK_KEYTYPE_NAMES[n]); |
| 849 | if (stat(fp, &buf) == 0) { |
| 850 | if (ssl_sock_load_files_into_ckch(fp, &ckchs->ckch[n], err) == 1) |
| 851 | goto end; |
| 852 | found = 1; |
| 853 | ckchs->multi = 1; |
| 854 | } |
| 855 | } |
| 856 | #endif |
| 857 | |
| 858 | if (!found) { |
| 859 | memprintf(err, "%sDidn't find any certificate for bundle '%s'.\n", err && *err ? *err : "", path); |
| 860 | goto end; |
| 861 | } |
| 862 | /* insert into the ckchs tree */ |
| 863 | memcpy(ckchs->path, path, strlen(path) + 1); |
| 864 | ebst_insert(&ckchs_tree, &ckchs->node); |
| 865 | } |
| 866 | return ckchs; |
| 867 | |
| 868 | end: |
| 869 | ckch_store_free(ckchs); |
| 870 | |
| 871 | return NULL; |
| 872 | } |
| 873 | |