From 48f11c2eecae1307cbf44e89247c533e13d33f34 Mon Sep 17 00:00:00 2001 From: Pavel Brylov Date: Wed, 17 Jul 2013 19:03:16 +0300 Subject: [PATCH] Added support for multiple ldap servers, contains non-backwards compatible changes to configuration --- README.md | 49 ++ ngx_http_auth_ldap_module.c | 977 ++++++++++++++++++++---------------- 2 files changed, 598 insertions(+), 428 deletions(-) diff --git a/README.md b/README.md index 87efd48..24a8033 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +# LDAP Authentication module for nginx +LDAP module for nginx which supports authentication against multiple LDAP servers. + +#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). + +So use carefully and consider the drawbacks. + # How to install ## FreeBSD @@ -25,3 +33,44 @@ in nginx source folder ./configure --add-module=path_to_http_auth_ldap_module make install ``` + +# Example configuration +Define list of your LDAP servers with required user/group requirements: + +```bash + ldap_server test1 { + url ldap://192.168.0.1:3268/DC=test,DC=local?sAMAccountName?sub?(objectClass=person); + binddn "TEST\\LDAPUSER"; + binddn_passwd LDAPPASSWORD; + group_attribute uniquemember; + group_attribute_is_dn on; + require user_valid; + } + + ldap_server test2 { + url ldap://192.168.0.2:3268/DC=test,DC=local?sAMAccountName?sub?(objectClass=person); + binddn "TEST\\LDAPUSER"; + binddn_passwd LDAPPASSWORD; + group_attribute uniquemember; + group_attribute_is_dn on; + require user_valid; + } +``` + +And add required servers in correct order into your location/server directive: +```bash + server { + listen 8000; + server_name localhost; + + auth_ldap "Forbidden"; + auth_ldap_servers test1; + auth_ldap_servers test2; + + location / { + root html; + index index.html index.htm; + } + +} +``` diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 0754c06..f10b5d0 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -29,25 +29,21 @@ #include #include -typedef struct { - ngx_str_t passwd; -} ngx_http_auth_ldap_ctx_t; - typedef struct { ngx_str_t username; ngx_str_t password; } ngx_ldap_userinfo; typedef struct { - ngx_str_t value; - ngx_array_t *lengths; - ngx_array_t *values; + ngx_str_t value; + ngx_array_t *lengths; + ngx_array_t *values; } ngx_ldap_require_t; typedef struct { LDAPURLDesc *ludpp; ngx_str_t url; - ngx_str_t realm; + ngx_str_t alias; ngx_str_t bind_dn; ngx_str_t bind_dn_passwd; @@ -55,342 +51,458 @@ 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_ldap_require_t */ + ngx_array_t *require_user; /* array of ngx_ldap_require_t */ ngx_flag_t require_valid_user; ngx_flag_t satisfy_all; -} ngx_http_auth_ldap_loc_conf_t; +} ngx_ldap_server; -static char * ngx_http_auth_ldap_url(ngx_conf_t *, ngx_command_t *, void *); -static char * ngx_http_auth_ldap_satisfy(ngx_conf_t *, ngx_command_t *, void *); -static char * ngx_http_auth_ldap_require(ngx_conf_t *, ngx_command_t *, void *); +typedef struct { + ngx_str_t realm; + ngx_array_t *servers; +} ngx_http_auth_ldap_loc_conf_t; +typedef struct { + ngx_array_t *servers; /* array of ngx_ldap_server */ + 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_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 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); 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 *, ngx_http_auth_ldap_ctx_t *, ngx_str_t *, - ngx_http_auth_ldap_loc_conf_t *); +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); 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("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_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_ldap_loc_conf_t, realm), - &ngx_http_auth_ldap_p }, - { - ngx_string("auth_ldap_url"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, - ngx_http_auth_ldap_url, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - { - ngx_string("auth_ldap_binddn"), - 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_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_ldap_loc_conf_t, bind_dn), - NULL }, - { - ngx_string("auth_ldap_binddn_passwd"), - 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_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_ldap_loc_conf_t, bind_dn_passwd), - NULL }, - { - ngx_string("auth_ldap_require"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE12, - ngx_http_auth_ldap_require, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - { - ngx_string("auth_ldap_satisfy"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, - ngx_http_auth_ldap_satisfy, - NGX_HTTP_LOC_CONF_OFFSET, + ngx_string("ldap_server"), + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, + ngx_http_auth_ldap_ldap_server_block, + NGX_HTTP_MAIN_CONF_OFFSET, 0, - NULL }, + NULL + }, { - ngx_string("auth_ldap_group_attribute"), + 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_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_ldap_loc_conf_t, group_attribute), - NULL }, + offsetof(ngx_http_auth_ldap_loc_conf_t, realm), + &ngx_http_auth_ldap_p + }, { - ngx_string("auth_ldap_group_attribute_is_dn"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_FLAG, - ngx_conf_set_flag_slot, + 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_conf_set_str_array_slot, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_ldap_loc_conf_t, group_attribute_dn), - NULL }, + offsetof(ngx_http_auth_ldap_loc_conf_t, servers), + NULL + }, ngx_null_command }; static ngx_http_module_t ngx_http_auth_ldap_module_ctx = { - NULL, /* preconfiguration */ - ngx_http_auth_ldap_init, /* postconfiguration */ - NULL, /* create main configuration */ - NULL, /* init main configuration */ - NULL, /* create server configuration */ - NULL, /* merge server configuration */ - ngx_http_auth_basic_create_loc_conf, /* create location configuration */ - ngx_http_auth_ldap_merge_loc_conf /* merge location configuration */ + NULL, /* preconfiguration */ + ngx_http_auth_ldap_init, /* postconfiguration */ + ngx_http_auth_ldap_create_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_basic_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_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 /**/ }; + +/** + * Reads ldap_server block and sets ngx_http_auth_ldap_ldap_server as a handler of each conf value + */ static char * -ngx_http_auth_ldap_url(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_auth_ldap_loc_conf_t *alcf = conf; - ngx_str_t *value; - u_char *p; +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; value = cf->args->elts; - int rc = ldap_url_parse((const char*) value[1].data, &alcf->ludpp); - 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."); - break; + name = value[1]; - case LDAP_URL_ERR_PARAM: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid parameter."); - break; + if (ngx_strlen(name.data) == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Error: no name of ldap server specified"); + return NGX_CONF_ERROR; + } - case LDAP_URL_ERR_BADSCHEME: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: URL doesnt begin with \"ldap[s]://\"."); - break; + server.alias = name; - case LDAP_URL_ERR_BADENCLOSURE: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: URL is missing trailing \">\"."); - break; + if (cnf->servers == NULL) { + cnf->servers = ngx_array_create(cf->pool, 7, sizeof(ngx_ldap_server)); + if (cnf->servers == NULL) { + return NGX_CONF_ERROR; + } + } - case LDAP_URL_ERR_BADURL: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid URL."); - break; + s = ngx_array_push(cnf->servers); + if (s == NULL) { + return NGX_CONF_ERROR; + } - case LDAP_URL_ERR_BADHOST: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Host port is invalid."); - break; + *s = server; - case LDAP_URL_ERR_BADATTRS: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid or missing attributes."); - break; + save = *cf; + cf->handler = ngx_http_auth_ldap_ldap_server; + cf->handler_conf = conf; + rv = ngx_conf_parse(cf, NULL); + *cf = save; - case LDAP_URL_ERR_BADSCOPE: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid or missing scope string."); - break; + if (rv != NGX_CONF_OK) { + return rv; + } - case LDAP_URL_ERR_BADFILTER: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid or missing filter."); - break; + return NGX_CONF_OK; +} - case LDAP_URL_ERR_BADEXTS: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid or missing extensions."); - break; - } - return NGX_CONF_ERROR; +/** + * Called for every variable inside ldap_server block + */ +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_ldap_server *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)); + + value = cf->args->elts; + + // 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) { + server->bind_dn = value[1]; + } 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) { + server->group_attribute = value[1]; + } else if(ngx_strcmp(value[0].data, "group_attribute_is_dn") == 0 && ngx_strcmp(value[1].data, "on")) { + server->group_attribute_dn = 1; + } else if(ngx_strcmp(value[0].data, "require") == 0) { + return ngx_http_auth_ldap_parse_require(cf, server); + } else if(ngx_strcmp(value[0].data, "satisfy") == 0) { + return ngx_http_auth_ldap_parse_satisfy(cf, server); } - if (alcf->ludpp->lud_attrs == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: No attrs in auth_ldap_url."); - return NGX_CONF_ERROR; + rv = NGX_CONF_OK; + + return rv; +} + +/** + * 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; + + if (ngx_strcmp(realm->data, "off") == 0) { + realm->len = 0; + realm->data = (u_char *) ""; + + return NGX_CONF_OK; } - alcf->url.len = ngx_strlen(alcf->ludpp->lud_scheme) + ngx_strlen(alcf->ludpp->lud_host) + 11; // 11 = len("://:/") + len("65535") + len("\0") - alcf->url.data = ngx_pcalloc(cf->pool, alcf->url.len); - p = ngx_sprintf(alcf->url.data, "%s://%s:%d/", (const char*) alcf->ludpp->lud_scheme, - (const char*) alcf->ludpp->lud_host, alcf->ludpp->lud_port); - *p = 0; + len = sizeof("Basic realm=\"") - 1 + realm->len + 1; + + basic = ngx_pcalloc(cf->pool, len); + if (basic == NULL) { + return NGX_CONF_ERROR; + } + + p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1); + p = ngx_cpymem(p, realm->data, realm->len); + *p = '"'; + + realm->len = len; + realm->data = basic; return NGX_CONF_OK; } +/** + * Parse URL conf parameter + */ static char * -ngx_http_auth_ldap_satisfy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_auth_ldap_loc_conf_t *alcf = conf; +ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_ldap_server *server) { ngx_str_t *value; + u_char *p; value = cf->args->elts; - if (ngx_strcmp(value[1].data, "all") == 0) { - alcf->satisfy_all = 1; - return NGX_CONF_OK; + server->url = *value; + + int rc = ldap_url_parse((const char*) value[1].data, &server->ludpp); + 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."); + break; + + case LDAP_URL_ERR_PARAM: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "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]://\"."); + break; + + case LDAP_URL_ERR_BADENCLOSURE: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: URL is missing trailing \">\"."); + break; + + case LDAP_URL_ERR_BADURL: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid URL."); + break; + + case LDAP_URL_ERR_BADHOST: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "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."); + break; + + case LDAP_URL_ERR_BADSCOPE: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "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."); + break; + + case LDAP_URL_ERR_BADEXTS: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: Invalid or missing extensions."); + break; + } + return NGX_CONF_ERROR; } - if (ngx_strcmp(value[1].data, "any") == 0) { - alcf->satisfy_all = 0; - return NGX_CONF_OK; + if (server->ludpp->lud_attrs == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "LDAP: No attrs in auth_ldap_url."); + return NGX_CONF_ERROR; } - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Incorrect value for auth_ldap_satisfy "); - 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; + + return NGX_CONF_OK; } +/** + * Parse "require" conf parameter + */ static char * -ngx_http_auth_ldap_require(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { +ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_ldap_server *server) { ngx_http_script_compile_t sc; - ngx_http_auth_ldap_loc_conf_t *alcf = conf; ngx_str_t *value; value = cf->args->elts; - if (alcf->require_user == NULL) { - alcf->require_user = ngx_array_create(cf->pool, 4, sizeof(ngx_ldap_require_t)); - if (alcf->require_user == NULL) { - return NGX_CONF_ERROR; - } + if (server->require_user == NULL) { + server->require_user = ngx_array_create(cf->pool, 4, sizeof(ngx_ldap_require_t)); + if (server->require_user == NULL) { + return NGX_CONF_ERROR; + } } - if (alcf->require_group == NULL) { - alcf->require_group = ngx_array_create(cf->pool, 4, sizeof(ngx_ldap_require_t)); - if (alcf->require_group == NULL) { - return NGX_CONF_ERROR; - } + if (server->require_group == NULL) { + server->require_group = ngx_array_create(cf->pool, 4, sizeof(ngx_ldap_require_t)); + if (server->require_group == NULL) { + return NGX_CONF_ERROR; + } } if (ngx_strcmp(value[1].data, "valid_user") == 0) { - alcf->require_valid_user = 1; + server->require_valid_user = 1; } 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(alcf->require_user); - } - - if (ngx_strcmp(value[1].data, "group") == 0) { - rule = ngx_array_push(alcf->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_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; + } + } } return NGX_CONF_OK; } +/** + * Parse "satisfy" conf parameter + */ +static char * +ngx_http_auth_ldap_parse_satisfy(ngx_conf_t *cf, ngx_ldap_server *server) { + ngx_str_t *value; + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "all") == 0) { + server->satisfy_all = 1; + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[1].data, "any") == 0) { + server->satisfy_all = 0; + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Incorrect value for auth_ldap_satisfy "); + return NGX_CONF_ERROR; +} + +/** + * 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_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_ldap_conf_t)); + if (conf == NULL) { + return NULL; + } + + return conf; +} + +/** + * Create location conf + */ static void * ngx_http_auth_basic_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; + return NULL; } - conf->satisfy_all = NGX_CONF_UNSET; - conf->require_valid_user = NGX_CONF_UNSET; - conf->group_attribute_dn = NGX_CONF_UNSET; + 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_str_value(conf->url, prev->url, "ldap://localhost/"); - ngx_conf_merge_str_value(conf->bind_dn, prev->bind_dn, ""); - ngx_conf_merge_str_value(conf->bind_dn_passwd, prev->bind_dn_passwd, ""); - ngx_conf_merge_str_value(conf->group_attribute, prev->group_attribute, "member"); - - ngx_conf_merge_value(conf->require_valid_user, prev->require_valid_user, 0); - ngx_conf_merge_value(conf->satisfy_all, prev->satisfy_all, 0); - ngx_conf_merge_value(conf->group_attribute_dn, prev->group_attribute_dn, 1); - - if (conf->require_user == NULL) { - conf->require_user = prev->require_user; - } - - if (conf->require_group == NULL) { - conf->require_group = prev->require_group; - } - - if (conf->ludpp == NULL) { - conf->ludpp = prev->ludpp; + conf->realm = prev->realm; } + ngx_conf_merge_ptr_value(conf->servers, prev->servers, NULL); return NGX_CONF_OK; } +/** + * LDAP Authentication handler + */ static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) { int rc; - ngx_http_auth_ldap_ctx_t *ctx; 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; + return NGX_DECLINED; } - ctx = ngx_http_get_module_ctx(r, ngx_http_auth_ldap_module); + ngx_http_auth_ldap_conf_t *cnf; - if (ctx) { - return ngx_http_auth_ldap_authenticate(r, ctx, &ctx->passwd, alcf); - } + 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); + return ngx_http_auth_ldap_set_realm(r, &alcf->realm); } if (rc == NGX_ERROR) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_HTTP_INTERNAL_SERVER_ERROR; } - return ngx_http_auth_ldap_authenticate(r, ctx, &ctx->passwd, alcf); + return ngx_http_auth_ldap_authenticate(r, alcf, cnf); } /** @@ -405,14 +517,14 @@ ngx_http_auth_ldap_get_user_info(ngx_http_request_t *r) { 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; - } + if (r->headers_in.user.data[len] == ':') { + break; + } } uname_buf = ngx_palloc(r->pool, len + 1); if (uname_buf == NULL) { - return NULL; + return NULL; } p = ngx_cpymem(uname_buf, r->headers_in.user.data, len); *p = '\0'; @@ -425,40 +537,35 @@ ngx_http_auth_ldap_get_user_info(ngx_http_request_t *r) { return uinfo; } -static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, - ngx_str_t *passwd, ngx_http_auth_ldap_loc_conf_t *conf) { +/** + * 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) { + + ngx_ldap_server *server, *servers; + servers = mconf->servers->elts; + int rc; + ngx_uint_t i, k; + ngx_str_t *alias; - LDAP *ld; - LDAPMessage *searchResult; - LDAPURLDesc *ludpp = conf->ludpp; int version = LDAP_VERSION3; - struct berval bvalue; - struct timeval timeOut = { 10, 0 }; int reqcert = LDAP_OPT_X_TLS_ALLOW; - - int rc; - ngx_uint_t i; - ngx_ldap_require_t *value; ngx_ldap_userinfo *uinfo; - + struct timeval timeOut = { 10, 0 }; ngx_flag_t pass = NGX_CONF_UNSET; - char *dn; - u_char *p, *filter; - - if (conf->ludpp == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - 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; + return NGX_HTTP_INTERNAL_SERVER_ERROR; } - if (uinfo->password.len == 0) - { - return ngx_http_auth_ldap_set_realm(r, &conf->realm); - } + if (uinfo->password.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); @@ -466,186 +573,228 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http 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", - ldap_err2string(rc)); + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "LDAP: unable to set require cert option: %s", + 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, uinfo, 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); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: URL: %s", conf->url.data); + return ngx_http_auth_ldap_set_realm(r, &conf->realm); +} - rc = ldap_initialize(&ld, (const char*) conf->url.data); +/** + * 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) { + LDAPURLDesc *ludpp = server->ludpp; + int rc; + LDAP *ld; + LDAPMessage *searchResult; + char *dn; + u_char *p, *filter; + ngx_ldap_require_t *value; + ngx_uint_t i; + struct berval bvalue; + ngx_flag_t pass = NGX_CONF_UNSET; + struct timeval timeOut = { 10, 0 }; + + if (server->ludpp == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: URL: %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, - ldap_err2string(rc), (const char*) conf->url.data); - return NGX_HTTP_INTERNAL_SERVER_ERROR; + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "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); /// Bind to the server - rc = ldap_simple_bind_s(ld, (const char *) conf->bind_dn.data, (const char *) conf->bind_dn_passwd.data); + 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: ldap_simple_bind_s error: %d, %s", rc, - ldap_err2string(rc)); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; + 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 + return 0; } 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->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); + p = ngx_sprintf(filter, "(&%s(%s=%s))", ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", ludpp->lud_attrs[0], uinfo->username.data); *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); + &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; + 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; } 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 (conf->require_user != NULL) { - value = conf->require_user->elts; - for (i = 0; i < conf->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'; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: compare with: %s", val.data); - if (ngx_strncmp(val.data, dn, val.len) == 0) { - pass = 1; - if (conf->satisfy_all == 0) { - break; - } - } else { - if (conf->satisfy_all == 1) { - ldap_memfree(dn); - ldap_msgfree(searchResult); - ldap_unbind_s(ld); - return ngx_http_auth_ldap_set_realm(r, &conf->realm); - } - } - } - } - - /// Check require group - if (conf->require_group != NULL) { - if (conf->group_attribute_dn == 1) { - bvalue.bv_val = dn; - bvalue.bv_len = ngx_strlen(dn); - } else { - bvalue.bv_val = (char*) uinfo->username.data; - bvalue.bv_len = uinfo->username.len; - } - - value = conf->require_group->elts; - - for (i = 0; i < conf->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'; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: group compare with: %s", val.data); - - rc = ldap_compare_ext_s(ld, (const char*) val.data, (const char*) conf->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(dn); - ldap_msgfree(searchResult); - ldap_unbind_s(ld); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - }*/ - - if (rc == LDAP_COMPARE_TRUE) { - pass = 1; - if (conf->satisfy_all == 0) { - break; - } - } else { - if (conf->satisfy_all == 1) { - pass = 0; - break; - } - } - } - } - - /// Check valid user - if ( pass != 0 || (conf->require_valid_user == 1 && conf->satisfy_all == 0 && pass == 0)) { - /// Bind user to the server - rc = ldap_simple_bind_s(ld, dn, (const char *) uinfo->password.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 (conf->require_valid_user == 1) - pass = 1; - } - } - - } - ldap_memfree(dn); + 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 (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'; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: compare with: %s", val.data); + 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; + } + } + } + } + + /// 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*) uinfo->username.data; + bvalue.bv_len = uinfo->username.len; + } + + value = server->require_group->elts; + + 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'; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: group compare with: %s", val.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 ) { + 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; + } + } + } + } + + /// 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); + 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_unbind_s(ld); - if (pass == 1) { - return NGX_OK; - } - - return ngx_http_auth_ldap_set_realm(r, &conf->realm); + 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; + return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->headers_out.www_authenticate->hash = 1; @@ -656,37 +805,9 @@ static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t * return NGX_HTTP_UNAUTHORIZED; } -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; - - if (ngx_strcmp(realm->data, "off") == 0) { - realm->len = 0; - realm->data = (u_char *) ""; - - return NGX_CONF_OK; - } - - len = sizeof("Basic realm=\"") - 1 + realm->len + 1; - - basic = ngx_pcalloc(cf->pool, len); - if (basic == NULL) { - return NGX_CONF_ERROR; - } - - p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1); - p = ngx_cpymem(p, realm->data, realm->len); - *p = '"'; - - realm->len = len; - realm->data = basic; - - return NGX_CONF_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; @@ -695,7 +816,7 @@ static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf) { h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); if (h == NULL) { - return NGX_ERROR; + return NGX_ERROR; } *h = ngx_http_auth_ldap_handler;