[nginx] SSL: the "ssl_password_file" directive.

Valentin Bartenev vbart at nginx.com
Thu Jun 26 17:40:55 UTC 2014


details:   http://hg.nginx.org/nginx/rev/42114bf12da0
branches:  
changeset: 5744:42114bf12da0
user:      Valentin Bartenev <vbart at nginx.com>
date:      Mon Jun 16 19:43:25 2014 +0400
description:
SSL: the "ssl_password_file" directive.

diffstat:

 src/event/ngx_event_openssl.c          |  223 +++++++++++++++++++++++++++++++-
 src/event/ngx_event_openssl.h          |    3 +-
 src/http/modules/ngx_http_ssl_module.c |   37 +++++-
 src/http/modules/ngx_http_ssl_module.h |    2 +
 src/mail/ngx_mail_ssl_module.c         |   37 +++++-
 src/mail/ngx_mail_ssl_module.h         |    2 +
 6 files changed, 293 insertions(+), 11 deletions(-)

diffs (truncated from 480 to 300 lines):

diff -r dde2ae4701e1 -r 42114bf12da0 src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c	Thu Jun 26 05:08:59 2014 +0400
+++ b/src/event/ngx_event_openssl.c	Mon Jun 16 19:43:25 2014 +0400
@@ -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_
 
 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;
@@ -348,19 +356,76 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_
         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)
@@ -597,6 +662,148 @@ ngx_ssl_rsa512_key_callback(ngx_ssl_conn
 }
 
 
+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)
 {
diff -r dde2ae4701e1 -r 42114bf12da0 src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h	Thu Jun 26 05:08:59 2014 +0400
+++ b/src/event/ngx_event_openssl.h	Mon Jun 16 19:43:25 2014 +0400
@@ -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_
     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,
diff -r dde2ae4701e1 -r 42114bf12da0 src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c	Thu Jun 26 05:08:59 2014 +0400
+++ b/src/http/modules/ngx_http_ssl_module.c	Mon Jun 16 19:43:25 2014 +0400
@@ -43,6 +43,8 @@ static char *ngx_http_ssl_merge_srv_conf
 
 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,



More information about the nginx-devel mailing list