From 4e6f698d07ff7353dea00fd20b15873563a0941f Mon Sep 17 00:00:00 2001 From: SG <13872653+mmguero@users.noreply.github.com> Date: Mon, 20 Apr 2020 14:54:55 -0600 Subject: [PATCH 01/15] Revert "Merge pull request #199 from AVENTER-UG/issue_180" This reverts commit bf64cf217abbe79917f9d44a651c2ecbb82ec993, reversing changes made to f022103e31e71e70a4bb64cd4c7792fa3f238b4a. This change isn't right -- it an LDAP setup when `group_attribute_is_dn on` is enabled, which is what this section of code (https://github.com/kvspb/nginx-auth-ldap/commit/bf64cf217abbe79917f9d44a651c2ecbb82ec993#diff-c05c0daefb48996cbf510b81002b49bcR2230) is conditionally targeting. This original PR #199 changed the underlying LDAP query (eg `user_val`) from looking up the user's DN as a group attribute in LDAP (eg set via the `group_attribute` directive in nginx) to looking up the _group's_ DN, which isn't right and won't work. This PR reverts the previous change to make this work correctly again. Fwiw, the originally-referenced issue #180 seems to be a completely different issue, relating to escaping and parentheses. --- ngx_http_auth_ldap_module.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 37d0718..2f4e592 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -2216,7 +2216,6 @@ ngx_http_auth_ldap_check_group(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t * ngx_memcpy(gr, val.data, val.len); gr[val.len] = '\0'; tail_gr = ngx_strchr(gr, ','); - if (tail_gr == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: Incorrect group DN: \"%s\"", gr); ctx->outcome = OUTCOME_ERROR; @@ -2230,9 +2229,9 @@ ngx_http_auth_ldap_check_group(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t * if (ctx->server->group_attribute_dn == 1) { user_val = ngx_pcalloc( r->pool, - ctx->dn.len + 1); - ngx_memcpy(user_val, ctx->dn.data, ctx->dn.len); - user_val[ctx->dn.len] = '\0'; + ctx->user_dn.len + 1); + ngx_memcpy(user_val, ctx->user_dn.data, ctx->user_dn.len); + user_val[ctx->user_dn.len] = '\0'; } else { user_val = ngx_pcalloc( r->pool, From 456514a8771d96bc2a8aea2198b56ae8ed6f2194 Mon Sep 17 00:00:00 2001 From: SG <13872653+mmguero@users.noreply.github.com> Date: Mon, 4 May 2020 16:12:01 -0600 Subject: [PATCH 02/15] Allow ssl_check_cert to be on (full verification), off (no verification) or chain (verify cert chain but not hostname/IP) --- ngx_http_auth_ldap_module.c | 75 +++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 2f4e592..f123924 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -69,6 +69,10 @@ extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); #define NGX_HTTP_AUTH_LDAP_MAX_SERVERS_SIZE 7 +#define SSL_CERT_VERIFY_OFF 0 +#define SSL_CERT_VERIFY_FULL 1 +#define SSL_CERT_VERIFY_CHAIN 2 + typedef struct { LDAPURLDesc *ludpp; @@ -431,18 +435,24 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) return NGX_CONF_ERROR; } server->connections = i; - } else if (ngx_strcmp(value[0].data, "ssl_check_cert") == 0 && ngx_strcmp(value[1].data, "on") == 0) { - #if OPENSSL_VERSION_NUMBER >= 0x10002000 - server->ssl_check_cert = 1; - #else - #if GNUC > 4 - #warning "http_auth_ldap: Compiling with OpenSSL < 1.0.2, certificate verification will be unavailable. OPENSSL_VERSION_NUMBER == " XSTR(OPENSSL_VERSION_NUMBER) - #endif - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "http_auth_ldap: 'ssl_cert_check': cannot verify remote certificate's domain name because " - "your version of OpenSSL is too old. " - "Please install OpenSSL >= 1.02 and recompile nginx."); - #endif + } else if (ngx_strcmp(value[0].data, "ssl_check_cert") == 0) { + #if OPENSSL_VERSION_NUMBER >= 0x10002000 + if (ngx_strcmp(value[1].data, "on") == 0) { + server->ssl_check_cert = SSL_CERT_VERIFY_FULL; + } else if (ngx_strcmp(value[1].data, "chain") == 0) { + server->ssl_check_cert = SSL_CERT_VERIFY_CHAIN; + } else { + server->ssl_check_cert = SSL_CERT_VERIFY_OFF; + } + #else + #if GNUC > 4 + #warning "http_auth_ldap: Compiling with OpenSSL < 1.0.2, certificate verification will be unavailable. OPENSSL_VERSION_NUMBER == " XSTR(OPENSSL_VERSION_NUMBER) + #endif + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "http_auth_ldap: 'ssl_cert_check': cannot verify remote certificate's domain name because " + "your version of OpenSSL is too old. " + "Please install OpenSSL >= 1.0.2 and recompile nginx."); + #endif } else if (ngx_strcmp(value[0].data, "ssl_ca_dir") == 0) { server->ssl_ca_dir = value[1]; } else if (ngx_strcmp(value[0].data, "ssl_ca_file") == 0) { @@ -1334,18 +1344,25 @@ ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn, ngx_flag_t vali long chain_verified = SSL_get_verify_result(conn->ssl->connection); int addr_verified; - char *hostname = c->server->ludpp->lud_host; - addr_verified = X509_check_host(cert, hostname, 0, 0, 0); - - if (!addr_verified) { // domain not in cert? try IP - size_t len; // get IP length - if (conn->sockaddr->sa_family == 4) len = 4; - else if (conn->sockaddr->sa_family == 6) len = 16; - else { // very unlikely indeed - ngx_http_auth_ldap_close_connection(c); - return; + if (c->server->ssl_check_cert == SSL_CERT_VERIFY_CHAIN) { + // chain_verified is enough, not requiring full name/IP verification + addr_verified = 1; + + } else { + // verify hostname/IP + char *hostname = c->server->ludpp->lud_host; + addr_verified = X509_check_host(cert, hostname, 0, 0, 0); + + if (!addr_verified) { // domain not in cert? try IP + size_t len; // get IP length + if (conn->sockaddr->sa_family == 4) len = 4; + else if (conn->sockaddr->sa_family == 6) len = 16; + else { // very unlikely indeed + ngx_http_auth_ldap_close_connection(c); + return; + } + addr_verified = X509_check_ip(cert, (const unsigned char*)conn->sockaddr->sa_data, len, 0); } - addr_verified = X509_check_ip(cert, (const unsigned char*)conn->sockaddr->sa_data, len, 0); } // Find anything fishy? @@ -1528,11 +1545,11 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) // if LDAP_SERVER_DOWN (usually timeouts or server disconnects) if (rc == LDAP_SERVER_DOWN && \ - c->server->max_down_retries_count < c->server->max_down_retries) { - /** - update counter (this is always reset in - ngx_http_auth_ldap_connect() for a successful ldap - connection + c->server->max_down_retries_count < c->server->max_down_retries) { + /** + update counter (this is always reset in + ngx_http_auth_ldap_connect() for a successful ldap + connection **/ c->server->max_down_retries_count++; ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: LDAP_SERVER_DOWN: retry count: %d", @@ -1542,7 +1559,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) // timer call to this read handler again ngx_http_auth_ldap_reconnect_handler(rev); return; - } + } return; } From 3686bd0859310c54ebe7bda50b872be6f81731b4 Mon Sep 17 00:00:00 2001 From: SG <13872653+mmguero@users.noreply.github.com> Date: Tue, 5 May 2020 10:55:35 -0600 Subject: [PATCH 03/15] testing fix for kvspb/nginx-auth-ldap#236 segmentation fault in ngx_http_auth_ldap_ssl_handshake_handler with ssl_check_cert and ssl_ca_dir --- ngx_http_auth_ldap_module.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index f123924..9dd1dcb 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -26,6 +26,7 @@ * SUCH DAMAGE. */ +#include #include #include #include @@ -437,7 +438,7 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) server->connections = i; } else if (ngx_strcmp(value[0].data, "ssl_check_cert") == 0) { #if OPENSSL_VERSION_NUMBER >= 0x10002000 - if (ngx_strcmp(value[1].data, "on") == 0) { + if ((ngx_strcmp(value[1].data, "on") == 0) || (ngx_strcmp(value[1].data, "full") == 0)) { server->ssl_check_cert = SSL_CERT_VERIFY_FULL; } else if (ngx_strcmp(value[1].data, "chain") == 0) { server->ssl_check_cert = SSL_CERT_VERIFY_CHAIN; @@ -1355,13 +1356,19 @@ ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn, ngx_flag_t vali if (!addr_verified) { // domain not in cert? try IP size_t len; // get IP length - if (conn->sockaddr->sa_family == 4) len = 4; - else if (conn->sockaddr->sa_family == 6) len = 16; + + struct sockaddr *conn_sockaddr = NULL; + if (conn->sockaddr != NULL) conn_sockaddr = conn->sockaddr; + else if (c->conn->sockaddr != NULL) conn_sockaddr = c->conn->sockaddr; + else conn_sockaddr = &c->server->parsed_url->sockaddr.sockaddr; + + if (conn_sockaddr->sa_family == AF_INET) len = 4; + else if (conn_sockaddr->sa_family == AF_INET6) len = 16; else { // very unlikely indeed ngx_http_auth_ldap_close_connection(c); return; } - addr_verified = X509_check_ip(cert, (const unsigned char*)conn->sockaddr->sa_data, len, 0); + addr_verified = X509_check_ip(cert, (const unsigned char*)conn_sockaddr->sa_data, len, 0); } } From 61b70e4282cf61db50f6369ef059d2a71763f667 Mon Sep 17 00:00:00 2001 From: SG <13872653+mmguero@users.noreply.github.com> Date: Tue, 5 May 2020 11:11:27 -0600 Subject: [PATCH 04/15] fix compiler error --- ngx_http_auth_ldap_module.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 9dd1dcb..341c9f8 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -1359,8 +1359,8 @@ ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn, ngx_flag_t vali struct sockaddr *conn_sockaddr = NULL; if (conn->sockaddr != NULL) conn_sockaddr = conn->sockaddr; - else if (c->conn->sockaddr != NULL) conn_sockaddr = c->conn->sockaddr; - else conn_sockaddr = &c->server->parsed_url->sockaddr.sockaddr; + else if (c->conn.sockaddr != NULL) conn_sockaddr = c->conn.sockaddr; + else conn_sockaddr = &c->server->parsed_url.sockaddr.sockaddr; if (conn_sockaddr->sa_family == AF_INET) len = 4; else if (conn_sockaddr->sa_family == AF_INET6) len = 16; From 3338f5fcf47cbf392a29510cb330b20f9458ed34 Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Fri, 5 Aug 2022 16:03:39 +0200 Subject: [PATCH 05/15] - Add support of a resolver to resolve LDAP hostname - Add possibility to retrieve LDAP attributes during LDAP search --- README.md | 215 ++++++++++++++++---- example.conf | 9 +- ngx_http_auth_ldap_module.c | 382 ++++++++++++++++++++++++++++++++++-- 3 files changed, 553 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 06bd0f2..c5d883c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,22 @@ # LDAP Authentication module for nginx LDAP module for nginx which supports authentication against multiple LDAP servers. -# How to install +## Project history -## FreeBSD +This project is a clone of [nginx-auth-ldap](https://github.com/kvspb/nginx-auth-ldap) original module from [kvspb](https://github.com/kvspb). + +The reasons for this fork are: + +* The original project seems abondonned (no commit since 2 years) +* Inherit from other contributors fixes/features + * [Pull request #237](https://github.com/kvspb/nginx-auth-ldap/pull/237) from [mmguero-dev](https://github.com/mmguero-dev/nginx-auth-ldap) +* Add new features + * Add the use of `resolver` to resolve hostname of the LDAP server + * Support LDAP attributes fecthing during search + +## How to install + +### FreeBSD ```bash cd /usr/ports/www/nginx && make config install clean @@ -16,10 +29,10 @@ Check HTTP_AUTH_LDAP options [*] HTTP_AUTH_LDAP 3rd party http_auth_ldap module ``` -## Linux +### Linux ```bash -cd ~ && git clone https://github.com/kvspb/nginx-auth-ldap.git +cd ~ && git clone https://github.com/Ericbla/nginx-auth-ldap.git ``` in nginx source folder @@ -29,11 +42,14 @@ in nginx source folder make install ``` -# Example configuration +## Example configuration + Define list of your LDAP servers with required user/group requirements: ```bash http { + auth_ldap_resolver 8.8.8.8; + ldap_server test1 { url ldap://192.168.0.1:3268/DC=test,DC=local?sAMAccountName?sub?(objectClass=person); binddn "TEST\\LDAPUSER"; @@ -55,6 +71,7 @@ Define list of your LDAP servers with required user/group requirements: ``` And add required servers in correct order into your location/server directive: + ```bash server { listen 8000; @@ -62,7 +79,7 @@ And add required servers in correct order into your location/server directive: auth_ldap "Forbidden"; auth_ldap_servers test1; - auth_ldap_servers test2; + auth_ldap_servers test2; location / { root html; @@ -72,33 +89,132 @@ And add required servers in correct order into your location/server directive: } ``` -# Available config parameters +## Available config parameters + +### auth_ldap_cache_enabled + +* Syntax: auth_ldap_cache_enabled on | off; +* Default: auth_ldap_cache_enabled off; +* Context: http + +### auth_ldap_cache_expiration_time + +* Syntax: auth_ldap_cache_expiration_time time; +* Default: auth_ldap_cache_expiration_time 10000; +* Context: http + +Cache expiration time (in ms). + +### auth_ldap_cache_size + +* Syntax: auth_ldap_cache_size size; +* Default: auth_ldap_cache_size 100; +* Context: http + +Number of cached LDAP authentications (min 100) + +### auth_ldap_servers_size + +* Syntax: auth_ldap_servers_size size; +* Syntax: auth_ldap_servers_size 7; +* Context: http + +Maximum number of `ldap_server` elements to support + +### auth_ldap + +* Syntax: auth_ldap off | _realm_; +* Default: -- +* Context: http, server, loc, limit_expect + +Set the _realm_ to be used with the `WWW-Authenticate` response header when authentication failed or is missing. + +### auth_ldap_servers + +* Syntax: auth_ldap_servers _name_; +* Default: -- +* Context: http, server, loc, limit_expect + +Select the server _name_ to work with user authentication + +### auth_ldap_resolver + +* Syntax: auth_ldap_resolver _address_ ... [valid=time] [ipv4=on|off] [ipv6=on|off] [status_zone=zone]; +* Default: -- +* Context: http + +The resolver to use as a fallback when the system hostname resolution +(gethostbyname()) can't resolve the LDAP server hostname. +See the `resolver` directive of the **ngx_http_core_module** + +### auth_ldap_resolver_timeout -## url -expected value: string +* Syntax: auth_ldap_resolver_timeout time; +* Default: auth_ldap_resolver_timeout 10000; +* Context: http -Available URL schemes: ldap://, ldaps:// +Resolver requests timeout (in ms). -## binddn -expected value: string +### ldap_server -## binddn_passwd -expected value: string +* Syntax: ldap_server _name_ { ... } +* Default: none +* Context: http -## group_attribute -expected value: string +## Configuration parameters for the `ldap_server` block -## group_attribute_is_dn -expected value: on or off, default off +### url -## require -expected value: valid_user, user, group +* Syntax: url _url_; +* Default: -- +* Context: `ldap_server` block -## satisfy -expected value: all, any +url format: ldap[s]://host[:port]/dn?attrs?scope?filter[?exts] -## max_down_retries -expected value: a number, default 0 +### binddn + +* Syntax: binddn _dn_; +* Default: -- +* Context: `ldap_server` block + +### binddn_passwd + +* Syntax: binddn_passwd _password_; +* Default: -- +* Context: `ldap_server` block + +### group_attribute + +* Syntax: group attr; +* Default: -- +* Context: `ldap_server` block + +### group_attribute_is_dn + +* Syntax: group_attribute_is_dn on | off; +* Default: group_attribute_is_dn off; +* Context: `ldap_server` block + +Tell to search for full DN in member object. + +### require + +* Syntax: require valid_user | user | group; +* Default: --; +* Context: `ldap_server` block + + +### satisfy + +* Syntax: satisfy all | any; +* Default: --; +* Context: `ldap_server` block + +### max_down_retries + +* Syntax: max_down_retries _number_; +* Default: max_down_retries 0; +* Context: `ldap_server` block Retry count for attempting to reconnect to an LDAP server if it is considered "DOWN". This may happen if a KEEP-ALIVE connection to an LDAP server times @@ -106,15 +222,16 @@ out or is terminated by the server end after some amount of time. This can usually help with the following error: -``` +```text http_auth_ldap: ldap_result() failed (-1: Can't contact LDAP server) ``` -## connections -expected value: a number greater than 0 -## ssl_check_cert -expected value: on or off, default off +### ssl_check_cert + +* Syntax: ssl_check_cert on | chain | off; +* Default: ssl_check_cert off; +* Context: `ldap_server` block Verify the remote certificate for LDAPs connections. If disabled, any remote certificate will be accepted which exposes you to possible man-in-the-middle attacks. Note that the server's @@ -123,23 +240,51 @@ See below how to trust CAs without installing them system-wide. This options needs OpenSSL >= 1.0.2; it is unavailable if compiled with older versions. -## ssl_ca_file -expected value: file path +When `chain` is given, verify cert chain but not hostname/IP in SAN + +### ssl_ca_file + +* Syntax: ssl_ca_file _file-path_; +* Default: --; +* Context: `ldap_server` block Trust the CA certificate in this file (see ssl_check_cert above). -## ssl_ca_dir -expected value: directory path +### ssl_ca_dir + +* Syntax: ssl_ca_file _dir-path_; +* Default: --; +* Context: `ldap_server` block Trust all CA certificates in this directory (see ssl_check_cert above). Note that you need to provide hash-based symlinks in the directory for this to work; you'll basically need to run OpenSSL's c_rehash command in this directory. -## referral -expected value: on, off +### referral + +* Syntax: referral on | off; +* Default: referral on; +* Context: `ldap_server` block LDAP library default is on. This option disables usage of referral messages from LDAP server. Usefull for authenticating against read only AD server without access to read write. +### attribute_header_prefix + +* Syntax: attribute_header_prefix _string_; +* Default: attribute_header_prefix X-LDAP-ATTRS-; +* Context: `ldap_server` block + +The prefix for the HEADER names used to carry the feteched attributes (default: "X-LDAP-ATTRS-") + +### search_attribute + +* Syntax: search_attribute _attr_; +* Default: -- +* Context: `ldap_server` block + +Add this LDAP attribute description for the search (require valid-user or require user). The attribute value will be return as a HTTP header () in the authentication response. + +_Note_: This parameter can be repeated several times when several attributes need to be fetched diff --git a/example.conf b/example.conf index 1512b2d..610c9fc 100644 --- a/example.conf +++ b/example.conf @@ -11,6 +11,9 @@ http { sendfile on; keepalive_timeout 65; + auth_ldap_resolver 8.8.8.8; + auth_ldap_cache_enabled on; + # define ldap server ldap_server ad_1 { # user search base. @@ -18,7 +21,11 @@ http { # bind as binddn "CN=Operator,OU=Service Accounts,DC=company,DC=com"; # bind pw - binddn_passwd ; + binddn_passwd ; + # Select attributes to be retrieved during the search (several are possible) + search_attribute mail; + search_attribute sn; + search_attribute givenName; # group attribute name which contains member object group_attribute member; # search for full DN in member object diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 341c9f8..8889494 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -26,13 +26,13 @@ * SUCH DAMAGE. */ -#include #include #include #include #include #include #include +//#include // used for manual warnings #define XSTR(x) STR(x) @@ -74,6 +74,12 @@ extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); #define SSL_CERT_VERIFY_FULL 1 #define SSL_CERT_VERIFY_CHAIN 2 +#define LDAP_ATTR_HEADER_DEFAULT_PREFIX "X-LDAP-ATTR-" + +#define SANITIZE_NO_CONV 0 +#define SANITIZE_TO_UPPER 1 +#define SANITIZE_TO_LOWER 2 + typedef struct { LDAPURLDesc *ludpp; @@ -107,6 +113,10 @@ typedef struct { ngx_msec_t request_timeout; ngx_queue_t free_connections; ngx_queue_t waiting_requests; + + ngx_array_t *search_attributes; // Search attributes from configuration parsing + char **attrs; // Search attributes formated for ldap_search_ext() + ngx_str_t attribute_header_prefix; } ngx_http_auth_ldap_server_t; typedef struct { @@ -118,6 +128,9 @@ typedef struct { #if (NGX_OPENSSL) ngx_ssl_t ssl; #endif + ngx_msec_t resolver_timeout; /* resolver_timeout */ + ngx_resolver_t *resolver; /* resolver */ + ngx_pool_t *cnf_pool; } ngx_http_auth_ldap_main_conf_t; typedef struct { @@ -183,6 +196,7 @@ typedef enum { typedef struct ngx_http_auth_ldap_connection { ngx_log_t *log; + ngx_http_auth_ldap_main_conf_t *main_cnf; ngx_http_auth_ldap_server_t *server; ngx_peer_connection_t conn; ngx_event_t reconnect_event; @@ -198,12 +212,14 @@ typedef struct ngx_http_auth_ldap_connection { LDAP* ld; ngx_http_auth_ldap_connection_state_t state; int msgid; + ngx_resolver_ctx_t *resolver_ctx; } ngx_http_auth_ldap_connection_t; static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); static char * ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char * ngx_http_auth_ldap_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); static char * ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); @@ -217,7 +233,11 @@ static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); static ngx_int_t ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle); static void ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c); static void ngx_http_auth_ldap_read_handler(ngx_event_t *rev); +static void ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c); +static void ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c); static void ngx_http_auth_ldap_reconnect_handler(ngx_event_t *); +static void ngx_http_auth_ldap_resolve_handler(ngx_resolver_ctx_t *ctx); +static ngx_int_t ngx_http_auth_ldap_init_servers(ngx_cycle_t *cycle); static ngx_int_t ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle); static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, @@ -230,6 +250,7 @@ static ngx_int_t ngx_http_auth_ldap_recover_bind(ngx_http_request_t *r, ngx_http #if (NGX_OPENSSL) static ngx_int_t ngx_http_auth_ldap_restore_handlers(ngx_connection_t *conn); #endif +static u_char * santitize_str(u_char *str, ngx_uint_t conv); ngx_http_auth_ldap_cache_t ngx_http_auth_ldap_cache; @@ -274,6 +295,22 @@ static ngx_command_t ngx_http_auth_ldap_commands[] = { offsetof(ngx_http_auth_ldap_main_conf_t, servers_size), NULL }, + { + ngx_string("auth_ldap_resolver"), + NGX_HTTP_MAIN_CONF | NGX_CONF_1MORE, + ngx_http_auth_ldap_resolver, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + NULL + }, + { + ngx_string("auth_ldap_resolver_timeout"), + NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_auth_ldap_main_conf_t, resolver_timeout), + NULL + }, { ngx_string("auth_ldap"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, @@ -320,6 +357,41 @@ ngx_module_t ngx_http_auth_ldap_module = { }; + +/*** Tools ***/ + +static u_char * santitize_str(u_char *str, ngx_uint_t conv) { + static u_char sanitizeTable[256] = {0xff}; + ngx_uint_t i; + u_char *p; + + if (str == NULL || *str == '\0') { + return str; + } + + // Initialize un-initialized table at first use + if (sanitizeTable[0] == 0xff) { + // Initialize un initialized table once + for (i = 0; i < sizeof(sanitizeTable); i++) { + // Convert any char other than ALPHA / DIGIT / "-" / "_" to the "_" char + sanitizeTable[i] = (isalnum(i) || i == '-' || i == '_') ? i : '_'; + } + } + + for (p = str; *p; p++) { + if (conv == SANITIZE_TO_UPPER && *p >= 97 && *p <= 122) { + // lowercase to uppercase + *p -= 32; + } else if (conv == SANITIZE_TO_LOWER && *p >= 65 && *p <= 90) { + // lowercase to uppercase + *p += 32; + } else { + *p = sanitizeTable[(int)*p]; + } + } + return str; // Fluent interface +} + /*** Configuration and initialization ***/ /** @@ -343,7 +415,7 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c return NGX_CONF_ERROR; } - if (cnf->servers == NULL) { + if (cnf->servers == NULL) { if (cnf->servers_size == NGX_CONF_UNSET) { cnf->servers_size = NGX_HTTP_AUTH_LDAP_MAX_SERVERS_SIZE; } @@ -351,6 +423,7 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c if (cnf->servers == NULL) { return NGX_CONF_ERROR; } + ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "http_auth_ldap: ngx_http_auth_ldap_ldap_server_block: Allocated servers array (capacity=%d) in main conf at addr=0x%p", cnf->servers_size, (void *)cnf->servers); } server = ngx_array_push(cnf->servers); @@ -358,6 +431,8 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c return NGX_CONF_ERROR; } + ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "http_auth_ldap: ngx_http_auth_ldap_ldap_server_block: allocated server block at 0x%p", (void *)server); + ngx_memzero(server, sizeof(*server)); server->connect_timeout = 10000; server->reconnect_timeout = 10000; @@ -365,7 +440,10 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c server->request_timeout = 10000; server->alias = name; server->referral = 1; - + server->search_attributes = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); + server->attribute_header_prefix.len = sizeof(LDAP_ATTR_HEADER_DEFAULT_PREFIX) -1; + server->attribute_header_prefix.data = (u_char *)LDAP_ATTR_HEADER_DEFAULT_PREFIX; + server->attrs = NULL; save = *cf; cf->handler = ngx_http_auth_ldap_ldap_server; cf->handler_conf = conf; @@ -404,6 +482,7 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) server = ((ngx_http_auth_ldap_server_t *) cnf->servers->elts + (cnf->servers->nelts - 1)); value = cf->args->elts; + //ngx_conf_log_error(NGX_LOG_DEBUG | NGX_LOG_DEBUG_HTTP, cf, 0,"http_auth_ldap: Configuring elt:%s with %d parameters", value[0].data, cf->args->nelts); /* TODO: Add more validation */ if (ngx_strcmp(value[0].data, "url") == 0) { @@ -416,6 +495,17 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) server->group_attribute = value[1]; } else if (ngx_strcmp(value[0].data, "group_attribute_is_dn") == 0 && ngx_strcmp(value[1].data, "on") == 0) { server->group_attribute_dn = 1; + } else if (ngx_strcmp(value[0].data, "search_attribute") == 0 && cf->args->nelts >= 2) { + ngx_str_t *attr = ngx_array_push(server->search_attributes); + *attr = value[1]; + //ngx_str_null(attr); + //attr->len = value[1].len; + //attr->data = ngx_pnalloc(cf->pool, attr->len +1); + //ngx_memcpy(attr->data, value[1].data, value[1].len); + //attr->data[value[1].len] = '\0'; // Ensure null terminated string + } else if (ngx_strcmp(value[0].data, "attribute_header_prefix") == 0 && cf->args->nelts >= 2) { + santitize_str(value[1].data, SANITIZE_NO_CONV); // The prefix is sanitized + server->attribute_header_prefix = value[1]; } else if (ngx_strcmp(value[0].data, "require") == 0) { return ngx_http_auth_ldap_parse_require(cf, server); } else if (ngx_strcmp(value[0].data, "satisfy") == 0) { @@ -518,12 +608,11 @@ ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) for (i = 1; i < cf->args->nelts; i++) { value = &((ngx_str_t *) cf->args->elts)[i]; server = NULL; - - if (mconf->servers == NULL) { + if (mconf->servers == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Using \"auth_ldap_servers\" when no \"ldap_server\" has been previously defined" - " (make sure that \"auth_ldap_servers\" goes after \"ldap_server\"s in your configuration file)", value); + " (make sure that \"auth_ldap_servers\" goes after \"ldap_server\"s in your configuration file)", value); return NGX_CONF_ERROR; - } + } for (j = 0; j < mconf->servers->nelts; j++) { s = &((ngx_http_auth_ldap_server_t *) mconf->servers->elts)[j]; @@ -557,6 +646,33 @@ ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } +/** + * Parse resolver directive + */ +static char * +ngx_http_auth_ldap_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_auth_ldap_main_conf_t *cnf = conf; + ngx_str_t *value; + + if (cnf->resolver) { + return "is duplicate"; + } + + value = cf->args->elts; + + cnf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); + if (cnf->resolver == NULL) { + return NGX_CONF_ERROR; + } + + ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, + "http_auth_ldap: ngx_http_auth_ldap_resolver: Configured resolver %V", + &value[1]); + + return NGX_CONF_OK; +} + /** * Parse URL conf parameter */ @@ -629,6 +745,7 @@ ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server server->parsed_url.url.data = (u_char *) server->ludpp->lud_host; server->parsed_url.url.len = ngx_strlen(server->ludpp->lud_host); server->parsed_url.default_port = server->ludpp->lud_port; + server->parsed_url.no_resolve = 1; // Do not use hostanme resolution checking during configuration parsing if (ngx_parse_url(cf->pool, &server->parsed_url) != NGX_OK) { if (server->parsed_url.err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: %s in LDAP hostname \"%V\"", @@ -636,7 +753,7 @@ ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server } return NGX_CONF_ERROR; } - + server->parsed_url.no_resolve = 0; // Reset the no_resolve flag if (ngx_strcmp(server->ludpp->lud_scheme, "ldap") == 0) { return NGX_CONF_OK; #if (NGX_OPENSSL) @@ -668,7 +785,7 @@ ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *se ngx_http_compile_complex_value_t ccv; value = cf->args->elts; - ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, "http_auth_ldap: parse_require"); + //ngx_conf_log_error(NGX_LOG_DEBUG | NGX_LOG_DEBUG_HTTP, cf, 0, "http_auth_ldap: parse_require"); if (ngx_strcmp(value[1].data, "valid_user") == 0) { server->require_valid_user = 1; @@ -760,6 +877,8 @@ ngx_http_auth_ldap_parse_referral(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *s return NGX_CONF_ERROR; } + + /** * Create main config which will store ldap_servers array */ @@ -773,10 +892,13 @@ ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf) return NULL; } + conf->cnf_pool = cf->pool; conf->cache_enabled = NGX_CONF_UNSET; conf->cache_expiration_time = NGX_CONF_UNSET_MSEC; conf->cache_size = NGX_CONF_UNSET_SIZE; conf->servers_size = NGX_CONF_UNSET; + conf->resolver_timeout = NGX_CONF_UNSET_MSEC; + conf->resolver = NULL; return conf; } @@ -786,6 +908,10 @@ ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent) { ngx_http_auth_ldap_main_conf_t *conf = parent; + if (conf->resolver_timeout == NGX_CONF_UNSET_MSEC) { + conf->resolver_timeout = 10000; + } + if (conf->cache_enabled == NGX_CONF_UNSET) { conf->cache_enabled = 0; } @@ -854,6 +980,11 @@ ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle) return NGX_OK; } + rc = ngx_http_auth_ldap_init_servers(cycle); + if (rc != NGX_OK) { + return rc; + } + rc = ngx_http_auth_ldap_init_cache(cycle); if (rc != NGX_OK) { return rc; @@ -1276,6 +1407,8 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) ngx_int_t rc; struct berval cred; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connection established"); + conn = c->conn.connection; ngx_del_timer(conn->read); conn->write->handler = ngx_http_auth_ldap_dummy_write_handler; @@ -1338,6 +1471,8 @@ ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn, ngx_flag_t vali ngx_http_auth_ldap_connection_t *c; c = conn->data; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: SSL handshake handler (validate=%d)", validate); + if (conn->ssl->handshaked) { #if OPENSSL_VERSION_NUMBER >= 0x10002000 if (validate) { // verify remote certificate if requested @@ -1417,6 +1552,8 @@ ngx_http_auth_ldap_ssl_handshake(ngx_http_auth_ldap_connection_t *c) { ngx_int_t rc; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: SSL handshake"); + c->conn.connection->pool = c->pool; rc = ngx_ssl_create_connection(c->ssl, c->conn.connection, NGX_SSL_BUFFER | NGX_SSL_CLIENT); if (rc != NGX_OK) { @@ -1637,6 +1774,39 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) ldap_memfree(dn); } } + /* Iterate through each attribute in the entry. */ + BerElement *ber = NULL; + char *attr = NULL; + struct berval **vals = NULL; + ngx_table_elt_t *h; + for (attr = ldap_first_attribute(c->ld, result, &ber); + attr != NULL; + attr = ldap_next_attribute(c->ld, result, ber)) { + /* Get only first value for each attribute. */ + if ((vals = ldap_get_values_len(c->ld, result, attr)) != NULL ) { + if(vals[0] != NULL) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received attribute %s: %s", attr, vals[0]->bv_val); + h = ngx_list_push(&c->rctx->r->headers_out.headers); + if (h != NULL) { + santitize_str((u_char *)attr, SANITIZE_NO_CONV); + int attr_len = strlen(attr); + h->hash = 1; + h->key.len = c->server->attribute_header_prefix.len + attr_len; + h->key.data = ngx_pnalloc(c->rctx->r->pool, h->key.len); + unsigned char *p = ngx_cpymem(h->key.data, c->server->attribute_header_prefix.data, c->server->attribute_header_prefix.len); + p = ngx_cpymem(p, attr, attr_len); + h->value.len = vals[0]->bv_len; + h->value.data = ngx_pnalloc(c->rctx->r->pool, h->value.len); + ngx_memcpy(h->value.data, vals[0]->bv_val, h->value.len); + } + } + ldap_value_free_len(vals); + } + ldap_memfree(attr); + } + if (ber != NULL) { + ber_free(ber, 0); + } } else if (ldap_msgtype(result) == LDAP_RES_SEARCH_RESULT) { ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received search result (%d: %s [%s])", error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); @@ -1669,16 +1839,78 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) static void ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) +{ + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: server \"%V\" (naddrs=%d).", + &c->server->alias, c->server->parsed_url.naddrs); + if (c->server->parsed_url.naddrs == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: No addr for server, re-parse url"); + c->server->parsed_url.no_resolve = 0; // Try to resolve this time + if (ngx_parse_url(c->pool, &c->server->parsed_url) != NGX_OK) { + ngx_log_error(NGX_LOG_WARN, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: Hostname \"%V\" not found with system resolver, try with DNS", + &c->server->parsed_url.host); + // Try to resolve the hostname through the resolver + if (c->main_cnf->resolver == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: No resolver configured"); + return; + } + ngx_resolver_ctx_t *resolver_ctx, temp; + temp.name = c->server->parsed_url.host; + resolver_ctx = ngx_resolve_start(c->main_cnf->resolver, &temp); + if (resolver_ctx == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: ngx_http_auth_ldap_connect: Unable to start the resolver"); + return; + } + if (resolver_ctx == NGX_NO_RESOLVER) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: No resolver defined to resolve %V", c->server->parsed_url.host); + return; + } + resolver_ctx->name = c->server->parsed_url.host; + resolver_ctx->handler = ngx_http_auth_ldap_resolve_handler; + resolver_ctx->data = c; + resolver_ctx->timeout = c->main_cnf->resolver_timeout; + c->resolver_ctx = resolver_ctx; + if (ngx_resolve_name(resolver_ctx) != NGX_OK) { + c->resolver_ctx = NULL; + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: Resolve %V failed", c->server->parsed_url.host); + return; + } + // The DNS Querry has been triggered. Let the ngx_http_auth_ldap_resolve_handler now take the control of the flow. + return; + } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: server \"%V\" (naddrs is now %d).", + &c->server->alias, c->server->parsed_url.naddrs); + } + + // Continue with the rest of the connection establishement + ngx_http_auth_ldap_connect_continue(c); +} + +static void +ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c) { ngx_peer_connection_t *pconn; ngx_connection_t *conn; ngx_addr_t *addr; ngx_int_t rc; + if (c->server->parsed_url.naddrs == 0) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect_continue: No addr for server"); + return; + } + addr = &c->server->parsed_url.addrs[ngx_random() % c->server->parsed_url.naddrs]; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connecting to LDAP server \"%V\".", - &addr->name); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http_auth_ldap: Connecting to LDAP server IP@ %V", + &addr->name); pconn = &c->conn; pconn->sockaddr = addr->sockaddr; @@ -1725,6 +1957,125 @@ ngx_http_auth_ldap_reconnect_handler(ngx_event_t *ev) ngx_http_auth_ldap_connect(c); } +/* Duplicated from ngnx_inet.c (as it is a static function in Nginx) */ +static ngx_int_t +my_ngx_inet_add_addr(ngx_pool_t *pool, ngx_url_t *u, struct sockaddr *sockaddr, + socklen_t socklen, ngx_uint_t total) +{ + u_char *p; + size_t len; + ngx_uint_t i, nports; + ngx_addr_t *addr; + struct sockaddr *sa; + + nports = u->last_port ? u->last_port - u->port + 1 : 1; + if (u->addrs == NULL) { + u->addrs = ngx_palloc(pool, total * nports * sizeof(ngx_addr_t)); + if (u->addrs == NULL) { + return NGX_ERROR; + } + } + for (i = 0; i < nports; i++) { + sa = ngx_pcalloc(pool, socklen); + if (sa == NULL) { + return NGX_ERROR; + } + ngx_memcpy(sa, sockaddr, socklen); + ngx_inet_set_port(sa, u->port + i); + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + len = NGX_INET6_ADDRSTRLEN + sizeof("[]:65536") - 1; + break; +#endif + + default: /* AF_INET */ + len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; + } + p = ngx_pnalloc(pool, len); + if (p == NULL) { + return NGX_ERROR; + } + len = ngx_sock_ntop(sa, socklen, p, len, 1); + addr = &u->addrs[u->naddrs++]; + addr->sockaddr = sa; + addr->socklen = socklen; + addr->name.len = len; + addr->name.data = p; + } + return NGX_OK; +} + +static void ngx_http_auth_ldap_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_http_auth_ldap_connection_t *c = ctx->data; + ngx_uint_t i; + ngx_resolver_addr_t *res_addr; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_resolve_handler: server \"%*s\".", + c->server->alias.len, c->server->alias.data); + + if (ctx->state) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_resolve_handler: %V could not be resolved (%i: %s)", + &ctx->name, ctx->state, ngx_resolver_strerror(ctx->state)); + return; + } + ngx_resolve_name_done(ctx); + c->resolver_ctx = NULL; + + // Update the parsed_url with the addresses resolved by DNS + for (i = 0, res_addr = ctx->addrs; res_addr != NULL && i < ctx->naddrs; i++, res_addr++) { + my_ngx_inet_add_addr(c->main_cnf->cnf_pool, &c->server->parsed_url, + res_addr->sockaddr, res_addr->socklen, ctx->naddrs); + } + + // Go on the the rest of the connection establishment + ngx_http_auth_ldap_connect_continue(c); +} + +/** + * Finalize servers configuration (at worker initialization) + */ +static ngx_int_t +ngx_http_auth_ldap_init_servers(ngx_cycle_t *cycle) +{ + ngx_http_auth_ldap_main_conf_t *halmcf; + ngx_http_auth_ldap_server_t *server; + ngx_uint_t i, j, attrs_count; + + halmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); + if (halmcf == NULL || halmcf->servers == NULL) { + return NGX_OK; + } + + for (i = 0; i < halmcf->servers->nelts; i++) { + server = &((ngx_http_auth_ldap_server_t *) halmcf->servers->elts)[i]; + + // Prepare the list of attributes (if any) to fetch during the ldap_search (a null terminated list of char* attribute names) + attrs_count = server->search_attributes->nelts; + + if (attrs_count == 0) { + // No search attributes specified + server->attrs = ngx_palloc(cycle->pool, 2 * sizeof(char *)); + server->attrs[0] = LDAP_NO_ATTRS; + attrs_count = 1; + } else { + server->attrs = ngx_palloc(cycle->pool, (attrs_count + 1) * sizeof(char *)); + ngx_str_t *search_attr_list = server->search_attributes->elts; + for (j = 0; j < attrs_count; j++) { + server->attrs[j] = (char *)search_attr_list[j].data; + } + } + server->attrs[attrs_count] = NULL; + } + + return NGX_OK; +} + + static ngx_int_t ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) { @@ -1738,7 +2089,7 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) halmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); if (halmcf == NULL || halmcf->servers == NULL) { - return NGX_OK; + return NGX_OK; } option = LDAP_VERSION3; @@ -1764,6 +2115,7 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) cleanup->data = c; c->log = cycle->log; + c->main_cnf = halmcf; c->server = server; c->state = STATE_DISCONNECTED; @@ -2084,7 +2436,6 @@ ngx_http_auth_ldap_search(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) { LDAPURLDesc *ludpp; u_char *filter; - char *attrs[2]; ngx_int_t rc; /* On the first call, initiate the LDAP search operation */ @@ -2104,10 +2455,7 @@ ngx_http_auth_ldap_search(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Search filter is \"%s\"", (const char *) filter); - attrs[0] = LDAP_NO_ATTRS; - attrs[1] = NULL; - - rc = ldap_search_ext(ctx->c->ld, ludpp->lud_dn, ludpp->lud_scope, (const char *) filter, attrs, 0, NULL, NULL, NULL, 0, &ctx->c->msgid); + rc = ldap_search_ext(ctx->c->ld, ludpp->lud_dn, ludpp->lud_scope, (const char *) filter, ctx->server->attrs, 0, NULL, NULL, NULL, 0, &ctx->c->msgid); if (rc != LDAP_SUCCESS) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() failed (%d, %s)", rc, ldap_err2string(rc)); From f6790b3dfa2bcff532f9c2e5a6ce0b95d43b0b5f Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Wed, 10 Aug 2022 09:35:53 +0200 Subject: [PATCH 06/15] - Fix resolver issues and use resolver at each connection (re-)establishement - Replaces list of 'search_attribute XXX' parameter by 'search_attributes XXX [YYY [ ... ]]' - Support additional/optional 'encoding' param for 'binddn_passwd' parameter --- README.md | 14 +- example.conf | 10 +- ngx_http_auth_ldap_module.c | 256 ++++++++++++++++++++++++++---------- 3 files changed, 199 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index c5d883c..650c7cb 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ The reasons for this fork are: * Add new features * Add the use of `resolver` to resolve hostname of the LDAP server * Support LDAP attributes fecthing during search + * Added an `encoding` attribute to the binddn_passwd parameter ## How to install @@ -177,12 +178,16 @@ url format: ldap[s]://host[:port]/dn?attrs?scope?filter[?exts] * Default: -- * Context: `ldap_server` block +The DN for the initial bind + ### binddn_passwd -* Syntax: binddn_passwd _password_; +* Syntax: binddn_passwd _password_ [text | base64 | hex]; * Default: -- * Context: `ldap_server` block +The initial bind password. can be encoded in clear text (the default) or be encoded in base64 or HEX representation + ### group_attribute * Syntax: group attr; @@ -279,12 +284,11 @@ to read write. The prefix for the HEADER names used to carry the feteched attributes (default: "X-LDAP-ATTRS-") -### search_attribute +### search_attributes -* Syntax: search_attribute _attr_; +* Syntax: search_attributes _attr1_ [ [ _attr2_ ] ... [ _attrN_ ] ]; * Default: -- * Context: `ldap_server` block -Add this LDAP attribute description for the search (require valid-user or require user). The attribute value will be return as a HTTP header () in the authentication response. +Space delimited list of LDAP attribute descriptions to include in the search (require valid-user or require user). Each attribute value will be return as a HTTP header () in the authentication response. -_Note_: This parameter can be repeated several times when several attributes need to be fetched diff --git a/example.conf b/example.conf index 610c9fc..2cee536 100644 --- a/example.conf +++ b/example.conf @@ -21,11 +21,11 @@ http { # bind as binddn "CN=Operator,OU=Service Accounts,DC=company,DC=com"; # bind pw - binddn_passwd ; - # Select attributes to be retrieved during the search (several are possible) - search_attribute mail; - search_attribute sn; - search_attribute givenName; + binddn_passwd ""; + # Or binddn_passwd "base64()" base64; + # Or binddn_passwd "hex()" hex; + # Select attributes to be retrieved during the search (space separated list of attributes) + search_attributes mail sn givenName; # group attribute name which contains member object group_attribute member; # search for full DN in member object diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 8889494..6682e2c 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -80,6 +80,12 @@ extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); #define SANITIZE_TO_UPPER 1 #define SANITIZE_TO_LOWER 2 +#define ENCODING_TEXT 0 +#define ENCODING_B64 1 +#define ENCODING_HEX 2 + +#define MAX_ATTRS_COUNT 5 /* Maximum search attributes to display in log */ + typedef struct { LDAPURLDesc *ludpp; @@ -114,7 +120,6 @@ typedef struct { ngx_queue_t free_connections; ngx_queue_t waiting_requests; - ngx_array_t *search_attributes; // Search attributes from configuration parsing char **attrs; // Search attributes formated for ldap_search_ext() ngx_str_t attribute_header_prefix; } ngx_http_auth_ldap_server_t; @@ -221,6 +226,7 @@ static char * ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static char * ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_auth_ldap_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); +static char * ngx_http_auth_ldap_parse_binddn_passwd(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); static char * ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); static char * ngx_http_auth_ldap_parse_referral(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); @@ -251,6 +257,9 @@ static ngx_int_t ngx_http_auth_ldap_recover_bind(ngx_http_request_t *r, ngx_http static ngx_int_t ngx_http_auth_ldap_restore_handlers(ngx_connection_t *conn); #endif static u_char * santitize_str(u_char *str, ngx_uint_t conv); +static ngx_uint_t my_hex_digit_value(u_char c); +static ngx_uint_t my_hex_decode(ngx_str_t *dst, ngx_str_t *src); +static void my_free_addrs_from_url(ngx_pool_t *pool, ngx_url_t *u); ngx_http_auth_ldap_cache_t ngx_http_auth_ldap_cache; @@ -392,6 +401,36 @@ static u_char * santitize_str(u_char *str, ngx_uint_t conv) { return str; // Fluent interface } + +static ngx_uint_t +my_hex_digit_value(u_char c) +{ + if (c >= '0' && c <= '9') { + return (ngx_uint_t)(c - '0'); + } + if (c >= 'a' && c <= 'f') { + return (ngx_uint_t)(c - 'a' + 10); + } + if (c >= 'A' && c <= 'F') { + return (ngx_uint_t)(c - 'A' + 10); + } + return 0; // Not an hex digit +} + +static ngx_uint_t +my_hex_decode(ngx_str_t *dst, ngx_str_t *src) +{ + u_char *s, *d; + ngx_uint_t i; + + for (i = 0, s = src->data, d = dst->data ; i < src->len -1; i += 2, s += 2) { + *d++ = (u_char)(16 * my_hex_digit_value(*s) + my_hex_digit_value(*(s + 1))); + } + + dst->len = (d - dst->data); + return (ngx_uint_t)dst->len; +} + /*** Configuration and initialization ***/ /** @@ -423,7 +462,6 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c if (cnf->servers == NULL) { return NGX_CONF_ERROR; } - ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "http_auth_ldap: ngx_http_auth_ldap_ldap_server_block: Allocated servers array (capacity=%d) in main conf at addr=0x%p", cnf->servers_size, (void *)cnf->servers); } server = ngx_array_push(cnf->servers); @@ -431,8 +469,6 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c return NGX_CONF_ERROR; } - ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "http_auth_ldap: ngx_http_auth_ldap_ldap_server_block: allocated server block at 0x%p", (void *)server); - ngx_memzero(server, sizeof(*server)); server->connect_timeout = 10000; server->reconnect_timeout = 10000; @@ -440,7 +476,6 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c server->request_timeout = 10000; server->alias = name; server->referral = 1; - server->search_attributes = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); server->attribute_header_prefix.len = sizeof(LDAP_ATTR_HEADER_DEFAULT_PREFIX) -1; server->attribute_header_prefix.data = (u_char *)LDAP_ATTR_HEADER_DEFAULT_PREFIX; server->attrs = NULL; @@ -482,7 +517,6 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) server = ((ngx_http_auth_ldap_server_t *) cnf->servers->elts + (cnf->servers->nelts - 1)); value = cf->args->elts; - //ngx_conf_log_error(NGX_LOG_DEBUG | NGX_LOG_DEBUG_HTTP, cf, 0,"http_auth_ldap: Configuring elt:%s with %d parameters", value[0].data, cf->args->nelts); /* TODO: Add more validation */ if (ngx_strcmp(value[0].data, "url") == 0) { @@ -490,19 +524,18 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) } else if (ngx_strcmp(value[0].data, "binddn") == 0) { server->bind_dn = value[1]; } else if (ngx_strcmp(value[0].data, "binddn_passwd") == 0) { - server->bind_dn_passwd = value[1]; + return ngx_http_auth_ldap_parse_binddn_passwd(cf, server); } else if (ngx_strcmp(value[0].data, "group_attribute") == 0) { server->group_attribute = value[1]; } else if (ngx_strcmp(value[0].data, "group_attribute_is_dn") == 0 && ngx_strcmp(value[1].data, "on") == 0) { server->group_attribute_dn = 1; - } else if (ngx_strcmp(value[0].data, "search_attribute") == 0 && cf->args->nelts >= 2) { - ngx_str_t *attr = ngx_array_push(server->search_attributes); - *attr = value[1]; - //ngx_str_null(attr); - //attr->len = value[1].len; - //attr->data = ngx_pnalloc(cf->pool, attr->len +1); - //ngx_memcpy(attr->data, value[1].data, value[1].len); - //attr->data[value[1].len] = '\0'; // Ensure null terminated string + } else if (ngx_strcmp(value[0].data, "search_attributes") == 0 && cf->args->nelts >= 2) { + ngx_uint_t j, attrs_count = cf->args->nelts -1; + server->attrs = ngx_palloc(cf->pool, (attrs_count + 1) * sizeof(char *)); + for (j = 0; j < attrs_count; j++) { + server->attrs[j] = (char *)value[j + 1].data; + } + server->attrs[attrs_count] = NULL; // Last element of the list } else if (ngx_strcmp(value[0].data, "attribute_header_prefix") == 0 && cf->args->nelts >= 2) { santitize_str(value[1].data, SANITIZE_NO_CONV); // The prefix is sanitized server->attribute_header_prefix = value[1]; @@ -774,6 +807,55 @@ ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server } } +static char * +ngx_http_auth_ldap_parse_binddn_passwd(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) +{ + ngx_str_t *value; + ngx_int_t encoding = ENCODING_TEXT; + + value = cf->args->elts; + if (cf->args->nelts < 2 || cf->args->nelts > 3) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Bad number of parameters for binddn_passwd"); + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 3 && value[1].len != 0) { + // Non empty password and have a second parameter value (encoding) + if (ngx_strcmp(value[2].data, "text") == 0) { + encoding = ENCODING_TEXT; + } else if (ngx_strcmp(value[2].data, "base64") == 0) { + encoding = ENCODING_B64; + } else if (ngx_strcmp(value[2].data, "hex") == 0) { + encoding = ENCODING_HEX; + } else { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "http_auth_ldap: Unknown encoding ('%V') for binddn_password. (assumming clear text)", + value[2]); + } + } + + if (encoding == ENCODING_TEXT) { + // Just copy reference + server->bind_dn_passwd = value[1]; + } else if (encoding == ENCODING_B64) { + // Base 64 decode + server->bind_dn_passwd.len = 0; + int decoded_length = 3 * value[1].len / 4; + server->bind_dn_passwd.data = ngx_pnalloc(cf->pool, decoded_length + 1); + ngx_decode_base64(&server->bind_dn_passwd, &value[1]); + server->bind_dn_passwd.data[decoded_length] = '\0'; + } else if (encoding == ENCODING_HEX) { + // Hex decode + server->bind_dn_passwd.len = 0; + int decoded_length = value[1].len / 2; + server->bind_dn_passwd.data = ngx_pnalloc(cf->pool, decoded_length + 1); + my_hex_decode(&server->bind_dn_passwd, &value[1]); + server->bind_dn_passwd.data[decoded_length] = '\0'; + } + + return NGX_CONF_OK; +} + /** * Parse "require" conf parameter */ @@ -1840,53 +1922,54 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) static void ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: server \"%V\" (naddrs=%d).", - &c->server->alias, c->server->parsed_url.naddrs); - if (c->server->parsed_url.naddrs == 0) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: No addr for server, re-parse url"); - c->server->parsed_url.no_resolve = 0; // Try to resolve this time - if (ngx_parse_url(c->pool, &c->server->parsed_url) != NGX_OK) { - ngx_log_error(NGX_LOG_WARN, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: Hostname \"%V\" not found with system resolver, try with DNS", - &c->server->parsed_url.host); - // Try to resolve the hostname through the resolver - if (c->main_cnf->resolver == NULL) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: No resolver configured"); - return; - } - ngx_resolver_ctx_t *resolver_ctx, temp; - temp.name = c->server->parsed_url.host; - resolver_ctx = ngx_resolve_start(c->main_cnf->resolver, &temp); - if (resolver_ctx == NULL) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: ngx_http_auth_ldap_connect: Unable to start the resolver"); - return; - } - if (resolver_ctx == NGX_NO_RESOLVER) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: No resolver defined to resolve %V", c->server->parsed_url.host); - return; - } - resolver_ctx->name = c->server->parsed_url.host; - resolver_ctx->handler = ngx_http_auth_ldap_resolve_handler; - resolver_ctx->data = c; - resolver_ctx->timeout = c->main_cnf->resolver_timeout; - c->resolver_ctx = resolver_ctx; - if (ngx_resolve_name(resolver_ctx) != NGX_OK) { - c->resolver_ctx = NULL; - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: Resolve %V failed", c->server->parsed_url.host); - return; - } - // The DNS Querry has been triggered. Let the ngx_http_auth_ldap_resolve_handler now take the control of the flow. + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: Server \"%V\".", + &c->server->alias); + + // Clear an free any previous addrs from parsed_url, so that we can resolve again the LDAP server hostname + my_free_addrs_from_url(c->main_cnf->cnf_pool, &c->server->parsed_url); + + c->server->parsed_url.no_resolve = 0; // Try to resolve this time + if (ngx_parse_url(c->pool, &c->server->parsed_url) != NGX_OK) { + ngx_log_error(NGX_LOG_WARN, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: Hostname \"%V\" not found with system resolver, try with DNS", + &c->server->parsed_url.host); + // Try to resolve the hostname through the resolver + if (c->main_cnf->resolver == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: No resolver configured"); + return; + } + ngx_resolver_ctx_t *resolver_ctx, temp; + temp.name = c->server->parsed_url.host; + resolver_ctx = ngx_resolve_start(c->main_cnf->resolver, &temp); + if (resolver_ctx == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: ngx_http_auth_ldap_connect: Unable to start the resolver"); + return; + } + if (resolver_ctx == NGX_NO_RESOLVER) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: No resolver defined to resolve %V", + c->server->parsed_url.host); + return; + } + resolver_ctx->name = c->server->parsed_url.host; + resolver_ctx->handler = ngx_http_auth_ldap_resolve_handler; + resolver_ctx->data = c; + resolver_ctx->timeout = c->main_cnf->resolver_timeout; + c->resolver_ctx = resolver_ctx; + if (ngx_resolve_name(resolver_ctx) != NGX_OK) { + c->resolver_ctx = NULL; + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: Resolve %V failed", c->server->parsed_url.host); return; } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: server \"%V\" (naddrs is now %d).", - &c->server->alias, c->server->parsed_url.naddrs); + // The DNS Querry has been triggered. Let the ngx_http_auth_ldap_resolve_handler now take the control of the flow. + return; } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_connect: server \"%V\" (naddrs is now %d).", + &c->server->alias, c->server->parsed_url.naddrs); // Continue with the rest of the connection establishement ngx_http_auth_ldap_connect_continue(c); @@ -1957,6 +2040,39 @@ ngx_http_auth_ldap_reconnect_handler(ngx_event_t *ev) ngx_http_auth_ldap_connect(c); } +static void +my_free_addrs_from_url(ngx_pool_t *pool, ngx_url_t *u) +{ + ngx_addr_t *addr; + ngx_uint_t i; + + if (u == NULL || u->addrs == NULL) { + return; + } + + for (i = 0; i < u->naddrs; i++) { + addr = &u->addrs[i]; + if (addr != NULL) { + if (addr->name.data != NULL) { + ngx_pfree(pool, addr->name.data); + addr->name.data = NULL; + addr->name.len = 0; + } + if (addr->sockaddr != NULL) { + ngx_pfree(pool, addr->sockaddr); + addr->sockaddr = NULL; + addr->socklen = 0; + } + } + } + + if (u->addrs != NULL) { + ngx_pfree(pool, u->addrs); + u->addrs = NULL; + u->naddrs = 0; + } +} + /* Duplicated from ngnx_inet.c (as it is a static function in Nginx) */ static ngx_int_t my_ngx_inet_add_addr(ngx_pool_t *pool, ngx_url_t *u, struct sockaddr *sockaddr, @@ -2026,6 +2142,9 @@ static void ngx_http_auth_ldap_resolve_handler(ngx_resolver_ctx_t *ctx) ngx_resolve_name_done(ctx); c->resolver_ctx = NULL; + // Clear and free any previous addrs in parsed_url + my_free_addrs_from_url(c->main_cnf->cnf_pool, &c->server->parsed_url); + // Update the parsed_url with the addresses resolved by DNS for (i = 0, res_addr = ctx->addrs; res_addr != NULL && i < ctx->naddrs; i++, res_addr++) { my_ngx_inet_add_addr(c->main_cnf->cnf_pool, &c->server->parsed_url, @@ -2044,7 +2163,7 @@ ngx_http_auth_ldap_init_servers(ngx_cycle_t *cycle) { ngx_http_auth_ldap_main_conf_t *halmcf; ngx_http_auth_ldap_server_t *server; - ngx_uint_t i, j, attrs_count; + ngx_uint_t i, j; halmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); if (halmcf == NULL || halmcf->servers == NULL) { @@ -2054,22 +2173,17 @@ ngx_http_auth_ldap_init_servers(ngx_cycle_t *cycle) for (i = 0; i < halmcf->servers->nelts; i++) { server = &((ngx_http_auth_ldap_server_t *) halmcf->servers->elts)[i]; - // Prepare the list of attributes (if any) to fetch during the ldap_search (a null terminated list of char* attribute names) - attrs_count = server->search_attributes->nelts; - - if (attrs_count == 0) { - // No search attributes specified + if (server->attrs == NULL) { + // No search attributes specified, so setup an empty attributes list + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "http_auth_ldap: Server '%V': No search_attributes", &server->alias); server->attrs = ngx_palloc(cycle->pool, 2 * sizeof(char *)); server->attrs[0] = LDAP_NO_ATTRS; - attrs_count = 1; + server->attrs[1] = NULL; } else { - server->attrs = ngx_palloc(cycle->pool, (attrs_count + 1) * sizeof(char *)); - ngx_str_t *search_attr_list = server->search_attributes->elts; - for (j = 0; j < attrs_count; j++) { - server->attrs[j] = (char *)search_attr_list[j].data; + for (j = 0; server->attrs[j] != NULL && j < MAX_ATTRS_COUNT; j++) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "http_auth_ldap: Server '%V': search_attribute[%d] = '%s'", &server->alias, j, server->attrs[j]); } } - server->attrs[attrs_count] = NULL; } return NGX_OK; From 221529ce444ba833150edc572225ade93d820345 Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Thu, 8 Sep 2022 13:50:26 +0200 Subject: [PATCH 07/15] Support Nginx 1.23 new headers list data structure --- ngx_http_auth_ldap_module.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 6682e2c..27f40f6 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -1873,6 +1873,9 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) santitize_str((u_char *)attr, SANITIZE_NO_CONV); int attr_len = strlen(attr); h->hash = 1; +#if (nginx_version >= 102300) + h->next = NULL; +#endif h->key.len = c->server->attribute_header_prefix.len + attr_len; h->key.data = ngx_pnalloc(c->rctx->r->pool, h->key.len); unsigned char *p = ngx_cpymem(h->key.data, c->server->attribute_header_prefix.data, c->server->attribute_header_prefix.len); @@ -2269,6 +2272,9 @@ ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm) } r->headers_out.www_authenticate->hash = 1; +#if (nginx_version >= 102300) + r->headers_out.www_authenticate->next = NULL; +#endif r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1; r->headers_out.www_authenticate->key.data = (u_char *) "WWW-Authenticate"; r->headers_out.www_authenticate->value = *realm; From 90708be75098dd9ac6ad0e9e8f1c86159452f409 Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Fri, 9 Sep 2022 15:23:33 +0200 Subject: [PATCH 08/15] - Resurect pending reconnect connections when needed - Fixes support of Nginx 1.23.0 --- README.md | 43 +++++++++++++++++-------- ngx_http_auth_ldap_module.c | 62 +++++++++++++++++++++++++++++++++---- 2 files changed, 86 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 650c7cb..0506d58 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,17 @@ This project is a clone of [nginx-auth-ldap](https://github.com/kvspb/nginx-auth The reasons for this fork are: -* The original project seems abondonned (no commit since 2 years) -* Inherit from other contributors fixes/features - * [Pull request #237](https://github.com/kvspb/nginx-auth-ldap/pull/237) from [mmguero-dev](https://github.com/mmguero-dev/nginx-auth-ldap) -* Add new features - * Add the use of `resolver` to resolve hostname of the LDAP server - * Support LDAP attributes fecthing during search - * Added an `encoding` attribute to the binddn_passwd parameter +* The original project seems abondonned (no commit since 2 years). +* Inherit from other contributors fixes/features: + * [Pull request #237](https://github.com/kvspb/nginx-auth-ldap/pull/237) from [mmguero-dev](https://github.com/mmguero-dev/nginx-auth-ldap). + * Compatible with Nginx 1.23.0 (http headers are now linked). +* Add new features: + * Add the use of `resolver` to resolve hostname of the LDAP server. + * Support LDAP attributes fecthing during search. + * Added an `encoding` attribute to the binddn_passwd parameter. + * Manage connections waiting a reconnect delay in a specific queue, so that we can + cancel the reconnect delay when a new request ask for an authentication and no free + connection is available. ## How to install @@ -101,10 +105,10 @@ And add required servers in correct order into your location/server directive: ### auth_ldap_cache_expiration_time * Syntax: auth_ldap_cache_expiration_time time; -* Default: auth_ldap_cache_expiration_time 10000; +* Default: auth_ldap_cache_expiration_time 10s; * Context: http -Cache expiration time (in ms). +Cache expiration time (see for time intervals syntax). ### auth_ldap_cache_size @@ -151,10 +155,10 @@ See the `resolver` directive of the **ngx_http_core_module** ### auth_ldap_resolver_timeout * Syntax: auth_ldap_resolver_timeout time; -* Default: auth_ldap_resolver_timeout 10000; +* Default: auth_ldap_resolver_timeout 10s; * Context: http -Resolver requests timeout (in ms). +Resolver requests timeout (see for time intervals syntax). ### ldap_server @@ -208,7 +212,6 @@ Tell to search for full DN in member object. * Default: --; * Context: `ldap_server` block - ### satisfy * Syntax: satisfy all | any; @@ -231,7 +234,6 @@ This can usually help with the following error: http_auth_ldap: ldap_result() failed (-1: Can't contact LDAP server) ``` - ### ssl_check_cert * Syntax: ssl_check_cert on | chain | off; @@ -292,3 +294,18 @@ The prefix for the HEADER names used to carry the feteched attributes (default: Space delimited list of LDAP attribute descriptions to include in the search (require valid-user or require user). Each attribute value will be return as a HTTP header () in the authentication response. +### reconnect_timeout + +* Syntax: reconnect_timeout _timespec_; +* Default: reconnect_timeout 10s; +* Context: `ldap_server` block + +The delay before reconnection attempts (see for _timespec_ syntax) + +### connections + +* Syntax: connections _count_; +* Default: connections 1; +* Context: `ldap_server` block + +The number of connections to the server use in // diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 27f40f6..1b005af 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -120,6 +120,7 @@ typedef struct { ngx_queue_t free_connections; ngx_queue_t waiting_requests; + ngx_queue_t pending_connections; char **attrs; // Search attributes formated for ldap_search_ext() ngx_str_t attribute_header_prefix; } ngx_http_auth_ldap_server_t; @@ -212,6 +213,7 @@ typedef struct ngx_http_auth_ldap_connection { #endif ngx_queue_t queue; + ngx_queue_t queue_pending; /* pendings connection (waiting re-connect) */ ngx_http_auth_ldap_ctx_t *rctx; LDAP* ld; @@ -238,6 +240,7 @@ static ngx_int_t ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle); static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); static ngx_int_t ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle); static void ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c); +static void ngx_http_auth_ldap_set_pending_connection(ngx_http_auth_ldap_connection_t *c); static void ngx_http_auth_ldap_read_handler(ngx_event_t *rev); static void ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c); static void ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c); @@ -1133,8 +1136,8 @@ ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle) cache->num_buckets = count; cache->elts_per_bucket = 8; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "http_auth_ldap: Allocating %ud bytes of LDAP cache.", - cache->num_buckets * cache->elts_per_bucket * sizeof(ngx_http_auth_ldap_cache_elt_t)); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "http_auth_ldap: Allocating %ud bytes of LDAP cache (ttl=%dms).", + cache->num_buckets * cache->elts_per_bucket * sizeof(ngx_http_auth_ldap_cache_elt_t), cache->expiration_time); cache->buckets = (ngx_http_auth_ldap_cache_elt_t *) ngx_calloc(count * 8 * sizeof(ngx_http_auth_ldap_cache_elt_t), cycle->log); if (cache->buckets == NULL) { @@ -1343,7 +1346,7 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c) c->rctx = NULL; if (c->state != STATE_DISCONNECTED) { c->state = STATE_DISCONNECTED; - ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout); + ngx_http_auth_ldap_set_pending_connection(c); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connection scheduled for reconnection in %d ms", c->server->reconnect_timeout); } } @@ -1384,6 +1387,18 @@ ngx_http_auth_ldap_get_connection(ngx_http_auth_ldap_ctx_t *ctx) return 1; } + /* Check if we have pending (waiting reconnect) connection */ + if (!ngx_queue_empty(&server->pending_connections)) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_get_connection: Have pending connection"); + q = ngx_queue_head(&server->pending_connections); + c = ngx_queue_data(q, ngx_http_auth_ldap_connection_t, queue_pending); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_get_connection: Shorten reconnect timer"); + /* Shorten the reconnection timer */ + ngx_add_timer(&c->reconnect_event, 0); + } + q = ngx_queue_next(&server->waiting_requests); while (q != ngx_queue_sentinel(&server->waiting_requests)) { if (q == &ctx->queue) { @@ -1420,6 +1435,26 @@ ngx_http_auth_ldap_return_connection(ngx_http_auth_ldap_connection_t *c) } } +static void +ngx_http_auth_ldap_set_pending_connection(ngx_http_auth_ldap_connection_t *c) +{ + ngx_queue_t *q; + + ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout); + /* Check if connection is already in the pending queue */ + for (q = ngx_queue_head(&c->server->pending_connections); + q != ngx_queue_sentinel(&c->server->pending_connections); + q = ngx_queue_next(q)) + { + if (q == &c->queue_pending) { + ngx_log_error(NGX_LOG_WARN, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_set_pending_connection: Connection already in pending queue"); + return; + } + } + ngx_queue_insert_tail(&c->server->pending_connections, &c->queue_pending); +} + static void ngx_http_auth_ldap_reply_connection(ngx_http_auth_ldap_connection_t *c, int error_code, char* error_msg) { @@ -1873,7 +1908,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) santitize_str((u_char *)attr, SANITIZE_NO_CONV); int attr_len = strlen(attr); h->hash = 1; -#if (nginx_version >= 102300) +#if (nginx_version >= 1023000) h->next = NULL; #endif h->key.len = c->server->attribute_header_prefix.len + attr_len; @@ -2011,7 +2046,7 @@ ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c) if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: Unable to connect to LDAP server \"%V\".", &addr->name); - ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout); + ngx_http_auth_ldap_set_pending_connection(c); return; } @@ -2040,6 +2075,20 @@ ngx_http_auth_ldap_reconnect_handler(ngx_event_t *ev) { ngx_connection_t *conn = ev->data; ngx_http_auth_ldap_connection_t *c = conn->data; + ngx_queue_t *q; + + /* Remove this connection from the pending queue */ + for (q = ngx_queue_head(&c->server->pending_connections); + q != ngx_queue_sentinel(&c->server->pending_connections); + q = ngx_queue_next(q)) + { + if (q == &c->queue_pending) { + ngx_queue_remove(q); + ngx_log_error(NGX_LOG_DEBUG, c->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_reconnect_handler: Connection removed from pending queue"); + break; + } + } ngx_http_auth_ldap_connect(c); } @@ -2216,6 +2265,7 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) server = &((ngx_http_auth_ldap_server_t *) halmcf->servers->elts)[i]; ngx_queue_init(&server->free_connections); ngx_queue_init(&server->waiting_requests); + ngx_queue_init(&server->pending_connections); if (server->connections <= 1) { server->connections = 1; } @@ -2272,7 +2322,7 @@ ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm) } r->headers_out.www_authenticate->hash = 1; -#if (nginx_version >= 102300) +#if (nginx_version >= 1023000) r->headers_out.www_authenticate->next = NULL; #endif r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1; From ccc8666c58e13885059b124040871a2e767a8bc2 Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Mon, 12 Sep 2022 15:23:36 +0200 Subject: [PATCH 09/15] Support caching of LDAP search attributes --- ngx_http_auth_ldap_module.c | 93 +++++++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 19 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 1b005af..7fe2258 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -84,7 +84,9 @@ extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); #define ENCODING_B64 1 #define ENCODING_HEX 2 -#define MAX_ATTRS_COUNT 5 /* Maximum search attributes to display in log */ +#define DEFAULT_ATTRS_COUNT 3 /* The default number of search attributes */ +#define MAX_ATTRS_COUNT 5 /* Maximum search attributes to display in log */ + typedef struct { @@ -144,11 +146,18 @@ typedef struct { ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t* */ } ngx_http_auth_ldap_loc_conf_t; + +typedef struct { + ngx_str_t attr_name; + ngx_str_t attr_value; +} ldap_search_attribute_t; + typedef struct { uint32_t small_hash; /* murmur2 hash of username ^ &server */ uint32_t outcome; /* OUTCOME_DENY or OUTCOME_ALLOW */ ngx_msec_t time; /* ngx_current_msec when created */ u_char big_hash[16]; /* md5 hash of (username, server, password) */ + ngx_array_t attributes; /* Attributes (ldap_search_attribute_t) retreived during the search */ } ngx_http_auth_ldap_cache_elt_t; typedef struct { @@ -156,6 +165,7 @@ typedef struct { ngx_uint_t num_buckets; ngx_uint_t elts_per_bucket; ngx_msec_t expiration_time; + ngx_pool_t *pool; } ngx_http_auth_ldap_cache_t; typedef enum { @@ -175,6 +185,7 @@ typedef struct { ngx_http_auth_ldap_request_phase_t phase; unsigned int iteration; int outcome; + ngx_array_t attributes; /* Attributes (ldap_search_attribute_t) retreived during the search */ struct ngx_http_auth_ldap_connection *c; ngx_queue_t queue; @@ -1117,6 +1128,9 @@ ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle) 577, 641, 701, 769, 839, 911, 983, 1049, 1109 }; + cache = &ngx_http_auth_ldap_cache; + cache->pool = cycle->pool; + conf = (ngx_http_auth_ldap_main_conf_t *) ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); if (conf == NULL || !conf->cache_enabled) { return NGX_OK; @@ -1131,7 +1145,7 @@ ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle) } } - cache = &ngx_http_auth_ldap_cache; + cache->expiration_time = conf->cache_expiration_time; cache->num_buckets = count; cache->elts_per_bucket = 8; @@ -1145,6 +1159,12 @@ ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle) return NGX_ERROR; } + /* Initialize the attributes array in each cache entry */ + ngx_http_auth_ldap_cache_elt_t *cache_entry = cache->buckets; + for (i = 0; i < cache->num_buckets * cache->elts_per_bucket; i++, cache_entry++) { + ngx_array_init(&cache_entry->attributes, cache->pool, DEFAULT_ATTRS_COUNT, sizeof(ldap_search_attribute_t)); + } + return NGX_OK; } @@ -1173,6 +1193,17 @@ ngx_http_auth_ldap_check_cache(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t * if (elt->small_hash == ctx->cache_small_hash && elt->time > time_limit && memcmp(elt->big_hash, ctx->cache_big_hash, 16) == 0) { + if (elt->outcome == OUTCOME_ALLOW || elt->outcome == OUTCOME_CACHED_ALLOW) { + /* Restore the cached attributes to the current context */ + ctx->attributes.nelts = 0; + for (i = 0; i < elt->attributes.nelts; i++) { + ldap_search_attribute_t *ctx_attr = ngx_array_push(&ctx->attributes); + *ctx_attr = *((ldap_search_attribute_t *)elt->attributes.elts + i); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_check_cache: restoring attribute '%V' = '%V' from cache", + &ctx_attr->attr_name, &ctx_attr->attr_value); + } + } return elt->outcome; } } @@ -1199,6 +1230,15 @@ ngx_http_auth_ldap_update_cache(ngx_http_auth_ldap_ctx_t *ctx, oldest_elt->outcome = outcome; oldest_elt->small_hash = ctx->cache_small_hash; ngx_memcpy(oldest_elt->big_hash, ctx->cache_big_hash, 16); + /* save (copy) each attribute from the context to the cache */ + oldest_elt->attributes.nelts = 0; + for (i = 0; i < ctx->attributes.nelts; i++) { + ldap_search_attribute_t *cache_attr = ngx_array_push(&oldest_elt->attributes); + *cache_attr = *((ldap_search_attribute_t *)ctx->attributes.elts + i); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_update_cache: saving '%V' = '%V' to cache", + &cache_attr->attr_name, &cache_attr->attr_value); + } } @@ -1895,7 +1935,6 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) BerElement *ber = NULL; char *attr = NULL; struct berval **vals = NULL; - ngx_table_elt_t *h; for (attr = ldap_first_attribute(c->ld, result, &ber); attr != NULL; attr = ldap_next_attribute(c->ld, result, ber)) { @@ -1903,22 +1942,18 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) if ((vals = ldap_get_values_len(c->ld, result, attr)) != NULL ) { if(vals[0] != NULL) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received attribute %s: %s", attr, vals[0]->bv_val); - h = ngx_list_push(&c->rctx->r->headers_out.headers); - if (h != NULL) { - santitize_str((u_char *)attr, SANITIZE_NO_CONV); - int attr_len = strlen(attr); - h->hash = 1; -#if (nginx_version >= 1023000) - h->next = NULL; -#endif - h->key.len = c->server->attribute_header_prefix.len + attr_len; - h->key.data = ngx_pnalloc(c->rctx->r->pool, h->key.len); - unsigned char *p = ngx_cpymem(h->key.data, c->server->attribute_header_prefix.data, c->server->attribute_header_prefix.len); - p = ngx_cpymem(p, attr, attr_len); - h->value.len = vals[0]->bv_len; - h->value.data = ngx_pnalloc(c->rctx->r->pool, h->value.len); - ngx_memcpy(h->value.data, vals[0]->bv_val, h->value.len); - } + /* Save attribute name and value in the context */ + ldap_search_attribute_t * elt = ngx_array_push(&c->rctx->attributes); + santitize_str((u_char *)attr, SANITIZE_NO_CONV); + int attr_len = strlen(attr); + elt->attr_name.len = c->server->attribute_header_prefix.len + attr_len; + /* Use the pool of global cache to allocate strings, so that they can be used everywhere */ + elt->attr_name.data = ngx_pnalloc(ngx_http_auth_ldap_cache.pool, elt->attr_name.len); + unsigned char *p = ngx_cpymem(elt->attr_name.data, c->server->attribute_header_prefix.data, c->server->attribute_header_prefix.len); + p = ngx_cpymem(p, attr, attr_len); + elt->attr_value.len = vals[0]->bv_len; + elt->attr_value.data = ngx_pnalloc(ngx_http_auth_ldap_cache.pool, elt->attr_value.len); + ngx_memcpy(elt->attr_value.data, vals[0]->bv_val, elt->attr_value.len); } ldap_value_free_len(vals); } @@ -2377,6 +2412,10 @@ ngx_http_auth_ldap_handler(ngx_http_request_t *r) return NGX_HTTP_INTERNAL_SERVER_ERROR; } ctx->r = r; + + /* Initialize the attributes array */ + ngx_array_init(&ctx->attributes, r->pool, DEFAULT_ATTRS_COUNT, sizeof(ldap_search_attribute_t)); + /* Other fields have been initialized to zero/NULL */ ngx_http_set_ctx(r, ctx, ngx_http_auth_ldap_module); } @@ -2587,6 +2626,22 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t } if (ctx->outcome == OUTCOME_ALLOW || ctx->outcome == OUTCOME_CACHED_ALLOW) { + /* Create response headers for each search attributes found in context */ + for (i = 0; i < ctx->attributes.nelts; i++) { + ldap_search_attribute_t *elt = (ldap_search_attribute_t *)ctx->attributes.elts + i; + ngx_table_elt_t *h = ngx_list_push(&r->headers_out.headers); + if (h != NULL) { + h->hash = 1; +#if (nginx_version >= 1023000) + h->next = NULL; +#endif + h->key = elt->attr_name; + h->value = elt->attr_value; + } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http_auth_ldap: ngx_http_auth_ldap_authenticate set response header %V : %V", + &elt->attr_name, &elt->attr_value); + } return NGX_OK; } From e815260d500997a6b2f7ed391a2e4312503876b5 Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Fri, 2 Dec 2022 15:23:18 +0100 Subject: [PATCH 10/15] - Fix max_down_retries >= 1 issue - Add clean_on_timeout parameter - Improve debug traces when multiple connections are used --- README.md | 19 +- config | 1 + ngx_http_auth_ldap_module.c | 361 ++++++++++++++++++++++-------------- 3 files changed, 235 insertions(+), 146 deletions(-) diff --git a/README.md b/README.md index 0506d58..81fd171 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # LDAP Authentication module for nginx + LDAP module for nginx which supports authentication against multiple LDAP servers. ## Project history @@ -17,7 +18,9 @@ The reasons for this fork are: * Added an `encoding` attribute to the binddn_passwd parameter. * Manage connections waiting a reconnect delay in a specific queue, so that we can cancel the reconnect delay when a new request ask for an authentication and no free - connection is available. + connection is available, but some are waiting to re-connect. + * Fix the usage of `max_down_retries` parameter + * Add the `clean_on_timeout` option ## How to install @@ -29,8 +32,7 @@ cd /usr/ports/www/nginx && make config install clean Check HTTP_AUTH_LDAP options - -``` +```text [*] HTTP_AUTH_LDAP 3rd party http_auth_ldap module ``` @@ -225,7 +227,7 @@ Tell to search for full DN in member object. * Context: `ldap_server` block Retry count for attempting to reconnect to an LDAP server if it is considered -"DOWN". This may happen if a KEEP-ALIVE connection to an LDAP server times +"DOWN". This may happen if a KEEP-ALIVE connection to an LDAP server times out or is terminated by the server end after some amount of time. This can usually help with the following error: @@ -309,3 +311,12 @@ The delay before reconnection attempts (see waiting_requests queue */ int replied; int error_code; ngx_str_t error_msg; @@ -223,14 +224,15 @@ typedef struct ngx_http_auth_ldap_connection { ngx_ssl_t *ssl; #endif - ngx_queue_t queue; - ngx_queue_t queue_pending; /* pendings connection (waiting re-connect) */ + ngx_queue_t queue; /* Queue element to be chained in server->free_connections queue */ + ngx_queue_t queue_pending; /* Queue element to be chained in server->pending_connections queue */ ngx_http_auth_ldap_ctx_t *rctx; LDAP* ld; ngx_http_auth_ldap_connection_state_t state; int msgid; ngx_resolver_ctx_t *resolver_ctx; + ngx_uint_t cnx_idx; /* index of the connection from 0 to server->connections -1 */ } ngx_http_auth_ldap_connection_t; static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -256,6 +258,7 @@ static void ngx_http_auth_ldap_read_handler(ngx_event_t *rev); static void ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c); static void ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c); static void ngx_http_auth_ldap_reconnect_handler(ngx_event_t *); +static void ngx_http_auth_ldap_reconnect_from_connection(ngx_http_auth_ldap_connection_t *c); static void ngx_http_auth_ldap_resolve_handler(ngx_resolver_ctx_t *ctx); static ngx_int_t ngx_http_auth_ldap_init_servers(ngx_cycle_t *cycle); static ngx_int_t ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle); @@ -486,10 +489,12 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c ngx_memzero(server, sizeof(*server)); server->connect_timeout = 10000; server->reconnect_timeout = 10000; + server->max_down_retries = 0; server->bind_timeout = 5000; server->request_timeout = 10000; server->alias = name; server->referral = 1; + server->clean_on_timeout = 0; server->attribute_header_prefix.len = sizeof(LDAP_ATTR_HEADER_DEFAULT_PREFIX) -1; server->attribute_header_prefix.data = (u_char *)LDAP_ATTR_HEADER_DEFAULT_PREFIX; server->attrs = NULL; @@ -559,6 +564,12 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) return ngx_http_auth_ldap_parse_satisfy(cf, server); } else if (ngx_strcmp(value[0].data, "referral") == 0) { return ngx_http_auth_ldap_parse_referral(cf, server); + } else if (ngx_strcmp(value[0].data, "clean_on_timeout") == 0) { + if (ngx_strcasecmp(value[1].data, (u_char *) "on") == 0) { + server->clean_on_timeout = 1; + } else { + server->clean_on_timeout = 0; + } } else if (ngx_strcmp(value[0].data, "max_down_retries") == 0) { i = ngx_atoi(value[1].data, value[1].len); if (i == NGX_ERROR) { @@ -714,7 +725,7 @@ ngx_http_auth_ldap_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, - "http_auth_ldap: ngx_http_auth_ldap_resolver: Configured resolver %V", + "ngx_http_auth_ldap_resolver: Configured resolver %V", &value[1]); return NGX_CONF_OK; @@ -1076,6 +1087,8 @@ ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle) return NGX_OK; } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "ngx_http_auth_ldap_init_worker:"); + rc = ngx_http_auth_ldap_init_servers(cycle); if (rc != NGX_OK) { return rc; @@ -1256,7 +1269,7 @@ ngx_http_auth_ldap_sb_remove(Sockbuf_IO_Desc *sbiod) { ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_remove()"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_remove() Cnx[%d]", c->cnx_idx); (void)c; /* 'c' would be left unused on debug builds */ sbiod->sbiod_pvt = NULL; @@ -1268,11 +1281,12 @@ ngx_http_auth_ldap_sb_close(Sockbuf_IO_Desc *sbiod) { ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_close()"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_close() Cnx[%d]", c->cnx_idx); if (!c->conn.connection->read->error && !c->conn.connection->read->eof) { if (ngx_shutdown_socket(c->conn.connection->fd, SHUT_RDWR) == -1) { ngx_connection_error(c->conn.connection, ngx_socket_errno, ngx_shutdown_socket_n " failed"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_close() Cnx[%d] shutdown failed", c->cnx_idx); ngx_http_auth_ldap_close_connection(c); return -1; } @@ -1286,7 +1300,7 @@ ngx_http_auth_ldap_sb_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) { ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_ctrl(opt=%d)", opt); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_ctrl(opt=%d) Cnx[%d]", opt, c->cnx_idx); switch (opt) { case LBER_SB_OPT_DATA_READY: @@ -1305,7 +1319,7 @@ ngx_http_auth_ldap_sb_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; ber_slen_t ret; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_read(len=%d)", len); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_read(len=%d) Cnx[%d]", len, c->cnx_idx); ret = c->conn.connection->recv(c->conn.connection, buf, len); if (ret < 0) { @@ -1322,9 +1336,10 @@ ngx_http_auth_ldap_sb_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; ber_slen_t ret; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_write(len=%d)", len); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_write(len=%d) Cnx[%d]", len, c->cnx_idx); ret = c->conn.connection->send(c->conn.connection, buf, len); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_write Cnx[%d] ret=%d", c->cnx_idx, ret); if (ret < 0) { errno = (ret == NGX_AGAIN) ? NGX_EAGAIN : NGX_ECONNRESET; return 0; @@ -1351,17 +1366,19 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c) { ngx_queue_t *q; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d]", c->cnx_idx); + if (c->ld) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Unbinding from the server \"%V\")", - &c->server->url); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] Unbinding from the server \"%V\")", + c->cnx_idx, &c->server->url); ldap_unbind_ext(c->ld, NULL, NULL); /* Unbind is always synchronous, even though the function name does not end with an '_s'. */ c->ld = NULL; } if (c->conn.connection) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Closing connection (fd=%d)", - c->conn.connection->fd); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] Closing connection (fd=%d)", + c->cnx_idx, c->conn.connection->fd); #if (NGX_OPENSSL) if (c->conn.connection->ssl) { @@ -1377,6 +1394,9 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c) q = ngx_queue_head(&c->server->free_connections); while (q != ngx_queue_sentinel(&c->server->free_connections)) { if (q == &c->queue) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_close_connection: removing cnx [%d] from free queue", + c->cnx_idx); ngx_queue_remove(q); break; } @@ -1386,8 +1406,10 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c) c->rctx = NULL; if (c->state != STATE_DISCONNECTED) { c->state = STATE_DISCONNECTED; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_close_connection: Cnx[%d] set pending reconnection", + c->cnx_idx); ngx_http_auth_ldap_set_pending_connection(c); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connection scheduled for reconnection in %d ms", c->server->reconnect_timeout); } } @@ -1414,13 +1436,15 @@ ngx_http_auth_ldap_get_connection(ngx_http_auth_ldap_ctx_t *ctx) server = ctx->server; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "http_auth_ldap: Wants a free connection to \"%V\"", - &server->alias); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, + "ngx_http_auth_ldap_get_connection: Wants a free connection to \"%V\"", &server->alias); if (!ngx_queue_empty(&server->free_connections)) { q = ngx_queue_last(&server->free_connections); ngx_queue_remove(q); c = ngx_queue_data(q, ngx_http_auth_ldap_connection_t, queue); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, + "ngx_http_auth_ldap_get_connection: Got cnx [%d] from free queue", c->cnx_idx); c->rctx = ctx; ctx->c = c; ctx->replied = 0; @@ -1429,12 +1453,10 @@ ngx_http_auth_ldap_get_connection(ngx_http_auth_ldap_ctx_t *ctx) /* Check if we have pending (waiting reconnect) connection */ if (!ngx_queue_empty(&server->pending_connections)) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_get_connection: Have pending connection"); q = ngx_queue_head(&server->pending_connections); c = ngx_queue_data(q, ngx_http_auth_ldap_connection_t, queue_pending); - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_get_connection: Shorten reconnect timer"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, + "ngx_http_auth_ldap_get_connection: Got cnx [%d] from pending queue -> shorten reconnect timer", c->cnx_idx); /* Shorten the reconnection timer */ ngx_add_timer(&c->reconnect_event, 0); } @@ -1442,13 +1464,13 @@ ngx_http_auth_ldap_get_connection(ngx_http_auth_ldap_ctx_t *ctx) q = ngx_queue_next(&server->waiting_requests); while (q != ngx_queue_sentinel(&server->waiting_requests)) { if (q == &ctx->queue) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "http_auth_ldap: Tried to insert a same request"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "ngx_http_auth_ldap_get_connection: Tried to insert a same request"); return 0; } q = ngx_queue_next(q); } ngx_queue_insert_head(&server->waiting_requests, &ctx->queue); - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "http_auth_ldap: No connection available at the moment, waiting..."); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "ngx_http_auth_ldap_get_connection: No connection available at the moment, waiting..."); return 0; } @@ -1457,8 +1479,9 @@ ngx_http_auth_ldap_return_connection(ngx_http_auth_ldap_connection_t *c) { ngx_queue_t *q; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Marking the connection to \"%V\" as free", - &c->server->alias); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_return_connection: Marking the connection [%d] to \"%V\" as free", + c->cnx_idx, &c->server->alias); if (c->rctx != NULL) { c->rctx->c = NULL; @@ -1471,6 +1494,8 @@ ngx_http_auth_ldap_return_connection(ngx_http_auth_ldap_connection_t *c) if (!ngx_queue_empty(&c->server->waiting_requests)) { q = ngx_queue_last(&c->server->waiting_requests); ngx_queue_remove(q); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_return_connection: Cnx[%d] Wakeup a waiting request", c->cnx_idx); ngx_http_auth_ldap_wake_request((ngx_queue_data(q, ngx_http_auth_ldap_ctx_t, queue))->r); } } @@ -1480,7 +1505,11 @@ ngx_http_auth_ldap_set_pending_connection(ngx_http_auth_ldap_connection_t *c) { ngx_queue_t *q; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_set_pending_connection: Connection [%d] scheduled for reconnection in %d ms", + c->cnx_idx, c->server->reconnect_timeout); ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout); + /* Check if connection is already in the pending queue */ for (q = ngx_queue_head(&c->server->pending_connections); q != ngx_queue_sentinel(&c->server->pending_connections); @@ -1492,6 +1521,8 @@ ngx_http_auth_ldap_set_pending_connection(ngx_http_auth_ldap_connection_t *c) return; } } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_set_pending_connection: Connection [%d] inserted in pending queue", c->cnx_idx); ngx_queue_insert_tail(&c->server->pending_connections, &c->queue_pending); } @@ -1500,8 +1531,8 @@ ngx_http_auth_ldap_reply_connection(ngx_http_auth_ldap_connection_t *c, int erro { ngx_http_auth_ldap_ctx_t *ctx = c->rctx; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: LDAP request to \"%V\" has finished", - &c->server->alias); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_reply_connection: cnx[%d] LDAP request to \"%V\" has finished", + c->cnx_idx, &c->server->alias); ctx->replied = 1; ctx->error_code = error_code; @@ -1564,7 +1595,7 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) ngx_int_t rc; struct berval cred; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connection established"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_connection_established: Cnx[%d] established", c->cnx_idx); conn = c->conn.connection; ngx_del_timer(conn->read); @@ -1573,11 +1604,11 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) /* Initialize OpenLDAP on the connection */ - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Initializing connection using URL \"%V\"", &c->server->url); - + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_connection_established: Cnx[%d] initializing LDAP using URL \"%V\"", + c->cnx_idx, &c->server->url); rc = ldap_init_fd(c->conn.connection->fd, LDAP_PROTO_EXT, (const char *) c->server->url.data, &c->ld); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, c->log, errno, "http_auth_ldap: ldap_init_fd() failed (%d: %s)", rc, ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, c->log, errno, "ngx_http_auth_ldap_connection_established: ldap_init_fd() failed (%d: %s)", rc, ldap_err2string(rc)); ngx_http_auth_ldap_close_connection(c); return; } @@ -1585,7 +1616,7 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) if (c->server->referral == 0) { rc = ldap_set_option(c->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); if (rc != LDAP_OPT_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: ldap_set_option() failed (%d: %s)", rc, ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_connection_established: ldap_set_option() failed (%d: %s)", rc, ldap_err2string(rc)); ngx_http_auth_ldap_close_connection(c); return; } @@ -1593,14 +1624,15 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) rc = ldap_get_option(c->ld, LDAP_OPT_SOCKBUF, (void *) &sb); if (rc != LDAP_OPT_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: ldap_get_option() failed (%d: %s)", rc, ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_connection_established: ldap_get_option() failed (%d: %s)", rc, ldap_err2string(rc)); ngx_http_auth_ldap_close_connection(c); return; } ber_sockbuf_add_io(sb, &ngx_http_auth_ldap_sbio, LBER_SBIOD_LEVEL_PROVIDER, (void *) c); - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connection initialized"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_connection_established: Connection [%d] LDAP initialized", c->cnx_idx); /* Perform initial bind to the server */ @@ -1609,16 +1641,21 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) cred.bv_len = c->server->bind_dn_passwd.len; rc = ldap_sasl_bind(c->ld, (const char *) c->server->bind_dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &c->msgid); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: ldap_sasl_bind() failed (%d: %s)", - rc, ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "ngx_http_auth_ldap_connection_established: [%d] initial ldap_sasl_bind() failed (%d: %s)", + c->cnx_idx, rc, ldap_err2string(rc)); ngx_http_auth_ldap_close_connection(c); return; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ldap_sasl_bind() -> msgid=%d", c->msgid); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_connection_established: [%d] initial ldap_sasl_bind() -> msgid=%d", + c->cnx_idx, c->msgid); c->state = STATE_INITIAL_BINDING; ngx_add_timer(c->conn.connection->read, c->server->bind_timeout); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: bind_timeout=%d", c->server->bind_timeout); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_connection_established: [%d] waiting initial bind response (timeout=%d)", + c->cnx_idx, c->server->bind_timeout); } #if (NGX_OPENSSL) @@ -1779,11 +1816,11 @@ ngx_http_auth_ldap_connect_handler(ngx_event_t *wev) ngx_http_auth_ldap_connection_t *c; int keepalive; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http_auth_ldap: Connect handler"); - conn = wev->data; c = conn->data; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_connect_handler: Cnx[%d]", c->cnx_idx); + if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_http_auth_ldap_close_connection(c); return; @@ -1792,7 +1829,7 @@ ngx_http_auth_ldap_connect_handler(ngx_event_t *wev) keepalive = 1; if (setsockopt(conn->fd, SOL_SOCKET, SO_KEEPALIVE, (const void *) &keepalive, sizeof(int)) == -1) { - ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, "http_auth_ldap: setsockopt(SO_KEEPALIVE) failed"); + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, "ngx_http_auth_ldap_connect_handler: setsockopt(SO_KEEPALIVE) failed"); } #if (NGX_OPENSSL) @@ -1817,19 +1854,20 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) char *error_msg; char *dn; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http_auth_ldap: Read handler"); - conn = rev->data; c = conn->data; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d]", c->cnx_idx); + if (c->ld == NULL) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: Could not connect"); + ngx_log_error(NGX_LOG_ERR, rev->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] No LDAP", c->cnx_idx); ngx_http_auth_ldap_close_connection(c); return; } if (rev->timedout) { - ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, "http_auth_ldap: Request timed out (state=%d)", c->state); + ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, + "ngx_http_auth_ldap_read_handler: Cnx[%d] Request timed out (state=%d)", c->cnx_idx, c->state); conn->timedout = 1; ngx_http_auth_ldap_close_connection(c); return; @@ -1840,39 +1878,44 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) for (;;) { rc = ldap_result(c->ld, LDAP_RES_ANY, 0, &timeout, &result); if (rc < 0) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: ldap_result() failed (%d: %s)", - rc, ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_result() failed (%d: %s)", + c->cnx_idx, rc, ldap_err2string(rc)); ngx_http_auth_ldap_close_connection(c); // if LDAP_SERVER_DOWN (usually timeouts or server disconnects) - if (rc == LDAP_SERVER_DOWN && \ - c->server->max_down_retries_count < c->server->max_down_retries) { - /** - update counter (this is always reset in - ngx_http_auth_ldap_connect() for a successful ldap - connection - **/ - c->server->max_down_retries_count++; - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: LDAP_SERVER_DOWN: retry count: %d", - c->server->max_down_retries_count); - c->state = STATE_DISCONNECTED; - // immediate reconnect synchronously, this schedules another - // timer call to this read handler again - ngx_http_auth_ldap_reconnect_handler(rev); - return; + if (rc == LDAP_SERVER_DOWN) { + if (c->server->max_down_retries_count < c->server->max_down_retries) { + /** + update counter (this is always reset in + ngx_http_auth_ldap_connect() for a successful ldap + connection + **/ + c->server->max_down_retries_count++; + ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] LDAP_SERVER_DOWN: retry count: %d", + c->cnx_idx, c->server->max_down_retries_count); + c->state = STATE_DISCONNECTED; + // immediate reconnect synchronously, this schedules another + // timer call to this read handler again + //ngx_http_auth_ldap_reconnect_handler(rev); + ngx_http_auth_ldap_reconnect_from_connection(c); + } else { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "ngx_http_auth_ldap_read_handler: Cnx[%d] LDAP_SERVER_DOWN: No more reconnect retry", c->cnx_idx); + } } return; } if (rc == 0) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ldap_result() -> rc=0"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_result() -> rc=0", c->cnx_idx); break; } - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ldap_result() -> rc=%d, msgid=%d, msgtype=%d", - rc, ldap_msgid(result), ldap_msgtype(result)); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_result() -> rc=%d, msgid=%d, msgtype=%d", + c->cnx_idx, rc, ldap_msgid(result), ldap_msgtype(result)); if (ldap_msgid(result) != c->msgid) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Message with unknown ID received, ignoring."); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_read_handler: Cnx[%d] Message with unknown ID received, ignoring.", c->cnx_idx); ldap_msgfree(result); continue; } @@ -1882,8 +1925,8 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) error_code = LDAP_NO_RESULTS_RETURNED; error_msg = NULL; } else if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: ldap_parse_result() failed (%d: %s)", - rc, ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_parse_result() failed (%d: %s)", + c->cnx_idx, rc, ldap_err2string(rc)); ldap_msgfree(result); ngx_http_auth_ldap_close_connection(c); return; @@ -1896,12 +1939,12 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) } ngx_del_timer(conn->read); if (error_code == LDAP_SUCCESS) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Initial bind successful"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Initial bind successful", c->cnx_idx); c->state = STATE_READY; ngx_http_auth_ldap_return_connection(c); } else { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: Initial bind failed (%d: %s [%s])", - error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); + ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Initial bind failed (%d: %s [%s])", + c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); ldap_memfree(error_msg); ldap_msgfree(result); ngx_http_auth_ldap_close_connection(c); @@ -1913,18 +1956,19 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) if (ldap_msgtype(result) != LDAP_RES_BIND) { break; } - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received bind response (%d: %s [%s])", - error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received bind response (%d: %s [%s])", + c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); break; case STATE_SEARCHING: if (ldap_msgtype(result) == LDAP_RES_SEARCH_ENTRY) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received a search entry"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received a search entry", c->cnx_idx); if (c->rctx->dn.data == NULL) { dn = ldap_get_dn(c->ld, result); if (dn != NULL) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Found entry with DN \"%s\"", dn); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_read_handler: Cnx[%d] Found entry with DN \"%s\"", c->cnx_idx, dn); c->rctx->dn.len = ngx_strlen(dn); c->rctx->dn.data = (u_char *) ngx_palloc(c->rctx->r->pool, c->rctx->dn.len + 1); ngx_memcpy(c->rctx->dn.data, dn, c->rctx->dn.len + 1); @@ -1941,7 +1985,8 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) /* Get only first value for each attribute. */ if ((vals = ldap_get_values_len(c->ld, result, attr)) != NULL ) { if(vals[0] != NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received attribute %s: %s", attr, vals[0]->bv_val); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received attribute %s: %s", + c->cnx_idx, attr, vals[0]->bv_val); /* Save attribute name and value in the context */ ldap_search_attribute_t * elt = ngx_array_push(&c->rctx->attributes); santitize_str((u_char *)attr, SANITIZE_NO_CONV); @@ -1963,8 +2008,8 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) ber_free(ber, 0); } } else if (ldap_msgtype(result) == LDAP_RES_SEARCH_RESULT) { - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received search result (%d: %s [%s])", - error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received search result (%d: %s [%s])", + c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); } break; @@ -1973,8 +2018,8 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) if (ldap_msgtype(result) != LDAP_RES_COMPARE) { break; } - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received comparison result (%d: %s [%s])", - error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received comparison result (%d: %s [%s])", + c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); break; @@ -1995,9 +2040,9 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) static void ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: Server \"%V\".", - &c->server->alias); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_connect: Cnx[%d] Server \"%V\".", + c->cnx_idx, &c->server->alias); // Clear an free any previous addrs from parsed_url, so that we can resolve again the LDAP server hostname my_free_addrs_from_url(c->main_cnf->cnf_pool, &c->server->parsed_url); @@ -2005,24 +2050,24 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) c->server->parsed_url.no_resolve = 0; // Try to resolve this time if (ngx_parse_url(c->pool, &c->server->parsed_url) != NGX_OK) { ngx_log_error(NGX_LOG_WARN, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: Hostname \"%V\" not found with system resolver, try with DNS", + "ngx_http_auth_ldap_connect: Hostname \"%V\" not found with system resolver, try with DNS", &c->server->parsed_url.host); // Try to resolve the hostname through the resolver if (c->main_cnf->resolver == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: No resolver configured"); + "ngx_http_auth_ldap_connect: No resolver configured"); return; } ngx_resolver_ctx_t *resolver_ctx, temp; temp.name = c->server->parsed_url.host; resolver_ctx = ngx_resolve_start(c->main_cnf->resolver, &temp); if (resolver_ctx == NULL) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: ngx_http_auth_ldap_connect: Unable to start the resolver"); + ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_connect: Unable to start the resolver"); return; } if (resolver_ctx == NGX_NO_RESOLVER) { ngx_log_error(NGX_LOG_ERR, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: No resolver defined to resolve %V", + "ngx_http_auth_ldap_connect: No resolver defined to resolve %V", c->server->parsed_url.host); return; } @@ -2034,14 +2079,14 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) if (ngx_resolve_name(resolver_ctx) != NGX_OK) { c->resolver_ctx = NULL; ngx_log_error(NGX_LOG_ERR, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: Resolve %V failed", c->server->parsed_url.host); + "ngx_http_auth_ldap_connect: Resolve %V failed", c->server->parsed_url.host); return; } // The DNS Querry has been triggered. Let the ngx_http_auth_ldap_resolve_handler now take the control of the flow. return; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect: server \"%V\" (naddrs is now %d).", + "ngx_http_auth_ldap_connect: server \"%V\" (naddrs is now %d).", &c->server->alias, c->server->parsed_url.naddrs); // Continue with the rest of the connection establishement @@ -2058,15 +2103,15 @@ ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c) if (c->server->parsed_url.naddrs == 0) { ngx_log_error(NGX_LOG_ERR, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_connect_continue: No addr for server"); + "ngx_http_auth_ldap_connect_continue: Cnx[%d] No addr for server", c->cnx_idx); return; } addr = &c->server->parsed_url.addrs[ngx_random() % c->server->parsed_url.naddrs]; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http_auth_ldap: Connecting to LDAP server IP@ %V", - &addr->name); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_connect_continue: Cnx[%d] Connecting to LDAP server IP@ %V", + c->cnx_idx, &addr->name); pconn = &c->conn; pconn->sockaddr = addr->sockaddr; @@ -2077,10 +2122,10 @@ ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c) pconn->log_error = NGX_ERROR_ERR; rc = ngx_event_connect_peer(pconn); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ngx_event_connect_peer() -> %d.", rc); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_connect_continue: ngx_event_connect_peer() -> %d.", rc); if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: Unable to connect to LDAP server \"%V\".", - &addr->name); + ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_connect_continue: Cnx[%d] Unable to connect to LDAP server \"%V\".", + c->cnx_idx, &addr->name); ngx_http_auth_ldap_set_pending_connection(c); return; } @@ -2093,7 +2138,9 @@ ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c) conn->write->handler = ngx_http_auth_ldap_connect_handler; conn->read->handler = ngx_http_auth_ldap_read_handler; ngx_add_timer(conn->read, c->server->connect_timeout); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: connect_timeout=%d.", c->server->connect_timeout); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_connect_continue: [%d] Waiting connection response (timeout=%d)", + c->cnx_idx, c->server->connect_timeout); c->server->max_down_retries_count = 0; /* reset retries count */ c->state = STATE_CONNECTING; @@ -2110,8 +2157,21 @@ ngx_http_auth_ldap_reconnect_handler(ngx_event_t *ev) { ngx_connection_t *conn = ev->data; ngx_http_auth_ldap_connection_t *c = conn->data; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "ngx_http_auth_ldap_reconnect_handler: ev=0x%p, conn=0x%p, c=0x%p", ev, conn, c); + + ngx_http_auth_ldap_reconnect_from_connection(c); +} + +static void +ngx_http_auth_ldap_reconnect_from_connection(ngx_http_auth_ldap_connection_t *c) +{ ngx_queue_t *q; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_reconnect_from_connection: Cnx[%d] c=0x%p", c->cnx_idx, c); + /* Remove this connection from the pending queue */ for (q = ngx_queue_head(&c->server->pending_connections); q != ngx_queue_sentinel(&c->server->pending_connections); @@ -2119,8 +2179,8 @@ ngx_http_auth_ldap_reconnect_handler(ngx_event_t *ev) { if (q == &c->queue_pending) { ngx_queue_remove(q); - ngx_log_error(NGX_LOG_DEBUG, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_reconnect_handler: Connection removed from pending queue"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_reconnect_from_connection: Cnx[%d] removed from pending queue", c->cnx_idx); break; } } @@ -2217,8 +2277,8 @@ static void ngx_http_auth_ldap_resolve_handler(ngx_resolver_ctx_t *ctx) ngx_resolver_addr_t *res_addr; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_resolve_handler: server \"%*s\".", - c->server->alias.len, c->server->alias.data); + "ngx_http_auth_ldap_resolve_handler: Cnx[%d] server \"%V\"", + c->cnx_idx, &c->server->alias); if (ctx->state) { ngx_log_error(NGX_LOG_ERR, c->log, 0, @@ -2232,8 +2292,14 @@ static void ngx_http_auth_ldap_resolve_handler(ngx_resolver_ctx_t *ctx) // Clear and free any previous addrs in parsed_url my_free_addrs_from_url(c->main_cnf->cnf_pool, &c->server->parsed_url); + u_char ip_addr_str[INET6_ADDRSTRLEN +1]; // Buffer for IPv4 or IPv6 address strings // Update the parsed_url with the addresses resolved by DNS for (i = 0, res_addr = ctx->addrs; res_addr != NULL && i < ctx->naddrs; i++, res_addr++) { + bzero(ip_addr_str, sizeof(ip_addr_str)); + ngx_sock_ntop(res_addr->sockaddr, res_addr->socklen, ip_addr_str, sizeof(ip_addr_str) -1, 0); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ngx_http_auth_ldap_resolve_handler: Cnx[%d] Found [%d] %V -> %s", + c->cnx_idx, i, &ctx->name, ip_addr_str); my_ngx_inet_add_addr(c->main_cnf->cnf_pool, &c->server->parsed_url, res_addr->sockaddr, res_addr->socklen, ctx->naddrs); } @@ -2262,13 +2328,13 @@ ngx_http_auth_ldap_init_servers(ngx_cycle_t *cycle) if (server->attrs == NULL) { // No search attributes specified, so setup an empty attributes list - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "http_auth_ldap: Server '%V': No search_attributes", &server->alias); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "ngx_http_auth_ldap_init_servers: Server '%V': No search_attributes", &server->alias); server->attrs = ngx_palloc(cycle->pool, 2 * sizeof(char *)); server->attrs[0] = LDAP_NO_ATTRS; server->attrs[1] = NULL; } else { for (j = 0; server->attrs[j] != NULL && j < MAX_ATTRS_COUNT; j++) { - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "http_auth_ldap: Server '%V': search_attribute[%d] = '%s'", &server->alias, j, server->attrs[j]); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "ngx_http_auth_ldap_init_servers: Server '%V': search_attribute[%d] = '%s'", &server->alias, j, server->attrs[j]); } } } @@ -2320,6 +2386,7 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) c->main_cnf = halmcf; c->server = server; c->state = STATE_DISCONNECTED; + c->cnx_idx = j; /* Various debug logging around timer management assume that the field 'data' in ngx_event_t is a pointer to ngx_connection_t, therefore we @@ -2435,9 +2502,19 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t ngx_http_auth_ldap_server_t *s; if (r->connection->write->timedout) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: Authentication timed out"); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Authentication timed out"); if (ctx->c != NULL) { - ngx_http_auth_ldap_return_connection(ctx->c); + if (ctx->server && ctx->server->clean_on_timeout) { + // In case of LDAP send timeout, imediately close and reconnect the connection + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ngx_http_auth_ldap_authenticate: Close/reconnect timeouted Cnx[%d]", ctx->c->cnx_idx); + ngx_http_auth_ldap_close_connection(ctx->c); + ngx_http_auth_ldap_reconnect_from_connection(ctx->c); + } else { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ngx_http_auth_ldap_authenticate: Return old timedout Cnx[%d]", ctx->c->cnx_idx); + ngx_http_auth_ldap_return_connection(ctx->c); + } } // Remove ctx from waiting_requests queue if it was added. @@ -2457,13 +2534,13 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t * this happens when we are trying to get an LDAP connection but all of them are busy right now. */ if (ctx->iteration > 0 && !ctx->replied && ctx->phase != PHASE_START) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: The LDAP operation did not finish yet"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: The LDAP operation did not finish yet"); return NGX_AGAIN; } for (;;) { loop: - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Authentication loop (phase=%d, iteration=%d)", + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Authentication loop (phase=%d, iteration=%d)", ctx->phase, ctx->iteration); switch (ctx->phase) { @@ -2472,7 +2549,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t ctx->outcome = OUTCOME_UNCERTAIN; ngx_add_timer(r->connection->write, ctx->server->request_timeout); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: request_timeout=%d",ctx->server->request_timeout); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Set request watchdog (timeout=%d)",ctx->server->request_timeout); /* Check cache if enabled */ @@ -2480,7 +2557,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t for (i = 0; i < conf->servers->nelts; i++) { s = &((ngx_http_auth_ldap_server_t *) conf->servers->elts)[i]; rc = ngx_http_auth_ldap_check_cache(r, ctx, &ngx_http_auth_ldap_cache, s); - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Using cached outcome %d from server %d", rc, i); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Using cached outcome %d from server %d", rc, i); if (rc == OUTCOME_DENY || rc == OUTCOME_ALLOW) { ctx->outcome = (rc == OUTCOME_DENY ? OUTCOME_CACHED_DENY : OUTCOME_CACHED_ALLOW); ctx->phase = PHASE_NEXT; @@ -2521,13 +2598,13 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t break; case PHASE_CHECK_USER: - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: User DN is \"%V\"", + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: User DN is \"%V\"", &ctx->user_dn); if (ctx->server->require_user != NULL) { rc = ngx_http_auth_ldap_check_user(r, ctx); if (rc != NGX_OK) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Not ok", &ctx->user_dn); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Not ok", &ctx->user_dn); /* User check failed, try next server */ ctx->phase = PHASE_NEXT; break; @@ -2537,7 +2614,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t /* User not yet fully authenticated, check group next */ if ((ctx->outcome == OUTCOME_UNCERTAIN) && (ctx->server->require_group != NULL)) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Moving to group check", &ctx->user_dn); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Moving to group check", &ctx->user_dn); ctx->phase = PHASE_CHECK_GROUP; ctx->iteration = 0; break; @@ -2549,7 +2626,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t break; case PHASE_CHECK_GROUP: - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Checking group", &ctx->user_dn); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Checking group", &ctx->user_dn); rc = ngx_http_auth_ldap_check_group(r, ctx); if (rc == NGX_AGAIN) { /* LDAP operation in progress, wait for the results */ @@ -2575,7 +2652,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t if ((ctx->server->satisfy_all == 0) && ( (ctx->server->require_user != NULL) || (ctx->server->require_group != NULL))){ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: no requirement satisfied"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: no requirement satisfied"); ctx->outcome = OUTCOME_DENY; ctx->phase = PHASE_NEXT; /*rc = NGX_DECLINED;*/ @@ -2621,7 +2698,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t if (ngx_http_auth_ldap_cache.buckets != NULL && (ctx->outcome == OUTCOME_DENY || ctx->outcome == OUTCOME_ALLOW)) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Caching outcome %d", ctx->outcome); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Caching outcome %d", ctx->outcome); ngx_http_auth_ldap_update_cache(ctx, &ngx_http_auth_ldap_cache, ctx->outcome); } @@ -2639,7 +2716,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t h->value = elt->attr_value; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_authenticate set response header %V : %V", + "ngx_http_auth_ldap_authenticate: Set response header %V : %V", &elt->attr_name, &elt->attr_value); } return NGX_OK; @@ -2677,19 +2754,19 @@ ngx_http_auth_ldap_search(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) ngx_sprintf(filter, "(&%s(%s=%V))%Z", ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", ludpp->lud_attrs[0], &r->headers_in.user); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Search filter is \"%s\"", + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_search: Search filter is \"%s\"", (const char *) filter); rc = ldap_search_ext(ctx->c->ld, ludpp->lud_dn, ludpp->lud_scope, (const char *) filter, ctx->server->attrs, 0, NULL, NULL, NULL, 0, &ctx->c->msgid); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() failed (%d, %s)", - rc, ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_search: ldap_search_ext() Cnx[%d] failed (%d, %s)", + ctx->c->cnx_idx, rc, ldap_err2string(rc)); ngx_http_auth_ldap_return_connection(ctx->c); return NGX_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() -> msgid=%d", - ctx->c->msgid); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_search: ldap_search_ext() Cnx[%d] -> msgid=%d", + ctx->c->cnx_idx, ctx->c->msgid); ctx->c->state = STATE_SEARCHING; ctx->iteration++; return NGX_AGAIN; @@ -2697,13 +2774,13 @@ ngx_http_auth_ldap_search(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) /* On the second call, handle the search results */ if (ctx->error_code != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() request failed (%d: %s)", - ctx->error_code, ldap_err2string(ctx->error_code)); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_search: ldap_search_ext() Cnx[%d] request failed (%d: %s)", + ctx->c->cnx_idx, ctx->error_code, ldap_err2string(ctx->error_code)); return NGX_ERROR; } if (ctx->dn.data == NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: Could not find user DN"); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_search: Cnx[%d] Could not find user DN", ctx->c->cnx_idx); return NGX_ERROR; } else { ctx->user_dn.len = ngx_strlen(ctx->dn.data); @@ -2887,15 +2964,15 @@ ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *c cred.bv_len = r->headers_in.passwd.len; rc = ldap_sasl_bind(ctx->c->ld, (const char *) ctx->user_dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &ctx->c->msgid); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_sasl_bind() failed (%d: %s)", - rc, ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] ldap_sasl_bind() failed (%d: %s)", + ctx->c->cnx_idx, rc, ldap_err2string(rc)); ctx->outcome = OUTCOME_ERROR; ngx_http_auth_ldap_return_connection(ctx->c); return NGX_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: ldap_sasl_bind() -> msgid=%d", - ctx->c->msgid); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] ldap_sasl_bind() -> msgid=%d", + ctx->c->cnx_idx, ctx->c->msgid); ctx->c->state = STATE_BINDING; ctx->iteration++; @@ -2904,11 +2981,11 @@ ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *c /* On the second call, process the operation result */ if (ctx->error_code != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: User bind failed (%d: %s)", - ctx->error_code, ldap_err2string(ctx->error_code)); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] User bind failed (%d: %s)", + ctx->c->cnx_idx, ctx->error_code, ldap_err2string(ctx->error_code)); ctx->outcome = OUTCOME_DENY; } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: User bind successful"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] User bind successful", ctx->c->cnx_idx); ctx->outcome = OUTCOME_ALLOW; } return NGX_OK; @@ -2926,20 +3003,20 @@ ngx_http_auth_ldap_recover_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t return NGX_AGAIN; } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Rebinding to binddn"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Rebinding to binddn"); cred.bv_val = (char *) ctx->server->bind_dn_passwd.data; cred.bv_len = ctx->server->bind_dn_passwd.len; rc = ldap_sasl_bind(ctx->c->ld, (const char *) ctx->server->bind_dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &ctx->c->msgid); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_sasl_bind() failed (%d: %s)", - rc, ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] ldap_sasl_bind() failed (%d: %s)", + ctx->c->cnx_idx, rc, ldap_err2string(rc)); ctx->outcome = OUTCOME_ERROR; ngx_http_auth_ldap_return_connection(ctx->c); return NGX_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: ldap_sasl_bind() -> msgid=%d", - ctx->c->msgid); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] ldap_sasl_bind() -> msgid=%d", + ctx->c->cnx_idx, ctx->c->msgid); ctx->c->state = STATE_BINDING; ctx->iteration++; return NGX_AGAIN; @@ -2947,10 +3024,10 @@ ngx_http_auth_ldap_recover_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t /* On the second call, process the operation result */ if (ctx->error_code != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: binddn bind failed (%d: %s)", - ctx->error_code, ldap_err2string(ctx->error_code)); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] binddn bind failed (%d: %s)", + ctx->c->cnx_idx, ctx->error_code, ldap_err2string(ctx->error_code)); } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: binddn bind successful"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] binddn bind successful", ctx->c->cnx_idx); } return NGX_OK; } From 312c5c67fd447b91743387ae9976947a02f07f20 Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Wed, 7 Dec 2022 13:43:38 +0100 Subject: [PATCH 11/15] - Fix reconnect event timer --- ngx_http_auth_ldap_module.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index c80ca91..512ff2f 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -1458,7 +1458,7 @@ ngx_http_auth_ldap_get_connection(ngx_http_auth_ldap_ctx_t *ctx) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "ngx_http_auth_ldap_get_connection: Got cnx [%d] from pending queue -> shorten reconnect timer", c->cnx_idx); /* Shorten the reconnection timer */ - ngx_add_timer(&c->reconnect_event, 0); + ngx_add_timer(&c->reconnect_event, 1); } q = ngx_queue_next(&server->waiting_requests); @@ -1897,6 +1897,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) // immediate reconnect synchronously, this schedules another // timer call to this read handler again //ngx_http_auth_ldap_reconnect_handler(rev); + ngx_del_timer(&c->reconnect_event); // Cancel the reconnect timer ngx_http_auth_ldap_reconnect_from_connection(c); } else { ngx_log_error(NGX_LOG_ERR, c->log, 0, From 51fe79c4039549217f590598b42578d481cbd1eb Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Thu, 9 Mar 2023 11:13:24 +0100 Subject: [PATCH 12/15] Fixes clean_on_timeout implementation --- ngx_http_auth_ldap_module.c | 115 ++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 512ff2f..45f3ddb 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -87,6 +87,8 @@ extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); #define DEFAULT_ATTRS_COUNT 3 /* The default number of search attributes */ #define MAX_ATTRS_COUNT 5 /* Maximum search attributes to display in log */ +#define RECONNECT_ASAP_MS 1000 /* Delay (in ms) for LDAP reconnection (when we want ASAP reconnect) */ + typedef struct { @@ -123,7 +125,7 @@ typedef struct { ngx_queue_t free_connections; /* Queue of free (ready) connections */ ngx_queue_t waiting_requests; /* Queue of ctx with not finished requests */ - ngx_queue_t pending_connections; /* Queue of pending connections (waiting re-connect) */ + ngx_queue_t pending_reconnections; /* Queue of pending connections (waiting re-connect) */ char **attrs; /* Search attributes formated for ldap_search_ext() */ ngx_str_t attribute_header_prefix; } ngx_http_auth_ldap_server_t; @@ -225,7 +227,7 @@ typedef struct ngx_http_auth_ldap_connection { #endif ngx_queue_t queue; /* Queue element to be chained in server->free_connections queue */ - ngx_queue_t queue_pending; /* Queue element to be chained in server->pending_connections queue */ + ngx_queue_t queue_pending; /* Queue element to be chained in server->pending_reconnections queue */ ngx_http_auth_ldap_ctx_t *rctx; LDAP* ld; @@ -252,8 +254,8 @@ static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *); static ngx_int_t ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle); static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); static ngx_int_t ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle); -static void ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c); -static void ngx_http_auth_ldap_set_pending_connection(ngx_http_auth_ldap_connection_t *c); +static void ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c, int retry_asap); +static void ngx_http_auth_ldap_set_pending_reconnection(ngx_http_auth_ldap_connection_t *c, ngx_msec_t reconnect_delay); static void ngx_http_auth_ldap_read_handler(ngx_event_t *rev); static void ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c); static void ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c); @@ -1287,7 +1289,7 @@ ngx_http_auth_ldap_sb_close(Sockbuf_IO_Desc *sbiod) if (ngx_shutdown_socket(c->conn.connection->fd, SHUT_RDWR) == -1) { ngx_connection_error(c->conn.connection, ngx_socket_errno, ngx_shutdown_socket_n " failed"); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_close() Cnx[%d] shutdown failed", c->cnx_idx); - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return -1; } } @@ -1322,6 +1324,7 @@ ngx_http_auth_ldap_sb_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_read(len=%d) Cnx[%d]", len, c->cnx_idx); ret = c->conn.connection->recv(c->conn.connection, buf, len); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_read Cnx[%d] recv ret=%d", c->cnx_idx, ret); if (ret < 0) { errno = (ret == NGX_AGAIN) ? NGX_EAGAIN : NGX_ECONNRESET; return -1; @@ -1362,11 +1365,12 @@ static Sockbuf_IO ngx_http_auth_ldap_sbio = /*** Asynchronous LDAP connection handling ***/ static void -ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c) +ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c, int retry_asap) { ngx_queue_t *q; + ngx_msec_t reconnect_delay = retry_asap ? RECONNECT_ASAP_MS : c->server->reconnect_timeout; // Default reconnect delay - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d]", c->cnx_idx); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] retry_asap=%d", c->cnx_idx, retry_asap); if (c->ld) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] Unbinding from the server \"%V\")", @@ -1409,7 +1413,7 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] set pending reconnection", c->cnx_idx); - ngx_http_auth_ldap_set_pending_connection(c); + ngx_http_auth_ldap_set_pending_reconnection(c, reconnect_delay); } } @@ -1452,13 +1456,14 @@ ngx_http_auth_ldap_get_connection(ngx_http_auth_ldap_ctx_t *ctx) } /* Check if we have pending (waiting reconnect) connection */ - if (!ngx_queue_empty(&server->pending_connections)) { - q = ngx_queue_head(&server->pending_connections); + if (!ngx_queue_empty(&server->pending_reconnections)) { + q = ngx_queue_head(&server->pending_reconnections); c = ngx_queue_data(q, ngx_http_auth_ldap_connection_t, queue_pending); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "ngx_http_auth_ldap_get_connection: Got cnx [%d] from pending queue -> shorten reconnect timer", c->cnx_idx); - /* Shorten the reconnection timer */ - ngx_add_timer(&c->reconnect_event, 1); + /* Use the shortest the reconnection delay as we really need a new connection here */ + ngx_del_timer(&c->reconnect_event); // Cancel the reconnect timer + ngx_add_timer(&c->reconnect_event, RECONNECT_ASAP_MS); } q = ngx_queue_next(&server->waiting_requests); @@ -1501,29 +1506,29 @@ ngx_http_auth_ldap_return_connection(ngx_http_auth_ldap_connection_t *c) } static void -ngx_http_auth_ldap_set_pending_connection(ngx_http_auth_ldap_connection_t *c) +ngx_http_auth_ldap_set_pending_reconnection(ngx_http_auth_ldap_connection_t *c, ngx_msec_t reconnect_delay) { ngx_queue_t *q; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "ngx_http_auth_ldap_set_pending_connection: Connection [%d] scheduled for reconnection in %d ms", - c->cnx_idx, c->server->reconnect_timeout); - ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout); + "ngx_http_auth_ldap_set_pending_reconnection: Connection [%d] scheduled for reconnection in %d ms", + c->cnx_idx, reconnect_delay); + ngx_add_timer(&c->reconnect_event, reconnect_delay); /* Check if connection is already in the pending queue */ - for (q = ngx_queue_head(&c->server->pending_connections); - q != ngx_queue_sentinel(&c->server->pending_connections); + for (q = ngx_queue_head(&c->server->pending_reconnections); + q != ngx_queue_sentinel(&c->server->pending_reconnections); q = ngx_queue_next(q)) { if (q == &c->queue_pending) { ngx_log_error(NGX_LOG_WARN, c->log, 0, - "http_auth_ldap: ngx_http_auth_ldap_set_pending_connection: Connection already in pending queue"); + "http_auth_ldap: ngx_http_auth_ldap_set_pending_reconnection: Connection already in pending queue"); return; } } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "ngx_http_auth_ldap_set_pending_connection: Connection [%d] inserted in pending queue", c->cnx_idx); - ngx_queue_insert_tail(&c->server->pending_connections, &c->queue_pending); + "ngx_http_auth_ldap_set_pending_reconnection: Connection [%d] inserted in pending queue", c->cnx_idx); + ngx_queue_insert_tail(&c->server->pending_reconnections, &c->queue_pending); } static void @@ -1554,7 +1559,7 @@ ngx_http_auth_ldap_dummy_write_handler(ngx_event_t *wev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http_auth_ldap: Dummy write handler"); if (ngx_handle_write_event(wev, 0) != NGX_OK) { - ngx_http_auth_ldap_close_connection(((ngx_connection_t *) wev->data)->data); + ngx_http_auth_ldap_close_connection(((ngx_connection_t *) wev->data)->data, 0); } } @@ -1609,7 +1614,7 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) rc = ldap_init_fd(c->conn.connection->fd, LDAP_PROTO_EXT, (const char *) c->server->url.data, &c->ld); if (rc != LDAP_SUCCESS) { ngx_log_error(NGX_LOG_ERR, c->log, errno, "ngx_http_auth_ldap_connection_established: ldap_init_fd() failed (%d: %s)", rc, ldap_err2string(rc)); - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return; } @@ -1617,7 +1622,7 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) rc = ldap_set_option(c->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); if (rc != LDAP_OPT_SUCCESS) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_connection_established: ldap_set_option() failed (%d: %s)", rc, ldap_err2string(rc)); - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return; } } @@ -1625,7 +1630,7 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) rc = ldap_get_option(c->ld, LDAP_OPT_SOCKBUF, (void *) &sb); if (rc != LDAP_OPT_SUCCESS) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_connection_established: ldap_get_option() failed (%d: %s)", rc, ldap_err2string(rc)); - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return; } @@ -1644,7 +1649,7 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_connection_established: [%d] initial ldap_sasl_bind() failed (%d: %s)", c->cnx_idx, rc, ldap_err2string(rc)); - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -1694,7 +1699,7 @@ ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn, ngx_flag_t vali if (conn_sockaddr->sa_family == AF_INET) len = 4; else if (conn_sockaddr->sa_family == AF_INET6) len = 16; else { // very unlikely indeed - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return; } addr_verified = X509_check_ip(cert, (const unsigned char*)conn_sockaddr->sa_data, len, 0); @@ -1713,7 +1718,7 @@ ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn, ngx_flag_t vali "http_auth_ldap: Remote side presented invalid SSL certificate: error %l, %s", chain_verified, X509_verify_cert_error_string(chain_verified)); } - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return; } } @@ -1727,7 +1732,7 @@ ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn, ngx_flag_t vali } else { // handshake failed ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: SSL handshake failed"); - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); } } @@ -1752,7 +1757,7 @@ ngx_http_auth_ldap_ssl_handshake(ngx_http_auth_ldap_connection_t *c) rc = ngx_ssl_create_connection(c->ssl, c->conn.connection, NGX_SSL_BUFFER | NGX_SSL_CLIENT); if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: SSL initialization failed"); - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return; } @@ -1822,7 +1827,7 @@ ngx_http_auth_ldap_connect_handler(ngx_event_t *wev) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_connect_handler: Cnx[%d]", c->cnx_idx); if (ngx_handle_write_event(wev, 0) != NGX_OK) { - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return; } @@ -1861,15 +1866,15 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) if (c->ld == NULL) { ngx_log_error(NGX_LOG_ERR, rev->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] No LDAP", c->cnx_idx); - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return; } if (rev->timedout) { ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, - "ngx_http_auth_ldap_read_handler: Cnx[%d] Request timed out (state=%d)", c->cnx_idx, c->state); + "ngx_http_auth_ldap_read_handler: Cnx[%d] Read timed out (state=%d)", c->cnx_idx, c->state); conn->timedout = 1; - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 1); return; } @@ -1880,8 +1885,8 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) if (rc < 0) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_result() failed (%d: %s)", c->cnx_idx, rc, ldap_err2string(rc)); - ngx_http_auth_ldap_close_connection(c); - + int reconnect_asap = 0; + // if LDAP_SERVER_DOWN (usually timeouts or server disconnects) if (rc == LDAP_SERVER_DOWN) { if (c->server->max_down_retries_count < c->server->max_down_retries) { @@ -1893,16 +1898,12 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) c->server->max_down_retries_count++; ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] LDAP_SERVER_DOWN: retry count: %d", c->cnx_idx, c->server->max_down_retries_count); - c->state = STATE_DISCONNECTED; - // immediate reconnect synchronously, this schedules another - // timer call to this read handler again - //ngx_http_auth_ldap_reconnect_handler(rev); - ngx_del_timer(&c->reconnect_event); // Cancel the reconnect timer - ngx_http_auth_ldap_reconnect_from_connection(c); + reconnect_asap = 1; } else { ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] LDAP_SERVER_DOWN: No more reconnect retry", c->cnx_idx); } + ngx_http_auth_ldap_close_connection(c, reconnect_asap); } return; @@ -1929,7 +1930,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_parse_result() failed (%d: %s)", c->cnx_idx, rc, ldap_err2string(rc)); ldap_msgfree(result); - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 1); return; } @@ -1948,7 +1949,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); ldap_memfree(error_msg); ldap_msgfree(result); - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 0); return; } break; @@ -2033,7 +2034,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) } if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_auth_ldap_close_connection(c); + ngx_http_auth_ldap_close_connection(c, 1); return; } } @@ -2127,7 +2128,7 @@ ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c) if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_connect_continue: Cnx[%d] Unable to connect to LDAP server \"%V\".", c->cnx_idx, &addr->name); - ngx_http_auth_ldap_set_pending_connection(c); + ngx_http_auth_ldap_set_pending_reconnection(c, c->server->reconnect_timeout); return; } @@ -2150,7 +2151,7 @@ ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c) static void ngx_http_auth_ldap_connection_cleanup(void *data) { - ngx_http_auth_ldap_close_connection((ngx_http_auth_ldap_connection_t *) data); + ngx_http_auth_ldap_close_connection((ngx_http_auth_ldap_connection_t *) data, 0); } static void @@ -2173,15 +2174,15 @@ ngx_http_auth_ldap_reconnect_from_connection(ngx_http_auth_ldap_connection_t *c) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_reconnect_from_connection: Cnx[%d] c=0x%p", c->cnx_idx, c); - /* Remove this connection from the pending queue */ - for (q = ngx_queue_head(&c->server->pending_connections); - q != ngx_queue_sentinel(&c->server->pending_connections); + /* Remove this connection from the pending reconnection queue */ + for (q = ngx_queue_head(&c->server->pending_reconnections); + q != ngx_queue_sentinel(&c->server->pending_reconnections); q = ngx_queue_next(q)) { if (q == &c->queue_pending) { ngx_queue_remove(q); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "ngx_http_auth_ldap_reconnect_from_connection: Cnx[%d] removed from pending queue", c->cnx_idx); + "ngx_http_auth_ldap_reconnect_from_connection: Cnx[%d] removed from pending reconnection queue", c->cnx_idx); break; } } @@ -2367,7 +2368,7 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) server = &((ngx_http_auth_ldap_server_t *) halmcf->servers->elts)[i]; ngx_queue_init(&server->free_connections); ngx_queue_init(&server->waiting_requests); - ngx_queue_init(&server->pending_connections); + ngx_queue_init(&server->pending_reconnections); if (server->connections <= 1) { server->connections = 1; } @@ -2506,11 +2507,13 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Authentication timed out"); if (ctx->c != NULL) { if (ctx->server && ctx->server->clean_on_timeout) { - // In case of LDAP send timeout, imediately close and reconnect the connection + // Authentication response timeouted => Close and clean the corresponding LDAP connection ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "ngx_http_auth_ldap_authenticate: Close/reconnect timeouted Cnx[%d]", ctx->c->cnx_idx); - ngx_http_auth_ldap_close_connection(ctx->c); - ngx_http_auth_ldap_reconnect_from_connection(ctx->c); + "ngx_http_auth_ldap_authenticate: Close timeouted Cnx[%d]", ctx->c->cnx_idx); + ngx_http_auth_ldap_close_connection(ctx->c, 1); + // Clean the connection + ctx->c->msgid = -1; + ctx->c = NULL; } else { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Return old timedout Cnx[%d]", ctx->c->cnx_idx); From 6d2c1ae01a55fc9c7117cc8c9d18e79d372cb0e8 Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Fri, 14 Jun 2024 14:19:11 +0200 Subject: [PATCH 13/15] Enhance log messages (level and content) --- ngx_http_auth_ldap_module.c | 69 ++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 45f3ddb..f83040e 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -1395,6 +1395,9 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c, int retr c->conn.connection = NULL; } + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "ngx_http_auth_ldap_close_connection: Cnx[%d] Closed", c->cnx_idx); + q = ngx_queue_head(&c->server->free_connections); while (q != ngx_queue_sentinel(&c->server->free_connections)) { if (q == &c->queue) { @@ -1420,7 +1423,7 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c, int retr static void ngx_http_auth_ldap_wake_request(ngx_http_request_t *r) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Waking authentication request \"%V\"", + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_wake_request: Waking authentication request \"%V\"", &r->request_line); ngx_http_core_run_phases(r); } @@ -1496,6 +1499,8 @@ ngx_http_auth_ldap_return_connection(ngx_http_auth_ldap_connection_t *c) } ngx_queue_insert_head(&c->server->free_connections, &c->queue); + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "ngx_http_auth_ldap_return_connectionn: Cnx[%d] Ready", c->cnx_idx); if (!ngx_queue_empty(&c->server->waiting_requests)) { q = ngx_queue_last(&c->server->waiting_requests); ngx_queue_remove(q); @@ -1510,9 +1515,8 @@ ngx_http_auth_ldap_set_pending_reconnection(ngx_http_auth_ldap_connection_t *c, { ngx_queue_t *q; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "ngx_http_auth_ldap_set_pending_reconnection: Connection [%d] scheduled for reconnection in %d ms", - c->cnx_idx, reconnect_delay); + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "ngx_http_auth_ldap_set_pending_reconnection: Cnx[%d] reconnection scheduled in %d ms", c->cnx_idx, reconnect_delay); ngx_add_timer(&c->reconnect_event, reconnect_delay); /* Check if connection is already in the pending queue */ @@ -1637,13 +1641,15 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) ber_sockbuf_add_io(sb, &ngx_http_auth_ldap_sbio, LBER_SBIOD_LEVEL_PROVIDER, (void *) c); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "ngx_http_auth_ldap_connection_established: Connection [%d] LDAP initialized", c->cnx_idx); + "ngx_http_auth_ldap_connection_established: Cnx[%d] LDAP initialized", c->cnx_idx); /* Perform initial bind to the server */ cred.bv_val = (char *) c->server->bind_dn_passwd.data; cred.bv_len = c->server->bind_dn_passwd.len; + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "ngx_http_auth_ldap_connection_established: Cnx[%d] Initial binding ...", c->cnx_idx); rc = ldap_sasl_bind(c->ld, (const char *) c->server->bind_dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &c->msgid); if (rc != LDAP_SUCCESS) { ngx_log_error(NGX_LOG_ERR, c->log, 0, @@ -1865,7 +1871,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d]", c->cnx_idx); if (c->ld == NULL) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] No LDAP", c->cnx_idx); + ngx_log_error(NGX_LOG_WARN, rev->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] No ldap handler (already closed)", c->cnx_idx); ngx_http_auth_ldap_close_connection(c, 0); return; } @@ -1883,10 +1889,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) for (;;) { rc = ldap_result(c->ld, LDAP_RES_ANY, 0, &timeout, &result); if (rc < 0) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_result() failed (%d: %s)", - c->cnx_idx, rc, ldap_err2string(rc)); int reconnect_asap = 0; - // if LDAP_SERVER_DOWN (usually timeouts or server disconnects) if (rc == LDAP_SERVER_DOWN) { if (c->server->max_down_retries_count < c->server->max_down_retries) { @@ -1896,7 +1899,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) connection **/ c->server->max_down_retries_count++; - ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] LDAP_SERVER_DOWN: retry count: %d", + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] LDAP_SERVER_DOWN (retry count: %d)", c->cnx_idx, c->server->max_down_retries_count); reconnect_asap = 1; } else { @@ -1904,11 +1907,15 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) "ngx_http_auth_ldap_read_handler: Cnx[%d] LDAP_SERVER_DOWN: No more reconnect retry", c->cnx_idx); } ngx_http_auth_ldap_close_connection(c, reconnect_asap); + } else { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_result() failed (%d: %s)", + c->cnx_idx, rc, ldap_err2string(rc)); } return; } if (rc == 0) { + // Timeout ({0, 0}) => No result message) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_result() -> rc=0", c->cnx_idx); break; } @@ -1958,14 +1965,14 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) if (ldap_msgtype(result) != LDAP_RES_BIND) { break; } - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received bind response (%d: %s [%s])", + ngx_log_error(NGX_LOG_INFO, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received bind response (%d: %s [%s])", c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); break; case STATE_SEARCHING: if (ldap_msgtype(result) == LDAP_RES_SEARCH_ENTRY) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received a search entry", c->cnx_idx); + ngx_log_error(NGX_LOG_INFO, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received a search entry", c->cnx_idx); if (c->rctx->dn.data == NULL) { dn = ldap_get_dn(c->ld, result); if (dn != NULL) { @@ -2010,7 +2017,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) ber_free(ber, 0); } } else if (ldap_msgtype(result) == LDAP_RES_SEARCH_RESULT) { - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received search result (%d: %s [%s])", + ngx_log_error(NGX_LOG_INFO, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received search result (%d: %s [%s])", c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); } @@ -2020,7 +2027,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) if (ldap_msgtype(result) != LDAP_RES_COMPARE) { break; } - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received comparison result (%d: %s [%s])", + ngx_log_error(NGX_LOG_INFO, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received comparison result (%d: %s [%s])", c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); break; @@ -2042,11 +2049,11 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) static void ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "ngx_http_auth_ldap_connect: Cnx[%d] Server \"%V\".", + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "ngx_http_auth_ldap_connect: Cnx[%d] Server \"%V\" connecting ...", c->cnx_idx, &c->server->alias); - // Clear an free any previous addrs from parsed_url, so that we can resolve again the LDAP server hostname + // Clear and free any previous addrs from parsed_url, so that we can resolve again the LDAP server hostname my_free_addrs_from_url(c->main_cnf->cnf_pool, &c->server->parsed_url); c->server->parsed_url.no_resolve = 0; // Try to resolve this time @@ -2111,8 +2118,8 @@ ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c) addr = &c->server->parsed_url.addrs[ngx_random() % c->server->parsed_url.naddrs]; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "ngx_http_auth_ldap_connect_continue: Cnx[%d] Connecting to LDAP server IP@ %V", + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "ngx_http_auth_ldap_connect_continue: Cnx[%d] Connecting to LDAP server IP@ %V ...", c->cnx_idx, &addr->name); pconn = &c->conn; @@ -2758,8 +2765,8 @@ ngx_http_auth_ldap_search(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) ngx_sprintf(filter, "(&%s(%s=%V))%Z", ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", ludpp->lud_attrs[0], &r->headers_in.user); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_search: Search filter is \"%s\"", - (const char *) filter); + ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_search: Cnx[%d] Searching (with filter \"%s\") ...", + ctx->c->cnx_idx, (const char *) filter); rc = ldap_search_ext(ctx->c->ld, ludpp->lud_dn, ludpp->lud_scope, (const char *) filter, ctx->server->attrs, 0, NULL, NULL, NULL, 0, &ctx->c->msgid); if (rc != LDAP_SUCCESS) { @@ -2787,6 +2794,8 @@ ngx_http_auth_ldap_search(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_search: Cnx[%d] Could not find user DN", ctx->c->cnx_idx); return NGX_ERROR; } else { + ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_search: Cnx[%d] Found dn: \"%s\")", + ctx->c->cnx_idx, ctx->dn.data); ctx->user_dn.len = ngx_strlen(ctx->dn.data); ctx->user_dn.data = (u_char *) ngx_palloc(ctx->r->pool, ctx->user_dn.len + 1); ngx_memcpy(ctx->user_dn.data, ctx->dn.data, ctx->user_dn.len + 1); @@ -2966,6 +2975,8 @@ ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *c cred.bv_val = (char *) r->headers_in.passwd.data; cred.bv_len = r->headers_in.passwd.len; + ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] Binding (user dn: %s) ...", + ctx->c->cnx_idx, ctx->user_dn.data); rc = ldap_sasl_bind(ctx->c->ld, (const char *) ctx->user_dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &ctx->c->msgid); if (rc != LDAP_SUCCESS) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] ldap_sasl_bind() failed (%d: %s)", @@ -2985,11 +2996,12 @@ ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *c /* On the second call, process the operation result */ if (ctx->error_code != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] User bind failed (%d: %s)", - ctx->c->cnx_idx, ctx->error_code, ldap_err2string(ctx->error_code)); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] User (dn: %s) bind failed (%d: %s)", + ctx->c->cnx_idx, ctx->user_dn.data, ctx->error_code, ldap_err2string(ctx->error_code)); ctx->outcome = OUTCOME_DENY; } else { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] User bind successful", ctx->c->cnx_idx); + ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] User (dn: %s) bind successfull", + ctx->c->cnx_idx, ctx->user_dn.data); ctx->outcome = OUTCOME_ALLOW; } return NGX_OK; @@ -3007,9 +3019,10 @@ ngx_http_auth_ldap_recover_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t return NGX_AGAIN; } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Rebinding to binddn"); cred.bv_val = (char *) ctx->server->bind_dn_passwd.data; cred.bv_len = ctx->server->bind_dn_passwd.len; + ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] Rebinding to binddn ...", + ctx->c->cnx_idx); rc = ldap_sasl_bind(ctx->c->ld, (const char *) ctx->server->bind_dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &ctx->c->msgid); if (rc != LDAP_SUCCESS) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] ldap_sasl_bind() failed (%d: %s)", @@ -3028,10 +3041,12 @@ ngx_http_auth_ldap_recover_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t /* On the second call, process the operation result */ if (ctx->error_code != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] binddn bind failed (%d: %s)", + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] Rebinding to binddn failed (%d: %s)", ctx->c->cnx_idx, ctx->error_code, ldap_err2string(ctx->error_code)); } else { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] binddn bind successful", ctx->c->cnx_idx); + + ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] Rebinding to binddn successful", + ctx->c->cnx_idx); } return NGX_OK; } From 3bdf4bcf91fde74d8e2b7aad93b8529d8c188db0 Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Thu, 11 Jul 2024 15:21:46 +0200 Subject: [PATCH 14/15] Fixes ngx_http_auth_ldap_close_connection recuursive loop --- ngx_http_auth_ldap_module.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index f83040e..55355bd 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -211,7 +211,8 @@ typedef enum { STATE_READY, STATE_BINDING, STATE_SEARCHING, - STATE_COMPARING + STATE_COMPARING, + STATE_DISCONNECTING } ngx_http_auth_ldap_connection_state_t; typedef struct ngx_http_auth_ldap_connection { @@ -1369,8 +1370,19 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c, int retr { ngx_queue_t *q; ngx_msec_t reconnect_delay = retry_asap ? RECONNECT_ASAP_MS : c->server->reconnect_timeout; // Default reconnect delay + ngx_http_auth_ldap_connection_state_t saved_state; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] retry_asap=%d", c->cnx_idx, retry_asap); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] retry_asap=%d state=%d", c->cnx_idx, retry_asap, c->state); + + if (c->state == STATE_DISCONNECTING) { + // Already in DISCONNECTING state. break here to avoid recuse in ngx_http_auth_ldap_close_connection + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] Already in DISCONNECTING state", c->cnx_idx); + return; + } + + // Temporary DISCONNECTING state to avoid loops in ngx_http_auth_ldap_close_connection + saved_state = c->state; + c->state = STATE_DISCONNECTING; if (c->ld) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] Unbinding from the server \"%V\")", @@ -1411,6 +1423,7 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c, int retr } c->rctx = NULL; + c->state = saved_state; // Restore initial state if (c->state != STATE_DISCONNECTED) { c->state = STATE_DISCONNECTED; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, From 930b19c8939d6c71b878288c9f42ba3b63f0a1da Mon Sep 17 00:00:00 2001 From: Eric BLANCHARD Date: Mon, 23 Jun 2025 09:30:43 +0200 Subject: [PATCH 15/15] Fixes re-bind error recovery --- ngx_http_auth_ldap_module.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 55355bd..031390e 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -2700,12 +2700,24 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t break; case PHASE_REBIND: - /* Initiate bind using the found DN and request password */ + /* Initiate bind using the Bind DN and associated password */ rc = ngx_http_auth_ldap_recover_bind(r, ctx); if (rc == NGX_AGAIN) { /* LDAP operation in progress, wait for the result */ return NGX_AGAIN; } + if (rc != NGX_OK) { + /* Re-Bind failed, but previous search and bind may have positive outcome */ + /* So close the current LDAP connection (that will be restarted ofter reconnect_timeout) */ + /* and continue with the next phase */ + if (ctx->c != NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Flushing [Cnx:%d] after re-bind failure", ctx->c->cnx_idx); + ngx_http_auth_ldap_close_connection(ctx->c, 0); + ctx->c = NULL; /* Prevent the cnx to be returned in free list in PHASE_NEXT */ + } + ctx->phase = PHASE_NEXT; + break; + } /* All steps done, finish the processing */ ctx->phase = PHASE_NEXT; @@ -3056,9 +3068,9 @@ ngx_http_auth_ldap_recover_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t if (ctx->error_code != LDAP_SUCCESS) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] Rebinding to binddn failed (%d: %s)", ctx->c->cnx_idx, ctx->error_code, ldap_err2string(ctx->error_code)); + return NGX_ERROR; } else { - - ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] Rebinding to binddn successful", + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] Rebinding to binddn successful", ctx->c->cnx_idx); } return NGX_OK;