Skip to content

Commit

Permalink
SSL: the "ssl_password_file" directive.
Browse files Browse the repository at this point in the history
  • Loading branch information
VBart committed Jun 16, 2014
1 parent 3d167cd commit 9f8785a
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 11 deletions.
223 changes: 215 additions & 8 deletions src/event/ngx_event_openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
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;
Expand Down Expand Up @@ -348,19 +356,76 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
return NGX_ERROR;
}

if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data,
SSL_FILETYPE_PEM)
== 0)
{
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)
{
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)
Expand Down Expand Up @@ -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)
{
Expand Down
3 changes: 2 additions & 1 deletion src/event/ngx_event_openssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
37 changes: 36 additions & 1 deletion src/http/modules/ngx_http_ssl_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down
2 changes: 2 additions & 0 deletions src/http/modules/ngx_http_ssl_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 9f8785a

Please sign in to comment.