diff --git a/conf/koi-win b/conf/koi-win index 2b12b8da1..99edb1b2b 100644 --- a/conf/koi-win +++ b/conf/koi-win @@ -1,12 +1,12 @@ charset_map koi8-r windows-1251 { + 80 88 ; # euro + 95 95 ; # bullet 9A A0 ; #   - 9C B0 ; # ° - 9E B7 ; # · A3 B8 ; # small yo @@ -18,12 +18,16 @@ charset_map koi8-r windows-1251 { AD B4 ; # small Ukrainian soft g AE A2 ; # small Byelorussian short u + B0 B0 ; # ° + B3 A8 ; # capital YO B4 AA ; # capital Ukrainian YE B6 B2 ; # capital Ukrainian I B7 AF ; # capital Ukrainian J + B9 B9 ; # No + BD A5 ; # capital Ukrainian soft G BE A1 ; # capital Byelorussian short U diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index 92fefa4db..7657a5cc5 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -9,6 +9,61 @@ nginx changelog + + + + +директивы ssl_verify_client, ssl_verify_depth и ssl_client_certificate. + + +the "ssl_verify_client", "ssl_verify_depth", and "ssl_client_certificate" +directives. + + + + + +теперь переменная $request_method возвращает метод только основного запроса. + + +the $request_method variable now returns the main request method. + + + + + +в таблице перекодировки koi-win изменены коды символа &deg;. + + +the &deg; symbol codes were changed in koi-win conversion table. + + + + + +в таблицу перекодировки koi-win добавлены символы евро и номера. + + +the euro и N symbols were added to koi-win conversion table. + + + + + +если nginx распределял запросы на несколько машин, то при падении +одной из них запросы, предназначенные для этой машины, перенаправлялись только +на одну машину вместо того, чтобы равномерно распределяться между остальными. + + +if nginx distributed the requests among several backends and some backend +failed, then requests intended for this backend was directed to one live +backend only instead of being distributed among the rest. + + + + + + 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; + + 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; + } + + 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--; + + if (pc->tries) { + continue; + } + + goto failed; + } pc->peers->weight--; if (pc->peers->weight == 0) { pc->peers->current++; - } - 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]; - - if (peer->max_fails == 0 || peer->fails <= peer->max_fails) { - break; - } - - if (now - peer->accessed > peer->fail_timeout) { - peer->fails = 0; - break; - } - - pc->cur_peer++; - - if (pc->cur_peer >= pc->peers->number) { - pc->cur_peer = 0; - } - - pc->tries--; - - if (pc->tries == 0) { - - /* all peers failed, mark them as live for quick recovery */ - - for (i = 0; i < pc->peers->number; i++) { - pc->peers->peer[i].fails = 0; + if (pc->peers->current >= pc->peers->number) { + pc->peers->current = 0; } - /* ngx_unlock_mutex(pc->peers->mutex); */ + pc->peers->weight = pc->peers->peer[pc->peers->current].weight; + } - return NGX_BUSY; + } else { + for ( ;; ) { + 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; + } + + pc->cur_peer++; + + if (pc->cur_peer >= pc->peers->number) { + pc->cur_peer = 0; + } + + pc->tries--; + + if (pc->tries) { + continue; + } + + 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); @@ -175,6 +176,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) { @@ -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 } }; @@ -179,6 +209,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) { @@ -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[] = +"" CRLF +"400 The SSL certificate error" +CRLF +"" CRLF +"

400 Bad Request

" CRLF +"
The SSL certificate error
" CRLF +; + + +static char error_496_page[] = +"" CRLF +"400 No required SSL certificate was sent" +CRLF +"" CRLF +"

400 Bad Request

" CRLF +"
No required SSL certificate was sent
" CRLF +; + + static char error_497_page[] = "" CRLF "400 The plain HTTP request was sent to HTTPS port" @@ -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;