SSL: the "ssl_password_file" directive.

This commit is contained in:
Valentin Bartenev 2014-06-16 19:43:25 +04:00
parent 3d167cd42e
commit 9f8785ae5e
6 changed files with 293 additions and 11 deletions

View File

@ -10,14 +10,20 @@
#include <ngx_event.h>
#define NGX_SSL_PASSWORD_BUFFER_SIZE 4096
typedef struct {
ngx_uint_t engine; /* unsigned engine:1; */
} ngx_openssl_conf_t;
static int ngx_ssl_password_callback(char *buf, int size, int rwflag,
void *userdata);
static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
int ret);
static void ngx_ssl_passwords_cleanup(void *data);
static void ngx_ssl_handshake_handler(ngx_event_t *ev);
static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);
static void ngx_ssl_write_handler(ngx_event_t *wev);
@ -257,11 +263,13 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)
ngx_int_t
ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_str_t *key)
ngx_str_t *key, ngx_array_t *passwords)
{
BIO *bio;
X509 *x509;
u_long n;
ngx_str_t *pwd;
ngx_uint_t tries;
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
return NGX_ERROR;
@ -348,19 +356,76 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
return NGX_ERROR;
}
if (passwords) {
tries = passwords->nelts;
pwd = passwords->elts;
SSL_CTX_set_default_passwd_cb(ssl->ctx, ngx_ssl_password_callback);
SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, pwd);
} else {
tries = 1;
#if (NGX_SUPPRESS_WARN)
pwd = NULL;
#endif
}
for ( ;; ) {
if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data,
SSL_FILETYPE_PEM)
== 0)
!= 0)
{
break;
}
if (--tries) {
n = ERR_peek_error();
if (ERR_GET_LIB(n) == ERR_LIB_EVP
&& ERR_GET_REASON(n) == EVP_R_BAD_DECRYPT)
{
ERR_clear_error();
SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, ++pwd);
continue;
}
}
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_PrivateKey_file(\"%s\") failed", key->data);
return NGX_ERROR;
}
SSL_CTX_set_default_passwd_cb(ssl->ctx, NULL);
return NGX_OK;
}
static int
ngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata)
{
ngx_str_t *pwd = userdata;
if (rwflag) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
"ngx_ssl_password_callback() is called for encryption");
return 0;
}
if (pwd->len > (size_t) size) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"password is truncated to %d bytes", size);
} else {
size = pwd->len;
}
ngx_memcpy(buf, pwd->data, size);
return size;
}
ngx_int_t
ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_int_t depth)
@ -597,6 +662,148 @@ ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
}
ngx_array_t *
ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)
{
u_char *p, *last, *end;
size_t len;
ssize_t n;
ngx_fd_t fd;
ngx_str_t *pwd;
ngx_array_t *passwords;
ngx_pool_cleanup_t *cln;
u_char buf[NGX_SSL_PASSWORD_BUFFER_SIZE];
if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
return NULL;
}
cln = ngx_pool_cleanup_add(cf->temp_pool, 0);
passwords = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t));
if (cln == NULL || passwords == NULL) {
return NULL;
}
cln->handler = ngx_ssl_passwords_cleanup;
cln->data = passwords;
fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_open_file_n " \"%s\" failed", file->data);
return NULL;
}
len = 0;
last = buf;
do {
n = ngx_read_fd(fd, last, NGX_SSL_PASSWORD_BUFFER_SIZE - len);
if (n == -1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_read_fd_n " \"%s\" failed", file->data);
passwords = NULL;
goto cleanup;
}
end = last + n;
if (len && n == 0) {
*end++ = LF;
}
p = buf;
for ( ;; ) {
last = ngx_strlchr(last, end, LF);
if (last == NULL) {
break;
}
len = last++ - p;
if (len && p[len - 1] == CR) {
len--;
}
if (len) {
pwd = ngx_array_push(passwords);
if (pwd == NULL) {
passwords = NULL;
goto cleanup;
}
pwd->len = len;
pwd->data = ngx_pnalloc(cf->temp_pool, len);
if (pwd->data == NULL) {
passwords->nelts--;
passwords = NULL;
goto cleanup;
}
ngx_memcpy(pwd->data, p, len);
}
p = last;
}
len = end - p;
if (len == NGX_SSL_PASSWORD_BUFFER_SIZE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too long line in \"%s\"", file->data);
passwords = NULL;
goto cleanup;
}
ngx_memmove(buf, p, len);
last = buf + len;
} while (n != 0);
if (passwords->nelts == 0) {
pwd = ngx_array_push(passwords);
if (pwd == NULL) {
passwords = NULL;
goto cleanup;
}
ngx_memzero(pwd, sizeof(ngx_str_t));
}
cleanup:
if (ngx_close_file(fd) == NGX_FILE_ERROR) {
ngx_conf_log_error(NGX_LOG_ALERT, cf, ngx_errno,
ngx_close_file_n " \"%s\" failed", file->data);
}
ngx_memzero(buf, NGX_SSL_PASSWORD_BUFFER_SIZE);
return passwords;
}
static void
ngx_ssl_passwords_cleanup(void *data)
{
ngx_array_t *passwords = data;
ngx_str_t *pwd;
ngx_uint_t i;
pwd = passwords->elts;
for (i = 0; i < passwords->nelts; i++) {
ngx_memzero(pwd[i].data, pwd[i].len);
}
}
ngx_int_t
ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
{

View File

@ -112,7 +112,7 @@ typedef struct {
ngx_int_t ngx_ssl_init(ngx_log_t *log);
ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);
ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_str_t *key);
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_int_t depth);
ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
@ -124,6 +124,7 @@ ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_resolver_t *resolver, ngx_msec_t resolver_timeout);
RSA *ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
int key_length);
ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,

