Klaus Demo nginx / 20c8700
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. Maxim Dounin 1 year, 9 months ago
1 changed file(s) with 201 addition(s) and 118 deletion(s). Raw diff Collapse all Expand all
1717 } ngx_openssl_conf_t;
1818
1919
20 static X509 *ngx_ssl_load_certificate(ngx_pool_t *pool, char **err,
21 ngx_str_t *cert, STACK_OF(X509) **chain);
22 static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
23 ngx_str_t *key, ngx_array_t *passwords);
2024 static int ngx_ssl_password_callback(char *buf, int size, int rwflag,
2125 void *userdata);
2226 static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
406410 ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
407411 ngx_str_t *key, ngx_array_t *passwords)
408412 {
409 BIO *bio;
410 X509 *x509;
411 u_long n;
412 ngx_str_t *pwd;
413 ngx_uint_t tries;
414
415 if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
416 return NGX_ERROR;
417 }
418
419 /*
420 * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
421 * allow to access certificate later from SSL_CTX, so we reimplement
422 * it here
423 */
424
425 bio = BIO_new_file((char *) cert->data, "r");
426 if (bio == NULL) {
427 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
428 "BIO_new_file(\"%s\") failed", cert->data);
429 return NGX_ERROR;
430 }
431
432 x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
413 char *err;
414 X509 *x509;
415 EVP_PKEY *pkey;
416 STACK_OF(X509) *chain;
417
418 x509 = ngx_ssl_load_certificate(cf->pool, &err, cert, &chain);
433419 if (x509 == NULL) {
434 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
435 "PEM_read_bio_X509_AUX(\"%s\") failed", cert->data);
436 BIO_free(bio);
420 if (err != NULL) {
421 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
422 "cannot load certificate \"%s\": %s",
423 cert->data, err);
424 }
425
437426 return NGX_ERROR;
438427 }
439428
441430 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
442431 "SSL_CTX_use_certificate(\"%s\") failed", cert->data);
443432 X509_free(x509);
444 BIO_free(bio);
433 sk_X509_pop_free(chain, X509_free);
445434 return NGX_ERROR;
446435 }
447436
450439 {
451440 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
452441 X509_free(x509);
453 BIO_free(bio);
442 sk_X509_pop_free(chain, X509_free);
454443 return NGX_ERROR;
455444 }
456445
460449 {
461450 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
462451 X509_free(x509);
463 BIO_free(bio);
464 return NGX_ERROR;
465 }
466
467 if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509)
468 == 0)
469 {
452 sk_X509_pop_free(chain, X509_free);
453 return NGX_ERROR;
454 }
455
456 if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) == 0) {
470457 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
471458 "SSL_CTX_set_ex_data() failed");
472459 X509_free(x509);
460 sk_X509_pop_free(chain, X509_free);
461 return NGX_ERROR;
462 }
463
464 /*
465 * Note that x509 is not freed here, but will be instead freed in
466 * ngx_ssl_cleanup_ctx(). This is because we need to preserve all
467 * certificates to be able to iterate all of them through exdata
468 * (ngx_ssl_certificate_index, ngx_ssl_next_certificate_index),
469 * while OpenSSL can free a certificate if it is replaced with another
470 * certificate of the same type.
471 */
472
473 #ifdef SSL_CTX_set0_chain
474
475 if (SSL_CTX_set0_chain(ssl->ctx, chain) == 0) {
476 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
477 "SSL_CTX_set0_chain(\"%s\") failed", cert->data);
478 sk_X509_pop_free(chain, X509_free);
479 return NGX_ERROR;
480 }
481
482 #else
483 {
484 int n;
485
486 /* SSL_CTX_set0_chain() is only available in OpenSSL 1.0.2+ */
487
488 n = sk_X509_num(chain);
489
490 while (n--) {
491 x509 = sk_X509_shift(chain);
492
493 if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
494 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
495 "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
496 cert->data);
497 sk_X509_pop_free(chain, X509_free);
498 return NGX_ERROR;
499 }
500 }
501
502 sk_X509_free(chain);
503 }
504 #endif
505
506 pkey = ngx_ssl_load_certificate_key(cf->pool, &err, key, passwords);
507 if (pkey == NULL) {
508 if (err != NULL) {
509 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
510 "cannot load certificate key \"%s\": %s",
511 key->data, err);
512 }
513
514 return NGX_ERROR;
515 }
516
517 if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
518 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
519 "SSL_CTX_use_PrivateKey(\"%s\") failed", key->data);
520 EVP_PKEY_free(pkey);
521 return NGX_ERROR;
522 }
523
524 EVP_PKEY_free(pkey);
525
526 return NGX_OK;
527 }
528
529
530 static X509 *
531 ngx_ssl_load_certificate(ngx_pool_t *pool, char **err, ngx_str_t *cert,
532 STACK_OF(X509) **chain)
533 {
534 BIO *bio;
535 X509 *x509, *temp;
536 u_long n;
537
538 if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, cert)
539 != NGX_OK)
540 {
541 *err = NULL;
542 return NULL;
543 }
544
545 /*
546 * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
547 * allow to access certificate later from SSL_CTX, so we reimplement
548 * it here
549 */
550
551 bio = BIO_new_file((char *) cert->data, "r");
552 if (bio == NULL) {
553 *err = "BIO_new_file() failed";
554 return NULL;
555 }
556
557 /* certificate itself */
558
559 x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
560 if (x509 == NULL) {
561 *err = "PEM_read_bio_X509_AUX() failed";
473562 BIO_free(bio);
474 return NGX_ERROR;
475 }
476
477 /* read rest of the chain */
563 return NULL;
564 }
565
566 /* rest of the chain */
567
568 *chain = sk_X509_new_null();
569 if (*chain == NULL) {
570 *err = "sk_X509_new_null() failed";
571 BIO_free(bio);
572 X509_free(x509);
573 return NULL;
574 }
478575
479576 for ( ;; ) {
480577
481 x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
482 if (x509 == NULL) {
578 temp = PEM_read_bio_X509(bio, NULL, NULL, NULL);
579 if (temp == NULL) {
483580 n = ERR_peek_last_error();
484581
485582 if (ERR_GET_LIB(n) == ERR_LIB_PEM
492589
493590 /* some real error */
494591
495 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
496 "PEM_read_bio_X509(\"%s\") failed", cert->data);
592 *err = "PEM_read_bio_X509() failed";
497593 BIO_free(bio);
498 return NGX_ERROR;
499 }
500
501 #ifdef SSL_CTRL_CHAIN_CERT
502
503 /*
504 * SSL_CTX_add0_chain_cert() is needed to add chain to
505 * a particular certificate when multiple certificates are used;
506 * only available in OpenSSL 1.0.2+
507 */
508
509 if (SSL_CTX_add0_chain_cert(ssl->ctx, x509) == 0) {
510 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
511 "SSL_CTX_add0_chain_cert(\"%s\") failed",
512 cert->data);
513594 X509_free(x509);
595 sk_X509_pop_free(*chain, X509_free);
596 return NULL;
597 }
598
599 if (sk_X509_push(*chain, temp) == 0) {
600 *err = "sk_X509_push() failed";
514601 BIO_free(bio);
515 return NGX_ERROR;
516 }
517
518 #else
519 if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
520 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
521 "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
522 cert->data);
523602 X509_free(x509);
524 BIO_free(bio);
525 return NGX_ERROR;
526 }
527 #endif
603 sk_X509_pop_free(*chain, X509_free);
604 return NULL;
605 }
528606 }
529607
530608 BIO_free(bio);
609
610 return x509;
611 }
612
613
614 static EVP_PKEY *
615 ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
616 ngx_str_t *key, ngx_array_t *passwords)
617 {
618 BIO *bio;
619 EVP_PKEY *pkey;
620 ngx_str_t *pwd;
621 ngx_uint_t tries;
622 pem_password_cb *cb;
531623
532624 if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) {
533625
541633 last = (u_char *) ngx_strchr(p, ':');
542634
543635 if (last == NULL) {
544 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
545 "invalid syntax in \"%V\"", key);
546 return NGX_ERROR;
636 *err = "invalid syntax";
637 return NULL;
547638 }
548639
549640 *last = '\0';
551642 engine = ENGINE_by_id((char *) p);
552643
553644 if (engine == NULL) {
554 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
555 "ENGINE_by_id(\"%s\") failed", p);
556 return NGX_ERROR;
645 *err = "ENGINE_by_id() failed";
646 return NULL;
557647 }
558648
559649 *last++ = ':';
561651 pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);
562652
563653 if (pkey == NULL) {
564 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
565 "ENGINE_load_private_key(\"%s\") failed", last);
654 *err = "ENGINE_load_private_key() failed";
566655 ENGINE_free(engine);
567 return NGX_ERROR;
656 return NULL;
568657 }
569658
570659 ENGINE_free(engine);
571660
572 if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
573 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
574 "SSL_CTX_use_PrivateKey(\"%s\") failed", last);
575 EVP_PKEY_free(pkey);
576 return NGX_ERROR;
577 }
578
579 EVP_PKEY_free(pkey);
580
581 return NGX_OK;
661 return pkey;
582662
583663 #else
584664
585 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
586 "loading \"engine:...\" certificate keys "
587 "is not supported");
588 return NGX_ERROR;
589
590 #endif
591 }
592
593 if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {
594 return NGX_ERROR;
665 *err = "loading \"engine:...\" certificate keys is not supported";
666 return NULL;
667
668 #endif
669 }
670
671 if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, key)
672 != NGX_OK)
673 {
674 *err = NULL;
675 return NULL;
676 }
677
678 bio = BIO_new_file((char *) key->data, "r");
679 if (bio == NULL) {
680 *err = "BIO_new_file() failed";
681 return NULL;
595682 }
596683
597684 if (passwords) {
598685 tries = passwords->nelts;
599686 pwd = passwords->elts;
600
601 SSL_CTX_set_default_passwd_cb(ssl->ctx, ngx_ssl_password_callback);
602 SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, pwd);
687 cb = ngx_ssl_password_callback;
603688
604689 } else {
605690 tries = 1;
606 #if (NGX_SUPPRESS_WARN)
607691 pwd = NULL;
608 #endif
692 cb = NULL;
609693 }
610694
611695 for ( ;; ) {
612696
613 if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data,
614 SSL_FILETYPE_PEM)
615 != 0)
616 {
697 pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
698 if (pkey != NULL) {
617699 break;
618700 }
619701
620702 if (--tries) {
621703 ERR_clear_error();
622 SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, ++pwd);
704 (void) BIO_reset(bio);
705 pwd++;
623706 continue;
624707 }
625708
626 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
627 "SSL_CTX_use_PrivateKey_file(\"%s\") failed", key->data);
628 return NGX_ERROR;
629 }
630
631 SSL_CTX_set_default_passwd_cb(ssl->ctx, NULL);
632
633 return NGX_OK;
709 *err = "PEM_read_bio_PrivateKey() failed";
710 BIO_free(bio);
711 return NULL;
712 }
713
714 BIO_free(bio);
715
716 return pkey;
634717 }
635718
636719