diff options
author | Glenn Strauss <gstrauss@gluelogic.com> | 2019-11-06 19:37:11 -0500 |
---|---|---|
committer | Glenn Strauss <gstrauss@gluelogic.com> | 2020-07-08 18:08:51 -0400 |
commit | 8e713130b390c687ee01f333986ece7e4ea2b2ba (patch) | |
tree | f6c2c9c5ba858e201745261c2349ecedf5fa2e25 | |
parent | 12b11f30421defc4ea61d2200241cf6bb976e10d (diff) | |
download | lighttpd-git-8e713130b390c687ee01f333986ece7e4ea2b2ba.tar.gz |
[mod_auth*] use config_plugin_values_init()
-rw-r--r-- | src/mod_auth.c | 266 | ||||
-rw-r--r-- | src/mod_authn_file.c | 163 | ||||
-rw-r--r-- | src/mod_authn_gssapi.c | 152 | ||||
-rw-r--r-- | src/mod_authn_ldap.c | 515 | ||||
-rw-r--r-- | src/mod_authn_mysql.c | 465 | ||||
-rw-r--r-- | src/mod_authn_pam.c | 125 | ||||
-rw-r--r-- | src/mod_authn_sasl.c | 239 |
7 files changed, 994 insertions, 931 deletions
diff --git a/src/mod_auth.c b/src/mod_auth.c index 93ccdf11..b1490a5a 100644 --- a/src/mod_auth.c +++ b/src/mod_auth.c @@ -19,21 +19,15 @@ */ typedef struct { - /* auth */ - array *auth_require; - buffer *auth_backend_conf; - unsigned short auth_extern_authn; - - /* generated */ - const http_auth_backend_t *auth_backend; + const http_auth_backend_t *auth_backend; + const array *auth_require; + unsigned int auth_extern_authn; } plugin_config; typedef struct { - PLUGIN_DATA; - - plugin_config **config_storage; - - plugin_config conf; + PLUGIN_DATA; + plugin_config defaults; + plugin_config conf; } plugin_data; static handler_t mod_auth_check_basic(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); @@ -56,31 +50,34 @@ INIT_FUNC(mod_auth_init) { return p; } -FREE_FUNC(mod_auth_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; - - array_free(s->auth_require); - buffer_free(s->auth_backend_conf); +static void mod_auth_free_config(plugin_data * const p) { + if (NULL == p->cvlist) return; + /* (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; + switch (cpv->k_id) { + case 1: /* auth.require */ + array_free(cpv->v.v); + break; + default: + break; + } + } + } +} - free(s); - } - free(p->config_storage); - } +FREE_FUNC(mod_auth_free) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; - free(p); + mod_auth_free_config(p); - return HANDLER_GO_ON; + free(p->cvlist); + free(p); + UNUSED(srv); + return HANDLER_GO_ON; } /* data type for mod_auth structured data @@ -268,63 +265,11 @@ static int mod_auth_require_parse (server *srv, http_auth_require_t * const requ return 1; /* success */ } -SETDEFAULTS_FUNC(mod_auth_set_defaults) { - plugin_data *p = p_d; - size_t i; - - config_values_t cv[] = { - { "auth.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { "auth.require", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { "auth.extern-authn", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },/* 2 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - p->config_storage = calloc(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; - size_t n; - const data_array *da; - - s = calloc(1, sizeof(plugin_config)); - s->auth_backend_conf = buffer_init(); - - s->auth_require = array_init(); - - cv[0].destination = s->auth_backend_conf; - cv[1].destination = s->auth_require; /* T_CONFIG_LOCAL; not modified by config_insert_values_global() */ - cv[2].destination = &s->auth_extern_authn; - - 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_backend_conf)) { - s->auth_backend = http_auth_backend_get(s->auth_backend_conf); - if (NULL == s->auth_backend) { - log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf); - - return HANDLER_ERROR; - } - } - - /* no auth.require for this section */ - if (NULL == (da = (const data_array *)array_get_element_klen(config->value, CONST_STR_LEN("auth.require")))) continue; - - if (da->type != TYPE_ARRAY || !array_is_kvarray(&da->value)) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "unexpected value for auth.require; expected ", - "auth.require = ( \"urlpath\" => ( \"option\" => \"value\" ) )"); - return HANDLER_ERROR; - } - - - for (n = 0; n < da->value.used; n++) { +static handler_t mod_auth_require_parse_array(server *srv, const array *value, array * const auth_require) +{ + for (uint32_t n = 0; n < value->used; ++n) { size_t m; - data_array *da_file = (data_array *)da->value.data[n]; + data_array *da_file = (data_array *)value->data[n]; const buffer *method = NULL, *realm = NULL, *require = NULL; const http_auth_scheme_t *auth_scheme; buffer *algos = NULL; @@ -417,54 +362,125 @@ SETDEFAULTS_FUNC(mod_auth_set_defaults) { dauth->fn->free((data_unset *)dauth); return HANDLER_ERROR; } - array_insert_unique(s->auth_require, (data_unset *)dauth); + array_insert_unique(auth_require, (data_unset *)dauth); } } - } return HANDLER_GO_ON; } -#define PATCH(x) \ - p->conf.x = s->x; -static int mod_auth_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH(auth_backend); - PATCH(auth_require); - PATCH(auth_extern_authn); - - /* skip the first, the global context */ - for (i = 1; i < srv->config_context->used; i++) { - if (!config_check_cond(con, i)) continue; /* condition not matched */ - - data_config *dc = (data_config *)srv->config_context->data[i]; - s = p->config_storage[i]; - - /* 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"))) { - PATCH(auth_backend); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.require"))) { - PATCH(auth_require); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.extern-authn"))) { - PATCH(auth_extern_authn); - } - } - } +static void mod_auth_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { + switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ + case 0: /* auth.backend */ + if (cpv->vtype == T_CONFIG_LOCAL) + pconf->auth_backend = cpv->v.v; + break; + case 1: /* auth.require */ + if (cpv->vtype == T_CONFIG_LOCAL) + pconf->auth_require = cpv->v.v; + break; + case 2: /* auth.extern-authn */ + pconf->auth_extern_authn = cpv->v.u; + default:/* should not happen */ + return; + } +} - return 0; +static void mod_auth_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { + do { + mod_auth_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} + +static void mod_auth_patch_config(connection * const con, plugin_data * const p) { + memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); + for (int i = 1, used = p->nconfig; i < used; ++i) { + if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id)) + mod_auth_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); + } +} + +SETDEFAULTS_FUNC(mod_auth_set_defaults) { + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("auth.backend"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.require"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.extern-authn"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } + }; + + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_auth")) + return HANDLER_ERROR; + + /* process and validate config directives + * (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 0: /* auth.backend */ + if (!buffer_string_is_empty(cpv->v.b)) { + const http_auth_backend_t * const auth_backend = + http_auth_backend_get(cpv->v.b); + if (NULL == auth_backend) { + log_error(srv->errh, __FILE__, __LINE__, + "auth.backend not supported: %s", cpv->v.b->ptr); + return HANDLER_ERROR; + } + *(const http_auth_backend_t **)&cpv->v.v = auth_backend; + cpv->vtype = T_CONFIG_LOCAL; + } + break; + case 1: /* auth.require */ + if (array_is_kvarray(cpv->v.a)) { + array * const a = array_init(); + if (HANDLER_GO_ON != + mod_auth_require_parse_array(srv, cpv->v.a, a)) { + array_free(a); + return HANDLER_ERROR; + } + cpv->v.a = a; + cpv->vtype = T_CONFIG_LOCAL; + } + else { + log_error(srv->errh, __FILE__, __LINE__, + "unexpected value for %s; expected " + "%s = ( \"urlpath\" => ( \"option\" => \"value\" ) )", + cpk[cpv->k_id].k, cpk[cpv->k_id].k); + return HANDLER_ERROR; + } + break; + case 2: /* auth.extern-authn */ + break; + default:/* should not happen */ + break; + } + } + } + + /* initialize p->defaults from global config context */ + if (p->nconfig > 0 && p->cvlist->v.u2[1]) { + const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; + if (-1 != cpv->k_id) + mod_auth_merge_config(&p->defaults, cpv); + } + + return HANDLER_GO_ON; } -#undef PATCH static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; data_auth *dauth; - mod_auth_patch_connection(srv, con, p); + mod_auth_patch_config(con, p); if (p->conf.auth_require == NULL) return HANDLER_GO_ON; diff --git a/src/mod_authn_file.c b/src/mod_authn_file.c index 8cd5d0d4..d7c149a2 100644 --- a/src/mod_authn_file.c +++ b/src/mod_authn_file.c @@ -47,15 +47,15 @@ */ typedef struct { - buffer *auth_plain_groupfile; - buffer *auth_plain_userfile; - buffer *auth_htdigest_userfile; - buffer *auth_htpasswd_userfile; + const buffer *auth_plain_groupfile; + const buffer *auth_plain_userfile; + const buffer *auth_htdigest_userfile; + const buffer *auth_htpasswd_userfile; } plugin_config; typedef struct { PLUGIN_DATA; - plugin_config **config_storage; + plugin_config defaults; plugin_config conf; } plugin_data; @@ -91,110 +91,81 @@ INIT_FUNC(mod_authn_file_init) { 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(p->cvlist); + free(p); + UNUSED(srv); + return HANDLER_GO_ON; +} - free(s); - } - free(p->config_storage); +static void mod_authn_file_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { + switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ + case 0: /* auth.backend.plain.groupfile */ + pconf->auth_plain_groupfile = cpv->v.b; + break; + case 1: /* auth.backend.plain.userfile */ + pconf->auth_plain_userfile = cpv->v.b; + break; + case 2: /* auth.backend.htdigest.userfile */ + pconf->auth_htdigest_userfile = cpv->v.b; + break; + case 3: /* auth.backend.htpasswd.userfile */ + pconf->auth_htpasswd_userfile = cpv->v.b; + break; + default:/* should not happen */ + return; } +} - free(p); +static void mod_authn_file_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { + do { + mod_authn_file_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} - return HANDLER_GO_ON; +static void mod_authn_file_patch_config(connection * const con, plugin_data * const p) { + memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); + for (int i = 1, used = p->nconfig; i < used; ++i) { + if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id)) + mod_authn_file_merge_config(&p->conf, + p->cvlist + p->cvlist[i].v.u2[0]); + } } 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 } + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("auth.backend.plain.groupfile"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.plain.userfile"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.htdigest.userfile"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.htpasswd.userfile"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } }; - p->config_storage = calloc(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(); + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_authn_file")) + return HANDLER_ERROR; - 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; - } + /* initialize p->defaults from global config context */ + if (p->nconfig > 0 && p->cvlist->v.u2[1]) { + const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; + if (-1 != cpv->k_id) + mod_authn_file_merge_config(&p->defaults, cpv); } 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++) { - if (!config_check_cond(con, i)) continue; /* condition not matched */ - - data_config *dc = (data_config *)srv->config_context->data[i]; - s = p->config_storage[i]; - - /* 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 - @@ -314,7 +285,7 @@ static int mod_authn_file_htdigest_get(server *srv, connection *con, void *p_d, const buffer *auth_fn; FILE *fp; - mod_authn_file_patch_connection(srv, con, p); + mod_authn_file_patch_config(con, p); auth_fn = p->conf.auth_htdigest_userfile; if (buffer_string_is_empty(auth_fn)) return -1; @@ -425,7 +396,7 @@ static handler_t mod_authn_file_plain_digest(server *srv, connection *con, void 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); + mod_authn_file_patch_config(con, p); rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_plain_userfile, ai->username, ai->ulen, password_buf); if (0 == rc) { /* generate password from plain-text */ @@ -439,7 +410,7 @@ static handler_t mod_authn_file_plain_basic(server *srv, connection *con, void * 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); + mod_authn_file_patch_config(con, p); rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_plain_userfile, CONST_BUF_LEN(username), password_buf); if (0 == rc) { rc = http_auth_const_time_memeq_pad(CONST_BUF_LEN(password_buf), pw, strlen(pw)) ? 0 : -1; @@ -663,7 +634,7 @@ static handler_t mod_authn_file_htpasswd_basic(server *srv, connection *con, voi 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); + mod_authn_file_patch_config(con, p); rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_htpasswd_userfile, CONST_BUF_LEN(username), password); if (0 == rc) { char sample[256]; diff --git a/src/mod_authn_gssapi.c b/src/mod_authn_gssapi.c index 23154d98..bc8f6d81 100644 --- a/src/mod_authn_gssapi.c +++ b/src/mod_authn_gssapi.c @@ -30,7 +30,6 @@ #include "http_header.h" #include "base.h" #include "log.h" -#include "md5.h" #include "base64.h" #include <errno.h> @@ -39,14 +38,14 @@ #include <unistd.h> typedef struct { - buffer *auth_gssapi_keytab; - buffer *auth_gssapi_principal; - unsigned short int auth_gssapi_store_creds; + const buffer *auth_gssapi_keytab; + const buffer *auth_gssapi_principal; + unsigned int auth_gssapi_store_creds; } plugin_config; typedef struct { PLUGIN_DATA; - plugin_config **config_storage; + plugin_config defaults; plugin_config conf; } plugin_data; @@ -71,104 +70,78 @@ INIT_FUNC(mod_authn_gssapi_init) { FREE_FUNC(mod_authn_gssapi_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_gssapi_keytab); - buffer_free(s->auth_gssapi_principal); + free(p->cvlist); + free(p); + UNUSED(srv); + return HANDLER_GO_ON; +} - free(s); - } - free(p->config_storage); +static void mod_authn_gssapi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { + switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ + case 0: /* auth.backend.gssapi.keytab */ + pconf->auth_gssapi_keytab = cpv->v.b; + break; + case 1: /* auth.backend.gssapi.principal */ + pconf->auth_gssapi_principal = cpv->v.b; + break; + case 2: /* auth.backend.gssapi.store-creds */ + pconf->auth_gssapi_store_creds = cpv->v.u; + break; + default:/* should not happen */ + return; } +} - free(p); +static void mod_authn_gssapi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { + do { + mod_authn_gssapi_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} - return HANDLER_GO_ON; +static void mod_authn_gssapi_patch_config(connection * const con, plugin_data * const p) { + memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); + for (int i = 1, used = p->nconfig; i < used; ++i) { + if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id)) + mod_authn_gssapi_merge_config(&p->conf, + p->cvlist + p->cvlist[i].v.u2[0]); + } } SETDEFAULTS_FUNC(mod_authn_gssapi_set_defaults) { - plugin_data *p = p_d; - size_t i; - config_values_t cv[] = { - { "auth.backend.gssapi.keytab", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.gssapi.principal", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.gssapi.store-creds",NULL, T_CONFIG_BOOLEAN,T_CONFIG_SCOPE_CONNECTION }, - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("auth.backend.gssapi.keytab"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.gssapi.principal"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.gssapi.store-creds"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } }; - p->config_storage = calloc(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_gssapi_keytab = buffer_init(); - s->auth_gssapi_principal = buffer_init(); + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_authn_gssapi")) + return HANDLER_ERROR; - cv[0].destination = s->auth_gssapi_keytab; - cv[1].destination = s->auth_gssapi_principal; - cv[2].destination = &s->auth_gssapi_store_creds; - /* default enabled for backwards compatibility; disable in future */ - s->auth_gssapi_store_creds = 1; + /* default enabled for backwards compatibility; disable in future */ + p->defaults.auth_gssapi_store_creds = 1; - 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; - } + /* initialize p->defaults from global config context */ + if (p->nconfig > 0 && p->cvlist->v.u2[1]) { + const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; + if (-1 != cpv->k_id) + mod_authn_gssapi_merge_config(&p->defaults, cpv); } return HANDLER_GO_ON; } -#define PATCH(x) \ - p->conf.x = s->x; -static int mod_authn_gssapi_patch_connection(server *srv, connection *con, plugin_data *p) -{ - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH(auth_gssapi_keytab); - PATCH(auth_gssapi_principal); - PATCH(auth_gssapi_store_creds); - - /* skip the first, the global context */ - for (i = 1; i < srv->config_context->used; i++) { - if (!config_check_cond(con, i)) continue; /* condition not matched */ - - data_config *dc = (data_config *)srv->config_context->data[i]; - s = p->config_storage[i]; - - /* 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.gssapi.keytab"))) { - PATCH(auth_gssapi_keytab); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.gssapi.principal"))) { - PATCH(auth_gssapi_principal); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.gssapi.store-creds"))) { - PATCH(auth_gssapi_store_creds); - } - } - } - - return 0; -} -#undef PATCH - static handler_t mod_authn_gssapi_send_400_bad_request (server *srv, connection *con) { UNUSED(srv); @@ -357,7 +330,7 @@ static handler_t mod_authn_gssapi_check_spnego(server *srv, connection *con, plu return mod_authn_gssapi_send_400_bad_request(srv, con); } - mod_authn_gssapi_patch_connection(srv, con, p); + mod_authn_gssapi_patch_config(con, p); { /* ??? Should code = krb5_kt_resolve(kcontext, p->conf.auth_gssapi_keytab->ptr, &keytab); @@ -661,7 +634,7 @@ static handler_t mod_authn_gssapi_basic(server *srv, connection *con, void *p_d, return mod_authn_gssapi_send_401_unauthorized_basic(con); } - mod_authn_gssapi_patch_connection(srv, con, p); + mod_authn_gssapi_patch_config(con, p); code = krb5_init_context(&kcontext); if (code) { @@ -669,6 +642,11 @@ static handler_t mod_authn_gssapi_basic(server *srv, connection *con, void *p_d, return mod_authn_gssapi_send_401_unauthorized_basic(con); /*(well, should be 500)*/ } + if (buffer_string_is_empty(p->conf.auth_gssapi_keytab)) { + log_error_write(srv, __FILE__, __LINE__, "s", "auth.backend.gssapi.keytab not configured"); + return mod_authn_gssapi_send_401_unauthorized_basic(con); /*(well, should be 500)*/ + } + code = krb5_kt_resolve(kcontext, p->conf.auth_gssapi_keytab->ptr, &keytab); if (code) { log_error_write(srv, __FILE__, __LINE__, "sdb", "krb5_kt_resolve():", code, p->conf.auth_gssapi_keytab); diff --git a/src/mod_authn_ldap.c b/src/mod_authn_ldap.c index ee2c5503..0c7699e6 100644 --- a/src/mod_authn_ldap.c +++ b/src/mod_authn_ldap.c @@ -14,24 +14,32 @@ typedef struct { LDAP *ldap; server *srv; + const char *auth_ldap_hostname; + const char *auth_ldap_binddn; + const char *auth_ldap_bindpw; + const char *auth_ldap_cafile; + int auth_ldap_starttls; +} plugin_config_ldap; - buffer *auth_ldap_hostname; - buffer *auth_ldap_basedn; - buffer *auth_ldap_binddn; - buffer *auth_ldap_bindpw; - buffer *auth_ldap_filter; - buffer *auth_ldap_cafile; - buffer *auth_ldap_groupmember; - unsigned short auth_ldap_starttls; - unsigned short auth_ldap_allow_empty_pw; +typedef struct { + plugin_config_ldap *ldc; + const char *auth_ldap_basedn; + const buffer *auth_ldap_filter; + const buffer *auth_ldap_groupmember; + int auth_ldap_allow_empty_pw; + + int auth_ldap_starttls; + const char *auth_ldap_binddn; + const char *auth_ldap_bindpw; + const char *auth_ldap_cafile; } 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 */ + plugin_config defaults; + plugin_config conf; - buffer *ldap_filter; + buffer ldap_filter; } plugin_data; static handler_t mod_authn_ldap_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); @@ -40,7 +48,6 @@ 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; @@ -49,39 +56,89 @@ INIT_FUNC(mod_authn_ldap_init) { return p; } +static void mod_authn_ldap_free_config(plugin_data *p) { + if (NULL == p->cvlist) return; + /* (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 0: /* auth.backend.ldap.hostname */ + if (cpv->vtype == T_CONFIG_LOCAL) { + plugin_config_ldap *s = cpv->v.v; + if (NULL != s->ldap) ldap_unbind_ext_s(s->ldap, NULL, NULL); + free(s); + } + break; + default: + break; + } + } + } +} + 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; + mod_authn_ldap_free_config(p); + free(p->ldap_filter.ptr); - 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->auth_ldap_groupmember); + free(p->cvlist); + free(p); + UNUSED(srv); + return HANDLER_GO_ON; +} - if (NULL != s->ldap) ldap_unbind_ext_s(s->ldap, NULL, NULL); - free(s); - } - free(p->config_storage); +static void mod_authn_ldap_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { + switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ + case 0: /* auth.backend.ldap.hostname */ + if (cpv->vtype == T_CONFIG_LOCAL) + pconf->ldc = cpv->v.v; + break; + case 1: /* auth.backend.ldap.base-dn */ + if (cpv->vtype == T_CONFIG_LOCAL) + pconf->auth_ldap_basedn = cpv->v.v; + break; + case 2: /* auth.backend.ldap.filter */ + pconf->auth_ldap_filter = cpv->v.v; + break; + case 3: /* auth.backend.ldap.ca-file */ + pconf->auth_ldap_cafile = cpv->v.v; + break; + case 4: /* auth.backend.ldap.starttls */ + pconf->auth_ldap_starttls = (int)cpv->v.u; + break; + case 5: /* auth.backend.ldap.bind-dn */ + pconf->auth_ldap_binddn = cpv->v.v; + break; + case 6: /* auth.backend.ldap.bind-pw */ + pconf->auth_ldap_bindpw = cpv->v.v; + break; + case 7: /* auth.backend.ldap.allow-empty-pw */ + pconf->auth_ldap_allow_empty_pw = (int)cpv->v.u; + break; + case 8: /* auth.backend.ldap.groupmember */ + pconf->auth_ldap_groupmember = cpv->v.b; + break; + default:/* should not happen */ + return; } +} - free(p); +static void mod_authn_ldap_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { + do { + mod_authn_ldap_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} - return HANDLER_GO_ON; +static void mod_authn_ldap_patch_config(connection * const con, plugin_data * const p) { + memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); + for (int i = 1, used = p->nconfig; i < used; ++i) { + if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id)) + mod_authn_ldap_merge_config(&p->conf, + p->cvlist + p->cvlist[i].v.u2[0]); + } } /*(copied from mod_vhostdb_ldap.c)*/ @@ -118,133 +175,155 @@ static void mod_authn_add_scheme (server *srv, buffer *host) } 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 */ - { "auth.backend.ldap.groupmember", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("auth.backend.ldap.hostname"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.ldap.base-dn"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.ldap.filter"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.ldap.ca-file"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.ldap.starttls"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.ldap.bind-dn"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.ldap.bind-pw"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.ldap.allow-empty-pw"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.ldap.groupmember"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } }; - p->config_storage = calloc(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_groupmember = buffer_init_string("memberUid"); - s->auth_ldap_starttls = 0; - 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); - cv[8].destination = s->auth_ldap_groupmember; - - 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; - } + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_authn_ldap")) + return HANDLER_ERROR; - if (!buffer_string_is_empty(s->auth_ldap_filter)) { - if (*s->auth_ldap_filter->ptr != ',') { - /*(translate '$' to '?' for consistency with other modules)*/ - char *d = s->auth_ldap_filter->ptr; - for (; NULL != (d = strchr(d, '$')); ++d) *d = '?'; - if (NULL == 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; + /* process and validate config directives + * (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + plugin_config_ldap *ldc = NULL; + char *binddn = NULL, *bindpw = NULL, *cafile = NULL; + int starttls = 0; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 0: /* auth.backend.ldap.hostname */ + if (!buffer_string_is_empty(cpv->v.b)) { + buffer *b; + *(const buffer **)&b = cpv->v.b; + mod_authn_add_scheme(srv, b); + ldc = malloc(sizeof(plugin_config_ldap)); + force_assert(ldc); + ldc->srv = srv; + ldc->auth_ldap_hostname = b->ptr; + cpv->v.v = ldc; + } + else { + cpv->v.v = NULL; } + cpv->vtype = T_CONFIG_LOCAL; + break; + case 1: /* auth.backend.ldap.base-dn */ + cpv->vtype = T_CONFIG_LOCAL; + cpv->v.v = !buffer_string_is_empty(cpv->v.b) + ? cpv->v.b->ptr + : NULL; + break; + case 2: /* auth.backend.ldap.filter */ + if (!buffer_string_is_empty(cpv->v.b)) { + buffer *b; + *(const buffer **)&b = cpv->v.b; + if (*b->ptr != ',') { + /*(translate $ to ? for consistency w/ other modules)*/ + char *d = b->ptr; + for (; NULL != (d = strchr(d, '$')); ++d) *d = '?'; + if (NULL == strchr(b->ptr, '?')) { + log_error(srv->errh, __FILE__, __LINE__, + "ldap: %s is missing a replace-operator '?'", + cpk[cpv->k_id].k); + return HANDLER_ERROR; + } + } + cpv->v.v = b; + } + else { + cpv->v.v = NULL; + } + cpv->vtype = T_CONFIG_LOCAL; + break; + case 3: /* auth.backend.ldap.ca-file */ + cafile = !buffer_string_is_empty(cpv->v.b) + ? cpv->v.b->ptr + : NULL; + cpv->vtype = T_CONFIG_LOCAL; + cpv->v.v = cafile; + break; + case 4: /* auth.backend.ldap.starttls */ + starttls = (int)cpv->v.u; + break; + case 5: /* auth.backend.ldap.bind-dn */ + binddn = !buffer_string_is_empty(cpv->v.b) + ? cpv->v.b->ptr + : NULL; + cpv->vtype = T_CONFIG_LOCAL; + cpv->v.v = binddn; + break; + case 6: /* auth.backend.ldap.bind-pw */ + cpv->vtype = T_CONFIG_LOCAL; + cpv->v.v = bindpw = cpv->v.b->ptr; + break; + case 7: /* auth.backend.ldap.allow-empty-pw */ + case 8: /* auth.backend.ldap.groupmember */ + break; + default:/* should not happen */ + break; } } - mod_authn_add_scheme(srv, s->auth_ldap_hostname); + if (ldc) { + ldc->auth_ldap_binddn = binddn; + ldc->auth_ldap_bindpw = bindpw; + ldc->auth_ldap_cafile = cafile; + ldc->auth_ldap_starttls = starttls; + } } - return HANDLER_GO_ON; -} + static const struct { const char *ptr; uint32_t used; uint32_t size; } + memberUid = { "memberUid", sizeof("memberUid"), 0 }; + *(const buffer **)&p->defaults.auth_ldap_groupmember = + (const buffer *)&memberUid; -#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); - PATCH(auth_ldap_groupmember); - p->anon_conf = s; - - /* skip the first, the global context */ - for (i = 1; i < srv->config_context->used; i++) { - if (!config_check_cond(con, i)) continue; /* condition not matched */ - - data_config *dc = (data_config *)srv->config_context->data[i]; - s = p->config_storage[i]; - - /* 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); - } 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); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.ldap.groupmember"))) { - PATCH(auth_ldap_groupmember); - } - } + /* initialize p->defaults from global config context */ + if (p->nconfig > 0 && p->cvlist->v.u2[1]) { + const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; + if (-1 != cpv->k_id) + mod_authn_ldap_merge_config(&p->defaults, cpv); } - return 0; + return HANDLER_GO_ON; } -#undef PATCH +__attribute_cold__ static void mod_authn_ldap_err(server *srv, const char *file, unsigned long line, const char *fn, int err) { log_error_write(srv,file,line,"sSss","ldap:",fn,":",ldap_err2string(err)); } +__attribute_cold__ static void mod_authn_ldap_opt_err(server *srv, const char *file, unsigned long line, const char *fn, LDAP *ld) { int err; @@ -383,13 +462,13 @@ static void mod_authn_append_ldap_filter_escape(buffer * const filter, const buf } } -static LDAP * mod_authn_ldap_host_init(server *srv, plugin_config *s) { +static LDAP * mod_authn_ldap_host_init(server *srv, plugin_config_ldap *s) { LDAP *ld; int ret; - if (buffer_string_is_empty(s->auth_ldap_hostname)) return NULL; + if (NULL == s->auth_ldap_hostname) return NULL; - if (LDAP_SUCCESS != ldap_initialize(&ld, s->auth_ldap_hostname->ptr)) { + if (LDAP_SUCCESS != ldap_initialize(&ld, s->auth_ldap_hostname)) { log_error_write(srv, __FILE__, __LINE__, "sss", "ldap:", "ldap_initialize():", strerror(errno)); return NULL; @@ -409,9 +488,9 @@ static LDAP * mod_authn_ldap_host_init(server *srv, plugin_config *s) { 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 (s->auth_ldap_cafile) { ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, - s->auth_ldap_cafile->ptr); + s->auth_ldap_cafile); if (LDAP_OPT_SUCCESS != ret) { mod_authn_ldap_err(srv, __FILE__, __LINE__, "ldap_set_option(LDAP_OPT_X_TLS_CACERTFILE)", @@ -455,18 +534,18 @@ static int mod_authn_ldap_bind(server *srv, LDAP *ld, const char *dn, const char } static int mod_authn_ldap_rebind_proc (LDAP *ld, LDAP_CONST char *url, ber_tag_t ldap_request, ber_int_t msgid, void *params) { - plugin_config *s = (plugin_config *)params; + const plugin_config_ldap *s = (const plugin_config_ldap *)params; UNUSED(url); UNUSED(ldap_request); UNUSED(msgid); - return !buffer_string_is_empty(s->auth_ldap_binddn) + return s->auth_ldap_binddn ? mod_authn_ldap_bind(s->srv, ld, - s->auth_ldap_binddn->ptr, - s->auth_ldap_bindpw->ptr) + s->auth_ldap_binddn, + s->auth_ldap_bindpw) : mod_authn_ldap_bind(s->srv, ld, NULL, NULL); } -static LDAPMessage * mod_authn_ldap_search(server *srv, plugin_config *s, char *base, char *filter) { +static LDAPMessage * mod_authn_ldap_search(server *srv, plugin_config_ldap *s, const char *base, const char *filter) { LDAPMessage *lm = NULL; char *attrs[] = { LDAP_NO_ATTRS, NULL }; int ret; @@ -521,7 +600,7 @@ static LDAPMessage * mod_authn_ldap_search(server *srv, plugin_config *s, char * return lm; } -static char * mod_authn_ldap_get_dn(server *srv, plugin_config *s, char *base, char *filter) { +static char * mod_authn_ldap_get_dn(server *srv, plugin_config_ldap *s, const char *base, const char *filter) { LDAP *ld; LDAPMessage *lm, *first; char *dn; @@ -576,11 +655,12 @@ static handler_t mod_authn_ldap_memberOf(server *srv, plugin_config *s, const ht } buffer_append_string_len(filter, CONST_STR_LEN(")")); + plugin_config_ldap * const ldc = s->ldc; for (size_t i = 0; i < groups->used; ++i) { - char *base = groups->data[i]->key.ptr; - LDAPMessage *lm = mod_authn_ldap_search(srv, s, base, filter->ptr); + const char *base = groups->data[i]->key.ptr; + LDAPMessage *lm = mod_authn_ldap_search(srv, ldc, base, filter->ptr); if (NULL != lm) { - int count = ldap_count_entries(s->ldap, lm); + int count = ldap_count_entries(ldc->ldap, lm); ldap_msgfree(lm); if (count > 0) { rc = HANDLER_GO_ON; @@ -597,88 +677,101 @@ static handler_t mod_authn_ldap_basic(server *srv, connection *con, void *p_d, c plugin_data *p = (plugin_data *)p_d; LDAP *ld; char *dn; - buffer *template; - handler_t rc; - mod_authn_ldap_patch_connection(srv, con, p); - p->anon_conf->srv = srv; - p->conf.srv = srv; + mod_authn_ldap_patch_config(con, p); if (pw[0] == '\0' && !p->conf.auth_ldap_allow_empty_pw) return HANDLER_ERROR; - template = p->conf.auth_ldap_filter; - if (buffer_string_is_empty(template)) { + const buffer * const template = p->conf.auth_ldap_filter; + if (NULL == template) return HANDLER_ERROR; - } /* build filter to get DN for uid = username */ - buffer_clear(p->ldap_filter); + buffer * const ldap_filter = &p->ldap_filter; + buffer_clear(ldap_filter); if (*template->ptr == ',') { /* special-case filter template beginning with ',' to be explicit DN */ - buffer_append_string_len(p->ldap_filter, CONST_STR_LEN("uid=")); - mod_authn_append_ldap_dn_escape(p->ldap_filter, username); - buffer_append_string_buffer(p->ldap_filter, template); - dn = p->ldap_filter->ptr; - } else { - for (char *b = template->ptr, *d; *b; b = d+1) { + buffer_append_string_len(ldap_filter, CONST_STR_LEN("uid=")); + mod_authn_append_ldap_dn_escape(ldap_filter, username); + buffer_append_string_buffer(ldap_filter, template); + dn = ldap_filter->ptr; + } + else { + for (const char *b = template->ptr, *d; *b; b = d+1) { if (NULL != (d = strchr(b, '?'))) { - buffer_append_string_len(p->ldap_filter, b, (size_t)(d - b)); - mod_authn_append_ldap_filter_escape(p->ldap_filter, username); - } else { + buffer_append_string_len(ldap_filter, b, (size_t)(d - b)); + mod_authn_append_ldap_filter_escape(ldap_filter, username); + } + else { d = template->ptr + buffer_string_length(template); - buffer_append_string_len(p->ldap_filter, b, (size_t)(d - b)); + buffer_append_string_len(ldap_filter, b, (size_t)(d - b)); break; } } /* ldap_search for DN (synchronous; blocking) */ - dn = mod_authn_ldap_get_dn(srv, p->anon_conf, - p->conf.auth_ldap_basedn->ptr, - p->ldap_filter->ptr); - if (NULL == dn) { - return HANDLER_ERROR; - } + dn = mod_authn_ldap_get_dn(srv, p->conf.ldc, + p->conf.auth_ldap_basedn, ldap_filter->ptr); + if (NULL == dn) return HANDLER_ERROR; } - /* auth against LDAP server (synchronous; blocking) */ - - ld = mod_authn_ldap_host_init(srv, &p->conf); - if (NULL == ld) { - if (dn != p->ldap_filter->ptr) ldap_memfree(dn); - return HANDLER_ERROR; + /*(Check ldc here rather than further up to preserve historical behavior + * where p->conf.ldc above (was p->anon_conf above) is set of directives in + * same context as auth_ldap_hostname. Preference: admin intentions are + * clearer if directives are always together in a set in same context)*/ + + plugin_config_ldap * const ldc_base = p->conf.ldc; + plugin_config_ldap ldc_custom; + + if ( p->conf.ldc->auth_ldap_starttls != p->conf.auth_ldap_starttls + || p->conf.ldc->auth_ldap_binddn != p->conf.auth_ldap_binddn + || p->conf.ldc->auth_ldap_bindpw != p->conf.auth_ldap_bindpw + || p->conf.ldc->auth_ldap_cafile != p->conf.auth_ldap_cafile ) { + ldc_custom.ldap = NULL; + ldc_custom.srv = srv; + ldc_custom.auth_ldap_hostname = ldc_base->auth_ldap_hostname; + ldc_custom.auth_ldap_starttls = p->conf.auth_ldap_starttls; + ldc_custom.auth_ldap_binddn = p->conf.auth_ldap_binddn; + ldc_custom.auth_ldap_bindpw = p->conf.auth_ldap_bindpw; + ldc_custom.auth_ldap_cafile = p->conf.auth_ldap_cafile; + p->conf.ldc = &ldc_custom; } - /* Disable referral tracking. Target user should be in provided scope */ - { + handler_t rc = HANDLER_ERROR; + do { + /* auth against LDAP server (synchronous; blocking) */ + + ld = mod_authn_ldap_host_init(srv, p->conf.ldc); + if (NULL == ld) + break; + + /* Disable referral tracking; target user should be in provided scope */ int ret = ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); if (LDAP_OPT_SUCCESS != ret) { mod_authn_ldap_err(srv,__FILE__,__LINE__,"ldap_set_option()",ret); - ldap_destroy(ld); - if (dn != p->ldap_filter->ptr) ldap_memfree(dn); - return HANDLER_ERROR; + break; } - } - if (LDAP_SUCCESS != mod_authn_ldap_bind(srv, ld, dn, pw)) { - ldap_destroy(ld); - if (dn != p->ldap_filter->ptr) ldap_memfree(dn); - return HANDLER_ERROR; - } + if (LDAP_SUCCESS != mod_authn_ldap_bind(srv, ld, dn, pw)) + break; - ldap_unbind_ext_s(ld, NULL, NULL); /* disconnect */ + ldap_unbind_ext_s(ld, NULL, NULL); /* disconnect */ + ld = NULL; - if (http_auth_match_rules(require, username->ptr, NULL, NULL)) { - rc = HANDLER_GO_ON; /* access granted */ - } else { - rc = HANDLER_ERROR; - if (require->group->used) { - /*(must not re-use p->ldap_filter, since it might be used for dn)*/ - rc = mod_authn_ldap_memberOf(srv, &p->conf, require, username, dn); + if (http_auth_match_rules(require, username->ptr, NULL, NULL)) { + rc = HANDLER_GO_ON; /* access granted */ } - } + else if (require->group->used) { + /*(must not re-use ldap_filter, since it might be used for dn)*/ + rc = mod_authn_ldap_memberOf(srv,&p->conf,require,username,dn); + } + } while (0); - if (dn != p->ldap_filter->ptr) ldap_memfree(dn); + if (NULL != ld) ldap_destroy(ld); + if (ldc_base != p->conf.ldc && NULL != p->conf.ldc->ldap) + ldap_unbind_ext_s(p->conf.ldc->ldap, NULL, NULL); + if (dn != ldap_filter->ptr) ldap_memfree(dn); return rc; } diff --git a/src/mod_authn_mysql.c b/src/mod_authn_mysql.c index dc11a706..1195aad8 100644 --- a/src/mod_authn_mysql.c +++ b/src/mod_authn_mysql.c @@ -39,99 +39,105 @@ #endif typedef struct { - MYSQL *mysql_conn; - buffer *mysql_conn_host; - buffer *mysql_conn_user; - buffer *mysql_conn_pass; - buffer *mysql_conn_db; - int mysql_conn_port; int auth_mysql_port; - buffer *auth_mysql_host; - buffer *auth_mysql_user; - buffer *auth_mysql_pass; - buffer *auth_mysql_db; - buffer *auth_mysql_socket; - buffer *auth_mysql_users_table; - buffer *auth_mysql_col_user; - buffer *auth_mysql_col_pass; - buffer *auth_mysql_col_realm; + const char *auth_mysql_host; + const char *auth_mysql_user; + const char *auth_mysql_pass; + const char *auth_mysql_db; + const char *auth_mysql_socket; + const char *auth_mysql_users_table; + const char *auth_mysql_col_user; + const char *auth_mysql_col_pass; + const char *auth_mysql_col_realm; } plugin_config; typedef struct { PLUGIN_DATA; - plugin_config **config_storage; + plugin_config defaults; plugin_config conf; + + MYSQL *mysql_conn; + const char *mysql_conn_host; + const char *mysql_conn_user; + const char *mysql_conn_pass; + const char *mysql_conn_db; + int mysql_conn_port; } plugin_data; -static void mod_authn_mysql_sock_close(plugin_config *pconf) { - if (NULL != pconf->mysql_conn) { - mysql_close(pconf->mysql_conn); - pconf->mysql_conn = NULL; +static void mod_authn_mysql_sock_close(plugin_data *p) { + if (NULL != p->mysql_conn) { + mysql_close(p->mysql_conn); + p->mysql_conn = NULL; } } -static MYSQL * mod_authn_mysql_sock_connect(server *srv, plugin_config *pconf) { - if (NULL != pconf->mysql_conn) { +static MYSQL * mod_authn_mysql_sock_connect(server *srv, plugin_data *p) { + plugin_config * const pconf = &p->conf; + if (NULL != p->mysql_conn) { /* reuse open db connection if same ptrs to host user pass db port */ - if ( pconf->mysql_conn_host == pconf->auth_mysql_host - && pconf->mysql_conn_user == pconf->auth_mysql_user - && pconf->mysql_conn_pass == pconf->auth_mysql_pass - && pconf->mysql_conn_db == pconf->auth_mysql_db - && pconf->mysql_conn_port == pconf->auth_mysql_port) { - return pconf->mysql_conn; + if ( p->mysql_conn_host == pconf->auth_mysql_host + && p->mysql_conn_user == pconf->auth_mysql_user + && p->mysql_conn_pass == pconf->auth_mysql_pass + && p->mysql_conn_db == pconf->auth_mysql_db + && p->mysql_conn_port == pconf->auth_mysql_port) { + return p->mysql_conn; } - mod_authn_mysql_sock_close(pconf); + mod_authn_mysql_sock_close(p); } /* !! mysql_init() is not thread safe !! (see MySQL doc) */ - pconf->mysql_conn = mysql_init(NULL); - if (mysql_real_connect(pconf->mysql_conn, - pconf->auth_mysql_host->ptr, - pconf->auth_mysql_user->ptr, - pconf->auth_mysql_pass->ptr, - pconf->auth_mysql_db->ptr, + p->mysql_conn = mysql_init(NULL); + if (mysql_real_connect(p->mysql_conn, + pconf->auth_mysql_host, + pconf->auth_mysql_user, + pconf->auth_mysql_pass, + pconf->auth_mysql_db, pconf->auth_mysql_port, - !buffer_string_is_empty(pconf->auth_mysql_socket) - ? pconf->auth_mysql_socket->ptr + (pconf->auth_mysql_socket && *pconf->auth_mysql_socket) + ? pconf->auth_mysql_socket : NULL, CLIENT_IGNORE_SIGPIPE)) { - /* (copy ptrs to config data (has lifetime until server shutdown)) */ - pconf->mysql_conn_host = pconf->auth_mysql_host; - pconf->mysql_conn_user = pconf->auth_mysql_user; - pconf->mysql_conn_pass = pconf->auth_mysql_pass; - pconf->mysql_conn_db = pconf->auth_mysql_db; - pconf->mysql_conn_port = pconf->auth_mysql_port; - return pconf->mysql_conn; + /* (copy ptrs to plugin data (has lifetime until server shutdown)) */ + p->mysql_conn_host = pconf->auth_mysql_host; + p->mysql_conn_user = pconf->auth_mysql_user; + p->mysql_conn_pass = pconf->auth_mysql_pass; + p->mysql_conn_db = pconf->auth_mysql_db; + p->mysql_conn_port = pconf->auth_mysql_port; + return p->mysql_conn; } else { - /*(note: any of these params might be buffers with b->ptr == NULL)*/ - log_error_write(srv, __FILE__, __LINE__, "sbsb"/*sb*/"sbss", - "opening connection to mysql:", pconf->auth_mysql_host, - "user:", pconf->auth_mysql_user, - /*"pass:", pconf->auth_mysql_pass,*//*(omit from logs)*/ - "db:", pconf->auth_mysql_db, - "failed:", mysql_error(pconf->mysql_conn)); - mod_authn_mysql_sock_close(pconf); + /*(note: any of these params might be NULL)*/ + log_error_write(srv, __FILE__, __LINE__, "ssss"/*ss*/"ssss", + "opening connection to mysql:", + pconf->auth_mysql_host ? pconf->auth_mysql_host : NULL, + "user:", + pconf->auth_mysql_user ? pconf->auth_mysql_user : NULL, + /*"pass:",*//*(omit pass from logs)*/ + /*pconf->auth_mysql_pass ? pconf->auth_mysql_pass : NULL,*/ + "db:", + pconf->auth_mysql_db ? pconf->auth_mysql_db : NULL, + "failed:", mysql_error(p->mysql_conn)); + mod_authn_mysql_sock_close(p); return NULL; } } -static MYSQL * mod_authn_mysql_sock_acquire(server *srv, plugin_config *pconf) { - return mod_authn_mysql_sock_connect(srv, pconf); +static MYSQL * mod_authn_mysql_sock_acquire(server *srv, plugin_data *p) { + return mod_authn_mysql_sock_connect(srv, p); } -static void mod_authn_mysql_sock_release(server *srv, plugin_config *pconf) { +static void mod_authn_mysql_sock_release(server *srv, plugin_data *p) { UNUSED(srv); - UNUSED(pconf); + UNUSED(p); /*(empty; leave db connection open)*/ /* Note: mod_authn_mysql_result() calls mod_authn_mysql_sock_error() * on error, so take that into account if making changes here. - * Must check if (NULL == pconf->mysql_conn) */ + * Must check if (NULL == p->mysql_conn) */ } -static void mod_authn_mysql_sock_error(server *srv, plugin_config *pconf) { +static void mod_authn_mysql_sock_error(server *srv, plugin_data *p) { UNUSED(srv); - mod_authn_mysql_sock_close(pconf); + mod_authn_mysql_sock_close(p); } static handler_t mod_authn_mysql_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); @@ -151,185 +157,151 @@ INIT_FUNC(mod_authn_mysql_init) { FREE_FUNC(mod_authn_mysql_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]; + mod_authn_mysql_sock_close(p); - if (NULL == s) continue; - - buffer_free(s->auth_mysql_host); - buffer_free(s->auth_mysql_user); - buffer_free(s->auth_mysql_pass); - buffer_free(s->auth_mysql_db); - buffer_free(s->auth_mysql_socket); - buffer_free(s->auth_mysql_users_table); - buffer_free(s->auth_mysql_col_user); - buffer_free(s->auth_mysql_col_pass); - buffer_free(s->auth_mysql_col_realm); - - if (s->mysql_conn) mod_authn_mysql_sock_close(s); + free(p->cvlist); + free(p); + UNUSED(srv); + return HANDLER_GO_ON; +} - free(s); - } - free(p->config_storage); +static void mod_authn_mysql_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { + switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ + case 0: /* auth.backend.mysql.host */ + pconf->auth_mysql_host = cpv->v.b->ptr; + break; + case 1: /* auth.backend.mysql.user */ + pconf->auth_mysql_user = cpv->v.b->ptr; + break; + case 2: /* auth.backend.mysql.pass */ + pconf->auth_mysql_pass = cpv->v.b->ptr; + break; + case 3: /* auth.backend.mysql.db */ + pconf->auth_mysql_db = cpv->v.b->ptr; + break; + case 4: /* auth.backend.mysql.port */ + pconf->auth_mysql_port = (int)cpv->v.shrt; + break; + case 5: /* auth.backend.mysql.socket */ + pconf->auth_mysql_socket = cpv->v.b->ptr; + break; + case 6: /* auth.backend.mysql.users_table */ + pconf->auth_mysql_users_table = cpv->v.b->ptr; + break; + case 7: /* auth.backend.mysql.col_user */ + pconf->auth_mysql_col_user = cpv->v.b->ptr; + break; + case 8: /* auth.backend.mysql.col_pass */ + pconf->auth_mysql_col_pass = cpv->v.b->ptr; + break; + case 9: /* auth.backend.mysql.col_realm */ + pconf->auth_mysql_col_realm = cpv->v.b->ptr; + break; + default:/* should not happen */ + return; } - mod_authn_mysql_sock_close(&p->conf); +} - free(p); +static void mod_authn_mysql_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { + do { + mod_authn_mysql_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} - return HANDLER_GO_ON; +static void mod_authn_mysql_patch_config(connection * const con, plugin_data * const p) { + memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); + for (int i = 1, used = p->nconfig; i < used; ++i) { + if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id)) + mod_authn_mysql_merge_config(&p->conf, + p->cvlist + p->cvlist[i].v.u2[0]); + } } SETDEFAULTS_FUNC(mod_authn_mysql_set_defaults) { - plugin_data *p = p_d; - size_t i; - config_values_t cv[] = { - { "auth.backend.mysql.host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.mysql.user", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.mysql.pass", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.mysql.db", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.mysql.port", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.mysql.socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.mysql.users_table", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.mysql.col_user", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.mysql.col_pass", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "auth.backend.mysql.col_realm", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("auth.backend.mysql.host"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.mysql.user"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.mysql.pass"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.mysql.db"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.mysql.port"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.mysql.socket"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.mysql.users_table"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.mysql.col_user"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.mysql.col_pass"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("auth.backend.mysql.col_realm"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } }; - p->config_storage = calloc(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->mysql_conn = NULL; - s->auth_mysql_host = buffer_init(); - s->auth_mysql_user = buffer_init(); - s->auth_mysql_pass = buffer_init(); - s->auth_mysql_db = buffer_init(); - s->auth_mysql_socket = buffer_init(); - s->auth_mysql_users_table = buffer_init(); - s->auth_mysql_col_user = buffer_init(); - s->auth_mysql_col_pass = buffer_init(); - s->auth_mysql_col_realm = buffer_init(); - - cv[0].destination = s->auth_mysql_host; - cv[1].destination = s->auth_mysql_user; - cv[2].destination = s->auth_mysql_pass; - cv[3].destination = s->auth_mysql_db; - cv[4].destination = &s->auth_mysql_port; - cv[5].destination = s->auth_mysql_socket; - cv[6].destination = s->auth_mysql_users_table; - cv[7].destination = s->auth_mysql_col_user; - cv[8].destination = s->auth_mysql_col_pass; - cv[9].destination = s->auth_mysql_col_realm; - - 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_is_empty(s->auth_mysql_col_user) - && buffer_string_is_empty(s->auth_mysql_col_user)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "auth.backend.mysql.col_user must not be blank"); - return HANDLER_ERROR; - } - if (!buffer_is_empty(s->auth_mysql_col_pass) - && buffer_string_is_empty(s->auth_mysql_col_pass)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "auth.backend.mysql.col_pass must not be blank"); - return HANDLER_ERROR; - } - if (!buffer_is_empty(s->auth_mysql_col_realm) - && buffer_string_is_empty(s->auth_mysql_col_realm)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "auth.backend.mysql.col_realm must not be blank"); - return HANDLER_ERROR; - } - } + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_authn_mysql")) + return HANDLER_ERROR; - if (p->config_storage[0]) { /*(always true)*/ - plugin_config *s = p->config_storage[0]; - if (buffer_is_empty(s->auth_mysql_col_user)) { - buffer_copy_string_len(s->auth_mysql_col_user, CONST_STR_LEN("user")); - } - if (buffer_is_empty(s->auth_mysql_col_pass)) { - buffer_copy_string_len(s->auth_mysql_col_pass, CONST_STR_LEN("password")); - } - if (buffer_is_empty(s->auth_mysql_col_realm)) { - buffer_copy_string_len(s->auth_mysql_col_realm, CONST_STR_LEN("realm")); + /* process and validate config directives + * (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 0: /* auth.backend.mysql.host */ + case 1: /* auth.backend.mysql.user */ + case 2: /* auth.backend.mysql.pass */ + case 3: /* auth.backend.mysql.db */ + case 4: /* auth.backend.mysql.port */ + case 5: /* auth.backend.mysql.socket */ + case 6: /* auth.backend.mysql.users_table */ + break; + case 7: /* auth.backend.mysql.col_user */ + case 8: /* auth.backend.mysql.col_pass */ + case 9: /* auth.backend.mysql.col_realm */ + if (buffer_string_is_empty(cpv->v.b)) { + log_error(srv->errh, __FILE__, __LINE__, + "%s must not be blank", cpk[cpv->k_id].k); + return HANDLER_ERROR; + } + break; + default:/* should not happen */ + break; + } } } - return HANDLER_GO_ON; -} + p->defaults.auth_mysql_col_user = "user"; + p->defaults.auth_mysql_col_pass = "password"; + p->defaults.auth_mysql_col_realm = "realm"; -#define PATCH(x) \ - p->conf.x = s->x; -static int mod_authn_mysql_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH(auth_mysql_host); - PATCH(auth_mysql_user); - PATCH(auth_mysql_pass); - PATCH(auth_mysql_db); - PATCH(auth_mysql_port); - PATCH(auth_mysql_socket); - PATCH(auth_mysql_users_table); - PATCH(auth_mysql_col_user); - PATCH(auth_mysql_col_pass); - PATCH(auth_mysql_col_realm); - - /* skip the first, the global context */ - for (i = 1; i < srv->config_context->used; i++) { - if (!config_check_cond(con, i)) continue; /* condition not matched */ - - data_config *dc = (data_config *)srv->config_context->data[i]; - s = p->config_storage[i]; - - /* 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.mysql.host"))) { - PATCH(auth_mysql_host); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.mysql.user"))) { - PATCH(auth_mysql_user); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.mysql.pass"))) { - PATCH(auth_mysql_pass); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.mysql.db"))) { - PATCH(auth_mysql_db); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.mysql.port"))) { - PATCH(auth_mysql_port); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.mysql.socket"))) { - PATCH(auth_mysql_socket); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.mysql.users_table"))) { - PATCH(auth_mysql_users_table); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.mysql.col_user"))) { - PATCH(auth_mysql_col_user); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.mysql.col_pass"))) { - PATCH(auth_mysql_col_pass); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("auth.backend.mysql.col_realm"))) { - PATCH(auth_mysql_col_realm); - } - } + /* initialize p->defaults from global config context */ + if (p->nconfig > 0 && p->cvlist->v.u2[1]) { + const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; + if (-1 != cpv->k_id) + mod_authn_mysql_merge_config(&p->defaults, cpv); } - return 0; + return HANDLER_GO_ON; } -#undef PATCH static int mod_authn_mysql_password_cmp(const char *userpw, unsigned long userpwlen, const char *reqpw) { #if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT) @@ -387,7 +359,7 @@ static int mod_authn_mysql_password_cmp(const char *userpw, unsigned long userpw } static int mod_authn_mysql_result(server *srv, plugin_data *p, http_auth_info_t *ai, const char *pw) { - MYSQL_RES *result = mysql_store_result(p->conf.mysql_conn); + MYSQL_RES *result = mysql_store_result(p->mysql_conn); int rc = -1; my_ulonglong num_rows; @@ -395,9 +367,9 @@ static int mod_authn_mysql_result(server *srv, plugin_data *p, http_auth_info_t /*(future: might log mysql_error() string)*/ #if 0 log_error_write(srv, __FILE__, __LINE__, "ss", "mysql_store_result:", - mysql_error(p->conf.mysql_conn)); + mysql_error(p->mysql_conn)); #endif - mod_authn_mysql_sock_error(srv, &p->conf); + mod_authn_mysql_sock_error(srv, p); return -1; } @@ -434,9 +406,9 @@ static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d, plugin_data *p = (plugin_data *)p_d; int rc = -1; - mod_authn_mysql_patch_connection(srv, con, p); + mod_authn_mysql_patch_config(con, p); - if (buffer_string_is_empty(p->conf.auth_mysql_users_table)) { + if (NULL == p->conf.auth_mysql_users_table) { /*(auth.backend.mysql.host, auth.backend.mysql.db might be NULL; do not log)*/ log_error_write(srv, __FILE__, __LINE__, "sb", "auth config missing auth.backend.mysql.users_table for uri:", @@ -453,35 +425,35 @@ static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d, if (ai->rlen > sizeof(urealm)/2-1) return HANDLER_ERROR; - if (!mod_authn_mysql_sock_acquire(srv, &p->conf)) { + if (!mod_authn_mysql_sock_acquire(srv, p)) { return HANDLER_ERROR; } #if 0 - mrc = mysql_real_escape_string_quote(p->conf.mysql_conn, uname, + mrc = mysql_real_escape_string_quote(p->mysql_conn, uname, ai->username, ai->ulen, '\''); if ((unsigned long)~0 == mrc) break; - mrc = mysql_real_escape_string_quote(p->conf.mysql_conn, urealm, + mrc = mysql_real_escape_string_quote(p->mysql_conn, urealm, ai->realm, ai->rlen, '\''); if ((unsigned long)~0 == mrc) break; #else - mrc = mysql_real_escape_string(p->conf.mysql_conn, uname, + mrc = mysql_real_escape_string(p->mysql_conn, uname, ai->username, ai->ulen); if ((unsigned long)~0 == mrc) break; - mrc = mysql_real_escape_string(p->conf.mysql_conn, urealm, + mrc = mysql_real_escape_string(p->mysql_conn, urealm, ai->realm, ai->rlen); if ((unsigned long)~0 == mrc) break; #endif rc = snprintf(q, sizeof(q), "SELECT %s FROM %s WHERE %s='%s' AND %s='%s'", - p->conf.auth_mysql_col_pass->ptr, - p->conf.auth_mysql_users_table->ptr, - p->conf.auth_mysql_col_user->ptr, + p->conf.auth_mysql_col_pass, + p->conf.auth_mysql_users_table, + p->conf.auth_mysql_col_user, uname, - p->conf.auth_mysql_col_realm->ptr, + p->conf.auth_mysql_col_realm, urealm); if (rc >= (int)sizeof(q)) { @@ -490,23 +462,26 @@ static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d, } /* for now we stay synchronous */ - if (0 != mysql_query(p->conf.mysql_conn, q)) { + if (0 != mysql_query(p->mysql_conn, q)) { /* reconnect to db and retry once if query error occurs */ - mod_authn_mysql_sock_error(srv, &p->conf); - if (!mod_authn_mysql_sock_acquire(srv, &p->conf)) { + mod_authn_mysql_sock_error(srv, p); + if (!mod_authn_mysql_sock_acquire(srv, p)) { rc = -1; break; } - if (0 != mysql_query(p->conf.mysql_conn, q)) { + if (0 != mysql_query(p->mysql_conn, q)) { /*(note: any of these params might be bufs w/ b->ptr == NULL)*/ - log_error_write(srv, __FILE__, __LINE__, "sbsb"/*sb*/"sbssss", - "mysql_query host:", p->conf.auth_mysql_host, - "user:", p->conf.auth_mysql_user, - /*(omit pass from logs)*/ - /*"pass:", p->conf.auth_mysql_pass,*/ - "db:", p->conf.auth_mysql_db, - "query:", q, - "failed:", mysql_error(p->conf.mysql_conn)); + log_error_write(srv, __FILE__, __LINE__, "ssss"/*ss*/"ssssss", + "mysql_query host:", + p->conf.auth_mysql_host ? p->conf.auth_mysql_host : NULL, + "user:", + p->conf.auth_mysql_user ? p->conf.auth_mysql_user : NULL, + /*"pass:",*//*(omit pass from logs)*/ + /*p->conf.auth_mysql_pass ? p->conf.auth_mysql_pass : NULL,*/ + "db:", + p->conf.auth_mysql_db ? p->conf.auth_mysql_db : NULL, + "query:", q, + "failed:", mysql_error(p->mysql_conn)); rc = -1; break; } @@ -516,7 +491,7 @@ static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d, } while (0); - mod_authn_mysql_sock_release(srv, &p->conf); + mod_authn_mysql_sock_release(srv, p); return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR; } diff --git a/src/mod_authn_pam.c b/src/mod_authn_pam.c index c9676b93..61925e80 100644 --- a/src/mod_authn_pam.c +++ b/src/mod_authn_pam.c @@ -22,13 +22,12 @@ #include <string.h> typedef struct { - array *opts; const char *service; } plugin_config; typedef struct { PLUGIN_DATA; - plugin_config **config_storage; + plugin_config defaults; plugin_config conf; } plugin_data; @@ -50,81 +49,83 @@ FREE_FUNC(mod_authn_pam_free) { plugin_data *p = p_d; if (!p) return HANDLER_GO_ON; - if (p->config_storage) { - for (size_t i = 0; i < srv->config_context->used; ++i) { - plugin_config *s = p->config_storage[i]; - if (NULL == s) continue; - array_free(s->opts); - free(s); - } - free(p->config_storage); - } + free(p->cvlist); free(p); UNUSED(srv); return HANDLER_GO_ON; } -SETDEFAULTS_FUNC(mod_authn_pam_set_defaults) { - plugin_data *p = p_d; - config_values_t cv[] = { - { "auth.backend.pam.opts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *)); - - for (size_t i = 0; i < srv->config_context->used; ++i) { - data_config const *config = (data_config const*)srv->config_context->data[i]; - const data_string *ds; - plugin_config *s = calloc(1, sizeof(plugin_config)); - s->opts = array_init(); - - cv[0].destination = s->opts; - - 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 (0 == s->opts->used) continue; - - ds = (const data_string *) - array_get_element_klen(s->opts, CONST_STR_LEN("service")); - s->service = (NULL != ds) ? ds->value.ptr : "http"; +static void mod_authn_pam_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { + switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ + case 0: /* auth.backend.pam.opts */ + if (cpv->vtype == T_CONFIG_LOCAL) + pconf->service = cpv->v.v; + break; + default:/* should not happen */ + return; } - - if (p->config_storage[0]->service == NULL) - p->config_storage[0]->service = "http"; - - return HANDLER_GO_ON; } -#define PATCH(x) \ - p->conf.x = s->x; -static int mod_authn_pam_patch_connection(server *srv, connection *con, plugin_data *p) { - plugin_config *s = p->config_storage[0]; - PATCH(service); +static void mod_authn_pam_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { + do { + mod_authn_pam_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} - /* skip the first, the global context */ - for (size_t i = 1; i < srv->config_context->used; ++i) { - if (!config_check_cond(con, i)) continue; /* condition not matched */ +static void mod_authn_pam_patch_config(connection * const con, plugin_data * const p) { + memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); + for (int i = 1, used = p->nconfig; i < used; ++i) { + if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id)) + mod_authn_pam_merge_config(&p->conf, + p->cvlist + p->cvlist[i].v.u2[0]); + } +} - data_config *dc = (data_config *)srv->config_context->data[i]; +SETDEFAULTS_FUNC(mod_authn_pam_set_defaults) { + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("auth.backend.pam.opts"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } + }; - /* merge config */ - s = p->config_storage[i]; - for (size_t 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.pam.opts"))) { - PATCH(service); + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_authn_pam")) + return HANDLER_ERROR; + + /* process and validate config directives + * (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 0: /* auth.backend.pam.opts */ + if (cpv->v.a->used) { + const data_string *ds = (const data_string *) + array_get_element_klen(cpv->v.a,CONST_STR_LEN("service")); + cpv->v.v = (NULL != ds) ? ds->value.ptr : "http"; + cpv->vtype = T_CONFIG_LOCAL; + } + break; + default:/* should not happen */ + break; } } } - return 0; + p->defaults.service = "http"; + + /* initialize p->defaults from global config context */ + if (p->nconfig > 0 && p->cvlist->v.u2[1]) { + const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; + if (-1 != cpv->k_id) + mod_authn_pam_merge_config(&p->defaults, cpv); + } + + return HANDLER_GO_ON; } -#undef PATCH static int mod_authn_pam_fn_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { const char * const pw = (char *)appdata_ptr; @@ -149,7 +150,7 @@ static handler_t mod_authn_pam_query(server *srv, connection *con, void *p_d, co UNUSED(realm); *(const char **)&conv.appdata_ptr = pw; /*(cast away const)*/ - mod_authn_pam_patch_connection(srv, con, p); + mod_authn_pam_patch_config(con, p); rc = pam_start(p->conf.service, username->ptr, &conv, &pamh); if (PAM_SUCCESS != rc diff --git a/src/mod_authn_sasl.c b/src/mod_authn_sasl.c index 83d06408..deafde30 100644 --- a/src/mod_authn_sasl.c +++ b/src/mod_authn_sasl.c @@ -19,12 +19,10 @@ #include "plugin.h" #include <sys/utsname.h> -#include <errno.h> #include <stdlib.h> #include <string.h> typedef struct { - array *opts; const char *service; const char *fqdn; const buffer *pwcheck_method; @@ -33,9 +31,9 @@ typedef struct { typedef struct { PLUGIN_DATA; - plugin_config **config_storage; + plugin_config defaults; plugin_config conf; - buffer *fqdn; + int initonce; } plugin_data; @@ -53,133 +51,164 @@ INIT_FUNC(mod_authn_sasl_init) { return p; } +static void mod_authn_sasl_free_config(plugin_data * const p) { + if (NULL == p->cvlist) return; + /* (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 0: /* auth.backend.sasl.opts */ + if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v); + break; + default: + break; + } + } + } +} + FREE_FUNC(mod_authn_sasl_free) { plugin_data *p = p_d; if (!p) return HANDLER_GO_ON; if (p->initonce) sasl_done(); - if (p->config_storage) { - for (size_t i = 0; i < srv->config_context->used; ++i) { - plugin_config *s = p->config_storage[i]; - if (NULL == s) continue; - array_free(s->opts); - free(s); - } - free(p->config_storage); - } - buffer_free(p->fqdn); + mod_authn_sasl_free_config(p); + + free(p->cvlist); free(p); UNUSED(srv); return HANDLER_GO_ON; } -SETDEFAULTS_FUNC(mod_authn_sasl_set_defaults) { - plugin_data *p = p_d; - size_t i; - config_values_t cv[] = { - { "auth.backend.sasl.opts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - p->config_storage = calloc(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]; - const data_string *ds; - plugin_config *s = calloc(1, sizeof(plugin_config)); - s->opts = array_init(); - - cv[0].destination = s->opts; +static void mod_authn_sasl_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { + switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ + case 0: /* auth.backend.sasl.opts */ + if (cpv->vtype == T_CONFIG_LOCAL) + memcpy(pconf, cpv->v.v, sizeof(plugin_config)); + break; + default:/* should not happen */ + return; + } +} - p->config_storage[i] = s; +static void mod_authn_sasl_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { + do { + mod_authn_sasl_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} - if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { - return HANDLER_ERROR; - } +static void mod_authn_sasl_patch_config(connection * const con, plugin_data * const p) { + memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); + for (int i = 1, used = p->nconfig; i < used; ++i) { + if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id)) + mod_authn_sasl_merge_config(&p->conf, + p->cvlist + p->cvlist[i].v.u2[0]); + } +} - if (0 == s->opts->used) continue; - - ds = (const data_string *) - array_get_element_klen(s->opts, CONST_STR_LEN("service")); - s->service = (NULL != ds) ? ds->value.ptr : "http"; - - ds = (const data_string *) - array_get_element_klen(s->opts, CONST_STR_LEN("fqdn")); - if (NULL != ds) s->fqdn = ds->value.ptr; - if (NULL == s->fqdn) { - if (NULL == p->fqdn) { - struct utsname uts; - if (0 != uname(&uts)) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "uname():", strerror(errno)); - return HANDLER_ERROR; - } - p->fqdn = buffer_init_string(uts.nodename); +static plugin_config * mod_authn_sasl_parse_opts(server *srv, const array * const opts) { + const data_string *ds; + const char *service = NULL; + const char *fqdn = NULL; + const buffer *pwcheck_method = NULL; + const buffer *sasldb_path = NULL; + + ds = (const data_string *) + array_get_element_klen(opts, CONST_STR_LEN("service")); + service = (NULL != ds) ? ds->value.ptr : "http"; + + ds = (const data_string *) + array_get_element_klen(opts, CONST_STR_LEN("fqdn")); + if (NULL != ds) fqdn = ds->value.ptr; + if (NULL == fqdn) { + static struct utsname uts; + if (uts.nodename[0] == '\0') { + if (0 != uname(&uts)) { + log_perror(srv->errh, __FILE__, __LINE__, "uname()"); + return NULL; } - s->fqdn = p->fqdn->ptr; } + fqdn = uts.nodename; + } - ds = (const data_string *) - array_get_element_klen(s->opts, CONST_STR_LEN("pwcheck_method")); - if (NULL != ds) { - s->pwcheck_method = &ds->value; - if (!buffer_is_equal_string(&ds->value, CONST_STR_LEN("saslauthd")) - && !buffer_is_equal_string(&ds->value, CONST_STR_LEN("auxprop")) - && !buffer_is_equal_string(&ds->value, CONST_STR_LEN("sasldb"))){ - log_error_write(srv, __FILE__, __LINE__, "sb", - "sasl pwcheck_method must be one of saslauthd, " - "sasldb, or auxprop, not:", &ds->value); - return HANDLER_ERROR; - } - if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("sasldb"))) { - /* Cyrus libsasl2 expects "auxprop" instead of "sasldb" - * (mod_authn_sasl_cb_getopt auxprop_plugin returns "sasldb") */ - buffer *pwcheck_method = - array_get_buf_ptr(s->opts, CONST_STR_LEN("pwcheck_method")); - buffer_copy_string_len(pwcheck_method, CONST_STR_LEN("auxprop")); - } + ds = (const data_string *) + array_get_element_klen(opts, CONST_STR_LEN("pwcheck_method")); + if (NULL != ds) { + pwcheck_method = &ds->value; + if (!buffer_is_equal_string(&ds->value, CONST_STR_LEN("saslauthd")) + && !buffer_is_equal_string(&ds->value, CONST_STR_LEN("auxprop")) + && !buffer_is_equal_string(&ds->value, CONST_STR_LEN("sasldb"))){ + log_error(srv->errh, __FILE__, __LINE__, + "sasl pwcheck_method must be one of saslauthd, " + "sasldb, or auxprop, not: %s", ds->value.ptr); + return NULL; + } + if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("sasldb"))) { + /* Cyrus libsasl2 expects "auxprop" instead of "sasldb" + * (mod_authn_sasl_cb_getopt auxprop_plugin returns "sasldb") */ + buffer *b; + *(const buffer **)&b = &ds->value; + buffer_copy_string_len(b, CONST_STR_LEN("auxprop")); } - - ds = (const data_string *) - array_get_element_klen(s->opts, CONST_STR_LEN("sasldb_path")); - if (NULL != ds) s->sasldb_path = &ds->value; } - return HANDLER_GO_ON; + ds = (const data_string *) + array_get_element_klen(opts, CONST_STR_LEN("sasldb_path")); + if (NULL != ds) sasldb_path = &ds->value; + + plugin_config *pconf = malloc(sizeof(plugin_config)); + force_assert(pconf); + pconf->service = service; + pconf->fqdn = fqdn; + pconf->pwcheck_method = pwcheck_method; + pconf->sasldb_path = sasldb_path; + return pconf; } -#define PATCH(x) \ - p->conf.x = s->x; -static int mod_authn_sasl_patch_connection(server *srv, connection *con, plugin_data *p) { - plugin_config *s = p->config_storage[0]; - PATCH(service); - PATCH(fqdn); - PATCH(pwcheck_method); - PATCH(sasldb_path); - - /* skip the first, the global context */ - for (size_t i = 1; i < srv->config_context->used; ++i) { - if (!config_check_cond(con, i)) continue; /* condition not matched */ - - data_config *dc = (data_config *)srv->config_context->data[i]; - - /* merge config */ - s = p->config_storage[i]; - for (size_t 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.sasl.opts"))) { - PATCH(service); - PATCH(fqdn); - PATCH(pwcheck_method); - PATCH(sasldb_path); +SETDEFAULTS_FUNC(mod_authn_sasl_set_defaults) { + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("auth.backend.sasl.opts"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } + }; + + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_authn_sasl")) + return HANDLER_ERROR; + + /* process and validate config directives + * (init i to 0 if global context; to 1 to skip empty global context) */ + for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { + config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; + for (; -1 != cpv->k_id; ++cpv) { + switch (cpv->k_id) { + case 0: /* auth.backend.sasl.opts */ + if (cpv->v.a->used) { + cpv->v.v = mod_authn_sasl_parse_opts(srv, cpv->v.a); + if (NULL == cpv->v.v) return HANDLER_ERROR; + cpv->vtype = T_CONFIG_LOCAL; + } + break; + default:/* should not happen */ + break; } } } - return 0; + /* initialize p->defaults from global config context */ + if (p->nconfig > 0 && p->cvlist->v.u2[1]) { + const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; + if (-1 != cpv->k_id) + mod_authn_sasl_merge_config(&p->defaults, cpv); + } + + return HANDLER_GO_ON; } -#undef PATCH static int mod_authn_sasl_cb_getopt(void *p_d, const char *plugin_name, const char *opt, const char **res, unsigned *len) { plugin_data *p = (plugin_data *)p_d; @@ -243,7 +272,7 @@ static handler_t mod_authn_sasl_query(server *srv, connection *con, void *p_d, c }; int rc; - mod_authn_sasl_patch_connection(srv, con, p); + mod_authn_sasl_patch_config(con, p); if (!p->initonce) { /* must be done once, but after fork() if multiple lighttpd workers */ |