Merge pull request #12 from pavelbrylov/master

Added support for multiple ldap servers, contains non-backwards compatible changes to configuration
main
Valery 12 years ago
commit 476e6bf981
  1. 49
      README.md
  2. 559
      ngx_http_auth_ldap_module.c

@ -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 # How to install
## FreeBSD ## FreeBSD
@ -25,3 +33,44 @@ in nginx source folder
./configure --add-module=path_to_http_auth_ldap_module ./configure --add-module=path_to_http_auth_ldap_module
make install 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;
}
}
```

@ -29,10 +29,6 @@
#include <ngx_http.h> #include <ngx_http.h>
#include <ldap.h> #include <ldap.h>
typedef struct {
ngx_str_t passwd;
} ngx_http_auth_ldap_ctx_t;
typedef struct { typedef struct {
ngx_str_t username; ngx_str_t username;
ngx_str_t password; ngx_str_t password;
@ -47,7 +43,7 @@ typedef struct {
typedef struct { typedef struct {
LDAPURLDesc *ludpp; LDAPURLDesc *ludpp;
ngx_str_t url; ngx_str_t url;
ngx_str_t realm; ngx_str_t alias;
ngx_str_t bind_dn; ngx_str_t bind_dn;
ngx_str_t bind_dn_passwd; ngx_str_t bind_dn_passwd;
@ -59,89 +55,73 @@ typedef struct {
ngx_array_t *require_user; /* 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 require_valid_user;
ngx_flag_t satisfy_all; 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 *); typedef struct {
static char * ngx_http_auth_ldap_satisfy(ngx_conf_t *, ngx_command_t *, void *); ngx_str_t realm;
static char * ngx_http_auth_ldap_require(ngx_conf_t *, ngx_command_t *, void *); 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 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_basic_create_loc_conf(ngx_conf_t *);
static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *); 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_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_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 *, 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_loc_conf_t *); ngx_http_auth_ldap_conf_t *mconf);
static char * ngx_http_auth_ldap(ngx_conf_t *cf, void *post, void *data); 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_conf_post_handler_pt ngx_http_auth_ldap_p = ngx_http_auth_ldap;
static ngx_command_t ngx_http_auth_ldap_commands[] = { static ngx_command_t ngx_http_auth_ldap_commands[] = {
{ {
ngx_string("auth_ldap"), ngx_string("ldap_server"),
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
ngx_conf_set_str_slot, ngx_http_auth_ldap_ldap_server_block,
NGX_HTTP_LOC_CONF_OFFSET, NGX_HTTP_MAIN_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, 0,
NULL }, NULL
},
{ {
ngx_string("auth_ldap_binddn"), 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, 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,
0,
NULL },
{
ngx_string("auth_ldap_group_attribute"),
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, 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_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET, NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_auth_ldap_loc_conf_t, group_attribute), offsetof(ngx_http_auth_ldap_loc_conf_t, realm),
NULL }, &ngx_http_auth_ldap_p
},
{ {
ngx_string("auth_ldap_group_attribute_is_dn"), ngx_string("auth_ldap_servers"),
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_FLAG, 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_flag_slot, ngx_conf_set_str_array_slot,
NGX_HTTP_LOC_CONF_OFFSET, NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_auth_ldap_loc_conf_t, group_attribute_dn), offsetof(ngx_http_auth_ldap_loc_conf_t, servers),
NULL }, NULL
},
ngx_null_command ngx_null_command
}; };
static ngx_http_module_t ngx_http_auth_ldap_module_ctx = { static ngx_http_module_t ngx_http_auth_ldap_module_ctx = {
NULL, /* preconfiguration */ NULL, /* preconfiguration */
ngx_http_auth_ldap_init, /* postconfiguration */ ngx_http_auth_ldap_init, /* postconfiguration */
NULL, /* create main configuration */ ngx_http_auth_ldap_create_conf, /* create main configuration */
NULL, /* init main configuration */ NULL, /* init main configuration */
NULL, /* create server configuration */ NULL, //ngx_http_auth_ldap_create_server_conf, /* create server configuration */
NULL, /* merge 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_basic_create_loc_conf, /* create location configuration */
ngx_http_auth_ldap_merge_loc_conf /* merge location configuration */ ngx_http_auth_ldap_merge_loc_conf /* merge location configuration */
}; };
@ -161,15 +141,142 @@ ngx_module_t ngx_http_auth_ldap_module = {
NGX_MODULE_V1_PADDING /**/ 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_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;
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");
return NGX_CONF_ERROR;
}
server.alias = name;
if (cnf->servers == NULL) {
cnf->servers = ngx_array_create(cf->pool, 7, sizeof(ngx_ldap_server));
if (cnf->servers == NULL) {
return NGX_CONF_ERROR;
}
}
s = ngx_array_push(cnf->servers);
if (s == NULL) {
return NGX_CONF_ERROR;
}
*s = server;
save = *cf;
cf->handler = ngx_http_auth_ldap_ldap_server;
cf->handler_conf = conf;
rv = ngx_conf_parse(cf, NULL);
*cf = save;
if (rv != NGX_CONF_OK) {
return rv;
}
return NGX_CONF_OK;
}
/**
* Called for every variable inside ldap_server block
*/
static char * static char *
ngx_http_auth_ldap_url(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
ngx_http_auth_ldap_loc_conf_t *alcf = conf; {
char *rv;
ngx_str_t *value; ngx_str_t *value;
u_char *p;
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);
}
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;
}
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_parse_url(ngx_conf_t *cf, ngx_ldap_server *server) {
ngx_str_t *value;
u_char *p;
value = cf->args->elts; value = cf->args->elts;
int rc = ldap_url_parse((const char*) value[1].data, &alcf->ludpp); server->url = *value;
int rc = ldap_url_parse((const char*) value[1].data, &server->ludpp);
if (rc != LDAP_SUCCESS) { if (rc != LDAP_SUCCESS) {
switch (rc) { switch (rc) {
case LDAP_URL_ERR_MEM: case LDAP_URL_ERR_MEM:
@ -215,65 +322,47 @@ ngx_http_auth_ldap_url(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
if (alcf->ludpp->lud_attrs == NULL) { 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, "LDAP: No attrs in auth_ldap_url.");
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
alcf->url.len = ngx_strlen(alcf->ludpp->lud_scheme) + ngx_strlen(alcf->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")
alcf->url.data = ngx_pcalloc(cf->pool, alcf->url.len); server->url.data = ngx_pcalloc(cf->pool, server->url.len);
p = ngx_sprintf(alcf->url.data, "%s://%s:%d/", (const char*) alcf->ludpp->lud_scheme, p = ngx_sprintf(server->url.data, "%s://%s:%d/", (const char*) server->ludpp->lud_scheme,
(const char*) alcf->ludpp->lud_host, alcf->ludpp->lud_port); (const char*) server->ludpp->lud_host, server->ludpp->lud_port);
*p = 0; *p = 0;
return NGX_CONF_OK; return NGX_CONF_OK;
} }
/**
* Parse "require" conf parameter
*/
static char * static char *
ngx_http_auth_ldap_satisfy(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_auth_ldap_loc_conf_t *alcf = conf;
ngx_str_t *value;
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "all") == 0) {
alcf->satisfy_all = 1;
return NGX_CONF_OK;
}
if (ngx_strcmp(value[1].data, "any") == 0) {
alcf->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;
}
static char *
ngx_http_auth_ldap_require(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
ngx_http_script_compile_t sc; ngx_http_script_compile_t sc;
ngx_http_auth_ldap_loc_conf_t *alcf = conf;
ngx_str_t *value; ngx_str_t *value;
value = cf->args->elts; value = cf->args->elts;
if (alcf->require_user == NULL) { if (server->require_user == NULL) {
alcf->require_user = ngx_array_create(cf->pool, 4, sizeof(ngx_ldap_require_t)); server->require_user = ngx_array_create(cf->pool, 4, sizeof(ngx_ldap_require_t));
if (alcf->require_user == NULL) { if (server->require_user == NULL) {
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
} }
if (alcf->require_group == NULL) { if (server->require_group == NULL) {
alcf->require_group = ngx_array_create(cf->pool, 4, sizeof(ngx_ldap_require_t)); server->require_group = ngx_array_create(cf->pool, 4, sizeof(ngx_ldap_require_t));
if (alcf->require_group == NULL) { if (server->require_group == NULL) {
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
} }
if (ngx_strcmp(value[1].data, "valid_user") == 0) { 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) if (ngx_strcmp(value[1].data, "user") == 0 || ngx_strcmp(value[1].data, "group") == 0)
@ -282,11 +371,11 @@ ngx_http_auth_ldap_require(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
ngx_ldap_require_t *rule = NULL; ngx_ldap_require_t *rule = NULL;
if (ngx_strcmp(value[1].data, "user") == 0) { if (ngx_strcmp(value[1].data, "user") == 0) {
rule = ngx_array_push(alcf->require_user); rule = ngx_array_push(server->require_user);
} }
if (ngx_strcmp(value[1].data, "group") == 0) { if (ngx_strcmp(value[1].data, "group") == 0) {
rule = ngx_array_push(alcf->require_group); rule = ngx_array_push(server->require_group);
} }
if (rule == NULL) { if (rule == NULL) {
@ -317,6 +406,47 @@ ngx_http_auth_ldap_require(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
return NGX_CONF_OK; 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 * static void *
ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf) { ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf) {
ngx_http_auth_ldap_loc_conf_t *conf; ngx_http_auth_ldap_loc_conf_t *conf;
@ -324,12 +454,14 @@ ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf) {
if (conf == NULL) { if (conf == NULL) {
return NULL; return NULL;
} }
conf->satisfy_all = NGX_CONF_UNSET; conf->servers = NGX_CONF_UNSET_PTR;
conf->require_valid_user = NGX_CONF_UNSET;
conf->group_attribute_dn = NGX_CONF_UNSET;
return conf; return conf;
} }
/**
* Merge location conf
*/
static char * 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 *prev = parent;
@ -338,34 +470,16 @@ ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) {
if (conf->realm.data == NULL) { if (conf->realm.data == NULL) {
conf->realm = prev->realm; conf->realm = prev->realm;
} }
ngx_conf_merge_ptr_value(conf->servers, prev->servers, NULL);
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;
}
return NGX_CONF_OK; return NGX_CONF_OK;
} }
/**
* 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) {
int rc; int rc;
ngx_http_auth_ldap_ctx_t *ctx;
ngx_http_auth_ldap_loc_conf_t *alcf; ngx_http_auth_ldap_loc_conf_t *alcf;
alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_ldap_module); alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_ldap_module);
@ -374,11 +488,9 @@ static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) {
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) { cnf = ngx_http_get_module_main_conf(r, ngx_http_auth_ldap_module);
return ngx_http_auth_ldap_authenticate(r, ctx, &ctx->passwd, alcf);
}
rc = ngx_http_auth_basic_user(r); rc = ngx_http_auth_basic_user(r);
@ -390,7 +502,7 @@ static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) {
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);
} }
/** /**
@ -425,32 +537,27 @@ ngx_http_auth_ldap_get_user_info(ngx_http_request_t *r) {
return uinfo; 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; int version = LDAP_VERSION3;
struct berval bvalue;
struct timeval timeOut = { 10, 0 };
int reqcert = LDAP_OPT_X_TLS_ALLOW; int reqcert = LDAP_OPT_X_TLS_ALLOW;
int rc;
ngx_uint_t i;
ngx_ldap_require_t *value;
ngx_ldap_userinfo *uinfo; ngx_ldap_userinfo *uinfo;
struct timeval timeOut = { 10, 0 };
ngx_flag_t pass = NGX_CONF_UNSET; 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); 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) { if (uinfo == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR; return NGX_HTTP_INTERNAL_SERVER_ERROR;
} }
@ -470,23 +577,72 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
ldap_err2string(rc)); ldap_err2string(rc));
} }
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: URL: %s", conf->url.data); // 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;
}
}
return ngx_http_auth_ldap_set_realm(r, &conf->realm);
}
/**
* 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*) conf->url.data); rc = ldap_initialize(&ld, (const char*) server->url.data);
if (rc != LDAP_SUCCESS) { 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, "LDAP: Session initializing failed: %d, %s, (%s)", rc,
ldap_err2string(rc), (const char*) conf->url.data); ldap_err2string(rc), (const char*) server->url.data);
return NGX_HTTP_INTERNAL_SERVER_ERROR; 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, "LDAP: Session initialized", NULL);
/// Bind to the server /// 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) { 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, "LDAP [%s]: ldap_simple_bind_s error: %d, %s", server->url.data, rc,
ldap_err2string(rc)); ldap_err2string(rc));
ldap_unbind_s(ld); ldap_unbind_s(ld);
return NGX_HTTP_INTERNAL_SERVER_ERROR; // 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, "LDAP: Bind successful", NULL);
@ -495,6 +651,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
r->pool, r->pool,
(ludpp->lud_filter != NULL ? ngx_strlen(ludpp->lud_filter) : ngx_strlen("(objectClass=*)")) + ngx_strlen("(&(=))") + ngx_strlen(ludpp->lud_attrs[0]) (ludpp->lud_filter != NULL ? ngx_strlen(ludpp->lud_filter) : ngx_strlen("(objectClass=*)")) + ngx_strlen("(&(=))") + ngx_strlen(ludpp->lud_attrs[0])
+ uinfo->username.len + 1); + 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 = ngx_sprintf(filter, "(&%s(%s=%s))", ludpp->lud_filter != NULL ? ludpp->lud_filter : "(objectClass=*)", ludpp->lud_attrs[0], uinfo->username.data);
*p = 0; *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, "LDAP: filter %s", (const char*) filter);
@ -516,16 +673,13 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: result DN %s", dn); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: result DN %s", dn);
/// Check require user /// Check require user
if (conf->require_user != NULL) { if (server->require_user != NULL) {
value = conf->require_user->elts; value = server->require_user->elts;
for (i = 0; i < conf->require_user->nelts; i++) { for (i = 0; i < server->require_user->nelts; i++) {
ngx_str_t val; ngx_str_t val;
if (value[i].lengths == NULL) if (value[i].lengths == NULL) {
{
val = value[i].value; val = value[i].value;
} } else {
else
{
if (ngx_http_script_run(r, &val, value[i].lengths->elts, 0, if (ngx_http_script_run(r, &val, value[i].lengths->elts, 0,
value[i].values->elts) == NULL) value[i].values->elts) == NULL)
{ {
@ -540,23 +694,23 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
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: %s", val.data);
if (ngx_strncmp(val.data, dn, val.len) == 0) { if (ngx_strncmp(val.data, dn, val.len) == 0) {
pass = 1; pass = 1;
if (conf->satisfy_all == 0) { if (server->satisfy_all == 0) {
break; break;
} }
} else { } else {
if (conf->satisfy_all == 1) { if (server->satisfy_all == 1) {
ldap_memfree(dn); ldap_memfree(dn);
ldap_msgfree(searchResult); ldap_msgfree(searchResult);
ldap_unbind_s(ld); ldap_unbind_s(ld);
return ngx_http_auth_ldap_set_realm(r, &conf->realm); return 0;
} }
} }
} }
} }
/// Check require group /// Check require group
if (conf->require_group != NULL) { if (server->require_group != NULL) {
if (conf->group_attribute_dn == 1) { if (server->group_attribute_dn == 1) {
bvalue.bv_val = dn; bvalue.bv_val = dn;
bvalue.bv_len = ngx_strlen(dn); bvalue.bv_len = ngx_strlen(dn);
} else { } else {
@ -564,16 +718,13 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
bvalue.bv_len = uinfo->username.len; bvalue.bv_len = uinfo->username.len;
} }
value = conf->require_group->elts; value = server->require_group->elts;
for (i = 0; i < conf->require_group->nelts; i++) { for (i = 0; i < server->require_group->nelts; i++) {
ngx_str_t val; ngx_str_t val;
if (value[i].lengths == NULL) if (value[i].lengths == NULL) {
{
val = value[i].value; val = value[i].value;
} } else {
else
{
if (ngx_http_script_run(r, &val, value[i].lengths->elts, 0, if (ngx_http_script_run(r, &val, value[i].lengths->elts, 0,
value[i].values->elts) == NULL) value[i].values->elts) == NULL)
{ {
@ -587,7 +738,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
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: %s", val.data);
rc = ldap_compare_ext_s(ld, (const char*) val.data, (const char*) conf->group_attribute.data, rc = ldap_compare_ext_s(ld, (const char*) val.data, (const char*) server->group_attribute.data,
&bvalue, NULL, NULL); &bvalue, NULL, NULL);
/*if (rc != LDAP_COMPARE_TRUE && rc != LDAP_COMPARE_FALSE && rc != LDAP_NO_SUCH_ATTRIBUTE ) { /*if (rc != LDAP_COMPARE_TRUE && rc != LDAP_COMPARE_FALSE && rc != LDAP_NO_SUCH_ATTRIBUTE ) {
@ -601,11 +752,11 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
if (rc == LDAP_COMPARE_TRUE) { if (rc == LDAP_COMPARE_TRUE) {
pass = 1; pass = 1;
if (conf->satisfy_all == 0) { if (server->satisfy_all == 0) {
break; break;
} }
} else { } else {
if (conf->satisfy_all == 1) { if (server->satisfy_all == 1) {
pass = 0; pass = 0;
break; break;
} }
@ -614,7 +765,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
} }
/// Check valid user /// Check valid user
if ( pass != 0 || (conf->require_valid_user == 1 && conf->satisfy_all == 0 && pass == 0)) { 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, dn, (const char *) uinfo->password.data); rc = ldap_simple_bind_s(ld, dn, (const char *) uinfo->password.data);
if (rc != LDAP_SUCCESS) { if (rc != LDAP_SUCCESS) {
@ -623,8 +774,7 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
pass = 0; pass = 0;
} else { } 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, "LDAP: User bind successful", NULL);
if (conf->require_valid_user == 1) if (server->require_valid_user == 1) pass = 1;
pass = 1;
} }
} }
@ -635,13 +785,12 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
ldap_msgfree(searchResult); ldap_msgfree(searchResult);
ldap_unbind_s(ld); ldap_unbind_s(ld);
if (pass == 1) { return pass;
return NGX_OK;
}
return ngx_http_auth_ldap_set_realm(r, &conf->realm);
} }
/**
* 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) { 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); r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
if (r->headers_out.www_authenticate == NULL) { if (r->headers_out.www_authenticate == NULL) {
@ -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; return NGX_HTTP_UNAUTHORIZED;
} }
static char * /**
ngx_http_auth_ldap(ngx_conf_t *cf, void *post, void *data) { * Init module and add ldap auth handler to NGX_HTTP_ACCESS_PHASE
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;
}
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_handler_pt *h;
ngx_http_core_main_conf_t *cmcf; ngx_http_core_main_conf_t *cmcf;

Loading…
Cancel
Save