]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: Add lua passdb/userdb support
authorAki Tuomi <aki.tuomi@dovecot.fi>
Wed, 8 Nov 2017 13:42:32 +0000 (15:42 +0200)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Fri, 24 Nov 2017 09:02:05 +0000 (11:02 +0200)
13 files changed:
src/auth/Makefile.am
src/auth/auth-request-var-expand.h
src/auth/db-lua.c [new file with mode: 0644]
src/auth/db-lua.h [new file with mode: 0644]
src/auth/passdb-lua.c [new file with mode: 0644]
src/auth/passdb.c
src/auth/test-auth.h
src/auth/test-lua.c [new file with mode: 0644]
src/auth/test-main.c
src/auth/test-mock.c
src/auth/userdb-lua.c [new file with mode: 0644]
src/auth/userdb.c
src/lib-lua/dlua-script-private.h

index b2c6a7f4b99fdd1bd64859c1b208cf4390e9c922..9ab626e8e19d499ef931916d2f2b5908136c0716 100644 (file)
@@ -12,9 +12,14 @@ if LDAP_PLUGIN
 LDAP_LIB = libauthdb_ldap.la
 endif
 
+if AUTH_LUA_PLUGIN
+LUA_LIB = libauthdb_lua.la
+endif
+
 auth_module_LTLIBRARIES = \
        $(GSSAPI_LIB) \
        $(LDAP_LIB) \
+       $(LUA_LIB) \
        libauthdb_imap.la
 
 pkglibexecdir = $(libexecdir)/dovecot
@@ -36,9 +41,11 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib-master \
        -I$(top_srcdir)/src/lib-oauth2 \
        -I$(top_srcdir)/src/lib-ssl-iostream \
+       -I$(top_srcdir)/src/lib-lua \
        -DAUTH_MODULE_DIR=\""$(auth_moduledir)"\" \
        -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
        -DPKG_RUNDIR=\""$(rundir)"\" \
+       $(LUA_CFLAGS) \
        $(AUTH_CFLAGS)
 
 auth_LDFLAGS = -export-dynamic
@@ -65,12 +72,17 @@ auth_libs = \
        $(LIBDOVECOT_SQL) \
        $(LIBSODIUM_LIBS)
 
+if !AUTH_LUA_PLUGIN
+auth_libs += ../lib-lua/libdovecot-lua.la
+endif
+
 auth_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS)
 auth_LDADD = $(auth_libs) $(LIBDOVECOT) $(AUTH_LIBS) $(BINARY_LDFLAGS)
 auth_DEPENDENCIES = $(auth_libs) $(LIBDOVECOT_DEPS)
 auth_SOURCES = main.c
 
 ldap_sources = db-ldap.c passdb-ldap.c userdb-ldap.c
+lua_sources = db-lua.c passdb-lua.c userdb-lua.c
 
 libauth_la_DEPENDENCIES = $(LIBDOVECOT_DEPS)
 libauth_la_SOURCES = \
@@ -142,7 +154,8 @@ libauth_la_SOURCES = \
        userdb-vpopmail.c \
        userdb-sql.c \
        userdb-template.c \
-       $(ldap_sources)
+       $(ldap_sources) \
+       $(lua_sources)
 
 headers = \
        auth.h \
@@ -197,6 +210,13 @@ libauthdb_ldap_la_CPPFLAGS = $(AM_CPPFLAGS) -DPLUGIN_BUILD
 libauthdb_ldap_la_SOURCES = $(ldap_sources)
 endif
 
+if AUTH_LUA_PLUGIN
+libauthdb_lua_la_LDFLAGS = -module -avoid-version
+libauthdb_lua_la_LIBADD = ../lib-lua/libdovecot-lua.la $(LUA_LIBS)
+libauthdb_lua_la_CPPFLAGS = $(AM_CPPFLAGS) -DPLUGIN_BUILD
+libauthdb_lua_la_SOURCES = $(lua_sources)
+endif
+
 libauthdb_imap_la_LDFLAGS = -module -avoid-version
 libauthdb_imap_la_LIBADD = \
        ../lib-imap-client/libimap_client.la \
@@ -232,7 +252,7 @@ test_programs = \
 
 noinst_PROGRAMS = $(test_programs)
 
-noinst_HEADERS = test-auth.h crypt-blowfish.h
+noinst_HEADERS = test-auth.h crypt-blowfish.h db-lua.h
 
 test_libs = \
        ../lib-dovecot/libdovecot.la
@@ -263,6 +283,10 @@ test_auth_SOURCES = \
        test-db-dict.c \
        test-mock.c \
        test-main.c
+if !AUTH_LUA_PLUGIN
+test_auth_SOURCES += test-lua.c
+endif
+
 test_auth_LDADD = $(test_libs) $(auth_libs) $(AUTH_LIBS)
 test_auth_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs)
 
index 5d450d05f6f905f7c4660b8c873af200558299d1..70535a3376c75ffb06e1d04ff300755e76982a3b 100644 (file)
@@ -12,6 +12,8 @@ auth_request_escape_func_t(const char *string,
 extern const struct var_expand_table
 auth_request_var_expand_static_tab[AUTH_REQUEST_VAR_TAB_COUNT+1];
 
+extern const struct var_expand_func_table auth_request_var_funcs_table[];
+
 const struct var_expand_table *
 auth_request_get_var_expand_table(const struct auth_request *auth_request,
                                  auth_request_escape_func_t *escape_func)
diff --git a/src/auth/db-lua.c b/src/auth/db-lua.c
new file mode 100644 (file)
index 0000000..3039561
--- /dev/null
@@ -0,0 +1,692 @@
+/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#if defined(BUILTIN_LUA) || defined(PLUGIN_BUILD)
+
+#include "llist.h"
+#include "istream.h"
+#include "array.h"
+#include "sha1.h"
+#include "hex-binary.h"
+#include "auth.h"
+#include "passdb.h"
+#include "userdb.h"
+#include "auth-request.h"
+#include "userdb-template.h"
+#include "passdb-template.h"
+#include "password-scheme.h"
+#include "auth-request-var-expand.h"
+
+#define AUTH_LUA_PASSDB_LOOKUP "auth_passdb_lookup"
+#define AUTH_LUA_USERDB_LOOKUP "auth_userdb_lookup"
+#define AUTH_LUA_USERDB_ITERATE "auth_userdb_iterate"
+
+#define AUTH_LUA_DOVECOT_AUTH "dovecot_auth"
+#define AUTH_LUA_AUTH_REQUEST "auth_request*"
+
+#include "db-lua.h"
+#include "dlua-script-private.h"
+
+struct auth_lua_userdb_iterate_context {
+       struct userdb_iterate_context ctx;
+       pool_t pool;
+       unsigned int idx;
+       ARRAY_TYPE(const_string) users;
+};
+
+static struct auth_request *
+auth_lua_check_auth_request(struct dlua_script *script, int arg);
+
+static int
+auth_request_lua_do_var_expand(struct auth_request *req, const char *tpl,
+                              const char **value_r, const char **error_r)
+{
+       const char *error;
+       if (t_auth_request_var_expand(tpl, req, NULL, value_r, &error) < 0) {
+               *error_r = t_strdup_printf("var_expand(%s) failed: %s",
+                                          tpl, error);
+               return -1;
+       }
+       return 0;
+}
+
+static int auth_request_lua_var_expand(lua_State *L)
+{
+       struct dlua_script *script = dlua_script_from_state(L);
+       struct auth_request *req = auth_lua_check_auth_request(script, 1);
+       const char *tpl = luaL_checkstring(L, 2);
+       const char *value, *error;
+
+       if (auth_request_lua_do_var_expand(req, tpl, &value, &error) < 0) {
+               luaL_error(L, error);
+       } else {
+               lua_pushstring(L, value);
+       }
+       return 1;
+}
+
+static const char *const *
+auth_request_template_build(struct auth_request *req, const char *str,
+                           unsigned int *count_r)
+{
+       if (req->userdb_lookup) {
+               struct userdb_template *tpl =
+                       userdb_template_build(pool_datastack_create(), "lua", str);
+               if (userdb_template_is_empty(tpl))
+                       return NULL;
+               return userdb_template_get_args(tpl, count_r);
+       } else {
+               struct passdb_template *tpl =
+                       passdb_template_build(pool_datastack_create(), str);
+               if (passdb_template_is_empty(tpl))
+                       return NULL;
+               return passdb_template_get_args(tpl, count_r);
+       }
+}
+
+static int auth_request_lua_response_from_template(lua_State *L)
+{
+       struct dlua_script *script = dlua_script_from_state(L);
+       struct auth_request *req = auth_lua_check_auth_request(script, 1);
+       const char *tplstr = luaL_checkstring(L, 2);
+       const char *error,*expanded;
+       unsigned int count,i;
+
+       const char *const *fields = auth_request_template_build(req, tplstr, &count);
+
+       /* push new table to stack */
+       lua_newtable(L);
+
+       if (fields == NULL)
+               return 1;
+
+       i_assert((count % 2) == 0);
+
+       for(i = 0; i < count; i+=2) {
+               const char *key = fields[i];
+               const char *value = fields[i+1];
+
+               if (value == NULL) {
+                       lua_pushnil(L);
+               } else if (auth_request_lua_do_var_expand(req, value, &expanded, &error) < 0) {
+                       luaL_error(L, error);
+                       break;
+               } else {
+                       lua_pushstring(L, expanded);
+               }
+               lua_setfield(L, -2, key);
+       }
+
+       /* stack should be left with table */
+       return 1;
+}
+
+static int auth_request_lua_log_debug(lua_State *L)
+{
+       if (global_auth_settings->debug) {
+               struct dlua_script *script = dlua_script_from_state(L);
+               struct auth_request *request = auth_lua_check_auth_request(script, 1);
+               const char *msg = luaL_checkstring(L, 2);
+               auth_request_log_debug(request, AUTH_SUBSYS_DB, "db-lua: %s", msg);
+       }
+       return 0;
+}
+
+static int auth_request_lua_log_info(lua_State *L)
+{
+       struct dlua_script *script = dlua_script_from_state(L);
+       struct auth_request *request = auth_lua_check_auth_request(script, 1);
+       const char *msg = luaL_checkstring(L, 2);
+       auth_request_log_info(request, AUTH_SUBSYS_DB, "db-lua: %s", msg);
+       return 0;
+}
+
+static int auth_request_lua_log_warning(lua_State *L)
+{
+       struct dlua_script *script = dlua_script_from_state(L);
+       struct auth_request *request = auth_lua_check_auth_request(script, 1);
+       const char *msg = luaL_checkstring(L, 2);
+       auth_request_log_warning(request, AUTH_SUBSYS_DB, "db-lua: %s", msg);
+       return 0;
+}
+
+static int auth_request_lua_log_error(lua_State *L)
+{
+       struct dlua_script *script = dlua_script_from_state(L);
+       struct auth_request *request = auth_lua_check_auth_request(script, 1);
+       const char *msg = luaL_checkstring(L, 2);
+       auth_request_log_error(request, AUTH_SUBSYS_DB, "db-lua: %s", msg);
+       return 0;
+}
+
+static int auth_request_lua_var_expand_func(lua_State *L)
+{
+       struct dlua_script *script = dlua_script_from_state(L);
+       struct auth_request *request = auth_lua_check_auth_request(script, 1);
+       const char *key = luaL_checkstring(L, 2);
+       const char *param = luaL_checkstring(L, 3);
+       const char *value, *error;
+       /* allows setting parameters for the call */
+       const char *tpl =
+                t_strdup_printf("%%{%s%s%s}",
+                                key,
+                                (*param == ';' ? "" : ":"),
+                                param);
+
+       if (auth_request_lua_do_var_expand(request, tpl, &value, &error) < 0) {
+               luaL_error(L, error);
+               return 1;
+       }
+       lua_pushstring(L, value);
+
+       return 1;
+}
+
+/* put all methods here */
+static const luaL_Reg auth_request_methods[] ={
+       { "var_expand", auth_request_lua_var_expand },
+       { "response_from_template", auth_request_lua_response_from_template },
+       { "log_debug", auth_request_lua_log_debug },
+       { "log_info", auth_request_lua_log_info },
+       { "log_warning", auth_request_lua_log_warning },
+       {" log_error", auth_request_lua_log_error },
+       { NULL, NULL }
+};
+
+static int auth_request_lua_index(lua_State *L)
+{
+       struct dlua_script *script = dlua_script_from_state(L);
+       struct auth_request *req = auth_lua_check_auth_request(script, 1);
+       const char *key = luaL_checkstring(L, 2);
+       lua_pop(L, 1);
+
+       const struct var_expand_table *table =
+               auth_request_get_var_expand_table(req, NULL);
+
+       /* check if it's variable */
+       for(unsigned int i = 0; i < AUTH_REQUEST_VAR_TAB_COUNT; i++) {
+               if (null_strcmp(table[i].long_key, key) == 0) {
+                       if (table[i].value != NULL)
+                               lua_pushstring(L, table[i].value);
+                       else
+                               lua_pushnil(L);
+                       return 1;
+               }
+       }
+
+       /* check if it's a variable function */
+       const struct var_expand_func_table *ftable = auth_request_var_funcs_table;
+       while(ftable->func != NULL) {
+               if (null_strcmp(key, ftable->key) == 0) {
+                       lua_pushcfunction(L, auth_request_lua_var_expand_func);
+                       return 1;
+               }
+               ftable++;
+       }
+
+       /* check if it's function, then */
+       const luaL_Reg *ptr = auth_request_methods;
+       while(ptr->name != NULL) {
+               if (null_strcmp(key, ptr->name) == 0) {
+                       lua_pushcfunction(L, ptr->func);
+                       return 1;
+               }
+               ptr++;
+       }
+
+       luaL_error(L, "no such method");
+       return 1;
+}
+
+static void auth_lua_push_auth_request(struct dlua_script *script, struct auth_request *req)
+{
+       /* we want to store a pointer, not the actual request */
+       struct auth_request **bp =
+               (struct auth_request**)lua_newuserdata(script->L, sizeof(req));
+       *bp = req;
+       /* associate metatable */
+       luaL_setmetatable(script->L, AUTH_LUA_AUTH_REQUEST);
+}
+
+static struct auth_request *
+auth_lua_check_auth_request(struct dlua_script *script, int arg)
+{
+       struct auth_request **bp =
+               (struct auth_request**)luaL_checkudata(script->L, arg, AUTH_LUA_AUTH_REQUEST);
+       return *bp;
+}
+
+static void auth_lua_auth_request_register(struct dlua_script *script)
+{
+       luaL_newmetatable(script->L, AUTH_LUA_AUTH_REQUEST);
+       lua_pushcfunction(script->L, auth_request_lua_index);
+       lua_setfield(script->L, -2, "__index");
+       lua_pop(script->L, 1);
+}
+
+static struct dlua_table_values auth_lua_dovecot_auth_values[] = {
+       DLUA_TABLE_ENUM(PASSDB_RESULT_INTERNAL_FAILURE),
+       DLUA_TABLE_ENUM(PASSDB_RESULT_SCHEME_NOT_AVAILABLE),
+       DLUA_TABLE_ENUM(PASSDB_RESULT_USER_UNKNOWN),
+       DLUA_TABLE_ENUM(PASSDB_RESULT_USER_DISABLED),
+       DLUA_TABLE_ENUM(PASSDB_RESULT_PASS_EXPIRED),
+       DLUA_TABLE_ENUM(PASSDB_RESULT_NEXT),
+       DLUA_TABLE_ENUM(PASSDB_RESULT_PASSWORD_MISMATCH),
+       DLUA_TABLE_ENUM(PASSDB_RESULT_OK),
+
+       DLUA_TABLE_ENUM(USERDB_RESULT_INTERNAL_FAILURE),
+       DLUA_TABLE_ENUM(USERDB_RESULT_USER_UNKNOWN),
+       DLUA_TABLE_ENUM(USERDB_RESULT_OK),
+
+       DLUA_TABLE_END
+};
+static luaL_Reg auth_lua_dovecot_auth_methods[] = {
+       { NULL, NULL }
+};
+
+static void auth_lua_dovecot_auth_register(struct dlua_script *script)
+{
+       dlua_getdovecot(script);
+       /* Create new table for holding values */
+       lua_newtable(script->L);
+
+       /* register constants */
+       dlua_setmembers(script, auth_lua_dovecot_auth_values, -1);
+
+       /* push new metatable to stack */
+       luaL_newmetatable(script->L, AUTH_LUA_DOVECOT_AUTH);
+       /* this will register functions to the metatable itself */
+       luaL_setfuncs(script->L, auth_lua_dovecot_auth_methods, 0);
+       /* point __index to self */
+       lua_pushvalue(script->L, -1);
+       lua_setfield(script->L, -1, "__index");
+       /* set table's metatable, pops stack */
+       lua_setmetatable(script->L, -2);
+
+       /* put this as "dovecot.auth" */
+       lua_setfield(script->L, -2, "auth");
+}
+
+int auth_lua_script_init(struct dlua_script *script, const char **error_r)
+{
+       dlua_dovecot_register(script);
+       auth_lua_dovecot_auth_register(script);
+       auth_lua_auth_request_register(script);
+       return dlua_script_init(script, error_r);
+}
+
+static int auth_lua_call_lookup(struct dlua_script *script, const char *fn,
+                               struct auth_request *req, const char **error_r)
+{
+       int err = 0;
+
+       i_assert(script != NULL);
+
+       /* call lua function passdb_lookup, it is expected to return fields */
+       lua_getglobal(script->L, fn);
+       if (!lua_isfunction(script->L, -1)) {
+               lua_pop(script->L, 1);
+               *error_r = t_strdup_printf("%s is not a function", fn);
+               return -1;
+       }
+
+       if (req->debug)
+               auth_request_log_debug(req, AUTH_SUBSYS_DB, "Calling %s", fn);
+
+       /* call with auth request as parameter */
+       auth_lua_push_auth_request(script, req);
+       if (lua_pcall(script->L, 1, 2, 0) != 0) {
+               *error_r = t_strdup_printf("db-lua: %s(req) failed: %s",
+                                          fn, lua_tostring(script->L, -1));
+               lua_pop(script->L, 1);
+               return -1;
+       } else if (!lua_isnumber(script->L, -2)) {
+               *error_r = t_strdup_printf("db-lua: %s(req) invalid return value "
+                                          "(expected number got %s)",
+                                          fn, luaL_typename(script->L, -2));
+               err = -1;
+       } else if (!lua_isstring(script->L, -1) && !lua_istable(script->L, -1)) {
+               *error_r = t_strdup_printf("db-lua: %s(req) invalid return value "
+                                          "(expected string or table, got %s)",
+                                          fn, luaL_typename(script->L, -1));
+               err = -1;
+       }
+
+       if (err != 0) {
+               lua_pop(script->L, 2);
+               lua_gc(script->L, LUA_GCCOLLECT, 0);
+               return PASSDB_RESULT_INTERNAL_FAILURE;
+       }
+
+       return 0;
+}
+
+static void
+auth_lua_export_fields(struct auth_request *req,
+                      const char *str,
+                      const char **scheme_r, const char **password_r)
+{
+       const char *const *fields = t_strsplit_spaces(str, " ");
+       while(*fields != NULL) {
+               const char *value = strchr(*fields, '=');
+               const char *key;
+
+               if (value == NULL) {
+                       key = *fields;
+                       value = "";
+               } else {
+                       key = t_strdup_until(*fields, value++);
+               }
+
+               if (password_r != NULL && strcmp(key, "password") == 0 &&
+                   req->userdb_lookup) {
+                       *scheme_r = password_get_scheme(&value);
+                       *password_r = value;
+               } else if (req->userdb_lookup) {
+                       auth_request_set_userdb_field(req, key, value);
+               } else {
+                       auth_request_set_field(req, key, value, STATIC_PASS_SCHEME);
+               }
+               fields++;
+       }
+}
+
+static void auth_lua_export_table(struct dlua_script *script, struct auth_request *req,
+                                const char **scheme_r, const char **password_r)
+{
+       lua_pushvalue(script->L, -1);
+       lua_pushnil(script->L);
+       while (lua_next(script->L, -2) != 0) {
+               const char *key = t_strdup(lua_tostring(script->L, -2));
+               const char *value;
+               if (lua_isnumber(script->L, -1)) {
+                       value = dec2str(lua_tointeger(script->L, -1));
+               } else if (lua_isboolean(script->L, -1)) {
+                       value = lua_toboolean(script->L, -1) ? "yes" : "no";
+               } else if (lua_isstring(script->L, -1)) {
+                       value = t_strdup(lua_tostring(script->L, -1));
+               } else if (lua_isnil(script->L, -1)) {
+                       value = "";
+               } else {
+                       auth_request_log_warning(req, AUTH_SUBSYS_DB,
+                                                "db-lua: '%s' has invalid value - ignoring",
+                                                key);
+                       value = "";
+               }
+
+               if (password_r != NULL && strcmp(key, "password") == 0 &&
+                   !req->userdb_lookup) {
+                       *scheme_r = password_get_scheme(&value);
+                       *password_r = value;
+               } else if (req->userdb_lookup) {
+                       auth_request_set_userdb_field(req, key, value);
+               } else {
+                       auth_request_set_field(req, key, value, STATIC_PASS_SCHEME);
+               }
+               lua_pop(script->L, 1);
+       }
+
+       lua_pop(script->L, 2);
+       lua_gc(script->L, LUA_GCCOLLECT, 0);
+}
+
+static enum userdb_result
+auth_lua_export_userdb_table(struct dlua_script *script, struct auth_request *req,
+                            const char **error_r)
+{
+       enum userdb_result ret = lua_tointeger(script->L, -2);
+
+       if (ret != USERDB_RESULT_OK) {
+               lua_pop(script->L, 2);
+               lua_gc(script->L, LUA_GCCOLLECT, 0);
+               *error_r = "userdb failed";
+               return ret;
+       }
+
+       auth_lua_export_table(script, req, NULL, NULL);
+       return USERDB_RESULT_OK;
+}
+
+static enum passdb_result
+auth_lua_export_passdb_table(struct dlua_script *script, struct auth_request *req,
+                            const char **scheme_r, const char **password_r,
+                            const char **error_r)
+{
+       enum passdb_result ret = lua_tointeger(script->L, -2);
+
+       if (ret != PASSDB_RESULT_OK) {
+               lua_pop(script->L, 2);
+               lua_gc(script->L, LUA_GCCOLLECT, 0);
+               *error_r = "passb failed";
+               return ret;
+       }
+
+       auth_lua_export_table(script, req, scheme_r, password_r);
+       return PASSDB_RESULT_OK;
+}
+
+static enum passdb_result
+auth_lua_call_lookup_finish(struct dlua_script *script, struct auth_request *req,
+                           const char **username_r, const char **password_r,
+                           const char **error_r)
+{
+       if (lua_istable(script->L, -1)) {
+               return auth_lua_export_passdb_table(script, req, NULL, NULL, error_r);
+       }
+
+       enum passdb_result ret = lua_tointeger(script->L, -2);
+       const char *str = t_strdup(lua_tostring(script->L, -1));
+       lua_pop(script->L, 2);
+       lua_gc(script->L, LUA_GCCOLLECT, 0);
+
+       if (ret != PASSDB_RESULT_OK && ret != PASSDB_RESULT_NEXT) {
+               *error_r = str;
+       } else {
+               auth_lua_export_fields(req, str, username_r, password_r);
+       }
+
+       return ret;
+}
+
+enum passdb_result
+auth_lua_call_password_verify(struct dlua_script *script,
+                             struct auth_request *req, const char *password, const char **error_r)
+{
+       int err = 0;
+       i_assert(script != NULL);
+
+       lua_getglobal(script->L, AUTH_LUA_PASSWORD_VERIFY);
+       if (!lua_isfunction(script->L, -1)) {
+               lua_pop(script->L, 1);
+               *error_r = t_strdup_printf("%s is not a function", AUTH_LUA_PASSWORD_VERIFY);
+               return PASSDB_RESULT_INTERNAL_FAILURE;
+       }
+
+       if (req->debug)
+               auth_request_log_debug(req, AUTH_SUBSYS_DB, "Calling %s", AUTH_LUA_PASSWORD_VERIFY);
+
+       /* call with auth request, password as parameters */
+       auth_lua_push_auth_request(script, req);
+       lua_pushstring(script->L, password);
+       if (lua_pcall(script->L, 2, 2, 0) != 0) {
+               *error_r = t_strdup_printf("db-lua: %s(req, password) failed: %s",
+                                          AUTH_LUA_PASSWORD_VERIFY,
+                                          lua_tostring(script->L, -1));
+               lua_pop(script->L, 1);
+               return PASSDB_RESULT_INTERNAL_FAILURE;
+       } else if (!lua_isnumber(script->L, -2)) {
+               *error_r = t_strdup_printf("db-lua: %s invalid return value "
+                                          "(expected number got %s)",
+                                          AUTH_LUA_PASSWORD_VERIFY,
+                                          luaL_typename(script->L, -2));
+               err = -1;
+       } else if (!lua_isstring(script->L, -1) && !lua_istable(script->L, -1)) {
+               *error_r = t_strdup_printf("db-lua: %s invalid return value "
+                                          "(expected string or table, got %s)",
+                                          AUTH_LUA_PASSWORD_VERIFY,
+                                          luaL_typename(script->L, -1));
+               err = -1;
+       }
+
+       if (err != 0) {
+               lua_pop(script->L, 2);
+               lua_gc(script->L, LUA_GCCOLLECT, 0);
+               return PASSDB_RESULT_INTERNAL_FAILURE;
+       }
+
+
+       return auth_lua_call_lookup_finish(script, req, NULL, NULL, error_r);
+}
+
+
+enum passdb_result
+auth_lua_call_passdb_lookup(struct dlua_script *script,
+                           struct auth_request *req, const char **scheme_r,
+                           const char **password_r, const char **error_r)
+{
+       *scheme_r = *password_r = NULL;
+       if (auth_lua_call_lookup(script, AUTH_LUA_PASSDB_LOOKUP, req, error_r) < 0) {
+               lua_gc(script->L, LUA_GCCOLLECT, 0);
+               return PASSDB_RESULT_INTERNAL_FAILURE;
+       }
+
+       return auth_lua_call_lookup_finish(script, req, scheme_r, password_r, error_r);
+}
+
+
+enum userdb_result
+auth_lua_call_userdb_lookup(struct dlua_script *script,
+                           struct auth_request *req, const char **error_r)
+{
+       if (auth_lua_call_lookup(script, AUTH_LUA_USERDB_LOOKUP, req, error_r) < 0) {
+               lua_gc(script->L, LUA_GCCOLLECT, 0);
+               return USERDB_RESULT_INTERNAL_FAILURE;
+       }
+
+       if (lua_istable(script->L, -1)) {
+               return auth_lua_export_userdb_table(script, req, error_r);
+       }
+
+       enum userdb_result ret = lua_tointeger(script->L, -2);
+       const char *str = t_strdup(lua_tostring(script->L, -1));
+       lua_pop(script->L, 2);
+       lua_gc(script->L, LUA_GCCOLLECT, 0);
+
+       if (ret != USERDB_RESULT_OK) {
+               *error_r = str;
+               return ret;
+       }
+       auth_lua_export_fields(req, str, NULL, NULL);
+
+       return USERDB_RESULT_OK;
+}
+
+struct userdb_iterate_context *
+auth_lua_call_userdb_iterate_init(struct dlua_script *script, struct auth_request *req,
+                                 userdb_iter_callback_t *callback, void *context)
+{
+       pool_t pool = pool_alloconly_create(MEMPOOL_GROWING"lua userdb iterate", 128);
+       struct auth_lua_userdb_iterate_context *actx =
+               p_new(pool, struct auth_lua_userdb_iterate_context, 1);
+       int ret;
+
+       actx->pool = pool;
+
+       lua_getglobal(script->L, AUTH_LUA_USERDB_ITERATE);
+       if (!lua_isfunction(script->L, -1)) {
+               actx->ctx.failed = TRUE;
+               return &actx->ctx;
+       }
+
+       if (req->debug)
+               auth_request_log_debug(req, AUTH_SUBSYS_DB, "Calling %s", AUTH_LUA_USERDB_ITERATE);
+
+       if ((ret = lua_pcall(script->L, 0, 1, 0)) != 0) {
+               auth_request_log_error(req, AUTH_SUBSYS_DB, "db-lua: " AUTH_LUA_USERDB_ITERATE " failed: %s",
+                                      lua_tostring(script->L, -1));
+               actx->ctx.failed = TRUE;
+               lua_pop(script->L, 1);
+               return &actx->ctx;
+       }
+
+       if (!lua_istable(script->L, -1)) {
+               auth_request_log_error(req, AUTH_SUBSYS_DB, "db-lua: Cannot iterate, return value is not table");
+               actx->ctx.failed = TRUE;
+               lua_pop(script->L, 1);
+               lua_gc(script->L, LUA_GCCOLLECT, 0);
+               return &actx->ctx;
+       }
+
+       p_array_init(&actx->users, pool, 8);
+
+       lua_pushvalue(script->L, -1);
+       lua_pushnil(script->L);
+       while (lua_next(script->L, -2) != 0) {
+               lua_pushvalue(script->L, -2);
+               if (!lua_isstring(script->L, -1)) {
+                       auth_request_log_error(req, AUTH_SUBSYS_DB, "db-lua: Value is not string");
+                       actx->ctx.failed = TRUE;
+                       lua_pop(script->L, 1);
+                       lua_gc(script->L, LUA_GCCOLLECT, 0);
+                       return &actx->ctx;
+               }
+               const char *str = p_strdup(pool, lua_tostring(script->L, -2));
+               array_append(&actx->users, &str, 1);
+               lua_pop(script->L, 2);
+       }
+
+       lua_gc(script->L, LUA_GCCOLLECT, 0);
+
+       actx->ctx.auth_request = req;
+       actx->ctx.callback = callback;
+       actx->ctx.context = context;
+
+       return &actx->ctx;
+}
+
+void auth_lua_userdb_iterate_next(struct userdb_iterate_context *ctx)
+{
+       struct auth_lua_userdb_iterate_context *actx =
+               container_of(ctx, struct auth_lua_userdb_iterate_context, ctx);
+
+       if (ctx->failed || actx->idx >= array_count(&actx->users)) {
+               ctx->callback(NULL, ctx->context);
+               return;
+       }
+
+       const char *const *user = array_idx(&actx->users, actx->idx++);
+       ctx->callback(*user, ctx->context);
+}
+
+int auth_lua_userdb_iterate_deinit(struct userdb_iterate_context *ctx)
+{
+       struct auth_lua_userdb_iterate_context *actx =
+               container_of(ctx, struct auth_lua_userdb_iterate_context, ctx);
+
+       int ret = ctx->failed ? -1 : 0;
+       pool_unref(&actx->pool);
+       return ret;
+}
+
+#ifndef BUILTIN_LUA
+/* Building a plugin */
+extern struct passdb_module_interface passdb_lua_plugin;
+extern struct userdb_module_interface userdb_lua_plugin;
+
+void authdb_lua_init(void);
+void authdb_lua_deinit(void);
+
+void authdb_lua_init(void)
+{
+       passdb_register_module(&passdb_lua_plugin);
+       userdb_register_module(&userdb_lua_plugin);
+
+}
+void authdb_lua_deinit(void)
+{
+       passdb_unregister_module(&passdb_lua_plugin);
+       userdb_unregister_module(&userdb_lua_plugin);
+}
+#endif
+
+#endif
diff --git a/src/auth/db-lua.h b/src/auth/db-lua.h
new file mode 100644 (file)
index 0000000..c9b622b
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef DB_LUA_H
+#define DB_LUA_H 1
+
+#include "dlua-script.h"
+
+#define AUTH_LUA_PASSWORD_VERIFY "auth_password_verify"
+
+struct dlua_script;
+
+int auth_lua_script_init(struct dlua_script *script, const char **error_r);
+
+int auth_lua_call_password_verify(struct dlua_script *script,
+                                 struct auth_request *req, const char *password,
+                                 const char **error_r);
+
+enum passdb_result
+auth_lua_call_passdb_lookup(struct dlua_script *script,
+                           struct auth_request *req, const char **scheme_r,
+                           const char **password_r, const char **error_r);
+
+enum userdb_result
+auth_lua_call_userdb_lookup(struct dlua_script *script,
+                           struct auth_request *req, const char **error_r);
+
+struct userdb_iterate_context *
+auth_lua_call_userdb_iterate_init(struct dlua_script *script, struct auth_request *req,
+                                 userdb_iter_callback_t *callback, void *context);
+void auth_lua_userdb_iterate_next(struct userdb_iterate_context *ctx);
+int auth_lua_userdb_iterate_deinit(struct userdb_iterate_context *ctx);
+
+#endif
diff --git a/src/auth/passdb-lua.c b/src/auth/passdb-lua.c
new file mode 100644 (file)
index 0000000..032f4ea
--- /dev/null
@@ -0,0 +1,175 @@
+/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
+
+#include "auth-common.h"
+#include "passdb.h"
+
+#if defined(BUILTIN_LUA) || defined(PLUGIN_BUILD)
+
+#include "db-lua.h"
+
+struct dlua_passdb_module {
+       struct passdb_module module;
+       struct dlua_script *script;
+       const char *file;
+       bool has_password_verify;
+};
+
+static enum passdb_result
+passdb_lua_verify_password(struct dlua_passdb_module *module,
+                          struct auth_request *request, const char *password)
+{
+       const char *error = NULL;
+       enum passdb_result result =
+               auth_lua_call_password_verify(module->script, request,
+                                             password, &error);
+       if (result == PASSDB_RESULT_PASSWORD_MISMATCH) {
+               auth_request_log_password_mismatch(request, AUTH_SUBSYS_DB);
+       } else if (result == PASSDB_RESULT_INTERNAL_FAILURE && error != NULL) {
+               auth_request_log_error(request, AUTH_SUBSYS_DB, "passdb-lua: %s",
+                                      error);
+       }
+       return result;
+}
+
+static enum passdb_result
+passdb_lua_lookup(struct auth_request *request,
+                 const char **scheme_r, const char **password_r)
+{
+       const char *error = NULL;
+       enum passdb_result result;
+       struct dlua_passdb_module *module =
+               (struct dlua_passdb_module *)request->passdb->passdb;
+
+       *scheme_r = *password_r = NULL;
+
+       result = auth_lua_call_passdb_lookup(module->script, request, scheme_r,
+                                            password_r, &error);
+
+       if (result == PASSDB_RESULT_INTERNAL_FAILURE && error != NULL) {
+               auth_request_log_error(request, AUTH_SUBSYS_DB, "db-lua: %s", error);
+       } else if (result != PASSDB_RESULT_OK) {
+               /* skip next bit */
+       } else if (!auth_fields_exists(request->extra_fields, "nopassword")) {
+               if (*password_r == NULL || **password_r == '\0') {
+                       auth_request_log_info(request, AUTH_SUBSYS_DB,
+                               "No password returned (and no nopassword)");
+                       result = PASSDB_RESULT_PASSWORD_MISMATCH;
+               }
+       } else if (*password_r == NULL || **password_r != '\0') {
+               auth_request_log_info(request, AUTH_SUBSYS_DB,
+                                     "nopassword given and password is not empty");
+               result = PASSDB_RESULT_PASSWORD_MISMATCH;
+       }
+       return result;
+}
+
+static void
+passdb_lua_lookup_credentials(struct auth_request *request,
+                             lookup_credentials_callback_t *callback)
+{
+       const char *lua_password, *lua_scheme;
+       enum passdb_result result =
+               passdb_lua_lookup(request, &lua_scheme, &lua_password);
+
+       passdb_handle_credentials(result, lua_password, lua_scheme, callback, request);
+}
+
+static void
+passdb_lua_verify_plain(struct auth_request *request, const char *password,
+                       verify_plain_callback_t *callback)
+{
+       struct dlua_passdb_module *module =
+               (struct dlua_passdb_module *)request->passdb->passdb;
+       const char *lua_password, *lua_scheme;
+       enum passdb_result result;
+
+       if (module->has_password_verify) {
+               result = passdb_lua_verify_password(module, request, password);
+       } else {
+               result = passdb_lua_lookup(request, &lua_scheme, &lua_password);
+               if (result == PASSDB_RESULT_OK) {
+                       if ((auth_request_password_verify(request, password, lua_password,
+                                                         lua_scheme, AUTH_SUBSYS_DB)) <=0) {
+                               result = PASSDB_RESULT_PASSWORD_MISMATCH;
+                       }
+               }
+       }
+       callback(result, request);
+}
+
+static struct passdb_module *
+passdb_lua_preinit(pool_t pool, const char *args)
+{
+       struct dlua_passdb_module *module;
+       bool blocking = TRUE;
+
+       module = p_new(pool, struct dlua_passdb_module, 1);
+       const char *const *fields = t_strsplit_spaces(args, " ");
+       while(*fields != NULL) {
+               if (strncmp(*fields, "file=", 5) == 0) {
+                        module->file = p_strdup(pool, (*fields)+5);
+               } else if (strncmp(*fields, "blocking=", 9) == 0) {
+                       const char *value = (*fields)+9;
+                       if (strcmp(value, "yes") == 0) {
+                               blocking = TRUE;
+                       } else if (strcmp(value, "no") == 0) {
+                               blocking = FALSE;
+                       } else {
+                               i_fatal("Invalid value %s. "
+                                       "Field blocking must be yes or no",
+                                       value);
+                       }
+               } else {
+                       i_fatal("Unsupported parameter %s", *fields);
+               }
+               fields++;
+       }
+
+       if (module->file == NULL)
+               i_fatal("passdb-lua: Missing mandatory file= parameter");
+
+       module->module.blocking = blocking;
+       return &module->module;
+}
+
+static void passdb_lua_init(struct passdb_module *_module)
+{
+       struct dlua_passdb_module *module =
+               (struct dlua_passdb_module *)_module;
+       const char *error;
+
+       if (dlua_script_create_file(module->file, &module->script, &error) < 0 ||
+           auth_lua_script_init(module->script, &error) < 0)
+               i_fatal("passdb-lua: initialization failed: %s", error);
+       module->has_password_verify =
+               dlua_script_has_function(module->script, AUTH_LUA_PASSWORD_VERIFY);
+}
+
+static void passdb_lua_deinit(struct passdb_module *_module)
+{
+       struct dlua_passdb_module *module =
+               (struct dlua_passdb_module *)_module;
+       dlua_script_unref(&module->script);
+}
+
+#ifndef PLUGIN_BUILD
+struct passdb_module_interface passdb_lua =
+#else
+struct passdb_module_interface passdb_lua_plugin =
+#endif
+{
+       "lua",
+
+       passdb_lua_preinit,
+       passdb_lua_init,
+       passdb_lua_deinit,
+
+       passdb_lua_verify_plain,
+       passdb_lua_lookup_credentials,
+       NULL
+};
+#else
+struct passdb_module_interface passdb_lua = {
+       .name = "lua"
+};
+#endif
index 3078bf832b39410f5badc584cd2f335017c3dc7f..49b50552d94e4716e371380214be5e4646a60edc 100644 (file)
@@ -289,6 +289,9 @@ void passdbs_generate_md5(unsigned char md5[STATIC_ARRAY MD5_RESULTLEN])
 extern struct passdb_module_interface passdb_passwd;
 extern struct passdb_module_interface passdb_bsdauth;
 extern struct passdb_module_interface passdb_dict;
+#ifdef HAVE_LUA
+extern struct passdb_module_interface passdb_lua;
+#endif
 extern struct passdb_module_interface passdb_shadow;
 extern struct passdb_module_interface passdb_passwd_file;
 extern struct passdb_module_interface passdb_pam;
@@ -307,6 +310,9 @@ void passdbs_init(void)
        passdb_register_module(&passdb_passwd);
        passdb_register_module(&passdb_bsdauth);
        passdb_register_module(&passdb_dict);
+#ifdef HAVE_LUA
+       passdb_register_module(&passdb_lua);
+#endif
        passdb_register_module(&passdb_passwd_file);
        passdb_register_module(&passdb_pam);
        passdb_register_module(&passdb_checkpassword);
index ae059da2167d13a746ddb5988142ce33bc6afdf8..f20aa87c17308986d8f8339ad0ed3313c48a3380 100644 (file)
@@ -6,9 +6,13 @@
 #include "lib.h"
 #include "test-common.h"
 
+struct auth_passdb;
+
 void test_auth_request_var_expand(void);
 void test_db_dict_parse_cache_key(void);
 void test_username_filter(void);
+void test_db_lua(void);
+struct auth_passdb *passdb_mock(void);
 
 #endif
 
diff --git a/src/auth/test-lua.c b/src/auth/test-lua.c
new file mode 100644 (file)
index 0000000..9b2ef76
--- /dev/null
@@ -0,0 +1,36 @@
+#include "test-auth.h"
+#include "istream.h"
+#include "auth-settings.h"
+#include "auth-request.h"
+#include "db-lua.h"
+
+void test_db_lua(void)
+{
+       const char *scheme,*pass;
+       struct auth_settings set;
+       global_auth_settings = &set;
+       passdbs_init();
+
+       struct auth_request *req = auth_request_new_dummy();
+       req->passdb = passdb_mock();
+       req->debug = TRUE;
+       req->user = "testuser";
+
+       static const char *luascript =
+"function auth_passdb_lookup(req)\n"
+"  req:log_debug(\"user \" .. req.user)\n"
+"  return dovecot.auth.PASSDB_RESULT_OK, req:var_expand(\"password=pass\")\n"
+"end\n";
+       const char *error = NULL;
+       struct dlua_script *script = NULL;
+
+       test_begin("auth db lua");
+
+       test_assert(dlua_script_create_string(luascript, &script, &error) == 0);
+       test_assert(auth_lua_script_init(script, &error) == 0);
+       if (script != NULL) {
+               test_assert(auth_lua_call_passdb_lookup(script, req, &scheme, &pass, &error) == 1);
+       }
+       dlua_script_unref(&script);
+       test_end();
+}
index 941fab9d3cb191768c338790341b5dda1b7ede52..fddf5e1ecd26be099ea26bc6ec0e2d7b2e4687c5 100644 (file)
@@ -11,6 +11,9 @@ int main(int argc, const char *argv[])
                TEST_NAMED(test_auth_request_var_expand)
                TEST_NAMED(test_db_dict_parse_cache_key)
                TEST_NAMED(test_username_filter)
+#if defined(BUILTIN_LUA)
+               TEST_NAMED(test_db_lua)
+#endif
                { NULL, NULL }
        };
 
