Project

General

Profile

Feature #1248 » lighttpd.ldap.userdn.patch

Resolution for the enhancement. In addition, I have changed the hack limiting the username to alphanums, and instead correctly escape unallowed characters in the username (permitting usernames like user@company.com ) -- douglas - Anonymous, 2007-06-27 14:50

View differences:

lighttpd-1.4.15.patched/src/http_auth.c 2007-06-27 17:17:51.000000000 +0200
#ifdef USE_LDAP
LDAP *ldap;
LDAPMessage *lm, *first;
char *dn;
const char *dn;
int ret;
char *attrs[] = { LDAP_NO_ATTRS, NULL };
size_t i;
const char *username_token = username->ptr;
unsigned int token_length = 0;
/* If we don't allow empty passwords, and this one is empty, there's
* no point in continuing. */
if (p->conf.auth_ldap_allow_empty_pw != 1 && pw[0] == '\0')
return -1;
/* for now we stay synchronous */
/*
* 1. connect anonymously (done in plugin init)
* 2. get DN for uid = username
* 3. auth against ldap server
* 4. (optional) check a field
* 5. disconnect
* 1. If we need to find the user DN
* a. connect anonymously (or using configured bind) (done in plugin init)
* b. get DN for uid = username
* 2. auth against ldap server
* 3. disconnect
*
*/
/* check username
*
* we have to protect us againt username which modifies out filter in
* a unpleasant way
* we have to protect us againt usernames which modify our filter or
* user-dn in an unpleasant way. Filters need to have the following
* characters escaped: ['\\', '*', '(', ')']
* Custom DNs are tougher, because the escape list is longer, and
* there are specialized rules for characters at the beginning and
* end of the value. Escaped: [',', '+', '"', '\\', '<', '>', ';']
* Also at the beginning of the string: [' ', '#']
* At the end of the string: [' ']
*/
for (i = 0; i < username->used - 1; i++) {
char c = username->ptr[i];
buffer_copy_string(p->ldap_username, "");
if (p->conf.auth_ldap_userdn->used) {
/* User-DN escape rules */
for (i = 0; i < username->used - 1; ++i) {
char c = username->ptr[i];
char s[3] = {'\\',c,0};
if ((0 == i) &&
((' ' == c) ||
('#' == c))) {
buffer_copy_string(p->ldap_username, s);
username_token = username->ptr + i + 1;
} else if (((username->used - 2) == i) &&
(' ' == c)) {
if (token_length) {
buffer_append_string_len(p->ldap_username, username_token, token_length);
token_length = 0;
}
buffer_append_string(p->ldap_username, s);
username_token = username->ptr + i + 1;
} else if ((',' != c) &&
('+' != c) &&
('"' != c) &&
('\\' != c) &&
('<' != c) &&
('>' != c) &&
(';' != c)) {
if (!isalpha(c) &&
!isdigit(c)) {
++token_length;
} else {
log_error_write(srv, __FILE__, __LINE__, "sbd",
"ldap: invalid character (a-zA-Z0-9 allowed) in username:", username, i);
if (token_length) {
buffer_append_string_len(p->ldap_username, username_token, token_length);
token_length = 0;
}
buffer_append_string(p->ldap_username, s);
username_token = username->ptr + i + 1;
}
}
} else {
/* LDAP filter escape rules */
for (i = 0; i < username->used - 1; ++i) {
char c = username->ptr[i];
char s[3] = {'\\',c,0};
if (('\\' != c) &&
('*' != c) &&
('(' != c) &&
(')' != c)) {
return -1;
++token_length;
} else {
if (token_length) {
buffer_append_string_len(p->ldap_username, username_token, token_length);
token_length = 0;
}
buffer_append_string(p->ldap_username, s);
username_token = username->ptr + i + 1;
}
}
}
if (p->conf.auth_ldap_allow_empty_pw != 1 && pw[0] == '\0')
return -1;
/* If there's a token we never added to the buffer */
if (token_length) {
buffer_append_string_len(p->ldap_username, username_token, token_length);
token_length = 0;
}
/* build filter */
buffer_copy_string_buffer(p->ldap_filter, p->conf.ldap_filter_pre);
buffer_append_string_buffer(p->ldap_filter, username);
buffer_append_string_buffer(p->ldap_filter, p->conf.ldap_filter_post);
if (p->conf.auth_ldap_userdn->used) {
/* build userdn */
buffer_copy_string_buffer(p->ldap_userdn, p->conf.ldap_userdn_pre);
buffer_append_string_buffer(p->ldap_userdn, p->ldap_username);
buffer_append_string_buffer(p->ldap_userdn, p->conf.ldap_userdn_post);
dn = p->ldap_userdn->ptr;
} else {
/* build filter */
buffer_copy_string_buffer(p->ldap_filter, p->conf.ldap_filter_pre);
buffer_append_string_buffer(p->ldap_filter, p->ldap_username);
buffer_append_string_buffer(p->ldap_filter, p->conf.ldap_filter_post);
/* 1b. */
if (p->conf.ldap == NULL ||
LDAP_SUCCESS != (ret = ldap_search_s(p->conf.ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) {
if (auth_ldap_init(srv, &p->conf) != HANDLER_GO_ON)
return -1;
if (LDAP_SUCCESS != (ret = ldap_search_s(p->conf.ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) {
log_error_write(srv, __FILE__, __LINE__, "sssb",
"ldap:", ldap_err2string(ret), "filter:", p->ldap_filter);
/* 2. */
if (p->conf.ldap == NULL ||
LDAP_SUCCESS != (ret = ldap_search_s(p->conf.ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) {
if (auth_ldap_init(srv, &p->conf) != HANDLER_GO_ON)
return -1;
if (LDAP_SUCCESS != (ret = ldap_search_s(p->conf.ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) {
log_error_write(srv, __FILE__, __LINE__, "sssb",
"ldap:", ldap_err2string(ret), "filter:", p->ldap_filter);
return -1;
}
}
}
if (NULL == (first = ldap_first_entry(p->conf.ldap, lm))) {
log_error_write(srv, __FILE__, __LINE__, "s", "ldap ...");
if (NULL == (first = ldap_first_entry(p->conf.ldap, lm))) {
log_error_write(srv, __FILE__, __LINE__, "s", "ldap ...");
ldap_msgfree(lm);
ldap_msgfree(lm);
return -1;
}
return -1;
}
if (NULL == (dn = ldap_get_dn(p->conf.ldap, first))) {
log_error_write(srv, __FILE__, __LINE__, "s", "ldap ...");
if (NULL == (dn = ldap_get_dn(p->conf.ldap, first))) {
log_error_write(srv, __FILE__, __LINE__, "s", "ldap ...");
ldap_msgfree(lm);
ldap_msgfree(lm);
return -1;
}
return -1;
}
ldap_msgfree(lm);
ldap_msgfree(lm);
}
/* 3. */
/* 2. */
if (NULL == (ldap = ldap_init(p->conf.auth_ldap_hostname->ptr, LDAP_PORT))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno));
return -1;
......
return -1;
}
/* 5. */
/* 3. */
ldap_unbind_s(ldap);
/* everything worked, good, access granted */
lighttpd-1.4.15.patched/src/http_auth.h 2007-06-27 14:39:21.000000000 +0200
buffer *auth_ldap_basedn;
buffer *auth_ldap_binddn;
buffer *auth_ldap_bindpw;
buffer *auth_ldap_userdn;
buffer *auth_ldap_filter;
buffer *auth_ldap_cafile;
unsigned short auth_ldap_starttls;
......
buffer *ldap_filter_pre;
buffer *ldap_filter_post;
buffer *ldap_userdn_pre;
buffer *ldap_userdn_post;
#endif
} mod_auth_plugin_config;
......
#ifdef USE_LDAP
buffer *ldap_filter;
buffer *ldap_username;
buffer *ldap_userdn;
#endif
mod_auth_plugin_config **config_storage;
lighttpd-1.4.15.patched/src/mod_auth.c 2007-06-27 16:56:46.000000000 +0200
p->auth_user = buffer_init();
#ifdef USE_LDAP
p->ldap_filter = buffer_init();
p->ldap_username = buffer_init();
p->ldap_userdn = buffer_init();
#endif
return p;
......
buffer_free(p->auth_user);
#ifdef USE_LDAP
buffer_free(p->ldap_filter);
buffer_free(p->ldap_username);
buffer_free(p->ldap_userdn);
#endif
if (p->config_storage) {
......
buffer_free(s->auth_ldap_basedn);
buffer_free(s->auth_ldap_binddn);
buffer_free(s->auth_ldap_bindpw);
buffer_free(s->auth_ldap_userdn);
buffer_free(s->auth_ldap_filter);
buffer_free(s->auth_ldap_cafile);
#ifdef USE_LDAP
buffer_free(s->ldap_filter_pre);
buffer_free(s->ldap_filter_post);
buffer_free(s->ldap_userdn_pre);
buffer_free(s->ldap_userdn_post);
if (s->ldap) ldap_unbind_s(s->ldap);
#endif
......
PATCH(auth_ldap_basedn);
PATCH(auth_ldap_binddn);
PATCH(auth_ldap_bindpw);
PATCH(auth_ldap_userdn);
PATCH(auth_ldap_filter);
PATCH(auth_ldap_cafile);
PATCH(auth_ldap_starttls);
......
PATCH(ldap);
PATCH(ldap_filter_pre);
PATCH(ldap_filter_post);
PATCH(ldap_userdn_pre);
PATCH(ldap_userdn_post);
#endif
/* skip the first, the global context */
......
PATCH(ldap);
PATCH(ldap_filter_pre);
PATCH(ldap_filter_post);
PATCH(ldap_userdn_pre);
PATCH(ldap_userdn_post);
#endif
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.base-dn"))) {
PATCH(auth_ldap_basedn);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-dn"))) {
PATCH(auth_ldap_binddn);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-pw"))) {
PATCH(auth_ldap_bindpw);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.user-dn"))) {
PATCH(auth_ldap_userdn);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.filter"))) {
PATCH(auth_ldap_filter);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.ca-file"))) {
......
{ "auth.backend.ldap.starttls", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
{ "auth.backend.ldap.bind-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
{ "auth.backend.ldap.bind-pw", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
{ "auth.backend.ldap.user-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
{ "auth.backend.ldap.allow-empty-pw", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
{ "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
{ "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
......
s->auth_ldap_basedn = buffer_init();
s->auth_ldap_binddn = buffer_init();
s->auth_ldap_bindpw = buffer_init();
s->auth_ldap_userdn = buffer_init();
s->auth_ldap_filter = buffer_init();
s->auth_ldap_cafile = buffer_init();
s->auth_ldap_starttls = 0;
......
#ifdef USE_LDAP
s->ldap_filter_pre = buffer_init();
s->ldap_filter_post = buffer_init();
s->ldap_userdn_pre = buffer_init();
s->ldap_userdn_post = buffer_init();
s->ldap = NULL;
#endif
......
cv[8].destination = &(s->auth_ldap_starttls);
cv[9].destination = s->auth_ldap_binddn;
cv[10].destination = s->auth_ldap_bindpw;
cv[11].destination = &(s->auth_ldap_allow_empty_pw);
cv[12].destination = s->auth_htdigest_userfile;
cv[13].destination = s->auth_htpasswd_userfile;
cv[14].destination = &(s->auth_debug);
cv[11].destination = s->auth_ldap_userdn;
cv[12].destination = &(s->auth_ldap_allow_empty_pw);
cv[13].destination = s->auth_htdigest_userfile;
cv[14].destination = s->auth_htpasswd_userfile;
cv[15].destination = &(s->auth_debug);
p->config_storage[i] = s;
ca = ((data_config *)srv->config_context->data[i])->value;
......
}
/* 1. */
if (s->auth_ldap_binddn->used) {
if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, s->auth_ldap_binddn->ptr, s->auth_ldap_bindpw->ptr))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
/* If the user DN is supplied, we don't need to do an initial bind
* to find it. */
if (s->auth_ldap_userdn->used) {
char *dollar;
/* parse userdn */
if (NULL == (dollar = strchr(s->auth_ldap_userdn->ptr, '$'))) {
log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.user-dn is missing a replace-operator '$'");
return HANDLER_ERROR;
}
} else {
if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, NULL, NULL))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
return HANDLER_ERROR;
buffer_copy_string_len(s->ldap_userdn_pre, s->auth_ldap_userdn->ptr, dollar - s->auth_ldap_userdn->ptr);
buffer_copy_string(s->ldap_userdn_post, dollar+1);
} else {
/* 1. */
if (s->auth_ldap_binddn->used) {
if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, s->auth_ldap_binddn->ptr, s->auth_ldap_bindpw->ptr))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
return HANDLER_ERROR;
}
} else {
if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, NULL, NULL))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
return HANDLER_ERROR;
}
}
}
}
(1-1/4)