comparison src/event/ngx_event_openssl.c @ 7460:77436d9951a1

SSL: reworked ngx_ssl_certificate(). This makes it possible to reuse certificate loading at runtime, as introduced in the following patches. Additionally, this improves error logging, so nginx will now log human-friendly messages "cannot load certificate" instead of only referring to sometimes cryptic names of OpenSSL functions.
author Maxim Dounin <mdounin@mdounin.ru>
date Mon, 25 Feb 2019 16:41:28 +0300
parents 982008fbc4ba
children a68799465b19
comparison
equal deleted inserted replaced
7459:982008fbc4ba 7460:77436d9951a1
16 typedef struct { 16 typedef struct {
17 ngx_uint_t engine; /* unsigned engine:1; */ 17 ngx_uint_t engine; /* unsigned engine:1; */
18 } ngx_openssl_conf_t; 18 } ngx_openssl_conf_t;
19 19
20 20
21 static X509 *ngx_ssl_load_certificate(ngx_pool_t *pool, char **err,
22 ngx_str_t *cert, STACK_OF(X509) **chain);
23 static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
24 ngx_str_t *key, ngx_array_t *passwords);
21 static int ngx_ssl_password_callback(char *buf, int size, int rwflag, 25 static int ngx_ssl_password_callback(char *buf, int size, int rwflag,
22 void *userdata); 26 void *userdata);
23 static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); 27 static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
24 static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, 28 static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
25 int ret); 29 int ret);
405 409
406 ngx_int_t 410 ngx_int_t
407 ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, 411 ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
408 ngx_str_t *key, ngx_array_t *passwords) 412 ngx_str_t *key, ngx_array_t *passwords)
409 { 413 {
410 BIO *bio; 414 char *err;
411 X509 *x509; 415 X509 *x509;
412 u_long n; 416 EVP_PKEY *pkey;
413 ngx_str_t *pwd; 417 STACK_OF(X509) *chain;
414 ngx_uint_t tries; 418
415 419 x509 = ngx_ssl_load_certificate(cf->pool, &err, cert, &chain);
416 if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
417 return NGX_ERROR;
418 }
419
420 /*
421 * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
422 * allow to access certificate later from SSL_CTX, so we reimplement
423 * it here
424 */
425
426 bio = BIO_new_file((char *) cert->data, "r");
427 if (bio == NULL) {
428 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
429 "BIO_new_file(\"%s\") failed", cert->data);
430 return NGX_ERROR;
431 }
432
433 x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
434 if (x509 == NULL) { 420 if (x509 == NULL) {
435 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, 421 if (err != NULL) {
436 "PEM_read_bio_X509_AUX(\"%s\") failed", cert->data); 422 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
437 BIO_free(bio); 423 "cannot load certificate \"%s\": %s",
424 cert->data, err);
425 }
426
438 return NGX_ERROR; 427 return NGX_ERROR;
439 } 428 }
440 429
441 if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) { 430 if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
442 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, 431 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
443 "SSL_CTX_use_certificate(\"%s\") failed", cert->data); 432 "SSL_CTX_use_certificate(\"%s\") failed", cert->data);
444 X509_free(x509); 433 X509_free(x509);
445 BIO_free(bio); 434 sk_X509_pop_free(chain, X509_free);
446 return NGX_ERROR; 435 return NGX_ERROR;
447 } 436 }
448 437
449 if (X509_set_ex_data(x509, ngx_ssl_certificate_name_index, cert->data) 438 if (X509_set_ex_data(x509, ngx_ssl_certificate_name_index, cert->data)
450 == 0) 439 == 0)
451 { 440 {
452 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); 441 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
453 X509_free(x509); 442 X509_free(x509);
454 BIO_free(bio); 443 sk_X509_pop_free(chain, X509_free);
455 return NGX_ERROR; 444 return NGX_ERROR;
456 } 445 }
457 446
458 if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index, 447 if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index,
459 SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index)) 448 SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index))
460 == 0) 449 == 0)
461 { 450 {
462 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); 451 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
463 X509_free(x509); 452 X509_free(x509);
464 BIO_free(bio); 453 sk_X509_pop_free(chain, X509_free);
465 return NGX_ERROR; 454 return NGX_ERROR;
466 } 455 }
467 456
468 if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) 457 if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) == 0) {
469 == 0)
470 {
471 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, 458 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
472 "SSL_CTX_set_ex_data() failed"); 459 "SSL_CTX_set_ex_data() failed");
473 X509_free(x509); 460 X509_free(x509);
461 sk_X509_pop_free(chain, X509_free);
462 return NGX_ERROR;
463 }
464
465 /*
466 * Note that x509 is not freed here, but will be instead freed in
467 * ngx_ssl_cleanup_ctx(). This is because we need to preserve all
468 * certificates to be able to iterate all of them through exdata
469 * (ngx_ssl_certificate_index, ngx_ssl_next_certificate_index),
470 * while OpenSSL can free a certificate if it is replaced with another
471 * certificate of the same type.
472 */
473
474 #ifdef SSL_CTX_set0_chain
475
476 if (SSL_CTX_set0_chain(ssl->ctx, chain) == 0) {
477 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
478 "SSL_CTX_set0_chain(\"%s\") failed", cert->data);
479 sk_X509_pop_free(chain, X509_free);
480 return NGX_ERROR;
481 }
482
483 #else
484 {
485 int n;
486
487 /* SSL_CTX_set0_chain() is only available in OpenSSL 1.0.2+ */
488
489 n = sk_X509_num(chain);
490
491 while (n--) {
492 x509 = sk_X509_shift(chain);
493
494 if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
495 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
496 "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
497 cert->data);
498 sk_X509_pop_free(chain, X509_free);
499 return NGX_ERROR;
500 }
501 }
502
503 sk_X509_free(chain);
504 }
505 #endif
506
507 pkey = ngx_ssl_load_certificate_key(cf->pool, &err, key, passwords);
508 if (pkey == NULL) {
509 if (err != NULL) {
510 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
511 "cannot load certificate key \"%s\": %s",
512 key->data, err);
513 }
514
515 return NGX_ERROR;
516 }
517
518 if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
519 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
520 "SSL_CTX_use_PrivateKey(\"%s\") failed", key->data);
521 EVP_PKEY_free(pkey);
522 return NGX_ERROR;
523 }
524
525 EVP_PKEY_free(pkey);
526
527 return NGX_OK;
528 }
529
530
531 static X509 *
532 ngx_ssl_load_certificate(ngx_pool_t *pool, char **err, ngx_str_t *cert,
533 STACK_OF(X509) **chain)
534 {
535 BIO *bio;
536 X509 *x509, *temp;
537 u_long n;
538
539 if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, cert)
540 != NGX_OK)
541 {
542 *err = NULL;
543 return NULL;
544 }
545
546 /*
547 * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
548 * allow to access certificate later from SSL_CTX, so we reimplement
549 * it here
550 */
551
552 bio = BIO_new_file((char *) cert->data, "r");
553 if (bio == NULL) {
554 *err = "BIO_new_file() failed";
555 return NULL;
556 }
557
558 /* certificate itself */
559
560 x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
561 if (x509 == NULL) {
562 *err = "PEM_read_bio_X509_AUX() failed";
474 BIO_free(bio); 563 BIO_free(bio);
475 return NGX_ERROR; 564 return NULL;
476 } 565 }
477 566
478 /* read rest of the chain */ 567 /* rest of the chain */
568
569 *chain = sk_X509_new_null();
570 if (*chain == NULL) {
571 *err = "sk_X509_new_null() failed";
572 BIO_free(bio);
573 X509_free(x509);
574 return NULL;
575 }
479 576
480 for ( ;; ) { 577 for ( ;; ) {
481 578
482 x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); 579 temp = PEM_read_bio_X509(bio, NULL, NULL, NULL);
483 if (x509 == NULL) { 580 if (temp == NULL) {
484 n = ERR_peek_last_error(); 581 n = ERR_peek_last_error();
485 582
486 if (ERR_GET_LIB(n) == ERR_LIB_PEM 583 if (ERR_GET_LIB(n) == ERR_LIB_PEM
487 && ERR_GET_REASON(n) == PEM_R_NO_START_LINE) 584 && ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
488 { 585 {
491 break; 588 break;
492 } 589 }
493 590
494 /* some real error */ 591 /* some real error */
495 592
496 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, 593 *err = "PEM_read_bio_X509() failed";
497 "PEM_read_bio_X509(\"%s\") failed", cert->data);
498 BIO_free(bio); 594 BIO_free(bio);
499 return NGX_ERROR;
500 }
501
502 #ifdef SSL_CTRL_CHAIN_CERT
503
504 /*
505 * SSL_CTX_add0_chain_cert() is needed to add chain to
506 * a particular certificate when multiple certificates are used;
507 * only available in OpenSSL 1.0.2+
508 */
509
510 if (SSL_CTX_add0_chain_cert(ssl->ctx, x509) == 0) {
511 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
512 "SSL_CTX_add0_chain_cert(\"%s\") failed",
513 cert->data);
514 X509_free(x509); 595 X509_free(x509);
596 sk_X509_pop_free(*chain, X509_free);
597 return NULL;
598 }
599
600 if (sk_X509_push(*chain, temp) == 0) {
601 *err = "sk_X509_push() failed";
515 BIO_free(bio); 602 BIO_free(bio);
516 return NGX_ERROR;
517 }
518
519 #else
520 if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
521 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
522 "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
523 cert->data);
524 X509_free(x509); 603 X509_free(x509);
525 BIO_free(bio); 604 sk_X509_pop_free(*chain, X509_free);
526 return NGX_ERROR; 605 return NULL;
527 } 606 }
528 #endif
529 } 607 }
530 608
531 BIO_free(bio); 609 BIO_free(bio);
610
611 return x509;
612 }
613
614
615 static EVP_PKEY *
616 ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
617 ngx_str_t *key, ngx_array_t *passwords)
618 {
619 BIO *bio;
620 EVP_PKEY *pkey;
621 ngx_str_t *pwd;
622 ngx_uint_t tries;
623 pem_password_cb *cb;
532 624
533 if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) { 625 if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) {
534 626
535 #ifndef OPENSSL_NO_ENGINE 627 #ifndef OPENSSL_NO_ENGINE
536 628
540 632
541 p = key->data + sizeof("engine:") - 1; 633 p = key->data + sizeof("engine:") - 1;
542 last = (u_char *) ngx_strchr(p, ':'); 634 last = (u_char *) ngx_strchr(p, ':');
543 635
544 if (last == NULL) { 636 if (last == NULL) {
545 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 637 *err = "invalid syntax";
546 "invalid syntax in \"%V\"", key); 638 return NULL;
547 return NGX_ERROR;
548 } 639 }
549 640
550 *last = '\0'; 641 *last = '\0';
551 642
552 engine = ENGINE_by_id((char *) p); 643 engine = ENGINE_by_id((char *) p);
553 644
554 if (engine == NULL) { 645 if (engine == NULL) {
555 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, 646 *err = "ENGINE_by_id() failed";
556 "ENGINE_by_id(\"%s\") failed", p); 647 return NULL;
557 return NGX_ERROR;
558 } 648 }
559 649
560 *last++ = ':'; 650 *last++ = ':';
561 651
562 pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0); 652 pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);
563 653
564 if (pkey == NULL) { 654 if (pkey == NULL) {
565 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, 655 *err = "ENGINE_load_private_key() failed";
566 "ENGINE_load_private_key(\"%s\") failed", last);
567 ENGINE_free(engine); 656 ENGINE_free(engine);
568 return NGX_ERROR; 657 return NULL;
569 } 658 }
570 659
571 ENGINE_free(engine); 660 ENGINE_free(engine);
572 661
573 if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) { 662 return pkey;
574 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
575 "SSL_CTX_use_PrivateKey(\"%s\") failed", last);
576 EVP_PKEY_free(pkey);
577 return NGX_ERROR;
578 }
579
580 EVP_PKEY_free(pkey);
581
582 return NGX_OK;
583 663
584 #else 664 #else
585 665
586 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 666 *err = "loading \"engine:...\" certificate keys is not supported";
587 "loading \"engine:...\" certificate keys " 667 return NULL;
588 "is not supported"); 668
589 return NGX_ERROR; 669 #endif
590 670 }
591 #endif 671
592 } 672 if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, key)
593 673 != NGX_OK)
594 if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) { 674 {
595 return NGX_ERROR; 675 *err = NULL;
676 return NULL;
677 }
678
679 bio = BIO_new_file((char *) key->data, "r");
680 if (bio == NULL) {
681 *err = "BIO_new_file() failed";
682 return NULL;
596 } 683 }
597 684
598 if (passwords) { 685 if (passwords) {
599 tries = passwords->nelts; 686 tries = passwords->nelts;
600 pwd = passwords->elts; 687 pwd = passwords->elts;
601 688 cb = ngx_ssl_password_callback;
602 SSL_CTX_set_default_passwd_cb(ssl->ctx, ngx_ssl_password_callback);
603 SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, pwd);
604 689
605 } else { 690 } else {
606 tries = 1; 691 tries = 1;
607 #if (NGX_SUPPRESS_WARN)
608 pwd = NULL; 692 pwd = NULL;
609 #endif 693 cb = NULL;
610 } 694 }
611 695
612 for ( ;; ) { 696 for ( ;; ) {
613 697
614 if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data, 698 pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
615 SSL_FILETYPE_PEM) 699 if (pkey != NULL) {
616 != 0)
617 {
618 break; 700 break;
619 } 701 }
620 702
621 if (--tries) { 703 if (--tries) {
622 ERR_clear_error(); 704 ERR_clear_error();
623 SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, ++pwd); 705 (void) BIO_reset(bio);
706 pwd++;
624 continue; 707 continue;
625 } 708 }
626 709
627 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, 710 *err = "PEM_read_bio_PrivateKey() failed";
628 "SSL_CTX_use_PrivateKey_file(\"%s\") failed", key->data); 711 BIO_free(bio);
629 return NGX_ERROR; 712 return NULL;
630 } 713 }
631 714
632 SSL_CTX_set_default_passwd_cb(ssl->ctx, NULL); 715 BIO_free(bio);
633 716
634 return NGX_OK; 717 return pkey;
635 } 718 }
636 719
637 720
638 static int 721 static int
639 ngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata) 722 ngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata)