[mod_auth] extensible interface for auth backends
Merge branch 'feature-auth-reorg' into gmaster
This commit is contained in:
commit
b22269c2f3
|
@ -512,6 +512,7 @@ set(COMMON_SRC
|
|||
connections-glue.c
|
||||
configfile-glue.c
|
||||
http-header-glue.c
|
||||
http_auth.c
|
||||
splaytree.c network_writev.c
|
||||
network_write_mmap.c network_write_no_mmap.c
|
||||
network_write.c network_linux_sendfile.c
|
||||
|
@ -557,7 +558,9 @@ set(L_INSTALL_TARGETS ${L_INSTALL_TARGETS} lighttpd)
|
|||
add_and_install_library(mod_access mod_access.c)
|
||||
add_and_install_library(mod_accesslog mod_accesslog.c)
|
||||
add_and_install_library(mod_alias mod_alias.c)
|
||||
add_and_install_library(mod_auth "mod_auth.c;http_auth.c")
|
||||
add_and_install_library(mod_auth "mod_auth.c")
|
||||
add_and_install_library(mod_authn_file "mod_authn_file.c")
|
||||
add_and_install_library(mod_authn_ldap "mod_authn_ldap.c")
|
||||
if(NOT WIN32)
|
||||
add_and_install_library(mod_cgi mod_cgi.c)
|
||||
endif()
|
||||
|
@ -654,15 +657,16 @@ endif()
|
|||
|
||||
target_link_libraries(mod_webdav ${L_MOD_WEBDAV})
|
||||
|
||||
set(L_MOD_AUTH)
|
||||
set(L_MOD_AUTHN_FILE)
|
||||
if(HAVE_LIBCRYPT)
|
||||
set(L_MOD_AUTH ${L_MOD_AUTH} crypt)
|
||||
set(L_MOD_AUTHN_FILE ${L_MOD_AUTHN_FILE} crypt)
|
||||
endif()
|
||||
target_link_libraries(mod_authn_file ${L_MOD_AUTHN_FILE})
|
||||
|
||||
if(HAVE_LDAP_H)
|
||||
set(L_MOD_AUTH ${L_MOD_AUTH} ldap lber)
|
||||
set(L_MOD_AUTHN_LDAP ${L_MOD_AUTHN_LDAP} ldap lber)
|
||||
endif()
|
||||
target_link_libraries(mod_auth ${L_MOD_AUTH})
|
||||
target_link_libraries(mod_authn_ldap ${L_MOD_AUTHN_LDAP})
|
||||
|
||||
if(HAVE_ZLIB_H)
|
||||
if(HAVE_BZLIB_H)
|
||||
|
|
|
@ -77,6 +77,7 @@ common_src=base64.c buffer.c log.c \
|
|||
connections-glue.c \
|
||||
configfile-glue.c \
|
||||
http-header-glue.c \
|
||||
http_auth.c \
|
||||
network_write.c network_linux_sendfile.c \
|
||||
network_write_mmap.c network_write_no_mmap.c \
|
||||
network_freebsd_sendfile.c network_writev.c \
|
||||
|
@ -252,9 +253,19 @@ mod_compress_la_LDFLAGS = $(common_module_ldflags)
|
|||
mod_compress_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd)
|
||||
|
||||
lib_LTLIBRARIES += mod_auth.la
|
||||
mod_auth_la_SOURCES = mod_auth.c http_auth.c
|
||||
mod_auth_la_SOURCES = mod_auth.c
|
||||
mod_auth_la_LDFLAGS = $(common_module_ldflags)
|
||||
mod_auth_la_LIBADD = $(CRYPT_LIB) $(SSL_LIB) $(LDAP_LIB) $(LBER_LIB) $(common_libadd)
|
||||
mod_auth_la_LIBADD = $(common_libadd)
|
||||
|
||||
lib_LTLIBRARIES += mod_authn_file.la
|
||||
mod_authn_file_la_SOURCES = mod_authn_file.c
|
||||
mod_authn_file_la_LDFLAGS = $(common_module_ldflags)
|
||||
mod_authn_file_la_LIBADD = $(CRYPT_LIB) $(SSL_LIB) $(common_libadd)
|
||||
|
||||
lib_LTLIBRARIES += mod_authn_ldap.la
|
||||
mod_authn_ldap_la_SOURCES = mod_authn_ldap.c
|
||||
mod_authn_ldap_la_LDFLAGS = $(common_module_ldflags)
|
||||
mod_authn_ldap_la_LIBADD = $(LDAP_LIB) $(LBER_LIB) $(common_libadd)
|
||||
|
||||
lib_LTLIBRARIES += mod_rewrite.la
|
||||
mod_rewrite_la_SOURCES = mod_rewrite.c
|
||||
|
@ -304,7 +315,9 @@ lighttpd_SOURCES = \
|
|||
mod_access.c \
|
||||
mod_accesslog.c \
|
||||
mod_alias.c \
|
||||
mod_auth.c http_auth.c \
|
||||
mod_auth.c \
|
||||
mod_authn_file.c \
|
||||
mod_authn_ldap.c \
|
||||
mod_cgi.c \
|
||||
mod_cml.c mod_cml_lua.c mod_cml_funcs.c \
|
||||
mod_compress.c \
|
||||
|
|
|
@ -54,6 +54,7 @@ common_src = Split("base64.c buffer.c log.c \
|
|||
connections-glue.c \
|
||||
configfile-glue.c \
|
||||
http-header-glue.c \
|
||||
http_auth.c \
|
||||
splaytree.c network_writev.c \
|
||||
network_write_mmap.c network_write_no_mmap.c \
|
||||
network_write.c network_linux_sendfile.c \
|
||||
|
@ -99,9 +100,9 @@ modules = {
|
|||
'mod_compress' : { 'src' : [ 'mod_compress.c' ], 'lib' : [ env['LIBZ'], env['LIBBZ2'] ] },
|
||||
'mod_redirect' : { 'src' : [ 'mod_redirect.c' ], 'lib' : [ env['LIBPCRE'] ] },
|
||||
'mod_rewrite' : { 'src' : [ 'mod_rewrite.c' ], 'lib' : [ env['LIBPCRE'] ] },
|
||||
'mod_auth' : {
|
||||
'src' : [ 'mod_auth.c', 'http_auth.c' ],
|
||||
'lib' : [ env['LIBCRYPT'], env['LIBLDAP'], env['LIBLBER'] ] },
|
||||
'mod_auth' : { 'src' : [ 'mod_auth.c' ] },
|
||||
'mod_authn_file' : { 'src' : [ 'mod_authn_file.c' ], 'lib' : [ env['LIBCRYPT'] ] },
|
||||
'mod_authn_ldap' : { 'src' : [ 'mod_authn_ldap.c' ], 'lib' : [ env['LIBLDAP'], env['LIBLBER'] ] },
|
||||
'mod_webdav' : { 'src' : [ 'mod_webdav.c' ], 'lib' : [ env['LIBXML2'], env['LIBSQLITE3'], env['LIBUUID'] ] },
|
||||
'mod_mysql_vhost' : { 'src' : [ 'mod_mysql_vhost.c' ], 'lib' : [ env['LIBMYSQL'] ] },
|
||||
# 'mod_uploadprogress' : { 'src' : [ 'mod_uploadprogress.c' ] },
|
||||
|
|
|
@ -341,6 +341,9 @@ static int config_insert(server *srv) {
|
|||
int prepend_mod_indexfile = 1;
|
||||
int append_mod_dirlisting = 1;
|
||||
int append_mod_staticfile = 1;
|
||||
int append_mod_authn_file = 1;
|
||||
int append_mod_authn_ldap = 1;
|
||||
int contains_mod_auth = 0;
|
||||
|
||||
/* prepend default modules */
|
||||
for (i = 0; i < srv->srvconf.modules->used; i++) {
|
||||
|
@ -358,9 +361,24 @@ static int config_insert(server *srv) {
|
|||
append_mod_dirlisting = 0;
|
||||
}
|
||||
|
||||
if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_file"))) {
|
||||
append_mod_authn_file = 0;
|
||||
}
|
||||
|
||||
if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_ldap"))) {
|
||||
append_mod_authn_ldap = 0;
|
||||
}
|
||||
|
||||
if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_auth"))) {
|
||||
contains_mod_auth = 1;
|
||||
}
|
||||
|
||||
if (0 == prepend_mod_indexfile &&
|
||||
0 == append_mod_dirlisting &&
|
||||
0 == append_mod_staticfile) {
|
||||
0 == append_mod_staticfile &&
|
||||
0 == append_mod_authn_file &&
|
||||
0 == append_mod_authn_ldap &&
|
||||
1 == contains_mod_auth) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -394,6 +412,24 @@ static int config_insert(server *srv) {
|
|||
buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_staticfile"));
|
||||
array_insert_unique(srv->srvconf.modules, (data_unset *)ds);
|
||||
}
|
||||
|
||||
/* mod_auth.c,http_auth.c auth backends were split into separate modules
|
||||
* Automatically load auth backend modules for compatibility with
|
||||
* existing lighttpd 1.4.x configs */
|
||||
if (contains_mod_auth) {
|
||||
if (append_mod_authn_file) {
|
||||
ds = data_string_init();
|
||||
buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_authn_file"));
|
||||
array_insert_unique(srv->srvconf.modules, (data_unset *)ds);
|
||||
}
|
||||
if (append_mod_authn_ldap) {
|
||||
#if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)
|
||||
ds = data_string_init();
|
||||
buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_authn_ldap"));
|
||||
array_insert_unique(srv->srvconf.modules, (data_unset *)ds);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
1140
src/http_auth.c
1140
src/http_auth.c
File diff suppressed because it is too large
Load Diff
|
@ -2,74 +2,16 @@
|
|||
#define _HTTP_AUTH_H_
|
||||
#include "first.h"
|
||||
|
||||
#include "server.h"
|
||||
#include "plugin.h"
|
||||
#include "base.h"
|
||||
|
||||
#if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)
|
||||
# define USE_LDAP
|
||||
# include <ldap.h>
|
||||
#endif
|
||||
typedef struct http_auth_backend_t {
|
||||
const char *name;
|
||||
handler_t(*basic)(server *srv, connection *con, void *p_d, const buffer *username, const buffer *realm, const char *pw);
|
||||
handler_t(*digest)(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]);
|
||||
void *p_d;
|
||||
} http_auth_backend_t;
|
||||
|
||||
typedef enum {
|
||||
AUTH_BACKEND_UNSET,
|
||||
AUTH_BACKEND_PLAIN,
|
||||
AUTH_BACKEND_LDAP,
|
||||
AUTH_BACKEND_HTPASSWD,
|
||||
AUTH_BACKEND_HTDIGEST
|
||||
} auth_backend_t;
|
||||
|
||||
typedef struct {
|
||||
/* auth */
|
||||
array *auth_require;
|
||||
|
||||
buffer *auth_plain_groupfile;
|
||||
buffer *auth_plain_userfile;
|
||||
|
||||
buffer *auth_htdigest_userfile;
|
||||
buffer *auth_htpasswd_userfile;
|
||||
|
||||
buffer *auth_backend_conf;
|
||||
|
||||
buffer *auth_ldap_hostname;
|
||||
buffer *auth_ldap_basedn;
|
||||
buffer *auth_ldap_binddn;
|
||||
buffer *auth_ldap_bindpw;
|
||||
buffer *auth_ldap_filter;
|
||||
buffer *auth_ldap_cafile;
|
||||
unsigned short auth_ldap_starttls;
|
||||
unsigned short auth_ldap_allow_empty_pw;
|
||||
|
||||
unsigned short auth_debug;
|
||||
|
||||
/* generated */
|
||||
auth_backend_t auth_backend;
|
||||
|
||||
#ifdef USE_LDAP
|
||||
LDAP *ldap;
|
||||
|
||||
buffer *ldap_filter_pre;
|
||||
buffer *ldap_filter_post;
|
||||
#endif
|
||||
} mod_auth_plugin_config;
|
||||
|
||||
typedef struct {
|
||||
PLUGIN_DATA;
|
||||
buffer *tmp_buf;
|
||||
|
||||
buffer *auth_user;
|
||||
|
||||
#ifdef USE_LDAP
|
||||
buffer *ldap_filter;
|
||||
#endif
|
||||
|
||||
mod_auth_plugin_config **config_storage;
|
||||
|
||||
mod_auth_plugin_config conf, *anon_conf; /* this is only used as long as no handler_ctx is setup */
|
||||
} mod_auth_plugin_data;
|
||||
|
||||
int http_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str);
|
||||
int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str);
|
||||
int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char (*hh)[33]);
|
||||
int http_auth_match_rules(server *srv, array *req, const char *username, const char *group, const char *host);
|
||||
const http_auth_backend_t * http_auth_backend_get (const buffer *name);
|
||||
void http_auth_backend_set (const http_auth_backend_t *backend);
|
||||
|
||||
#endif
|
||||
|
|
953
src/mod_auth.c
953
src/mod_auth.c
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,674 @@
|
|||
#include "first.h"
|
||||
|
||||
|
||||
/*(htpasswd)*/
|
||||
#ifdef HAVE_CRYPT_H
|
||||
# include <crypt.h>
|
||||
#elif defined(__linux__)
|
||||
/* linux needs _XOPEN_SOURCE */
|
||||
# define _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LIBCRYPT) && !defined(HAVE_CRYPT)
|
||||
/* always assume crypt() is present if we have -lcrypt */
|
||||
# define HAVE_CRYPT
|
||||
#endif
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#include "base64.h"
|
||||
#include <openssl/sha.h>
|
||||
#endif
|
||||
|
||||
#include "safe_memclear.h"
|
||||
/*(htpasswd)*/
|
||||
|
||||
|
||||
#include "plugin.h"
|
||||
#include "http_auth.h"
|
||||
#include "log.h"
|
||||
#include "response.h"
|
||||
|
||||
#include "inet_ntop_cache.h"
|
||||
#include "base64.h"
|
||||
#include "md5.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* htdigest, htpasswd, plain auth backends
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
buffer *auth_plain_groupfile;
|
||||
buffer *auth_plain_userfile;
|
||||
buffer *auth_htdigest_userfile;
|
||||
buffer *auth_htpasswd_userfile;
|
||||
} plugin_config;
|
||||
|
||||
typedef struct {
|
||||
PLUGIN_DATA;
|
||||
plugin_config **config_storage;
|
||||
plugin_config conf;
|
||||
} plugin_data;
|
||||
|
||||
static handler_t mod_authn_file_htdigest_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]);
|
||||
static handler_t mod_authn_file_htdigest_basic(server *srv, connection *con, void *p_d, const buffer *username, const buffer *realm, const char *pw);
|
||||
static handler_t mod_authn_file_plain_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]);
|
||||
static handler_t mod_authn_file_plain_basic(server *srv, connection *con, void *p_d, const buffer *username, const buffer *realm, const char *pw);
|
||||
static handler_t mod_authn_file_htpasswd_basic(server *srv, connection *con, void *p_d, const buffer *username, const buffer *realm, const char *pw);
|
||||
|
||||
INIT_FUNC(mod_authn_file_init) {
|
||||
static http_auth_backend_t http_auth_backend_htdigest =
|
||||
{ "htdigest", mod_authn_file_htdigest_basic, mod_authn_file_htdigest_digest, NULL };
|
||||
static http_auth_backend_t http_auth_backend_htpasswd =
|
||||
{ "htpasswd", mod_authn_file_htpasswd_basic, NULL, NULL };
|
||||
static http_auth_backend_t http_auth_backend_plain =
|
||||
{ "plain", mod_authn_file_plain_basic, mod_authn_file_plain_digest, NULL };
|
||||
plugin_data *p = calloc(1, sizeof(*p));
|
||||
|
||||
/* register http_auth_backend_htdigest */
|
||||
http_auth_backend_htdigest.p_d = p;
|
||||
http_auth_backend_set(&http_auth_backend_htdigest);
|
||||
|
||||
/* register http_auth_backend_htpasswd */
|
||||
http_auth_backend_htpasswd.p_d = p;
|
||||
http_auth_backend_set(&http_auth_backend_htpasswd);
|
||||
|
||||
/* register http_auth_backend_plain */
|
||||
http_auth_backend_plain.p_d = p;
|
||||
http_auth_backend_set(&http_auth_backend_plain);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
FREE_FUNC(mod_authn_file_free) {
|
||||
plugin_data *p = p_d;
|
||||
|
||||
UNUSED(srv);
|
||||
|
||||
if (!p) return HANDLER_GO_ON;
|
||||
|
||||
if (p->config_storage) {
|
||||
size_t i;
|
||||
for (i = 0; i < srv->config_context->used; i++) {
|
||||
plugin_config *s = p->config_storage[i];
|
||||
|
||||
if (NULL == s) continue;
|
||||
|
||||
buffer_free(s->auth_plain_groupfile);
|
||||
buffer_free(s->auth_plain_userfile);
|
||||
buffer_free(s->auth_htdigest_userfile);
|
||||
buffer_free(s->auth_htpasswd_userfile);
|
||||
|
||||
free(s);
|
||||
}
|
||||
free(p->config_storage);
|
||||
}
|
||||
|
||||
free(p);
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
SETDEFAULTS_FUNC(mod_authn_file_set_defaults) {
|
||||
plugin_data *p = p_d;
|
||||
size_t i;
|
||||
|
||||
config_values_t cv[] = {
|
||||
{ "auth.backend.plain.groupfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
|
||||
{ "auth.backend.plain.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
|
||||
{ "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
|
||||
{ "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
|
||||
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
|
||||
};
|
||||
|
||||
p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
|
||||
|
||||
for (i = 0; i < srv->config_context->used; i++) {
|
||||
data_config const* config = (data_config const*)srv->config_context->data[i];
|
||||
plugin_config *s;
|
||||
|
||||
s = calloc(1, sizeof(plugin_config));
|
||||
s->auth_plain_groupfile = buffer_init();
|
||||
s->auth_plain_userfile = buffer_init();
|
||||
s->auth_htdigest_userfile = buffer_init();
|
||||
s->auth_htpasswd_userfile = buffer_init();
|
||||
|
||||
cv[0].destination = s->auth_plain_groupfile;
|
||||
cv[1].destination = s->auth_plain_userfile;
|
||||
cv[2].destination = s->auth_htdigest_userfile;
|
||||
cv[3].destination = s->auth_htpasswd_userfile;
|
||||
|
||||
p->config_storage[i] = s;
|
||||
|
||||
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
#define PATCH(x) \
|
||||
p->conf.x = s->x;
|
||||
static int mod_authn_file_patch_connection(server *srv, connection *con, plugin_data *p) {
|
||||
size_t i, j;
|
||||
plugin_config *s = p->config_storage[0];
|
||||
|
||||
PATCH(auth_plain_groupfile);
|
||||
PATCH(auth_plain_userfile);
|
||||
PATCH(auth_htdigest_userfile);
|
||||
PATCH(auth_htpasswd_userfile);
|
||||
|
||||
/* skip the first, the global context */
|
||||
for (i = 1; i < srv->config_context->used; i++) {
|
||||
data_config *dc = (data_config *)srv->config_context->data[i];
|
||||
s = p->config_storage[i];
|
||||
|
||||
/* condition didn't match */
|
||||
if (!config_check_cond(srv, con, dc)) continue;
|
||||
|
||||
/* merge config */
|
||||
for (j = 0; j < dc->value->used; j++) {
|
||||
data_unset *du = dc->value->data[j];
|
||||
|
||||
if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.groupfile"))) {
|
||||
PATCH(auth_plain_groupfile);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.userfile"))) {
|
||||
PATCH(auth_plain_userfile);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htdigest.userfile"))) {
|
||||
PATCH(auth_htdigest_userfile);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htpasswd.userfile"))) {
|
||||
PATCH(auth_htpasswd_userfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#undef PATCH
|
||||
|
||||
|
||||
static int mod_authn_file_htdigest_get(server *srv, const buffer *auth_fn, const buffer *username, const buffer *realm, unsigned char HA1[16]) {
|
||||
FILE *fp;
|
||||
char f_user[1024];
|
||||
|
||||
if (buffer_string_is_empty(auth_fn)) return -1;
|
||||
if (buffer_is_empty(username) || buffer_is_empty(realm)) return -1;
|
||||
|
||||
fp = fopen(auth_fn->ptr, "r");
|
||||
if (NULL == fp) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", auth_fn, "failed:", strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (NULL != fgets(f_user, sizeof(f_user), fp)) {
|
||||
char *f_pwd, *f_realm;
|
||||
size_t u_len, r_len;
|
||||
|
||||
/* skip blank lines and comment lines (beginning '#') */
|
||||
if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue;
|
||||
|
||||
/*
|
||||
* htdigest format
|
||||
*
|
||||
* user:realm:md5(user:realm:password)
|
||||
*/
|
||||
|
||||
if (NULL == (f_realm = strchr(f_user, ':'))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sbs",
|
||||
"parsed error in", auth_fn,
|
||||
"expected 'username:realm:hashed password'");
|
||||
|
||||
continue; /* skip bad lines */
|
||||
}
|
||||
|
||||
if (NULL == (f_pwd = strchr(f_realm + 1, ':'))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sbs",
|
||||
"parsed error in", auth_fn,
|
||||
"expected 'username:realm:hashed password'");
|
||||
|
||||
continue; /* skip bad lines */
|
||||
}
|
||||
|
||||
/* get pointers to the fields */
|
||||
u_len = f_realm - f_user;
|
||||
f_realm++;
|
||||
r_len = f_pwd - f_realm;
|
||||
f_pwd++;
|
||||
|
||||
if (buffer_string_length(username) == u_len &&
|
||||
(buffer_string_length(realm) == r_len) &&
|
||||
(0 == strncmp(username->ptr, f_user, u_len)) &&
|
||||
(0 == strncmp(realm->ptr, f_realm, r_len))) {
|
||||
/* found */
|
||||
|
||||
size_t pwd_len = strlen(f_pwd);
|
||||
if (f_pwd[pwd_len-1] == '\n') --pwd_len;
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (pwd_len != 32) return -1;
|
||||
/* transform the 32-byte-hex-md5 (f_pwd) to a 16-byte-md5 (HA1) */
|
||||
for (int i = 0; i < 16; i++) {
|
||||
HA1[i] = hex2int(f_pwd[i*2]) << 4;
|
||||
HA1[i] |= hex2int(f_pwd[i*2+1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static handler_t mod_authn_file_htdigest_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]) {
|
||||
plugin_data *p = (plugin_data *)p_d;
|
||||
buffer *username_buf = buffer_init_string(username);
|
||||
buffer *realm_buf = buffer_init_string(realm);
|
||||
int rc;
|
||||
mod_authn_file_patch_connection(srv, con, p);
|
||||
rc = mod_authn_file_htdigest_get(srv, p->conf.auth_htdigest_userfile, username_buf, realm_buf, HA1);
|
||||
buffer_free(realm_buf);
|
||||
buffer_free(username_buf);
|
||||
UNUSED(con);
|
||||
return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR;
|
||||
}
|
||||
|
||||
static handler_t mod_authn_file_htdigest_basic(server *srv, connection *con, void *p_d, const buffer *username, const buffer *realm, const char *pw) {
|
||||
plugin_data *p = (plugin_data *)p_d;
|
||||
li_MD5_CTX Md5Ctx;
|
||||
unsigned char HA1[16];
|
||||
unsigned char htdigest[16];
|
||||
|
||||
mod_authn_file_patch_connection(srv, con, p);
|
||||
if (mod_authn_file_htdigest_get(srv, p->conf.auth_htdigest_userfile, username, realm, htdigest)) return HANDLER_ERROR;
|
||||
|
||||
li_MD5_Init(&Md5Ctx);
|
||||
li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(username));
|
||||
li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
|
||||
li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(realm));
|
||||
li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
|
||||
li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw));
|
||||
li_MD5_Final(HA1, &Md5Ctx);
|
||||
|
||||
UNUSED(con);
|
||||
return (0 == memcmp(HA1, htdigest, sizeof(HA1)))
|
||||
? HANDLER_GO_ON
|
||||
: HANDLER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int mod_authn_file_htpasswd_get(server *srv, const buffer *auth_fn, const buffer *username, buffer *password) {
|
||||
FILE *fp;
|
||||
char f_user[1024];
|
||||
|
||||
if (buffer_is_empty(username)) return -1;
|
||||
|
||||
if (buffer_string_is_empty(auth_fn)) return -1;
|
||||
fp = fopen(auth_fn->ptr, "r");
|
||||
if (NULL == fp) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sbss",
|
||||
"opening plain-userfile", auth_fn, "failed:", strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (NULL != fgets(f_user, sizeof(f_user), fp)) {
|
||||
char *f_pwd;
|
||||
size_t u_len;
|
||||
|
||||
/* skip blank lines and comment lines (beginning '#') */
|
||||
if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue;
|
||||
|
||||
/*
|
||||
* htpasswd format
|
||||
*
|
||||
* user:crypted passwd
|
||||
*/
|
||||
|
||||
if (NULL == (f_pwd = strchr(f_user, ':'))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sbs",
|
||||
"parsed error in", auth_fn,
|
||||
"expected 'username:hashed password'");
|
||||
|
||||
continue; /* skip bad lines */
|
||||
}
|
||||
|
||||
/* get pointers to the fields */
|
||||
u_len = f_pwd - f_user;
|
||||
f_pwd++;
|
||||
|
||||
if (buffer_string_length(username) == u_len &&
|
||||
(0 == strncmp(username->ptr, f_user, u_len))) {
|
||||
/* found */
|
||||
|
||||
size_t pwd_len = strlen(f_pwd);
|
||||
if (f_pwd[pwd_len-1] == '\n') --pwd_len;
|
||||
|
||||
buffer_copy_string_len(password, f_pwd, pwd_len);
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static handler_t mod_authn_file_plain_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]) {
|
||||
plugin_data *p = (plugin_data *)p_d;
|
||||
buffer *username_buf = buffer_init_string(username);
|
||||
buffer *password_buf = buffer_init();/* password-string from auth-backend */
|
||||
int rc;
|
||||
mod_authn_file_patch_connection(srv, con, p);
|
||||
rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_plain_userfile, username_buf, password_buf);
|
||||
if (0 == rc) {
|
||||
/* generate password from plain-text */
|
||||
li_MD5_CTX Md5Ctx;
|
||||
li_MD5_Init(&Md5Ctx);
|
||||
li_MD5_Update(&Md5Ctx, (unsigned char *)username_buf->ptr, buffer_string_length(username_buf));
|
||||
li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
|
||||
li_MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm));
|
||||
li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
|
||||
li_MD5_Update(&Md5Ctx, (unsigned char *)password_buf->ptr, buffer_string_length(password_buf));
|
||||
li_MD5_Final(HA1, &Md5Ctx);
|
||||
}
|
||||
buffer_free(password_buf);
|
||||
buffer_free(username_buf);
|
||||
UNUSED(con);
|
||||
return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR;
|
||||
}
|
||||
|
||||
static handler_t mod_authn_file_plain_basic(server *srv, connection *con, void *p_d, const buffer *username, const buffer *realm, const char *pw) {
|
||||
plugin_data *p = (plugin_data *)p_d;
|
||||
buffer *password_buf = buffer_init();/* password-string from auth-backend */
|
||||
int rc;
|
||||
mod_authn_file_patch_connection(srv, con, p);
|
||||
rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_plain_userfile, username, password_buf);
|
||||
if (0 == rc) {
|
||||
rc = buffer_is_equal_string(password_buf, pw, strlen(pw)) ? 0 : -1;
|
||||
}
|
||||
buffer_free(password_buf);
|
||||
UNUSED(con);
|
||||
UNUSED(realm);
|
||||
return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* the $apr1$ handling is taken from apache 1.3.x
|
||||
*/
|
||||
|
||||
/*
|
||||
* The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0
|
||||
* MD5 crypt() function, which is licenced as follows:
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define APR_MD5_DIGESTSIZE 16
|
||||
#define APR1_ID "$apr1$"
|
||||
|
||||
/*
|
||||
* The following MD5 password encryption code was largely borrowed from
|
||||
* the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is
|
||||
* licenced as stated above.
|
||||
*/
|
||||
|
||||
static void to64(char *s, unsigned long v, int n)
|
||||
{
|
||||
static const unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */
|
||||
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
while (--n >= 0) {
|
||||
*s++ = itoa64[v&0x3f];
|
||||
v >>= 6;
|
||||
}
|
||||
}
|
||||
|
||||
static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) {
|
||||
/*
|
||||
* Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,
|
||||
* plus 4 for the '$' separators, plus the password hash itself.
|
||||
* Let's leave a goodly amount of leeway.
|
||||
*/
|
||||
|
||||
char passwd[120], *p;
|
||||
const char *sp, *ep;
|
||||
unsigned char final[APR_MD5_DIGESTSIZE];
|
||||
ssize_t sl, pl, i;
|
||||
li_MD5_CTX ctx, ctx1;
|
||||
unsigned long l;
|
||||
|
||||
/*
|
||||
* Refine the salt first. It's possible we were given an already-hashed
|
||||
* string as the salt argument, so extract the actual salt value from it
|
||||
* if so. Otherwise just use the string up to the first '$' as the salt.
|
||||
*/
|
||||
sp = salt;
|
||||
|
||||
/*
|
||||
* If it starts with the magic string, then skip that.
|
||||
*/
|
||||
if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) {
|
||||
sp += strlen(APR1_ID);
|
||||
}
|
||||
|
||||
/*
|
||||
* It stops at the first '$' or 8 chars, whichever comes first
|
||||
*/
|
||||
for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the length of the true salt
|
||||
*/
|
||||
sl = ep - sp;
|
||||
|
||||
/*
|
||||
* 'Time to make the doughnuts..'
|
||||
*/
|
||||
li_MD5_Init(&ctx);
|
||||
|
||||
/*
|
||||
* The password first, since that is what is most unknown
|
||||
*/
|
||||
li_MD5_Update(&ctx, pw, strlen(pw));
|
||||
|
||||
/*
|
||||
* Then our magic string
|
||||
*/
|
||||
li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID));
|
||||
|
||||
/*
|
||||
* Then the raw salt
|
||||
*/
|
||||
li_MD5_Update(&ctx, sp, sl);
|
||||
|
||||
/*
|
||||
* Then just as many characters of the MD5(pw, salt, pw)
|
||||
*/
|
||||
li_MD5_Init(&ctx1);
|
||||
li_MD5_Update(&ctx1, pw, strlen(pw));
|
||||
li_MD5_Update(&ctx1, sp, sl);
|
||||
li_MD5_Update(&ctx1, pw, strlen(pw));
|
||||
li_MD5_Final(final, &ctx1);
|
||||
for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) {
|
||||
li_MD5_Update(&ctx, final,
|
||||
(pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't leave anything around in vm they could use.
|
||||
*/
|
||||
memset(final, 0, sizeof(final));
|
||||
|
||||
/*
|
||||
* Then something really weird...
|
||||
*/
|
||||
for (i = strlen(pw); i != 0; i >>= 1) {
|
||||
if (i & 1) {
|
||||
li_MD5_Update(&ctx, final, 1);
|
||||
}
|
||||
else {
|
||||
li_MD5_Update(&ctx, pw, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now make the output string. We know our limitations, so we
|
||||
* can use the string routines without bounds checking.
|
||||
*/
|
||||
strcpy(passwd, APR1_ID);
|
||||
strncat(passwd, sp, sl);
|
||||
strcat(passwd, "$");
|
||||
|
||||
li_MD5_Final(final, &ctx);
|
||||
|
||||
/*
|
||||
* And now, just to make sure things don't run too fast..
|
||||
* On a 60 Mhz Pentium this takes 34 msec, so you would
|
||||
* need 30 seconds to build a 1000 entry dictionary...
|
||||
*/
|
||||
for (i = 0; i < 1000; i++) {
|
||||
li_MD5_Init(&ctx1);
|
||||
if (i & 1) {
|
||||
li_MD5_Update(&ctx1, pw, strlen(pw));
|
||||
}
|
||||
else {
|
||||
li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
|
||||
}
|
||||
if (i % 3) {
|
||||
li_MD5_Update(&ctx1, sp, sl);
|
||||
}
|
||||
|
||||
if (i % 7) {
|
||||
li_MD5_Update(&ctx1, pw, strlen(pw));
|
||||
}
|
||||
|
||||
if (i & 1) {
|
||||
li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
|
||||
}
|
||||
else {
|
||||
li_MD5_Update(&ctx1, pw, strlen(pw));
|
||||
}
|
||||
li_MD5_Final(final,&ctx1);
|
||||
}
|
||||
|
||||
p = passwd + strlen(passwd);
|
||||
|
||||
l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;
|
||||
l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;
|
||||
l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;
|
||||
l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;
|
||||
l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;
|
||||
l = final[11] ; to64(p, l, 2); p += 2;
|
||||
*p = '\0';
|
||||
|
||||
/*
|
||||
* Don't leave anything around in vm they could use.
|
||||
*/
|
||||
safe_memclear(final, sizeof(final));
|
||||
|
||||
/* FIXME
|
||||
*/
|
||||
#define apr_cpystrn strncpy
|
||||
apr_cpystrn(result, passwd, nbytes - 1);
|
||||
}
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
static void apr_sha_encode(const char *pw, char *result, size_t nbytes) {
|
||||
unsigned char digest[20];
|
||||
size_t base64_written;
|
||||
|
||||
SHA1((const unsigned char*) pw, strlen(pw), digest);
|
||||
|
||||
memset(result, 0, nbytes);
|
||||
|
||||
/* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */
|
||||
if (nbytes < 5 + 28 + 1) return;
|
||||
|
||||
memcpy(result, "{SHA}", 5);
|
||||
base64_written = li_to_base64(result + 5, nbytes - 5, digest, 20, BASE64_STANDARD);
|
||||
force_assert(base64_written == 28);
|
||||
result[5 + base64_written] = '\0'; /* terminate string */
|
||||
}
|
||||
#endif
|
||||
|
||||
static handler_t mod_authn_file_htpasswd_basic(server *srv, connection *con, void *p_d, const buffer *username, const buffer *realm, const char *pw) {
|
||||
plugin_data *p = (plugin_data *)p_d;
|
||||
buffer *password = buffer_init();/* password-string from auth-backend */
|
||||
int rc;
|
||||
mod_authn_file_patch_connection(srv, con, p);
|
||||
rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_htpasswd_userfile, username, password);
|
||||
if (0 == rc) {
|
||||
char sample[120];
|
||||
rc = -1;
|
||||
if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) {
|
||||
/*
|
||||
* The hash was created using $apr1$ custom algorithm.
|
||||
*/
|
||||
apr_md5_encode(pw, password->ptr, sample, sizeof(sample));
|
||||
rc = strcmp(sample, password->ptr);
|
||||
}
|
||||
#ifdef USE_OPENSSL
|
||||
else if (0 == strncmp(password->ptr, "{SHA}", 5)) {
|
||||
apr_sha_encode(pw, sample, sizeof(sample));
|
||||
rc = strcmp(sample, password->ptr);
|
||||
}
|
||||
#endif
|
||||
#if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT)
|
||||
/* a simple DES password is 2 + 11 characters. everything else should be longer. */
|
||||
else if (buffer_string_length(password) >= 13) {
|
||||
char *crypted;
|
||||
#if defined(HAVE_CRYPT_R)
|
||||
struct crypt_data crypt_tmp_data;
|
||||
crypt_tmp_data.initialized = 0;
|
||||
crypted = crypt_r(pw, password->ptr, &crypt_tmp_data);
|
||||
#else
|
||||
crypted = crypt(pw, password->ptr);
|
||||
#endif
|
||||
if (NULL != crypted) {
|
||||
rc = strcmp(password->ptr, crypted);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
buffer_free(password);
|
||||
UNUSED(con);
|
||||
UNUSED(realm);
|
||||
return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
int mod_authn_file_plugin_init(plugin *p);
|
||||
int mod_authn_file_plugin_init(plugin *p) {
|
||||
p->version = LIGHTTPD_VERSION_ID;
|
||||
p->name = buffer_init_string("authn_file");
|
||||
p->init = mod_authn_file_init;
|
||||
p->set_defaults= mod_authn_file_set_defaults;
|
||||
p->cleanup = mod_authn_file_free;
|
||||
|
||||
p->data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,448 @@
|
|||
#include "first.h"
|
||||
|
||||
#include "plugin.h"
|
||||
|
||||
#if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)
|
||||
|
||||
#define USE_LDAP
|
||||
#include <ldap.h>
|
||||
|
||||
#include "server.h"
|
||||
#include "http_auth.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
LDAP *ldap;
|
||||
|
||||
buffer *ldap_filter_pre;
|
||||
buffer *ldap_filter_post;
|
||||
|
||||
buffer *auth_ldap_hostname;
|
||||
buffer *auth_ldap_basedn;
|
||||
buffer *auth_ldap_binddn;
|
||||
buffer *auth_ldap_bindpw;
|
||||
buffer *auth_ldap_filter;
|
||||
buffer *auth_ldap_cafile;
|
||||
unsigned short auth_ldap_starttls;
|
||||
unsigned short auth_ldap_allow_empty_pw;
|
||||
} plugin_config;
|
||||
|
||||
typedef struct {
|
||||
PLUGIN_DATA;
|
||||
plugin_config **config_storage;
|
||||
plugin_config conf, *anon_conf; /* this is only used as long as no handler_ctx is setup */
|
||||
|
||||
buffer *ldap_filter;
|
||||
} plugin_data;
|
||||
|
||||
static handler_t mod_authn_ldap_basic(server *srv, connection *con, void *p_d, const buffer *username, const buffer *realm, const char *pw);
|
||||
|
||||
INIT_FUNC(mod_authn_ldap_init) {
|
||||
static http_auth_backend_t http_auth_backend_ldap =
|
||||
{ "ldap", mod_authn_ldap_basic, NULL, NULL };
|
||||
plugin_data *p = calloc(1, sizeof(*p));
|
||||
p->ldap_filter = buffer_init();
|
||||
|
||||
/* register http_auth_backend_ldap */
|
||||
http_auth_backend_ldap.p_d = p;
|
||||
http_auth_backend_set(&http_auth_backend_ldap);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
FREE_FUNC(mod_authn_ldap_free) {
|
||||
plugin_data *p = p_d;
|
||||
|
||||
UNUSED(srv);
|
||||
|
||||
if (!p) return HANDLER_GO_ON;
|
||||
|
||||
buffer_free(p->ldap_filter);
|
||||
|
||||
if (p->config_storage) {
|
||||
size_t i;
|
||||
for (i = 0; i < srv->config_context->used; i++) {
|
||||
plugin_config *s = p->config_storage[i];
|
||||
|
||||
if (NULL == s) continue;
|
||||
|
||||
buffer_free(s->auth_ldap_hostname);
|
||||
buffer_free(s->auth_ldap_basedn);
|
||||
buffer_free(s->auth_ldap_binddn);
|
||||
buffer_free(s->auth_ldap_bindpw);
|
||||
buffer_free(s->auth_ldap_filter);
|
||||
buffer_free(s->auth_ldap_cafile);
|
||||
|
||||
buffer_free(s->ldap_filter_pre);
|
||||
buffer_free(s->ldap_filter_post);
|
||||
|
||||
if (s->ldap) ldap_unbind_s(s->ldap);
|
||||
|
||||
free(s);
|
||||
}
|
||||
free(p->config_storage);
|
||||
}
|
||||
|
||||
free(p);
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
static handler_t mod_authn_ldap_host_init(server *srv, plugin_config *s) {
|
||||
int ret;
|
||||
#if 0
|
||||
if (s->auth_ldap_basedn->used == 0) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.base-dn has to be set");
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (buffer_string_is_empty(s->auth_ldap_hostname)) return HANDLER_GO_ON;
|
||||
|
||||
/* free old context */
|
||||
if (NULL != s->ldap) ldap_unbind_s(s->ldap);
|
||||
|
||||
if (NULL == (s->ldap = ldap_init(s->auth_ldap_hostname->ptr, LDAP_PORT))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno));
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
ret = LDAP_VERSION3;
|
||||
if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(s->ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
if (s->auth_ldap_starttls) {
|
||||
/* if no CA file is given, it is ok, as we will use encryption
|
||||
* if the server requires a CAfile it will tell us */
|
||||
if (!buffer_string_is_empty(s->auth_ldap_cafile)) {
|
||||
if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
|
||||
s->auth_ldap_cafile->ptr))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "ss",
|
||||
"Loading CA certificate failed:", ldap_err2string(ret));
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(s->ldap, NULL, NULL))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret));
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 1. */
|
||||
if (!buffer_string_is_empty(s->auth_ldap_binddn)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
SETDEFAULTS_FUNC(mod_authn_ldap_set_defaults) {
|
||||
plugin_data *p = p_d;
|
||||
size_t i;
|
||||
config_values_t cv[] = {
|
||||
{ "auth.backend.ldap.hostname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
|
||||
{ "auth.backend.ldap.base-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
|
||||
{ "auth.backend.ldap.filter", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
|
||||
{ "auth.backend.ldap.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
|
||||
{ "auth.backend.ldap.starttls", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
|
||||
{ "auth.backend.ldap.bind-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
|
||||
{ "auth.backend.ldap.bind-pw", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
|
||||
{ "auth.backend.ldap.allow-empty-pw", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
|
||||
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
|
||||
};
|
||||
|
||||
p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
|
||||
|
||||
for (i = 0; i < srv->config_context->used; i++) {
|
||||
data_config const* config = (data_config const*)srv->config_context->data[i];
|
||||
plugin_config *s;
|
||||
|
||||
s = calloc(1, sizeof(plugin_config));
|
||||
|
||||
s->auth_ldap_hostname = buffer_init();
|
||||
s->auth_ldap_basedn = buffer_init();
|
||||
s->auth_ldap_binddn = buffer_init();
|
||||
s->auth_ldap_bindpw = buffer_init();
|
||||
s->auth_ldap_filter = buffer_init();
|
||||
s->auth_ldap_cafile = buffer_init();
|
||||
s->auth_ldap_starttls = 0;
|
||||
s->ldap_filter_pre = buffer_init();
|
||||
s->ldap_filter_post = buffer_init();
|
||||
s->ldap = NULL;
|
||||
|
||||
cv[0].destination = s->auth_ldap_hostname;
|
||||
cv[1].destination = s->auth_ldap_basedn;
|
||||
cv[2].destination = s->auth_ldap_filter;
|
||||
cv[3].destination = s->auth_ldap_cafile;
|
||||
cv[4].destination = &(s->auth_ldap_starttls);
|
||||
cv[5].destination = s->auth_ldap_binddn;
|
||||
cv[6].destination = s->auth_ldap_bindpw;
|
||||
cv[7].destination = &(s->auth_ldap_allow_empty_pw);
|
||||
|
||||
p->config_storage[i] = s;
|
||||
|
||||
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
if (!buffer_string_is_empty(s->auth_ldap_filter)) {
|
||||
char *dollar;
|
||||
|
||||
/* parse filter */
|
||||
|
||||
if (NULL == (dollar = strchr(s->auth_ldap_filter->ptr, '$'))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.filter is missing a replace-operator '$'");
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
buffer_copy_string_len(s->ldap_filter_pre, s->auth_ldap_filter->ptr, dollar - s->auth_ldap_filter->ptr);
|
||||
buffer_copy_string(s->ldap_filter_post, dollar+1);
|
||||
}
|
||||
}
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
#define PATCH(x) \
|
||||
p->conf.x = s->x;
|
||||
static int mod_authn_ldap_patch_connection(server *srv, connection *con, plugin_data *p) {
|
||||
size_t i, j;
|
||||
plugin_config *s = p->config_storage[0];
|
||||
|
||||
PATCH(auth_ldap_hostname);
|
||||
PATCH(auth_ldap_basedn);
|
||||
PATCH(auth_ldap_binddn);
|
||||
PATCH(auth_ldap_bindpw);
|
||||
PATCH(auth_ldap_filter);
|
||||
PATCH(auth_ldap_cafile);
|
||||
PATCH(auth_ldap_starttls);
|
||||
PATCH(auth_ldap_allow_empty_pw);
|
||||
p->anon_conf = s;
|
||||
PATCH(ldap_filter_pre);
|
||||
PATCH(ldap_filter_post);
|
||||
|
||||
/* skip the first, the global context */
|
||||
for (i = 1; i < srv->config_context->used; i++) {
|
||||
data_config *dc = (data_config *)srv->config_context->data[i];
|
||||
s = p->config_storage[i];
|
||||
|
||||
/* condition didn't match */
|
||||
if (!config_check_cond(srv, con, dc)) continue;
|
||||
|
||||
/* merge config */
|
||||
for (j = 0; j < dc->value->used; j++) {
|
||||
data_unset *du = dc->value->data[j];
|
||||
|
||||
if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.hostname"))) {
|
||||
PATCH(auth_ldap_hostname);
|
||||
p->anon_conf = s;
|
||||
} 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.filter"))) {
|
||||
PATCH(auth_ldap_filter);
|
||||
PATCH(ldap_filter_pre);
|
||||
PATCH(ldap_filter_post);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.ca-file"))) {
|
||||
PATCH(auth_ldap_cafile);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.starttls"))) {
|
||||
PATCH(auth_ldap_starttls);
|
||||
} 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.allow-empty-pw"))) {
|
||||
PATCH(auth_ldap_allow_empty_pw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#undef PATCH
|
||||
|
||||
static handler_t mod_authn_ldap_basic(server *srv, connection *con, void *p_d, const buffer *username, const buffer *realm, const char *pw) {
|
||||
plugin_data *p = (plugin_data *)p_d;
|
||||
LDAP *ldap;
|
||||
LDAPMessage *lm, *first;
|
||||
char *dn;
|
||||
int ret;
|
||||
char *attrs[] = { LDAP_NO_ATTRS, NULL };
|
||||
size_t i, len;
|
||||
UNUSED(realm);
|
||||
|
||||
mod_authn_ldap_patch_connection(srv, con, p);
|
||||
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
|
||||
/* check username
|
||||
*
|
||||
* we have to protect againt username which modifies our filter in
|
||||
* an unpleasant way
|
||||
*/
|
||||
|
||||
len = buffer_string_length(username);
|
||||
for (i = 0; i < len; i++) {
|
||||
char c = username->ptr[i];
|
||||
|
||||
if (!isalpha(c) &&
|
||||
!isdigit(c) &&
|
||||
(c != ' ') &&
|
||||
(c != '@') &&
|
||||
(c != '-') &&
|
||||
(c != '_') &&
|
||||
(c != '.') ) {
|
||||
|
||||
log_error_write(srv, __FILE__, __LINE__, "sbd", "ldap: invalid character (- _.@a-zA-Z0-9 allowed) in username:", username, i);
|
||||
|
||||
con->http_status = 400; /* Bad Request */
|
||||
con->mode = DIRECT;
|
||||
return HANDLER_FINISHED;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->conf.auth_ldap_allow_empty_pw != 1 && pw[0] == '\0')
|
||||
return HANDLER_ERROR;
|
||||
|
||||
/* build filter */
|
||||
buffer_copy_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);
|
||||
|
||||
|
||||
/* 2. */
|
||||
if (p->anon_conf->ldap == NULL ||
|
||||
LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) {
|
||||
|
||||
/* try again (or initial request); the ldap library sometimes fails for the first call but reconnects */
|
||||
if (p->anon_conf->ldap == NULL || ret != LDAP_SERVER_DOWN ||
|
||||
LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) {
|
||||
|
||||
if (mod_authn_ldap_host_init(srv, p->anon_conf) != HANDLER_GO_ON)
|
||||
return HANDLER_ERROR;
|
||||
|
||||
if (NULL == p->anon_conf->ldap) return HANDLER_ERROR;
|
||||
|
||||
if (LDAP_SUCCESS != (ret = ldap_search_s(p->anon_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 HANDLER_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL == (first = ldap_first_entry(p->anon_conf->ldap, lm))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "s", "ldap ...");
|
||||
|
||||
ldap_msgfree(lm);
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
if (NULL == (dn = ldap_get_dn(p->anon_conf->ldap, first))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "s", "ldap ...");
|
||||
|
||||
ldap_msgfree(lm);
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
ldap_msgfree(lm);
|
||||
|
||||
|
||||
/* 3. */
|
||||
if (NULL == (ldap = ldap_init(p->conf.auth_ldap_hostname->ptr, LDAP_PORT))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno));
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
ret = LDAP_VERSION3;
|
||||
if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
|
||||
|
||||
ldap_unbind_s(ldap);
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
if (p->conf.auth_ldap_starttls == 1) {
|
||||
if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(ldap, NULL, NULL))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret));
|
||||
|
||||
ldap_unbind_s(ldap);
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(ldap, dn, pw))) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
|
||||
|
||||
ldap_unbind_s(ldap);
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
/* 5. */
|
||||
ldap_unbind_s(ldap);
|
||||
|
||||
/* everything worked, good, access granted */
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
int mod_authn_ldap_plugin_init(plugin *p);
|
||||
int mod_authn_ldap_plugin_init(plugin *p) {
|
||||
p->version = LIGHTTPD_VERSION_ID;
|
||||
p->name = buffer_init_string("authn_ldap");
|
||||
p->init = mod_authn_ldap_init;
|
||||
p->set_defaults = mod_authn_ldap_set_defaults;
|
||||
p->cleanup = mod_authn_ldap_free;
|
||||
|
||||
p->data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int mod_authn_ldap_plugin_init(plugin *p);
|
||||
int mod_authn_ldap_plugin_init(plugin *p) {
|
||||
UNUSED(p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue