diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index ff604c562..890b72542 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -9,6 +9,10 @@ #include #include +#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib) +#include +#endif + #define NGX_SSL_PASSWORD_BUFFER_SIZE 4096 @@ -19,6 +23,13 @@ typedef struct { static ngx_inline ngx_int_t ngx_ssl_cert_already_in_hash(void); +#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib) +static int ngx_ssl_cert_compression_callback(ngx_ssl_conn_t *ssl_conn, + CBB *out, const uint8_t *in, size_t in_len); +static void *ngx_ssl_cert_compression_alloc(void *opaque, u_int items, + u_int size); +static void ngx_ssl_cert_compression_free(void *opaque, void *address); +#endif 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); @@ -128,6 +139,7 @@ int ngx_ssl_ticket_keys_index; int ngx_ssl_ocsp_index; int ngx_ssl_index; int ngx_ssl_certificate_name_index; +int ngx_ssl_certificate_comp_index; u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE]; @@ -270,6 +282,14 @@ ngx_ssl_init(ngx_log_t *log) return NGX_ERROR; } + ngx_ssl_certificate_comp_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, + NULL, NULL); + if (ngx_ssl_certificate_comp_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, + "SSL_CTX_get_ex_new_index() failed"); + return NGX_ERROR; + } + return NGX_OK; } @@ -296,6 +316,14 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) return NGX_ERROR; } + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_comp_index, NULL) + == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_ex_data() failed"); + return NGX_ERROR; + } + ngx_rbtree_init(&ssl->staple_rbtree, &ssl->staple_sentinel, ngx_rbtree_insert_value); @@ -387,6 +415,11 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION); #endif +#ifdef SSL_OP_NO_TX_CERTIFICATE_COMPRESSION + SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION); + SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_RX_CERTIFICATE_COMPRESSION); +#endif + #ifdef SSL_OP_NO_ANTI_REPLAY SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_ANTI_REPLAY); #endif @@ -659,6 +692,163 @@ retry: } +ngx_int_t +ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_uint_t enable) +{ + if (!enable) { + return NGX_OK; + } + +#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION + + if (SSL_CTX_compress_certs(ssl->ctx, 0) != 0) { + SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION); + } + +#elif (NGX_ZLIB && defined TLSEXT_cert_compression_zlib) + + if (SSL_CTX_add_cert_compression_alg(ssl->ctx, TLSEXT_cert_compression_zlib, + ngx_ssl_cert_compression_callback, + NULL)) + { + return NGX_OK; + } + +#else + + ngx_log_error(NGX_LOG_WARN, ssl->log, 0, + "\"ssl_certificate_compression\" ignored, not supported"); + +#endif + + return NGX_OK; +} + + +#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib) + +static int +ngx_ssl_cert_compression_callback(ngx_ssl_conn_t *ssl_conn, CBB *out, + const uint8_t *in, size_t in_len) +{ + int rc; + u_char *p; + SSL_CTX *ssl_ctx; + z_stream zstream; + ngx_str_t *comp, tmp; + ngx_pool_t *pool; + ngx_connection_t *c; + + ssl_ctx = SSL_get_SSL_CTX(ssl_conn); + comp = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_certificate_comp_index); + + if (comp != NULL) { + return CBB_add_bytes(out, comp->data, comp->len); + } + + c = ngx_ssl_get_connection(ssl_conn); + + pool = ngx_create_pool(256, c->log); + if (pool == NULL) { + return 0; + } + + pool->log = c->log; + + ngx_memzero(&zstream, sizeof(z_stream)); + + zstream.zalloc = ngx_ssl_cert_compression_alloc; + zstream.zfree = ngx_ssl_cert_compression_free; + zstream.opaque = pool; + + rc = 0; + + tmp.len = compressBound(in_len); + tmp.data = ngx_palloc(pool, tmp.len); + if (tmp.data == NULL) { + goto done; + } + + zstream.next_in = (u_char *) in; + zstream.avail_in = in_len; + zstream.next_out = tmp.data; + zstream.avail_out = tmp.len; + + rc = deflateInit(&zstream, Z_DEFAULT_COMPRESSION); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflateInit() failed: %d", rc); + goto done; + } + + rc = deflate(&zstream, Z_FINISH); + + if (rc != Z_STREAM_END) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "deflate(Z_FINISH) failed: %d", rc); + goto done; + } + + tmp.len -= zstream.avail_out; + + rc = deflateEnd(&zstream); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflateEnd() failed: %d", rc); + goto done; + } + + p = ngx_alloc(sizeof(ngx_str_t) + tmp.len, c->log); + if (p == NULL) { + goto done; + } + + comp = (ngx_str_t *) p; + + comp->len = tmp.len; + comp->data = p + sizeof(ngx_str_t); + + ngx_memcpy(comp->data, tmp.data, tmp.len); + + SSL_CTX_set_ex_data(ssl_ctx, ngx_ssl_certificate_comp_index, comp); + + rc = CBB_add_bytes(out, comp->data, comp->len); + +done: + + ngx_destroy_pool(pool); + + return rc; +} + + +static void * +ngx_ssl_cert_compression_alloc(void *opaque, u_int items, u_int size) +{ + ngx_pool_t *pool = opaque; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pool->log, 0, + "cert compression alloc: n:%ud s:%ud", items, size); + + return ngx_palloc(pool, items * size); +} + + +static void +ngx_ssl_cert_compression_free(void *opaque, void *address) +{ +#if 0 + ngx_pool_t *pool = opaque; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0, + "cert compression free: %p", address); +#endif +} + +#endif + + ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, ngx_uint_t prefer_server_ciphers) @@ -4757,6 +4947,7 @@ ngx_ssl_cleanup_ctx(void *data) ngx_ssl_t *ssl = data; X509 *cert; + ngx_str_t *comp; ngx_uint_t i; for (i = 0; i < ssl->certs.nelts; i++) { @@ -4764,6 +4955,12 @@ ngx_ssl_cleanup_ctx(void *data) X509_free(cert); } + comp = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_comp_index); + + if (comp != NULL) { + ngx_free(comp); + } + SSL_CTX_free(ssl->ctx); } diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 0c9e9e840..e7ccd51e8 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -236,6 +236,8 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_int_t ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *cert, ngx_str_t *key, ngx_ssl_cache_t *cache, ngx_array_t *passwords); +ngx_int_t ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_uint_t enable); 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/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index dbfe5c08b..fbf4ab871 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -124,6 +124,13 @@ static ngx_command_t ngx_http_ssl_commands[] = { 0, NULL }, + { ngx_string("ssl_certificate_compression"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, certificate_compression), + NULL }, + { ngx_string("ssl_dhparam"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -621,6 +628,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) */ sscf->prefer_server_ciphers = NGX_CONF_UNSET; + sscf->certificate_compression = NGX_CONF_UNSET; sscf->early_data = NGX_CONF_UNSET; sscf->reject_handshake = NGX_CONF_UNSET; sscf->buffer_size = NGX_CONF_UNSET_SIZE; @@ -658,6 +666,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->prefer_server_ciphers, prev->prefer_server_ciphers, 0); + ngx_conf_merge_value(conf->certificate_compression, + prev->certificate_compression, 0); + ngx_conf_merge_value(conf->early_data, prev->early_data, 0); ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); @@ -792,6 +803,13 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) { return NGX_CONF_ERROR; } + + if (ngx_ssl_certificate_compression(cf, &conf->ssl, + conf->certificate_compression) + != NGX_OK) + { + return NGX_CONF_ERROR; + } } conf->ssl.buffer_size = conf->buffer_size; diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h index 8650fab93..9b26529fa 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -18,6 +18,7 @@ typedef struct { ngx_ssl_t ssl; ngx_flag_t prefer_server_ciphers; + ngx_flag_t certificate_compression; ngx_flag_t early_data; ngx_flag_t reject_handshake; diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c index 176e9c624..079d0e773 100644 --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -97,6 +97,13 @@ static ngx_command_t ngx_mail_ssl_commands[] = { 0, NULL }, + { ngx_string("ssl_certificate_compression"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, certificate_compression), + NULL }, + { ngx_string("ssl_dhparam"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -314,6 +321,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf) scf->passwords = NGX_CONF_UNSET_PTR; scf->conf_commands = NGX_CONF_UNSET_PTR; scf->prefer_server_ciphers = NGX_CONF_UNSET; + scf->certificate_compression = NGX_CONF_UNSET; scf->verify = NGX_CONF_UNSET_UINT; scf->verify_depth = NGX_CONF_UNSET_UINT; scf->builtin_session_cache = NGX_CONF_UNSET; @@ -343,6 +351,9 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->prefer_server_ciphers, prev->prefer_server_ciphers, 0); + ngx_conf_merge_value(conf->certificate_compression, + prev->certificate_compression, 0); + ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); @@ -446,6 +457,13 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } + if (ngx_ssl_certificate_compression(cf, &conf->ssl, + conf->certificate_compression) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + if (conf->verify) { if (conf->verify != 3 diff --git a/src/mail/ngx_mail_ssl_module.h b/src/mail/ngx_mail_ssl_module.h index c0eb6a38f..a0e9a173a 100644 --- a/src/mail/ngx_mail_ssl_module.h +++ b/src/mail/ngx_mail_ssl_module.h @@ -21,6 +21,7 @@ typedef struct { ngx_flag_t prefer_server_ciphers; + ngx_flag_t certificate_compression; ngx_ssl_t ssl; diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index 2f1b99624..7ce1175f1 100644 --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -133,6 +133,13 @@ static ngx_command_t ngx_stream_ssl_commands[] = { 0, NULL }, + { ngx_string("ssl_certificate_compression"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, certificate_compression), + NULL }, + { ngx_string("ssl_dhparam"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -881,6 +888,7 @@ ngx_stream_ssl_create_srv_conf(ngx_conf_t *cf) sscf->passwords = NGX_CONF_UNSET_PTR; sscf->conf_commands = NGX_CONF_UNSET_PTR; sscf->prefer_server_ciphers = NGX_CONF_UNSET; + sscf->certificate_compression = NGX_CONF_UNSET; sscf->reject_handshake = NGX_CONF_UNSET; sscf->verify = NGX_CONF_UNSET_UINT; sscf->verify_depth = NGX_CONF_UNSET_UINT; @@ -914,6 +922,9 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->prefer_server_ciphers, prev->prefer_server_ciphers, 0); + ngx_conf_merge_value(conf->certificate_compression, + prev->certificate_compression, 0); + ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, @@ -1039,6 +1050,13 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) { return NGX_CONF_ERROR; } + + if (ngx_ssl_certificate_compression(cf, &conf->ssl, + conf->certificate_compression) + != NGX_OK) + { + return NGX_CONF_ERROR; + } } if (conf->verify) { diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h index ffa03a6f3..31f138cfd 100644 --- a/src/stream/ngx_stream_ssl_module.h +++ b/src/stream/ngx_stream_ssl_module.h @@ -18,6 +18,7 @@ typedef struct { ngx_msec_t handshake_timeout; ngx_flag_t prefer_server_ciphers; + ngx_flag_t certificate_compression; ngx_flag_t reject_handshake; ngx_ssl_t ssl;