├── LICENSE ├── README.md ├── config ├── example.conf └── ngx_http_auth_ldap_module.c /LICENSE: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011-2013 Valery Komarov 3 | * Copyright (C) 2013 Jiri Hruska 4 | * Copyright (C) 2015-2016 Victor Hahn Castell 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | * SUCH DAMAGE. 27 | */ 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LDAP Authentication module for nginx 2 | 3 | LDAP module for nginx which supports authentication against multiple LDAP servers. 4 | 5 | ## Project history 6 | 7 | This project is a clone of [nginx-auth-ldap](https://github.com/kvspb/nginx-auth-ldap) original module from [kvspb](https://github.com/kvspb). 8 | 9 | The reasons for this fork are: 10 | 11 | * The original project seems abondonned (no commit since 2 years). 12 | * Inherit from other contributors fixes/features: 13 | * [Pull request #237](https://github.com/kvspb/nginx-auth-ldap/pull/237) from [mmguero-dev](https://github.com/mmguero-dev/nginx-auth-ldap). 14 | * Compatible with Nginx 1.23.0 (http headers are now linked). 15 | * Add new features: 16 | * Add the use of `resolver` to resolve hostname of the LDAP server. 17 | * Support LDAP attributes fecthing during search. 18 | * Added an `encoding` attribute to the binddn_passwd parameter. 19 | * Manage connections waiting a reconnect delay in a specific queue, so that we can 20 | cancel the reconnect delay when a new request ask for an authentication and no free 21 | connection is available, but some are waiting to re-connect. 22 | * Fix the usage of `max_down_retries` parameter 23 | * Add the `clean_on_timeout` option 24 | 25 | ## How to install 26 | 27 | ### FreeBSD 28 | 29 | ```bash 30 | cd /usr/ports/www/nginx && make config install clean 31 | ``` 32 | 33 | Check HTTP_AUTH_LDAP options 34 | 35 | ```text 36 | [*] HTTP_AUTH_LDAP 3rd party http_auth_ldap module 37 | ``` 38 | 39 | ### Linux 40 | 41 | ```bash 42 | cd ~ && git clone https://github.com/Ericbla/nginx-auth-ldap.git 43 | ``` 44 | 45 | in nginx source folder 46 | 47 | ```bash 48 | ./configure --add-module=path_to_http_auth_ldap_module 49 | make install 50 | ``` 51 | 52 | ## Example configuration 53 | 54 | Define list of your LDAP servers with required user/group requirements: 55 | 56 | ```bash 57 | http { 58 | auth_ldap_resolver 8.8.8.8; 59 | 60 | ldap_server test1 { 61 | url ldap://192.168.0.1:3268/DC=test,DC=local?sAMAccountName?sub?(objectClass=person); 62 | binddn "TEST\\LDAPUSER"; 63 | binddn_passwd LDAPPASSWORD; 64 | group_attribute uniquemember; 65 | group_attribute_is_dn on; 66 | require valid_user; 67 | } 68 | 69 | ldap_server test2 { 70 | url ldap://192.168.0.2:3268/DC=test,DC=local?sAMAccountName?sub?(objectClass=person); 71 | binddn "TEST\\LDAPUSER"; 72 | binddn_passwd LDAPPASSWORD; 73 | group_attribute uniquemember; 74 | group_attribute_is_dn on; 75 | require valid_user; 76 | } 77 | } 78 | ``` 79 | 80 | And add required servers in correct order into your location/server directive: 81 | 82 | ```bash 83 | server { 84 | listen 8000; 85 | server_name localhost; 86 | 87 | auth_ldap "Forbidden"; 88 | auth_ldap_servers test1; 89 | auth_ldap_servers test2; 90 | 91 | location / { 92 | root html; 93 | index index.html index.htm; 94 | } 95 | 96 | } 97 | ``` 98 | 99 | ## Available config parameters 100 | 101 | ### auth_ldap_cache_enabled 102 | 103 | * Syntax: auth_ldap_cache_enabled on | off; 104 | * Default: auth_ldap_cache_enabled off; 105 | * Context: http 106 | 107 | ### auth_ldap_cache_expiration_time 108 | 109 | * Syntax: auth_ldap_cache_expiration_time time; 110 | * Default: auth_ldap_cache_expiration_time 10s; 111 | * Context: http 112 | 113 | Cache expiration time (see for time intervals syntax). 114 | 115 | ### auth_ldap_cache_size 116 | 117 | * Syntax: auth_ldap_cache_size size; 118 | * Default: auth_ldap_cache_size 100; 119 | * Context: http 120 | 121 | Number of cached LDAP authentications (min 100) 122 | 123 | ### auth_ldap_servers_size 124 | 125 | * Syntax: auth_ldap_servers_size size; 126 | * Syntax: auth_ldap_servers_size 7; 127 | * Context: http 128 | 129 | Maximum number of `ldap_server` elements to support 130 | 131 | ### auth_ldap 132 | 133 | * Syntax: auth_ldap off | _realm_; 134 | * Default: -- 135 | * Context: http, server, loc, limit_expect 136 | 137 | Set the _realm_ to be used with the `WWW-Authenticate` response header when authentication failed or is missing. 138 | 139 | ### auth_ldap_servers 140 | 141 | * Syntax: auth_ldap_servers _name_; 142 | * Default: -- 143 | * Context: http, server, loc, limit_expect 144 | 145 | Select the server _name_ to work with user authentication 146 | 147 | ### auth_ldap_resolver 148 | 149 | * Syntax: auth_ldap_resolver _address_ ... [valid=time] [ipv4=on|off] [ipv6=on|off] [status_zone=zone]; 150 | * Default: -- 151 | * Context: http 152 | 153 | The resolver to use as a fallback when the system hostname resolution 154 | (gethostbyname()) can't resolve the LDAP server hostname. 155 | See the `resolver` directive of the **ngx_http_core_module** 156 | 157 | ### auth_ldap_resolver_timeout 158 | 159 | * Syntax: auth_ldap_resolver_timeout time; 160 | * Default: auth_ldap_resolver_timeout 10s; 161 | * Context: http 162 | 163 | Resolver requests timeout (see for time intervals syntax). 164 | 165 | ### ldap_server 166 | 167 | * Syntax: ldap_server _name_ { ... } 168 | * Default: none 169 | * Context: http 170 | 171 | ## Configuration parameters for the `ldap_server` block 172 | 173 | ### url 174 | 175 | * Syntax: url _url_; 176 | * Default: -- 177 | * Context: `ldap_server` block 178 | 179 | url format: ldap[s]://host[:port]/dn?attrs?scope?filter[?exts] 180 | 181 | ### binddn 182 | 183 | * Syntax: binddn _dn_; 184 | * Default: -- 185 | * Context: `ldap_server` block 186 | 187 | The DN for the initial bind 188 | 189 | ### binddn_passwd 190 | 191 | * Syntax: binddn_passwd _password_ [text | base64 | hex]; 192 | * Default: -- 193 | * Context: `ldap_server` block 194 | 195 | The initial bind password. can be encoded in clear text (the default) or be encoded in base64 or HEX representation 196 | 197 | ### group_attribute 198 | 199 | * Syntax: group attr; 200 | * Default: -- 201 | * Context: `ldap_server` block 202 | 203 | ### group_attribute_is_dn 204 | 205 | * Syntax: group_attribute_is_dn on | off; 206 | * Default: group_attribute_is_dn off; 207 | * Context: `ldap_server` block 208 | 209 | Tell to search for full DN in member object. 210 | 211 | ### require 212 | 213 | * Syntax: require valid_user | user | group; 214 | * Default: --; 215 | * Context: `ldap_server` block 216 | 217 | ### satisfy 218 | 219 | * Syntax: satisfy all | any; 220 | * Default: --; 221 | * Context: `ldap_server` block 222 | 223 | ### max_down_retries 224 | 225 | * Syntax: max_down_retries _number_; 226 | * Default: max_down_retries 0; 227 | * Context: `ldap_server` block 228 | 229 | Retry count for attempting to reconnect to an LDAP server if it is considered 230 | "DOWN". This may happen if a KEEP-ALIVE connection to an LDAP server times 231 | out or is terminated by the server end after some amount of time. 232 | 233 | This can usually help with the following error: 234 | 235 | ```text 236 | http_auth_ldap: ldap_result() failed (-1: Can't contact LDAP server) 237 | ``` 238 | 239 | ### ssl_check_cert 240 | 241 | * Syntax: ssl_check_cert on | chain | off; 242 | * Default: ssl_check_cert off; 243 | * Context: `ldap_server` block 244 | 245 | Verify the remote certificate for LDAPs connections. If disabled, any remote certificate will be 246 | accepted which exposes you to possible man-in-the-middle attacks. Note that the server's 247 | certificate will need to be signed by a proper CA trusted by your system if this is enabled. 248 | See below how to trust CAs without installing them system-wide. 249 | 250 | This options needs OpenSSL >= 1.0.2; it is unavailable if compiled with older versions. 251 | 252 | When `chain` is given, verify cert chain but not hostname/IP in SAN 253 | 254 | ### ssl_ca_file 255 | 256 | * Syntax: ssl_ca_file _file-path_; 257 | * Default: --; 258 | * Context: `ldap_server` block 259 | 260 | Trust the CA certificate in this file (see ssl_check_cert above). 261 | 262 | ### ssl_ca_dir 263 | 264 | * Syntax: ssl_ca_file _dir-path_; 265 | * Default: --; 266 | * Context: `ldap_server` block 267 | 268 | Trust all CA certificates in this directory (see ssl_check_cert above). 269 | 270 | Note that you need to provide hash-based symlinks in the directory for this to work; 271 | you'll basically need to run OpenSSL's c_rehash command in this directory. 272 | 273 | ### referral 274 | 275 | * Syntax: referral on | off; 276 | * Default: referral on; 277 | * Context: `ldap_server` block 278 | 279 | LDAP library default is on. This option disables usage of referral messages from 280 | LDAP server. Usefull for authenticating against read only AD server without access 281 | to read write. 282 | 283 | ### attribute_header_prefix 284 | 285 | * Syntax: attribute_header_prefix _string_; 286 | * Default: attribute_header_prefix X-LDAP-ATTRS-; 287 | * Context: `ldap_server` block 288 | 289 | The prefix for the HEADER names used to carry the feteched attributes (default: "X-LDAP-ATTRS-") 290 | 291 | ### search_attributes 292 | 293 | * Syntax: search_attributes _attr1_ [ [ _attr2_ ] ... [ _attrN_ ] ]; 294 | * Default: -- 295 | * Context: `ldap_server` block 296 | 297 | 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. 298 | 299 | ### reconnect_timeout 300 | 301 | * Syntax: reconnect_timeout _timespec_; 302 | * Default: reconnect_timeout 10s; 303 | * Context: `ldap_server` block 304 | 305 | The delay before reconnection attempts (see for _timespec_ syntax) 306 | 307 | ### connections 308 | 309 | * Syntax: connections _count_; 310 | * Default: connections 1; 311 | * Context: `ldap_server` block 312 | 313 | The number of connections to the server use in // 314 | 315 | ### clean_on_timeout 316 | 317 | * Syntax: clean_on_timeout on | off; 318 | * Default: clean_on_timeout off; 319 | * Context: `ldap_server` block 320 | 321 | Tell the module to shutdown an re-connect a LDAP server connection after a 322 | send timeout detected (instead of just marking the connection as free again). 323 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_auth_ldap_module 2 | 3 | LDAP_REQUIRED_LIBS="-lldap" 4 | 5 | case "$NGX_PLATFORM" in 6 | Darwin:*|FreeBSD:*|Linux:*|SunOS:*) 7 | LDAP_REQUIRED_LIBS="$LDAP_REQUIRED_LIBS -llber" 8 | ;; 9 | esac 10 | 11 | if test -n "$ngx_module_link"; then 12 | ngx_module_type=HTTP 13 | ngx_module_name=ngx_http_auth_ldap_module 14 | ngx_module_incs= 15 | ngx_module_deps= 16 | ngx_module_order="ngx_http_auth_ldap_module ngx_http_access_module" 17 | ngx_module_srcs="$ngx_addon_dir/ngx_http_auth_ldap_module.c" 18 | ngx_module_libs="$LDAP_REQUIRED_LIBS" 19 | . auto/module 20 | else 21 | HTTP_MODULES="$HTTP_MODULES ngx_http_auth_ldap_module" 22 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_auth_ldap_module.c" 23 | CORE_LIBS="$CORE_LIBS $LDAP_REQUIRED_LIBS" 24 | fi 25 | -------------------------------------------------------------------------------- /example.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | events { 4 | worker_connections 1024; 5 | } 6 | 7 | http { 8 | include mime.types; 9 | default_type application/octet-stream; 10 | 11 | sendfile on; 12 | keepalive_timeout 65; 13 | 14 | auth_ldap_resolver 8.8.8.8; 15 | auth_ldap_cache_enabled on; 16 | 17 | # define ldap server 18 | ldap_server ad_1 { 19 | # user search base. 20 | url "ldap://:3268/OU=Offices,DC=company,DC=com?sAMAccountName?sub?(objectClass=person)"; 21 | # bind as 22 | binddn "CN=Operator,OU=Service Accounts,DC=company,DC=com"; 23 | # bind pw 24 | binddn_passwd ""; 25 | # Or binddn_passwd "base64()" base64; 26 | # Or binddn_passwd "hex()" hex; 27 | # Select attributes to be retrieved during the search (space separated list of attributes) 28 | search_attributes mail sn givenName; 29 | # group attribute name which contains member object 30 | group_attribute member; 31 | # search for full DN in member object 32 | group_attribute_is_dn on; 33 | # matching algorithm (any / all) 34 | satisfy any; 35 | # list of allowed groups 36 | require group "CN=Admins,OU=My Security Groups,DC=company,DC=com"; 37 | require group "CN=New York Users,OU=My Security Groups,DC=company,DC=com"; 38 | # list of allowed users 39 | # require 'valid_user' cannot be used together with 'user' as valid user is a superset 40 | # require valid_user; 41 | require user "CN=Batman,OU=Users,OU=New York Office,OU=Offices,DC=company,DC=com"; 42 | require user "CN=Robocop,OU=Users,OU=New York Office,OU=Offices,DC=company,DC=com"; 43 | } 44 | 45 | } 46 | 47 | server { 48 | listen 8081; 49 | server_name localhost; 50 | 51 | location / { 52 | # adding ldap authentication 53 | auth_ldap "Closed content"; 54 | auth_ldap_servers ad_1; 55 | 56 | root html; 57 | index index.html index.htm; 58 | } 59 | 60 | error_page 500 502 503 504 /50x.html; 61 | 62 | location = /50x.html { 63 | root html; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ngx_http_auth_ldap_module.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011-2013 Valery Komarov 3 | * Copyright (C) 2013 Jiri Hruska 4 | * Copyright (C) 2015-2016 Victor Hahn Castell 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | * SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | //#include 36 | 37 | // used for manual warnings 38 | #define XSTR(x) STR(x) 39 | #define STR(x) #x 40 | // make sure manual warnings don't get escalated to errors 41 | #ifdef __clang__ 42 | #pragma clang diagnostic warning "-W#warnings" 43 | #else 44 | #ifdef __GNUC__ 45 | #if GNUC > 4 46 | #pragma GCC diagnostic warning "-Wcpp" 47 | #endif 48 | #endif 49 | #endif 50 | // TODO: do the same stuff for MSVC and/or other compilers 51 | 52 | 53 | #ifndef LDAP_PROTO_EXT 54 | /* Some OpenLDAP headers are accidentally missing ldap_init_fd() etc. */ 55 | 56 | #define LDAP_PROTO_TCP 1 /* ldap:// */ 57 | #define LDAP_PROTO_UDP 2 /* reserved */ 58 | #define LDAP_PROTO_IPC 3 /* ldapi:// */ 59 | #define LDAP_PROTO_EXT 4 /* user-defined socket/sockbuf */ 60 | 61 | extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); 62 | #endif 63 | 64 | #define OUTCOME_ERROR -1 /* Some error occured in the process */ 65 | #define OUTCOME_DENY 0 66 | #define OUTCOME_ALLOW 1 67 | #define OUTCOME_CACHED_DENY 2 /* Cached results */ 68 | #define OUTCOME_CACHED_ALLOW 3 69 | #define OUTCOME_UNCERTAIN 4 /* Not yet decided */ 70 | 71 | #define NGX_HTTP_AUTH_LDAP_MAX_SERVERS_SIZE 7 72 | 73 | #define SSL_CERT_VERIFY_OFF 0 74 | #define SSL_CERT_VERIFY_FULL 1 75 | #define SSL_CERT_VERIFY_CHAIN 2 76 | 77 | #define LDAP_ATTR_HEADER_DEFAULT_PREFIX "X-LDAP-ATTR-" 78 | 79 | #define SANITIZE_NO_CONV 0 80 | #define SANITIZE_TO_UPPER 1 81 | #define SANITIZE_TO_LOWER 2 82 | 83 | #define ENCODING_TEXT 0 84 | #define ENCODING_B64 1 85 | #define ENCODING_HEX 2 86 | 87 | #define DEFAULT_ATTRS_COUNT 3 /* The default number of search attributes */ 88 | #define MAX_ATTRS_COUNT 5 /* Maximum search attributes to display in log */ 89 | 90 | #define RECONNECT_ASAP_MS 1000 /* Delay (in ms) for LDAP reconnection (when we want ASAP reconnect) */ 91 | 92 | 93 | 94 | typedef struct { 95 | LDAPURLDesc *ludpp; 96 | ngx_str_t url; 97 | ngx_url_t parsed_url; 98 | ngx_str_t alias; 99 | 100 | ngx_str_t bind_dn; 101 | ngx_str_t bind_dn_passwd; 102 | 103 | ngx_str_t group_attribute; 104 | ngx_flag_t group_attribute_dn; 105 | 106 | ngx_flag_t ssl_check_cert; 107 | ngx_str_t ssl_ca_dir; 108 | ngx_str_t ssl_ca_file; 109 | 110 | ngx_array_t *require_group; /* array of ngx_http_complex_value_t */ 111 | ngx_array_t *require_user; /* array of ngx_http_complex_value_t */ 112 | ngx_flag_t require_valid_user; 113 | ngx_http_complex_value_t require_valid_user_dn; 114 | ngx_flag_t satisfy_all; 115 | ngx_flag_t referral; 116 | ngx_flag_t clean_on_timeout; 117 | 118 | ngx_uint_t connections; 119 | ngx_uint_t max_down_retries; 120 | ngx_uint_t max_down_retries_count; 121 | ngx_msec_t connect_timeout; 122 | ngx_msec_t reconnect_timeout; 123 | ngx_msec_t bind_timeout; 124 | ngx_msec_t request_timeout; 125 | ngx_queue_t free_connections; /* Queue of free (ready) connections */ 126 | ngx_queue_t waiting_requests; /* Queue of ctx with not finished requests */ 127 | 128 | ngx_queue_t pending_reconnections; /* Queue of pending connections (waiting re-connect) */ 129 | char **attrs; /* Search attributes formated for ldap_search_ext() */ 130 | ngx_str_t attribute_header_prefix; 131 | } ngx_http_auth_ldap_server_t; 132 | 133 | typedef struct { 134 | ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t */ 135 | ngx_flag_t cache_enabled; 136 | ngx_msec_t cache_expiration_time; 137 | size_t cache_size; 138 | ngx_int_t servers_size; 139 | #if (NGX_OPENSSL) 140 | ngx_ssl_t ssl; 141 | #endif 142 | ngx_msec_t resolver_timeout; /* resolver_timeout */ 143 | ngx_resolver_t *resolver; /* resolver */ 144 | ngx_pool_t *cnf_pool; 145 | } ngx_http_auth_ldap_main_conf_t; 146 | 147 | typedef struct { 148 | ngx_str_t realm; 149 | ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t* */ 150 | } ngx_http_auth_ldap_loc_conf_t; 151 | 152 | 153 | typedef struct { 154 | ngx_str_t attr_name; 155 | ngx_str_t attr_value; 156 | } ldap_search_attribute_t; 157 | 158 | typedef struct { 159 | uint32_t small_hash; /* murmur2 hash of username ^ &server */ 160 | uint32_t outcome; /* OUTCOME_DENY or OUTCOME_ALLOW */ 161 | ngx_msec_t time; /* ngx_current_msec when created */ 162 | u_char big_hash[16]; /* md5 hash of (username, server, password) */ 163 | ngx_array_t attributes; /* Attributes (ldap_search_attribute_t) retreived during the search */ 164 | } ngx_http_auth_ldap_cache_elt_t; 165 | 166 | typedef struct { 167 | ngx_http_auth_ldap_cache_elt_t *buckets; 168 | ngx_uint_t num_buckets; 169 | ngx_uint_t elts_per_bucket; 170 | ngx_msec_t expiration_time; 171 | ngx_pool_t *pool; 172 | } ngx_http_auth_ldap_cache_t; 173 | 174 | typedef enum { 175 | PHASE_START, 176 | PHASE_SEARCH, 177 | PHASE_CHECK_USER, 178 | PHASE_CHECK_GROUP, 179 | PHASE_CHECK_BIND, 180 | PHASE_REBIND, 181 | PHASE_NEXT 182 | } ngx_http_auth_ldap_request_phase_t; 183 | 184 | typedef struct { 185 | ngx_http_request_t *r; 186 | ngx_uint_t server_index; 187 | ngx_http_auth_ldap_server_t *server; 188 | ngx_http_auth_ldap_request_phase_t phase; 189 | unsigned int iteration; 190 | int outcome; 191 | ngx_array_t attributes; /* Attributes (ldap_search_attribute_t) retreived during the search */ 192 | 193 | struct ngx_http_auth_ldap_connection *c; 194 | ngx_queue_t queue; /* Queue element to be chained in server->waiting_requests queue */ 195 | int replied; 196 | int error_code; 197 | ngx_str_t error_msg; 198 | ngx_str_t dn; 199 | ngx_str_t user_dn; 200 | ngx_str_t group_dn; 201 | 202 | ngx_http_auth_ldap_cache_elt_t *cache_bucket; 203 | u_char cache_big_hash[16]; 204 | uint32_t cache_small_hash; 205 | } ngx_http_auth_ldap_ctx_t; 206 | 207 | typedef enum { 208 | STATE_DISCONNECTED, 209 | STATE_INITIAL_BINDING, 210 | STATE_CONNECTING, 211 | STATE_READY, 212 | STATE_BINDING, 213 | STATE_SEARCHING, 214 | STATE_COMPARING, 215 | STATE_DISCONNECTING 216 | } ngx_http_auth_ldap_connection_state_t; 217 | 218 | typedef struct ngx_http_auth_ldap_connection { 219 | ngx_log_t *log; 220 | ngx_http_auth_ldap_main_conf_t *main_cnf; 221 | ngx_http_auth_ldap_server_t *server; 222 | ngx_peer_connection_t conn; 223 | ngx_event_t reconnect_event; 224 | 225 | #if (NGX_OPENSSL) 226 | ngx_pool_t *pool; 227 | ngx_ssl_t *ssl; 228 | #endif 229 | 230 | ngx_queue_t queue; /* Queue element to be chained in server->free_connections queue */ 231 | ngx_queue_t queue_pending; /* Queue element to be chained in server->pending_reconnections queue */ 232 | ngx_http_auth_ldap_ctx_t *rctx; 233 | 234 | LDAP* ld; 235 | ngx_http_auth_ldap_connection_state_t state; 236 | int msgid; 237 | ngx_resolver_ctx_t *resolver_ctx; 238 | ngx_uint_t cnx_idx; /* index of the connection from 0 to server->connections -1 */ 239 | } ngx_http_auth_ldap_connection_t; 240 | 241 | static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 242 | static char * ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); 243 | static char * ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 244 | static char * ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 245 | static char * ngx_http_auth_ldap_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 246 | static char * ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); 247 | static char * ngx_http_auth_ldap_parse_binddn_passwd(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); 248 | static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); 249 | static char * ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); 250 | static char * ngx_http_auth_ldap_parse_referral(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); 251 | static void * ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf); 252 | static char * ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent); 253 | static void * ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *); 254 | static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *); 255 | static ngx_int_t ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle); 256 | static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); 257 | static ngx_int_t ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle); 258 | static void ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c, int retry_asap); 259 | static void ngx_http_auth_ldap_set_pending_reconnection(ngx_http_auth_ldap_connection_t *c, ngx_msec_t reconnect_delay); 260 | static void ngx_http_auth_ldap_read_handler(ngx_event_t *rev); 261 | static void ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c); 262 | static void ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c); 263 | static void ngx_http_auth_ldap_reconnect_handler(ngx_event_t *); 264 | static void ngx_http_auth_ldap_reconnect_from_connection(ngx_http_auth_ldap_connection_t *c); 265 | static void ngx_http_auth_ldap_resolve_handler(ngx_resolver_ctx_t *ctx); 266 | static ngx_int_t ngx_http_auth_ldap_init_servers(ngx_cycle_t *cycle); 267 | static ngx_int_t ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle); 268 | static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r); 269 | static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, 270 | ngx_http_auth_ldap_loc_conf_t *conf); 271 | static ngx_int_t ngx_http_auth_ldap_search(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx); 272 | static ngx_int_t ngx_http_auth_ldap_check_user(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx); 273 | static ngx_int_t ngx_http_auth_ldap_check_group(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx); 274 | static ngx_int_t ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx); 275 | static ngx_int_t ngx_http_auth_ldap_recover_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx); 276 | #if (NGX_OPENSSL) 277 | static ngx_int_t ngx_http_auth_ldap_restore_handlers(ngx_connection_t *conn); 278 | #endif 279 | static u_char * santitize_str(u_char *str, ngx_uint_t conv); 280 | static ngx_uint_t my_hex_digit_value(u_char c); 281 | static ngx_uint_t my_hex_decode(ngx_str_t *dst, ngx_str_t *src); 282 | static void my_free_addrs_from_url(ngx_pool_t *pool, ngx_url_t *u); 283 | 284 | ngx_http_auth_ldap_cache_t ngx_http_auth_ldap_cache; 285 | 286 | static ngx_command_t ngx_http_auth_ldap_commands[] = { 287 | { 288 | ngx_string("ldap_server"), 289 | NGX_HTTP_MAIN_CONF | NGX_CONF_BLOCK | NGX_CONF_TAKE1, 290 | ngx_http_auth_ldap_ldap_server_block, 291 | NGX_HTTP_MAIN_CONF_OFFSET, 292 | 0, 293 | NULL 294 | }, 295 | { 296 | ngx_string("auth_ldap_cache_enabled"), 297 | NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, 298 | ngx_conf_set_flag_slot, 299 | NGX_HTTP_MAIN_CONF_OFFSET, 300 | offsetof(ngx_http_auth_ldap_main_conf_t, cache_enabled), 301 | NULL 302 | }, 303 | { 304 | ngx_string("auth_ldap_cache_expiration_time"), 305 | NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, 306 | ngx_conf_set_msec_slot, 307 | NGX_HTTP_MAIN_CONF_OFFSET, 308 | offsetof(ngx_http_auth_ldap_main_conf_t, cache_expiration_time), 309 | NULL 310 | }, 311 | { 312 | ngx_string("auth_ldap_cache_size"), 313 | NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, 314 | ngx_conf_set_size_slot, 315 | NGX_HTTP_MAIN_CONF_OFFSET, 316 | offsetof(ngx_http_auth_ldap_main_conf_t, cache_size), 317 | NULL 318 | }, 319 | { 320 | ngx_string("auth_ldap_servers_size"), 321 | NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, 322 | ngx_conf_set_num_slot, 323 | NGX_HTTP_MAIN_CONF_OFFSET, 324 | offsetof(ngx_http_auth_ldap_main_conf_t, servers_size), 325 | NULL 326 | }, 327 | { 328 | ngx_string("auth_ldap_resolver"), 329 | NGX_HTTP_MAIN_CONF | NGX_CONF_1MORE, 330 | ngx_http_auth_ldap_resolver, 331 | NGX_HTTP_MAIN_CONF_OFFSET, 332 | 0, 333 | NULL 334 | }, 335 | { 336 | ngx_string("auth_ldap_resolver_timeout"), 337 | NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, 338 | ngx_conf_set_msec_slot, 339 | NGX_HTTP_MAIN_CONF_OFFSET, 340 | offsetof(ngx_http_auth_ldap_main_conf_t, resolver_timeout), 341 | NULL 342 | }, 343 | { 344 | ngx_string("auth_ldap"), 345 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, 346 | ngx_http_auth_ldap, 347 | NGX_HTTP_LOC_CONF_OFFSET, 348 | 0, 349 | NULL 350 | }, 351 | { 352 | ngx_string("auth_ldap_servers"), 353 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_ANY, 354 | ngx_http_auth_ldap_servers, 355 | NGX_HTTP_LOC_CONF_OFFSET, 356 | 0, 357 | NULL 358 | }, 359 | ngx_null_command 360 | }; 361 | 362 | static ngx_http_module_t ngx_http_auth_ldap_module_ctx = { 363 | NULL, /* preconfiguration */ 364 | ngx_http_auth_ldap_init, /* postconfiguration */ 365 | ngx_http_auth_ldap_create_main_conf, /* create main configuration */ 366 | ngx_http_auth_ldap_init_main_conf, /* init main configuration */ 367 | NULL, /* create server configuration */ 368 | NULL, /* merge server configuration */ 369 | ngx_http_auth_ldap_create_loc_conf, /* create location configuration */ 370 | ngx_http_auth_ldap_merge_loc_conf /* merge location configuration */ 371 | }; 372 | 373 | ngx_module_t ngx_http_auth_ldap_module = { 374 | NGX_MODULE_V1, 375 | &ngx_http_auth_ldap_module_ctx, /* module context */ 376 | ngx_http_auth_ldap_commands, /* module directives */ 377 | NGX_HTTP_MODULE, /* module type */ 378 | NULL, /* init master */ 379 | NULL, /* init module */ 380 | ngx_http_auth_ldap_init_worker, /* init process */ 381 | NULL, /* init thread */ 382 | NULL, /* exit thread */ 383 | NULL, /* exit process */ 384 | NULL, /* exit master */ 385 | NGX_MODULE_V1_PADDING 386 | }; 387 | 388 | 389 | 390 | /*** Tools ***/ 391 | 392 | static u_char * santitize_str(u_char *str, ngx_uint_t conv) { 393 | static u_char sanitizeTable[256] = {0xff}; 394 | ngx_uint_t i; 395 | u_char *p; 396 | 397 | if (str == NULL || *str == '\0') { 398 | return str; 399 | } 400 | 401 | // Initialize un-initialized table at first use 402 | if (sanitizeTable[0] == 0xff) { 403 | // Initialize un initialized table once 404 | for (i = 0; i < sizeof(sanitizeTable); i++) { 405 | // Convert any char other than ALPHA / DIGIT / "-" / "_" to the "_" char 406 | sanitizeTable[i] = (isalnum(i) || i == '-' || i == '_') ? i : '_'; 407 | } 408 | } 409 | 410 | for (p = str; *p; p++) { 411 | if (conv == SANITIZE_TO_UPPER && *p >= 97 && *p <= 122) { 412 | // lowercase to uppercase 413 | *p -= 32; 414 | } else if (conv == SANITIZE_TO_LOWER && *p >= 65 && *p <= 90) { 415 | // lowercase to uppercase 416 | *p += 32; 417 | } else { 418 | *p = sanitizeTable[(int)*p]; 419 | } 420 | } 421 | return str; // Fluent interface 422 | } 423 | 424 | 425 | static ngx_uint_t 426 | my_hex_digit_value(u_char c) 427 | { 428 | if (c >= '0' && c <= '9') { 429 | return (ngx_uint_t)(c - '0'); 430 | } 431 | if (c >= 'a' && c <= 'f') { 432 | return (ngx_uint_t)(c - 'a' + 10); 433 | } 434 | if (c >= 'A' && c <= 'F') { 435 | return (ngx_uint_t)(c - 'A' + 10); 436 | } 437 | return 0; // Not an hex digit 438 | } 439 | 440 | static ngx_uint_t 441 | my_hex_decode(ngx_str_t *dst, ngx_str_t *src) 442 | { 443 | u_char *s, *d; 444 | ngx_uint_t i; 445 | 446 | for (i = 0, s = src->data, d = dst->data ; i < src->len -1; i += 2, s += 2) { 447 | *d++ = (u_char)(16 * my_hex_digit_value(*s) + my_hex_digit_value(*(s + 1))); 448 | } 449 | 450 | dst->len = (d - dst->data); 451 | return (ngx_uint_t)dst->len; 452 | } 453 | 454 | /*** Configuration and initialization ***/ 455 | 456 | /** 457 | * Reads ldap_server block and sets ngx_http_auth_ldap_ldap_server as a handler of each conf value 458 | */ 459 | static char * 460 | ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 461 | { 462 | char *rv; 463 | ngx_str_t *value, name; 464 | ngx_conf_t save; 465 | ngx_http_auth_ldap_server_t *server; 466 | ngx_http_auth_ldap_main_conf_t *cnf = conf; 467 | 468 | value = cf->args->elts; 469 | 470 | name = value[1]; 471 | 472 | if (ngx_strlen(name.data) == 0) { 473 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Missing server name in ldap_server"); 474 | return NGX_CONF_ERROR; 475 | } 476 | 477 | if (cnf->servers == NULL) { 478 | if (cnf->servers_size == NGX_CONF_UNSET) { 479 | cnf->servers_size = NGX_HTTP_AUTH_LDAP_MAX_SERVERS_SIZE; 480 | } 481 | cnf->servers = ngx_array_create(cf->pool, cnf->servers_size, sizeof(ngx_http_auth_ldap_server_t)); 482 | if (cnf->servers == NULL) { 483 | return NGX_CONF_ERROR; 484 | } 485 | } 486 | 487 | server = ngx_array_push(cnf->servers); 488 | if (server == NULL) { 489 | return NGX_CONF_ERROR; 490 | } 491 | 492 | ngx_memzero(server, sizeof(*server)); 493 | server->connect_timeout = 10000; 494 | server->reconnect_timeout = 10000; 495 | server->max_down_retries = 0; 496 | server->bind_timeout = 5000; 497 | server->request_timeout = 10000; 498 | server->alias = name; 499 | server->referral = 1; 500 | server->clean_on_timeout = 0; 501 | server->attribute_header_prefix.len = sizeof(LDAP_ATTR_HEADER_DEFAULT_PREFIX) -1; 502 | server->attribute_header_prefix.data = (u_char *)LDAP_ATTR_HEADER_DEFAULT_PREFIX; 503 | server->attrs = NULL; 504 | save = *cf; 505 | cf->handler = ngx_http_auth_ldap_ldap_server; 506 | cf->handler_conf = conf; 507 | rv = ngx_conf_parse(cf, NULL); 508 | *cf = save; 509 | 510 | if (rv != NGX_CONF_OK) { 511 | return rv; 512 | } 513 | 514 | return NGX_CONF_OK; 515 | } 516 | 517 | #define CONF_MSEC_VALUE(cf,value,server,x) \ 518 | if (ngx_strcmp(value[0].data, #x) == 0) { \ 519 | ngx_msec_t _i = ngx_parse_time(&value[1], 0); \ 520 | if (_i == (ngx_msec_t) NGX_ERROR || _i == 0) { \ 521 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: '" #x "' value has to be a valid time unit greater than 0"); \ 522 | return NGX_CONF_ERROR; \ 523 | } \ 524 | server->x = _i; \ 525 | } 526 | /** 527 | * Called for every variable inside ldap_server block 528 | */ 529 | static char * 530 | ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) 531 | { 532 | char *rv; 533 | ngx_str_t *value; 534 | ngx_http_auth_ldap_server_t *server; 535 | ngx_http_auth_ldap_main_conf_t *cnf = conf; 536 | ngx_int_t i; 537 | 538 | /* It should be safe to just use latest server from array */ 539 | server = ((ngx_http_auth_ldap_server_t *) cnf->servers->elts + (cnf->servers->nelts - 1)); 540 | 541 | value = cf->args->elts; 542 | 543 | /* TODO: Add more validation */ 544 | if (ngx_strcmp(value[0].data, "url") == 0) { 545 | return ngx_http_auth_ldap_parse_url(cf, server); 546 | } else if (ngx_strcmp(value[0].data, "binddn") == 0) { 547 | server->bind_dn = value[1]; 548 | } else if (ngx_strcmp(value[0].data, "binddn_passwd") == 0) { 549 | return ngx_http_auth_ldap_parse_binddn_passwd(cf, server); 550 | } else if (ngx_strcmp(value[0].data, "group_attribute") == 0) { 551 | server->group_attribute = value[1]; 552 | } else if (ngx_strcmp(value[0].data, "group_attribute_is_dn") == 0 && ngx_strcmp(value[1].data, "on") == 0) { 553 | server->group_attribute_dn = 1; 554 | } else if (ngx_strcmp(value[0].data, "search_attributes") == 0 && cf->args->nelts >= 2) { 555 | ngx_uint_t j, attrs_count = cf->args->nelts -1; 556 | server->attrs = ngx_palloc(cf->pool, (attrs_count + 1) * sizeof(char *)); 557 | for (j = 0; j < attrs_count; j++) { 558 | server->attrs[j] = (char *)value[j + 1].data; 559 | } 560 | server->attrs[attrs_count] = NULL; // Last element of the list 561 | } else if (ngx_strcmp(value[0].data, "attribute_header_prefix") == 0 && cf->args->nelts >= 2) { 562 | santitize_str(value[1].data, SANITIZE_NO_CONV); // The prefix is sanitized 563 | server->attribute_header_prefix = value[1]; 564 | } else if (ngx_strcmp(value[0].data, "require") == 0) { 565 | return ngx_http_auth_ldap_parse_require(cf, server); 566 | } else if (ngx_strcmp(value[0].data, "satisfy") == 0) { 567 | return ngx_http_auth_ldap_parse_satisfy(cf, server); 568 | } else if (ngx_strcmp(value[0].data, "referral") == 0) { 569 | return ngx_http_auth_ldap_parse_referral(cf, server); 570 | } else if (ngx_strcmp(value[0].data, "clean_on_timeout") == 0) { 571 | if (ngx_strcasecmp(value[1].data, (u_char *) "on") == 0) { 572 | server->clean_on_timeout = 1; 573 | } else { 574 | server->clean_on_timeout = 0; 575 | } 576 | } else if (ngx_strcmp(value[0].data, "max_down_retries") == 0) { 577 | i = ngx_atoi(value[1].data, value[1].len); 578 | if (i == NGX_ERROR) { 579 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: 'max_down_retries' value must be an integer: using default of 0"); 580 | i = 0; 581 | } 582 | server->max_down_retries = i; 583 | } else if (ngx_strcmp(value[0].data, "connections") == 0) { 584 | i = ngx_atoi(value[1].data, value[1].len); 585 | if (i == NGX_ERROR || i == 0) { 586 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: 'connections' value has to be a number greater than 0"); 587 | return NGX_CONF_ERROR; 588 | } 589 | server->connections = i; 590 | } else if (ngx_strcmp(value[0].data, "ssl_check_cert") == 0) { 591 | #if OPENSSL_VERSION_NUMBER >= 0x10002000 592 | if ((ngx_strcmp(value[1].data, "on") == 0) || (ngx_strcmp(value[1].data, "full") == 0)) { 593 | server->ssl_check_cert = SSL_CERT_VERIFY_FULL; 594 | } else if (ngx_strcmp(value[1].data, "chain") == 0) { 595 | server->ssl_check_cert = SSL_CERT_VERIFY_CHAIN; 596 | } else { 597 | server->ssl_check_cert = SSL_CERT_VERIFY_OFF; 598 | } 599 | #else 600 | #if GNUC > 4 601 | #warning "http_auth_ldap: Compiling with OpenSSL < 1.0.2, certificate verification will be unavailable. OPENSSL_VERSION_NUMBER == " XSTR(OPENSSL_VERSION_NUMBER) 602 | #endif 603 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 604 | "http_auth_ldap: 'ssl_cert_check': cannot verify remote certificate's domain name because " 605 | "your version of OpenSSL is too old. " 606 | "Please install OpenSSL >= 1.0.2 and recompile nginx."); 607 | #endif 608 | } else if (ngx_strcmp(value[0].data, "ssl_ca_dir") == 0) { 609 | server->ssl_ca_dir = value[1]; 610 | } else if (ngx_strcmp(value[0].data, "ssl_ca_file") == 0) { 611 | server->ssl_ca_file = value[1]; 612 | } 613 | else CONF_MSEC_VALUE(cf,value,server,connect_timeout) 614 | else CONF_MSEC_VALUE(cf,value,server,reconnect_timeout) 615 | else CONF_MSEC_VALUE(cf,value,server,bind_timeout) 616 | else CONF_MSEC_VALUE(cf,value,server,request_timeout) 617 | else if (ngx_strcmp(value[0].data, "include") == 0) { 618 | return ngx_conf_include(cf, dummy, conf); 619 | } 620 | 621 | rv = NGX_CONF_OK; 622 | 623 | return rv; 624 | } 625 | 626 | /** 627 | * Parse auth_ldap directive 628 | */ 629 | static char * 630 | ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 631 | { 632 | ngx_str_t *value = cf->args->elts; 633 | ngx_http_auth_ldap_loc_conf_t *cnf = conf; 634 | u_char *p; 635 | 636 | if (ngx_strcmp(value[1].data, "off") == 0) { 637 | ngx_str_set(&cnf->realm, ""); 638 | return NGX_CONF_OK; 639 | } 640 | 641 | cnf->realm.len = sizeof("Basic realm=\"") - 1 + value[1].len + 1; 642 | cnf->realm.data = ngx_pcalloc(cf->pool, cnf->realm.len); 643 | if (cnf->realm.data == NULL) { 644 | return NGX_CONF_ERROR; 645 | } 646 | 647 | p = ngx_cpymem(cnf->realm.data, "Basic realm=\"", sizeof("Basic realm=\"") - 1); 648 | p = ngx_cpymem(p, value[1].data, value[1].len); 649 | *p = '"'; 650 | 651 | return NGX_CONF_OK; 652 | } 653 | 654 | /** 655 | * Parse auth_ldap_servers directive 656 | */ 657 | static char * 658 | ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 659 | { 660 | ngx_http_auth_ldap_loc_conf_t *cnf; 661 | ngx_http_auth_ldap_main_conf_t *mconf; 662 | ngx_http_auth_ldap_server_t *server, *s, **target; 663 | ngx_str_t *value; 664 | ngx_uint_t i, j; 665 | 666 | cnf = conf; 667 | mconf = ngx_http_conf_get_module_main_conf(cf, ngx_http_auth_ldap_module); 668 | 669 | for (i = 1; i < cf->args->nelts; i++) { 670 | value = &((ngx_str_t *) cf->args->elts)[i]; 671 | server = NULL; 672 | if (mconf->servers == NULL) { 673 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Using \"auth_ldap_servers\" when no \"ldap_server\" has been previously defined" 674 | " (make sure that \"auth_ldap_servers\" goes after \"ldap_server\"s in your configuration file)", value); 675 | return NGX_CONF_ERROR; 676 | } 677 | 678 | for (j = 0; j < mconf->servers->nelts; j++) { 679 | s = &((ngx_http_auth_ldap_server_t *) mconf->servers->elts)[j]; 680 | if (s->alias.len == value->len && ngx_memcmp(s->alias.data, value->data, s->alias.len) == 0) { 681 | server = s; 682 | break; 683 | } 684 | } 685 | 686 | if (server == NULL) { 687 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Server \"%V\" has not been defined", value); 688 | return NGX_CONF_ERROR; 689 | } 690 | 691 | 692 | if (cnf->servers == NGX_CONF_UNSET_PTR) { 693 | cnf->servers = ngx_array_create(cf->pool, 4, sizeof(ngx_http_auth_ldap_server_t *)); 694 | if (cnf->servers == NULL) { 695 | return NGX_CONF_ERROR; 696 | } 697 | } 698 | 699 | target = (ngx_http_auth_ldap_server_t **) ngx_array_push(cnf->servers); 700 | if (target == NULL) { 701 | return NGX_CONF_ERROR; 702 | } 703 | 704 | *target = server; 705 | } 706 | 707 | return NGX_CONF_OK; 708 | } 709 | 710 | /** 711 | * Parse resolver directive 712 | */ 713 | static char * 714 | ngx_http_auth_ldap_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 715 | { 716 | ngx_http_auth_ldap_main_conf_t *cnf = conf; 717 | ngx_str_t *value; 718 | 719 | if (cnf->resolver) { 720 | return "is duplicate"; 721 | } 722 | 723 | value = cf->args->elts; 724 | 725 | cnf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); 726 | if (cnf->resolver == NULL) { 727 | return NGX_CONF_ERROR; 728 | } 729 | 730 | ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, 731 | "ngx_http_auth_ldap_resolver: Configured resolver %V", 732 | &value[1]); 733 | 734 | return NGX_CONF_OK; 735 | } 736 | 737 | /** 738 | * Parse URL conf parameter 739 | */ 740 | static char * 741 | ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) 742 | { 743 | ngx_str_t *value; 744 | u_char *p; 745 | 746 | value = cf->args->elts; 747 | 748 | int rc = ldap_url_parse((const char *) value[1].data, &server->ludpp); 749 | if (rc != LDAP_SUCCESS) { 750 | switch (rc) { 751 | case LDAP_URL_ERR_MEM: 752 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Cannot allocate memory space."); 753 | break; 754 | 755 | case LDAP_URL_ERR_PARAM: 756 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid parameter."); 757 | break; 758 | 759 | case LDAP_URL_ERR_BADSCHEME: 760 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: URL doesnt begin with \"ldap[s]://\"."); 761 | break; 762 | 763 | case LDAP_URL_ERR_BADENCLOSURE: 764 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: URL is missing trailing \">\"."); 765 | break; 766 | 767 | case LDAP_URL_ERR_BADURL: 768 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid URL."); 769 | break; 770 | 771 | case LDAP_URL_ERR_BADHOST: 772 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Host port is invalid."); 773 | break; 774 | 775 | case LDAP_URL_ERR_BADATTRS: 776 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid or missing attributes."); 777 | break; 778 | 779 | case LDAP_URL_ERR_BADSCOPE: 780 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid or missing scope string."); 781 | break; 782 | 783 | case LDAP_URL_ERR_BADFILTER: 784 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid or missing filter."); 785 | break; 786 | 787 | case LDAP_URL_ERR_BADEXTS: 788 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid or missing extensions."); 789 | break; 790 | } 791 | return NGX_CONF_ERROR; 792 | } 793 | 794 | if (server->ludpp->lud_attrs == NULL) { 795 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: No user attribute specified in auth_ldap_url."); 796 | return NGX_CONF_ERROR; 797 | } 798 | 799 | server->url.data = ngx_palloc(cf->pool, ngx_strlen(server->ludpp->lud_scheme) + sizeof("://") - 1 + 800 | ngx_strlen(server->ludpp->lud_host) + sizeof(":65535")); 801 | p = ngx_sprintf(server->url.data, "%s://%s:%d%Z", server->ludpp->lud_scheme, server->ludpp->lud_host, 802 | server->ludpp->lud_port); 803 | server->url.len = p - server->url.data - 1; 804 | 805 | ngx_memzero(&server->parsed_url, sizeof(ngx_url_t)); 806 | server->parsed_url.url.data = (u_char *) server->ludpp->lud_host; 807 | server->parsed_url.url.len = ngx_strlen(server->ludpp->lud_host); 808 | server->parsed_url.default_port = server->ludpp->lud_port; 809 | server->parsed_url.no_resolve = 1; // Do not use hostanme resolution checking during configuration parsing 810 | if (ngx_parse_url(cf->pool, &server->parsed_url) != NGX_OK) { 811 | if (server->parsed_url.err) { 812 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: %s in LDAP hostname \"%V\"", 813 | server->parsed_url.err, &server->parsed_url.url); 814 | } 815 | return NGX_CONF_ERROR; 816 | } 817 | server->parsed_url.no_resolve = 0; // Reset the no_resolve flag 818 | if (ngx_strcmp(server->ludpp->lud_scheme, "ldap") == 0) { 819 | return NGX_CONF_OK; 820 | #if (NGX_OPENSSL) 821 | } else if (ngx_strcmp(server->ludpp->lud_scheme, "ldaps") == 0) { 822 | ngx_http_auth_ldap_main_conf_t *halmcf = 823 | ngx_http_conf_get_module_main_conf(cf, ngx_http_auth_ldap_module); 824 | ngx_uint_t protos = NGX_SSL_SSLv2 | NGX_SSL_SSLv3 | 825 | NGX_SSL_TLSv1 | NGX_SSL_TLSv1_1 | NGX_SSL_TLSv1_2; 826 | if (halmcf->ssl.ctx == NULL && ngx_ssl_create(&halmcf->ssl, protos, halmcf) != NGX_OK) { 827 | return NGX_CONF_ERROR; 828 | } 829 | return NGX_CONF_OK; 830 | #endif 831 | } else { 832 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Protocol \"%s://\" is not supported.", 833 | server->ludpp->lud_scheme); 834 | return NGX_CONF_ERROR; 835 | } 836 | } 837 | 838 | static char * 839 | ngx_http_auth_ldap_parse_binddn_passwd(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) 840 | { 841 | ngx_str_t *value; 842 | ngx_int_t encoding = ENCODING_TEXT; 843 | 844 | value = cf->args->elts; 845 | if (cf->args->nelts < 2 || cf->args->nelts > 3) { 846 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Bad number of parameters for binddn_passwd"); 847 | return NGX_CONF_ERROR; 848 | } 849 | 850 | if (cf->args->nelts == 3 && value[1].len != 0) { 851 | // Non empty password and have a second parameter value (encoding) 852 | if (ngx_strcmp(value[2].data, "text") == 0) { 853 | encoding = ENCODING_TEXT; 854 | } else if (ngx_strcmp(value[2].data, "base64") == 0) { 855 | encoding = ENCODING_B64; 856 | } else if (ngx_strcmp(value[2].data, "hex") == 0) { 857 | encoding = ENCODING_HEX; 858 | } else { 859 | ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 860 | "http_auth_ldap: Unknown encoding ('%V') for binddn_password. (assumming clear text)", 861 | value[2]); 862 | } 863 | } 864 | 865 | if (encoding == ENCODING_TEXT) { 866 | // Just copy reference 867 | server->bind_dn_passwd = value[1]; 868 | } else if (encoding == ENCODING_B64) { 869 | // Base 64 decode 870 | server->bind_dn_passwd.len = 0; 871 | int decoded_length = 3 * value[1].len / 4; 872 | server->bind_dn_passwd.data = ngx_pnalloc(cf->pool, decoded_length + 1); 873 | ngx_decode_base64(&server->bind_dn_passwd, &value[1]); 874 | server->bind_dn_passwd.data[decoded_length] = '\0'; 875 | } else if (encoding == ENCODING_HEX) { 876 | // Hex decode 877 | server->bind_dn_passwd.len = 0; 878 | int decoded_length = value[1].len / 2; 879 | server->bind_dn_passwd.data = ngx_pnalloc(cf->pool, decoded_length + 1); 880 | my_hex_decode(&server->bind_dn_passwd, &value[1]); 881 | server->bind_dn_passwd.data[decoded_length] = '\0'; 882 | } 883 | 884 | return NGX_CONF_OK; 885 | } 886 | 887 | /** 888 | * Parse "require" conf parameter 889 | */ 890 | static char * 891 | ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) 892 | { 893 | ngx_str_t *value; 894 | ngx_http_complex_value_t* target = NULL; 895 | ngx_http_compile_complex_value_t ccv; 896 | 897 | value = cf->args->elts; 898 | //ngx_conf_log_error(NGX_LOG_DEBUG | NGX_LOG_DEBUG_HTTP, cf, 0, "http_auth_ldap: parse_require"); 899 | 900 | if (ngx_strcmp(value[1].data, "valid_user") == 0) { 901 | server->require_valid_user = 1; 902 | if (cf->args->nelts < 3) { 903 | return NGX_CONF_OK; 904 | } 905 | if (server->require_valid_user_dn.value.data != NULL) { 906 | return "is duplicate"; 907 | } 908 | target = &server->require_valid_user_dn; 909 | } else if (ngx_strcmp(value[1].data, "user") == 0) { 910 | if (server->require_user == NULL) { 911 | server->require_user = ngx_array_create(cf->pool, 4, sizeof(ngx_http_complex_value_t)); 912 | if (server->require_user == NULL) { 913 | return NGX_CONF_ERROR; 914 | } 915 | } 916 | target = ngx_array_push(server->require_user); 917 | } else if (ngx_strcmp(value[1].data, "group") == 0) { 918 | ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, "http_auth_ldap: Setting group"); 919 | if (server->require_group == NULL) { 920 | server->require_group = ngx_array_create(cf->pool, 4, sizeof(ngx_http_complex_value_t)); 921 | if (server->require_group == NULL) { 922 | return NGX_CONF_ERROR; 923 | } 924 | } 925 | target = ngx_array_push(server->require_group); 926 | } 927 | 928 | if (target == NULL) { 929 | return NGX_CONF_ERROR; 930 | } 931 | 932 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 933 | ccv.cf = cf; 934 | ccv.value = &value[2]; 935 | ccv.complex_value = target; 936 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 937 | return NGX_CONF_ERROR; 938 | } 939 | 940 | return NGX_CONF_OK; 941 | } 942 | 943 | /** 944 | * Parse "satisfy" conf parameter 945 | */ 946 | static char * 947 | ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) 948 | { 949 | ngx_str_t *value; 950 | value = cf->args->elts; 951 | 952 | if (ngx_strcmp(value[1].data, "all") == 0) { 953 | ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, "http_auth_ldap: Setting satisfy all"); 954 | server->satisfy_all = 1; 955 | return NGX_CONF_OK; 956 | } 957 | 958 | if (ngx_strcmp(value[1].data, "any") == 0) { 959 | server->satisfy_all = 0; 960 | return NGX_CONF_OK; 961 | } 962 | 963 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Incorrect value for auth_ldap_satisfy"); 964 | return NGX_CONF_ERROR; 965 | } 966 | 967 | /** 968 | * Parse "referral" conf parameter 969 | */ 970 | static char * 971 | ngx_http_auth_ldap_parse_referral(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) 972 | { 973 | ngx_str_t *value; 974 | value = cf->args->elts; 975 | 976 | if (ngx_strcmp(value[1].data, "on") == 0) { 977 | server->referral = 1; 978 | return NGX_CONF_OK; 979 | } 980 | 981 | if (ngx_strcmp(value[1].data, "off") == 0) { 982 | server->referral = 0; 983 | return NGX_CONF_OK; 984 | } 985 | 986 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Incorrect value for referral"); 987 | return NGX_CONF_ERROR; 988 | } 989 | 990 | 991 | 992 | /** 993 | * Create main config which will store ldap_servers array 994 | */ 995 | static void * 996 | ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf) 997 | { 998 | ngx_http_auth_ldap_main_conf_t *conf; 999 | 1000 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_ldap_main_conf_t)); 1001 | if (conf == NULL) { 1002 | return NULL; 1003 | } 1004 | 1005 | conf->cnf_pool = cf->pool; 1006 | conf->cache_enabled = NGX_CONF_UNSET; 1007 | conf->cache_expiration_time = NGX_CONF_UNSET_MSEC; 1008 | conf->cache_size = NGX_CONF_UNSET_SIZE; 1009 | conf->servers_size = NGX_CONF_UNSET; 1010 | conf->resolver_timeout = NGX_CONF_UNSET_MSEC; 1011 | conf->resolver = NULL; 1012 | 1013 | return conf; 1014 | } 1015 | 1016 | static char * 1017 | ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent) 1018 | { 1019 | ngx_http_auth_ldap_main_conf_t *conf = parent; 1020 | 1021 | if (conf->resolver_timeout == NGX_CONF_UNSET_MSEC) { 1022 | conf->resolver_timeout = 10000; 1023 | } 1024 | 1025 | if (conf->cache_enabled == NGX_CONF_UNSET) { 1026 | conf->cache_enabled = 0; 1027 | } 1028 | if (conf->cache_enabled == 0) { 1029 | return NGX_CONF_OK; 1030 | } 1031 | 1032 | if (conf->cache_size == NGX_CONF_UNSET_SIZE) { 1033 | conf->cache_size = 100; 1034 | } 1035 | if (conf->cache_size < 100) { 1036 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "http_auth_ldap: auth_ldap_cache_size cannot be smaller than 100 entries."); 1037 | return NGX_CONF_ERROR; 1038 | } 1039 | 1040 | if (conf->cache_expiration_time == NGX_CONF_UNSET_MSEC) { 1041 | conf->cache_expiration_time = 10000; 1042 | } 1043 | if (conf->cache_expiration_time < 1000) { 1044 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "http_auth_ldap: auth_ldap_cache_expiration_time cannot be smaller than 1000 ms."); 1045 | return NGX_CONF_ERROR; 1046 | } 1047 | 1048 | return NGX_CONF_OK; 1049 | } 1050 | 1051 | /** 1052 | * Create location conf 1053 | */ 1054 | static void * 1055 | ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *cf) 1056 | { 1057 | ngx_http_auth_ldap_loc_conf_t *conf; 1058 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_ldap_loc_conf_t)); 1059 | if (conf == NULL) { 1060 | return NULL; 1061 | } 1062 | conf->servers = NGX_CONF_UNSET_PTR; 1063 | 1064 | return conf; 1065 | } 1066 | 1067 | /** 1068 | * Merge location conf 1069 | */ 1070 | static char * 1071 | ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 1072 | { 1073 | ngx_http_auth_ldap_loc_conf_t *prev = parent; 1074 | ngx_http_auth_ldap_loc_conf_t *conf = child; 1075 | 1076 | if (conf->realm.data == NULL) { 1077 | conf->realm = prev->realm; 1078 | } 1079 | ngx_conf_merge_ptr_value(conf->servers, prev->servers, NULL); 1080 | 1081 | return NGX_CONF_OK; 1082 | } 1083 | 1084 | static ngx_int_t 1085 | ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle) 1086 | { 1087 | ngx_int_t rc; 1088 | 1089 | if (ngx_process != NGX_PROCESS_SINGLE && ngx_process != NGX_PROCESS_WORKER) { 1090 | return NGX_OK; 1091 | } 1092 | 1093 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "ngx_http_auth_ldap_init_worker:"); 1094 | 1095 | rc = ngx_http_auth_ldap_init_servers(cycle); 1096 | if (rc != NGX_OK) { 1097 | return rc; 1098 | } 1099 | 1100 | rc = ngx_http_auth_ldap_init_cache(cycle); 1101 | if (rc != NGX_OK) { 1102 | return rc; 1103 | } 1104 | 1105 | rc = ngx_http_auth_ldap_init_connections(cycle); 1106 | if (rc != NGX_OK) { 1107 | return rc; 1108 | } 1109 | 1110 | return NGX_OK; 1111 | } 1112 | 1113 | /** 1114 | * Init module and add ldap auth handler to NGX_HTTP_ACCESS_PHASE 1115 | */ 1116 | static ngx_int_t 1117 | ngx_http_auth_ldap_init(ngx_conf_t *cf) 1118 | { 1119 | ngx_http_handler_pt *h; 1120 | ngx_http_core_main_conf_t *cmcf; 1121 | 1122 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 1123 | 1124 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); 1125 | if (h == NULL) { 1126 | return NGX_ERROR; 1127 | } 1128 | 1129 | *h = ngx_http_auth_ldap_handler; 1130 | return NGX_OK; 1131 | } 1132 | 1133 | 1134 | /*** Authentication cache ***/ 1135 | 1136 | static ngx_int_t 1137 | ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle) 1138 | { 1139 | ngx_http_auth_ldap_main_conf_t *conf; 1140 | ngx_uint_t want, count, i; 1141 | ngx_http_auth_ldap_cache_t *cache; 1142 | static const uint16_t primes[] = { 1143 | 13, 53, 101, 151, 199, 263, 317, 383, 443, 503, 1144 | 577, 641, 701, 769, 839, 911, 983, 1049, 1109 1145 | }; 1146 | 1147 | cache = &ngx_http_auth_ldap_cache; 1148 | cache->pool = cycle->pool; 1149 | 1150 | conf = (ngx_http_auth_ldap_main_conf_t *) ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); 1151 | if (conf == NULL || !conf->cache_enabled) { 1152 | return NGX_OK; 1153 | } 1154 | 1155 | want = (conf->cache_size + 7) / 8; 1156 | count = 0; 1157 | for (i = 0; i < sizeof(primes)/sizeof(primes[0]); i++) { 1158 | count = primes[i]; 1159 | if (count >= want) { 1160 | break; 1161 | } 1162 | } 1163 | 1164 | 1165 | cache->expiration_time = conf->cache_expiration_time; 1166 | cache->num_buckets = count; 1167 | cache->elts_per_bucket = 8; 1168 | 1169 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "http_auth_ldap: Allocating %ud bytes of LDAP cache (ttl=%dms).", 1170 | cache->num_buckets * cache->elts_per_bucket * sizeof(ngx_http_auth_ldap_cache_elt_t), cache->expiration_time); 1171 | 1172 | cache->buckets = (ngx_http_auth_ldap_cache_elt_t *) ngx_calloc(count * 8 * sizeof(ngx_http_auth_ldap_cache_elt_t), cycle->log); 1173 | if (cache->buckets == NULL) { 1174 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "http_auth_ldap: Unable to allocate memory for LDAP cache."); 1175 | return NGX_ERROR; 1176 | } 1177 | 1178 | /* Initialize the attributes array in each cache entry */ 1179 | ngx_http_auth_ldap_cache_elt_t *cache_entry = cache->buckets; 1180 | for (i = 0; i < cache->num_buckets * cache->elts_per_bucket; i++, cache_entry++) { 1181 | ngx_array_init(&cache_entry->attributes, cache->pool, DEFAULT_ATTRS_COUNT, sizeof(ldap_search_attribute_t)); 1182 | } 1183 | 1184 | return NGX_OK; 1185 | } 1186 | 1187 | static ngx_int_t 1188 | ngx_http_auth_ldap_check_cache(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, 1189 | ngx_http_auth_ldap_cache_t *cache, ngx_http_auth_ldap_server_t *server) 1190 | { 1191 | ngx_http_auth_ldap_cache_elt_t *elt; 1192 | ngx_md5_t md5ctx; 1193 | ngx_msec_t time_limit; 1194 | ngx_uint_t i; 1195 | 1196 | ctx->cache_small_hash = ngx_murmur_hash2(r->headers_in.user.data, r->headers_in.user.len) ^ (uint32_t) (ngx_uint_t) server; 1197 | 1198 | ngx_md5_init(&md5ctx); 1199 | ngx_md5_update(&md5ctx, r->headers_in.user.data, r->headers_in.user.len); 1200 | ngx_md5_update(&md5ctx, server, offsetof(ngx_http_auth_ldap_server_t, free_connections)); 1201 | ngx_md5_update(&md5ctx, r->headers_in.passwd.data, r->headers_in.passwd.len); 1202 | ngx_md5_final(ctx->cache_big_hash, &md5ctx); 1203 | 1204 | ctx->cache_bucket = &cache->buckets[ctx->cache_small_hash % cache->num_buckets]; 1205 | 1206 | elt = ctx->cache_bucket; 1207 | time_limit = ngx_current_msec - cache->expiration_time; 1208 | for (i = 0; i < cache->elts_per_bucket; i++, elt++) { 1209 | if (elt->small_hash == ctx->cache_small_hash && 1210 | elt->time > time_limit && 1211 | memcmp(elt->big_hash, ctx->cache_big_hash, 16) == 0) { 1212 | if (elt->outcome == OUTCOME_ALLOW || elt->outcome == OUTCOME_CACHED_ALLOW) { 1213 | /* Restore the cached attributes to the current context */ 1214 | ctx->attributes.nelts = 0; 1215 | for (i = 0; i < elt->attributes.nelts; i++) { 1216 | ldap_search_attribute_t *ctx_attr = ngx_array_push(&ctx->attributes); 1217 | *ctx_attr = *((ldap_search_attribute_t *)elt->attributes.elts + i); 1218 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1219 | "http_auth_ldap: ngx_http_auth_ldap_check_cache: restoring attribute '%V' = '%V' from cache", 1220 | &ctx_attr->attr_name, &ctx_attr->attr_value); 1221 | } 1222 | } 1223 | return elt->outcome; 1224 | } 1225 | } 1226 | 1227 | return -1; 1228 | } 1229 | 1230 | static void 1231 | ngx_http_auth_ldap_update_cache(ngx_http_auth_ldap_ctx_t *ctx, 1232 | ngx_http_auth_ldap_cache_t *cache, ngx_flag_t outcome) 1233 | { 1234 | ngx_http_auth_ldap_cache_elt_t *elt, *oldest_elt; 1235 | ngx_uint_t i; 1236 | 1237 | elt = ctx->cache_bucket; 1238 | oldest_elt = elt; 1239 | for (i = 1; i < cache->elts_per_bucket; i++, elt++) { 1240 | if (elt->time < oldest_elt->time) { 1241 | oldest_elt = elt; 1242 | } 1243 | } 1244 | 1245 | oldest_elt->time = ngx_current_msec; 1246 | oldest_elt->outcome = outcome; 1247 | oldest_elt->small_hash = ctx->cache_small_hash; 1248 | ngx_memcpy(oldest_elt->big_hash, ctx->cache_big_hash, 16); 1249 | /* save (copy) each attribute from the context to the cache */ 1250 | oldest_elt->attributes.nelts = 0; 1251 | for (i = 0; i < ctx->attributes.nelts; i++) { 1252 | ldap_search_attribute_t *cache_attr = ngx_array_push(&oldest_elt->attributes); 1253 | *cache_attr = *((ldap_search_attribute_t *)ctx->attributes.elts + i); 1254 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, 1255 | "http_auth_ldap: ngx_http_auth_ldap_update_cache: saving '%V' = '%V' to cache", 1256 | &cache_attr->attr_name, &cache_attr->attr_value); 1257 | } 1258 | } 1259 | 1260 | 1261 | /*** OpenLDAP SockBuf implementation over nginx socket functions ***/ 1262 | 1263 | static int 1264 | ngx_http_auth_ldap_sb_setup(Sockbuf_IO_Desc *sbiod, void *arg) 1265 | { 1266 | sbiod->sbiod_pvt = arg; 1267 | return 0; 1268 | } 1269 | 1270 | static int 1271 | ngx_http_auth_ldap_sb_remove(Sockbuf_IO_Desc *sbiod) 1272 | { 1273 | ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; 1274 | 1275 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_remove() Cnx[%d]", c->cnx_idx); 1276 | (void)c; /* 'c' would be left unused on debug builds */ 1277 | 1278 | sbiod->sbiod_pvt = NULL; 1279 | return 0; 1280 | } 1281 | 1282 | static int 1283 | ngx_http_auth_ldap_sb_close(Sockbuf_IO_Desc *sbiod) 1284 | { 1285 | ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; 1286 | 1287 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_close() Cnx[%d]", c->cnx_idx); 1288 | 1289 | if (!c->conn.connection->read->error && !c->conn.connection->read->eof) { 1290 | if (ngx_shutdown_socket(c->conn.connection->fd, SHUT_RDWR) == -1) { 1291 | ngx_connection_error(c->conn.connection, ngx_socket_errno, ngx_shutdown_socket_n " failed"); 1292 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_close() Cnx[%d] shutdown failed", c->cnx_idx); 1293 | ngx_http_auth_ldap_close_connection(c, 0); 1294 | return -1; 1295 | } 1296 | } 1297 | 1298 | return 0; 1299 | } 1300 | 1301 | static int 1302 | ngx_http_auth_ldap_sb_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) 1303 | { 1304 | ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; 1305 | 1306 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_ctrl(opt=%d) Cnx[%d]", opt, c->cnx_idx); 1307 | 1308 | switch (opt) { 1309 | case LBER_SB_OPT_DATA_READY: 1310 | if (c->conn.connection->read->ready) { 1311 | return 1; 1312 | } 1313 | return 0; 1314 | } 1315 | 1316 | return 0; 1317 | } 1318 | 1319 | static ber_slen_t 1320 | ngx_http_auth_ldap_sb_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) 1321 | { 1322 | ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; 1323 | ber_slen_t ret; 1324 | 1325 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_read(len=%d) Cnx[%d]", len, c->cnx_idx); 1326 | 1327 | ret = c->conn.connection->recv(c->conn.connection, buf, len); 1328 | 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); 1329 | if (ret < 0) { 1330 | errno = (ret == NGX_AGAIN) ? NGX_EAGAIN : NGX_ECONNRESET; 1331 | return -1; 1332 | } 1333 | 1334 | return ret; 1335 | } 1336 | 1337 | static ber_slen_t 1338 | ngx_http_auth_ldap_sb_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) 1339 | { 1340 | ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; 1341 | ber_slen_t ret; 1342 | 1343 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_write(len=%d) Cnx[%d]", len, c->cnx_idx); 1344 | 1345 | ret = c->conn.connection->send(c->conn.connection, buf, len); 1346 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_write Cnx[%d] ret=%d", c->cnx_idx, ret); 1347 | if (ret < 0) { 1348 | errno = (ret == NGX_AGAIN) ? NGX_EAGAIN : NGX_ECONNRESET; 1349 | return 0; 1350 | } 1351 | 1352 | return ret; 1353 | } 1354 | 1355 | static Sockbuf_IO ngx_http_auth_ldap_sbio = 1356 | { 1357 | ngx_http_auth_ldap_sb_setup, 1358 | ngx_http_auth_ldap_sb_remove, 1359 | ngx_http_auth_ldap_sb_ctrl, 1360 | ngx_http_auth_ldap_sb_read, 1361 | ngx_http_auth_ldap_sb_write, 1362 | ngx_http_auth_ldap_sb_close 1363 | }; 1364 | 1365 | 1366 | /*** Asynchronous LDAP connection handling ***/ 1367 | 1368 | static void 1369 | ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c, int retry_asap) 1370 | { 1371 | ngx_queue_t *q; 1372 | ngx_msec_t reconnect_delay = retry_asap ? RECONNECT_ASAP_MS : c->server->reconnect_timeout; // Default reconnect delay 1373 | ngx_http_auth_ldap_connection_state_t saved_state; 1374 | 1375 | 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); 1376 | 1377 | if (c->state == STATE_DISCONNECTING) { 1378 | // Already in DISCONNECTING state. break here to avoid recuse in ngx_http_auth_ldap_close_connection 1379 | 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); 1380 | return; 1381 | } 1382 | 1383 | // Temporary DISCONNECTING state to avoid loops in ngx_http_auth_ldap_close_connection 1384 | saved_state = c->state; 1385 | c->state = STATE_DISCONNECTING; 1386 | 1387 | if (c->ld) { 1388 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] Unbinding from the server \"%V\")", 1389 | c->cnx_idx, &c->server->url); 1390 | ldap_unbind_ext(c->ld, NULL, NULL); 1391 | /* Unbind is always synchronous, even though the function name does not end with an '_s'. */ 1392 | c->ld = NULL; 1393 | } 1394 | 1395 | if (c->conn.connection) { 1396 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_close_connection: Cnx[%d] Closing connection (fd=%d)", 1397 | c->cnx_idx, c->conn.connection->fd); 1398 | 1399 | #if (NGX_OPENSSL) 1400 | if (c->conn.connection->ssl) { 1401 | c->conn.connection->ssl->no_wait_shutdown = 1; 1402 | (void) ngx_ssl_shutdown(c->conn.connection); 1403 | } 1404 | #endif 1405 | 1406 | ngx_close_connection(c->conn.connection); 1407 | c->conn.connection = NULL; 1408 | } 1409 | 1410 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 1411 | "ngx_http_auth_ldap_close_connection: Cnx[%d] Closed", c->cnx_idx); 1412 | 1413 | q = ngx_queue_head(&c->server->free_connections); 1414 | while (q != ngx_queue_sentinel(&c->server->free_connections)) { 1415 | if (q == &c->queue) { 1416 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 1417 | "ngx_http_auth_ldap_close_connection: removing cnx [%d] from free queue", 1418 | c->cnx_idx); 1419 | ngx_queue_remove(q); 1420 | break; 1421 | } 1422 | q = ngx_queue_next(q); 1423 | } 1424 | 1425 | c->rctx = NULL; 1426 | c->state = saved_state; // Restore initial state 1427 | if (c->state != STATE_DISCONNECTED) { 1428 | c->state = STATE_DISCONNECTED; 1429 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 1430 | "ngx_http_auth_ldap_close_connection: Cnx[%d] set pending reconnection", 1431 | c->cnx_idx); 1432 | ngx_http_auth_ldap_set_pending_reconnection(c, reconnect_delay); 1433 | } 1434 | } 1435 | 1436 | static void 1437 | ngx_http_auth_ldap_wake_request(ngx_http_request_t *r) 1438 | { 1439 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_wake_request: Waking authentication request \"%V\"", 1440 | &r->request_line); 1441 | ngx_http_core_run_phases(r); 1442 | } 1443 | 1444 | static int 1445 | ngx_http_auth_ldap_get_connection(ngx_http_auth_ldap_ctx_t *ctx) 1446 | { 1447 | ngx_http_auth_ldap_server_t *server; 1448 | ngx_queue_t *q; 1449 | ngx_http_auth_ldap_connection_t *c; 1450 | 1451 | /* 1452 | * If we already have a connection, just say we got them one. 1453 | */ 1454 | if (ctx->c != NULL) 1455 | return 1; 1456 | 1457 | server = ctx->server; 1458 | 1459 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, 1460 | "ngx_http_auth_ldap_get_connection: Wants a free connection to \"%V\"", &server->alias); 1461 | 1462 | if (!ngx_queue_empty(&server->free_connections)) { 1463 | q = ngx_queue_last(&server->free_connections); 1464 | ngx_queue_remove(q); 1465 | c = ngx_queue_data(q, ngx_http_auth_ldap_connection_t, queue); 1466 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, 1467 | "ngx_http_auth_ldap_get_connection: Got cnx [%d] from free queue", c->cnx_idx); 1468 | c->rctx = ctx; 1469 | ctx->c = c; 1470 | ctx->replied = 0; 1471 | return 1; 1472 | } 1473 | 1474 | /* Check if we have pending (waiting reconnect) connection */ 1475 | if (!ngx_queue_empty(&server->pending_reconnections)) { 1476 | q = ngx_queue_head(&server->pending_reconnections); 1477 | c = ngx_queue_data(q, ngx_http_auth_ldap_connection_t, queue_pending); 1478 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, 1479 | "ngx_http_auth_ldap_get_connection: Got cnx [%d] from pending queue -> shorten reconnect timer", c->cnx_idx); 1480 | /* Use the shortest the reconnection delay as we really need a new connection here */ 1481 | ngx_del_timer(&c->reconnect_event); // Cancel the reconnect timer 1482 | ngx_add_timer(&c->reconnect_event, RECONNECT_ASAP_MS); 1483 | } 1484 | 1485 | q = ngx_queue_next(&server->waiting_requests); 1486 | while (q != ngx_queue_sentinel(&server->waiting_requests)) { 1487 | if (q == &ctx->queue) { 1488 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "ngx_http_auth_ldap_get_connection: Tried to insert a same request"); 1489 | return 0; 1490 | } 1491 | q = ngx_queue_next(q); 1492 | } 1493 | ngx_queue_insert_head(&server->waiting_requests, &ctx->queue); 1494 | 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..."); 1495 | return 0; 1496 | } 1497 | 1498 | static void 1499 | ngx_http_auth_ldap_return_connection(ngx_http_auth_ldap_connection_t *c) 1500 | { 1501 | ngx_queue_t *q; 1502 | 1503 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, 1504 | "ngx_http_auth_ldap_return_connection: Marking the connection [%d] to \"%V\" as free", 1505 | c->cnx_idx, &c->server->alias); 1506 | 1507 | if (c->rctx != NULL) { 1508 | c->rctx->c = NULL; 1509 | c->rctx = NULL; 1510 | c->msgid = -1; 1511 | c->state = STATE_READY; 1512 | } 1513 | 1514 | ngx_queue_insert_head(&c->server->free_connections, &c->queue); 1515 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 1516 | "ngx_http_auth_ldap_return_connectionn: Cnx[%d] Ready", c->cnx_idx); 1517 | if (!ngx_queue_empty(&c->server->waiting_requests)) { 1518 | q = ngx_queue_last(&c->server->waiting_requests); 1519 | ngx_queue_remove(q); 1520 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 1521 | "ngx_http_auth_ldap_return_connection: Cnx[%d] Wakeup a waiting request", c->cnx_idx); 1522 | ngx_http_auth_ldap_wake_request((ngx_queue_data(q, ngx_http_auth_ldap_ctx_t, queue))->r); 1523 | } 1524 | } 1525 | 1526 | static void 1527 | ngx_http_auth_ldap_set_pending_reconnection(ngx_http_auth_ldap_connection_t *c, ngx_msec_t reconnect_delay) 1528 | { 1529 | ngx_queue_t *q; 1530 | 1531 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 1532 | "ngx_http_auth_ldap_set_pending_reconnection: Cnx[%d] reconnection scheduled in %d ms", c->cnx_idx, reconnect_delay); 1533 | ngx_add_timer(&c->reconnect_event, reconnect_delay); 1534 | 1535 | /* Check if connection is already in the pending queue */ 1536 | for (q = ngx_queue_head(&c->server->pending_reconnections); 1537 | q != ngx_queue_sentinel(&c->server->pending_reconnections); 1538 | q = ngx_queue_next(q)) 1539 | { 1540 | if (q == &c->queue_pending) { 1541 | ngx_log_error(NGX_LOG_WARN, c->log, 0, 1542 | "http_auth_ldap: ngx_http_auth_ldap_set_pending_reconnection: Connection already in pending queue"); 1543 | return; 1544 | } 1545 | } 1546 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 1547 | "ngx_http_auth_ldap_set_pending_reconnection: Connection [%d] inserted in pending queue", c->cnx_idx); 1548 | ngx_queue_insert_tail(&c->server->pending_reconnections, &c->queue_pending); 1549 | } 1550 | 1551 | static void 1552 | ngx_http_auth_ldap_reply_connection(ngx_http_auth_ldap_connection_t *c, int error_code, char* error_msg) 1553 | { 1554 | ngx_http_auth_ldap_ctx_t *ctx = c->rctx; 1555 | 1556 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_reply_connection: cnx[%d] LDAP request to \"%V\" has finished", 1557 | c->cnx_idx, &c->server->alias); 1558 | 1559 | ctx->replied = 1; 1560 | ctx->error_code = error_code; 1561 | if (error_msg) { 1562 | ctx->error_msg.len = ngx_strlen(error_msg); 1563 | ctx->error_msg.data = ngx_palloc(ctx->r->pool, ctx->error_msg.len); 1564 | ngx_memcpy(ctx->error_msg.data, error_msg, ctx->error_msg.len); 1565 | } else { 1566 | ctx->error_msg.len = 0; 1567 | ctx->error_msg.data = NULL; 1568 | } 1569 | 1570 | ngx_http_auth_ldap_wake_request(ctx->r); 1571 | } 1572 | 1573 | static void 1574 | ngx_http_auth_ldap_dummy_write_handler(ngx_event_t *wev) 1575 | { 1576 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http_auth_ldap: Dummy write handler"); 1577 | 1578 | if (ngx_handle_write_event(wev, 0) != NGX_OK) { 1579 | ngx_http_auth_ldap_close_connection(((ngx_connection_t *) wev->data)->data, 0); 1580 | } 1581 | } 1582 | 1583 | 1584 | #if (NGX_OPENSSL) 1585 | /* Make sure the event handlers are activated. */ 1586 | static ngx_int_t 1587 | ngx_http_auth_ldap_restore_handlers(ngx_connection_t *conn) 1588 | { 1589 | ngx_int_t rc; 1590 | 1591 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, conn->log, 0, "http_auth_ldap: Restoring event handlers. read=%d write=%d", conn->read->active, conn->write->active); 1592 | 1593 | if (!conn->read->active) { 1594 | rc = ngx_add_event(conn->read, NGX_READ_EVENT, 0); 1595 | if (rc != NGX_OK) { 1596 | return rc; 1597 | } 1598 | } 1599 | 1600 | if (!conn->write->active && 1601 | (conn->write->handler != ngx_http_auth_ldap_dummy_write_handler)) { 1602 | rc = ngx_add_event(conn->write, NGX_WRITE_EVENT, 0); 1603 | if (rc != NGX_OK) { 1604 | return rc; 1605 | } 1606 | } 1607 | 1608 | return NGX_OK; 1609 | } 1610 | #endif 1611 | 1612 | static void 1613 | ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) 1614 | { 1615 | ngx_connection_t *conn; 1616 | Sockbuf *sb; 1617 | ngx_int_t rc; 1618 | struct berval cred; 1619 | 1620 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_connection_established: Cnx[%d] established", c->cnx_idx); 1621 | 1622 | conn = c->conn.connection; 1623 | ngx_del_timer(conn->read); 1624 | conn->write->handler = ngx_http_auth_ldap_dummy_write_handler; 1625 | 1626 | 1627 | /* Initialize OpenLDAP on the connection */ 1628 | 1629 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_connection_established: Cnx[%d] initializing LDAP using URL \"%V\"", 1630 | c->cnx_idx, &c->server->url); 1631 | rc = ldap_init_fd(c->conn.connection->fd, LDAP_PROTO_EXT, (const char *) c->server->url.data, &c->ld); 1632 | if (rc != LDAP_SUCCESS) { 1633 | 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)); 1634 | ngx_http_auth_ldap_close_connection(c, 0); 1635 | return; 1636 | } 1637 | 1638 | if (c->server->referral == 0) { 1639 | rc = ldap_set_option(c->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1640 | if (rc != LDAP_OPT_SUCCESS) { 1641 | 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)); 1642 | ngx_http_auth_ldap_close_connection(c, 0); 1643 | return; 1644 | } 1645 | } 1646 | 1647 | rc = ldap_get_option(c->ld, LDAP_OPT_SOCKBUF, (void *) &sb); 1648 | if (rc != LDAP_OPT_SUCCESS) { 1649 | 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)); 1650 | ngx_http_auth_ldap_close_connection(c, 0); 1651 | return; 1652 | } 1653 | 1654 | ber_sockbuf_add_io(sb, &ngx_http_auth_ldap_sbio, LBER_SBIOD_LEVEL_PROVIDER, (void *) c); 1655 | 1656 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 1657 | "ngx_http_auth_ldap_connection_established: Cnx[%d] LDAP initialized", c->cnx_idx); 1658 | 1659 | 1660 | /* Perform initial bind to the server */ 1661 | 1662 | cred.bv_val = (char *) c->server->bind_dn_passwd.data; 1663 | cred.bv_len = c->server->bind_dn_passwd.len; 1664 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 1665 | "ngx_http_auth_ldap_connection_established: Cnx[%d] Initial binding ...", c->cnx_idx); 1666 | rc = ldap_sasl_bind(c->ld, (const char *) c->server->bind_dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &c->msgid); 1667 | if (rc != LDAP_SUCCESS) { 1668 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 1669 | "ngx_http_auth_ldap_connection_established: [%d] initial ldap_sasl_bind() failed (%d: %s)", 1670 | c->cnx_idx, rc, ldap_err2string(rc)); 1671 | ngx_http_auth_ldap_close_connection(c, 0); 1672 | return; 1673 | } 1674 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, 1675 | "ngx_http_auth_ldap_connection_established: [%d] initial ldap_sasl_bind() -> msgid=%d", 1676 | c->cnx_idx, c->msgid); 1677 | 1678 | c->state = STATE_INITIAL_BINDING; 1679 | ngx_add_timer(c->conn.connection->read, c->server->bind_timeout); 1680 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, 1681 | "ngx_http_auth_ldap_connection_established: [%d] waiting initial bind response (timeout=%d)", 1682 | c->cnx_idx, c->server->bind_timeout); 1683 | } 1684 | 1685 | #if (NGX_OPENSSL) 1686 | static void 1687 | ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn, ngx_flag_t validate) 1688 | { 1689 | ngx_http_auth_ldap_connection_t *c; 1690 | c = conn->data; 1691 | 1692 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: SSL handshake handler (validate=%d)", validate); 1693 | 1694 | if (conn->ssl->handshaked) { 1695 | #if OPENSSL_VERSION_NUMBER >= 0x10002000 1696 | if (validate) { // verify remote certificate if requested 1697 | X509 *cert = SSL_get_peer_certificate(conn->ssl->connection); 1698 | long chain_verified = SSL_get_verify_result(conn->ssl->connection); 1699 | 1700 | int addr_verified; 1701 | if (c->server->ssl_check_cert == SSL_CERT_VERIFY_CHAIN) { 1702 | // chain_verified is enough, not requiring full name/IP verification 1703 | addr_verified = 1; 1704 | 1705 | } else { 1706 | // verify hostname/IP 1707 | char *hostname = c->server->ludpp->lud_host; 1708 | addr_verified = X509_check_host(cert, hostname, 0, 0, 0); 1709 | 1710 | if (!addr_verified) { // domain not in cert? try IP 1711 | size_t len; // get IP length 1712 | 1713 | struct sockaddr *conn_sockaddr = NULL; 1714 | if (conn->sockaddr != NULL) conn_sockaddr = conn->sockaddr; 1715 | else if (c->conn.sockaddr != NULL) conn_sockaddr = c->conn.sockaddr; 1716 | else conn_sockaddr = &c->server->parsed_url.sockaddr.sockaddr; 1717 | 1718 | if (conn_sockaddr->sa_family == AF_INET) len = 4; 1719 | else if (conn_sockaddr->sa_family == AF_INET6) len = 16; 1720 | else { // very unlikely indeed 1721 | ngx_http_auth_ldap_close_connection(c, 0); 1722 | return; 1723 | } 1724 | addr_verified = X509_check_ip(cert, (const unsigned char*)conn_sockaddr->sa_data, len, 0); 1725 | } 1726 | } 1727 | 1728 | // Find anything fishy? 1729 | if ( !(cert && addr_verified && chain_verified == X509_V_OK) ) { 1730 | if (!addr_verified) { 1731 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 1732 | "http_auth_ldap: Remote side presented invalid SSL certificate: " 1733 | "does not match address (neither server's domain nor IP in certificate's CN or SAN)"); 1734 | fprintf(stderr, "DEBUG: SSL cert domain mismatch\n"); fflush(stderr); 1735 | } else { 1736 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 1737 | "http_auth_ldap: Remote side presented invalid SSL certificate: error %l, %s", 1738 | chain_verified, X509_verify_cert_error_string(chain_verified)); 1739 | } 1740 | ngx_http_auth_ldap_close_connection(c, 0); 1741 | return; 1742 | } 1743 | } 1744 | #endif 1745 | 1746 | // handshaked validation successful -- or not required in the first place 1747 | conn->read->handler = &ngx_http_auth_ldap_read_handler; 1748 | ngx_http_auth_ldap_restore_handlers(conn); 1749 | ngx_http_auth_ldap_connection_established(c); 1750 | return; 1751 | } 1752 | else { // handshake failed 1753 | ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: SSL handshake failed"); 1754 | ngx_http_auth_ldap_close_connection(c, 0); 1755 | } 1756 | } 1757 | 1758 | static void 1759 | ngx_http_auth_ldap_ssl_handshake_validating_handler(ngx_connection_t *conn) 1760 | { ngx_http_auth_ldap_ssl_handshake_handler(conn, 1); } 1761 | 1762 | static void 1763 | ngx_http_auth_ldap_ssl_handshake_non_validating_handler(ngx_connection_t *conn) 1764 | { ngx_http_auth_ldap_ssl_handshake_handler(conn, 0); } 1765 | 1766 | typedef void (*ngx_http_auth_ldap_ssl_callback)(ngx_connection_t *conn); 1767 | 1768 | static void 1769 | ngx_http_auth_ldap_ssl_handshake(ngx_http_auth_ldap_connection_t *c) 1770 | { 1771 | ngx_int_t rc; 1772 | 1773 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: SSL handshake"); 1774 | 1775 | c->conn.connection->pool = c->pool; 1776 | rc = ngx_ssl_create_connection(c->ssl, c->conn.connection, NGX_SSL_BUFFER | NGX_SSL_CLIENT); 1777 | if (rc != NGX_OK) { 1778 | ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: SSL initialization failed"); 1779 | ngx_http_auth_ldap_close_connection(c, 0); 1780 | return; 1781 | } 1782 | 1783 | c->log->action = "SSL handshaking to LDAP server"; 1784 | ngx_connection_t *transport = c->conn.connection; 1785 | 1786 | ngx_http_auth_ldap_ssl_callback callback; 1787 | if (c->server->ssl_check_cert) { 1788 | // load CA certificates: custom ones if specified, default ones instead 1789 | if (c->server->ssl_ca_file.data || c->server->ssl_ca_dir.data) { 1790 | #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) 1791 | int setcode = SSL_CTX_load_verify_locations(transport->ssl->session_ctx, 1792 | (char*)(c->server->ssl_ca_file.data), (char*)(c->server->ssl_ca_dir.data)); 1793 | #else 1794 | int setcode = SSL_CTX_load_verify_locations(transport->ssl->connection->ctx, 1795 | (char*)(c->server->ssl_ca_file.data), (char*)(c->server->ssl_ca_dir.data)); 1796 | #endif 1797 | if (setcode != 1) { 1798 | unsigned long error_code = ERR_get_error(); 1799 | char *error_msg = ERR_error_string(error_code, NULL); 1800 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 1801 | "http_auth_ldap: SSL initialization failed. Could not set custom CA certificate location. " 1802 | "Error: %lu, %s", error_code, error_msg); 1803 | } 1804 | } 1805 | #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) 1806 | int setcode = SSL_CTX_set_default_verify_paths(transport->ssl->session_ctx); 1807 | #else 1808 | int setcode = SSL_CTX_set_default_verify_paths(transport->ssl->connection->ctx); 1809 | #endif 1810 | if (setcode != 1) { 1811 | unsigned long error_code = ERR_get_error(); 1812 | char *error_msg = ERR_error_string(error_code, NULL); 1813 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 1814 | "http_auth_ldap: SSL initialization failed. Could not use default CA certificate location. " 1815 | "Error: %lu, %s", error_code, error_msg); 1816 | } 1817 | 1818 | // use validating version of next function 1819 | callback = &ngx_http_auth_ldap_ssl_handshake_validating_handler; 1820 | } else { 1821 | // use non-validating version of next function 1822 | callback = &ngx_http_auth_ldap_ssl_handshake_non_validating_handler; 1823 | } 1824 | 1825 | rc = ngx_ssl_handshake(transport); 1826 | if (rc == NGX_AGAIN) { 1827 | transport->ssl->handler = callback; 1828 | return; 1829 | } 1830 | 1831 | (*callback)(transport); 1832 | return; 1833 | } 1834 | #endif 1835 | 1836 | static void 1837 | ngx_http_auth_ldap_connect_handler(ngx_event_t *wev) 1838 | { 1839 | ngx_connection_t *conn; 1840 | ngx_http_auth_ldap_connection_t *c; 1841 | int keepalive; 1842 | 1843 | conn = wev->data; 1844 | c = conn->data; 1845 | 1846 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_connect_handler: Cnx[%d]", c->cnx_idx); 1847 | 1848 | if (ngx_handle_write_event(wev, 0) != NGX_OK) { 1849 | ngx_http_auth_ldap_close_connection(c, 0); 1850 | return; 1851 | } 1852 | 1853 | keepalive = 1; 1854 | if (setsockopt(conn->fd, SOL_SOCKET, SO_KEEPALIVE, (const void *) &keepalive, sizeof(int)) == -1) 1855 | { 1856 | ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, "ngx_http_auth_ldap_connect_handler: setsockopt(SO_KEEPALIVE) failed"); 1857 | } 1858 | 1859 | #if (NGX_OPENSSL) 1860 | if (ngx_strcmp(c->server->ludpp->lud_scheme, "ldaps") == 0) { 1861 | ngx_http_auth_ldap_ssl_handshake(c); 1862 | return; 1863 | } 1864 | #endif 1865 | 1866 | ngx_http_auth_ldap_connection_established(c); 1867 | } 1868 | 1869 | static void 1870 | ngx_http_auth_ldap_read_handler(ngx_event_t *rev) 1871 | { 1872 | ngx_connection_t *conn; 1873 | ngx_http_auth_ldap_connection_t *c; 1874 | ngx_int_t rc; 1875 | struct timeval timeout = {0, 0}; 1876 | LDAPMessage *result; 1877 | int error_code; 1878 | char *error_msg; 1879 | char *dn; 1880 | 1881 | conn = rev->data; 1882 | c = conn->data; 1883 | 1884 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d]", c->cnx_idx); 1885 | 1886 | if (c->ld == NULL) { 1887 | 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); 1888 | ngx_http_auth_ldap_close_connection(c, 0); 1889 | return; 1890 | } 1891 | 1892 | if (rev->timedout) { 1893 | ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, 1894 | "ngx_http_auth_ldap_read_handler: Cnx[%d] Read timed out (state=%d)", c->cnx_idx, c->state); 1895 | conn->timedout = 1; 1896 | ngx_http_auth_ldap_close_connection(c, 1); 1897 | return; 1898 | } 1899 | 1900 | c->log->action = "reading response from LDAP"; 1901 | 1902 | for (;;) { 1903 | rc = ldap_result(c->ld, LDAP_RES_ANY, 0, &timeout, &result); 1904 | if (rc < 0) { 1905 | int reconnect_asap = 0; 1906 | // if LDAP_SERVER_DOWN (usually timeouts or server disconnects) 1907 | if (rc == LDAP_SERVER_DOWN) { 1908 | if (c->server->max_down_retries_count < c->server->max_down_retries) { 1909 | /** 1910 | update counter (this is always reset in 1911 | ngx_http_auth_ldap_connect() for a successful ldap 1912 | connection 1913 | **/ 1914 | c->server->max_down_retries_count++; 1915 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] LDAP_SERVER_DOWN (retry count: %d)", 1916 | c->cnx_idx, c->server->max_down_retries_count); 1917 | reconnect_asap = 1; 1918 | } else { 1919 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 1920 | "ngx_http_auth_ldap_read_handler: Cnx[%d] LDAP_SERVER_DOWN: No more reconnect retry", c->cnx_idx); 1921 | } 1922 | ngx_http_auth_ldap_close_connection(c, reconnect_asap); 1923 | } else { 1924 | ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_result() failed (%d: %s)", 1925 | c->cnx_idx, rc, ldap_err2string(rc)); 1926 | } 1927 | 1928 | return; 1929 | } 1930 | if (rc == 0) { 1931 | // Timeout ({0, 0}) => No result message) 1932 | 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); 1933 | break; 1934 | } 1935 | 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", 1936 | c->cnx_idx, rc, ldap_msgid(result), ldap_msgtype(result)); 1937 | 1938 | if (ldap_msgid(result) != c->msgid) { 1939 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 1940 | "ngx_http_auth_ldap_read_handler: Cnx[%d] Message with unknown ID received, ignoring.", c->cnx_idx); 1941 | ldap_msgfree(result); 1942 | continue; 1943 | } 1944 | 1945 | rc = ldap_parse_result(c->ld, result, &error_code, NULL, &error_msg, NULL, NULL, 0); 1946 | if (rc == LDAP_NO_RESULTS_RETURNED) { 1947 | error_code = LDAP_NO_RESULTS_RETURNED; 1948 | error_msg = NULL; 1949 | } else if (rc != LDAP_SUCCESS) { 1950 | ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] ldap_parse_result() failed (%d: %s)", 1951 | c->cnx_idx, rc, ldap_err2string(rc)); 1952 | ldap_msgfree(result); 1953 | ngx_http_auth_ldap_close_connection(c, 1); 1954 | return; 1955 | } 1956 | 1957 | switch (c->state) { 1958 | case STATE_INITIAL_BINDING: 1959 | if (ldap_msgtype(result) != LDAP_RES_BIND) { 1960 | break; 1961 | } 1962 | ngx_del_timer(conn->read); 1963 | if (error_code == LDAP_SUCCESS) { 1964 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Initial bind successful", c->cnx_idx); 1965 | c->state = STATE_READY; 1966 | ngx_http_auth_ldap_return_connection(c); 1967 | } else { 1968 | ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Initial bind failed (%d: %s [%s])", 1969 | c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); 1970 | ldap_memfree(error_msg); 1971 | ldap_msgfree(result); 1972 | ngx_http_auth_ldap_close_connection(c, 0); 1973 | return; 1974 | } 1975 | break; 1976 | 1977 | case STATE_BINDING: 1978 | if (ldap_msgtype(result) != LDAP_RES_BIND) { 1979 | break; 1980 | } 1981 | ngx_log_error(NGX_LOG_INFO, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received bind response (%d: %s [%s])", 1982 | c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); 1983 | ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); 1984 | break; 1985 | 1986 | case STATE_SEARCHING: 1987 | if (ldap_msgtype(result) == LDAP_RES_SEARCH_ENTRY) { 1988 | ngx_log_error(NGX_LOG_INFO, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received a search entry", c->cnx_idx); 1989 | if (c->rctx->dn.data == NULL) { 1990 | dn = ldap_get_dn(c->ld, result); 1991 | if (dn != NULL) { 1992 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, 1993 | "ngx_http_auth_ldap_read_handler: Cnx[%d] Found entry with DN \"%s\"", c->cnx_idx, dn); 1994 | c->rctx->dn.len = ngx_strlen(dn); 1995 | c->rctx->dn.data = (u_char *) ngx_palloc(c->rctx->r->pool, c->rctx->dn.len + 1); 1996 | ngx_memcpy(c->rctx->dn.data, dn, c->rctx->dn.len + 1); 1997 | ldap_memfree(dn); 1998 | } 1999 | } 2000 | /* Iterate through each attribute in the entry. */ 2001 | BerElement *ber = NULL; 2002 | char *attr = NULL; 2003 | struct berval **vals = NULL; 2004 | for (attr = ldap_first_attribute(c->ld, result, &ber); 2005 | attr != NULL; 2006 | attr = ldap_next_attribute(c->ld, result, ber)) { 2007 | /* Get only first value for each attribute. */ 2008 | if ((vals = ldap_get_values_len(c->ld, result, attr)) != NULL ) { 2009 | if(vals[0] != NULL) { 2010 | ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received attribute %s: %s", 2011 | c->cnx_idx, attr, vals[0]->bv_val); 2012 | /* Save attribute name and value in the context */ 2013 | ldap_search_attribute_t * elt = ngx_array_push(&c->rctx->attributes); 2014 | santitize_str((u_char *)attr, SANITIZE_NO_CONV); 2015 | int attr_len = strlen(attr); 2016 | elt->attr_name.len = c->server->attribute_header_prefix.len + attr_len; 2017 | /* Use the pool of global cache to allocate strings, so that they can be used everywhere */ 2018 | elt->attr_name.data = ngx_pnalloc(ngx_http_auth_ldap_cache.pool, elt->attr_name.len); 2019 | unsigned char *p = ngx_cpymem(elt->attr_name.data, c->server->attribute_header_prefix.data, c->server->attribute_header_prefix.len); 2020 | p = ngx_cpymem(p, attr, attr_len); 2021 | elt->attr_value.len = vals[0]->bv_len; 2022 | elt->attr_value.data = ngx_pnalloc(ngx_http_auth_ldap_cache.pool, elt->attr_value.len); 2023 | ngx_memcpy(elt->attr_value.data, vals[0]->bv_val, elt->attr_value.len); 2024 | } 2025 | ldap_value_free_len(vals); 2026 | } 2027 | ldap_memfree(attr); 2028 | } 2029 | if (ber != NULL) { 2030 | ber_free(ber, 0); 2031 | } 2032 | } else if (ldap_msgtype(result) == LDAP_RES_SEARCH_RESULT) { 2033 | ngx_log_error(NGX_LOG_INFO, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received search result (%d: %s [%s])", 2034 | c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); 2035 | ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); 2036 | } 2037 | break; 2038 | 2039 | case STATE_COMPARING: 2040 | if (ldap_msgtype(result) != LDAP_RES_COMPARE) { 2041 | break; 2042 | } 2043 | ngx_log_error(NGX_LOG_INFO, c->log, 0, "ngx_http_auth_ldap_read_handler: Cnx[%d] Received comparison result (%d: %s [%s])", 2044 | c->cnx_idx, error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); 2045 | ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); 2046 | break; 2047 | 2048 | default: 2049 | break; 2050 | } 2051 | 2052 | ldap_memfree(error_msg); 2053 | ldap_msgfree(result); 2054 | } 2055 | 2056 | if (ngx_handle_read_event(rev, 0) != NGX_OK) { 2057 | ngx_http_auth_ldap_close_connection(c, 1); 2058 | return; 2059 | } 2060 | } 2061 | 2062 | static void 2063 | ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) 2064 | { 2065 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 2066 | "ngx_http_auth_ldap_connect: Cnx[%d] Server \"%V\" connecting ...", 2067 | c->cnx_idx, &c->server->alias); 2068 | 2069 | // Clear and free any previous addrs from parsed_url, so that we can resolve again the LDAP server hostname 2070 | my_free_addrs_from_url(c->main_cnf->cnf_pool, &c->server->parsed_url); 2071 | 2072 | c->server->parsed_url.no_resolve = 0; // Try to resolve this time 2073 | if (ngx_parse_url(c->pool, &c->server->parsed_url) != NGX_OK) { 2074 | ngx_log_error(NGX_LOG_WARN, c->log, 0, 2075 | "ngx_http_auth_ldap_connect: Hostname \"%V\" not found with system resolver, try with DNS", 2076 | &c->server->parsed_url.host); 2077 | // Try to resolve the hostname through the resolver 2078 | if (c->main_cnf->resolver == NULL) { 2079 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 2080 | "ngx_http_auth_ldap_connect: No resolver configured"); 2081 | return; 2082 | } 2083 | ngx_resolver_ctx_t *resolver_ctx, temp; 2084 | temp.name = c->server->parsed_url.host; 2085 | resolver_ctx = ngx_resolve_start(c->main_cnf->resolver, &temp); 2086 | if (resolver_ctx == NULL) { 2087 | ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_connect: Unable to start the resolver"); 2088 | return; 2089 | } 2090 | if (resolver_ctx == NGX_NO_RESOLVER) { 2091 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 2092 | "ngx_http_auth_ldap_connect: No resolver defined to resolve %V", 2093 | c->server->parsed_url.host); 2094 | return; 2095 | } 2096 | resolver_ctx->name = c->server->parsed_url.host; 2097 | resolver_ctx->handler = ngx_http_auth_ldap_resolve_handler; 2098 | resolver_ctx->data = c; 2099 | resolver_ctx->timeout = c->main_cnf->resolver_timeout; 2100 | c->resolver_ctx = resolver_ctx; 2101 | if (ngx_resolve_name(resolver_ctx) != NGX_OK) { 2102 | c->resolver_ctx = NULL; 2103 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 2104 | "ngx_http_auth_ldap_connect: Resolve %V failed", c->server->parsed_url.host); 2105 | return; 2106 | } 2107 | // The DNS Querry has been triggered. Let the ngx_http_auth_ldap_resolve_handler now take the control of the flow. 2108 | return; 2109 | } 2110 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, 2111 | "ngx_http_auth_ldap_connect: server \"%V\" (naddrs is now %d).", 2112 | &c->server->alias, c->server->parsed_url.naddrs); 2113 | 2114 | // Continue with the rest of the connection establishement 2115 | ngx_http_auth_ldap_connect_continue(c); 2116 | } 2117 | 2118 | static void 2119 | ngx_http_auth_ldap_connect_continue(ngx_http_auth_ldap_connection_t *c) 2120 | { 2121 | ngx_peer_connection_t *pconn; 2122 | ngx_connection_t *conn; 2123 | ngx_addr_t *addr; 2124 | ngx_int_t rc; 2125 | 2126 | if (c->server->parsed_url.naddrs == 0) { 2127 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 2128 | "ngx_http_auth_ldap_connect_continue: Cnx[%d] No addr for server", c->cnx_idx); 2129 | return; 2130 | } 2131 | 2132 | addr = &c->server->parsed_url.addrs[ngx_random() % c->server->parsed_url.naddrs]; 2133 | 2134 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 2135 | "ngx_http_auth_ldap_connect_continue: Cnx[%d] Connecting to LDAP server IP@ %V ...", 2136 | c->cnx_idx, &addr->name); 2137 | 2138 | pconn = &c->conn; 2139 | pconn->sockaddr = addr->sockaddr; 2140 | pconn->socklen = addr->socklen; 2141 | pconn->name = &addr->name; 2142 | pconn->get = ngx_event_get_peer; 2143 | pconn->log = c->log; 2144 | pconn->log_error = NGX_ERROR_ERR; 2145 | 2146 | rc = ngx_event_connect_peer(pconn); 2147 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_connect_continue: ngx_event_connect_peer() -> %d.", rc); 2148 | if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { 2149 | ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_auth_ldap_connect_continue: Cnx[%d] Unable to connect to LDAP server \"%V\".", 2150 | c->cnx_idx, &addr->name); 2151 | ngx_http_auth_ldap_set_pending_reconnection(c, c->server->reconnect_timeout); 2152 | return; 2153 | } 2154 | 2155 | conn = pconn->connection; 2156 | conn->data = c; 2157 | #if (NGX_OPENSSL) 2158 | conn->pool = c->pool; 2159 | #endif 2160 | conn->write->handler = ngx_http_auth_ldap_connect_handler; 2161 | conn->read->handler = ngx_http_auth_ldap_read_handler; 2162 | ngx_add_timer(conn->read, c->server->connect_timeout); 2163 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, 2164 | "ngx_http_auth_ldap_connect_continue: [%d] Waiting connection response (timeout=%d)", 2165 | c->cnx_idx, c->server->connect_timeout); 2166 | 2167 | c->server->max_down_retries_count = 0; /* reset retries count */ 2168 | c->state = STATE_CONNECTING; 2169 | } 2170 | 2171 | static void 2172 | ngx_http_auth_ldap_connection_cleanup(void *data) 2173 | { 2174 | ngx_http_auth_ldap_close_connection((ngx_http_auth_ldap_connection_t *) data, 0); 2175 | } 2176 | 2177 | static void 2178 | ngx_http_auth_ldap_reconnect_handler(ngx_event_t *ev) 2179 | { 2180 | ngx_connection_t *conn = ev->data; 2181 | ngx_http_auth_ldap_connection_t *c = conn->data; 2182 | 2183 | ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ev->log, 0, 2184 | "ngx_http_auth_ldap_reconnect_handler: ev=0x%p, conn=0x%p, c=0x%p", ev, conn, c); 2185 | 2186 | ngx_http_auth_ldap_reconnect_from_connection(c); 2187 | } 2188 | 2189 | static void 2190 | ngx_http_auth_ldap_reconnect_from_connection(ngx_http_auth_ldap_connection_t *c) 2191 | { 2192 | ngx_queue_t *q; 2193 | 2194 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, 2195 | "ngx_http_auth_ldap_reconnect_from_connection: Cnx[%d] c=0x%p", c->cnx_idx, c); 2196 | 2197 | /* Remove this connection from the pending reconnection queue */ 2198 | for (q = ngx_queue_head(&c->server->pending_reconnections); 2199 | q != ngx_queue_sentinel(&c->server->pending_reconnections); 2200 | q = ngx_queue_next(q)) 2201 | { 2202 | if (q == &c->queue_pending) { 2203 | ngx_queue_remove(q); 2204 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 2205 | "ngx_http_auth_ldap_reconnect_from_connection: Cnx[%d] removed from pending reconnection queue", c->cnx_idx); 2206 | break; 2207 | } 2208 | } 2209 | ngx_http_auth_ldap_connect(c); 2210 | } 2211 | 2212 | static void 2213 | my_free_addrs_from_url(ngx_pool_t *pool, ngx_url_t *u) 2214 | { 2215 | ngx_addr_t *addr; 2216 | ngx_uint_t i; 2217 | 2218 | if (u == NULL || u->addrs == NULL) { 2219 | return; 2220 | } 2221 | 2222 | for (i = 0; i < u->naddrs; i++) { 2223 | addr = &u->addrs[i]; 2224 | if (addr != NULL) { 2225 | if (addr->name.data != NULL) { 2226 | ngx_pfree(pool, addr->name.data); 2227 | addr->name.data = NULL; 2228 | addr->name.len = 0; 2229 | } 2230 | if (addr->sockaddr != NULL) { 2231 | ngx_pfree(pool, addr->sockaddr); 2232 | addr->sockaddr = NULL; 2233 | addr->socklen = 0; 2234 | } 2235 | } 2236 | } 2237 | 2238 | if (u->addrs != NULL) { 2239 | ngx_pfree(pool, u->addrs); 2240 | u->addrs = NULL; 2241 | u->naddrs = 0; 2242 | } 2243 | } 2244 | 2245 | /* Duplicated from ngnx_inet.c (as it is a static function in Nginx) */ 2246 | static ngx_int_t 2247 | my_ngx_inet_add_addr(ngx_pool_t *pool, ngx_url_t *u, struct sockaddr *sockaddr, 2248 | socklen_t socklen, ngx_uint_t total) 2249 | { 2250 | u_char *p; 2251 | size_t len; 2252 | ngx_uint_t i, nports; 2253 | ngx_addr_t *addr; 2254 | struct sockaddr *sa; 2255 | 2256 | nports = u->last_port ? u->last_port - u->port + 1 : 1; 2257 | if (u->addrs == NULL) { 2258 | u->addrs = ngx_palloc(pool, total * nports * sizeof(ngx_addr_t)); 2259 | if (u->addrs == NULL) { 2260 | return NGX_ERROR; 2261 | } 2262 | } 2263 | for (i = 0; i < nports; i++) { 2264 | sa = ngx_pcalloc(pool, socklen); 2265 | if (sa == NULL) { 2266 | return NGX_ERROR; 2267 | } 2268 | ngx_memcpy(sa, sockaddr, socklen); 2269 | ngx_inet_set_port(sa, u->port + i); 2270 | switch (sa->sa_family) { 2271 | 2272 | #if (NGX_HAVE_INET6) 2273 | case AF_INET6: 2274 | len = NGX_INET6_ADDRSTRLEN + sizeof("[]:65536") - 1; 2275 | break; 2276 | #endif 2277 | 2278 | default: /* AF_INET */ 2279 | len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; 2280 | } 2281 | p = ngx_pnalloc(pool, len); 2282 | if (p == NULL) { 2283 | return NGX_ERROR; 2284 | } 2285 | len = ngx_sock_ntop(sa, socklen, p, len, 1); 2286 | addr = &u->addrs[u->naddrs++]; 2287 | addr->sockaddr = sa; 2288 | addr->socklen = socklen; 2289 | addr->name.len = len; 2290 | addr->name.data = p; 2291 | } 2292 | return NGX_OK; 2293 | } 2294 | 2295 | static void ngx_http_auth_ldap_resolve_handler(ngx_resolver_ctx_t *ctx) 2296 | { 2297 | ngx_http_auth_ldap_connection_t *c = ctx->data; 2298 | ngx_uint_t i; 2299 | ngx_resolver_addr_t *res_addr; 2300 | 2301 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, 2302 | "ngx_http_auth_ldap_resolve_handler: Cnx[%d] server \"%V\"", 2303 | c->cnx_idx, &c->server->alias); 2304 | 2305 | if (ctx->state) { 2306 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 2307 | "http_auth_ldap: ngx_http_auth_ldap_resolve_handler: %V could not be resolved (%i: %s)", 2308 | &ctx->name, ctx->state, ngx_resolver_strerror(ctx->state)); 2309 | return; 2310 | } 2311 | ngx_resolve_name_done(ctx); 2312 | c->resolver_ctx = NULL; 2313 | 2314 | // Clear and free any previous addrs in parsed_url 2315 | my_free_addrs_from_url(c->main_cnf->cnf_pool, &c->server->parsed_url); 2316 | 2317 | u_char ip_addr_str[INET6_ADDRSTRLEN +1]; // Buffer for IPv4 or IPv6 address strings 2318 | // Update the parsed_url with the addresses resolved by DNS 2319 | for (i = 0, res_addr = ctx->addrs; res_addr != NULL && i < ctx->naddrs; i++, res_addr++) { 2320 | bzero(ip_addr_str, sizeof(ip_addr_str)); 2321 | ngx_sock_ntop(res_addr->sockaddr, res_addr->socklen, ip_addr_str, sizeof(ip_addr_str) -1, 0); 2322 | ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, 2323 | "ngx_http_auth_ldap_resolve_handler: Cnx[%d] Found [%d] %V -> %s", 2324 | c->cnx_idx, i, &ctx->name, ip_addr_str); 2325 | my_ngx_inet_add_addr(c->main_cnf->cnf_pool, &c->server->parsed_url, 2326 | res_addr->sockaddr, res_addr->socklen, ctx->naddrs); 2327 | } 2328 | 2329 | // Go on the the rest of the connection establishment 2330 | ngx_http_auth_ldap_connect_continue(c); 2331 | } 2332 | 2333 | /** 2334 | * Finalize servers configuration (at worker initialization) 2335 | */ 2336 | static ngx_int_t 2337 | ngx_http_auth_ldap_init_servers(ngx_cycle_t *cycle) 2338 | { 2339 | ngx_http_auth_ldap_main_conf_t *halmcf; 2340 | ngx_http_auth_ldap_server_t *server; 2341 | ngx_uint_t i, j; 2342 | 2343 | halmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); 2344 | if (halmcf == NULL || halmcf->servers == NULL) { 2345 | return NGX_OK; 2346 | } 2347 | 2348 | for (i = 0; i < halmcf->servers->nelts; i++) { 2349 | server = &((ngx_http_auth_ldap_server_t *) halmcf->servers->elts)[i]; 2350 | 2351 | if (server->attrs == NULL) { 2352 | // No search attributes specified, so setup an empty attributes list 2353 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "ngx_http_auth_ldap_init_servers: Server '%V': No search_attributes", &server->alias); 2354 | server->attrs = ngx_palloc(cycle->pool, 2 * sizeof(char *)); 2355 | server->attrs[0] = LDAP_NO_ATTRS; 2356 | server->attrs[1] = NULL; 2357 | } else { 2358 | for (j = 0; server->attrs[j] != NULL && j < MAX_ATTRS_COUNT; j++) { 2359 | 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]); 2360 | } 2361 | } 2362 | } 2363 | 2364 | return NGX_OK; 2365 | } 2366 | 2367 | 2368 | static ngx_int_t 2369 | ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) 2370 | { 2371 | ngx_http_auth_ldap_connection_t *c; 2372 | ngx_http_auth_ldap_main_conf_t *halmcf; 2373 | ngx_http_auth_ldap_server_t *server; 2374 | ngx_pool_cleanup_t *cleanup; 2375 | ngx_connection_t *dummy_conn; 2376 | ngx_uint_t i, j; 2377 | int option; 2378 | 2379 | halmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); 2380 | if (halmcf == NULL || halmcf->servers == NULL) { 2381 | return NGX_OK; 2382 | } 2383 | 2384 | option = LDAP_VERSION3; 2385 | ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &option); 2386 | 2387 | for (i = 0; i < halmcf->servers->nelts; i++) { 2388 | server = &((ngx_http_auth_ldap_server_t *) halmcf->servers->elts)[i]; 2389 | ngx_queue_init(&server->free_connections); 2390 | ngx_queue_init(&server->waiting_requests); 2391 | ngx_queue_init(&server->pending_reconnections); 2392 | if (server->connections <= 1) { 2393 | server->connections = 1; 2394 | } 2395 | 2396 | for (j = 0; j < server->connections; j++) { 2397 | c = ngx_pcalloc(cycle->pool, sizeof(ngx_http_auth_ldap_connection_t)); 2398 | cleanup = ngx_pool_cleanup_add(cycle->pool, 0); 2399 | dummy_conn = ngx_pcalloc(cycle->pool, sizeof(ngx_connection_t)); 2400 | if (c == NULL || cleanup == NULL || dummy_conn == NULL) { 2401 | return NGX_ERROR; 2402 | } 2403 | 2404 | cleanup->handler = &ngx_http_auth_ldap_connection_cleanup; 2405 | cleanup->data = c; 2406 | 2407 | c->log = cycle->log; 2408 | c->main_cnf = halmcf; 2409 | c->server = server; 2410 | c->state = STATE_DISCONNECTED; 2411 | c->cnx_idx = j; 2412 | 2413 | /* Various debug logging around timer management assume that the field 2414 | 'data' in ngx_event_t is a pointer to ngx_connection_t, therefore we 2415 | have a dummy such structure around so that it does not crash etc. */ 2416 | dummy_conn->data = c; 2417 | c->reconnect_event.log = c->log; 2418 | c->reconnect_event.data = dummy_conn; 2419 | c->reconnect_event.handler = ngx_http_auth_ldap_reconnect_handler; 2420 | 2421 | #if (NGX_OPENSSL) 2422 | c->pool = cycle->pool; 2423 | c->ssl = &halmcf->ssl; 2424 | #endif 2425 | 2426 | ngx_http_auth_ldap_connect(c); 2427 | } 2428 | } 2429 | 2430 | return NGX_OK; 2431 | } 2432 | 2433 | 2434 | 2435 | /*** Per-request authentication processing ***/ 2436 | 2437 | /** 2438 | * Respond with "403 Forbidden" and add correct headers 2439 | */ 2440 | static ngx_int_t 2441 | ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm) 2442 | { 2443 | r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); 2444 | if (r->headers_out.www_authenticate == NULL) { 2445 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 2446 | } 2447 | 2448 | r->headers_out.www_authenticate->hash = 1; 2449 | #if (nginx_version >= 1023000) 2450 | r->headers_out.www_authenticate->next = NULL; 2451 | #endif 2452 | r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1; 2453 | r->headers_out.www_authenticate->key.data = (u_char *) "WWW-Authenticate"; 2454 | r->headers_out.www_authenticate->value = *realm; 2455 | 2456 | return NGX_HTTP_UNAUTHORIZED; 2457 | } 2458 | 2459 | /** 2460 | * LDAP Authentication handler 2461 | */ 2462 | static ngx_int_t 2463 | ngx_http_auth_ldap_handler(ngx_http_request_t *r) 2464 | { 2465 | ngx_http_auth_ldap_loc_conf_t *alcf; 2466 | ngx_http_auth_ldap_ctx_t *ctx; 2467 | int rc; 2468 | 2469 | alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_ldap_module); 2470 | if (alcf->realm.len == 0) { 2471 | return NGX_DECLINED; 2472 | } 2473 | 2474 | if (alcf->servers == NULL || alcf->servers->nelts == 0) { 2475 | /* No LDAP servers for the location. */ 2476 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: \"auth_ldap\" requires " 2477 | "one or more \"auth_ldap_servers\" in the same location"); 2478 | return ngx_http_auth_ldap_set_realm(r, &alcf->realm); 2479 | } 2480 | 2481 | ctx = ngx_http_get_module_ctx(r, ngx_http_auth_ldap_module); 2482 | if (ctx == NULL) { 2483 | rc = ngx_http_auth_basic_user(r); 2484 | if (rc == NGX_DECLINED) { 2485 | return ngx_http_auth_ldap_set_realm(r, &alcf->realm); 2486 | } 2487 | if (rc == NGX_ERROR) { 2488 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 2489 | } 2490 | 2491 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Username is \"%V\"", 2492 | &r->headers_in.user); 2493 | if (r->headers_in.passwd.len == 0) 2494 | { 2495 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Password is empty"); 2496 | return ngx_http_auth_ldap_set_realm(r, &alcf->realm); 2497 | } 2498 | 2499 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_ldap_ctx_t)); 2500 | if (ctx == NULL) { 2501 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 2502 | } 2503 | ctx->r = r; 2504 | 2505 | /* Initialize the attributes array */ 2506 | ngx_array_init(&ctx->attributes, r->pool, DEFAULT_ATTRS_COUNT, sizeof(ldap_search_attribute_t)); 2507 | 2508 | /* Other fields have been initialized to zero/NULL */ 2509 | ngx_http_set_ctx(r, ctx, ngx_http_auth_ldap_module); 2510 | } 2511 | 2512 | return ngx_http_auth_ldap_authenticate(r, ctx, alcf); 2513 | } 2514 | 2515 | /** 2516 | * Iteratively handle all phases of the authentication process, might be called many times 2517 | */ 2518 | static ngx_int_t 2519 | ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, 2520 | ngx_http_auth_ldap_loc_conf_t *conf) 2521 | { 2522 | ngx_int_t rc; 2523 | ngx_uint_t i; 2524 | ngx_http_auth_ldap_server_t *s; 2525 | 2526 | if (r->connection->write->timedout) { 2527 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Authentication timed out"); 2528 | if (ctx->c != NULL) { 2529 | if (ctx->server && ctx->server->clean_on_timeout) { 2530 | // Authentication response timeouted => Close and clean the corresponding LDAP connection 2531 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 2532 | "ngx_http_auth_ldap_authenticate: Close timeouted Cnx[%d]", ctx->c->cnx_idx); 2533 | ngx_http_auth_ldap_close_connection(ctx->c, 1); 2534 | // Clean the connection 2535 | ctx->c->msgid = -1; 2536 | ctx->c = NULL; 2537 | } else { 2538 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 2539 | "ngx_http_auth_ldap_authenticate: Return old timedout Cnx[%d]", ctx->c->cnx_idx); 2540 | ngx_http_auth_ldap_return_connection(ctx->c); 2541 | } 2542 | } 2543 | 2544 | // Remove ctx from waiting_requests queue if it was added. 2545 | if (ngx_queue_next(&ctx->queue)) { 2546 | ngx_queue_remove(&ctx->queue); 2547 | } 2548 | 2549 | return NGX_ERROR; 2550 | } 2551 | 2552 | /* 2553 | * If we are not starting up a request (ctx->phase != PHASE_START) and we actually already 2554 | * sent a request (ctx->iteration > 0) and didn't receive a reply yet (!ctx->replied) we 2555 | * ask to be called again at a later time when we hopefully have received a reply. 2556 | * 2557 | * It is quite possible that we reach this if while not having sent a request yet (ctx->iteration == 0) - 2558 | * this happens when we are trying to get an LDAP connection but all of them are busy right now. 2559 | */ 2560 | if (ctx->iteration > 0 && !ctx->replied && ctx->phase != PHASE_START) { 2561 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: The LDAP operation did not finish yet"); 2562 | return NGX_AGAIN; 2563 | } 2564 | 2565 | for (;;) { 2566 | loop: 2567 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Authentication loop (phase=%d, iteration=%d)", 2568 | ctx->phase, ctx->iteration); 2569 | 2570 | switch (ctx->phase) { 2571 | case PHASE_START: 2572 | ctx->server = ((ngx_http_auth_ldap_server_t **) conf->servers->elts)[ctx->server_index]; 2573 | ctx->outcome = OUTCOME_UNCERTAIN; 2574 | 2575 | ngx_add_timer(r->connection->write, ctx->server->request_timeout); 2576 | 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); 2577 | 2578 | 2579 | /* Check cache if enabled */ 2580 | if (ngx_http_auth_ldap_cache.buckets != NULL) { 2581 | for (i = 0; i < conf->servers->nelts; i++) { 2582 | s = &((ngx_http_auth_ldap_server_t *) conf->servers->elts)[i]; 2583 | rc = ngx_http_auth_ldap_check_cache(r, ctx, &ngx_http_auth_ldap_cache, s); 2584 | 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); 2585 | if (rc == OUTCOME_DENY || rc == OUTCOME_ALLOW) { 2586 | ctx->outcome = (rc == OUTCOME_DENY ? OUTCOME_CACHED_DENY : OUTCOME_CACHED_ALLOW); 2587 | ctx->phase = PHASE_NEXT; 2588 | goto loop; 2589 | } 2590 | } 2591 | } 2592 | 2593 | if (ctx->server->require_valid_user_dn.value.data != NULL) { 2594 | /* Construct user DN */ 2595 | if (ngx_http_complex_value(r, &ctx->server->require_valid_user_dn, &ctx->user_dn) != NGX_OK) { 2596 | ngx_del_timer(r->connection->write); 2597 | return NGX_ERROR; 2598 | } 2599 | ctx->phase = PHASE_CHECK_USER; 2600 | break; 2601 | } 2602 | 2603 | ctx->phase = PHASE_SEARCH; 2604 | ctx->iteration = 0; 2605 | break; 2606 | 2607 | case PHASE_SEARCH: 2608 | /* Search the directory to retrieve full user DN */ 2609 | rc = ngx_http_auth_ldap_search(r, ctx); 2610 | if (rc == NGX_AGAIN) { 2611 | /* LDAP operation in progress, wait for the results */ 2612 | return NGX_AGAIN; 2613 | } 2614 | if (rc != NGX_OK) { 2615 | /* Search failed, try next server */ 2616 | ctx->phase = PHASE_NEXT; 2617 | break; 2618 | } 2619 | 2620 | /* User DN has been found, check user next */ 2621 | ctx->phase = PHASE_CHECK_USER; 2622 | break; 2623 | 2624 | case PHASE_CHECK_USER: 2625 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: User DN is \"%V\"", 2626 | &ctx->user_dn); 2627 | 2628 | if (ctx->server->require_user != NULL) { 2629 | rc = ngx_http_auth_ldap_check_user(r, ctx); 2630 | if (rc != NGX_OK) { 2631 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Not ok", &ctx->user_dn); 2632 | /* User check failed, try next server */ 2633 | ctx->phase = PHASE_NEXT; 2634 | break; 2635 | } 2636 | } 2637 | 2638 | /* User not yet fully authenticated, check group next */ 2639 | if ((ctx->outcome == OUTCOME_UNCERTAIN) && 2640 | (ctx->server->require_group != NULL)) { 2641 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Moving to group check", &ctx->user_dn); 2642 | ctx->phase = PHASE_CHECK_GROUP; 2643 | ctx->iteration = 0; 2644 | break; 2645 | } 2646 | 2647 | /* No groups to validate, try binding next */ 2648 | ctx->phase = PHASE_CHECK_BIND; 2649 | ctx->iteration = 0; 2650 | break; 2651 | 2652 | case PHASE_CHECK_GROUP: 2653 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Checking group", &ctx->user_dn); 2654 | rc = ngx_http_auth_ldap_check_group(r, ctx); 2655 | if (rc == NGX_AGAIN) { 2656 | /* LDAP operation in progress, wait for the results */ 2657 | return NGX_AGAIN; 2658 | } 2659 | if (rc != NGX_OK) { 2660 | /* Group check failed, try next server */ 2661 | ctx->phase = PHASE_NEXT; 2662 | break; 2663 | } 2664 | 2665 | /* Groups validated, try binding next */ 2666 | ctx->phase = PHASE_CHECK_BIND; 2667 | ctx->iteration = 0; 2668 | break; 2669 | 2670 | case PHASE_CHECK_BIND: 2671 | if (ctx->outcome == OUTCOME_UNCERTAIN) { 2672 | /* If we're still uncertain when satisfy is 'any' and there 2673 | * is at least one require user/group rule, it means no 2674 | * rule has matched. 2675 | */ 2676 | if ((ctx->server->satisfy_all == 0) && ( 2677 | (ctx->server->require_user != NULL) || 2678 | (ctx->server->require_group != NULL))){ 2679 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: no requirement satisfied"); 2680 | ctx->outcome = OUTCOME_DENY; 2681 | ctx->phase = PHASE_NEXT; 2682 | /*rc = NGX_DECLINED;*/ 2683 | break; 2684 | } else { 2685 | /* So far so good */ 2686 | ctx->outcome = OUTCOME_ALLOW; 2687 | } 2688 | } 2689 | 2690 | /* Initiate bind using the found DN and request password */ 2691 | rc = ngx_http_auth_ldap_check_bind(r, ctx); 2692 | if (rc == NGX_AGAIN) { 2693 | /* LDAP operation in progress, wait for the result */ 2694 | return NGX_AGAIN; 2695 | } 2696 | 2697 | /* All steps done, finish the processing */ 2698 | ctx->phase = PHASE_REBIND; 2699 | ctx->iteration = 0; 2700 | break; 2701 | 2702 | case PHASE_REBIND: 2703 | /* Initiate bind using the found DN and request password */ 2704 | rc = ngx_http_auth_ldap_recover_bind(r, ctx); 2705 | if (rc == NGX_AGAIN) { 2706 | /* LDAP operation in progress, wait for the result */ 2707 | return NGX_AGAIN; 2708 | } 2709 | 2710 | /* All steps done, finish the processing */ 2711 | ctx->phase = PHASE_NEXT; 2712 | break; 2713 | 2714 | case PHASE_NEXT: 2715 | if (r->connection->write->timer_set) { 2716 | ngx_del_timer(r->connection->write); 2717 | } 2718 | 2719 | if (ctx->c != NULL) { 2720 | ngx_http_auth_ldap_return_connection(ctx->c); 2721 | } 2722 | 2723 | if (ngx_http_auth_ldap_cache.buckets != NULL && 2724 | (ctx->outcome == OUTCOME_DENY || ctx->outcome == OUTCOME_ALLOW)) { 2725 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_authenticate: Caching outcome %d", ctx->outcome); 2726 | ngx_http_auth_ldap_update_cache(ctx, &ngx_http_auth_ldap_cache, ctx->outcome); 2727 | } 2728 | 2729 | if (ctx->outcome == OUTCOME_ALLOW || ctx->outcome == OUTCOME_CACHED_ALLOW) { 2730 | /* Create response headers for each search attributes found in context */ 2731 | for (i = 0; i < ctx->attributes.nelts; i++) { 2732 | ldap_search_attribute_t *elt = (ldap_search_attribute_t *)ctx->attributes.elts + i; 2733 | ngx_table_elt_t *h = ngx_list_push(&r->headers_out.headers); 2734 | if (h != NULL) { 2735 | h->hash = 1; 2736 | #if (nginx_version >= 1023000) 2737 | h->next = NULL; 2738 | #endif 2739 | h->key = elt->attr_name; 2740 | h->value = elt->attr_value; 2741 | } 2742 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 2743 | "ngx_http_auth_ldap_authenticate: Set response header %V : %V", 2744 | &elt->attr_name, &elt->attr_value); 2745 | } 2746 | return NGX_OK; 2747 | } 2748 | 2749 | ctx->server_index++; 2750 | if (ctx->server_index >= conf->servers->nelts) { 2751 | return ngx_http_auth_ldap_set_realm(r, &conf->realm); 2752 | } 2753 | 2754 | ctx->phase = PHASE_START; 2755 | break; 2756 | } 2757 | } 2758 | } 2759 | 2760 | static ngx_int_t 2761 | ngx_http_auth_ldap_search(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) 2762 | { 2763 | LDAPURLDesc *ludpp; 2764 | u_char *filter; 2765 | ngx_int_t rc; 2766 | 2767 | /* On the first call, initiate the LDAP search operation */ 2768 | if (ctx->iteration == 0) { 2769 | if (!ngx_http_auth_ldap_get_connection(ctx)) { 2770 | return NGX_AGAIN; 2771 | } 2772 | 2773 | ludpp = ctx->server->ludpp; 2774 | filter = ngx_pcalloc( 2775 | r->pool, 2776 | (ludpp->lud_filter != NULL ? ngx_strlen(ludpp->lud_filter) : ngx_strlen("(objectClass=*)")) + 2777 | ngx_strlen("(&(=))") + ngx_strlen(ludpp->lud_attrs[0]) + r->headers_in.user.len + 1); 2778 | ngx_sprintf(filter, "(&%s(%s=%V))%Z", 2779 | ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", 2780 | ludpp->lud_attrs[0], &r->headers_in.user); 2781 | ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_search: Cnx[%d] Searching (with filter \"%s\") ...", 2782 | ctx->c->cnx_idx, (const char *) filter); 2783 | 2784 | 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); 2785 | if (rc != LDAP_SUCCESS) { 2786 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_search: ldap_search_ext() Cnx[%d] failed (%d, %s)", 2787 | ctx->c->cnx_idx, rc, ldap_err2string(rc)); 2788 | ngx_http_auth_ldap_return_connection(ctx->c); 2789 | return NGX_ERROR; 2790 | } 2791 | 2792 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_search: ldap_search_ext() Cnx[%d] -> msgid=%d", 2793 | ctx->c->cnx_idx, ctx->c->msgid); 2794 | ctx->c->state = STATE_SEARCHING; 2795 | ctx->iteration++; 2796 | return NGX_AGAIN; 2797 | } 2798 | 2799 | /* On the second call, handle the search results */ 2800 | if (ctx->error_code != LDAP_SUCCESS) { 2801 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_search: ldap_search_ext() Cnx[%d] request failed (%d: %s)", 2802 | ctx->c->cnx_idx, ctx->error_code, ldap_err2string(ctx->error_code)); 2803 | return NGX_ERROR; 2804 | } 2805 | 2806 | if (ctx->dn.data == NULL) { 2807 | 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); 2808 | return NGX_ERROR; 2809 | } else { 2810 | ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_search: Cnx[%d] Found dn: \"%s\")", 2811 | ctx->c->cnx_idx, ctx->dn.data); 2812 | ctx->user_dn.len = ngx_strlen(ctx->dn.data); 2813 | ctx->user_dn.data = (u_char *) ngx_palloc(ctx->r->pool, ctx->user_dn.len + 1); 2814 | ngx_memcpy(ctx->user_dn.data, ctx->dn.data, ctx->user_dn.len + 1); 2815 | ctx->dn.data = NULL; 2816 | ctx->dn.len = 0; 2817 | } 2818 | 2819 | return NGX_OK; 2820 | } 2821 | 2822 | static ngx_int_t 2823 | ngx_http_auth_ldap_check_user(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) 2824 | { 2825 | ngx_http_complex_value_t *values; 2826 | ngx_uint_t i; 2827 | 2828 | values = ctx->server->require_user->elts; 2829 | for (i = 0; i < ctx->server->require_user->nelts; i++) { 2830 | ngx_str_t val; 2831 | if (ngx_http_complex_value(r, &values[i], &val) != NGX_OK) { 2832 | ctx->outcome = OUTCOME_ERROR; 2833 | return NGX_ERROR; 2834 | } 2835 | 2836 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Comparing user DN with \"%V\"", &val); 2837 | if (val.len == ctx->user_dn.len && ngx_memcmp(val.data, ctx->user_dn.data, val.len) == 0) { 2838 | if (ctx->server->satisfy_all == 0) { 2839 | ctx->outcome = OUTCOME_ALLOW; 2840 | return NGX_OK; 2841 | } 2842 | } else { 2843 | if (ctx->server->satisfy_all == 1) { 2844 | ctx->outcome = OUTCOME_DENY; 2845 | return NGX_DECLINED; 2846 | } 2847 | } 2848 | } 2849 | 2850 | return NGX_OK; 2851 | } 2852 | 2853 | static ngx_int_t 2854 | ngx_http_auth_ldap_check_group(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) 2855 | { 2856 | ngx_http_complex_value_t *values; 2857 | ngx_int_t rc; 2858 | u_char *filter; 2859 | char *user_val; 2860 | char *attrs[2]; 2861 | size_t for_filter; 2862 | 2863 | /* Handle result of the search started during previous call */ 2864 | if (ctx->iteration > 0) { 2865 | ctx->group_dn.data = ctx->dn.data; 2866 | if (ctx->group_dn.data == NULL) { 2867 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() returned NULL result"); 2868 | if (ctx->server->satisfy_all == 1) { 2869 | ctx->outcome = OUTCOME_DENY; 2870 | return NGX_DECLINED; 2871 | } 2872 | } else { 2873 | if (ctx->error_code == LDAP_SUCCESS) { 2874 | if (ctx->server->satisfy_all == 0) { 2875 | ctx->outcome = OUTCOME_ALLOW; 2876 | return NGX_OK; 2877 | } 2878 | } else if (ctx->error_code == LDAP_NO_RESULTS_RETURNED) { 2879 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() request failed (%d: %s)", 2880 | ctx->error_code, ldap_err2string(ctx->error_code)); 2881 | if (ctx->server->satisfy_all == 1) { 2882 | ctx->outcome = OUTCOME_DENY; 2883 | return NGX_DECLINED; 2884 | } 2885 | } else { 2886 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() request failed (%d: %s)", 2887 | ctx->error_code, ldap_err2string(ctx->error_code)); 2888 | return NGX_ERROR; 2889 | } 2890 | } 2891 | } 2892 | 2893 | /* Check next group */ 2894 | if (ctx->iteration >= ctx->server->require_group->nelts) { 2895 | /* No more groups */ 2896 | return NGX_OK; 2897 | } 2898 | 2899 | if (!ngx_http_auth_ldap_get_connection(ctx)) { 2900 | return NGX_AGAIN; 2901 | } 2902 | 2903 | ctx->replied = 0; 2904 | ngx_str_t val; 2905 | values = ctx->server->require_group->elts; 2906 | if (ngx_http_complex_value(r, &values[ctx->iteration], &val) != NGX_OK) { 2907 | ctx->outcome = OUTCOME_ERROR; 2908 | ngx_http_auth_ldap_return_connection(ctx->c); 2909 | return NGX_ERROR; 2910 | } 2911 | 2912 | char *gr; 2913 | char *cn_gr; 2914 | char *tail_gr; 2915 | gr = ngx_pcalloc(r->pool,val.len+1); 2916 | ngx_memcpy(gr, val.data, val.len); 2917 | gr[val.len] = '\0'; 2918 | tail_gr = ngx_strchr(gr, ','); 2919 | if (tail_gr == NULL) { 2920 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: Incorrect group DN: \"%s\"", gr); 2921 | ctx->outcome = OUTCOME_ERROR; 2922 | ngx_http_auth_ldap_return_connection(ctx->c); 2923 | return NGX_ERROR; 2924 | } 2925 | *tail_gr = '\0'; 2926 | tail_gr++; 2927 | cn_gr = gr; 2928 | 2929 | if (ctx->server->group_attribute_dn == 1) { 2930 | user_val = ngx_pcalloc( 2931 | r->pool, 2932 | ctx->user_dn.len + 1); 2933 | ngx_memcpy(user_val, ctx->user_dn.data, ctx->user_dn.len); 2934 | user_val[ctx->user_dn.len] = '\0'; 2935 | } else { 2936 | user_val = ngx_pcalloc( 2937 | r->pool, 2938 | r->headers_in.user.len + 1); 2939 | ngx_memcpy(user_val, r->headers_in.user.data, r->headers_in.user.len); 2940 | user_val[r->headers_in.user.len] = '\0'; 2941 | } 2942 | 2943 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Search user in group \"%V\"", &val); 2944 | 2945 | if (ctx->server->group_attribute.data == NULL) { 2946 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: group_attribute.data is \"%V\" so calling a failure here", ctx->server->group_attribute.data); 2947 | rc = !LDAP_SUCCESS; 2948 | } else { 2949 | for_filter = ngx_strlen(cn_gr) + ctx->server->group_attribute.len + ngx_strlen((const char *) user_val) + ngx_strlen("(&()(=))") + 1; 2950 | filter = ngx_pcalloc( 2951 | r->pool, 2952 | for_filter); 2953 | ngx_sprintf(filter, "(&(%s)(%s=%s))", cn_gr, ctx->server->group_attribute.data, user_val); 2954 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Search group filter is \"%s\"", (const char *) filter); 2955 | attrs[0] = LDAP_NO_ATTRS; 2956 | attrs[1] = NULL; 2957 | rc = ldap_search_ext(ctx->c->ld, tail_gr, LDAP_SCOPE_ONELEVEL, (const char *) filter, attrs, 0, NULL, NULL, NULL, 0, &ctx->c->msgid); 2958 | } 2959 | if (rc != LDAP_SUCCESS) { 2960 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() failed (%d: %s)", 2961 | rc, ldap_err2string(rc)); 2962 | ctx->outcome = OUTCOME_ERROR; 2963 | ngx_http_auth_ldap_return_connection(ctx->c); 2964 | return NGX_ERROR; 2965 | } 2966 | 2967 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() -> msgid=%d", 2968 | ctx->c->msgid); 2969 | ctx->c->state = STATE_SEARCHING; 2970 | ctx->iteration++; 2971 | return NGX_AGAIN; 2972 | } 2973 | 2974 | /** 2975 | * Initiate and handle a bind operation using the authentication parameters 2976 | */ 2977 | static ngx_int_t 2978 | ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) 2979 | { 2980 | struct berval cred; 2981 | ngx_int_t rc; 2982 | 2983 | /* On the first call, initiate the bind LDAP operation */ 2984 | if (ctx->iteration == 0) { 2985 | if (!ngx_http_auth_ldap_get_connection(ctx)) { 2986 | return NGX_AGAIN; 2987 | } 2988 | 2989 | cred.bv_val = (char *) r->headers_in.passwd.data; 2990 | cred.bv_len = r->headers_in.passwd.len; 2991 | ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] Binding (user dn: %s) ...", 2992 | ctx->c->cnx_idx, ctx->user_dn.data); 2993 | rc = ldap_sasl_bind(ctx->c->ld, (const char *) ctx->user_dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &ctx->c->msgid); 2994 | if (rc != LDAP_SUCCESS) { 2995 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] ldap_sasl_bind() failed (%d: %s)", 2996 | ctx->c->cnx_idx, rc, ldap_err2string(rc)); 2997 | ctx->outcome = OUTCOME_ERROR; 2998 | ngx_http_auth_ldap_return_connection(ctx->c); 2999 | return NGX_ERROR; 3000 | } 3001 | 3002 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] ldap_sasl_bind() -> msgid=%d", 3003 | ctx->c->cnx_idx, ctx->c->msgid); 3004 | ctx->c->state = STATE_BINDING; 3005 | ctx->iteration++; 3006 | 3007 | return NGX_AGAIN; 3008 | } 3009 | 3010 | /* On the second call, process the operation result */ 3011 | if (ctx->error_code != LDAP_SUCCESS) { 3012 | 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)", 3013 | ctx->c->cnx_idx, ctx->user_dn.data, ctx->error_code, ldap_err2string(ctx->error_code)); 3014 | ctx->outcome = OUTCOME_DENY; 3015 | } else { 3016 | ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_check_bind: Cnx[%d] User (dn: %s) bind successfull", 3017 | ctx->c->cnx_idx, ctx->user_dn.data); 3018 | ctx->outcome = OUTCOME_ALLOW; 3019 | } 3020 | return NGX_OK; 3021 | } 3022 | 3023 | static ngx_int_t 3024 | ngx_http_auth_ldap_recover_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) 3025 | { 3026 | struct berval cred; 3027 | ngx_int_t rc; 3028 | 3029 | /* On the first call, initiate the bind LDAP operation */ 3030 | if (ctx->iteration == 0) { 3031 | if (!ngx_http_auth_ldap_get_connection(ctx)) { 3032 | return NGX_AGAIN; 3033 | } 3034 | 3035 | cred.bv_val = (char *) ctx->server->bind_dn_passwd.data; 3036 | cred.bv_len = ctx->server->bind_dn_passwd.len; 3037 | ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] Rebinding to binddn ...", 3038 | ctx->c->cnx_idx); 3039 | rc = ldap_sasl_bind(ctx->c->ld, (const char *) ctx->server->bind_dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &ctx->c->msgid); 3040 | if (rc != LDAP_SUCCESS) { 3041 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] ldap_sasl_bind() failed (%d: %s)", 3042 | ctx->c->cnx_idx, rc, ldap_err2string(rc)); 3043 | ctx->outcome = OUTCOME_ERROR; 3044 | ngx_http_auth_ldap_return_connection(ctx->c); 3045 | return NGX_ERROR; 3046 | } 3047 | 3048 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] ldap_sasl_bind() -> msgid=%d", 3049 | ctx->c->cnx_idx, ctx->c->msgid); 3050 | ctx->c->state = STATE_BINDING; 3051 | ctx->iteration++; 3052 | return NGX_AGAIN; 3053 | } 3054 | 3055 | /* On the second call, process the operation result */ 3056 | if (ctx->error_code != LDAP_SUCCESS) { 3057 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] Rebinding to binddn failed (%d: %s)", 3058 | ctx->c->cnx_idx, ctx->error_code, ldap_err2string(ctx->error_code)); 3059 | } else { 3060 | 3061 | ngx_log_error(NGX_LOG_INFO, ctx->c->log, 0, "ngx_http_auth_ldap_recover_bind: Cnx[%d] Rebinding to binddn successful", 3062 | ctx->c->cnx_idx); 3063 | } 3064 | return NGX_OK; 3065 | } 3066 | --------------------------------------------------------------------------------