[PATCH 5 of 6] SSL: caching private keys
Mini Hawthorne
mini at nginx.com
Tue Jul 23 19:30:28 UTC 2024
# HG changeset patch
# User Mini Hawthorne <mini at f5.com>
# Date 1721762945 0
# Tue Jul 23 19:29:05 2024 +0000
# Node ID 298a9eaa59d2a16f85b6aa3584eb5f8298e6c9bc
# Parent 867e05f555e6f593589a0278c865e7dcffe597f4
SSL: caching private keys.
Added ngx_ssl_cache_key which caches private keys. Special support is included
for "engine:..." keys.
EVP_KEY objects are a reference-counted container for key material, so shallow
copies and OpenSSL stack management aren't needed as with certificates.
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -18,10 +18,6 @@ typedef struct {
} ngx_openssl_conf_t;
-static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
- ngx_str_t *key, ngx_array_t *passwords);
-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);
@@ -536,7 +532,8 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_
}
#endif
- pkey = ngx_ssl_load_certificate_key(cf->pool, &err, key, passwords);
+ pkey = ngx_ssl_cache_fetch(cf->cycle, cf->pool, &ngx_ssl_cache_key, &err,
+ key, passwords);
if (pkey == NULL) {
if (err != NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
@@ -610,10 +607,11 @@ ngx_ssl_connection_certificate(ngx_conne
#endif
- pkey = ngx_ssl_load_certificate_key(pool, &err, key, passwords);
+ pkey = ngx_ssl_cache_fetch((ngx_cycle_t *) ngx_cycle, c->pool,
+ &ngx_ssl_cache_key, &err, key, passwords);
if (pkey == NULL) {
if (err != NULL) {
- ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+ ngx_ssl_error(NGX_LOG_EMERG, c->log, 0,
"cannot load certificate key \"%s\": %s",
key->data, err);
}
@@ -634,151 +632,6 @@ ngx_ssl_connection_certificate(ngx_conne
}
-static EVP_PKEY *
-ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
- ngx_str_t *key, ngx_array_t *passwords)
-{
- BIO *bio;
- EVP_PKEY *pkey;
- ngx_str_t *pwd;
- ngx_uint_t tries;
- pem_password_cb *cb;
-
- if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) {
-
-#ifndef OPENSSL_NO_ENGINE
-
- u_char *p, *last;
- ENGINE *engine;
-
- p = key->data + sizeof("engine:") - 1;
- last = (u_char *) ngx_strchr(p, ':');
-
- if (last == NULL) {
- *err = "invalid syntax";
- return NULL;
- }
-
- *last = '\0';
-
- engine = ENGINE_by_id((char *) p);
-
- *last++ = ':';
-
- if (engine == NULL) {
- *err = "ENGINE_by_id() failed";
- return NULL;
- }
-
- pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);
-
- if (pkey == NULL) {
- *err = "ENGINE_load_private_key() failed";
- ENGINE_free(engine);
- return NULL;
- }
-
- ENGINE_free(engine);
-
- return pkey;
-
-#else
-
- *err = "loading \"engine:...\" certificate keys is not supported";
- return NULL;
-
-#endif
- }
-
- if (ngx_strncmp(key->data, "data:", sizeof("data:") - 1) == 0) {
-
- bio = BIO_new_mem_buf(key->data + sizeof("data:") - 1,
- key->len - (sizeof("data:") - 1));
- if (bio == NULL) {
- *err = "BIO_new_mem_buf() failed";
- return NULL;
- }
-
- } else {
-
- if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, key)
- != NGX_OK)
- {
- *err = NULL;
- return NULL;
- }
-
- bio = BIO_new_file((char *) key->data, "r");
- if (bio == NULL) {
- *err = "BIO_new_file() failed";
- return NULL;
- }
- }
-
- if (passwords) {
- tries = passwords->nelts;
- pwd = passwords->elts;
- cb = ngx_ssl_password_callback;
-
- } else {
- tries = 1;
- pwd = NULL;
- cb = NULL;
- }
-
- for ( ;; ) {
-
- pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
- if (pkey != NULL) {
- break;
- }
-
- if (tries-- > 1) {
- ERR_clear_error();
- (void) BIO_reset(bio);
- pwd++;
- continue;
- }
-
- *err = "PEM_read_bio_PrivateKey() failed";
- BIO_free(bio);
- return NULL;
- }
-
- BIO_free(bio);
-
- return pkey;
-}
-
-
-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 == NULL) {
- 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_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
ngx_uint_t prefer_server_ciphers)
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -353,6 +353,7 @@ extern int ngx_ssl_index;
extern ngx_ssl_cache_type_t ngx_ssl_cache_cert;
extern ngx_ssl_cache_type_t ngx_ssl_cache_crl;
+extern ngx_ssl_cache_type_t ngx_ssl_cache_key;
#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */
diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c
--- a/src/event/ngx_event_openssl_cache.c
+++ b/src/event/ngx_event_openssl_cache.c
@@ -54,6 +54,12 @@ static void *ngx_ssl_cache_crl_create(ng
static void ngx_ssl_cache_crl_free(void *data);
static void *ngx_ssl_cache_crl_ref(char **err, void *data);
+static void *ngx_ssl_cache_key_create(ngx_str_t *id, char **err, void *data);
+static int ngx_ssl_cache_key_password_callback(char *buf, int size, int rwflag,
+ void *userdata);
+static void ngx_ssl_cache_key_free(void *data);
+static void *ngx_ssl_cache_key_ref(char **err, void *data);
+
static BIO *ngx_ssl_cache_create_bio(ngx_str_t *id, char **err);
@@ -82,6 +88,15 @@ ngx_ssl_cache_type_t ngx_ssl_cache_crl
};
+ngx_ssl_cache_type_t ngx_ssl_cache_key = {
+ "private key",
+
+ ngx_ssl_cache_key_create,
+ ngx_ssl_cache_key_free,
+ ngx_ssl_cache_key_ref,
+};
+
+
ngx_module_t ngx_openssl_cache_module = {
NGX_MODULE_V1,
&ngx_openssl_cache_module_ctx, /* module context */
@@ -508,6 +523,154 @@ ngx_ssl_cache_crl_ref(char **err, void *
}
+static void *
+ngx_ssl_cache_key_create(ngx_str_t *id, char **err, void *data)
+{
+ ngx_array_t *passwords = data;
+
+ BIO *bio;
+ EVP_PKEY *pkey;
+ ngx_str_t *pwd;
+ ngx_uint_t tries;
+ pem_password_cb *cb;
+
+ if (ngx_strncmp(id->data, "engine:", sizeof("engine:") - 1) == 0) {
+
+#ifndef OPENSSL_NO_ENGINE
+
+ u_char *p, *last;
+ ENGINE *engine;
+
+ p = id->data + sizeof("engine:") - 1;
+ last = (u_char *) ngx_strchr(p, ':');
+
+ if (last == NULL) {
+ *err = "invalid syntax";
+ return NULL;
+ }
+
+ *last = '\0';
+
+ engine = ENGINE_by_id((char *) p);
+
+ *last++ = ':';
+
+ if (engine == NULL) {
+ *err = "ENGINE_by_id() failed";
+ return NULL;
+ }
+
+ pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);
+
+ if (pkey == NULL) {
+ *err = "ENGINE_load_private_key() failed";
+ ENGINE_free(engine);
+ return NULL;
+ }
+
+ ENGINE_free(engine);
+
+ return pkey;
+#else
+ *err = "loading \"engine:...\" certificate keys is not supported";
+ return NULL;
+#endif
+ }
+
+ /* figure out where to load from */
+ bio = ngx_ssl_cache_create_bio(id, err);
+ if (bio == NULL) {
+ return NULL;
+ }
+
+ if (passwords) {
+ tries = passwords->nelts;
+ pwd = passwords->elts;
+ cb = ngx_ssl_cache_key_password_callback;
+
+ } else {
+ tries = 1;
+ pwd = NULL;
+ cb = NULL;
+ }
+
+ for ( ;; ) {
+
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
+ if (pkey != NULL) {
+ break;
+ }
+
+ if (tries-- > 1) {
+ ERR_clear_error();
+ (void) BIO_reset(bio);
+ pwd++;
+ continue;
+ }
+
+ *err = "PEM_read_bio_PrivateKey() failed";
+ BIO_free(bio);
+ return NULL;
+ }
+
+ BIO_free(bio);
+
+ return pkey;
+}
+
+
+static int
+ngx_ssl_cache_key_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_cache_key_password_callback() is called "
+ "for encryption");
+ return 0;
+ }
+
+ if (pwd == NULL) {
+ 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;
+}
+
+
+static void
+ngx_ssl_cache_key_free(void *data)
+{
+ EVP_PKEY_free(data);
+}
+
+
+static void *
+ngx_ssl_cache_key_ref(char **err, void *data)
+{
+ EVP_PKEY *pkey = data;
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ EVP_PKEY_up_ref(pkey);
+#else
+ CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
+#endif
+
+ return data;
+}
+
+
static BIO *
ngx_ssl_cache_create_bio(ngx_str_t *id, char **err)
{
More information about the nginx-devel
mailing list