diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index a7b389444..2b5c3ab93 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -82,6 +82,7 @@ static time_t ngx_ssl_parse_time( static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_openssl_provider(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); @@ -94,6 +95,13 @@ static ngx_command_t ngx_openssl_commands[] = { 0, NULL }, + { ngx_string("ssl_provider"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE, + ngx_openssl_provider, + 0, + 0, + NULL }, + ngx_null_command }; @@ -5982,6 +5990,84 @@ ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +static char * +ngx_openssl_provider(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x30200000L) + + u_char *p; + ngx_uint_t i; + ngx_str_t *value; + OSSL_PARAM *param; + ngx_array_t params; + + if (cf->cycle->modules_used) { + return "is specified too late"; + } + + value = cf->args->elts; + + if (OSSL_PROVIDER_available(NULL, (char *) value[1].data)) { + ngx_conf_log_error(NGX_LOG_INFO, cf, 0, + "ssl_provider \"%V\" is already loaded", &value[1]); + return NGX_CONF_OK; + } + + if (ngx_array_init(¶ms, cf->pool, cf->args->nelts - 1, + sizeof(OSSL_PARAM)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + ngx_memzero(params.elts, (cf->args->nelts - 1) * sizeof(OSSL_PARAM)); + + for (i = 2; i < cf->args->nelts; i++) { + p = (u_char *) ngx_strchr(value[i].data, '='); + + if (p == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + param = ngx_array_push(¶ms); + if (param == NULL) { + return NGX_CONF_ERROR; + } + + *p++ = '\0'; + + param->key = (char *) value[i].data; + param->data_type = OSSL_PARAM_UTF8_STRING; + param->data = p; + param->data_size = value[i].data + value[i].len - p; + } + + param = ngx_array_push(¶ms); + if (param == NULL) { + return NGX_CONF_ERROR; + } + + if (OSSL_PROVIDER_load_ex(NULL, (char* ) value[1].data, + (params.nelts > 1) ? params.elts : NULL) + == NULL) + { + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, + "OSSL_PROVIDER_load_ex(\"%V\") failed", &value[1]); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + +#else + + return "is not supported"; + +#endif +} + + static void ngx_openssl_exit(ngx_cycle_t *cycle) { diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 9e68deb44..7c2a0f6dd 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -38,6 +38,9 @@ #ifndef OPENSSL_NO_OCSP #include #endif +#if (OPENSSL_VERSION_NUMBER >= 0x30200000L) +#include +#endif #include #include #include diff --git a/t/ssl_provider.t b/t/ssl_provider.t new file mode 100644 index 000000000..b817db3a8 --- /dev/null +++ b/t/ssl_provider.t @@ -0,0 +1,188 @@ +#!/usr/bin/perl + +# (C) Sergey Kandaurov +# (C) Aleksei Bavshin +# (C) Nginx, Inc. + +# Tests for "ssl_provider" directive. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +plan(skip_all => 'win32') if $^O eq 'MSWin32'; + +plan(skip_all => 'may not work, incompatible with sanitizers') + unless $ENV{TEST_NGINX_UNSAFE}; + +my $t = Test::Nginx->new()->has(qw/http proxy http_ssl openssl:3.2.0/) + ->has_daemon('openssl')->has_daemon('softhsm2-util'); + +my $libsofthsm2_path; +my @so_paths = ( + '/usr/lib/softhsm', # Debian-based + '/usr/local/lib/softhsm', # FreeBSD + '/opt/local/lib/softhsm', # MacPorts + '/lib64', # RHEL-based + split /:/, $ENV{TEST_NGINX_SOFTHSM} || '' +); + +for my $so_path (@so_paths) { + $so_path .= '/libsofthsm2.so'; + if (-e $so_path) { + $libsofthsm2_path = $so_path; + last; + } +}; + +plan(skip_all => "libsofthsm2.so not found") unless $libsofthsm2_path; + +$t->write_file_expand('nginx.conf', <write_file('openssl.conf', $openssl_conf); + +my $d = $t->testdir(); + +$t->write_file('softhsm2.conf', <>$d/openssl.out 2>&1"); + + system("openssl genrsa -out $d/$name.key 2048 " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create private key: $!\n"; + + system("softhsm2-util --import $d/$name.key --id 00 --label nx_key_0 " + . '--token NginxZero --pin 1234 ' + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't import private key: $!\n"; + + system("openssl req -x509 -new -config $d/openssl.conf " + . "-subj /CN=$name/ -out $d/$name.crt -text -passin pass:1234 " + . '-key "pkcs11:token=NginxZero;object=nx_key_0" ' + . ">>$d/openssl.out 2>&1") == 0 + or plan(skip_all => "missing pkcs11-provider"); +} + +$t->write_file('pin.txt', '1234'); +$t->write_file('index.html', ''); + +$t->run()->plan(2); + +############################################################################### + +like(http_get('/proxy'), qr/200 OK/, 'ssl provider keys'); +like(http_get('/var'), qr/200 OK/, 'ssl_certificate with variable'); + +###############################################################################