diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/nginx.h | 2 | ||||
-rw-r--r-- | src/event/ngx_event_connect.c | 89 | ||||
-rw-r--r-- | src/event/ngx_event_openssl.c | 105 | ||||
-rw-r--r-- | src/event/ngx_event_openssl.h | 11 | ||||
-rw-r--r-- | src/http/modules/ngx_http_ssl_module.c | 109 | ||||
-rw-r--r-- | src/http/modules/ngx_http_ssl_module.h | 4 | ||||
-rw-r--r-- | src/http/ngx_http_request.c | 36 | ||||
-rw-r--r-- | src/http/ngx_http_request.h | 5 | ||||
-rw-r--r-- | src/http/ngx_http_special_response.c | 28 | ||||
-rw-r--r-- | src/http/ngx_http_variables.c | 13 |
10 files changed, 358 insertions, 44 deletions
diff --git a/src/core/nginx.h b/src/core/nginx.h index 7fd9d7286..e49b91c91 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VER "nginx/0.3.44" +#define NGINX_VER "nginx/0.3.45" #define NGINX_VAR "NGINX" #define NGX_OLDPID_EXT ".oldbin" diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c index e4ca23840..157c48002 100644 --- a/src/event/ngx_event_connect.c +++ b/src/event/ngx_event_connect.c @@ -61,54 +61,75 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc) /* it's a first try - get a current peer */ - pc->cur_peer = pc->peers->current; + for ( ;; ) { + pc->cur_peer = pc->peers->current; - pc->peers->weight--; + peer = &pc->peers->peer[pc->cur_peer]; + + if (peer->max_fails == 0 || peer->fails <= peer->max_fails) { + break; + } + + if (now - peer->accessed > peer->fail_timeout) { + peer->fails = 0; + break; + } - if (pc->peers->weight == 0) { pc->peers->current++; - } - if (pc->peers->current >= pc->peers->number) { - pc->peers->current = 0; - } + if (pc->peers->current >= pc->peers->number) { + pc->peers->current = 0; + } - if (pc->peers->weight == 0) { pc->peers->weight = pc->peers->peer[pc->peers->current].weight; - } - } - for ( ;; ) { - peer = &pc->peers->peer[pc->cur_peer]; + pc->tries--; - if (peer->max_fails == 0 || peer->fails <= peer->max_fails) { - break; - } + if (pc->tries) { + continue; + } - if (now - peer->accessed > peer->fail_timeout) { - peer->fails = 0; - break; + goto failed; } - pc->cur_peer++; + pc->peers->weight--; - if (pc->cur_peer >= pc->peers->number) { - pc->cur_peer = 0; + if (pc->peers->weight == 0) { + pc->peers->current++; + + if (pc->peers->current >= pc->peers->number) { + pc->peers->current = 0; + } + + pc->peers->weight = pc->peers->peer[pc->peers->current].weight; } - pc->tries--; + } else { + for ( ;; ) { + peer = &pc->peers->peer[pc->cur_peer]; - if (pc->tries == 0) { + if (peer->max_fails == 0 || peer->fails <= peer->max_fails) { + break; + } + + if (now - peer->accessed > peer->fail_timeout) { + peer->fails = 0; + break; + } - /* all peers failed, mark them as live for quick recovery */ + pc->cur_peer++; - for (i = 0; i < pc->peers->number; i++) { - pc->peers->peer[i].fails = 0; + if (pc->cur_peer >= pc->peers->number) { + pc->cur_peer = 0; } - /* ngx_unlock_mutex(pc->peers->mutex); */ + pc->tries--; + + if (pc->tries) { + continue; + } - return NGX_BUSY; + goto failed; } } } @@ -319,6 +340,18 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc) wev->ready = 1; return NGX_OK; + +failed: + + /* all peers failed, mark them as live for quick recovery */ + + for (i = 0; i < pc->peers->number; i++) { + pc->peers->peer[i].fails = 0; + } + + /* ngx_unlock_mutex(pc->peers->mutex); */ + + return NGX_BUSY; } diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 3a7835ef6..0b217cffd 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -164,7 +164,8 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, } if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data, - SSL_FILETYPE_PEM) == 0) + SSL_FILETYPE_PEM) + == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_use_PrivateKey_file(\"%s\") failed", key->data); @@ -176,6 +177,26 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t +ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert) +{ + if (ngx_conf_full_name(cf->cycle, cert) == NGX_ERROR) { + 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_generate_rsa512_key(ngx_ssl_t *ssl) { if (SSL_CTX_need_tmp_RSA(ssl->ctx) == 0) { @@ -1023,6 +1044,88 @@ ngx_ssl_get_cipher_name(ngx_connection_t *c) } +ngx_int_t +ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + char *p; + size_t len; + X509 *cert; + X509_NAME *name; + + s->len = 0; + + cert = SSL_get_peer_certificate(c->ssl->connection); + + if (cert == NULL) { + return NGX_OK; + } + + name = X509_get_subject_name(cert); + + if (name == NULL) { + return NGX_ERROR; + } + + p = X509_NAME_oneline(name, NULL, 0); + + for (len = 0; p[len]; len++) { /* void */ } + + s->len = len; + s->data = ngx_palloc(pool, len); + if (s->data == NULL) { + OPENSSL_free(p); + return NGX_ERROR; + } + + ngx_memcpy(s->data, p, len); + + OPENSSL_free(p); + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + char *p; + size_t len; + X509 *cert; + X509_NAME *name; + + s->len = 0; + + cert = SSL_get_peer_certificate(c->ssl->connection); + + if (cert == NULL) { + return NGX_OK; + } + + name = X509_get_issuer_name(cert); + + if (name == NULL) { + return NGX_ERROR; + } + + p = X509_NAME_oneline(name, NULL, 0); + + for (len = 0; p[len]; len++) { /* void */ } + + s->len = len; + s->data = ngx_palloc(pool, len); + if (s->data == NULL) { + OPENSSL_free(p); + return NGX_ERROR; + } + + ngx_memcpy(s->data, p, len); + + OPENSSL_free(p); + + return NGX_OK; +} + + static void * ngx_openssl_create_conf(ngx_cycle_t *cycle) { diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 82bfa139f..4af42190b 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -31,6 +31,7 @@ typedef struct { typedef struct { SSL *connection; + ngx_int_t last; ngx_buf_t *buf; @@ -60,10 +61,15 @@ typedef struct { #define NGX_SSL_BUFSIZE 16384 +#define NGX_SSL_VERIFY SSL_VERIFY_PEER + + ngx_int_t ngx_ssl_init(ngx_log_t *log); ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols); 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 ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl); ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags); @@ -75,6 +81,11 @@ ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session); u_char *ngx_ssl_get_protocol(ngx_connection_t *c); u_char *ngx_ssl_get_cipher_name(ngx_connection_t *c); +ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); +ngx_int_t ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); + ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index e2191ef47..a57fbfb6f 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -19,6 +19,10 @@ typedef u_char *(*ngx_ssl_variable_handler_pt)(ngx_connection_t *); static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_ssl_client_s_dn(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_ssl_client_i_dn(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_ssl_add_variables(ngx_conf_t *cf); static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf); @@ -43,7 +47,6 @@ static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = { }; - static ngx_command_t ngx_http_ssl_commands[] = { { ngx_string("ssl"), @@ -81,6 +84,27 @@ static ngx_command_t ngx_http_ssl_commands[] = { offsetof(ngx_http_ssl_srv_conf_t, ciphers), NULL }, + { ngx_string("ssl_verify_client"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, verify), + NULL }, + + { ngx_string("ssl_verify_depth"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, verify_depth), + NULL }, + + { ngx_string("ssl_client_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, client_certificate), + NULL }, + { ngx_string("ssl_prefer_server_ciphers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE @@ -142,6 +166,12 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = { { ngx_string("ssl_cipher"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGABLE, 0 }, + { ngx_string("ssl_client_s_dn"), NULL, ngx_http_ssl_client_s_dn, + 0, NGX_HTTP_VAR_CHANGABLE, 0 }, + + { ngx_string("ssl_client_i_dn"), NULL, ngx_http_ssl_client_i_dn, + 0, NGX_HTTP_VAR_CHANGABLE, 0 }, + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -180,6 +210,58 @@ ngx_http_ssl_variable(ngx_http_request_t *r, static ngx_int_t +ngx_http_ssl_client_s_dn(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + if (r->connection->ssl) { + if (ngx_ssl_get_subject_dn(r->connection, r->pool, (ngx_str_t *) v) + != NGX_OK) + { + return NGX_ERROR; + } + + if (v->len) { + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + + return NGX_OK; + } + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_ssl_client_i_dn(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + if (r->connection->ssl) { + if (ngx_ssl_get_issuer_dn(r->connection, r->pool, (ngx_str_t *) v) + != NGX_OK) + { + return NGX_ERROR; + } + + if (v->len) { + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + + return NGX_OK; + } + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_ssl_add_variables(ngx_conf_t *cf) { ngx_http_variable_t *var, *v; @@ -217,12 +299,16 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) * scf->certificate.data = NULL; * scf->certificate_key.len = 0; * scf->certificate_key.data = NULL; + * scf->client_certificate.len = 0; + * scf->client_certificate.data = NULL; * scf->ciphers.len = 0; * scf->ciphers.data = NULL; */ scf->enable = NGX_CONF_UNSET; scf->session_timeout = NGX_CONF_UNSET; + scf->verify = NGX_CONF_UNSET; + scf->verify_depth = NGX_CONF_UNSET; scf->prefer_server_ciphers = NGX_CONF_UNSET; return scf; @@ -253,12 +339,18 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) (NGX_CONF_BITMASK_SET |NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1)); + ngx_conf_merge_value(conf->verify, prev->verify, 0); + ngx_conf_merge_value(conf->verify_depth, prev->verify_depth, 1); + ngx_conf_merge_str_value(conf->certificate, prev->certificate, NGX_DEFLAUT_CERTIFICATE); ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, NGX_DEFLAUT_CERTIFICATE_KEY); + ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate, + ""); + ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFLAUT_CIPHERS); @@ -291,6 +383,21 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) &conf->ciphers); } + if (conf->verify) { + SSL_CTX_set_verify(conf->ssl.ctx, NGX_SSL_VERIFY, NULL); + + SSL_CTX_set_verify_depth(conf->ssl.ctx, conf->verify_depth); + + if (conf->client_certificate.len) { + if (ngx_ssl_client_certificate(cf, &conf->ssl, + &conf->client_certificate) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + } + #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE if (conf->prefer_server_ciphers) { diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h index 4207cdfdb..85803096e 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -22,10 +22,14 @@ typedef struct { ngx_uint_t protocols; + ngx_int_t verify; + ngx_int_t verify_depth; + time_t session_timeout; ngx_str_t certificate; ngx_str_t certificate_key; + ngx_str_t client_certificate; ngx_str_t ciphers; } ngx_http_ssl_srv_conf_t; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 680b3bd45..5207fa1cc 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1172,8 +1172,12 @@ ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r) { - size_t len; - u_char *ua, *user_agent, ch; + size_t len; + u_char *ua, *user_agent, ch; +#if (NGX_HTTP_SSL) + long rc; + ngx_http_ssl_srv_conf_t *sscf; +#endif if (r->headers_in.host) { for (len = 0; len < r->headers_in.host->value.len; len++) { @@ -1243,6 +1247,34 @@ ngx_http_process_request_header(ngx_http_request_t *r) return NGX_ERROR; } +#if (NGX_HTTP_SSL) + + if (r->connection->ssl) { + sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); + + if (sscf->verify) { + rc = SSL_get_verify_result(r->connection->ssl->connection); + + if (rc != X509_V_OK) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client SSL certificate verify error: %l ", rc); + ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR); + return NGX_ERROR; + } + + if (SSL_get_peer_certificate(r->connection->ssl->connection) + == NULL) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent no required SSL certificate"); + ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT); + return NGX_ERROR; + } + } + } + +#endif + if (r->headers_in.connection) { if (r->headers_in.connection->value.len == 5 && ngx_strcasecmp(r->headers_in.connection->value.data, "close") diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index cc40f449a..e467dde17 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -75,7 +75,10 @@ /* The special code to close connection without any response */ #define NGX_HTTP_CLOSE 444 -#define NGX_HTTP_OWN_CODES NGX_HTTP_TO_HTTPS +#define NGX_HTTP_OWN_CODES 495 + +#define NGX_HTTPS_CERT_ERROR 495 +#define NGX_HTTPS_NO_CERT 496 /* * We use the special code for the plain HTTP requests that are sent to diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c index d1072a9af..734ad6947 100644 --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -163,6 +163,26 @@ static char error_416_page[] = ; +static char error_495_page[] = +"<html>" CRLF +"<head><title>400 The SSL certificate error</title></head>" +CRLF +"<body bgcolor=\"white\">" CRLF +"<center><h1>400 Bad Request</h1></center>" CRLF +"<center>The SSL certificate error</center>" CRLF +; + + +static char error_496_page[] = +"<html>" CRLF +"<head><title>400 No required SSL certificate was sent</title></head>" +CRLF +"<body bgcolor=\"white\">" CRLF +"<center><h1>400 Bad Request</h1></center>" CRLF +"<center>No required SSL certificate was sent</center>" CRLF +; + + static char error_497_page[] = "<html>" CRLF "<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>" @@ -254,6 +274,8 @@ static ngx_str_t error_pages[] = { #define NGX_HTTP_LEVEL_400 17 + ngx_string(error_495_page), /* 495, https certificate error */ + ngx_string(error_496_page), /* 496, https no certificate */ ngx_string(error_497_page), /* 497, http to https */ ngx_string(error_404_page), /* 498, invalid host name */ ngx_null_string, /* 499, client had closed connection */ @@ -296,6 +318,8 @@ ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE: case NGX_HTTP_REQUEST_URI_TOO_LARGE: case NGX_HTTP_TO_HTTPS: + case NGX_HTTPS_CERT_ERROR: + case NGX_HTTPS_NO_CERT: case NGX_HTTP_INTERNAL_SERVER_ERROR: r->keepalive = 0; } @@ -305,6 +329,8 @@ ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) switch (error) { case NGX_HTTP_BAD_REQUEST: case NGX_HTTP_TO_HTTPS: + case NGX_HTTPS_CERT_ERROR: + case NGX_HTTPS_NO_CERT: r->lingering_close = 0; } } @@ -372,6 +398,8 @@ ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) + NGX_HTTP_LEVEL_400; switch (error) { case NGX_HTTP_TO_HTTPS: + case NGX_HTTPS_CERT_ERROR: + case NGX_HTTPS_NO_CERT: r->headers_out.status = NGX_HTTP_BAD_REQUEST; error = NGX_HTTP_BAD_REQUEST; break; diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c index 0f162a70e..83f2adc0b 100644 --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -810,19 +810,12 @@ static ngx_int_t ngx_http_variable_request_method(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - if (r->method_name.data) { - if (r->upstream && r->upstream->method.len) { - v->len = r->upstream->method.len; - v->data = r->upstream->method.data; - - } else { - v->len = r->method_name.len; - v->data = r->method_name.data; - } - + if (r->main->method_name.data) { + v->len = r->main->method_name.len; v->valid = 1; v->no_cachable = 0; v->not_found = 0; + v->data = r->main->method_name.data; } else { v->not_found = 1; |