From 04ed544f492f72f5fb6b4e211a5456349911819a Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Mon, 22 Jul 2013 15:46:40 +0200 Subject: [PATCH 01/18] Fix a typo in Pavel Brylov's configuration refactoring --- ngx_http_auth_ldap_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index f10b5d0..c83db56 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -218,7 +218,7 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) server->bind_dn_passwd = value[1]; } else if(ngx_strcmp(value[0].data, "group_attribute") == 0) { server->group_attribute = value[1]; - } else if(ngx_strcmp(value[0].data, "group_attribute_is_dn") == 0 && ngx_strcmp(value[1].data, "on")) { + } else if(ngx_strcmp(value[0].data, "group_attribute_is_dn") == 0 && ngx_strcmp(value[1].data, "on") == 0) { server->group_attribute_dn = 1; } else if(ngx_strcmp(value[0].data, "require") == 0) { return ngx_http_auth_ldap_parse_require(cf, server); From 2d30d6e4ae719e3e22e7d11ef82723f40bc922cc Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Mon, 22 Jul 2013 16:56:49 +0200 Subject: [PATCH 02/18] Remove forgotten "auth_basic" from ngx_http_auth_basic_create_loc_conf() --- ngx_http_auth_ldap_module.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index c83db56..4da0bb0 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -76,7 +76,7 @@ static char * ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_ldap_server * static char * ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); -static void * ngx_http_auth_basic_create_loc_conf(ngx_conf_t *); +static void * ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *); static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *); static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_ldap_server *server, ngx_ldap_userinfo *uinfo, ngx_http_auth_ldap_loc_conf_t *conf); @@ -122,7 +122,7 @@ static ngx_http_module_t ngx_http_auth_ldap_module_ctx = { NULL, /* init main configuration */ NULL, //ngx_http_auth_ldap_create_server_conf, /* create server configuration */ NULL, //ngx_http_auth_ldap_merge_server_conf, /* merge server configuration */ - ngx_http_auth_basic_create_loc_conf, /* create location configuration */ + ngx_http_auth_ldap_create_loc_conf, /* create location configuration */ ngx_http_auth_ldap_merge_loc_conf /* merge location configuration */ }; @@ -448,7 +448,7 @@ ngx_http_auth_ldap_create_conf(ngx_conf_t *cf) * Create location conf */ static void * -ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf) { +ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *cf) { ngx_http_auth_ldap_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_ldap_loc_conf_t)); if (conf == NULL) { From 4228721e81e3b763c701364ea5abf5e406ff1e77 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Tue, 23 Jul 2013 18:24:33 +0200 Subject: [PATCH 03/18] Minor refactoring and cleanup - Renamed ngx_ldap_server to ngx_http_auth_ldap_server_t. - Used NGX_CONF_ANY instead of many NGX_CONF_TAKExxxx for the configuration option taking any number of arguments. --- ngx_http_auth_ldap_module.c | 44 +++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 4da0bb0..83dc39c 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -55,7 +55,7 @@ typedef struct { ngx_array_t *require_user; /* array of ngx_ldap_require_t */ ngx_flag_t require_valid_user; ngx_flag_t satisfy_all; -} ngx_ldap_server; +} ngx_http_auth_ldap_server_t; typedef struct { ngx_str_t realm; @@ -63,22 +63,22 @@ typedef struct { } ngx_http_auth_ldap_loc_conf_t; typedef struct { - ngx_array_t *servers; /* array of ngx_ldap_server */ + ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t */ ngx_hash_t srv; } ngx_http_auth_ldap_conf_t; static void * ngx_http_auth_ldap_create_conf(ngx_conf_t *cf); static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char * ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_ldap_server *server); -static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_ldap_server *server); -static char * ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_ldap_server *server); +static char * ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); +static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); +static char * ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); static char * ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); static void * ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *); static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *); -static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_ldap_server *server, +static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, ngx_ldap_userinfo *uinfo, ngx_http_auth_ldap_loc_conf_t *conf); static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm); static ngx_ldap_userinfo * ngx_http_auth_ldap_get_user_info(ngx_http_request_t *); @@ -90,7 +90,7 @@ static ngx_conf_post_handler_pt ngx_http_auth_ldap_p = ngx_http_auth_ldap; static ngx_command_t ngx_http_auth_ldap_commands[] = { { ngx_string("ldap_server"), - NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF | NGX_CONF_BLOCK | NGX_CONF_TAKE1, ngx_http_auth_ldap_ldap_server_block, NGX_HTTP_MAIN_CONF_OFFSET, 0, @@ -106,7 +106,7 @@ static ngx_command_t ngx_http_auth_ldap_commands[] = { }, { ngx_string("auth_ldap_servers"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1234 | NGX_CONF_TAKE5 |NGX_CONF_TAKE6|NGX_CONF_TAKE7, + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_ANY, ngx_conf_set_str_array_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_auth_ldap_loc_conf_t, servers), @@ -148,11 +148,11 @@ ngx_module_t ngx_http_auth_ldap_module = { static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - char *rv; - ngx_str_t *value, name; - ngx_conf_t save; - ngx_ldap_server server, *s; - ngx_http_auth_ldap_conf_t *cnf = conf; + char *rv; + ngx_str_t *value, name; + ngx_conf_t save; + ngx_http_auth_ldap_server_t server, *s; + ngx_http_auth_ldap_conf_t *cnf = conf; value = cf->args->elts; @@ -166,7 +166,7 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c server.alias = name; if (cnf->servers == NULL) { - cnf->servers = ngx_array_create(cf->pool, 7, sizeof(ngx_ldap_server)); + cnf->servers = ngx_array_create(cf->pool, 7, sizeof(ngx_http_auth_ldap_server_t)); if (cnf->servers == NULL) { return NGX_CONF_ERROR; } @@ -201,11 +201,11 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) char *rv; ngx_str_t *value; - ngx_ldap_server *server; + ngx_http_auth_ldap_server_t *server; ngx_http_auth_ldap_conf_t *cnf = conf; // It should be safe to just use latest server from array - server = ((ngx_ldap_server*)cnf->servers->elts + (cnf->servers->nelts - 1)); + server = ((ngx_http_auth_ldap_server_t*)cnf->servers->elts + (cnf->servers->nelts - 1)); value = cf->args->elts; @@ -269,7 +269,7 @@ ngx_http_auth_ldap(ngx_conf_t *cf, void *post, void *data) { * Parse URL conf parameter */ static char * -ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_ldap_server *server) { +ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) { ngx_str_t *value; u_char *p; value = cf->args->elts; @@ -340,7 +340,7 @@ ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_ldap_server *server) { * Parse "require" conf parameter */ static char * -ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_ldap_server *server) { +ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) { ngx_http_script_compile_t sc; ngx_str_t *value; @@ -410,7 +410,7 @@ ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_ldap_server *server) { * Parse "satisfy" conf parameter */ static char * -ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_ldap_server *server) { +ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) { ngx_str_t *value; value = cf->args->elts; @@ -543,7 +543,7 @@ ngx_http_auth_ldap_get_user_info(ngx_http_request_t *r) { static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf, ngx_http_auth_ldap_conf_t *mconf) { - ngx_ldap_server *server, *servers; + ngx_http_auth_ldap_server_t *server, *servers; servers = mconf->servers->elts; int rc; ngx_uint_t i, k; @@ -608,7 +608,9 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http /** * Actual authentication against LDAP server */ -static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_ldap_server *server, ngx_ldap_userinfo *uinfo, ngx_http_auth_ldap_loc_conf_t *conf) { +static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, + ngx_ldap_userinfo *uinfo, ngx_http_auth_ldap_loc_conf_t *conf) { + LDAPURLDesc *ludpp = server->ludpp; int rc; LDAP *ld; From 17fce4fe0a9d4f5d3f18869ac296b511bc039b44 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Mon, 22 Jul 2013 15:18:15 +0200 Subject: [PATCH 04/18] Remove ngx_ldap_userinfo and custom parsing of the Authorization header The core fills both decoded user name and password into r->headers_in.user or r->headers_in.passwd, respectively, when ngx_http_auth_basic_user() is called. Maybe it did not work like this before, but there is definitely no need to handle the decoding ourselves now (nginx 1.4.1). --- ngx_http_auth_ldap_module.c | 64 ++++++------------------------------- 1 file changed, 10 insertions(+), 54 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 83dc39c..e5e4d92 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -29,11 +29,6 @@ #include #include -typedef struct { - ngx_str_t username; - ngx_str_t password; -} ngx_ldap_userinfo; - typedef struct { ngx_str_t value; ngx_array_t *lengths; @@ -79,9 +74,8 @@ static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); static void * ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *); static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *); static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, - ngx_ldap_userinfo *uinfo, ngx_http_auth_ldap_loc_conf_t *conf); + ngx_http_auth_ldap_loc_conf_t *conf); static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm); -static ngx_ldap_userinfo * ngx_http_auth_ldap_get_user_info(ngx_http_request_t *); static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf, ngx_http_auth_ldap_conf_t *mconf); static char * ngx_http_auth_ldap(ngx_conf_t *cf, void *post, void *data); @@ -505,38 +499,6 @@ static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) { return ngx_http_auth_ldap_authenticate(r, alcf, cnf); } -/** - * Get login and password from http request. - */ -static ngx_ldap_userinfo* -ngx_http_auth_ldap_get_user_info(ngx_http_request_t *r) { - size_t len; - ngx_ldap_userinfo* uinfo; - u_char *uname_buf, *p; - - uinfo = ngx_palloc(r->pool, sizeof(ngx_ldap_userinfo)); - - for (len = 0; len < r->headers_in.user.len; len++) { - if (r->headers_in.user.data[len] == ':') { - break; - } - } - - uname_buf = ngx_palloc(r->pool, len + 1); - if (uname_buf == NULL) { - return NULL; - } - p = ngx_cpymem(uname_buf, r->headers_in.user.data, len); - *p = '\0'; - - uinfo->username.data = uname_buf; - uinfo->username.len = len; - uinfo->password.data = r->headers_in.passwd.data; - uinfo->password.len = r->headers_in.passwd.len; - - return uinfo; -} - /** * Read user credentials from request, set LDAP parameters and call authentication against required servers */ @@ -551,18 +513,12 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http int version = LDAP_VERSION3; int reqcert = LDAP_OPT_X_TLS_ALLOW; - ngx_ldap_userinfo *uinfo; struct timeval timeOut = { 10, 0 }; ngx_flag_t pass = NGX_CONF_UNSET; - uinfo = ngx_http_auth_ldap_get_user_info(r); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP username: %s", uinfo->username.data); - - if (uinfo == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP username: %V", &r->headers_in.user); - if (uinfo->password.len == 0) + if (r->headers_in.passwd.len == 0) { return ngx_http_auth_ldap_set_realm(r, &conf->realm); } @@ -586,7 +542,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http server = &servers[i]; if (server->alias.len == alias->len && ngx_strncmp(server->alias.data, alias->data, server->alias.len) == 0) { found = 1; - pass = ngx_http_auth_ldap_authenticate_against_server(r, server, uinfo, conf); + pass = ngx_http_auth_ldap_authenticate_against_server(r, server, conf); if (pass == 1) { return NGX_OK; } else if (pass == NGX_HTTP_INTERNAL_SERVER_ERROR) { @@ -609,7 +565,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http * Actual authentication against LDAP server */ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, - ngx_ldap_userinfo *uinfo, ngx_http_auth_ldap_loc_conf_t *conf) { + ngx_http_auth_ldap_loc_conf_t *conf) { LDAPURLDesc *ludpp = server->ludpp; int rc; @@ -652,9 +608,9 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request filter = ngx_pcalloc( r->pool, (ludpp->lud_filter != NULL ? ngx_strlen(ludpp->lud_filter) : ngx_strlen("(objectClass=*)")) + ngx_strlen("(&(=))") + ngx_strlen(ludpp->lud_attrs[0]) - + uinfo->username.len + 1); + + r->headers_in.user.len + 1); - p = ngx_sprintf(filter, "(&%s(%s=%s))", ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", ludpp->lud_attrs[0], uinfo->username.data); + p = ngx_sprintf(filter, "(&%s(%s=%V))", ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", ludpp->lud_attrs[0], &r->headers_in.user); *p = 0; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: filter %s", (const char*) filter); @@ -716,8 +672,8 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request bvalue.bv_val = dn; bvalue.bv_len = ngx_strlen(dn); } else { - bvalue.bv_val = (char*) uinfo->username.data; - bvalue.bv_len = uinfo->username.len; + bvalue.bv_val = (char*) r->headers_in.user.data; + bvalue.bv_len = r->headers_in.user.len; } value = server->require_group->elts; @@ -769,7 +725,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request /// Check valid user if ( pass != 0 || (server->require_valid_user == 1 && server->satisfy_all == 0 && pass == 0)) { /// Bind user to the server - rc = ldap_simple_bind_s(ld, dn, (const char *) uinfo->password.data); + rc = ldap_simple_bind_s(ld, dn, (const char *) r->headers_in.passwd.data); if (rc != LDAP_SUCCESS) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_simple_bind_s error: %d, %s", rc, ldap_err2string(rc)); From 4947e2047dd0dd5df6da13fe1c8fe708c2c88e47 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Mon, 22 Jul 2013 15:39:08 +0200 Subject: [PATCH 05/18] Use ngx_http_complex_value_t instead of custom script handling Makes the code smaller and cleaner by just using the core functionality. --- ngx_http_auth_ldap_module.c | 122 ++++++++++++------------------------ 1 file changed, 40 insertions(+), 82 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index e5e4d92..ba91573 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -29,12 +29,6 @@ #include #include -typedef struct { - ngx_str_t value; - ngx_array_t *lengths; - ngx_array_t *values; -} ngx_ldap_require_t; - typedef struct { LDAPURLDesc *ludpp; ngx_str_t url; @@ -46,8 +40,8 @@ typedef struct { ngx_str_t group_attribute; ngx_flag_t group_attribute_dn; - ngx_array_t *require_group; /* array of ngx_ldap_require_t */ - ngx_array_t *require_user; /* array of ngx_ldap_require_t */ + ngx_array_t *require_group; /* array of ngx_http_complex_value_t */ + ngx_array_t *require_user; /* array of ngx_http_complex_value_t */ ngx_flag_t require_valid_user; ngx_flag_t satisfy_all; } ngx_http_auth_ldap_server_t; @@ -336,65 +330,43 @@ ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) { - ngx_http_script_compile_t sc; ngx_str_t *value; + ngx_http_complex_value_t* rule = NULL; + ngx_http_compile_complex_value_t ccv; value = cf->args->elts; - if (server->require_user == NULL) { - server->require_user = ngx_array_create(cf->pool, 4, sizeof(ngx_ldap_require_t)); + if (ngx_strcmp(value[1].data, "valid_user") == 0) { + server->require_valid_user = 1; + return NGX_CONF_OK; + } else if (ngx_strcmp(value[1].data, "user") == 0) { if (server->require_user == NULL) { - return NGX_CONF_ERROR; + server->require_user = ngx_array_create(cf->pool, 4, sizeof(ngx_http_complex_value_t)); + if (server->require_user == NULL) { + return NGX_CONF_ERROR; + } } - } - - if (server->require_group == NULL) { - server->require_group = ngx_array_create(cf->pool, 4, sizeof(ngx_ldap_require_t)); + rule = ngx_array_push(server->require_user); + } else if (ngx_strcmp(value[1].data, "group") == 0) { if (server->require_group == NULL) { - return NGX_CONF_ERROR; + server->require_group = ngx_array_create(cf->pool, 4, sizeof(ngx_http_complex_value_t)); + if (server->require_group == NULL) { + return NGX_CONF_ERROR; + } } + rule = ngx_array_push(server->require_group); } - if (ngx_strcmp(value[1].data, "valid_user") == 0) { - server->require_valid_user = 1; + if (rule == NULL) { + return NGX_CONF_ERROR; } - if (ngx_strcmp(value[1].data, "user") == 0 || ngx_strcmp(value[1].data, "group") == 0) - { - ngx_int_t n; - ngx_ldap_require_t *rule = NULL; - - if (ngx_strcmp(value[1].data, "user") == 0) { - rule = ngx_array_push(server->require_user); - } - - if (ngx_strcmp(value[1].data, "group") == 0) { - rule = ngx_array_push(server->require_group); - } - - if (rule == NULL) { - return NGX_CONF_ERROR; - } - - rule->value.data = value[2].data; - rule->value.len = value[2].len; - rule->values = NULL; - rule->lengths = NULL; - - n = ngx_http_script_variables_count(&value[2]); - if(n > 0) { - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - sc.cf = cf; - sc.source = &value[2]; - sc.lengths = &rule->lengths; - sc.values = &rule->values; - sc.complete_lengths = 1; - sc.complete_values = 1; - - if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_CONF_ERROR; - } - } + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = rule; + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; } return NGX_CONF_OK; @@ -573,7 +545,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request LDAPMessage *searchResult; char *dn; u_char *p, *filter; - ngx_ldap_require_t *value; + ngx_http_complex_value_t *value; ngx_uint_t i; struct berval bvalue; ngx_flag_t pass = NGX_CONF_UNSET; @@ -635,21 +607,14 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request value = server->require_user->elts; for (i = 0; i < server->require_user->nelts; i++) { ngx_str_t val; - if (value[i].lengths == NULL) { - val = value[i].value; - } else { - if (ngx_http_script_run(r, &val, value[i].lengths->elts, 0, - value[i].values->elts) == NULL) - { - ldap_memfree(dn); - ldap_msgfree(searchResult); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - val.data[val.len] = '\0'; + if (ngx_http_complex_value(r, &value[i], &val) != NGX_OK) { + ldap_memfree(dn); + ldap_msgfree(searchResult); + ldap_unbind_s(ld); + return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: compare with: %s", val.data); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: compare with: %V", &val); if (ngx_strncmp(val.data, dn, val.len) == 0) { pass = 1; if (server->satisfy_all == 0) { @@ -680,21 +645,14 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request for (i = 0; i < server->require_group->nelts; i++) { ngx_str_t val; - if (value[i].lengths == NULL) { - val = value[i].value; - } else { - if (ngx_http_script_run(r, &val, value[i].lengths->elts, 0, - value[i].values->elts) == NULL) - { - ldap_memfree(dn); - ldap_msgfree(searchResult); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - val.data[val.len] = '\0'; + if (ngx_http_complex_value(r, &value[i], &val) != NGX_OK) { + ldap_memfree(dn); + ldap_msgfree(searchResult); + ldap_unbind_s(ld); + return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: group compare with: %s", val.data); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: group compare with: %V", &val); rc = ldap_compare_ext_s(ld, (const char*) val.data, (const char*) server->group_attribute.data, &bvalue, NULL, NULL); From 89f0f755925b0349d63b117e41a6e481ae32e5d6 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Tue, 23 Jul 2013 18:51:49 +0200 Subject: [PATCH 06/18] Simplify processing of auth_ldap configuration directives --- ngx_http_auth_ldap_module.c | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index ba91573..1e10071 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -72,8 +72,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm); static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf, ngx_http_auth_ldap_conf_t *mconf); -static char * ngx_http_auth_ldap(ngx_conf_t *cf, void *post, void *data); -static ngx_conf_post_handler_pt ngx_http_auth_ldap_p = ngx_http_auth_ldap; +static char * ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_command_t ngx_http_auth_ldap_commands[] = { { @@ -87,10 +86,10 @@ static ngx_command_t ngx_http_auth_ldap_commands[] = { { ngx_string("auth_ldap"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_http_auth_ldap, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_ldap_loc_conf_t, realm), - &ngx_http_auth_ldap_p + 0, + NULL }, { ngx_string("auth_ldap_servers"), @@ -223,33 +222,27 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) * Parse auth_ldap directive */ static char * -ngx_http_auth_ldap(ngx_conf_t *cf, void *post, void *data) { - ngx_str_t *realm = data; - - size_t len; - u_char *basic, *p; +ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - if (ngx_strcmp(realm->data, "off") == 0) { - realm->len = 0; - realm->data = (u_char *) ""; + ngx_str_t *value = cf->args->elts; + ngx_http_auth_ldap_loc_conf_t *cnf = conf; + u_char *p; + if (ngx_strcmp(value[1].data, "off") == 0) { + ngx_str_set(&cnf->realm, ""); return NGX_CONF_OK; } - len = sizeof("Basic realm=\"") - 1 + realm->len + 1; - - basic = ngx_pcalloc(cf->pool, len); - if (basic == NULL) { + cnf->realm.len = sizeof("Basic realm=\"") - 1 + value[1].len + 1; + cnf->realm.data = ngx_pcalloc(cf->pool, cnf->realm.len); + if (cnf->realm.data == NULL) { return NGX_CONF_ERROR; } - p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1); - p = ngx_cpymem(p, realm->data, realm->len); + p = ngx_cpymem(cnf->realm.data, "Basic realm=\"", sizeof("Basic realm=\"") - 1); + p = ngx_cpymem(p, value[1].data, value[1].len); *p = '"'; - realm->len = len; - realm->data = basic; - return NGX_CONF_OK; } From a043544e2d1d295daae73f4abe4b6354f8ec18b3 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Tue, 23 Jul 2013 19:36:05 +0200 Subject: [PATCH 07/18] Resolve server names only once during configuration --- ngx_http_auth_ldap_module.c | 138 +++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 57 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 1e10071..bba83bb 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -47,17 +47,15 @@ typedef struct { } ngx_http_auth_ldap_server_t; typedef struct { - ngx_str_t realm; - ngx_array_t *servers; -} ngx_http_auth_ldap_loc_conf_t; + ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t */ +} ngx_http_auth_ldap_main_conf_t; typedef struct { - ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t */ - ngx_hash_t srv; -} ngx_http_auth_ldap_conf_t; - + ngx_str_t realm; + ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t* */ +} ngx_http_auth_ldap_loc_conf_t; -static void * ngx_http_auth_ldap_create_conf(ngx_conf_t *cf); +static void * ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf); static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); @@ -70,9 +68,9 @@ static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *); static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, ngx_http_auth_ldap_loc_conf_t *conf); static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm); -static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf, - ngx_http_auth_ldap_conf_t *mconf); +static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf); static char * ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char * ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_command_t ngx_http_auth_ldap_commands[] = { { @@ -94,9 +92,9 @@ static ngx_command_t ngx_http_auth_ldap_commands[] = { { ngx_string("auth_ldap_servers"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_ANY, - ngx_conf_set_str_array_slot, + ngx_http_auth_ldap_servers, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_ldap_loc_conf_t, servers), + 0, NULL }, ngx_null_command @@ -105,7 +103,7 @@ static ngx_command_t ngx_http_auth_ldap_commands[] = { static ngx_http_module_t ngx_http_auth_ldap_module_ctx = { NULL, /* preconfiguration */ ngx_http_auth_ldap_init, /* postconfiguration */ - ngx_http_auth_ldap_create_conf, /* create main configuration */ + ngx_http_auth_ldap_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ NULL, //ngx_http_auth_ldap_create_server_conf, /* create server configuration */ NULL, //ngx_http_auth_ldap_merge_server_conf, /* merge server configuration */ @@ -135,11 +133,11 @@ ngx_module_t ngx_http_auth_ldap_module = { static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - char *rv; - ngx_str_t *value, name; - ngx_conf_t save; - ngx_http_auth_ldap_server_t server, *s; - ngx_http_auth_ldap_conf_t *cnf = conf; + char *rv; + ngx_str_t *value, name; + ngx_conf_t save; + ngx_http_auth_ldap_server_t server, *s; + ngx_http_auth_ldap_main_conf_t *cnf = conf; value = cf->args->elts; @@ -189,7 +187,7 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) ngx_str_t *value; ngx_http_auth_ldap_server_t *server; - ngx_http_auth_ldap_conf_t *cnf = conf; + ngx_http_auth_ldap_main_conf_t *cnf = conf; // It should be safe to just use latest server from array server = ((ngx_http_auth_ldap_server_t*)cnf->servers->elts + (cnf->servers->nelts - 1)); @@ -246,6 +244,57 @@ ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return NGX_CONF_OK; } +/** + * Parse auth_ldap_servers directive + */ +static char * +ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { + + ngx_http_auth_ldap_loc_conf_t *cnf; + ngx_http_auth_ldap_main_conf_t *mconf; + ngx_http_auth_ldap_server_t *server, *s, **target; + ngx_str_t *value; + ngx_uint_t i, j; + + cnf = conf; + mconf = ngx_http_conf_get_module_main_conf(cf, ngx_http_auth_ldap_module); + + for (i = 1; i < cf->args->nelts; i++) { + value = &((ngx_str_t *) cf->args->elts)[i]; + server = NULL; + + for (j = 0; j < mconf->servers->nelts; j++) { + s = &((ngx_http_auth_ldap_server_t *) mconf->servers->elts)[j]; + if (s->alias.len == value->len && ngx_memcmp(s->alias.data, value->data, s->alias.len) == 0) { + server = s; + break; + } + } + + if (server == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP server \"%V\" is not defined!", value); + return NGX_CONF_ERROR; + } + + + if (cnf->servers == NGX_CONF_UNSET_PTR) { + cnf->servers = ngx_array_create(cf->pool, 4, sizeof(ngx_http_auth_ldap_server_t *)); + if (cnf->servers == NULL) { + return NGX_CONF_ERROR; + } + } + + target = (ngx_http_auth_ldap_server_t **) ngx_array_push(cnf->servers); + if (target == NULL) { + return NGX_CONF_ERROR; + } + + *target = server; + } + + return NGX_CONF_OK; +} + /** * Parse URL conf parameter */ @@ -391,11 +440,11 @@ ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *se * Create main config which will store ldap_servers array */ static void * -ngx_http_auth_ldap_create_conf(ngx_conf_t *cf) +ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf) { - ngx_http_auth_ldap_conf_t *conf; + ngx_http_auth_ldap_main_conf_t *conf; - conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_ldap_conf_t)); + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_ldap_main_conf_t)); if (conf == NULL) { return NULL; } @@ -442,39 +491,29 @@ static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) { ngx_http_auth_ldap_loc_conf_t *alcf; alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_ldap_module); - if (alcf->realm.len == 0) { return NGX_DECLINED; } - ngx_http_auth_ldap_conf_t *cnf; - - cnf = ngx_http_get_module_main_conf(r, ngx_http_auth_ldap_module); - rc = ngx_http_auth_basic_user(r); - if (rc == NGX_DECLINED) { return ngx_http_auth_ldap_set_realm(r, &alcf->realm); } - if (rc == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - return ngx_http_auth_ldap_authenticate(r, alcf, cnf); + return ngx_http_auth_ldap_authenticate(r, alcf); } /** * Read user credentials from request, set LDAP parameters and call authentication against required servers */ -static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf, - ngx_http_auth_ldap_conf_t *mconf) { +static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf) { - ngx_http_auth_ldap_server_t *server, *servers; - servers = mconf->servers->elts; + ngx_http_auth_ldap_server_t *server; int rc; - ngx_uint_t i, k; - ngx_str_t *alias; + ngx_uint_t i; int version = LDAP_VERSION3; int reqcert = LDAP_OPT_X_TLS_ALLOW; @@ -498,27 +537,12 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http ldap_err2string(rc)); } - // TODO: We might be using hash here, cause this loops is quite ugly, but it is simple and it works - int found; - for (k = 0; k < conf->servers->nelts; k++) { - alias = ((ngx_str_t*)conf->servers->elts + k); - found = 0; - for (i = 0; i < mconf->servers->nelts; i++) { - server = &servers[i]; - if (server->alias.len == alias->len && ngx_strncmp(server->alias.data, alias->data, server->alias.len) == 0) { - found = 1; - pass = ngx_http_auth_ldap_authenticate_against_server(r, server, conf); - if (pass == 1) { - return NGX_OK; - } else if (pass == NGX_HTTP_INTERNAL_SERVER_ERROR) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - } - } - - // If requested ldap server is not found, return 500 and write to log - if (found == 0) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: Server \"%s\" is not defined!", alias->data); + for (i = 0; i < conf->servers->nelts; i++) { + server = ((ngx_http_auth_ldap_server_t **) conf->servers->elts)[i]; + pass = ngx_http_auth_ldap_authenticate_against_server(r, server, conf); + if (pass == 1) { + return NGX_OK; + } else if (pass == NGX_HTTP_INTERNAL_SERVER_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } From bd4959918c7eeb0d1cb1799a6e387ed6e859d2f9 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Mon, 22 Jul 2013 16:05:29 +0200 Subject: [PATCH 08/18] Allow specifying the full user DN to avoid searching the LDAP The configuration can now contain a directive like this require valid_user cn=$remote_user,dc=example,dc=com which will result in the bind as the respective user to be the only command sent to the server, skipping the search request. --- ngx_http_auth_ldap_module.c | 236 +++++++++++++++++++----------------- 1 file changed, 127 insertions(+), 109 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index bba83bb..080af26 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -43,6 +43,7 @@ typedef struct { ngx_array_t *require_group; /* array of ngx_http_complex_value_t */ ngx_array_t *require_user; /* array of ngx_http_complex_value_t */ ngx_flag_t require_valid_user; + ngx_http_complex_value_t require_valid_user_dn; ngx_flag_t satisfy_all; } ngx_http_auth_ldap_server_t; @@ -373,14 +374,20 @@ static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) { ngx_str_t *value; - ngx_http_complex_value_t* rule = NULL; + ngx_http_complex_value_t* target = NULL; ngx_http_compile_complex_value_t ccv; value = cf->args->elts; if (ngx_strcmp(value[1].data, "valid_user") == 0) { server->require_valid_user = 1; - return NGX_CONF_OK; + if (cf->args->nelts < 3) { + return NGX_CONF_OK; + } + if (server->require_valid_user_dn.value.data != NULL) { + return "is duplicate"; + } + target = &server->require_valid_user_dn; } else if (ngx_strcmp(value[1].data, "user") == 0) { if (server->require_user == NULL) { server->require_user = ngx_array_create(cf->pool, 4, sizeof(ngx_http_complex_value_t)); @@ -388,7 +395,7 @@ ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *se return NGX_CONF_ERROR; } } - rule = ngx_array_push(server->require_user); + target = ngx_array_push(server->require_user); } else if (ngx_strcmp(value[1].data, "group") == 0) { if (server->require_group == NULL) { server->require_group = ngx_array_create(cf->pool, 4, sizeof(ngx_http_complex_value_t)); @@ -396,17 +403,17 @@ ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *se return NGX_CONF_ERROR; } } - rule = ngx_array_push(server->require_group); + target = ngx_array_push(server->require_group); } - if (rule == NULL) { + if (target == NULL) { return NGX_CONF_ERROR; } ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[2]; - ccv.complex_value = rule; + ccv.complex_value = target; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } @@ -560,7 +567,8 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request int rc; LDAP *ld; LDAPMessage *searchResult; - char *dn; + char* ldn = NULL; + ngx_str_t dn; u_char *p, *filter; ngx_http_complex_value_t *value; ngx_uint_t i; @@ -593,129 +601,139 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Bind successful", NULL); - /// Create filter for search users by uid - filter = ngx_pcalloc( - r->pool, - (ludpp->lud_filter != NULL ? ngx_strlen(ludpp->lud_filter) : ngx_strlen("(objectClass=*)")) + ngx_strlen("(&(=))") + ngx_strlen(ludpp->lud_attrs[0]) - + r->headers_in.user.len + 1); + if (server->require_valid_user_dn.value.data != NULL) { + // Construct user DN + if (ngx_http_complex_value(r, &server->require_valid_user_dn, &dn) != NGX_OK) { + ldap_unbind_s(ld); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } else { + /// Create filter for search users by uid + filter = ngx_pcalloc( + r->pool, + (ludpp->lud_filter != NULL ? ngx_strlen(ludpp->lud_filter) : ngx_strlen("(objectClass=*)")) + + ngx_strlen("(&(=))") + ngx_strlen(ludpp->lud_attrs[0]) + r->headers_in.user.len + 1); + + p = ngx_sprintf(filter, "(&%s(%s=%V))", + ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", + ludpp->lud_attrs[0], &r->headers_in.user); + *p = 0; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: filter %s", (const char*) filter); + + /// Search the directory + rc = ldap_search_ext_s(ld, ludpp->lud_dn, ludpp->lud_scope, (const char*) filter, NULL, 0, NULL, NULL, &timeOut, 0, + &searchResult); + + if (rc != LDAP_SUCCESS) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_search_ext_s: %d, %s", rc, ldap_err2string(rc)); + ldap_msgfree(searchResult); + ldap_unbind_s(ld); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } - p = ngx_sprintf(filter, "(&%s(%s=%V))", ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", ludpp->lud_attrs[0], &r->headers_in.user); - *p = 0; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: filter %s", (const char*) filter); + if (ldap_count_entries(ld, searchResult) > 0) { + ldn = ldap_get_dn(ld, searchResult); + } + ldap_msgfree(searchResult); - /// Search the directory - rc = ldap_search_ext_s(ld, ludpp->lud_dn, ludpp->lud_scope, (const char*) filter, NULL, 0, NULL, NULL, &timeOut, 0, - &searchResult); + if (!ldn) { + ldap_unbind_s(ld); + return 0; + } - if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_search_ext_s: %d, %s", rc, ldap_err2string(rc)); - ldap_msgfree(searchResult); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; + dn.data = (u_char*) ldn; + dn.len = ngx_strlen(ldn); } - if (ldap_count_entries(ld, searchResult) > 0) { - dn = ldap_get_dn(ld, searchResult); - if (dn != NULL) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: result DN %s", dn); - - /// Check require user - if (server->require_user != NULL) { - value = server->require_user->elts; - for (i = 0; i < server->require_user->nelts; i++) { - ngx_str_t val; - if (ngx_http_complex_value(r, &value[i], &val) != NGX_OK) { - ldap_memfree(dn); - ldap_msgfree(searchResult); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: compare with: %V", &val); - if (ngx_strncmp(val.data, dn, val.len) == 0) { - pass = 1; - if (server->satisfy_all == 0) { - break; - } - } else { - if (server->satisfy_all == 1) { - ldap_memfree(dn); - ldap_msgfree(searchResult); - ldap_unbind_s(ld); - return 0; - } - } - } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: result DN %V", &dn); + + /// Check require user + if (server->require_user != NULL) { + value = server->require_user->elts; + for (i = 0; i < server->require_user->nelts; i++) { + ngx_str_t val; + if (ngx_http_complex_value(r, &value[i], &val) != NGX_OK) { + ldap_memfree(ldn); + ldap_unbind_s(ld); + return NGX_HTTP_INTERNAL_SERVER_ERROR; } - /// Check require group - if (server->require_group != NULL) { - if (server->group_attribute_dn == 1) { - bvalue.bv_val = dn; - bvalue.bv_len = ngx_strlen(dn); - } else { - bvalue.bv_val = (char*) r->headers_in.user.data; - bvalue.bv_len = r->headers_in.user.len; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: compare with: %V", &val); + if (val.len == dn.len && ngx_memcmp(val.data, dn.data, val.len) == 0) { + pass = 1; + if (server->satisfy_all == 0) { + break; + } + } else { + if (server->satisfy_all == 1) { + ldap_memfree(ldn); + ldap_unbind_s(ld); + return 0; } + } + } + } - value = server->require_group->elts; + /// Check require group + if (server->require_group != NULL) { + if (server->group_attribute_dn == 1) { + bvalue.bv_val = (char*) dn.data; + bvalue.bv_len = dn.len; + } else { + bvalue.bv_val = (char*) r->headers_in.user.data; + bvalue.bv_len = r->headers_in.user.len; + } - for (i = 0; i < server->require_group->nelts; i++) { - ngx_str_t val; - if (ngx_http_complex_value(r, &value[i], &val) != NGX_OK) { - ldap_memfree(dn); - ldap_msgfree(searchResult); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + value = server->require_group->elts; + for (i = 0; i < server->require_group->nelts; i++) { + ngx_str_t val; + if (ngx_http_complex_value(r, &value[i], &val) != NGX_OK) { + ldap_memfree(ldn); + ldap_unbind_s(ld); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: group compare with: %V", &val); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: group compare with: %V", &val); + rc = ldap_compare_ext_s(ld, (const char*) val.data, (const char*) server->group_attribute.data, + &bvalue, NULL, NULL); - rc = ldap_compare_ext_s(ld, (const char*) val.data, (const char*) server->group_attribute.data, - &bvalue, NULL, NULL); + /*if (rc != LDAP_COMPARE_TRUE && rc != LDAP_COMPARE_FALSE && rc != LDAP_NO_SUCH_ATTRIBUTE) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_search_ext_s: %d, %s", rc, + ldap_err2string(rc)); + ldap_memfree(ldn); + ldap_unbind_s(ld); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + }*/ - /*if (rc != LDAP_COMPARE_TRUE && rc != LDAP_COMPARE_FALSE && rc != LDAP_NO_SUCH_ATTRIBUTE ) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_search_ext_s: %d, %s", rc, - ldap_err2string(rc)); - ldap_memfree(dn); - ldap_msgfree(searchResult); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - }*/ - - if (rc == LDAP_COMPARE_TRUE) { - pass = 1; - if (server->satisfy_all == 0) { - break; - } - } else { - if (server->satisfy_all == 1) { - pass = 0; - break; - } - } + if (rc == LDAP_COMPARE_TRUE) { + pass = 1; + if (server->satisfy_all == 0) { + break; } - } - - /// Check valid user - if ( pass != 0 || (server->require_valid_user == 1 && server->satisfy_all == 0 && pass == 0)) { - /// Bind user to the server - rc = ldap_simple_bind_s(ld, dn, (const char *) r->headers_in.passwd.data); - if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_simple_bind_s error: %d, %s", rc, - ldap_err2string(rc)); + } else { + if (server->satisfy_all == 1) { pass = 0; - } else { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: User bind successful", NULL); - if (server->require_valid_user == 1) pass = 1; + break; } } + } + } + /// Check valid user + if (pass != 0 || (server->require_valid_user == 1 && server->satisfy_all == 0 && pass == 0)) { + /// Bind user to the server + rc = ldap_simple_bind_s(ld, (const char *) dn.data, (const char *) r->headers_in.passwd.data); + if (rc != LDAP_SUCCESS) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_simple_bind_s error: %d, %s", rc, + ldap_err2string(rc)); + pass = 0; + } else { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: User bind successful", NULL); + if (server->require_valid_user == 1) pass = 1; } - ldap_memfree(dn); } - ldap_msgfree(searchResult); + ldap_memfree(ldn); ldap_unbind_s(ld); return pass; From 8b2dcc75220f93ae403ca356eb6e6c7f6fdd30fb Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Wed, 24 Jul 2013 16:47:43 +0200 Subject: [PATCH 09/18] Implement a per-process cache for increased performance A configured number of cached (server,username,password) entries is held for a configured amount of time to avoid repeated authentications for each of several requests quickly following each other. --- ngx_http_auth_ldap_module.c | 280 +++++++++++++++++++++++++++++++----- 1 file changed, 248 insertions(+), 32 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 080af26..c70aa31 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -27,6 +27,7 @@ #include #include #include +#include #include typedef struct { @@ -49,6 +50,9 @@ typedef struct { typedef struct { ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t */ + ngx_flag_t cache_enabled; + ngx_msec_t cache_expiration_time; + size_t cache_size; } ngx_http_auth_ldap_main_conf_t; typedef struct { @@ -56,7 +60,29 @@ typedef struct { ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t* */ } ngx_http_auth_ldap_loc_conf_t; +typedef struct { + uint32_t small_hash; /* murmur2 hash of username ^ &server */ + uint32_t outcome; /* 0 = authentication failed, 1 = succeeded */ + ngx_msec_t time; /* ngx_current_msec when created */ + u_char big_hash[16]; /* md5 hash of (username, server, password) */ +} ngx_http_auth_ldap_cache_elt_t; + +typedef struct { + ngx_http_auth_ldap_cache_elt_t *buckets; + ngx_uint_t num_buckets; + ngx_uint_t elts_per_bucket; + ngx_msec_t expiration_time; +} ngx_http_auth_ldap_cache_t; + +typedef struct { + ngx_http_auth_ldap_cache_elt_t *cache_bucket; + u_char cache_big_hash[16]; + uint32_t cache_small_hash; +} ngx_http_auth_ldap_ctx_t; + static void * ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf); +static char * ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent); +static ngx_int_t ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle); static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); @@ -69,10 +95,13 @@ static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *); static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, ngx_http_auth_ldap_loc_conf_t *conf); static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm); -static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf); +static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, + ngx_http_auth_ldap_loc_conf_t *conf); static char * ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +ngx_http_auth_ldap_cache_t ngx_http_auth_ldap_cache; + static ngx_command_t ngx_http_auth_ldap_commands[] = { { ngx_string("ldap_server"), @@ -82,6 +111,30 @@ static ngx_command_t ngx_http_auth_ldap_commands[] = { 0, NULL }, + { + ngx_string("auth_ldap_cache_enabled"), + NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_auth_ldap_main_conf_t, cache_enabled), + NULL + }, + { + ngx_string("auth_ldap_cache_expiration_time"), + NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_auth_ldap_main_conf_t, cache_expiration_time), + NULL + }, + { + ngx_string("auth_ldap_cache_size"), + NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_auth_ldap_main_conf_t, cache_size), + NULL + }, { ngx_string("auth_ldap"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, @@ -102,29 +155,29 @@ static ngx_command_t ngx_http_auth_ldap_commands[] = { }; static ngx_http_module_t ngx_http_auth_ldap_module_ctx = { - NULL, /* preconfiguration */ - ngx_http_auth_ldap_init, /* postconfiguration */ + NULL, /* preconfiguration */ + ngx_http_auth_ldap_init, /* postconfiguration */ ngx_http_auth_ldap_create_main_conf, /* create main configuration */ - NULL, /* init main configuration */ - NULL, //ngx_http_auth_ldap_create_server_conf, /* create server configuration */ - NULL, //ngx_http_auth_ldap_merge_server_conf, /* merge server configuration */ - ngx_http_auth_ldap_create_loc_conf, /* create location configuration */ - ngx_http_auth_ldap_merge_loc_conf /* merge location configuration */ + ngx_http_auth_ldap_init_main_conf, /* init main configuration */ + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + ngx_http_auth_ldap_create_loc_conf, /* create location configuration */ + ngx_http_auth_ldap_merge_loc_conf /* merge location configuration */ }; ngx_module_t ngx_http_auth_ldap_module = { NGX_MODULE_V1, - &ngx_http_auth_ldap_module_ctx, /* module context */ - ngx_http_auth_ldap_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - NULL, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ - NGX_MODULE_V1_PADDING /**/ + &ngx_http_auth_ldap_module_ctx, /* module context */ + ngx_http_auth_ldap_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + ngx_http_auth_ldap_init_worker, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING }; @@ -456,9 +509,144 @@ ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf) return NULL; } + conf->cache_enabled = NGX_CONF_UNSET; + conf->cache_expiration_time = NGX_CONF_UNSET_MSEC; + conf->cache_size = NGX_CONF_UNSET_SIZE; + return conf; } +static char * +ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent) +{ + ngx_http_auth_ldap_main_conf_t *conf = parent; + + if (conf->cache_enabled == NGX_CONF_UNSET) { + conf->cache_enabled = 0; + } + if (conf->cache_enabled == 0) { + return NGX_CONF_OK; + } + + if (conf->cache_size == NGX_CONF_UNSET_SIZE) { + conf->cache_size = 100; + } + if (conf->cache_size < 100) { + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "auth_ldap_cache_size cannot be smaller than 100 entries."); + return NGX_CONF_ERROR; + } + + if (conf->cache_expiration_time == NGX_CONF_UNSET_MSEC) { + conf->cache_expiration_time = 10000; + } + if (conf->cache_expiration_time < 1000) { + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "auth_ldap_cache_expiration_time cannot be smaller than 1000 ms."); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + +static ngx_int_t +ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle) +{ + ngx_http_auth_ldap_main_conf_t *conf; + ngx_uint_t want, count, i; + ngx_http_auth_ldap_cache_t *cache; + static const uint16_t primes[] = { + 13, 53, 101, 151, 199, 263, 317, 383, 443, 503, + 577, 641, 701, 769, 839, 911, 983, 1049, 1109 + }; + + + if (ngx_process != NGX_PROCESS_SINGLE && ngx_process != NGX_PROCESS_WORKER) { + return NGX_OK; + } + + + conf = (ngx_http_auth_ldap_main_conf_t *) ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); + if (conf == NULL || !conf->cache_enabled) { + return NGX_OK; + } + + want = (conf->cache_size + 7) / 8; + for (i = 0; i < sizeof(primes)/sizeof(primes[0]); i++) { + count = primes[i]; + if (count >= want) { + break; + } + } + + cache = &ngx_http_auth_ldap_cache; + cache->expiration_time = conf->cache_expiration_time; + cache->num_buckets = count; + cache->elts_per_bucket = 8; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "http_auth_ldap: Allocating %ud bytes of LDAP cache.", + cache->num_buckets * cache->elts_per_bucket * sizeof(ngx_http_auth_ldap_cache_elt_t)); + + cache->buckets = (ngx_http_auth_ldap_cache_elt_t *) ngx_calloc(count * 8 * sizeof(ngx_http_auth_ldap_cache_elt_t), cycle->log); + if (cache->buckets == NULL) { + ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "http_auth_ldap: Unable to allocate memory for LDAP cache."); + return NGX_ERROR; + } + + return NGX_OK; +} + +static ngx_int_t +ngx_http_auth_ldap_check_cache(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, + ngx_http_auth_ldap_cache_t *cache, ngx_http_auth_ldap_server_t *server) +{ + ngx_http_auth_ldap_cache_elt_t *elt; + ngx_md5_t md5ctx; + ngx_msec_t time_limit; + ngx_uint_t i; + + ctx->cache_small_hash = ngx_murmur_hash2(r->headers_in.user.data, r->headers_in.user.len) ^ (uint32_t)(ngx_uint_t)server; + + ngx_md5_init(&md5ctx); + ngx_md5_update(&md5ctx, r->headers_in.user.data, r->headers_in.user.len); + ngx_md5_update(&md5ctx, server, sizeof(*server)); + ngx_md5_update(&md5ctx, r->headers_in.passwd.data, r->headers_in.passwd.len); + ngx_md5_final(ctx->cache_big_hash, &md5ctx); + + ctx->cache_bucket = &cache->buckets[ctx->cache_small_hash % cache->num_buckets]; + + elt = ctx->cache_bucket; + time_limit = ngx_current_msec - cache->expiration_time; + for (i = 0; i < cache->elts_per_bucket; i++, elt++) { + if (elt->small_hash == ctx->cache_small_hash && + elt->time > time_limit && + memcmp(elt->big_hash, ctx->cache_big_hash, 16) == 0) { + return elt->outcome; + } + } + + return -1; +} + +static void +ngx_http_auth_ldap_update_cache(ngx_http_auth_ldap_ctx_t *ctx, + ngx_http_auth_ldap_cache_t *cache, ngx_flag_t outcome) +{ + ngx_http_auth_ldap_cache_elt_t *elt, *oldest_elt; + ngx_uint_t i; + + elt = ctx->cache_bucket; + oldest_elt = elt; + for (i = 1; i < cache->elts_per_bucket; i++, elt++) { + if (elt->time < oldest_elt->time) { + oldest_elt = elt; + } + } + + oldest_elt->time = ngx_current_msec; + oldest_elt->outcome = outcome; + oldest_elt->small_hash = ctx->cache_small_hash; + ngx_memcpy(oldest_elt->big_hash, ctx->cache_big_hash, 16); +} + /** * Create location conf */ @@ -494,29 +682,40 @@ ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { * LDAP Authentication handler */ static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) { - int rc; ngx_http_auth_ldap_loc_conf_t *alcf; + ngx_http_auth_ldap_ctx_t *ctx; + int rc; alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_ldap_module); if (alcf->realm.len == 0) { return NGX_DECLINED; } - rc = ngx_http_auth_basic_user(r); - if (rc == NGX_DECLINED) { - return ngx_http_auth_ldap_set_realm(r, &alcf->realm); - } - if (rc == NGX_ERROR) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + ctx = ngx_http_get_module_ctx(r, ngx_http_auth_ldap_module); + if (ctx == NULL) { + rc = ngx_http_auth_basic_user(r); + if (rc == NGX_DECLINED) { + return ngx_http_auth_ldap_set_realm(r, &alcf->realm); + } + if (rc == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_ldap_ctx_t)); + if (ctx == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + ngx_http_set_ctx(r, ctx, ngx_http_auth_ldap_module); } - return ngx_http_auth_ldap_authenticate(r, alcf); + return ngx_http_auth_ldap_authenticate(r, ctx, alcf); } /** * Read user credentials from request, set LDAP parameters and call authentication against required servers */ -static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf) { +static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, + ngx_http_auth_ldap_loc_conf_t *conf) { ngx_http_auth_ldap_server_t *server; int rc; @@ -525,7 +724,6 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http int version = LDAP_VERSION3; int reqcert = LDAP_OPT_X_TLS_ALLOW; struct timeval timeOut = { 10, 0 }; - ngx_flag_t pass = NGX_CONF_UNSET; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP username: %V", &r->headers_in.user); @@ -546,10 +744,28 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http for (i = 0; i < conf->servers->nelts; i++) { server = ((ngx_http_auth_ldap_server_t **) conf->servers->elts)[i]; - pass = ngx_http_auth_ldap_authenticate_against_server(r, server, conf); - if (pass == 1) { + + if (ngx_http_auth_ldap_cache.buckets != NULL) { + rc = ngx_http_auth_ldap_check_cache(r, ctx, &ngx_http_auth_ldap_cache, server); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Cached outcome %d", rc); + if (rc == 0) { + continue; + } + if (rc == 1) { + return NGX_OK; + } + } + + rc = ngx_http_auth_ldap_authenticate_against_server(r, server, conf); + + if ((rc == 0 || rc == 1) && ngx_http_auth_ldap_cache.buckets != NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Caching outcome %d", rc); + ngx_http_auth_ldap_update_cache(ctx, &ngx_http_auth_ldap_cache, rc); + } + + if (rc == 1) { return NGX_OK; - } else if (pass == NGX_HTTP_INTERNAL_SERVER_ERROR) { + } else if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } From c8d76769d4650f0c653441790163160d5ab06c33 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Fri, 30 Aug 2013 04:11:05 +0200 Subject: [PATCH 10/18] Add contribution notice to the licenses --- LICENSE | 1 + ngx_http_auth_ldap_module.c | 1 + 2 files changed, 2 insertions(+) diff --git a/LICENSE b/LICENSE index 2288ef8..46319ca 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ /** * Copyright (C) 2011-2013 Valery Komarov + * Copyright (C) 2013 Jiri Hruska * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index c70aa31..6b89051 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -1,5 +1,6 @@ /** * Copyright (C) 2011-2013 Valery Komarov + * Copyright (C) 2013 Jiri Hruska * All rights reserved. * * Redistribution and use in source and binary forms, with or without From 1b6ee8e0c1375c5f3b5a3d0025f005579b4d34e9 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Fri, 30 Aug 2013 02:27:02 +0200 Subject: [PATCH 11/18] Clean function order a bit before further big changes --- ngx_http_auth_ldap_module.c | 183 ++++++++++++++++++++---------------- 1 file changed, 101 insertions(+), 82 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 6b89051..6697511 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -81,25 +81,25 @@ typedef struct { uint32_t cache_small_hash; } ngx_http_auth_ldap_ctx_t; -static void * ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf); -static char * ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent); -static ngx_int_t ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle); static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char * ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); +static char * ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char * ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); static char * ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server); -static char * ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); -static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r); -static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); +static void * ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf); +static char * ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent); static void * ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *); static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *); -static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, - ngx_http_auth_ldap_loc_conf_t *conf); -static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm); +static ngx_int_t ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle); +static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); +static ngx_int_t ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle); +static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, ngx_http_auth_ldap_loc_conf_t *conf); -static char * ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char * ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, + ngx_http_auth_ldap_loc_conf_t *conf); ngx_http_auth_ldap_cache_t ngx_http_auth_ldap_cache; @@ -182,6 +182,8 @@ ngx_module_t ngx_http_auth_ldap_module = { }; +/*** Configuration and initialization ***/ + /** * Reads ldap_server block and sets ngx_http_auth_ldap_ldap_server as a handler of each conf value */ @@ -548,8 +550,77 @@ ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent) return NGX_CONF_OK; } +/** + * Create location conf + */ +static void * +ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *cf) { + ngx_http_auth_ldap_loc_conf_t *conf; + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_ldap_loc_conf_t)); + if (conf == NULL) { + return NULL; + } + conf->servers = NGX_CONF_UNSET_PTR; + + return conf; +} + +/** + * Merge location conf + */ +static char * +ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { + ngx_http_auth_ldap_loc_conf_t *prev = parent; + ngx_http_auth_ldap_loc_conf_t *conf = child; + + if (conf->realm.data == NULL) { + conf->realm = prev->realm; + } + ngx_conf_merge_ptr_value(conf->servers, prev->servers, NULL); + + return NGX_CONF_OK; +} + static ngx_int_t ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle) +{ + ngx_int_t rc; + + if (ngx_process != NGX_PROCESS_SINGLE && ngx_process != NGX_PROCESS_WORKER) { + return NGX_OK; + } + + rc = ngx_http_auth_ldap_init_cache(cycle); + if (rc != NGX_OK) { + return rc; + } + + return NGX_OK; +} + +/** + * Init module and add ldap auth handler to NGX_HTTP_ACCESS_PHASE + */ +static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf) { + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_auth_ldap_handler; + return NGX_OK; +} + + +/*** Authentication cache ***/ + +static ngx_int_t +ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle) { ngx_http_auth_ldap_main_conf_t *conf; ngx_uint_t want, count, i; @@ -559,12 +630,6 @@ ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle) 577, 641, 701, 769, 839, 911, 983, 1049, 1109 }; - - if (ngx_process != NGX_PROCESS_SINGLE && ngx_process != NGX_PROCESS_WORKER) { - return NGX_OK; - } - - conf = (ngx_http_auth_ldap_main_conf_t *) ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); if (conf == NULL || !conf->cache_enabled) { return NGX_OK; @@ -648,35 +713,24 @@ ngx_http_auth_ldap_update_cache(ngx_http_auth_ldap_ctx_t *ctx, ngx_memcpy(oldest_elt->big_hash, ctx->cache_big_hash, 16); } -/** - * Create location conf - */ -static void * -ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *cf) { - ngx_http_auth_ldap_loc_conf_t *conf; - conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_ldap_loc_conf_t)); - if (conf == NULL) { - return NULL; - } - conf->servers = NGX_CONF_UNSET_PTR; - return conf; -} +/*** Per-request authentication processing ***/ /** - * Merge location conf + * Respond with "403 Forbidden" and add correct headers */ -static char * -ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { - ngx_http_auth_ldap_loc_conf_t *prev = parent; - ngx_http_auth_ldap_loc_conf_t *conf = child; - - if (conf->realm.data == NULL) { - conf->realm = prev->realm; +static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm) { + r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); + if (r->headers_out.www_authenticate == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_conf_merge_ptr_value(conf->servers, prev->servers, NULL); - return NGX_CONF_OK; + r->headers_out.www_authenticate->hash = 1; + r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1; + r->headers_out.www_authenticate->key.data = (u_char *) "WWW-Authenticate"; + r->headers_out.www_authenticate->value = *realm; + + return NGX_HTTP_UNAUTHORIZED; } /** @@ -702,6 +756,13 @@ static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP username: %V", &r->headers_in.user); + if (r->headers_in.passwd.len == 0) + { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP password is empty"); + return ngx_http_auth_ldap_set_realm(r, &alcf->realm); + } + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_ldap_ctx_t)); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -726,13 +787,6 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http int reqcert = LDAP_OPT_X_TLS_ALLOW; struct timeval timeOut = { 10, 0 }; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP username: %V", &r->headers_in.user); - - if (r->headers_in.passwd.len == 0) - { - return ngx_http_auth_ldap_set_realm(r, &conf->realm); - } - /// Set LDAP version to 3 and set connection timeout. ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &version); ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &timeOut); @@ -955,38 +1009,3 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request return pass; } - -/** - * Respond with forbidden and add correct headers - */ -static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm) { - r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); - if (r->headers_out.www_authenticate == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - r->headers_out.www_authenticate->hash = 1; - r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1; - r->headers_out.www_authenticate->key.data = (u_char *) "WWW-Authenticate"; - r->headers_out.www_authenticate->value = *realm; - - return NGX_HTTP_UNAUTHORIZED; -} - -/** - * Init module and add ldap auth handler to NGX_HTTP_ACCESS_PHASE - */ -static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf) { - ngx_http_handler_pt *h; - ngx_http_core_main_conf_t *cmcf; - - cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); - - h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); - if (h == NULL) { - return NGX_ERROR; - } - - *h = ngx_http_auth_ldap_handler; - return NGX_OK; -} From 16c3452ec03a7ea138863e5d8516c4f14d3dc69d Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Fri, 30 Aug 2013 02:36:52 +0200 Subject: [PATCH 12/18] Minor code formatting cleanup --- ngx_http_auth_ldap_module.c | 116 ++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 53 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 6697511..bdc7db8 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -62,10 +62,10 @@ typedef struct { } ngx_http_auth_ldap_loc_conf_t; typedef struct { - uint32_t small_hash; /* murmur2 hash of username ^ &server */ - uint32_t outcome; /* 0 = authentication failed, 1 = succeeded */ - ngx_msec_t time; /* ngx_current_msec when created */ - u_char big_hash[16]; /* md5 hash of (username, server, password) */ + uint32_t small_hash; /* murmur2 hash of username ^ &server */ + uint32_t outcome; /* 0 = authentication failed, 1 = succeeded */ + ngx_msec_t time; /* ngx_current_msec when created */ + u_char big_hash[16]; /* md5 hash of (username, server, password) */ } ngx_http_auth_ldap_cache_elt_t; typedef struct { @@ -240,31 +240,30 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c static char * ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - char *rv; - ngx_str_t *value; - - ngx_http_auth_ldap_server_t *server; + char *rv; + ngx_str_t *value; + ngx_http_auth_ldap_server_t *server; ngx_http_auth_ldap_main_conf_t *cnf = conf; - // It should be safe to just use latest server from array - server = ((ngx_http_auth_ldap_server_t*)cnf->servers->elts + (cnf->servers->nelts - 1)); + /* It should be safe to just use latest server from array */ + server = ((ngx_http_auth_ldap_server_t *) cnf->servers->elts + (cnf->servers->nelts - 1)); value = cf->args->elts; - // TODO: Add more validation + /* TODO: Add more validation */ if (ngx_strcmp(value[0].data, "url") == 0) { return ngx_http_auth_ldap_parse_url(cf, server); - } else if(ngx_strcmp(value[0].data, "binddn") == 0) { + } else if (ngx_strcmp(value[0].data, "binddn") == 0) { server->bind_dn = value[1]; - } else if(ngx_strcmp(value[0].data, "binddn_passwd") == 0) { + } else if (ngx_strcmp(value[0].data, "binddn_passwd") == 0) { server->bind_dn_passwd = value[1]; - } else if(ngx_strcmp(value[0].data, "group_attribute") == 0) { + } else if (ngx_strcmp(value[0].data, "group_attribute") == 0) { server->group_attribute = value[1]; - } else if(ngx_strcmp(value[0].data, "group_attribute_is_dn") == 0 && ngx_strcmp(value[1].data, "on") == 0) { + } else if (ngx_strcmp(value[0].data, "group_attribute_is_dn") == 0 && ngx_strcmp(value[1].data, "on") == 0) { server->group_attribute_dn = 1; - } else if(ngx_strcmp(value[0].data, "require") == 0) { + } else if (ngx_strcmp(value[0].data, "require") == 0) { return ngx_http_auth_ldap_parse_require(cf, server); - } else if(ngx_strcmp(value[0].data, "satisfy") == 0) { + } else if (ngx_strcmp(value[0].data, "satisfy") == 0) { return ngx_http_auth_ldap_parse_satisfy(cf, server); } @@ -277,8 +276,8 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) * Parse auth_ldap directive */ static char * -ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - +ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ ngx_str_t *value = cf->args->elts; ngx_http_auth_ldap_loc_conf_t *cnf = conf; u_char *p; @@ -305,8 +304,8 @@ ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { * Parse auth_ldap_servers directive */ static char * -ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - +ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ ngx_http_auth_ldap_loc_conf_t *cnf; ngx_http_auth_ldap_main_conf_t *mconf; ngx_http_auth_ldap_server_t *server, *s, **target; @@ -356,14 +355,14 @@ ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { * Parse URL conf parameter */ static char * -ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) { +ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) +{ ngx_str_t *value; u_char *p; - value = cf->args->elts; - server->url = *value; + value = cf->args->elts; - int rc = ldap_url_parse((const char*) value[1].data, &server->ludpp); + int rc = ldap_url_parse((const char *) value[1].data, &server->ludpp); if (rc != LDAP_SUCCESS) { switch (rc) { case LDAP_URL_ERR_MEM: @@ -414,7 +413,7 @@ ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server return NGX_CONF_ERROR; } - server->url.len = ngx_strlen(server->ludpp->lud_scheme) + ngx_strlen(server->ludpp->lud_host) + 11; // 11 = len("://:/") + len("65535") + len("\0") + server->url.len = ngx_strlen(server->ludpp->lud_scheme) + ngx_strlen(server->ludpp->lud_host) + 11; /* 11 = len("://:/") + len("65535") + len("\0") */ server->url.data = ngx_pcalloc(cf->pool, server->url.len); p = ngx_sprintf(server->url.data, "%s://%s:%d/", (const char*) server->ludpp->lud_scheme, (const char*) server->ludpp->lud_host, server->ludpp->lud_port); @@ -427,8 +426,8 @@ ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server * Parse "require" conf parameter */ static char * -ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) { - +ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) +{ ngx_str_t *value; ngx_http_complex_value_t* target = NULL; ngx_http_compile_complex_value_t ccv; @@ -481,7 +480,8 @@ ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *se * Parse "satisfy" conf parameter */ static char * -ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) { +ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server) +{ ngx_str_t *value; value = cf->args->elts; @@ -495,7 +495,7 @@ ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *se return NGX_CONF_OK; } - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Incorrect value for auth_ldap_satisfy "); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Incorrect value for auth_ldap_satisfy"); return NGX_CONF_ERROR; } @@ -554,7 +554,8 @@ ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent) * Create location conf */ static void * -ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *cf) { +ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *cf) +{ ngx_http_auth_ldap_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_ldap_loc_conf_t)); if (conf == NULL) { @@ -569,7 +570,8 @@ ngx_http_auth_ldap_create_loc_conf(ngx_conf_t *cf) { * Merge location conf */ static char * -ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { +ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) +{ ngx_http_auth_ldap_loc_conf_t *prev = parent; ngx_http_auth_ldap_loc_conf_t *conf = child; @@ -601,7 +603,9 @@ ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle) /** * Init module and add ldap auth handler to NGX_HTTP_ACCESS_PHASE */ -static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf) { +static ngx_int_t +ngx_http_auth_ldap_init(ngx_conf_t *cf) +{ ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; @@ -669,7 +673,7 @@ ngx_http_auth_ldap_check_cache(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t * ngx_msec_t time_limit; ngx_uint_t i; - ctx->cache_small_hash = ngx_murmur_hash2(r->headers_in.user.data, r->headers_in.user.len) ^ (uint32_t)(ngx_uint_t)server; + ctx->cache_small_hash = ngx_murmur_hash2(r->headers_in.user.data, r->headers_in.user.len) ^ (uint32_t) (ngx_uint_t) server; ngx_md5_init(&md5ctx); ngx_md5_update(&md5ctx, r->headers_in.user.data, r->headers_in.user.len); @@ -719,7 +723,9 @@ ngx_http_auth_ldap_update_cache(ngx_http_auth_ldap_ctx_t *ctx, /** * Respond with "403 Forbidden" and add correct headers */ -static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm) { +static ngx_int_t +ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm) +{ r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); if (r->headers_out.www_authenticate == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -736,7 +742,9 @@ static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t * /** * LDAP Authentication handler */ -static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) { +static ngx_int_t +ngx_http_auth_ldap_handler(ngx_http_request_t *r) +{ ngx_http_auth_ldap_loc_conf_t *alcf; ngx_http_auth_ldap_ctx_t *ctx; int rc; @@ -756,7 +764,8 @@ static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP username: %V", &r->headers_in.user); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP username: %V", + &r->headers_in.user); if (r->headers_in.passwd.len == 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP password is empty"); @@ -776,9 +785,10 @@ static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) { /** * Read user credentials from request, set LDAP parameters and call authentication against required servers */ -static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, - ngx_http_auth_ldap_loc_conf_t *conf) { - +static ngx_int_t +ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, + ngx_http_auth_ldap_loc_conf_t *conf) +{ ngx_http_auth_ldap_server_t *server; int rc; ngx_uint_t i; @@ -787,7 +797,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http int reqcert = LDAP_OPT_X_TLS_ALLOW; struct timeval timeOut = { 10, 0 }; - /// Set LDAP version to 3 and set connection timeout. + /* Set LDAP version to 3 and set connection timeout. */ ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &version); ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &timeOut); @@ -832,8 +842,8 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http * Actual authentication against LDAP server */ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, - ngx_http_auth_ldap_loc_conf_t *conf) { - + ngx_http_auth_ldap_loc_conf_t *conf) +{ LDAPURLDesc *ludpp = server->ludpp; int rc; LDAP *ld; @@ -861,25 +871,25 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Session initialized", NULL); - /// Bind to the server + /* Bind to the server */ rc = ldap_simple_bind_s(ld, (const char *) server->bind_dn.data, (const char *) server->bind_dn_passwd.data); if (rc != LDAP_SUCCESS) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP [%s]: ldap_simple_bind_s error: %d, %s", server->url.data, rc, ldap_err2string(rc)); ldap_unbind_s(ld); - // Do not throw 500 in case connection failure, multiple servers might be used for failover scenario + /* Do not throw 500 in case connection failure, multiple servers might be used for failover scenario */ return 0; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Bind successful", NULL); if (server->require_valid_user_dn.value.data != NULL) { - // Construct user DN + /* Construct user DN */ if (ngx_http_complex_value(r, &server->require_valid_user_dn, &dn) != NGX_OK) { ldap_unbind_s(ld); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } else { - /// Create filter for search users by uid + /* Create filter for search users by uid */ filter = ngx_pcalloc( r->pool, (ludpp->lud_filter != NULL ? ngx_strlen(ludpp->lud_filter) : ngx_strlen("(objectClass=*)")) + @@ -891,7 +901,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request *p = 0; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: filter %s", (const char*) filter); - /// Search the directory + /* Search the directory */ rc = ldap_search_ext_s(ld, ludpp->lud_dn, ludpp->lud_scope, (const char*) filter, NULL, 0, NULL, NULL, &timeOut, 0, &searchResult); @@ -918,7 +928,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: result DN %V", &dn); - /// Check require user + /* Check require user */ if (server->require_user != NULL) { value = server->require_user->elts; for (i = 0; i < server->require_user->nelts; i++) { @@ -945,7 +955,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request } } - /// Check require group + /* Check require group */ if (server->require_group != NULL) { if (server->group_attribute_dn == 1) { bvalue.bv_val = (char*) dn.data; @@ -965,7 +975,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: group compare with: %V", &val); - rc = ldap_compare_ext_s(ld, (const char*) val.data, (const char*) server->group_attribute.data, + rc = ldap_compare_ext_s(ld, (const char *) val.data, (const char *) server->group_attribute.data, &bvalue, NULL, NULL); /*if (rc != LDAP_COMPARE_TRUE && rc != LDAP_COMPARE_FALSE && rc != LDAP_NO_SUCH_ATTRIBUTE) { @@ -990,9 +1000,9 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request } } - /// Check valid user + /* Check valid user */ if (pass != 0 || (server->require_valid_user == 1 && server->satisfy_all == 0 && pass == 0)) { - /// Bind user to the server + /* Bind user to the server */ rc = ldap_simple_bind_s(ld, (const char *) dn.data, (const char *) r->headers_in.passwd.data); if (rc != LDAP_SUCCESS) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_simple_bind_s error: %d, %s", rc, From 7acf3b4a98d2a797e75041b04c81e4a552f2b2ff Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Fri, 30 Aug 2013 02:44:05 +0200 Subject: [PATCH 13/18] Unification and small cleanup of log messages Most importantly, all messages start with "http_auth_ldap: " now. --- ngx_http_auth_ldap_module.c | 71 +++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index bdc7db8..a750f66 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -201,7 +201,7 @@ ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *c name = value[1]; if (ngx_strlen(name.data) == 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Error: no name of ldap server specified"); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Missing server name in ldap_server"); return NGX_CONF_ERROR; } @@ -328,7 +328,7 @@ ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } if (server == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP server \"%V\" is not defined!", value); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Server \"%V\" has not been defined", value); return NGX_CONF_ERROR; } @@ -366,50 +366,50 @@ ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server if (rc != LDAP_SUCCESS) { switch (rc) { case LDAP_URL_ERR_MEM: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Cannot allocate memory space."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Cannot allocate memory space."); break; case LDAP_URL_ERR_PARAM: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid parameter."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid parameter."); break; case LDAP_URL_ERR_BADSCHEME: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: URL doesnt begin with \"ldap[s]://\"."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: URL doesnt begin with \"ldap[s]://\"."); break; case LDAP_URL_ERR_BADENCLOSURE: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: URL is missing trailing \">\"."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: URL is missing trailing \">\"."); break; case LDAP_URL_ERR_BADURL: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid URL."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid URL."); break; case LDAP_URL_ERR_BADHOST: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Host port is invalid."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Host port is invalid."); break; case LDAP_URL_ERR_BADATTRS: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid or missing attributes."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid or missing attributes."); break; case LDAP_URL_ERR_BADSCOPE: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid or missing scope string."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid or missing scope string."); break; case LDAP_URL_ERR_BADFILTER: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid or missing filter."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid or missing filter."); break; case LDAP_URL_ERR_BADEXTS: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid or missing extensions."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Invalid or missing extensions."); break; } return NGX_CONF_ERROR; } if (server->ludpp->lud_attrs == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: No attrs in auth_ldap_url."); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: No user attribute specified in auth_ldap_url."); return NGX_CONF_ERROR; } @@ -495,7 +495,7 @@ ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *se return NGX_CONF_OK; } - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Incorrect value for auth_ldap_satisfy"); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Incorrect value for auth_ldap_satisfy"); return NGX_CONF_ERROR; } @@ -535,7 +535,7 @@ ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent) conf->cache_size = 100; } if (conf->cache_size < 100) { - ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "auth_ldap_cache_size cannot be smaller than 100 entries."); + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "http_auth_ldap: auth_ldap_cache_size cannot be smaller than 100 entries."); return NGX_CONF_ERROR; } @@ -543,7 +543,7 @@ ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent) conf->cache_expiration_time = 10000; } if (conf->cache_expiration_time < 1000) { - ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "auth_ldap_cache_expiration_time cannot be smaller than 1000 ms."); + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "http_auth_ldap: auth_ldap_cache_expiration_time cannot be smaller than 1000 ms."); return NGX_CONF_ERROR; } @@ -764,11 +764,11 @@ ngx_http_auth_ldap_handler(ngx_http_request_t *r) return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP username: %V", + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Username is \"%V\"", &r->headers_in.user); if (r->headers_in.passwd.len == 0) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP password is empty"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Password is empty"); return ngx_http_auth_ldap_set_realm(r, &alcf->realm); } @@ -803,7 +803,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &reqcert); if (rc != LDAP_OPT_SUCCESS) { - ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "LDAP: unable to set require cert option: %s", + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "http_auth_ldap: Unable to set require cert option: %s", ldap_err2string(rc)); } @@ -812,7 +812,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t if (ngx_http_auth_ldap_cache.buckets != NULL) { rc = ngx_http_auth_ldap_check_cache(r, ctx, &ngx_http_auth_ldap_cache, server); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Cached outcome %d", rc); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Using cached outcome %d", rc); if (rc == 0) { continue; } @@ -824,7 +824,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t rc = ngx_http_auth_ldap_authenticate_against_server(r, server, conf); if ((rc == 0 || rc == 1) && ngx_http_auth_ldap_cache.buckets != NULL) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Caching outcome %d", rc); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Caching outcome %d", rc); ngx_http_auth_ldap_update_cache(ctx, &ngx_http_auth_ldap_cache, rc); } @@ -861,26 +861,26 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: URL: %s", server->url.data); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: URL is \"%s\"", server->url.data); rc = ldap_initialize(&ld, (const char*) server->url.data); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: Session initializing failed: %d, %s, (%s)", rc, + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: Session initializing failed: %d, %s, (%s)", rc, ldap_err2string(rc), (const char*) server->url.data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Session initialized", NULL); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Session initialized", NULL); /* Bind to the server */ rc = ldap_simple_bind_s(ld, (const char *) server->bind_dn.data, (const char *) server->bind_dn_passwd.data); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP [%s]: ldap_simple_bind_s error: %d, %s", server->url.data, rc, - ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_simple_bind_s(%s) error: %d, %s", + server->url.data, rc, ldap_err2string(rc)); ldap_unbind_s(ld); /* Do not throw 500 in case connection failure, multiple servers might be used for failover scenario */ return 0; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Bind successful", NULL); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Bind successful", NULL); if (server->require_valid_user_dn.value.data != NULL) { /* Construct user DN */ @@ -899,14 +899,15 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", ludpp->lud_attrs[0], &r->headers_in.user); *p = 0; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: filter %s", (const char*) filter); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Search filter is \"%s\"", + (const char*) filter); /* Search the directory */ rc = ldap_search_ext_s(ld, ludpp->lud_dn, ludpp->lud_scope, (const char*) filter, NULL, 0, NULL, NULL, &timeOut, 0, &searchResult); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_search_ext_s: %d, %s", rc, ldap_err2string(rc)); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext_s: %d, %s", rc, ldap_err2string(rc)); ldap_msgfree(searchResult); ldap_unbind_s(ld); return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -926,7 +927,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request dn.len = ngx_strlen(ldn); } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: result DN %V", &dn); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Found user DN \"%V\"", &dn); /* Check require user */ if (server->require_user != NULL) { @@ -939,7 +940,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: compare with: %V", &val); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Comparing user DN with \"%V\"", &val); if (val.len == dn.len && ngx_memcmp(val.data, dn.data, val.len) == 0) { pass = 1; if (server->satisfy_all == 0) { @@ -974,12 +975,12 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: group compare with: %V", &val); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Comparing user group with \"%V\"", &val); rc = ldap_compare_ext_s(ld, (const char *) val.data, (const char *) server->group_attribute.data, &bvalue, NULL, NULL); /*if (rc != LDAP_COMPARE_TRUE && rc != LDAP_COMPARE_FALSE && rc != LDAP_NO_SUCH_ATTRIBUTE) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_search_ext_s: %d, %s", rc, + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext_s: %d, %s", rc, ldap_err2string(rc)); ldap_memfree(ldn); ldap_unbind_s(ld); @@ -1005,11 +1006,11 @@ static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request /* Bind user to the server */ rc = ldap_simple_bind_s(ld, (const char *) dn.data, (const char *) r->headers_in.passwd.data); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "LDAP: ldap_simple_bind_s error: %d, %s", rc, + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_simple_bind_s error: %d, %s", rc, ldap_err2string(rc)); pass = 0; } else { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: User bind successful", NULL); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: User bind successful", NULL); if (server->require_valid_user == 1) pass = 1; } } From afda839edbf817b9610feb3c19488ae0b6eff480 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Fri, 30 Aug 2013 01:18:03 +0200 Subject: [PATCH 14/18] Handle LDAP operations asynchronously using nginx event architecture A connection is opened using nginx framework and then fed to OpenLDAP using ldap_init_fd() call with custom SockBuf IO handlers. When some credentials need to be validated, ngx_http_auth_ldap_authenticate() is called multiple times, returning NGX_AGAIN each time the process is waiting for the LDAP server to reply. (Note: This only an initial implementation and as such, it is of course very buggy and limited. Further work is needed to make it useful.) --- config | 2 +- ngx_http_auth_ldap_module.c | 1072 +++++++++++++++++++++++++++++------ 2 files changed, 885 insertions(+), 189 deletions(-) diff --git a/config b/config index 44aaead..b3b71d9 100644 --- a/config +++ b/config @@ -2,4 +2,4 @@ ngx_addon_name=ngx_http_auth_ldap_module HTTP_MODULES="$HTTP_MODULES ngx_http_auth_ldap_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_auth_ldap_module.c" CORE_LIBS="$CORE_LIBS -lldap" -CFLAGS="$CFLAGS -DLDAP_DEPRECATED" \ No newline at end of file +CFLAGS="$CFLAGS" diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index a750f66..ffebed1 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -31,9 +31,21 @@ #include #include +#ifndef LDAP_PROTO_EXT +/* Some OpenLDAP headers are accidentally missing ldap_init_fd() etc. */ + +#define LDAP_PROTO_TCP 1 /* ldap:// */ +#define LDAP_PROTO_UDP 2 /* reserved */ +#define LDAP_PROTO_IPC 3 /* ldapi:// */ +#define LDAP_PROTO_EXT 4 /* user-defined socket/sockbuf */ + +extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); +#endif + typedef struct { LDAPURLDesc *ludpp; ngx_str_t url; + ngx_url_t parsed_url; ngx_str_t alias; ngx_str_t bind_dn; @@ -47,6 +59,9 @@ typedef struct { ngx_flag_t require_valid_user; ngx_http_complex_value_t require_valid_user_dn; ngx_flag_t satisfy_all; + + ngx_queue_t free_connections; + ngx_queue_t waiting_requests; } ngx_http_auth_ldap_server_t; typedef struct { @@ -75,12 +90,57 @@ typedef struct { ngx_msec_t expiration_time; } ngx_http_auth_ldap_cache_t; +typedef enum { + PHASE_START, + PHASE_SEARCH, + PHASE_CHECK_USER, + PHASE_CHECK_GROUP, + PHASE_CHECK_BIND, + PHASE_NEXT +} ngx_http_auth_ldap_request_phase_t; + typedef struct { + ngx_http_request_t *r; + ngx_uint_t server_index; + ngx_http_auth_ldap_server_t *server; + ngx_http_auth_ldap_request_phase_t phase; + unsigned int iteration; + int outcome; + + struct ngx_http_auth_ldap_connection *c; + ngx_queue_t queue; + int error_code; + ngx_str_t error_msg; + ngx_str_t dn; + ngx_http_auth_ldap_cache_elt_t *cache_bucket; u_char cache_big_hash[16]; uint32_t cache_small_hash; } ngx_http_auth_ldap_ctx_t; +typedef enum { + STATE_DISCONNECTED, + STATE_INITIAL_BINDING, + STATE_CONNECTING, + STATE_READY, + STATE_BINDING, + STATE_SEARCHING, + STATE_COMPARING +} ngx_http_auth_ldap_connection_state_t; + +typedef struct ngx_http_auth_ldap_connection { + ngx_log_t *log; + ngx_http_auth_ldap_server_t *server; + ngx_peer_connection_t conn; + + ngx_queue_t queue; + ngx_http_auth_ldap_ctx_t *rctx; + + LDAP* ld; + ngx_http_auth_ldap_connection_state_t state; + int msgid; +} ngx_http_auth_ldap_connection_t; + static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); static char * ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -95,13 +155,18 @@ static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *); static ngx_int_t ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle); static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); static ngx_int_t ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle); +static void ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c); +static ngx_int_t ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle); static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, ngx_http_auth_ldap_loc_conf_t *conf); -static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, - ngx_http_auth_ldap_loc_conf_t *conf); +static ngx_int_t ngx_http_auth_ldap_search(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx); +static ngx_int_t ngx_http_auth_ldap_check_user(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx); +static ngx_int_t ngx_http_auth_ldap_check_group(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx); +static ngx_int_t ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx); ngx_http_auth_ldap_cache_t ngx_http_auth_ldap_cache; +ngx_http_auth_ldap_connection_t ngx_http_auth_ldap_connection; static ngx_command_t ngx_http_auth_ldap_commands[] = { { @@ -413,11 +478,23 @@ ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server return NGX_CONF_ERROR; } - server->url.len = ngx_strlen(server->ludpp->lud_scheme) + ngx_strlen(server->ludpp->lud_host) + 11; /* 11 = len("://:/") + len("65535") + len("\0") */ - server->url.data = ngx_pcalloc(cf->pool, server->url.len); - p = ngx_sprintf(server->url.data, "%s://%s:%d/", (const char*) server->ludpp->lud_scheme, - (const char*) server->ludpp->lud_host, server->ludpp->lud_port); - *p = 0; + server->url.data = ngx_palloc(cf->pool, ngx_strlen(server->ludpp->lud_scheme) + sizeof("://") - 1 + + ngx_strlen(server->ludpp->lud_host) + sizeof(":65535")); + p = ngx_sprintf(server->url.data, "%s://%s:%d%Z", server->ludpp->lud_scheme, server->ludpp->lud_host, + server->ludpp->lud_port); + server->url.len = p - server->url.data - 1; + + ngx_memzero(&server->parsed_url, sizeof(ngx_url_t)); + server->parsed_url.url.data = (u_char *) server->ludpp->lud_host; + server->parsed_url.url.len = ngx_strlen(server->ludpp->lud_host); + server->parsed_url.default_port = server->ludpp->lud_port; + if (ngx_parse_url(cf->pool, &server->parsed_url) != NGX_OK) { + if (server->parsed_url.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: %s in LDAP hostname \"%V\"", + server->parsed_url.err, &server->parsed_url.url); + } + return NGX_CONF_ERROR; + } return NGX_CONF_OK; } @@ -597,6 +674,11 @@ ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle) return rc; } + rc = ngx_http_auth_ldap_init_connections(cycle); + if (rc != NGX_OK) { + return rc; + } + return NGX_OK; } @@ -677,7 +759,7 @@ ngx_http_auth_ldap_check_cache(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t * ngx_md5_init(&md5ctx); ngx_md5_update(&md5ctx, r->headers_in.user.data, r->headers_in.user.len); - ngx_md5_update(&md5ctx, server, sizeof(*server)); + ngx_md5_update(&md5ctx, server, offsetof(ngx_http_auth_ldap_server_t, free_connections)); ngx_md5_update(&md5ctx, r->headers_in.passwd.data, r->headers_in.passwd.len); ngx_md5_final(ctx->cache_big_hash, &md5ctx); @@ -718,6 +800,495 @@ ngx_http_auth_ldap_update_cache(ngx_http_auth_ldap_ctx_t *ctx, } +/*** OpenLDAP SockBuf implementation over nginx socket functions ***/ + +static int +ngx_http_auth_ldap_sb_setup(Sockbuf_IO_Desc *sbiod, void *arg) +{ + sbiod->sbiod_pvt = arg; + return 0; +} + +static int +ngx_http_auth_ldap_sb_remove(Sockbuf_IO_Desc *sbiod) +{ + ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_remove()"); + (void)c; /* 'c' would be left unused on debug builds */ + + sbiod->sbiod_pvt = NULL; + return 0; +} + +static int +ngx_http_auth_ldap_sb_close(Sockbuf_IO_Desc *sbiod) +{ + ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_close()"); + + if (!c->conn.connection->read->error) { + if (ngx_shutdown_socket(c->conn.connection->fd, SHUT_RDWR) == -1) { + ngx_connection_error(c->conn.connection, ngx_socket_errno, ngx_shutdown_socket_n " failed"); + ngx_http_auth_ldap_close_connection(c); + return -1; + } + } + + return 0; +} + +static int +ngx_http_auth_ldap_sb_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) +{ + ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_ctrl(opt=%d)", opt); + + switch (opt) { + case LBER_SB_OPT_DATA_READY: + if (c->conn.connection->read->ready) { + return 1; + } + return 0; + } + + return 0; +} + +static ber_slen_t +ngx_http_auth_ldap_sb_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; + ber_slen_t ret; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_read(len=%d)", len); + + ret = c->conn.connection->recv(c->conn.connection, buf, len); + if (ret < 0) { + errno = (ret == NGX_AGAIN) ? NGX_EAGAIN : NGX_ECONNRESET; + return -1; + } + + return ret; +} + +static ber_slen_t +ngx_http_auth_ldap_sb_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + ngx_http_auth_ldap_connection_t *c = (ngx_http_auth_ldap_connection_t *)sbiod->sbiod_pvt; + ber_slen_t ret; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ngx_http_auth_ldap_sb_write(len=%d)", len); + + ret = c->conn.connection->send(c->conn.connection, buf, len); + if (ret < 0) { + errno = (ret == NGX_AGAIN) ? NGX_EAGAIN : NGX_ECONNRESET; + return 0; + } + + return ret; +} + +static Sockbuf_IO ngx_http_auth_ldap_sbio = +{ + ngx_http_auth_ldap_sb_setup, + ngx_http_auth_ldap_sb_remove, + ngx_http_auth_ldap_sb_ctrl, + ngx_http_auth_ldap_sb_read, + ngx_http_auth_ldap_sb_write, + ngx_http_auth_ldap_sb_close +}; + + +/*** Asynchronous LDAP connection handling ***/ + +static void +ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Closing connection"); + + if (c->ld) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Unbinding from the server"); + ldap_unbind_ext(c->ld, NULL, NULL); + /* Unbind is always synchronous, even though the function name does not end with an '_s'. */ + c->ld = NULL; + } + + if (c->conn.connection) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Closing connection fd=%d", + c->conn.connection->fd); + ngx_close_connection(c->conn.connection); + } + + c->rctx = NULL; + c->state = STATE_DISCONNECTED; +} + +static void +ngx_http_auth_ldap_wake_request(ngx_http_request_t *r) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Waking authentication request \"%V\"", + &r->request_line); + ngx_http_core_run_phases(r); +} + +static int +ngx_http_auth_ldap_get_connection(ngx_http_auth_ldap_ctx_t *ctx) +{ + ngx_http_auth_ldap_server_t *server; + ngx_queue_t *q; + ngx_http_auth_ldap_connection_t *c; + + server = ctx->server; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "http_auth_ldap: Wants a free connection to \"%V\"", + &server->alias); + + if (!ngx_queue_empty(&server->free_connections)) { + q = ngx_queue_last(&server->free_connections); + ngx_queue_remove(q); + c = ngx_queue_data(q, ngx_http_auth_ldap_connection_t, queue); + c->rctx = ctx; + ctx->c = c; + return 1; + } + + ngx_queue_insert_head(&server->waiting_requests, &ctx->queue); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "http_auth_ldap: No connection available at the moment, waiting..."); + return 0; +} + +static void +ngx_http_auth_ldap_return_connection(ngx_http_auth_ldap_connection_t *c) +{ + ngx_queue_t *q; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Marking the connection to \"%V\" as free", + &c->server->alias); + + if (c->rctx != NULL) { + c->rctx->c = NULL; + c->rctx = NULL; + c->msgid = -1; + c->state = STATE_READY; + } + + ngx_queue_insert_head(&c->server->free_connections, &c->queue); + if (!ngx_queue_empty(&c->server->waiting_requests)) { + q = ngx_queue_last(&c->server->waiting_requests); + ngx_queue_remove(q); + ngx_http_auth_ldap_wake_request((ngx_queue_data(q, ngx_http_auth_ldap_ctx_t, queue))->r); + } +} + +static void +ngx_http_auth_ldap_reply_connection(ngx_http_auth_ldap_connection_t *c, int error_code, char* error_msg) +{ + ngx_http_auth_ldap_ctx_t *ctx = c->rctx; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: LDAP request to \"%V\" has finished", + &c->server->alias); + + ctx->error_code = error_code; + if (error_msg) { + ctx->error_msg.len = ngx_strlen(error_msg); + ctx->error_msg.data = ngx_palloc(ctx->r->pool, ctx->error_msg.len); + ngx_memcpy(ctx->error_msg.data, error_msg, ctx->error_msg.len); + } else { + ctx->error_msg.len = 0; + ctx->error_msg.data = NULL; + } + + ngx_http_auth_ldap_return_connection(c); + + ngx_http_auth_ldap_wake_request(ctx->r); +} + +static void +ngx_http_auth_ldap_dummy_write_handler(ngx_event_t *wev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http_auth_ldap: Dummy write handler"); + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_auth_ldap_close_connection(((ngx_connection_t *) wev->data)->data); + } +} + +static void +ngx_http_auth_ldap_connect_handler(ngx_event_t *wev) +{ + ngx_peer_connection_t *conn; + ngx_http_auth_ldap_connection_t *c; + Sockbuf *sb; + ngx_int_t rc; + int keepalive; + struct berval cred; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http_auth_ldap: Connect handler"); + + conn = ((ngx_connection_t *) wev->data)->data; + c = conn->data; + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_auth_ldap_close_connection(c); + return; + } + + ngx_del_timer(conn->connection->read); + conn->connection->write->handler = ngx_http_auth_ldap_dummy_write_handler; + + keepalive = 1; + if (setsockopt(conn->connection->fd, SOL_SOCKET, SO_KEEPALIVE, (const void *) &keepalive, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, "http_auth_ldap: setsockopt(SO_KEEPALIVE) failed"); + } + + + /* Initialize OpenLDAP on the connection */ + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Initializing connection using URL \"%V\"", &c->server->url); + + rc = ldap_init_fd(conn->connection->fd, LDAP_PROTO_EXT, (const char *) c->server->url.data, &c->ld); + if (rc != LDAP_SUCCESS) { + ngx_log_error(NGX_LOG_INFO, c->log, errno, "http_auth_ldap: ldap_init_fd() failed (%d: %s)", rc, ldap_err2string(rc)); + ngx_http_auth_ldap_close_connection(c); + return; + } + + rc = ldap_get_option(c->ld, LDAP_OPT_SOCKBUF, (void *) &sb); + if (rc != LDAP_OPT_SUCCESS) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "http_auth_ldap: ldap_get_option() failed (%d: %s)", rc, ldap_err2string(rc)); + ngx_http_auth_ldap_close_connection(c); + return; + } + + ber_sockbuf_add_io(sb, &ngx_http_auth_ldap_sbio, LBER_SBIOD_LEVEL_PROVIDER, (void *) c); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connection initialized"); + + + /* Perform initial bind to the server */ + + cred.bv_val = (char *) c->server->bind_dn_passwd.data; + cred.bv_len = c->server->bind_dn_passwd.len; + rc = ldap_sasl_bind(c->ld, (const char *) c->server->bind_dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &c->msgid); + if (rc != LDAP_SUCCESS) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "http_auth_ldap: ldap_sasl_bind() failed (%d: %s)", + rc, ldap_err2string(rc)); + ngx_http_auth_ldap_close_connection(c); + return; + } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ldap_sasl_bind() -> msgid=%d", c->msgid); + + c->state = STATE_INITIAL_BINDING; + ngx_add_timer(conn->connection->read, 5000); /* TODO: Bind timeout */ +} + +static void +ngx_http_auth_ldap_read_handler(ngx_event_t *rev) +{ + ngx_peer_connection_t *conn; + ngx_http_auth_ldap_connection_t *c; + ngx_int_t rc; + struct timeval timeout = {0, 0}; + LDAPMessage *result; + int error_code; + char *error_msg; + char *dn; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http_auth_ldap: Read handler"); + + conn = ((ngx_connection_t *)rev->data)->data; + c = conn->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "http_auth_ldap: Request timed out (state=%d)", c->state); + conn->connection->timedout = 1; + ngx_http_auth_ldap_close_connection(c); + return; + } + + c->log->action = "reading response from LDAP"; + + for (;;) { + rc = ldap_result(c->ld, LDAP_RES_ANY, 0, &timeout, &result); + if (rc < 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "http_auth_ldap: ldap_result() failed (%d: %s)", + rc, ldap_err2string(rc)); + ngx_http_auth_ldap_close_connection(c); + return; + } + if (rc == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ldap_result() -> rc=0"); + break; + } + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ldap_result() -> rc=%d, msgid=%d, msgtype=%d", + rc, ldap_msgid(result), ldap_msgtype(result)); + + if (ldap_msgid(result) != c->msgid) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Message with unknown ID received, ignoring."); + ldap_msgfree(result); + continue; + } + + rc = ldap_parse_result(c->ld, result, &error_code, NULL, &error_msg, NULL, NULL, 0); + if (rc == LDAP_NO_RESULTS_RETURNED) { + error_code = LDAP_NO_RESULTS_RETURNED; + error_msg = NULL; + } else if (rc != LDAP_SUCCESS) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "http_auth_ldap: ldap_parse_result() failed (%d: %s)", + rc, ldap_err2string(rc)); + ldap_msgfree(result); + ngx_http_auth_ldap_close_connection(c); + return; + } + + switch (c->state) { + case STATE_INITIAL_BINDING: + if (ldap_msgtype(result) != LDAP_RES_BIND) { + break; + } + ngx_del_timer(conn->connection->read); + if (error_code == LDAP_SUCCESS) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Initial bind successful"); + c->state = STATE_READY; + ngx_http_auth_ldap_return_connection(c); + } else { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "http_auth_ldap: Initial bind failed (%d: %s [%s])", + error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); + ldap_memfree(error_msg); + ldap_msgfree(result); + ngx_http_auth_ldap_close_connection(c); + return; + } + break; + + case STATE_BINDING: + if (ldap_msgtype(result) != LDAP_RES_BIND) { + break; + } + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received bind response (%d: %s [%s])", + error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); + ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); + break; + + case STATE_SEARCHING: + if (ldap_msgtype(result) == LDAP_RES_SEARCH_ENTRY) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received a search entry"); + if (c->rctx->dn.data == NULL) { + dn = ldap_get_dn(c->ld, result); + if (dn != NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Found entry with DN \"%s\"", dn); + c->rctx->dn.len = ngx_strlen(dn); + c->rctx->dn.data = (u_char *) ngx_palloc(c->rctx->r->pool, c->rctx->dn.len + 1); + ngx_memcpy(c->rctx->dn.data, dn, c->rctx->dn.len + 1); + ldap_memfree(dn); + } + } + } else if (ldap_msgtype(result) == LDAP_RES_SEARCH_RESULT) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received search result (%d: %s [%s])", + error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); + ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); + } + break; + + case STATE_COMPARING: + if (ldap_msgtype(result) != LDAP_RES_COMPARE) { + break; + } + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Received comparison result (%d: %s [%s])", + error_code, ldap_err2string(error_code), error_msg ? error_msg : "-"); + ngx_http_auth_ldap_reply_connection(c, error_code, error_msg); + break; + + default: + break; + } + + ldap_memfree(error_msg); + ldap_msgfree(result); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_auth_ldap_close_connection(c); + return; + } +} + +static void +ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) +{ + ngx_peer_connection_t *conn; + ngx_addr_t *addr; + ngx_int_t rc; + + addr = &c->server->parsed_url.addrs[ngx_random() % c->server->parsed_url.naddrs]; + + conn = &c->conn; + conn->data = c; + conn->sockaddr = addr->sockaddr; + conn->socklen = addr->socklen; + conn->name = &addr->name; + conn->get = ngx_event_get_peer; + conn->log = c->log; + conn->log_error = NGX_ERROR_ERR; + + rc = ngx_event_connect_peer(conn); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ngx_event_connect_peer() -> %d.", rc); + if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: Unable to connect to LDAP server \"%V\".", + &addr->name); + return; + } + + ngx_add_timer(conn->connection->read, 10000); /* TODO: Connect timeout */ + + conn->connection->data = conn; + conn->connection->write->handler = ngx_http_auth_ldap_connect_handler; + conn->connection->read->handler = ngx_http_auth_ldap_read_handler; + + c->state = STATE_CONNECTING; +} + +static ngx_int_t +ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) +{ + ngx_http_auth_ldap_connection_t *c; + ngx_http_auth_ldap_main_conf_t *halmcf; + ngx_http_auth_ldap_server_t *servers; + ngx_int_t rc; + int option; + + option = LDAP_VERSION3; + rc = ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &option); + + option = LDAP_OPT_X_TLS_ALLOW; + rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &option); + if (rc != LDAP_OPT_SUCCESS) { + ngx_log_error(NGX_LOG_WARN, cycle->log, 0, "http_auth_ldap: Unable to set TLS_REQUIRE_CERT option (%d: %s)", + rc, ldap_err2string(rc)); + } + + halmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); + servers = halmcf->servers->elts; + + c = &ngx_http_auth_ldap_connection; + c->log = cycle->log; + c->server = &servers[0]; + c->state = STATE_DISCONNECTED; + + ngx_queue_init(&c->server->free_connections); + ngx_queue_init(&c->server->waiting_requests); + + ngx_http_auth_ldap_connect(c); + + return NGX_OK; +} + + + /*** Per-request authentication processing ***/ /** @@ -776,6 +1347,8 @@ ngx_http_auth_ldap_handler(ngx_http_request_t *r) if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } + ctx->r = r; + /* Other fields have been initialized to zero/NULL */ ngx_http_set_ctx(r, ctx, ngx_http_auth_ldap_module); } @@ -783,240 +1356,363 @@ ngx_http_auth_ldap_handler(ngx_http_request_t *r) } /** - * Read user credentials from request, set LDAP parameters and call authentication against required servers + * Iteratively handle all phases of the authentication process, might be called many times */ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, ngx_http_auth_ldap_loc_conf_t *conf) { - ngx_http_auth_ldap_server_t *server; - int rc; - ngx_uint_t i; + ngx_int_t rc; - int version = LDAP_VERSION3; - int reqcert = LDAP_OPT_X_TLS_ALLOW; - struct timeval timeOut = { 10, 0 }; + if (r->connection->write->timedout) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: Authentication timed out"); + if (ctx->c != NULL) { + ngx_http_auth_ldap_return_connection(ctx->c); + } + return NGX_ERROR; + } - /* Set LDAP version to 3 and set connection timeout. */ - ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &version); - ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &timeOut); + for (;;) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Authentication loop (phase=%d, iteration=%d)", + ctx->phase, ctx->iteration); + + switch (ctx->phase) { + case PHASE_START: + ctx->server = ((ngx_http_auth_ldap_server_t **) conf->servers->elts)[ctx->server_index]; + ctx->outcome = -1; + + ngx_add_timer(r->connection->write, 10000); /* TODO: Per-server request timeout */ + + /* Check cache if enabled */ + if (ngx_http_auth_ldap_cache.buckets != NULL) { + rc = ngx_http_auth_ldap_check_cache(r, ctx, &ngx_http_auth_ldap_cache, ctx->server); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Using cached outcome %d", rc); + if (rc == 0 || rc == 1) { + ctx->outcome = 2 + rc; + ctx->phase = PHASE_NEXT; + break; + } + } - rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &reqcert); - if (rc != LDAP_OPT_SUCCESS) { - ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "http_auth_ldap: Unable to set require cert option: %s", - ldap_err2string(rc)); - } + if (ctx->server->require_valid_user_dn.value.data != NULL) { + /* Construct user DN */ + if (ngx_http_complex_value(r, &ctx->server->require_valid_user_dn, &ctx->dn) != NGX_OK) { + ngx_del_timer(r->connection->write); + return NGX_ERROR; + } + ctx->phase = PHASE_CHECK_USER; + break; + } + + ctx->phase = PHASE_SEARCH; + ctx->iteration = 0; + break; - for (i = 0; i < conf->servers->nelts; i++) { - server = ((ngx_http_auth_ldap_server_t **) conf->servers->elts)[i]; + case PHASE_SEARCH: + /* Search the directory to retrieve full user DN */ + rc = ngx_http_auth_ldap_search(r, ctx); + if (rc == NGX_AGAIN) { + /* LDAP operation in progress, wait for the results */ + return NGX_AGAIN; + } + if (rc != NGX_OK) { + /* Search failed, try next server */ + ctx->phase = PHASE_NEXT; + break; + } - if (ngx_http_auth_ldap_cache.buckets != NULL) { - rc = ngx_http_auth_ldap_check_cache(r, ctx, &ngx_http_auth_ldap_cache, server); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Using cached outcome %d", rc); - if (rc == 0) { - continue; - } - if (rc == 1) { - return NGX_OK; - } - } + /* User DN has been found, check user next */ + ctx->phase = PHASE_CHECK_USER; + break; - rc = ngx_http_auth_ldap_authenticate_against_server(r, server, conf); + case PHASE_CHECK_USER: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: User DN is \"%V\"", + &ctx->dn); + + if (ctx->server->require_user != NULL) { + rc = ngx_http_auth_ldap_check_user(r, ctx); + if (rc != NGX_OK) { + /* User check failed, try next server */ + ctx->phase = PHASE_NEXT; + break; + } + } - if ((rc == 0 || rc == 1) && ngx_http_auth_ldap_cache.buckets != NULL) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Caching outcome %d", rc); - ngx_http_auth_ldap_update_cache(ctx, &ngx_http_auth_ldap_cache, rc); - } + /* User validated, check group next */ + if (ctx->server->require_group != NULL) { + ctx->phase = PHASE_CHECK_GROUP; + ctx->iteration = 0; + break; + } - if (rc == 1) { - return NGX_OK; - } else if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - } + /* No groups to validate, try binding next */ + ctx->phase = PHASE_CHECK_BIND; + ctx->iteration = 0; + break; - return ngx_http_auth_ldap_set_realm(r, &conf->realm); -} + case PHASE_CHECK_GROUP: + rc = ngx_http_auth_ldap_check_group(r, ctx); + if (rc == NGX_AGAIN) { + /* LDAP operation in progress, wait for the results */ + return NGX_AGAIN; + } + if (rc != NGX_OK) { + /* Group check failed, try next server */ + ctx->phase = PHASE_NEXT; + break; + } -/** - * Actual authentication against LDAP server - */ -static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server, - ngx_http_auth_ldap_loc_conf_t *conf) -{ - LDAPURLDesc *ludpp = server->ludpp; - int rc; - LDAP *ld; - LDAPMessage *searchResult; - char* ldn = NULL; - ngx_str_t dn; - u_char *p, *filter; - ngx_http_complex_value_t *value; - ngx_uint_t i; - struct berval bvalue; - ngx_flag_t pass = NGX_CONF_UNSET; - struct timeval timeOut = { 10, 0 }; + /* Groups validated, try binding next */ + ctx->phase = PHASE_CHECK_BIND; + ctx->iteration = 0; + break; - if (server->ludpp == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + case PHASE_CHECK_BIND: + if (ctx->server->require_valid_user == 0) { + ctx->phase = PHASE_NEXT; + break; + } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: URL is \"%s\"", server->url.data); + /* Initiate bind using the found DN and request password */ + rc = ngx_http_auth_ldap_check_bind(r, ctx); + if (rc == NGX_AGAIN) { + /* LDAP operation in progress, wait for the result */ + return NGX_AGAIN; + } - rc = ldap_initialize(&ld, (const char*) server->url.data); - if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: Session initializing failed: %d, %s, (%s)", rc, - ldap_err2string(rc), (const char*) server->url.data); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Session initialized", NULL); + /* All steps done, finish the processing */ + ctx->phase = PHASE_NEXT; + break; - /* Bind to the server */ - rc = ldap_simple_bind_s(ld, (const char *) server->bind_dn.data, (const char *) server->bind_dn_passwd.data); - if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_simple_bind_s(%s) error: %d, %s", - server->url.data, rc, ldap_err2string(rc)); - ldap_unbind_s(ld); - /* Do not throw 500 in case connection failure, multiple servers might be used for failover scenario */ - return 0; + case PHASE_NEXT: + if (r->connection->write->timer_set) { + ngx_del_timer(r->connection->write); + } + + if (ctx->c != NULL) { + ngx_http_auth_ldap_return_connection(ctx->c); + } + + if (ngx_http_auth_ldap_cache.buckets != NULL && (ctx->outcome == 0 || ctx->outcome == 1)) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Caching outcome %d", ctx->outcome); + ngx_http_auth_ldap_update_cache(ctx, &ngx_http_auth_ldap_cache, ctx->outcome); + } + + if (ctx->outcome == 1 || ctx->outcome == 2 + 1) { + return NGX_OK; + } + + ctx->server_index++; + if (ctx->server_index >= conf->servers->nelts) { + return ngx_http_auth_ldap_set_realm(r, &conf->realm); + } + + ctx->phase = PHASE_START; + break; + } } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Bind successful", NULL); +} - if (server->require_valid_user_dn.value.data != NULL) { - /* Construct user DN */ - if (ngx_http_complex_value(r, &server->require_valid_user_dn, &dn) != NGX_OK) { - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; +static ngx_int_t +ngx_http_auth_ldap_search(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) +{ + LDAPURLDesc *ludpp; + u_char *filter; + char *attrs[2]; + ngx_int_t rc; + + /* On the first call, initiate the LDAP search operation */ + if (ctx->iteration == 0) { + if (!ngx_http_auth_ldap_get_connection(ctx)) { + return NGX_AGAIN; } - } else { - /* Create filter for search users by uid */ + + ludpp = ctx->server->ludpp; filter = ngx_pcalloc( r->pool, (ludpp->lud_filter != NULL ? ngx_strlen(ludpp->lud_filter) : ngx_strlen("(objectClass=*)")) + ngx_strlen("(&(=))") + ngx_strlen(ludpp->lud_attrs[0]) + r->headers_in.user.len + 1); - - p = ngx_sprintf(filter, "(&%s(%s=%V))", + ngx_sprintf(filter, "(&%s(%s=%V))%Z", ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", ludpp->lud_attrs[0], &r->headers_in.user); - *p = 0; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Search filter is \"%s\"", - (const char*) filter); + (const char *) filter); - /* Search the directory */ - rc = ldap_search_ext_s(ld, ludpp->lud_dn, ludpp->lud_scope, (const char*) filter, NULL, 0, NULL, NULL, &timeOut, 0, - &searchResult); + attrs[0] = LDAP_NO_ATTRS; + attrs[1] = NULL; + rc = ldap_search_ext(ctx->c->ld, ludpp->lud_dn, ludpp->lud_scope, (const char *) filter, attrs, 0, NULL, NULL, NULL, 0, &ctx->c->msgid); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext_s: %d, %s", rc, ldap_err2string(rc)); - ldap_msgfree(searchResult); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() failed (%d, %s)", + rc, ldap_err2string(rc)); + ngx_http_auth_ldap_return_connection(ctx->c); + return NGX_ERROR; } - if (ldap_count_entries(ld, searchResult) > 0) { - ldn = ldap_get_dn(ld, searchResult); - } - ldap_msgfree(searchResult); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() -> msgid=%d", + ctx->c->msgid); + ctx->c->state = STATE_SEARCHING; + ctx->iteration++; + return NGX_AGAIN; + } - if (!ldn) { - ldap_unbind_s(ld); - return 0; - } + /* On the second call, handle the search results */ + if (ctx->error_code != LDAP_SUCCESS) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext() request failed (%d: %s)", + ctx->error_code, ldap_err2string(ctx->error_code)); + return NGX_ERROR; + } - dn.data = (u_char*) ldn; - dn.len = ngx_strlen(ldn); + if (ctx->dn.data == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: Could not find user DN"); + return NGX_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Found user DN \"%V\"", &dn); + return NGX_OK; +} - /* Check require user */ - if (server->require_user != NULL) { - value = server->require_user->elts; - for (i = 0; i < server->require_user->nelts; i++) { - ngx_str_t val; - if (ngx_http_complex_value(r, &value[i], &val) != NGX_OK) { - ldap_memfree(ldn); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } +static ngx_int_t +ngx_http_auth_ldap_check_user(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) +{ + ngx_http_complex_value_t *values; + ngx_uint_t i; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Comparing user DN with \"%V\"", &val); - if (val.len == dn.len && ngx_memcmp(val.data, dn.data, val.len) == 0) { - pass = 1; - if (server->satisfy_all == 0) { - break; - } - } else { - if (server->satisfy_all == 1) { - ldap_memfree(ldn); - ldap_unbind_s(ld); - return 0; - } + values = ctx->server->require_user->elts; + for (i = 0; i < ctx->server->require_user->nelts; i++) { + ngx_str_t val; + if (ngx_http_complex_value(r, &values[i], &val) != NGX_OK) { + ctx->outcome = -1; + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Comparing user DN with \"%V\"", &val); + if (val.len == ctx->dn.len && ngx_memcmp(val.data, ctx->dn.data, val.len) == 0) { + if (ctx->server->satisfy_all == 0) { + ctx->outcome = 1; + return NGX_OK; + } + } else { + if (ctx->server->satisfy_all == 1) { + ctx->outcome = 0; + return NGX_DECLINED; } } } - /* Check require group */ - if (server->require_group != NULL) { - if (server->group_attribute_dn == 1) { - bvalue.bv_val = (char*) dn.data; - bvalue.bv_len = dn.len; + return NGX_OK; +} + +static ngx_int_t +ngx_http_auth_ldap_check_group(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) +{ + ngx_http_complex_value_t *values; + struct berval bvalue; + ngx_int_t rc; + + /* Handle result of the comparison started during previous call */ + if (ctx->iteration > 0) { + if (ctx->error_code == LDAP_COMPARE_TRUE) { + if (ctx->server->satisfy_all == 0) { + ctx->outcome = 1; + return NGX_OK; + } + } else if (ctx->error_code == LDAP_COMPARE_FALSE || ctx->error_code == LDAP_NO_SUCH_ATTRIBUTE) { + if (ctx->server->satisfy_all == 1) { + ctx->outcome = 0; + return NGX_DECLINED; + } } else { - bvalue.bv_val = (char*) r->headers_in.user.data; - bvalue.bv_len = r->headers_in.user.len; + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_compare_ext() request failed (%d: %s)", + ctx->error_code, ldap_err2string(ctx->error_code)); + return NGX_ERROR; } + } - value = server->require_group->elts; - for (i = 0; i < server->require_group->nelts; i++) { - ngx_str_t val; - if (ngx_http_complex_value(r, &value[i], &val) != NGX_OK) { - ldap_memfree(ldn); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + /* Check next group */ + if (ctx->iteration >= ctx->server->require_group->nelts) { + return NGX_OK; + } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Comparing user group with \"%V\"", &val); - rc = ldap_compare_ext_s(ld, (const char *) val.data, (const char *) server->group_attribute.data, - &bvalue, NULL, NULL); + if (!ngx_http_auth_ldap_get_connection(ctx)) { + return NGX_AGAIN; + } - /*if (rc != LDAP_COMPARE_TRUE && rc != LDAP_COMPARE_FALSE && rc != LDAP_NO_SUCH_ATTRIBUTE) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_search_ext_s: %d, %s", rc, - ldap_err2string(rc)); - ldap_memfree(ldn); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - }*/ + ngx_str_t val; + values = ctx->server->require_group->elts; + if (ngx_http_complex_value(r, &values[ctx->iteration], &val) != NGX_OK) { + ctx->outcome = -1; + ngx_http_auth_ldap_return_connection(ctx->c); + return NGX_ERROR; + } - if (rc == LDAP_COMPARE_TRUE) { - pass = 1; - if (server->satisfy_all == 0) { - break; - } - } else { - if (server->satisfy_all == 1) { - pass = 0; - break; - } - } - } + if (ctx->server->group_attribute_dn == 1) { + bvalue.bv_val = (char*) ctx->dn.data; + bvalue.bv_len = ctx->dn.len; + } else { + bvalue.bv_val = (char*) r->headers_in.user.data; + bvalue.bv_len = r->headers_in.user.len; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Comparing user group with \"%V\"", &val); + + rc = ldap_compare_ext(ctx->c->ld, (const char *) val.data, (const char *) ctx->server->group_attribute.data, + &bvalue, NULL, NULL, &ctx->c->msgid); + if (rc != LDAP_SUCCESS) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_compare_ext() failed (%d: %s)", + rc, ldap_err2string(rc)); + ctx->outcome = -1; + ngx_http_auth_ldap_return_connection(ctx->c); + return NGX_ERROR; } - /* Check valid user */ - if (pass != 0 || (server->require_valid_user == 1 && server->satisfy_all == 0 && pass == 0)) { - /* Bind user to the server */ - rc = ldap_simple_bind_s(ld, (const char *) dn.data, (const char *) r->headers_in.passwd.data); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: ldap_compare_ext() -> msgid=%d", + ctx->c->msgid); + ctx->c->state = STATE_COMPARING; + ctx->iteration++; + return NGX_AGAIN; +} + +/** + * Initiate and handle a bind operation using the authentication parameters + */ +static ngx_int_t +ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx) +{ + struct berval cred; + ngx_int_t rc; + + /* On the first call, initiate the bind LDAP operation */ + if (ctx->iteration == 0) { + if (!ngx_http_auth_ldap_get_connection(ctx)) { + return NGX_AGAIN; + } + + cred.bv_val = (char *) r->headers_in.passwd.data; + cred.bv_len = r->headers_in.passwd.len; + rc = ldap_sasl_bind(ctx->c->ld, (const char *) ctx->dn.data, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &ctx->c->msgid); if (rc != LDAP_SUCCESS) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: ldap_simple_bind_s error: %d, %s", rc, - ldap_err2string(rc)); - pass = 0; - } else { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: User bind successful", NULL); - if (server->require_valid_user == 1) pass = 1; + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "http_auth_ldap: ldap_sasl_bind() failed (%d: %s)", + rc, ldap_err2string(rc)); + ctx->outcome = -1; + ngx_http_auth_ldap_return_connection(ctx->c); + return NGX_ERROR; } - } - ldap_memfree(ldn); - ldap_unbind_s(ld); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: ldap_sasl_bind() -> msgid=%d", + ctx->c->msgid); + ctx->c->state = STATE_BINDING; + ctx->iteration++; + return NGX_AGAIN; + } - return pass; + /* On the second call, process the operation result */ + if (ctx->error_code != LDAP_SUCCESS) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http_auth_ldap: User bind failed (%d: %s)", + ctx->error_code, ldap_err2string(ctx->error_code)); + ctx->outcome = 0; + } else { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: User bind successful"); + ctx->outcome = 1; + } + return NGX_OK; } From eb313ca473c8b00f2f2e94fd589c5c7474994904 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Fri, 30 Aug 2013 04:43:38 +0200 Subject: [PATCH 15/18] Open configurable amount of connections to all servers Only one connection to the first server was opened up to now. --- ngx_http_auth_ldap_module.c | 49 +++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index ffebed1..680b2e5 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -60,6 +60,7 @@ typedef struct { ngx_http_complex_value_t require_valid_user_dn; ngx_flag_t satisfy_all; + ngx_uint_t connections; ngx_queue_t free_connections; ngx_queue_t waiting_requests; } ngx_http_auth_ldap_server_t; @@ -166,7 +167,6 @@ static ngx_int_t ngx_http_auth_ldap_check_group(ngx_http_request_t *r, ngx_http_ static ngx_int_t ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx); ngx_http_auth_ldap_cache_t ngx_http_auth_ldap_cache; -ngx_http_auth_ldap_connection_t ngx_http_auth_ldap_connection; static ngx_command_t ngx_http_auth_ldap_commands[] = { { @@ -309,6 +309,7 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) ngx_str_t *value; ngx_http_auth_ldap_server_t *server; ngx_http_auth_ldap_main_conf_t *cnf = conf; + ngx_int_t i; /* It should be safe to just use latest server from array */ server = ((ngx_http_auth_ldap_server_t *) cnf->servers->elts + (cnf->servers->nelts - 1)); @@ -330,6 +331,13 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) return ngx_http_auth_ldap_parse_require(cf, server); } else if (ngx_strcmp(value[0].data, "satisfy") == 0) { return ngx_http_auth_ldap_parse_satisfy(cf, server); + } else if (ngx_strcmp(value[0].data, "connections") == 0) { + i = ngx_atoi(value[1].data, value[1].len); + if (i == NGX_ERROR || i == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: 'connections' value has to be a number greater than 0"); + return NGX_CONF_ERROR; + } + server->connections = i; } rv = NGX_CONF_OK; @@ -1252,12 +1260,20 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) c->state = STATE_CONNECTING; } +static void +ngx_http_auth_ldap_connection_cleanup(void *data) +{ + ngx_http_auth_ldap_close_connection((ngx_http_auth_ldap_connection_t *) data); +} + static ngx_int_t ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) { ngx_http_auth_ldap_connection_t *c; ngx_http_auth_ldap_main_conf_t *halmcf; - ngx_http_auth_ldap_server_t *servers; + ngx_http_auth_ldap_server_t *server; + ngx_pool_cleanup_t *cleanup; + ngx_uint_t i, j; ngx_int_t rc; int option; @@ -1272,17 +1288,30 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) } halmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); - servers = halmcf->servers->elts; + for (i = 0; i < halmcf->servers->nelts; i++) { + server = &((ngx_http_auth_ldap_server_t *) halmcf->servers->elts)[i]; + ngx_queue_init(&server->free_connections); + ngx_queue_init(&server->waiting_requests); + if (server->connections <= 1) { + server->connections = 1; + } - c = &ngx_http_auth_ldap_connection; - c->log = cycle->log; - c->server = &servers[0]; - c->state = STATE_DISCONNECTED; + for (j = 0; j < server->connections; j++) { + c = ngx_pcalloc(cycle->pool, sizeof(ngx_http_auth_ldap_connection_t)); + cleanup = ngx_pool_cleanup_add(cycle->pool, 0); + if (c == NULL || cleanup == NULL) { + return NGX_ERROR; + } - ngx_queue_init(&c->server->free_connections); - ngx_queue_init(&c->server->waiting_requests); + c->log = cycle->log; + c->server = server; + c->state = STATE_DISCONNECTED; + ngx_http_auth_ldap_connect(c); - ngx_http_auth_ldap_connect(c); + cleanup->handler = &ngx_http_auth_ldap_connection_cleanup; + cleanup->data = c; + } + } return NGX_OK; } From 9b192fee5340eecab46e51bc34faf949929abb97 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Fri, 30 Aug 2013 04:57:18 +0200 Subject: [PATCH 16/18] Fix intermittent authentication failures Sometimes the authentication handler got called again just after the search operation has been started, immediately failing because nothing has been found yet. Added an extra safety check for these cases. --- ngx_http_auth_ldap_module.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 680b2e5..f896b12 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -110,6 +110,7 @@ typedef struct { struct ngx_http_auth_ldap_connection *c; ngx_queue_t queue; + int replied; int error_code; ngx_str_t error_msg; ngx_str_t dn; @@ -960,6 +961,7 @@ ngx_http_auth_ldap_get_connection(ngx_http_auth_ldap_ctx_t *ctx) c = ngx_queue_data(q, ngx_http_auth_ldap_connection_t, queue); c->rctx = ctx; ctx->c = c; + ctx->replied = 0; return 1; } @@ -999,6 +1001,7 @@ ngx_http_auth_ldap_reply_connection(ngx_http_auth_ldap_connection_t *c, int erro ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: LDAP request to \"%V\" has finished", &c->server->alias); + ctx->replied = 1; ctx->error_code = error_code; if (error_msg) { ctx->error_msg.len = ngx_strlen(error_msg); @@ -1401,6 +1404,11 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t return NGX_ERROR; } + if (!ctx->replied && ctx->phase != PHASE_START) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: The LDAP operation did not finish yet"); + return NGX_AGAIN; + } + for (;;) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: Authentication loop (phase=%d, iteration=%d)", ctx->phase, ctx->iteration); From 007ef91db133cf592c4a03843d2953c973339d5c Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Fri, 30 Aug 2013 07:12:54 +0200 Subject: [PATCH 17/18] Implement reconnect when LDAP connection goes down --- ngx_http_auth_ldap_module.c | 55 +++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index f896b12..2294262 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -134,6 +134,7 @@ typedef struct ngx_http_auth_ldap_connection { ngx_log_t *log; ngx_http_auth_ldap_server_t *server; ngx_peer_connection_t conn; + ngx_event_t reconnect_event; ngx_queue_t queue; ngx_http_auth_ldap_ctx_t *rctx; @@ -916,23 +917,38 @@ static Sockbuf_IO ngx_http_auth_ldap_sbio = static void ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Closing connection"); + ngx_queue_t *q; if (c->ld) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Unbinding from the server"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Unbinding from the server \"%V\")", + &c->server->url); ldap_unbind_ext(c->ld, NULL, NULL); /* Unbind is always synchronous, even though the function name does not end with an '_s'. */ c->ld = NULL; } if (c->conn.connection) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Closing connection fd=%d", + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Closing connection (fd=%d)", c->conn.connection->fd); ngx_close_connection(c->conn.connection); + c->conn.connection = NULL; + } + + q = ngx_queue_head(&c->server->free_connections); + while (q != ngx_queue_sentinel(&c->server->free_connections)) { + if (q == &c->queue) { + ngx_queue_remove(q); + break; + } + q = ngx_queue_next(q); } c->rctx = NULL; - c->state = STATE_DISCONNECTED; + if (c->state != STATE_DISCONNECTED) { + c->state = STATE_DISCONNECTED; + ngx_add_timer(&c->reconnect_event, 10000); /* TODO: Reconnect timeout */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connection scheduled for reconnection in 10000 ms"); + } } static void @@ -1114,6 +1130,12 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev) conn = ((ngx_connection_t *)rev->data)->data; c = conn->data; + if (c->ld == NULL) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "http_auth_ldap: Could not connect"); + ngx_http_auth_ldap_close_connection(c); + return; + } + if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "http_auth_ldap: Request timed out (state=%d)", c->state); conn->connection->timedout = 1; @@ -1237,6 +1259,9 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) addr = &c->server->parsed_url.addrs[ngx_random() % c->server->parsed_url.naddrs]; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connecting to LDAP server \"%V\".", + &addr->name); + conn = &c->conn; conn->data = c; conn->sockaddr = addr->sockaddr; @@ -1251,6 +1276,7 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: Unable to connect to LDAP server \"%V\".", &addr->name); + ngx_add_timer(&c->reconnect_event, 10000); /* TODO: Reconnect timeout */ return; } @@ -1269,6 +1295,14 @@ ngx_http_auth_ldap_connection_cleanup(void *data) ngx_http_auth_ldap_close_connection((ngx_http_auth_ldap_connection_t *) data); } +static void +ngx_http_auth_ldap_reconnect_handler(ngx_event_t *ev) +{ + ngx_connection_t *conn = ev->data; + ngx_http_auth_ldap_connection_t *c = conn->data; + ngx_http_auth_ldap_connect(c); +} + static ngx_int_t ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) { @@ -1276,6 +1310,7 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) ngx_http_auth_ldap_main_conf_t *halmcf; ngx_http_auth_ldap_server_t *server; ngx_pool_cleanup_t *cleanup; + ngx_connection_t *dummy_conn; ngx_uint_t i, j; ngx_int_t rc; int option; @@ -1302,13 +1337,23 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle) for (j = 0; j < server->connections; j++) { c = ngx_pcalloc(cycle->pool, sizeof(ngx_http_auth_ldap_connection_t)); cleanup = ngx_pool_cleanup_add(cycle->pool, 0); - if (c == NULL || cleanup == NULL) { + dummy_conn = ngx_pcalloc(cycle->pool, sizeof(ngx_connection_t)); + if (c == NULL || cleanup == NULL || dummy_conn == NULL) { return NGX_ERROR; } c->log = cycle->log; c->server = server; c->state = STATE_DISCONNECTED; + + /* Various debug logging around timer management assume that the field + 'data' in ngx_event_t is a pointer to ngx_connection_t, therefore we + have a dummy such structure around so that it does not crash etc. */ + dummy_conn->data = c; + c->reconnect_event.log = c->log; + c->reconnect_event.data = dummy_conn; + c->reconnect_event.handler = ngx_http_auth_ldap_reconnect_handler; + ngx_http_auth_ldap_connect(c); cleanup->handler = &ngx_http_auth_ldap_connection_cleanup; From 20abfeae74dfa6f6f4683cc2bc210cd8a5090db6 Mon Sep 17 00:00:00 2001 From: Jiri Hruska Date: Fri, 30 Aug 2013 07:29:14 +0200 Subject: [PATCH 18/18] Update README.md --- README.md | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 24a8033..daed0a6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,28 @@ -# LDAP Authentication module for nginx -LDAP module for nginx which supports authentication against multiple LDAP servers. +# About this repository + +This is a fork of the original nginx LDAP HTTP authentication module with the following improvements: ++ Uses asynchronous LDAP operations through nginx's event-driven framework ++ Creates configurable number of persistent connections to each server per each worker ++ Supports configurable cache per worker process for improved performance of consecutive requests ++ Transfers only the DN when searching, not the whole entry (several KB) ++ Allows only one LDAP (bind) operation per request when the whole user DN can be composed using variables ++ Has cleaner code and debug log messages ++ Contains other minor bug fixes + +I made these changes for a project of a company I don't work for anymore and I am no longer able or willing to continue developing or maintaining the code, because I have no use for it. The project goes on (and naturally will be the next big thing!!1), but I don't know how or whether at all will my successor publish further additions or fixes. So anybody interested is welcome to fork the repository and make it into a proper stable and respected nginx module, finally. -#Warning -This module blocks whole nginx worker while communicating with ldap servers, so it can easily make "bad apache" out of your awesome nginx. But is might be useful if you don't have apache in your stack and don't want to add it, but need ldap auth on separate host (say backoffice or admin panel). +A brief TODO list, just off the top of my head: ++ Test, test, test everything and test it thoroughly. An automated test suite might be a good idea. ++ Configurable timeouts of various events, currently hardcoded ++ Some global code review after the quick and dirty development ++ Better documentation -So use carefully and consider the drawbacks. +Good luck! + +--- + +# LDAP Authentication module for nginx +LDAP module for nginx which supports authentication against multiple LDAP servers. # How to install