vendor/nginx/src/event/ngx_event_openssl.c in nginxtra-1.6.3.9 vs vendor/nginx/src/event/ngx_event_openssl.c in nginxtra-1.8.0.9
- old
+ new
@@ -8,18 +8,24 @@
#include <ngx_config.h>
#include <ngx_core.h>
#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);
static void ngx_ssl_read_handler(ngx_event_t *rev);
static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
@@ -44,10 +50,14 @@
static int ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
HMAC_CTX *hctx, int enc);
#endif
+#if (OPENSSL_VERSION_NUMBER < 0x10002002L || defined LIBRESSL_VERSION_NUMBER)
+static ngx_int_t ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *str);
+#endif
+
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 void ngx_openssl_exit(ngx_cycle_t *cycle);
@@ -96,11 +106,13 @@
ngx_int_t
ngx_ssl_init(ngx_log_t *log)
{
+#ifndef OPENSSL_IS_BORINGSSL
OPENSSL_config(NULL);
+#endif
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
@@ -194,46 +206,74 @@
ssl->buffer_size = NGX_SSL_BUFSIZE;
/* client side options */
+#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG);
+#endif
+
+#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_NETSCAPE_CHALLENGE_BUG);
+#endif
/* server side options */
+#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);
+#endif
+
+#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
+#endif
#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
/* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */
SSL_CTX_set_options(ssl->ctx, SSL_OP_MSIE_SSLV2_RSA_PADDING);
#endif
+#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);
+#endif
+
+#ifdef SSL_OP_TLS_D5_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG);
+#endif
+
+#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG);
+#endif
+#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+#endif
SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);
+#ifdef SSL_CTRL_CLEAR_OPTIONS
+ /* only in 0.9.8m+ */
+ SSL_CTX_clear_options(ssl->ctx,
+ SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);
+#endif
+
if (!(protocols & NGX_SSL_SSLv2)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2);
}
if (!(protocols & NGX_SSL_SSLv3)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv3);
}
if (!(protocols & NGX_SSL_TLSv1)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1);
}
#ifdef SSL_OP_NO_TLSv1_1
+ SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TLSv1_1);
if (!(protocols & NGX_SSL_TLSv1_1)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_1);
}
#endif
#ifdef SSL_OP_NO_TLSv1_2
+ SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TLSv1_2);
if (!(protocols & NGX_SSL_TLSv1_2)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_2);
}
#endif
@@ -243,25 +283,31 @@
#ifdef SSL_MODE_RELEASE_BUFFERS
SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS);
#endif
+#ifdef SSL_MODE_NO_AUTO_CHAIN
+ SSL_CTX_set_mode(ssl->ctx, SSL_MODE_NO_AUTO_CHAIN);
+#endif
+
SSL_CTX_set_read_ahead(ssl->ctx, 1);
SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback);
return NGX_OK;
}
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;
}
@@ -340,27 +386,139 @@
}
}
BIO_free(bio);
+ if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) {
+
+#ifndef OPENSSL_NO_ENGINE
+
+ u_char *p, *last;
+ ENGINE *engine;
+ EVP_PKEY *pkey;
+
+ p = key->data + sizeof("engine:") - 1;
+ last = (u_char *) ngx_strchr(p, ':');
+
+ if (last == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid syntax in \"%V\"", key);
+ return NGX_ERROR;
+ }
+
+ *last = '\0';
+
+ engine = ENGINE_by_id((char *) p);
+
+ if (engine == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "ENGINE_by_id(\"%s\") failed", p);
+ return NGX_ERROR;
+ }
+
+ *last++ = ':';
+
+ pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);
+
+ if (pkey == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "ENGINE_load_private_key(\"%s\") failed", last);
+ ENGINE_free(engine);
+ return NGX_ERROR;
+ }
+
+ ENGINE_free(engine);
+
+ if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_use_PrivateKey(\"%s\") failed", last);
+ EVP_PKEY_free(pkey);
+ return NGX_ERROR;
+ }
+
+ EVP_PKEY_free(pkey);
+
+ return NGX_OK;
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "loading \"engine:...\" certificate keys "
+ "is not supported");
+ return NGX_ERROR;
+
+#endif
+ }
+
if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {
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) {
+ 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)
{
STACK_OF(X509_NAME) *list;
@@ -519,11 +677,11 @@
iname = X509_get_issuer_name(cert);
issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)";
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
"verify:%d, error:%d, depth:%d, "
- "subject:\"%s\",issuer: \"%s\"",
+ "subject:\"%s\", issuer:\"%s\"",
ok, err, depth, subject, issuer);
if (sname) {
OPENSSL_free(subject);
}
@@ -583,20 +741,168 @@
ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
int key_length)
{
static RSA *key;
- if (key_length == 512) {
- if (key == NULL) {
- key = RSA_generate_key(512, RSA_F4, NULL, NULL);
- }
+ if (key_length != 512) {
+ return NULL;
}
+#ifndef OPENSSL_NO_DEPRECATED
+
+ if (key == NULL) {
+ key = RSA_generate_key(512, RSA_F4, NULL, NULL);
+ }
+
+#endif
+
return key;
}
+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)
{
DH *dh;
BIO *bio;
@@ -850,15 +1156,19 @@
c->recv = ngx_ssl_recv;
c->send = ngx_ssl_write;
c->recv_chain = ngx_ssl_recv_chain;
c->send_chain = ngx_ssl_send_chain;
+#ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS
+
/* initial handshake done, disable renegotiation (CVE-2009-3555) */
if (c->ssl->connection->s3) {
c->ssl->connection->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
}
+#endif
+
return NGX_OK;
}
sslerr = SSL_get_error(c->ssl->connection, n);
@@ -901,12 +1211,12 @@
c->ssl->no_wait_shutdown = 1;
c->ssl->no_send_shutdown = 1;
c->read->eof = 1;
if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
- ngx_log_error(NGX_LOG_INFO, c->log, err,
- "peer closed connection in SSL handshake");
+ ngx_connection_error(c, err,
+ "peer closed connection in SSL handshake");
return NGX_ERROR;
}
c->read->error = 1;
@@ -939,25 +1249,36 @@
c->ssl->handler(c);
}
ssize_t
-ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl)
+ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit)
{
u_char *last;
- ssize_t n, bytes;
+ ssize_t n, bytes, size;
ngx_buf_t *b;
bytes = 0;
b = cl->buf;
last = b->last;
for ( ;; ) {
+ size = b->end - last;
- n = ngx_ssl_recv(c, last, b->end - last);
+ if (limit) {
+ if (bytes >= limit) {
+ return bytes;
+ }
+ if (bytes + size > limit) {
+ size = (ssize_t) (limit - bytes);
+ }
+ }
+
+ n = ngx_ssl_recv(c, last, size);
+
if (n > 0) {
last += n;
bytes += n;
if (last == b->end) {
@@ -1205,11 +1526,10 @@
if (n == NGX_AGAIN) {
return in;
}
in->buf->pos += n;
- c->sent += n;
if (in->buf->pos == in->buf->last) {
in = in->next;
}
}
@@ -1306,11 +1626,10 @@
if (n == NGX_AGAIN) {
break;
}
buf->pos += n;
- c->sent += n;
if (n < size) {
break;
}
@@ -1364,10 +1683,12 @@
}
ngx_post_event(c->read, &ngx_posted_events);
}
+ c->sent += n;
+
return n;
}
sslerr = SSL_get_error(c->ssl->connection, n);
@@ -1956,13 +2277,14 @@
static int
ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
{
int len;
- u_char *p, *id, *cached_sess;
+ u_char *p, *id, *cached_sess, *session_id;
uint32_t hash;
SSL_CTX *ssl_ctx;
+ unsigned int session_id_length;
ngx_shm_zone_t *shm_zone;
ngx_connection_t *c;
ngx_slab_pool_t *shpool;
ngx_ssl_sess_id_t *sess_id;
ngx_ssl_session_cache_t *cache;
@@ -2021,45 +2343,56 @@
if (sess_id == NULL) {
goto failed;
}
}
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+
+ session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);
+
+#else
+
+ session_id = sess->session_id;
+ session_id_length = sess->session_id_length;
+
+#endif
+
#if (NGX_PTR_SIZE == 8)
id = sess_id->sess_id;
#else
- id = ngx_slab_alloc_locked(shpool, sess->session_id_length);
+ id = ngx_slab_alloc_locked(shpool, session_id_length);
if (id == NULL) {
/* drop the oldest non-expired session and try once more */
ngx_ssl_expire_sessions(cache, shpool, 0);
- id = ngx_slab_alloc_locked(shpool, sess->session_id_length);
+ id = ngx_slab_alloc_locked(shpool, session_id_length);
if (id == NULL) {
goto failed;
}
}
#endif
ngx_memcpy(cached_sess, buf, len);
- ngx_memcpy(id, sess->session_id, sess->session_id_length);
+ ngx_memcpy(id, session_id, session_id_length);
- hash = ngx_crc32_short(sess->session_id, sess->session_id_length);
+ hash = ngx_crc32_short(session_id, session_id_length);
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "ssl new session: %08XD:%d:%d",
- hash, sess->session_id_length, len);
+ "ssl new session: %08XD:%ud:%d",
+ hash, session_id_length, len);
sess_id->node.key = hash;
- sess_id->node.data = (u_char) sess->session_id_length;
+ sess_id->node.data = (u_char) session_id_length;
sess_id->id = id;
sess_id->len = len;
sess_id->session = cached_sess;
sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx);
@@ -2203,14 +2536,14 @@
static void
ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
{
- size_t len;
u_char *id;
uint32_t hash;
ngx_int_t rc;
+ unsigned int len;
ngx_shm_zone_t *shm_zone;
ngx_slab_pool_t *shpool;
ngx_rbtree_node_t *node, *sentinel;
ngx_ssl_sess_id_t *sess_id;
ngx_ssl_session_cache_t *cache;
@@ -2221,17 +2554,25 @@
return;
}
cache = shm_zone->data;
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+
+ id = (u_char *) SSL_SESSION_get_id(sess, &len);
+
+#else
+
id = sess->session_id;
- len = (size_t) sess->session_id_length;
+ len = sess->session_id_length;
+#endif
+
hash = ngx_crc32_short(id, len);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
- "ssl remove session: %08XD:%uz", hash, len);
+ "ssl remove session: %08XD:%ud", hash, len);
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
ngx_shmtx_lock(&shpool->mutex);
@@ -2513,20 +2854,20 @@
#endif
if (enc == 1) {
/* encrypt session ticket */
- ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"ssl session ticket encrypt, key: \"%*s\" (%s session)",
ngx_hex_dump(buf, key[0].name, 16) - buf, buf,
SSL_session_reused(ssl_conn) ? "reused" : "new");
RAND_pseudo_bytes(iv, 16);
EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key[0].aes_key, iv);
HMAC_Init_ex(hctx, key[0].hmac_key, 16,
ngx_ssl_session_ticket_md(), NULL);
- memcpy(name, key[0].name, 16);
+ ngx_memcpy(name, key[0].name, 16);
return 0;
} else {
/* decrypt session ticket */
@@ -2535,19 +2876,19 @@
if (ngx_memcmp(name, key[i].name, 16) == 0) {
goto found;
}
}
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"ssl session ticket decrypt, key: \"%*s\" not found",
ngx_hex_dump(buf, name, 16) - buf, buf);
return 0;
found:
- ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"ssl session ticket decrypt, key: \"%*s\"%s",
ngx_hex_dump(buf, key[i].name, 16) - buf, buf,
(i == 0) ? " (default)" : "");
HMAC_Init_ex(hctx, key[i].hmac_key, 16,
@@ -2582,10 +2923,179 @@
SSL_CTX_free(ssl->ctx);
}
ngx_int_t
+ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name)
+{
+ X509 *cert;
+
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+ if (cert == NULL) {
+ return NGX_ERROR;
+ }
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10002002L && !defined LIBRESSL_VERSION_NUMBER)
+
+ /* X509_check_host() is only available in OpenSSL 1.0.2+ */
+
+ if (name->len == 0) {
+ goto failed;
+ }
+
+ if (X509_check_host(cert, (char *) name->data, name->len, 0, NULL) != 1) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "X509_check_host(): no match");
+ goto failed;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "X509_check_host(): match");
+
+ goto found;
+
+#else
+ {
+ int n, i;
+ X509_NAME *sname;
+ ASN1_STRING *str;
+ X509_NAME_ENTRY *entry;
+ GENERAL_NAME *altname;
+ STACK_OF(GENERAL_NAME) *altnames;
+
+ /*
+ * As per RFC6125 and RFC2818, we check subjectAltName extension,
+ * and if it's not present - commonName in Subject is checked.
+ */
+
+ altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+ if (altnames) {
+ n = sk_GENERAL_NAME_num(altnames);
+
+ for (i = 0; i < n; i++) {
+ altname = sk_GENERAL_NAME_value(altnames, i);
+
+ if (altname->type != GEN_DNS) {
+ continue;
+ }
+
+ str = altname->d.dNSName;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL subjectAltName: \"%*s\"",
+ ASN1_STRING_length(str), ASN1_STRING_data(str));
+
+ if (ngx_ssl_check_name(name, str) == NGX_OK) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL subjectAltName: match");
+ GENERAL_NAMES_free(altnames);
+ goto found;
+ }
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL subjectAltName: no match");
+
+ GENERAL_NAMES_free(altnames);
+ goto failed;
+ }
+
+ /*
+ * If there is no subjectAltName extension, check commonName
+ * in Subject. While RFC2818 requires to only check "most specific"
+ * CN, both Apache and OpenSSL check all CNs, and so do we.
+ */
+
+ sname = X509_get_subject_name(cert);
+
+ if (sname == NULL) {
+ goto failed;
+ }
+
+ i = -1;
+ for ( ;; ) {
+ i = X509_NAME_get_index_by_NID(sname, NID_commonName, i);
+
+ if (i < 0) {
+ break;
+ }
+
+ entry = X509_NAME_get_entry(sname, i);
+ str = X509_NAME_ENTRY_get_data(entry);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL commonName: \"%*s\"",
+ ASN1_STRING_length(str), ASN1_STRING_data(str));
+
+ if (ngx_ssl_check_name(name, str) == NGX_OK) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL commonName: match");
+ goto found;
+ }
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL commonName: no match");
+ }
+#endif
+
+failed:
+
+ X509_free(cert);
+ return NGX_ERROR;
+
+found:
+
+ X509_free(cert);
+ return NGX_OK;
+}
+
+
+#if (OPENSSL_VERSION_NUMBER < 0x10002002L || defined LIBRESSL_VERSION_NUMBER)
+
+static ngx_int_t
+ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *pattern)
+{
+ u_char *s, *p, *end;
+ size_t slen, plen;
+
+ s = name->data;
+ slen = name->len;
+
+ p = ASN1_STRING_data(pattern);
+ plen = ASN1_STRING_length(pattern);
+
+ if (slen == plen && ngx_strncasecmp(s, p, plen) == 0) {
+ return NGX_OK;
+ }
+
+ if (plen > 2 && p[0] == '*' && p[1] == '.') {
+ plen -= 1;
+ p += 1;
+
+ end = s + slen;
+ s = ngx_strlchr(s, end, '.');
+
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ slen = end - s;
+
+ if (plen == slen && ngx_strncasecmp(s, p, plen) == 0) {
+ return NGX_OK;
+ }
+ }
+
+ return NGX_ERROR;
+}
+
+#endif
+
+
+ngx_int_t
ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
s->data = (u_char *) SSL_get_version(c->ssl->connection);
return NGX_OK;
}
@@ -2600,23 +3110,31 @@
ngx_int_t
ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
- int len;
- u_char *buf;
- SSL_SESSION *sess;
+ u_char *buf;
+ SSL_SESSION *sess;
+ unsigned int len;
sess = SSL_get0_session(c->ssl->connection);
if (sess == NULL) {
s->len = 0;
return NGX_OK;
}
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+
+ buf = (u_char *) SSL_SESSION_get_id(sess, &len);
+
+#else
+
buf = sess->session_id;
len = sess->session_id_length;
+#endif
+
s->len = 2 * len;
s->data = ngx_pnalloc(pool, 2 * len);
if (s->data == NULL) {
return NGX_ERROR;
}
@@ -2640,10 +3158,32 @@
return NGX_OK;
}
ngx_int_t
+ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+ const char *servername;
+
+ servername = SSL_get_servername(c->ssl->connection,
+ TLSEXT_NAMETYPE_host_name);
+ if (servername) {
+ s->data = (u_char *) servername;
+ s->len = ngx_strlen(servername);
+ return NGX_OK;
+ }
+
+#endif
+
+ s->len = 0;
+ return NGX_OK;
+}
+
+
+ngx_int_t
ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
size_t len;
BIO *bio;
X509 *cert;
@@ -2857,10 +3397,44 @@
return NGX_OK;
}
ngx_int_t
+ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ X509 *cert;
+ unsigned int len;
+ u_char buf[EVP_MAX_MD_SIZE];
+
+ s->len = 0;
+
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+ if (cert == NULL) {
+ return NGX_OK;
+ }
+
+ if (!X509_digest(cert, EVP_sha1(), buf, &len)) {
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ s->len = 2 * len;
+ s->data = ngx_pnalloc(pool, 2 * len);
+ if (s->data == NULL) {
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ ngx_hex_dump(s->data, buf, len);
+
+ X509_free(cert);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
X509 *cert;
if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {
@@ -2904,10 +3478,12 @@
static char *
ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
+#ifndef OPENSSL_NO_ENGINE
+
ngx_openssl_conf_t *oscf = conf;
ENGINE *engine;
ngx_str_t *value;
@@ -2938,14 +3514,22 @@
}
ENGINE_free(engine);
return NGX_CONF_OK;
+
+#else
+
+ return "is not supported";
+
+#endif
}
static void
ngx_openssl_exit(ngx_cycle_t *cycle)
{
EVP_cleanup();
+#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
+#endif
}