summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornginx <nginx@nginx.org>2012-10-02 13:57:46 +0000
committerJon Kolb <jon@b0g.us>2012-10-02 13:57:46 +0000
commitab4bd1d23e43e937e2b9f08ae34904f9d0d7c60b (patch)
treebd12699d3f01641b0ad8d1fc16f3566535ab7859
parentb4d1d3eec8e8bf9edf4feb5e3caf49fffffb5267 (diff)
downloadnginx-ab4bd1d23e43e937e2b9f08ae34904f9d0d7c60b.tar.gz
Changes with nginx 1.3.7 02 Oct 2012v1.3.7
*) Feature: OCSP stapling support. Thanks to Comodo, DigiCert and GlobalSign for sponsoring this work. *) Feature: the "ssl_trusted_certificate" directive. *) Feature: resolver now randomly rotates addresses returned from cache. Thanks to Anton Jouline. *) Bugfix: OpenSSL 0.9.7 compatibility.
-rw-r--r--CHANGES13
-rw-r--r--CHANGES.ru14
-rw-r--r--auto/lib/perl/conf10
-rw-r--r--auto/make6
-rw-r--r--auto/options4
-rw-r--r--auto/sources3
-rw-r--r--src/core/nginx.h4
-rw-r--r--src/core/ngx_core.h2
-rw-r--r--src/core/ngx_cycle.c18
-rw-r--r--src/core/ngx_cycle.h2
-rw-r--r--src/core/ngx_file.c12
-rw-r--r--src/core/ngx_file.h2
-rw-r--r--src/core/ngx_resolver.c27
-rw-r--r--src/event/ngx_event_openssl.c152
-rw-r--r--src/event/ngx_event_openssl.h9
-rw-r--r--src/event/ngx_event_openssl_stapling.c1749
-rw-r--r--src/http/modules/ngx_http_ssl_module.c110
-rw-r--r--src/http/modules/ngx_http_ssl_module.h6
-rw-r--r--src/http/modules/perl/nginx.pm2
-rw-r--r--src/os/unix/ngx_process_cycle.c14
20 files changed, 2110 insertions, 49 deletions
diff --git a/CHANGES b/CHANGES
index 8e47898c4..102b6679d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,17 @@
+Changes with nginx 1.3.7 02 Oct 2012
+
+ *) Feature: OCSP stapling support.
+ Thanks to Comodo, DigiCert and GlobalSign for sponsoring this work.
+
+ *) Feature: the "ssl_trusted_certificate" directive.
+
+ *) Feature: resolver now randomly rotates addresses returned from cache.
+ Thanks to Anton Jouline.
+
+ *) Bugfix: OpenSSL 0.9.7 compatibility.
+
+
Changes with nginx 1.3.6 12 Sep 2012
*) Feature: the ngx_http_gunzip_filter_module.
diff --git a/CHANGES.ru b/CHANGES.ru
index b1a3efc2a..54091682e 100644
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,18 @@
+Изменения в nginx 1.3.7 02.10.2012
+
+ *) Добавление: поддержка OCSP stapling.
+ Спасибо Comodo, DigiCert и GlobalSign за спонсирование разработки.
+
+ *) Добавление: директива ssl_trusted_certificate.
+
+ *) Добавление: теперь resolver случайным образом меняет порядок
+ возвращаемых закэшированных адресов.
+ Спасибо Антону Жулину.
+
+ *) Исправление: совместимость с OpenSSL 0.9.7.
+
+
Изменения в nginx 1.3.6 12.09.2012
*) Добавление: модуль ngx_http_gunzip_filter_module.
diff --git a/auto/lib/perl/conf b/auto/lib/perl/conf
index 52c7084e9..5ce6c91e6 100644
--- a/auto/lib/perl/conf
+++ b/auto/lib/perl/conf
@@ -12,7 +12,7 @@ NGX_PERL_VER=`$NGX_PERL -v 2>&1 | grep '^This is perl' 2>&1 \
if test -n "$NGX_PERL_VER"; then
echo " + perl version: $NGX_PERL_VER"
- if [ "`echo 'use 5.006001; print "OK"' | $NGX_PERL 2>&1`" != OK ]; then
+ if [ "`$NGX_PERL -e 'use 5.006001; print "OK"'`" != "OK" ]; then
echo
echo "$0: error: perl 5.6.1 or higher is required"
echo
@@ -20,6 +20,14 @@ if test -n "$NGX_PERL_VER"; then
exit 1;
fi
+ if [ "`$NGX_PERL -MExtUtils::Embed -e 'print "OK"'`" != "OK" ]; then
+ echo
+ echo "$0: error: perl module ExtUtils::Embed is required"
+ echo
+
+ exit 1;
+ fi
+
NGX_PERL_CFLAGS="$CFLAGS `$NGX_PERL -MExtUtils::Embed -e ccopts`"
NGX_PM_CFLAGS=`$NGX_PERL -MExtUtils::Embed -e ccopts`
diff --git a/auto/make b/auto/make
index e7f549040..05b74543c 100644
--- a/auto/make
+++ b/auto/make
@@ -49,7 +49,7 @@ END
ngx_all_srcs="$CORE_SRCS"
-# the core dependences and include pathes
+# the core dependences and include paths
ngx_deps=`echo $CORE_DEPS $NGX_AUTO_CONFIG_H $NGX_PCH \
| sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont\1/g" \
@@ -69,7 +69,7 @@ CORE_INCS = $ngx_include_opt$ngx_incs
END
-# the http dependences and include pathes
+# the http dependences and include paths
if [ $HTTP = YES ]; then
@@ -95,7 +95,7 @@ END
fi
-# the mail dependences and include pathes
+# the mail dependences and include paths
if [ $MAIL = YES ]; then
diff --git a/auto/options b/auto/options
index 191d6c807..a75bead54 100644
--- a/auto/options
+++ b/auto/options
@@ -388,6 +388,10 @@ cat << END
--without-http_browser_module disable ngx_http_browser_module
--without-http_upstream_ip_hash_module
disable ngx_http_upstream_ip_hash_module
+ --without-http_upstream_least_conn_module
+ disable ngx_http_upstream_least_conn_module
+ --without-http_upstream_keepalive_module
+ disable ngx_http_upstream_keepalive_module
--with-http_perl_module enable ngx_http_perl_module
--with-perl_modules_path=PATH set Perl modules path
diff --git a/auto/sources b/auto/sources
index 37677c737..cc19f8d51 100644
--- a/auto/sources
+++ b/auto/sources
@@ -77,7 +77,8 @@ REGEX_SRCS=src/core/ngx_regex.c
OPENSSL_MODULE=ngx_openssl_module
OPENSSL_DEPS=src/event/ngx_event_openssl.h
-OPENSSL_SRCS=src/event/ngx_event_openssl.c
+OPENSSL_SRCS="src/event/ngx_event_openssl.c \
+ src/event/ngx_event_openssl_stapling.c"
EVENT_MODULES="ngx_events_module ngx_event_core_module"
diff --git a/src/core/nginx.h b/src/core/nginx.h
index d09b5763b..c6dce45b0 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
#define _NGINX_H_INCLUDED_
-#define nginx_version 1003006
-#define NGINX_VERSION "1.3.6"
+#define nginx_version 1003007
+#define NGINX_VERSION "1.3.7"
#define NGINX_VER "nginx/" NGINX_VERSION
#define NGINX_VAR "NGINX"
diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h
index 435ce64e7..bccc60341 100644
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -69,12 +69,12 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
#include <ngx_slab.h>
#include <ngx_inet.h>
#include <ngx_cycle.h>
+#include <ngx_resolver.h>
#if (NGX_OPENSSL)
#include <ngx_event_openssl.h>
#endif
#include <ngx_process_cycle.h>
#include <ngx_conf_file.h>
-#include <ngx_resolver.h>
#include <ngx_open_file_cache.h>
#include <ngx_os.h>
#include <ngx_connection.h>
diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c
index e5468ae9b..f15372990 100644
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -118,18 +118,18 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
}
- n = old_cycle->pathes.nelts ? old_cycle->pathes.nelts : 10;
+ n = old_cycle->paths.nelts ? old_cycle->paths.nelts : 10;
- cycle->pathes.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *));
- if (cycle->pathes.elts == NULL) {
+ cycle->paths.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *));
+ if (cycle->paths.elts == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
- cycle->pathes.nelts = 0;
- cycle->pathes.size = sizeof(ngx_path_t *);
- cycle->pathes.nalloc = n;
- cycle->pathes.pool = pool;
+ cycle->paths.nelts = 0;
+ cycle->paths.size = sizeof(ngx_path_t *);
+ cycle->paths.nalloc = n;
+ cycle->paths.pool = pool;
if (old_cycle->open_files.part.nelts) {
@@ -334,7 +334,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
}
- if (ngx_create_pathes(cycle, ccf->user) != NGX_OK) {
+ if (ngx_create_paths(cycle, ccf->user) != NGX_OK) {
goto failed;
}
@@ -1038,6 +1038,8 @@ ngx_signal_process(ngx_cycle_t *cycle, char *sig)
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+ ngx_memzero(&file, sizeof(ngx_file_t));
+
file.name = ccf->pid;
file.log = cycle->log;
diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h
index 551b6288e..b55fee0f1 100644
--- a/src/core/ngx_cycle.h
+++ b/src/core/ngx_cycle.h
@@ -48,7 +48,7 @@ struct ngx_cycle_s {
ngx_queue_t reusable_connections_queue;
ngx_array_t listening;
- ngx_array_t pathes;
+ ngx_array_t paths;
ngx_list_t open_files;
ngx_list_t shared_memory;
diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c
index d9b30f844..f13fb4952 100644
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -412,8 +412,8 @@ ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot)
path = *slot;
- p = cf->cycle->pathes.elts;
- for (i = 0; i < cf->cycle->pathes.nelts; i++) {
+ p = cf->cycle->paths.elts;
+ for (i = 0; i < cf->cycle->paths.nelts; i++) {
if (p[i]->name.len == path->name.len
&& ngx_strcmp(p[i]->name.data, path->name.data) == 0)
{
@@ -457,7 +457,7 @@ ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot)
}
}
- p = ngx_array_push(&cf->cycle->pathes);
+ p = ngx_array_push(&cf->cycle->paths);
if (p == NULL) {
return NGX_ERROR;
}
@@ -469,14 +469,14 @@ ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot)
ngx_int_t
-ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user)
+ngx_create_paths(ngx_cycle_t *cycle, ngx_uid_t user)
{
ngx_err_t err;
ngx_uint_t i;
ngx_path_t **path;
- path = cycle->pathes.elts;
- for (i = 0; i < cycle->pathes.nelts; i++) {
+ path = cycle->paths.elts;
+ for (i = 0; i < cycle->paths.nelts; i++) {
if (ngx_create_dir(path[i]->name.data, 0700) == NGX_FILE_ERROR) {
err = ngx_errno;
diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h
index 7023e6778..18abf1423 100644
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -130,7 +130,7 @@ void ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len);
ngx_int_t ngx_create_path(ngx_file_t *file, ngx_path_t *path);
ngx_err_t ngx_create_full_path(u_char *dir, ngx_uint_t access);
ngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot);
-ngx_int_t ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user);
+ngx_int_t ngx_create_paths(ngx_cycle_t *cycle, ngx_uid_t user);
ngx_int_t ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to,
ngx_ext_rename_file_t *ext);
ngx_int_t ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf);
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
index 178e0831d..dcdbe0cc8 100644
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -88,6 +88,8 @@ static void *ngx_resolver_calloc(ngx_resolver_t *r, size_t size);
static void ngx_resolver_free(ngx_resolver_t *r, void *p);
static void ngx_resolver_free_locked(ngx_resolver_t *r, void *p);
static void *ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size);
+static in_addr_t *ngx_resolver_rotate(ngx_resolver_t *r, in_addr_t *src,
+ ngx_uint_t n);
static u_char *ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len);
@@ -445,8 +447,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
if (naddrs != 1) {
addr = 0;
- addrs = ngx_resolver_dup(r, rn->u.addrs,
- naddrs * sizeof(in_addr_t));
+ addrs = ngx_resolver_rotate(r, rn->u.addrs, naddrs);
if (addrs == NULL) {
return NGX_ERROR;
}
@@ -2135,6 +2136,28 @@ ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size)
}
+static in_addr_t *
+ngx_resolver_rotate(ngx_resolver_t *r, in_addr_t *src, ngx_uint_t n)
+{
+ void *dst, *p;
+ ngx_uint_t j;
+
+ dst = ngx_resolver_alloc(r, n * sizeof(in_addr_t));
+
+ j = ngx_random() % n;
+
+ if (j == 0) {
+ ngx_memcpy(dst, src, n * sizeof(in_addr_t));
+ return dst;
+ }
+
+ p = ngx_cpymem(dst, &src[j], (n - j) * sizeof(in_addr_t));
+ ngx_memcpy(p, src, j * sizeof(in_addr_t));
+
+ return dst;
+}
+
+
char *
ngx_resolver_strerror(ngx_int_t err)
{
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 4356a05ef..81da708f3 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -82,6 +82,8 @@ ngx_module_t ngx_openssl_module = {
int ngx_ssl_connection_index;
int ngx_ssl_server_conf_index;
int ngx_ssl_session_cache_index;
+int ngx_ssl_certificate_index;
+int ngx_ssl_stapling_index;
ngx_int_t
@@ -94,23 +96,25 @@ ngx_ssl_init(ngx_log_t *log)
OpenSSL_add_all_algorithms();
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
#ifndef SSL_OP_NO_COMPRESSION
{
/*
* Disable gzip compression in OpenSSL prior to 1.0.0 version,
* this saves about 522K per connection.
*/
- int i, n;
+ int n;
STACK_OF(SSL_COMP) *ssl_comp_methods;
ssl_comp_methods = SSL_COMP_get_compression_methods();
n = sk_SSL_COMP_num(ssl_comp_methods);
- for (i = 0; i < n; i++) {
- (void) sk_SSL_COMP_delete(ssl_comp_methods, i);
+ while (n--) {
+ (void) sk_SSL_COMP_pop(ssl_comp_methods);
}
}
#endif
+#endif
ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
@@ -135,6 +139,22 @@ ngx_ssl_init(ngx_log_t *log)
return NGX_ERROR;
}
+ ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ if (ngx_ssl_certificate_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+ "SSL_CTX_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_ssl_stapling_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ if (ngx_ssl_stapling_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+ "SSL_CTX_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
return NGX_OK;
}
@@ -216,19 +236,89 @@ ngx_int_t
ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_str_t *key)
{
+ BIO *bio;
+ X509 *x509;
+ u_long n;
+
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
return NGX_ERROR;
}
- if (SSL_CTX_use_certificate_chain_file(ssl->ctx, (char *) cert->data)
+ /*
+ * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
+ * allow to access certificate later from SSL_CTX, so we reimplement
+ * it here
+ */
+
+ bio = BIO_new_file((char *) cert->data, "r");
+ if (bio == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "BIO_new_file(\"%s\") failed", cert->data);
+ return NGX_ERROR;
+ }
+
+ x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "PEM_read_bio_X509_AUX(\"%s\") failed", cert->data);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_use_certificate(\"%s\") failed", cert->data);
+ X509_free(x509);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
- "SSL_CTX_use_certificate_chain_file(\"%s\") failed",
- cert->data);
+ "SSL_CTX_set_ex_data() failed");
return NGX_ERROR;
}
+ X509_free(x509);
+
+ /* read rest of the chain */
+
+ for ( ;; ) {
+
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ n = ERR_peek_last_error();
+
+ if (ERR_GET_LIB(n) == ERR_LIB_PEM
+ && ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
+ {
+ /* end of file */
+ ERR_clear_error();
+ break;
+ }
+
+ /* some real error */
+
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "PEM_read_bio_X509(\"%s\") failed", cert->data);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
+ cert->data);
+ X509_free(x509);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+ }
+
+ BIO_free(bio);
+
if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {
return NGX_ERROR;
}
@@ -295,6 +385,33 @@ ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_int_t
+ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
+ ngx_int_t depth)
+{
+ SSL_CTX_set_verify_depth(ssl->ctx, depth);
+
+ if (cert->len == 0) {
+ return NGX_OK;
+ }
+
+ if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_load_verify_locations(\"%s\") failed",
+ cert->data);
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
{
X509_STORE *store;
@@ -1473,10 +1590,12 @@ ngx_ssl_clear_error(ngx_log_t *log)
void ngx_cdecl
ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
{
- u_long n;
- va_list args;
- u_char *p, *last;
- u_char errstr[NGX_MAX_CONF_ERRSTR];
+ int flags;
+ u_long n;
+ va_list args;
+ u_char *p, *last;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+ const char *data;
last = errstr + NGX_MAX_CONF_ERRSTR;
@@ -1488,14 +1607,14 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
for ( ;; ) {
- n = ERR_get_error();
+ n = ERR_peek_error_line_data(NULL, NULL, &data, &flags);
if (n == 0) {
break;
}
if (p >= last) {
- continue;
+ goto next;
}
*p++ = ' ';
@@ -1505,6 +1624,15 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
while (p < last && *p) {
p++;
}
+
+ if (p < last && *data && (flags & ERR_TXT_STRING)) {
+ *p++ = ':';
+ p = ngx_cpystrn(p, (u_char *) data, last - p);
+ }
+
+ next:
+
+ (void) ERR_get_error();
}
ngx_log_error(level, log, err, "%s)", errstr);
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index cd6d88518..d1fb5739f 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -17,6 +17,7 @@
#include <openssl/conf.h>
#include <openssl/engine.h>
#include <openssl/evp.h>
+#include <openssl/ocsp.h>
#define NGX_SSL_NAME "OpenSSL"
@@ -101,7 +102,13 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_str_t *key);
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,
+ ngx_str_t *cert, ngx_int_t depth);
ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl);
+ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);
+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(SSL *ssl, int is_export, int key_length);
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);
@@ -157,6 +164,8 @@ void ngx_ssl_cleanup_ctx(void *data);
extern int ngx_ssl_connection_index;
extern int ngx_ssl_server_conf_index;
extern int ngx_ssl_session_cache_index;
+extern int ngx_ssl_certificate_index;
+extern int ngx_ssl_stapling_index;
#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */
diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
new file mode 100644
index 000000000..aaa8d8ac4
--- /dev/null
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -0,0 +1,1749 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+
+
+#ifdef SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB
+
+
+typedef struct {
+ ngx_str_t staple;
+ ngx_msec_t timeout;
+
+ ngx_resolver_t *resolver;
+ ngx_msec_t resolver_timeout;
+
+ ngx_addr_t *addrs;
+ ngx_str_t host;
+ ngx_str_t uri;
+ in_port_t port;
+
+ SSL_CTX *ssl_ctx;
+
+ X509 *cert;
+ X509 *issuer;
+
+ time_t valid;
+
+ unsigned verify:1;
+ unsigned loading:1;
+} ngx_ssl_stapling_t;
+
+
+typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t;
+
+struct ngx_ssl_ocsp_ctx_s {
+ X509 *cert;
+ X509 *issuer;
+
+ ngx_uint_t naddrs;
+
+ ngx_addr_t *addrs;
+ ngx_str_t host;
+ ngx_str_t uri;
+ in_port_t port;
+
+ ngx_resolver_t *resolver;
+ ngx_msec_t resolver_timeout;
+
+ ngx_msec_t timeout;
+
+ void (*handler)(ngx_ssl_ocsp_ctx_t *r);
+ void *data;
+
+ ngx_buf_t *request;
+ ngx_buf_t *response;
+ ngx_peer_connection_t peer;
+
+ ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *r);
+
+ ngx_uint_t state;
+
+ ngx_uint_t code;
+ ngx_uint_t count;
+
+ ngx_uint_t done;
+
+ u_char *header_name_start;
+ u_char *header_name_end;
+ u_char *header_start;
+ u_char *header_end;
+
+ ngx_pool_t *pool;
+ ngx_log_t *log;
+};
+
+
+static ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *file);
+static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl);
+static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *responder);
+
+static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn,
+ void *data);
+static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple);
+static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);
+
+static void ngx_ssl_stapling_cleanup(void *data);
+
+static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void);
+static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx);
+static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx);
+static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve);
+static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx);
+static void ngx_ssl_ocsp_write_handler(ngx_event_t *wev);
+static void ngx_ssl_ocsp_read_handler(ngx_event_t *rev);
+static void ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev);
+
+static ngx_int_t ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx);
+
+static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len);
+
+
+ngx_int_t
+ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
+ ngx_str_t *responder, ngx_uint_t verify)
+{
+ ngx_int_t rc;
+ ngx_pool_cleanup_t *cln;
+ ngx_ssl_stapling_t *staple;
+
+ staple = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_stapling_t));
+ if (staple == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_ssl_stapling_cleanup;
+ cln->data = staple;
+
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_stapling_index, staple)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_set_ex_data() failed");
+ return NGX_ERROR;
+ }
+
+ staple->ssl_ctx = ssl->ctx;
+ staple->timeout = 60000;
+ staple->verify = verify;
+
+ if (file->len) {
+ /* use OCSP response from the file */
+
+ if (ngx_ssl_stapling_file(cf, ssl, file) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ goto done;
+ }
+
+ rc = ngx_ssl_stapling_issuer(cf, ssl);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ rc = ngx_ssl_stapling_responder(cf, ssl, responder);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+done:
+
+ SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);
+ SSL_CTX_set_tlsext_status_arg(ssl->ctx, staple);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
+{
+ BIO *bio;
+ int len;
+ u_char *p, *buf;
+ OCSP_RESPONSE *response;
+ ngx_ssl_stapling_t *staple;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+
+ if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ bio = BIO_new_file((char *) file->data, "r");
+ if (bio == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "BIO_new_file(\"%s\") failed", file->data);
+ return NGX_ERROR;
+ }
+
+ response = d2i_OCSP_RESPONSE_bio(bio, NULL);
+ if (response == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "d2i_OCSP_RESPONSE_bio(\"%s\") failed", file->data);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ len = i2d_OCSP_RESPONSE(response, NULL);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "i2d_OCSP_RESPONSE(\"%s\") failed", file->data);
+ goto failed;
+ }
+
+ buf = ngx_alloc(len, ssl->log);
+ if (buf == NULL) {
+ goto failed;
+ }
+
+ p = buf;
+ len = i2d_OCSP_RESPONSE(response, &p);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "i2d_OCSP_RESPONSE(\"%s\") failed", file->data);
+ ngx_free(buf);
+ goto failed;
+ }
+
+ OCSP_RESPONSE_free(response);
+ BIO_free(bio);
+
+ staple->staple.data = buf;
+ staple->staple.len = len;
+
+ return NGX_OK;
+
+failed:
+
+ OCSP_RESPONSE_free(response);
+ BIO_free(bio);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl)
+{
+ int i, n, rc;
+ X509 *cert, *issuer;
+ X509_STORE *store;
+ X509_STORE_CTX *store_ctx;
+ STACK_OF(X509) *chain;
+ ngx_ssl_stapling_t *staple;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+ cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain);
+#else
+ chain = ssl->ctx->extra_certs;
+#endif
+
+ n = sk_X509_num(chain);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
+ "SSL get issuer: %d extra certs", n);
+
+ for (i = 0; i < n; i++) {
+ issuer = sk_X509_value(chain, i);
+ if (X509_check_issued(issuer, cert) == X509_V_OK) {
+ CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
+ "SSL get issuer: found %p in extra certs", issuer);
+
+ staple->cert = cert;
+ staple->issuer = issuer;
+
+ return NGX_OK;
+ }
+ }
+
+ store = SSL_CTX_get_cert_store(ssl->ctx);
+ if (store == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_get_cert_store() failed");
+ return NGX_ERROR;
+ }
+
+ store_ctx = X509_STORE_CTX_new();
+ if (store_ctx == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_STORE_CTX_new() failed");
+ return NGX_ERROR;
+ }
+
+ if (X509_STORE_CTX_init(store_ctx, store, NULL, NULL) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_STORE_CTX_init() failed");
+ return NGX_ERROR;
+ }
+
+ rc = X509_STORE_CTX_get1_issuer(&issuer, store_ctx, cert);
+
+ if (rc == -1) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_STORE_CTX_get1_issuer() failed");
+ X509_STORE_CTX_free(store_ctx);
+ return NGX_ERROR;
+ }
+
+ if (rc == 0) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, issuer certificate not found");
+ X509_STORE_CTX_free(store_ctx);
+ return NGX_DECLINED;
+ }
+
+ X509_STORE_CTX_free(store_ctx);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
+ "SSL get issuer: found %p in cert store", issuer);
+
+ staple->cert = cert;
+ staple->issuer = issuer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder)
+{
+ ngx_url_t u;
+ char *s;
+ ngx_ssl_stapling_t *staple;
+ STACK_OF(OPENSSL_STRING) *aia;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+
+ if (responder->len == 0) {
+
+ /* extract OCSP responder URL from certificate */
+
+ aia = X509_get1_ocsp(staple->cert);
+ if (aia == NULL) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "no OCSP responder URL in the certificate");
+ return NGX_DECLINED;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ s = sk_OPENSSL_STRING_value(aia, 0);
+#else
+ s = sk_value(aia, 0);
+#endif
+ if (s == NULL) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "no OCSP responder URL in the certificate");
+ X509_email_free(aia);
+ return NGX_DECLINED;
+ }
+
+ responder->len = ngx_strlen(s);
+ responder->data = ngx_palloc(cf->pool, responder->len);
+ if (responder->data == NULL) {
+ X509_email_free(aia);
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(responder->data, s, responder->len);
+ X509_email_free(aia);
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = *responder;
+ u.default_port = 80;
+ u.uri_part = 1;
+
+ if (u.url.len > 7
+ && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)
+ {
+ u.url.len -= 7;
+ u.url.data += 7;
+
+ } else {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "invalid URL prefix in OCSP responder \"%V\"", &u.url);
+ return NGX_DECLINED;
+ }
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "%s in OCSP responder \"%V\"", u.err, &u.url);
+ return NGX_DECLINED;
+ }
+
+ return NGX_ERROR;
+ }
+
+ staple->addrs = u.addrs;
+ staple->host = u.host;
+ staple->uri = u.uri;
+ staple->port = u.port;
+
+ if (staple->uri.len == 0) {
+ ngx_str_set(&staple->uri, "/");
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
+{
+ ngx_ssl_stapling_t *staple;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+
+ staple->resolver = resolver;
+ staple->resolver_timeout = resolver_timeout;
+
+ return NGX_OK;
+}
+
+
+static int
+ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
+{
+ int rc;
+ u_char *p;
+ ngx_connection_t *c;
+ ngx_ssl_stapling_t *staple;
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL certificate status callback");
+
+ staple = data;
+ rc = SSL_TLSEXT_ERR_NOACK;
+
+ if (staple->staple.len) {
+ /* we have to copy ocsp response as OpenSSL will free it by itself */
+
+ p = OPENSSL_malloc(staple->staple.len);
+ if (p == NULL) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "OPENSSL_malloc() failed");
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ ngx_memcpy(p, staple->staple.data, staple->staple.len);
+
+ SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, staple->staple.len);
+
+ rc = SSL_TLSEXT_ERR_OK;
+ }
+
+ ngx_ssl_stapling_update(staple);
+
+ return rc;
+}
+
+
+static void
+ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple)
+{
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ if (staple->host.len == 0
+ || staple->loading || staple->valid >= ngx_time())
+ {
+ return;
+ }
+
+ staple->loading = 1;
+
+ ctx = ngx_ssl_ocsp_start();
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->cert = staple->cert;
+ ctx->issuer = staple->issuer;
+
+ ctx->addrs = staple->addrs;
+ ctx->host = staple->host;
+ ctx->uri = staple->uri;
+ ctx->port = staple->port;
+ ctx->timeout = staple->timeout;
+
+ ctx->resolver = staple->resolver;
+ ctx->resolver_timeout = staple->resolver_timeout;
+
+ ctx->handler = ngx_ssl_stapling_ocsp_handler;
+ ctx->data = staple;
+
+ ngx_ssl_ocsp_request(ctx);
+
+ return;
+}
+
+
+static void
+ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
+ const
+#endif
+ u_char *p;
+ int n;
+ size_t len;
+ ngx_str_t response;
+ X509_STORE *store;
+ STACK_OF(X509) *chain;
+ OCSP_CERTID *id;
+ OCSP_RESPONSE *ocsp;
+ OCSP_BASICRESP *basic;
+ ngx_ssl_stapling_t *staple;
+ ASN1_GENERALIZEDTIME *thisupdate, *nextupdate;
+
+ staple = ctx->data;
+ ocsp = NULL;
+ basic = NULL;
+ id = NULL;
+
+ if (ctx->code != 200) {
+ goto error;
+ }
+
+ /* check the response */
+
+ len = ctx->response->last - ctx->response->pos;
+ p = ctx->response->pos;
+
+ ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+ if (ocsp == NULL) {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "d2i_OCSP_RESPONSE() failed");
+ goto error;
+ }
+
+ n = OCSP_response_status(ocsp);
+
+ if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP response not successful (%d: %s)",
+ n, OCSP_response_status_str(n));
+ goto error;
+ }
+
+ basic = OCSP_response_get1_basic(ocsp);
+ if (basic == NULL) {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP_response_get1_basic() failed");
+ goto error;
+ }
+
+ store = SSL_CTX_get_cert_store(staple->ssl_ctx);
+ if (store == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "SSL_CTX_get_cert_store() failed");
+ goto error;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain);
+#else
+ chain = staple->ssl_ctx->extra_certs;
+#endif
+
+ if (OCSP_basic_verify(basic, chain, store,
+ staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY)
+ != 1)
+ {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP_basic_verify() failed");
+ goto error;
+ }
+
+ id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
+ if (id == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_cert_to_id() failed");
+ goto error;
+ }
+
+ if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,
+ &thisupdate, &nextupdate)
+ != 1)
+ {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "certificate status not found in the OCSP response",
+ n, OCSP_response_status_str(n));
+ goto error;
+ }
+
+ if (n != V_OCSP_CERTSTATUS_GOOD) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "certificate status \"%s\" in the OCSP response",
+ n, OCSP_cert_status_str(n));
+ goto error;
+ }
+
+ if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP_check_validity() failed");
+ goto error;
+ }
+
+ OCSP_CERTID_free(id);
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(ocsp);
+
+ /* copy the response to memory not in ctx->pool */
+
+ response.len = len;
+ response.data = ngx_alloc(response.len, ctx->log);
+
+ if (response.data == NULL) {
+ goto done;
+ }
+
+ ngx_memcpy(response.data, ctx->response->pos, response.len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp response, %s, %uz",
+ OCSP_cert_status_str(n), response.len);
+
+ if (staple->staple.data) {
+ ngx_free(staple->staple.data);
+ }
+
+ staple->staple = response;
+
+done:
+
+ staple->loading = 0;
+ staple->valid = ngx_time() + 3600; /* ssl_stapling_valid */
+
+ ngx_ssl_ocsp_done(ctx);
+ return;
+
+error:
+
+ staple->loading = 0;
+ staple->valid = ngx_time() + 300; /* ssl_stapling_err_valid */
+
+ if (id) {
+ OCSP_CERTID_free(id);
+ }
+
+ if (basic) {
+ OCSP_BASICRESP_free(basic);
+ }
+
+ if (ocsp) {
+ OCSP_RESPONSE_free(ocsp);
+ }
+
+ ngx_ssl_ocsp_done(ctx);
+}
+
+
+static void
+ngx_ssl_stapling_cleanup(void *data)
+{
+ ngx_ssl_stapling_t *staple = data;
+
+ if (staple->issuer) {
+ X509_free(staple->issuer);
+ }
+
+ if (staple->staple.data) {
+ ngx_free(staple->staple.data);
+ }
+}
+
+
+static ngx_ssl_ocsp_ctx_t *
+ngx_ssl_ocsp_start(void)
+{
+ ngx_log_t *log;
+ ngx_pool_t *pool;
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ pool = ngx_create_pool(2048, ngx_cycle->log);
+ if (pool == NULL) {
+ return NULL;
+ }
+
+ ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t));
+ if (ctx == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ log = ngx_palloc(pool, sizeof(ngx_log_t));
+ if (log == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ ctx->pool = pool;
+
+ *log = *ctx->pool->log;
+
+ ctx->pool->log = log;
+ ctx->log = log;
+
+ log->handler = ngx_ssl_ocsp_log_error;
+ log->data = ctx;
+ log->action = "requesting certificate status";
+
+ return ctx;
+}
+
+
+static void
+ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp done");
+
+ if (ctx->peer.connection) {
+ ngx_close_connection(ctx->peer.connection);
+ }
+
+ ngx_destroy_pool(ctx->pool);
+}
+
+
+static void
+ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp error");
+
+ ctx->code = 0;
+ ctx->handler(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_resolver_ctx_t *resolve, temp;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp request");
+
+ if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (ctx->resolver) {
+ /* resolve OCSP responder hostname */
+
+ temp.name = ctx->host;
+
+ resolve = ngx_resolve_start(ctx->resolver, &temp);
+ if (resolve == NULL) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (resolve == NGX_NO_RESOLVER) {
+ ngx_log_error(NGX_LOG_WARN, ctx->log, 0,
+ "no resolver defined to resolve %V", &ctx->host);
+ goto connect;
+ }
+
+ resolve->name = ctx->host;
+ resolve->type = NGX_RESOLVE_A;
+ resolve->handler = ngx_ssl_ocsp_resolve_handler;
+ resolve->data = ctx;
+ resolve->timeout = ctx->resolver_timeout;
+
+ if (ngx_resolve_name(resolve) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ return;
+ }
+
+connect:
+
+ ngx_ssl_ocsp_connect(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve)
+{
+ ngx_ssl_ocsp_ctx_t *ctx = resolve->data;
+
+ u_char *p;
+ size_t len;
+ in_port_t port;
+ ngx_uint_t i;
+ struct sockaddr_in *sin;
+
+ ngx_log_debug0(NGX_LOG_ALERT, ctx->log, 0,
+ "ssl ocsp resolve handler");
+
+ if (resolve->state) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "%V could not be resolved (%i: %s)",
+ &resolve->name, resolve->state,
+ ngx_resolver_strerror(resolve->state));
+ goto failed;
+ }
+
+#if (NGX_DEBUG)
+ {
+ in_addr_t addr;
+
+ for (i = 0; i < resolve->naddrs; i++) {
+ addr = ntohl(resolve->addrs[i]);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "name was resolved to %ud.%ud.%ud.%ud",
+ (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff, addr & 0xff);
+ }
+ }
+#endif
+
+ ctx->naddrs = resolve->naddrs;
+ ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t));
+
+ if (ctx->addrs == NULL) {
+ goto failed;
+ }
+
+ port = htons(ctx->port);
+
+ for (i = 0; i < resolve->naddrs; i++) {
+
+ sin = ngx_pcalloc(ctx->pool, sizeof(struct sockaddr_in));
+ if (sin == NULL) {
+ goto failed;
+ }
+
+ sin->sin_family = AF_INET;
+ sin->sin_port = port;
+ sin->sin_addr.s_addr = resolve->addrs[i];
+
+ ctx->addrs[i].sockaddr = (struct sockaddr *) sin;
+ ctx->addrs[i].socklen = sizeof(struct sockaddr_in);
+
+ len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
+
+ p = ngx_pnalloc(ctx->pool, len);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1);
+
+ ctx->addrs[i].name.len = len;
+ ctx->addrs[i].name.data = p;
+ }
+
+ ngx_resolve_name_done(resolve);
+
+ ngx_ssl_ocsp_connect(ctx);
+ return;
+
+failed:
+
+ ngx_resolve_name_done(resolve);
+ ngx_ssl_ocsp_error(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_int_t rc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp connect");
+
+ /* TODO: use all ip addresses */
+
+ ctx->peer.sockaddr = ctx->addrs[0].sockaddr;
+ ctx->peer.socklen = ctx->addrs[0].socklen;
+ ctx->peer.name = &ctx->addrs[0].name;
+ ctx->peer.get = ngx_event_get_peer;
+ ctx->peer.log = ctx->log;
+ ctx->peer.log_error = NGX_ERROR_ERR;
+
+ rc = ngx_event_connect_peer(&ctx->peer);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp connect peer done");
+
+ if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ ctx->peer.connection->data = ctx;
+ ctx->peer.connection->pool = ctx->pool;
+
+ ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler;
+ ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler;
+
+ ctx->process = ngx_ssl_ocsp_process_status_line;
+
+ ngx_add_timer(ctx->peer.connection->read, ctx->timeout);
+ ngx_add_timer(ctx->peer.connection->write, ctx->timeout);
+
+ if (rc == NGX_OK) {
+ ngx_ssl_ocsp_write_handler(ctx->peer.connection->write);
+ return;
+ }
+}
+
+
+static void
+ngx_ssl_ocsp_write_handler(ngx_event_t *wev)
+{
+ ssize_t n, size;
+ ngx_connection_t *c;
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ c = wev->data;
+ ctx = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0,
+ "ssl ocsp write handler");
+
+ if (wev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
+ "OCSP responder timed out");
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ size = ctx->request->last - ctx->request->pos;
+
+ n = ngx_send(c, ctx->request->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (n > 0) {
+ ctx->request->pos += n;
+
+ if (n == size) {
+ wev->handler = ngx_ssl_ocsp_dummy_handler;
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ }
+
+ return;
+ }
+ }
+
+ if (!wev->timer_set) {
+ ngx_add_timer(wev, ctx->timeout);
+ }
+}
+
+
+static void
+ngx_ssl_ocsp_read_handler(ngx_event_t *rev)
+{
+ ssize_t n, size;
+ ngx_int_t rc;
+ ngx_ssl_ocsp_ctx_t *ctx;
+ ngx_connection_t *c;
+
+ c = rev->data;
+ ctx = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0,
+ "ssl ocsp read handler");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
+ "OCSP responder timed out");
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (ctx->response == NULL) {
+ ctx->response = ngx_create_temp_buf(ctx->pool, 16384);
+ if (ctx->response == NULL) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+ }
+
+ for ( ;; ) {
+
+ size = ctx->response->end - ctx->response->last;
+
+ n = ngx_recv(c, ctx->response->last, size);
+
+ if (n > 0) {
+ ctx->response->last += n;
+
+ rc = ctx->process(ctx);
+
+ if (rc == NGX_ERROR) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ continue;
+ }
+
+ if (n == NGX_AGAIN) {
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ }
+
+ return;
+ }
+
+ break;
+ }
+
+ ctx->done = 1;
+
+ rc = ctx->process(ctx);
+
+ if (rc == NGX_DONE) {
+ /* ctx->handler() was called */
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder prematurely closed connection");
+
+ ngx_ssl_ocsp_error(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "ssl ocsp dummy handler");
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ int len;
+ u_char *p;
+ uintptr_t escape;
+ ngx_str_t binary, base64;
+ ngx_buf_t *b;
+ OCSP_CERTID *id;
+ OCSP_REQUEST *ocsp;
+
+ ocsp = OCSP_REQUEST_new();
+ if (ocsp == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_REQUEST_new() failed");
+ return NGX_ERROR;
+ }
+
+ id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
+ if (id == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_cert_to_id() failed");
+ goto failed;
+ }
+
+ if (OCSP_request_add0_id(ocsp, id) == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_request_add0_id() failed");
+ goto failed;
+ }
+
+ len = i2d_OCSP_REQUEST(ocsp, NULL);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "i2d_OCSP_REQUEST() failed");
+ goto failed;
+ }
+
+ binary.len = len;
+ binary.data = ngx_palloc(ctx->pool, len);
+ if (binary.data == NULL) {
+ goto failed;
+ }
+
+ p = binary.data;
+ len = i2d_OCSP_REQUEST(ocsp, &p);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ctx->log, 0,
+ "i2d_OCSP_REQUEST() failed");
+ goto failed;
+ }
+
+ base64.len = ngx_base64_encoded_length(binary.len);
+ base64.data = ngx_palloc(ctx->pool, base64.len);
+ if (base64.data == NULL) {
+ goto failed;
+ }
+
+ ngx_encode_base64(&base64, &binary);
+
+ escape = ngx_escape_uri(NULL, base64.data, base64.len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp request length %z, escape %d",
+ base64.len, escape);
+
+ len = sizeof("GET ") - 1 + ctx->uri.len + sizeof("/") - 1
+ + base64.len + 2 * escape + sizeof(" HTTP/1.0" CRLF) - 1
+ + sizeof("Host: ") - 1 + ctx->host.len + sizeof(CRLF) - 1
+ + sizeof(CRLF) - 1;
+
+ b = ngx_create_temp_buf(ctx->pool, len);
+ if (b == NULL) {
+ goto failed;
+ }
+
+ p = b->last;
+
+ p = ngx_cpymem(p, "GET ", sizeof("GET ") - 1);
+ p = ngx_cpymem(p, ctx->uri.data, ctx->uri.len);
+
+ if (ctx->uri.data[ctx->uri.len - 1] != '/') {
+ *p++ = '/';
+ }
+
+ if (escape == 0) {
+ p = ngx_cpymem(p, base64.data, base64.len);
+
+ } else {
+ p = (u_char *) ngx_escape_uri(p, base64.data, base64.len,
+ NGX_ESCAPE_URI_COMPONENT);
+ }
+
+ p = ngx_cpymem(p, " HTTP/1.0" CRLF, sizeof(" HTTP/1.0" CRLF) - 1);
+ p = ngx_cpymem(p, "Host: ", sizeof("Host: ") - 1);
+ p = ngx_cpymem(p, ctx->host.data, ctx->host.len);
+ *p++ = CR; *p++ = LF;
+
+ /* add "\r\n" at the header end */
+ *p++ = CR; *p++ = LF;
+
+ b->last = p;
+ ctx->request = b;
+
+ return NGX_OK;
+
+failed:
+
+ OCSP_REQUEST_free(ocsp);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_int_t rc;
+
+ rc = ngx_ssl_ocsp_parse_status_line(ctx);
+
+ if (rc == NGX_OK) {
+#if 0
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp status line \"%*s\"",
+ ctx->response->pos - ctx->response->start,
+ ctx->response->start);
+#endif
+
+ ctx->process = ngx_ssl_ocsp_process_headers;
+ return ctx->process(ctx);
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder sent invalid response");
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ u_char ch;
+ u_char *p;
+ ngx_buf_t *b;
+ enum {
+ sw_start = 0,
+ sw_H,
+ sw_HT,
+ sw_HTT,
+ sw_HTTP,
+ sw_first_major_digit,
+ sw_major_digit,
+ sw_first_minor_digit,
+ sw_minor_digit,
+ sw_status,
+ sw_space_after_status,
+ sw_status_text,
+ sw_almost_done
+ } state;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp process status line");
+
+ state = ctx->state;
+ b = ctx->response;
+
+ for (p = b->pos; p < b->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* "HTTP/" */
+ case sw_start:
+ switch (ch) {
+ case 'H':
+ state = sw_H;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_H:
+ switch (ch) {
+ case 'T':
+ state = sw_HT;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HT:
+ switch (ch) {
+ case 'T':
+ state = sw_HTT;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HTT:
+ switch (ch) {
+ case 'P':
+ state = sw_HTTP;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HTTP:
+ switch (ch) {
+ case '/':
+ state = sw_first_major_digit;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ /* the first digit of major HTTP version */
+ case sw_first_major_digit:
+ if (ch < '1' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ state = sw_major_digit;
+ break;
+
+ /* the major HTTP version or dot */
+ case sw_major_digit:
+ if (ch == '.') {
+ state = sw_first_minor_digit;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ break;
+
+ /* the first digit of minor HTTP version */
+ case sw_first_minor_digit:
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ state = sw_minor_digit;
+ break;
+
+ /* the minor HTTP version or the end of the request line */
+ case sw_minor_digit:
+ if (ch == ' ') {
+ state = sw_status;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ break;
+
+ /* HTTP status code */
+ case sw_status:
+ if (ch == ' ') {
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ ctx->code = ctx->code * 10 + ch - '0';
+
+ if (++ctx->count == 3) {
+ state = sw_space_after_status;
+ }
+
+ break;
+
+ /* space or end of line */
+ case sw_space_after_status:
+ switch (ch) {
+ case ' ':
+ state = sw_status_text;
+ break;
+ case '.': /* IIS may send 403.1, 403.2, etc */
+ state = sw_status_text;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ /* any text until end of line */
+ case sw_status_text:
+ switch (ch) {
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ /* end of status line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ b->pos = p;
+ ctx->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ b->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ size_t len;
+ ngx_int_t rc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp process headers");
+
+ for ( ;; ) {
+ rc = ngx_ssl_ocsp_parse_header_line(ctx);
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp header \"%*s: %*s\"",
+ ctx->header_name_end - ctx->header_name_start,
+ ctx->header_name_start,
+ ctx->header_end - ctx->header_start,
+ ctx->header_start);
+
+ len = ctx->header_name_end - ctx->header_name_start;
+
+ if (len == sizeof("Content-Type") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Content-Type",
+ sizeof("Content-Type") - 1)
+ == 0)
+ {
+ len = ctx->header_end - ctx->header_start;
+
+ if (len != sizeof("application/ocsp-response") - 1
+ || ngx_strncasecmp(ctx->header_start,
+ (u_char *) "application/ocsp-response",
+ sizeof("application/ocsp-response") - 1)
+ != 0)
+ {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder sent invalid "
+ "\"Content-Type\" header: \"%*s\"",
+ ctx->header_end - ctx->header_start,
+ ctx->header_start);
+ return NGX_ERROR;
+ }
+
+ continue;
+ }
+
+ /* TODO: honor Content-Length */
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder sent invalid response");
+
+ return NGX_ERROR;
+ }
+
+ ctx->process = ngx_ssl_ocsp_process_body;
+ return ctx->process(ctx);
+}
+
+static ngx_int_t
+ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ u_char c, ch, *p;
+ enum {
+ sw_start = 0,
+ sw_name,
+ sw_space_before_value,
+ sw_value,
+ sw_space_after_value,
+ sw_almost_done,
+ sw_header_almost_done
+ } state;
+
+ state = ctx->state;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+#if 0
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "s:%d in:'%02Xd:%c'", state, ch, ch);
+#endif
+
+ switch (state) {
+
+ /* first char */
+ case sw_start:
+
+ switch (ch) {
+ case CR:
+ ctx->header_end = p;
+ state = sw_header_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto header_done;
+ default:
+ state = sw_name;
+ ctx->header_name_start = p;
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ return NGX_ERROR;
+ }
+ break;
+
+ /* header name */
+ case sw_name:
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch == ':') {
+ ctx->header_name_end = p;
+ state = sw_space_before_value;
+ break;
+ }
+
+ if (ch == '-') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ if (ch == CR) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ }
+
+ return NGX_ERROR;
+
+ /* space* before header value */
+ case sw_space_before_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ default:
+ ctx->header_start = p;
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* header value */
+ case sw_value:
+ switch (ch) {
+ case ' ':
+ ctx->header_end = p;
+ state = sw_space_after_value;
+ break;
+ case CR:
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto done;
+ }
+ break;
+
+ /* space* before end of header line */
+ case sw_space_after_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* end of header line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+
+ /* end of header */
+ case sw_header_almost_done:
+ switch (ch) {
+ case LF:
+ goto header_done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_OK;
+
+header_done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp process body");
+
+ if (ctx->done) {
+ ctx->handler(ctx);
+ return NGX_DONE;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static u_char *
+ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ p = buf;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ }
+
+ ctx = log->data;
+
+ if (ctx) {
+ p = ngx_snprintf(p, len, ", responder: %V", &ctx->host);
+ }
+
+ return p;
+}
+
+
+#else
+
+
+ngx_int_t
+ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
+ ngx_str_t *responder, ngx_uint_t verify)
+{
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, not supported");
+
+ return NGX_OK;
+}
+
+ngx_int_t
+ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
+{
+ return NGX_OK;
+}
+
+
+#endif
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index d759489a6..0073dba3c 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -33,6 +33,8 @@ static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
+static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
+
static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = {
{ ngx_string("SSLv2"), NGX_SSL_SSLv2 },
@@ -124,6 +126,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
offsetof(ngx_http_ssl_srv_conf_t, client_certificate),
NULL },
+ { ngx_string("ssl_trusted_certificate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, trusted_certificate),
+ NULL },
+
{ ngx_string("ssl_prefer_server_ciphers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
@@ -152,13 +161,41 @@ static ngx_command_t ngx_http_ssl_commands[] = {
offsetof(ngx_http_ssl_srv_conf_t, crl),
NULL },
+ { ngx_string("ssl_stapling"),
+ 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, stapling),
+ NULL },
+
+ { ngx_string("ssl_stapling_file"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, stapling_file),
+ NULL },
+
+ { ngx_string("ssl_stapling_responder"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, stapling_responder),
+ NULL },
+
+ { ngx_string("ssl_stapling_verify"),
+ 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, stapling_verify),
+ NULL },
+
ngx_null_command
};
static ngx_http_module_t ngx_http_ssl_module_ctx = {
ngx_http_ssl_add_variables, /* preconfiguration */
- NULL, /* postconfiguration */
+ ngx_http_ssl_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
@@ -325,9 +362,12 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
* sscf->dhparam = { 0, NULL };
* sscf->ecdh_curve = { 0, NULL };
* sscf->client_certificate = { 0, NULL };
+ * sscf->trusted_certificate = { 0, NULL };
* sscf->crl = { 0, NULL };
* sscf->ciphers = { 0, NULL };
* sscf->shm_zone = NULL;
+ * sscf->stapling_file = { 0, NULL };
+ * sscf->stapling_responder = { 0, NULL };
*/
sscf->enable = NGX_CONF_UNSET;
@@ -336,6 +376,8 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
sscf->verify_depth = NGX_CONF_UNSET_UINT;
sscf->builtin_session_cache = NGX_CONF_UNSET;
sscf->session_timeout = NGX_CONF_UNSET;
+ sscf->stapling = NGX_CONF_UNSET;
+ sscf->stapling_verify = NGX_CONF_UNSET;
return sscf;
}
@@ -380,6 +422,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
"");
+ ngx_conf_merge_str_value(conf->trusted_certificate,
+ prev->trusted_certificate, "");
ngx_conf_merge_str_value(conf->crl, prev->crl, "");
ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
@@ -387,6 +431,11 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
+ ngx_conf_merge_value(conf->stapling, prev->stapling, 0);
+ ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0);
+ ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, "");
+ ngx_conf_merge_str_value(conf->stapling_responder,
+ prev->stapling_responder, "");
conf->ssl.log = cf->log;
@@ -479,10 +528,18 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
return NGX_CONF_ERROR;
}
+ }
- if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
- return NGX_CONF_ERROR;
- }
+ if (ngx_ssl_trusted_certificate(cf, &conf->ssl,
+ &conf->trusted_certificate,
+ conf->verify_depth)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
+ return NGX_CONF_ERROR;
}
if (conf->prefer_server_ciphers) {
@@ -515,6 +572,17 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR;
}
+ if (conf->stapling) {
+
+ if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,
+ &conf->stapling_responder, conf->stapling_verify)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ }
+
return NGX_CONF_OK;
}
@@ -650,3 +718,37 @@ invalid:
return NGX_CONF_ERROR;
}
+
+
+static ngx_int_t
+ngx_http_ssl_init(ngx_conf_t *cf)
+{
+ ngx_uint_t s;
+ ngx_http_ssl_srv_conf_t *sscf;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t **cscfp;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+ cscfp = cmcf->servers.elts;
+
+ for (s = 0; s < cmcf->servers.nelts; s++) {
+
+ sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
+
+ if (!sscf->stapling) {
+ continue;
+ }
+
+ clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
+
+ if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver,
+ clcf->resolver_timeout)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
index 58659ab95..c4c576ef6 100644
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -35,12 +35,18 @@ typedef struct {
ngx_str_t dhparam;
ngx_str_t ecdh_curve;
ngx_str_t client_certificate;
+ ngx_str_t trusted_certificate;
ngx_str_t crl;
ngx_str_t ciphers;
ngx_shm_zone_t *shm_zone;
+ ngx_flag_t stapling;
+ ngx_flag_t stapling_verify;
+ ngx_str_t stapling_file;
+ ngx_str_t stapling_responder;
+
u_char *file;
ngx_uint_t line;
} ngx_http_ssl_srv_conf_t;
diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm
index 67a6dea43..5f5585a3e 100644
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -50,7 +50,7 @@ our @EXPORT = qw(
HTTP_INSUFFICIENT_STORAGE
);
-our $VERSION = '1.3.6';
+our $VERSION = '1.3.7';
require XSLoader;
XSLoader::load('nginx', $VERSION);
diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c
index 0474a26ca..73fb44c8d 100644
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -371,6 +371,8 @@ ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
ngx_pass_open_channel(cycle, &ch);
}
+
+ cpu_affinity = 0;
}
@@ -384,8 +386,8 @@ ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn)
manager = 0;
loader = 0;
- path = ngx_cycle->pathes.elts;
- for (i = 0; i < ngx_cycle->pathes.nelts; i++) {
+ path = ngx_cycle->paths.elts;
+ for (i = 0; i < ngx_cycle->paths.nelts; i++) {
if (path[i]->manager) {
manager = 1;
@@ -1339,8 +1341,8 @@ ngx_cache_manager_process_handler(ngx_event_t *ev)
next = 60 * 60;
- path = ngx_cycle->pathes.elts;
- for (i = 0; i < ngx_cycle->pathes.nelts; i++) {
+ path = ngx_cycle->paths.elts;
+ for (i = 0; i < ngx_cycle->paths.nelts; i++) {
if (path[i]->manager) {
n = path[i]->manager(path[i]->data);
@@ -1368,8 +1370,8 @@ ngx_cache_loader_process_handler(ngx_event_t *ev)
cycle = (ngx_cycle_t *) ngx_cycle;
- path = cycle->pathes.elts;
- for (i = 0; i < cycle->pathes.nelts; i++) {
+ path = cycle->paths.elts;
+ for (i = 0; i < cycle->paths.nelts; i++) {
if (ngx_terminate || ngx_quit) {
break;