View File

@ -43,6 +43,8 @@ static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
@ -91,6 +93,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
offsetof(ngx_http_ssl_srv_conf_t, certificate_key),
NULL },
{ ngx_string("ssl_password_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_ssl_password_file,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("ssl_dhparam"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
@ -514,6 +523,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
sscf->buffer_size = NGX_CONF_UNSET_SIZE;
sscf->verify = NGX_CONF_UNSET_UINT;
sscf->verify_depth = NGX_CONF_UNSET_UINT;
sscf->passwords = NGX_CONF_UNSET_PTR;
sscf->builtin_session_cache = NGX_CONF_UNSET;
sscf->session_timeout = NGX_CONF_UNSET;
sscf->session_tickets = NGX_CONF_UNSET;
@ -563,6 +573,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
@ -652,7 +664,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
cln->data = &conf->ssl;
if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
&conf->certificate_key)
&conf->certificate_key, conf->passwords)
!= NGX_OK)
{
return NGX_CONF_ERROR;
@ -781,6 +793,29 @@ ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
static char *
ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_ssl_srv_conf_t *sscf = conf;
ngx_str_t *value;
if (sscf->passwords != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
sscf->passwords = ngx_ssl_read_password_file(cf, &value[1]);
if (sscf->passwords == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{

View File

@ -42,6 +42,8 @@ typedef struct {
ngx_str_t ciphers;
ngx_array_t *passwords;
ngx_shm_zone_t *shm_zone;
ngx_flag_t session_tickets;

View File

@ -21,6 +21,8 @@ static char *ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
@ -74,6 +76,13 @@ static ngx_command_t ngx_mail_ssl_commands[] = {
offsetof(ngx_mail_ssl_conf_t, certificate_key),
NULL },
{ ngx_string("ssl_password_file"),
NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
ngx_mail_ssl_password_file,
NGX_MAIL_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("ssl_dhparam"),
NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
@ -195,6 +204,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
scf->enable = NGX_CONF_UNSET;
scf->starttls = NGX_CONF_UNSET_UINT;
scf->passwords = NGX_CONF_UNSET_PTR;
scf->prefer_server_ciphers = NGX_CONF_UNSET;
scf->builtin_session_cache = NGX_CONF_UNSET;
scf->session_timeout = NGX_CONF_UNSET;
@ -231,6 +241,8 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
@ -302,7 +314,7 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
cln->data = &conf->ssl;
if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
&conf->certificate_key)
&conf->certificate_key, conf->passwords)
!= NGX_OK)
{
return NGX_CONF_ERROR;
@ -421,6 +433,29 @@ ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
static char *
ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_mail_ssl_conf_t *scf = conf;
ngx_str_t *value;
if (scf->passwords != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
scf->passwords = ngx_ssl_read_password_file(cf, &value[1]);
if (scf->passwords == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{

View File

@ -39,6 +39,8 @@ typedef struct {
ngx_str_t ciphers;
ngx_array_t *passwords;
ngx_shm_zone_t *shm_zone;
ngx_flag_t session_tickets;