index c4e0e6b423dcacd8f8ca2db85383218e72a7562a..b24e47be9a0e1b90e88dce8c06ef82ff6bab2e5e 100644 (file)
@@ -1,13 +1,91 @@
 /* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
 
-#include "lib.h"
+#include "test-auth.h"
 #include "auth-common.h"
+#include "passdb.h"
 
 struct auth_penalty *auth_penalty;
 time_t process_start_time;
 bool worker, worker_restart_request;
+static struct passdb_module *mock_passdb_mod = NULL;
+
 void auth_module_load(const char *names ATTR_UNUSED)
 {
 }
 void auth_refresh_proctitle(void) {
 }
+
+static void passdb_mock_init(struct passdb_module *module ATTR_UNUSED)
+{
+}
+static void passdb_mock_deinit(struct passdb_module *module ATTR_UNUSED)
+{
+}
+static void passdb_mock_verify_plain(struct auth_request *request, const char *password ATTR_UNUSED,
+                                    verify_plain_callback_t *callback)
+{
+       callback(PASSDB_RESULT_OK, request);
+}
+
+static struct passdb_module_interface mock_interface = {
+       .name = "mock",
+       .init = passdb_mock_init,
+       .deinit = passdb_mock_deinit,
+       .verify_plain = passdb_mock_verify_plain,
+};
+
+static struct auth_passdb_settings set = {
+       .name = "mock",
+       .driver = "mock",
+       .args = "",
+       .default_fields = "",
+       .override_fields = "",
+       .mechanisms = "",
+       .username_filter = "",
+       .skip = "never",
+       .result_success = "return-ok",
+       .result_failure = "continue",
+       .result_internalfail = "continue",
+       .deny = FALSE,
+       .pass = FALSE,
+       .master = FALSE,
+       .auth_verbose = "default"
+};
+
+static void passdb_mock_mod_init(void)
+{
+       if (mock_passdb_mod != NULL)
+               return;
+
+       passdb_register_module(&mock_interface);
+
+       struct auth_passdb_settings set = {
+               .name = "mock",
+               .driver = "mock",
+               .args = "",
+               .default_fields = "",
+               .override_fields = "",
+               .mechanisms = "",
+               .username_filter = "",
+
+               .skip = "never",
+               .result_success = "return-ok",
+               .result_failure = "continue",
+               .result_internalfail = "continue",
+
+               .deny = FALSE,
+               .pass = FALSE,
+               .master = FALSE,
+               .auth_verbose = "default"
+       };
+       mock_passdb_mod = passdb_preinit(default_pool, &set);
+}
+
+struct auth_passdb *passdb_mock(void)
+{
+       passdb_mock_mod_init();
+       struct auth_passdb *ret = i_new(struct auth_passdb, 1);
+       ret->set = &set;
+       ret->passdb = mock_passdb_mod;
+       return ret;
+}
diff --git a/src/auth/userdb-lua.c b/src/auth/userdb-lua.c
new file mode 100644 (file)
index 0000000..15840e6
--- /dev/null
@@ -0,0 +1,125 @@
+/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */
+
+#include "auth-common.h"
+#include "userdb.h"
+
+#if defined(BUILTIN_LUA) || defined(PLUGIN_BUILD)
+
+#include "db-lua.h"
+
+struct dlua_userdb_module {
+       struct userdb_module module;
+       struct dlua_script *script;
+       const char *file;
+};
+
+static void userdb_lua_lookup(struct auth_request *auth_request,
+                             userdb_callback_t *callback)
+{
+       struct userdb_module *_module = auth_request->userdb->userdb;
+       struct dlua_userdb_module *module =
+               (struct dlua_userdb_module *)_module;
+       const char *error;
+       enum userdb_result result =
+               auth_lua_call_userdb_lookup(module->script, auth_request, &error);
+       callback(result, auth_request);
+}
+
+static struct userdb_module *
+userdb_lua_preinit(pool_t pool, const char *args)
+{
+       struct dlua_userdb_module *module;
+       bool blocking = TRUE;
+
+       module = p_new(pool, struct dlua_userdb_module, 1);
+       const char *const *fields = t_strsplit_spaces(args, " ");
+       while(*fields != NULL) {
+               if (strncmp(*fields, "file=", 5) == 0) {
+                        module->file = p_strdup(pool, (*fields)+5);
+               } else if (strncmp(*fields, "blocking=", 9) == 0) {
+                       const char *value = (*fields)+9;
+                       if (strcmp(value, "yes") == 0) {
+                               blocking = TRUE;
+                       } else if (strcmp(value, "no") == 0) {
+                               blocking = FALSE;
+                       } else {
+                               i_fatal("Invalid value %s. "
+                                       "Field blocking must be yes or no",
+                                       value);
+                       }
+               } else {
+                       i_fatal("Unsupported parameter %s", *fields);
+               }
+               fields++;
+       }
+
+       if (module->file == NULL)
+               i_fatal("passdb-lua: Missing mandatory file= parameter");
+
+       module->module.blocking = blocking;
+       return &module->module;
+}
+
+static void userdb_lua_init(struct userdb_module *_module)
+{
+       struct dlua_userdb_module *module =
+               (struct dlua_userdb_module *)_module;
+       const char *error;
+
+       if (dlua_script_create_file(module->file, &module->script, &error) < 0 ||
+           auth_lua_script_init(module->script, &error) < 0)
+               i_fatal("userdb-lua: initialization failed: %s", error);
+}
+
+static void userdb_lua_deinit(struct userdb_module *_module)
+{
+       struct dlua_userdb_module *module =
+               (struct dlua_userdb_module *)_module;
+       dlua_script_unref(&module->script);
+}
+
+static struct userdb_iterate_context *
+userdb_lua_iterate_init(struct auth_request *auth_request,
+                       userdb_iter_callback_t *callback,
+                       void *context)
+{
+       struct userdb_module *_module = auth_request->userdb->userdb;
+       struct dlua_userdb_module *module =
+               (struct dlua_userdb_module *)_module;
+       return auth_lua_call_userdb_iterate_init(module->script, auth_request,
+                                                callback, context);
+}
+
+static void userdb_lua_iterate_next(struct userdb_iterate_context *ctx)
+{
+       auth_lua_userdb_iterate_next(ctx);
+}
+
+static int userdb_lua_iterate_deinit(struct userdb_iterate_context *ctx)
+{
+       return auth_lua_userdb_iterate_deinit(ctx);
+}
+
+#ifndef PLUGIN_BUILD
+struct userdb_module_interface userdb_lua =
+#else
+struct userdb_module_interface userdb_lua_plugin =
+#endif
+{
+       "lua",
+
+       userdb_lua_preinit,
+       userdb_lua_init,
+       userdb_lua_deinit,
+
+       userdb_lua_lookup,
+
+       userdb_lua_iterate_init,
+       userdb_lua_iterate_next,
+       userdb_lua_iterate_deinit
+};
+#else
+struct userdb_module_interface userdb_lua = {
+       .name = "lua"
+};
+#endif
index c45eda499e0c243331ce6a7bd2c782198ec8a85c..a2187e36c304b5bd7bfad7c4de41310e1f7205e6 100644 (file)
@@ -220,6 +220,9 @@ extern struct userdb_module_interface userdb_ldap;
 extern struct userdb_module_interface userdb_sql;
 extern struct userdb_module_interface userdb_checkpassword;
 extern struct userdb_module_interface userdb_dict;
+#ifdef HAVE_LUA
+extern struct userdb_module_interface userdb_lua;
+#endif
 
 void userdbs_init(void)
 {
@@ -234,6 +237,9 @@ void userdbs_init(void)
        userdb_register_module(&userdb_sql);
        userdb_register_module(&userdb_checkpassword);
        userdb_register_module(&userdb_dict);
+#ifdef HAVE_LUA
+       userdb_register_module(&userdb_lua);
+#endif
 }
 
 void userdbs_deinit(void)
index 6676477434b81924a9151fe7434310e3e1872861..4914c91923f38080b2920333a1c74e3e60ef5767 100644 (file)
@@ -44,7 +44,7 @@ struct dlua_script {
        struct istream *in;
        ssize_t last_read;
 
-       unsigned int ref;
+       int ref;
        bool init:1;
 };