From: Timo Sirainen Date: Sun, 3 Mar 2024 15:00:32 +0000 (+0200) Subject: auth: passdb/userdb sql - Convert to new settings X-Git-Tag: 2.4.1~942 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ef0c63b690e6ef9fbd53cb815dfab50d1667ba3a;p=thirdparty%2Fdovecot%2Fcore.git auth: passdb/userdb sql - Convert to new settings sql_drivers_deinit() needed to be moved earlier, because the sql_db is now kept in cache until it's called. In the earlier code the sql_db was deinitialized when the passdb/userdb sql was deinitialized. If the sql_db deinit happens too late, it's late in aborting requests and that causes crashes. --- diff --git a/src/auth/db-sql.c b/src/auth/db-sql.c index 953dd32b1a..a3a23fdfab 100644 --- a/src/auth/db-sql.c +++ b/src/auth/db-sql.c @@ -4,126 +4,13 @@ #if defined(PASSDB_SQL) || defined(USERDB_SQL) -#include "settings-legacy.h" #include "auth-request.h" #include "auth-worker-server.h" -#include "auth-common.h" #include "db-sql.h" -#define DEF_STR(name) DEF_STRUCT_STR(name, db_sql_settings) -#define DEF_INT(name) DEF_STRUCT_INT(name, db_sql_settings) -#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, db_sql_settings) - -static struct setting_def setting_defs[] = { - DEF_STR(driver), - DEF_STR(connect), - DEF_STR(password_query), - DEF_STR(user_query), - DEF_STR(update_query), - DEF_STR(iterate_query), - DEF_STR(default_pass_scheme), - - { 0, NULL, 0 } -}; - -static struct db_sql_settings default_db_sql_settings = { - .driver = NULL, - .connect = NULL, - .password_query = "", - .user_query = "", - .update_query = "", - .iterate_query = "", - .default_pass_scheme = "MD5", -}; - -static struct db_sql_connection *connections = NULL; - -static struct db_sql_connection *sql_conn_find(const char *config_path) -{ - struct db_sql_connection *conn; - - for (conn = connections; conn != NULL; conn = conn->next) { - if (strcmp(conn->config_path, config_path) == 0) - return conn; - } - - return NULL; -} - -static const char *parse_setting(const char *key, const char *value, - struct db_sql_connection *conn) -{ - return parse_setting_from_defs(conn->pool, setting_defs, - &conn->set, key, value); -} - -struct db_sql_connection *db_sql_init(const char *config_path) -{ - struct db_sql_connection *conn; - struct sql_legacy_settings set; - const char *error; - pool_t pool; - - conn = sql_conn_find(config_path); - if (conn != NULL) { - conn->refcount++; - return conn; - } - - if (*config_path == '\0') - i_fatal("sql: Configuration file path not given"); - - pool = pool_alloconly_create("db_sql_connection", 1024); - conn = p_new(pool, struct db_sql_connection, 1); - conn->pool = pool; - - conn->refcount = 1; - - conn->config_path = p_strdup(pool, config_path); - conn->set = default_db_sql_settings; - if (!settings_read_nosection(config_path, parse_setting, conn, &error)) - i_fatal("sql %s: %s", config_path, error); - - if (conn->set.driver == NULL) { - i_fatal("sql: driver not set in configuration file %s", - config_path); - } - if (conn->set.connect == NULL) { - i_fatal("sql: connect string not set in configuration file %s", - config_path); - } - i_zero(&set); - set.driver = conn->set.driver; - set.connect_string = conn->set.connect; - set.event_parent = auth_event; - if (sql_init_legacy_full(&set, &conn->db, &error) < 0) { - i_fatal("sql: %s", error); - } - - conn->next = connections; - connections = conn; - return conn; -} - -void db_sql_unref(struct db_sql_connection **_conn) -{ - struct db_sql_connection *conn = *_conn; - - /* abort all pending auth requests before setting conn to NULL, - so that callbacks can still access it */ - sql_disconnect(conn->db); - - *_conn = NULL; - if (--conn->refcount > 0) - return; - - sql_unref(&conn->db); - pool_unref(&conn->pool); -} - -void db_sql_connect(struct db_sql_connection *conn) +void db_sql_connect(struct sql_db *db) { - if (sql_connect(conn->db) < 0 && worker) { + if (sql_connect(db) < 0 && worker) { /* auth worker's sql connection failed. we can't do anything useful until the connection works. there's no point in having tons of worker processes all logging failures, @@ -134,7 +21,7 @@ void db_sql_connect(struct db_sql_connection *conn) } } -void db_sql_success(struct db_sql_connection *conn ATTR_UNUSED) +void db_sql_success(void) { if (worker) auth_worker_server_send_success(); diff --git a/src/auth/db-sql.h b/src/auth/db-sql.h index 81e5401da3..d3c786f293 100644 --- a/src/auth/db-sql.h +++ b/src/auth/db-sql.h @@ -3,31 +3,7 @@ #include "sql-api.h" -struct db_sql_settings { - const char *driver; - const char *connect; - const char *password_query; - const char *user_query; - const char *update_query; - const char *iterate_query; - const char *default_pass_scheme; -}; - -struct db_sql_connection { - struct db_sql_connection *next; - - pool_t pool; - int refcount; - - char *config_path; - struct db_sql_settings set; - struct sql_db *db; -}; - -struct db_sql_connection *db_sql_init(const char *config_path); -void db_sql_unref(struct db_sql_connection **conn); - -void db_sql_connect(struct db_sql_connection *conn); -void db_sql_success(struct db_sql_connection *conn); +void db_sql_connect(struct sql_db *db); +void db_sql_success(void); #endif diff --git a/src/auth/passdb-sql.c b/src/auth/passdb-sql.c index 0658ba0723..aab83459e0 100644 --- a/src/auth/passdb-sql.c +++ b/src/auth/passdb-sql.c @@ -6,6 +6,8 @@ #ifdef PASSDB_SQL #include "safe-memset.h" +#include "settings.h" +#include "settings-parser.h" #include "password-scheme.h" #include "auth-cache.h" #include "db-sql.h" @@ -15,7 +17,7 @@ struct sql_passdb_module { struct passdb_module module; - struct db_sql_connection *conn; + struct sql_db *db; }; struct passdb_sql_request { @@ -27,12 +29,40 @@ struct passdb_sql_request { } callback; }; +struct passdb_sql_settings { + pool_t pool; + const char *query; + const char *update_query; +}; +#undef DEF +#define DEF(type, name) \ + SETTING_DEFINE_STRUCT_##type("passdb_sql_"#name, name, struct passdb_sql_settings) +static const struct setting_define passdb_sql_setting_defines[] = { + DEF(STR, query), + DEF(STR, update_query), + + SETTING_DEFINE_LIST_END +}; + +static const struct passdb_sql_settings passdb_sql_default_settings = { + .query = "", + .update_query = "", +}; +const struct setting_parser_info passdb_sql_setting_parser_info = { + .name = "passdb_sql", + + .defines = passdb_sql_setting_defines, + .defaults = &passdb_sql_default_settings, + + .struct_size = sizeof(struct passdb_sql_settings), + .pool_offset1 = 1 + offsetof(struct passdb_sql_settings, pool), +}; + static void sql_query_save_results(struct sql_result *result, struct passdb_sql_request *sql_request) { struct auth_request *auth_request = sql_request->auth_request; struct passdb_module *_module = auth_request->passdb->passdb; - struct sql_passdb_module *module = (struct sql_passdb_module *)_module; unsigned int i, fields_count; const char *name, *value; @@ -47,7 +77,7 @@ static void sql_query_save_results(struct sql_result *result, auth_request_set_null_field(auth_request, name); else { auth_request_set_field(auth_request, name, value, - module->conn->set.default_pass_scheme); + _module->default_pass_scheme); } } } @@ -56,8 +86,6 @@ static void sql_query_callback(struct sql_result *result, struct passdb_sql_request *sql_request) { struct auth_request *auth_request = sql_request->auth_request; - struct passdb_module *_module = auth_request->passdb->passdb; - struct sql_passdb_module *module = (struct sql_passdb_module *)_module; enum passdb_result passdb_result; const char *password, *scheme; char *dup_password = NULL; @@ -68,7 +96,7 @@ static void sql_query_callback(struct sql_result *result, ret = sql_result_next_row(result); if (ret >= 0) - db_sql_success(module->conn); + db_sql_success(); if (ret < 0) { e_error(authdb_event(auth_request), "Password query failed: %s", sql_result_get_error(result)); @@ -133,39 +161,41 @@ static void sql_query_callback(struct sql_result *result, auth_request_unref(&auth_request); } -static const char * -passdb_sql_escape(const char *str, const struct auth_request *auth_request) +static const char *passdb_sql_escape(const char *str, void *context) { - struct passdb_module *_module = auth_request->passdb->passdb; - struct sql_passdb_module *module = (struct sql_passdb_module *)_module; - - return sql_escape_string(module->conn->db, str); + struct sql_db *db = context; + return sql_escape_string(db, str); } static void sql_lookup_pass(struct passdb_sql_request *sql_request) { struct passdb_module *_module = sql_request->auth_request->passdb->passdb; - struct sql_passdb_module *module = (struct sql_passdb_module *)_module; - const char *query, *error; - - if (t_auth_request_var_expand(module->conn->set.password_query, - sql_request->auth_request, - passdb_sql_escape, &query, &error) <= 0) { - e_debug(authdb_event(sql_request->auth_request), - "Failed to expand password_query=%s: %s", - module->conn->set.password_query, error); - sql_request->callback.verify_plain(PASSDB_RESULT_INTERNAL_FAILURE, - sql_request->auth_request); + struct sql_passdb_module *module = + container_of(_module, struct sql_passdb_module, module); + const struct passdb_sql_settings *set; + const char *error; + + struct settings_get_params params = { + .escape_func = passdb_sql_escape, + .escape_context = module->db, + }; + if (settings_get_params(authdb_event(sql_request->auth_request), + &passdb_sql_setting_parser_info, ¶ms, + &set, &error) < 0) { + e_error(authdb_event(sql_request->auth_request), "%s", error); + sql_request->callback.verify_plain( + PASSDB_RESULT_INTERNAL_FAILURE, + sql_request->auth_request); return; } e_debug(authdb_event(sql_request->auth_request), - "query: %s", query); + "query: %s", set->query); auth_request_ref(sql_request->auth_request); - sql_query(module->conn->db, query, - sql_query_callback, sql_request); + sql_query(module->db, set->query, sql_query_callback, sql_request); + settings_free(set); } static void sql_verify_plain(struct auth_request *request, @@ -213,19 +243,18 @@ static void sql_set_credentials(struct auth_request *request, set_credentials_callback_t *callback) { struct sql_passdb_module *module = - (struct sql_passdb_module *) request->passdb->passdb; + container_of(request->passdb->passdb, + struct sql_passdb_module, module); struct sql_transaction_context *transaction; struct passdb_sql_request *sql_request; - const char *query, *error; + const struct passdb_sql_settings *set; + const char *error; request->mech_password = p_strdup(request->pool, new_credentials); - if (t_auth_request_var_expand(module->conn->set.update_query, - request, passdb_sql_escape, - &query, &error) <= 0) { - e_error(authdb_event(request), - "Failed to expand update_query=%s: %s", - module->conn->set.update_query, error); + if (settings_get(authdb_event(request), &passdb_sql_setting_parser_info, 0, + &set, &error) < 0) { + e_error(authdb_event(request), "%s", error); callback(FALSE, request); return; } @@ -234,52 +263,69 @@ static void sql_set_credentials(struct auth_request *request, sql_request->auth_request = request; sql_request->callback.set_credentials = callback; - transaction = sql_transaction_begin(module->conn->db); - sql_update(transaction, query); + transaction = sql_transaction_begin(module->db); + sql_update(transaction, set->update_query); sql_transaction_commit(&transaction, sql_set_credentials_callback, sql_request); + settings_free(set); } -static struct passdb_module * -passdb_sql_preinit(pool_t pool, const char *args) +static int +passdb_sql_preinit(pool_t pool, struct event *event, + struct passdb_module **module_r, const char **error_r) { struct sql_passdb_module *module; - struct db_sql_connection *conn; + const struct passdb_sql_settings *set; + + if (settings_get(event, &passdb_sql_setting_parser_info, + SETTINGS_GET_FLAG_NO_CHECK | + SETTINGS_GET_FLAG_NO_EXPAND, + &set, error_r) < 0) + return -1; module = p_new(pool, struct sql_passdb_module, 1); - module->conn = conn = db_sql_init(args); + if (sql_init_auto(event, &module->db, error_r) <= 0) { + settings_free(set); + return -1; + } module->module.default_cache_key = - auth_cache_parse_key(pool, conn->set.password_query); - module->module.default_pass_scheme = conn->set.default_pass_scheme; - return &module->module; + auth_cache_parse_key(pool, set->query); + settings_free(set); + + *module_r = &module->module; + return 0; } static void passdb_sql_init(struct passdb_module *_module) { struct sql_passdb_module *module = - (struct sql_passdb_module *)_module; + container_of(_module, struct sql_passdb_module, module); enum sql_db_flags flags; - flags = sql_get_flags(module->conn->db); + flags = sql_get_flags(module->db); module->module.blocking = (flags & SQL_DB_FLAG_BLOCKING) != 0; if (!module->module.blocking || worker) - db_sql_connect(module->conn); + db_sql_connect(module->db); } static void passdb_sql_deinit(struct passdb_module *_module) { struct sql_passdb_module *module = - (struct sql_passdb_module *)_module; + container_of(_module, struct sql_passdb_module, module); - db_sql_unref(&module->conn); + /* Abort any pending requests, even if the database is still + kept referenced. */ + sql_disconnect(module->db); + sql_unref(&module->db); } struct passdb_module_interface passdb_sql = { .name = "sql", + .fields_supported = TRUE, - .preinit_legacy = passdb_sql_preinit, + .preinit = passdb_sql_preinit, .init = passdb_sql_init, .deinit = passdb_sql_deinit, diff --git a/src/auth/userdb-sql.c b/src/auth/userdb-sql.c index 5c59c7d2f1..ffba715e28 100644 --- a/src/auth/userdb-sql.c +++ b/src/auth/userdb-sql.c @@ -5,6 +5,8 @@ #ifdef USERDB_SQL +#include "settings.h" +#include "settings-parser.h" #include "auth-cache.h" #include "db-sql.h" @@ -13,7 +15,7 @@ struct sql_userdb_module { struct userdb_module module; - struct db_sql_connection *conn; + struct sql_db *db; }; struct userdb_sql_request { @@ -28,6 +30,34 @@ struct sql_userdb_iterate_context { bool call_iter:1; }; +struct userdb_sql_settings { + pool_t pool; + const char *query; + const char *iterate_query; +}; +#undef DEF +#define DEF(type, name) \ + SETTING_DEFINE_STRUCT_##type("userdb_sql_"#name, name, struct userdb_sql_settings) +static const struct setting_define userdb_sql_setting_defines[] = { + DEF(STR, query), + DEF(STR, iterate_query), + + SETTING_DEFINE_LIST_END +}; +static const struct userdb_sql_settings userdb_sql_default_settings = { + .query = "", + .iterate_query = "", +}; +const struct setting_parser_info userdb_sql_setting_parser_info = { + .name = "userdb_sql", + + .defines = userdb_sql_setting_defines, + .defaults = &userdb_sql_default_settings, + + .struct_size = sizeof(struct userdb_sql_settings), + .pool_offset1 = 1 + offsetof(struct userdb_sql_settings, pool), +}; + static void userdb_sql_iterate_next(struct userdb_iterate_context *_ctx); static int userdb_sql_iterate_deinit(struct userdb_iterate_context *_ctx); @@ -54,15 +84,12 @@ static void sql_query_callback(struct sql_result *sql_result, struct userdb_sql_request *sql_request) { struct auth_request *auth_request = sql_request->auth_request; - struct userdb_module *_module = auth_request->userdb->userdb; - struct sql_userdb_module *module = - (struct sql_userdb_module *)_module; enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE; int ret; ret = sql_result_next_row(sql_result); if (ret >= 0) - db_sql_success(module->conn); + db_sql_success(); if (ret < 0) { e_error(authdb_event(auth_request), "User query failed: %s", sql_result_get_error(sql_result)); @@ -79,14 +106,10 @@ static void sql_query_callback(struct sql_result *sql_result, i_free(sql_request); } -static const char * -userdb_sql_escape(const char *str, const struct auth_request *auth_request) +static const char *userdb_sql_escape(const char *str, void *context) { - struct userdb_module *_module = auth_request->userdb->userdb; - struct sql_userdb_module *module = - (struct sql_userdb_module *)_module; - - return sql_escape_string(module->conn->db, str); + struct sql_db *db = context; + return sql_escape_string(db, str); } static void userdb_sql_lookup(struct auth_request *auth_request, @@ -94,16 +117,19 @@ static void userdb_sql_lookup(struct auth_request *auth_request, { struct userdb_module *_module = auth_request->userdb->userdb; struct sql_userdb_module *module = - (struct sql_userdb_module *)_module; + container_of(_module, struct sql_userdb_module, module); struct userdb_sql_request *sql_request; - const char *query, *error; - - if (t_auth_request_var_expand(module->conn->set.user_query, - auth_request, userdb_sql_escape, - &query, &error) <= 0) { - e_error(authdb_event(auth_request), - "Failed to expand user_query=%s: %s", - module->conn->set.user_query, error); + const struct userdb_sql_settings *set; + const char *error; + + struct settings_get_params params = { + .escape_func = userdb_sql_escape, + .escape_context = module->db, + }; + if (settings_get_params(authdb_event(auth_request), + &userdb_sql_setting_parser_info, ¶ms, + &set, &error) < 0) { + e_error(authdb_event(auth_request), "%s", error); callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); return; } @@ -113,10 +139,10 @@ static void userdb_sql_lookup(struct auth_request *auth_request, sql_request->callback = callback; sql_request->auth_request = auth_request; - e_debug(authdb_event(auth_request), "%s", query); + e_debug(authdb_event(auth_request), "%s", set->query); - sql_query(module->conn->db, query, - sql_query_callback, sql_request); + sql_query(module->db, set->query, sql_query_callback, sql_request); + settings_free(set); } static void sql_iter_query_callback(struct sql_result *sql_result, @@ -137,9 +163,10 @@ userdb_sql_iterate_init(struct auth_request *auth_request, { struct userdb_module *_module = auth_request->userdb->userdb; struct sql_userdb_module *module = - (struct sql_userdb_module *)_module; + container_of(_module, struct sql_userdb_module, module); struct sql_userdb_iterate_context *ctx; - const char *query, *error; + const struct userdb_sql_settings *set; + const char *error; ctx = i_new(struct sql_userdb_iterate_context, 1); ctx->ctx.auth_request = auth_request; @@ -147,19 +174,17 @@ userdb_sql_iterate_init(struct auth_request *auth_request, ctx->ctx.context = context; auth_request_ref(auth_request); - if (t_auth_request_var_expand(module->conn->set.iterate_query, - auth_request, userdb_sql_escape, - &query, &error) <= 0) { - e_error(authdb_event(auth_request), - "Failed to expand iterate_query=%s: %s", - module->conn->set.iterate_query, error); + if (settings_get(authdb_event(auth_request), + &userdb_sql_setting_parser_info, 0, + &set, &error) < 0) { + e_error(authdb_event(auth_request), "%s", error); ctx->ctx.failed = TRUE; return &ctx->ctx; } - sql_query(module->conn->db, query, - sql_iter_query_callback, ctx); - e_debug(authdb_event(auth_request), "%s", query); + sql_query(module->db, set->iterate_query, sql_iter_query_callback, ctx); + e_debug(authdb_event(auth_request), "%s", set->iterate_query); + settings_free(set); return &ctx->ctx; } @@ -196,9 +221,7 @@ static int userdb_sql_iterate_get_user(struct sql_userdb_iterate_context *ctx, static void userdb_sql_iterate_next(struct userdb_iterate_context *_ctx) { struct sql_userdb_iterate_context *ctx = - (struct sql_userdb_iterate_context *)_ctx; - struct userdb_module *_module = _ctx->auth_request->userdb->userdb; - struct sql_userdb_module *module = (struct sql_userdb_module *)_module; + container_of(_ctx, struct sql_userdb_iterate_context, ctx); const char *user; int ret; @@ -214,7 +237,7 @@ static void userdb_sql_iterate_next(struct userdb_iterate_context *_ctx) ret = sql_result_next_row(ctx->result); if (ret >= 0) - db_sql_success(module->conn); + db_sql_success(); if (ret > 0) { if (userdb_sql_iterate_get_user(ctx, &user) < 0) e_error(authdb_event(_ctx->auth_request), @@ -239,7 +262,7 @@ static void userdb_sql_iterate_next(struct userdb_iterate_context *_ctx) static int userdb_sql_iterate_deinit(struct userdb_iterate_context *_ctx) { struct sql_userdb_iterate_context *ctx = - (struct sql_userdb_iterate_context *)_ctx; + container_of(_ctx, struct sql_userdb_iterate_context, ctx); int ret = _ctx->failed ? -1 : 0; auth_request_unref(&_ctx->auth_request); @@ -254,44 +277,60 @@ static int userdb_sql_iterate_deinit(struct userdb_iterate_context *_ctx) return ret; } -static struct userdb_module * -userdb_sql_preinit(pool_t pool, const char *args) +static int +userdb_sql_preinit(pool_t pool, struct event *event, + struct userdb_module **module_r, const char **error_r) { struct sql_userdb_module *module; + const struct userdb_sql_settings *set; + + if (settings_get(event, &userdb_sql_setting_parser_info, + SETTINGS_GET_FLAG_NO_CHECK | + SETTINGS_GET_FLAG_NO_EXPAND, + &set, error_r) < 0) + return -1; module = p_new(pool, struct sql_userdb_module, 1); - module->conn = db_sql_init(args); + if (sql_init_auto(event, &module->db, error_r) <= 0) { + settings_free(set); + return -1; + } module->module.default_cache_key = - auth_cache_parse_key(pool, module->conn->set.user_query); - return &module->module; + auth_cache_parse_key(pool, set->query); + settings_free(set); + *module_r = &module->module; + return 0; } static void userdb_sql_init(struct userdb_module *_module) { struct sql_userdb_module *module = - (struct sql_userdb_module *)_module; + container_of(_module, struct sql_userdb_module, module); enum sql_db_flags flags; - flags = sql_get_flags(module->conn->db); + flags = sql_get_flags(module->db); _module->blocking = (flags & SQL_DB_FLAG_BLOCKING) != 0; if (!_module->blocking || worker) - db_sql_connect(module->conn); + db_sql_connect(module->db); } static void userdb_sql_deinit(struct userdb_module *_module) { struct sql_userdb_module *module = - (struct sql_userdb_module *)_module; + container_of(_module, struct sql_userdb_module, module); - db_sql_unref(&module->conn); + /* Abort any pending requests, even if the database is still + kept referenced. */ + sql_disconnect(module->db); + sql_unref(&module->db); } struct userdb_module_interface userdb_sql = { .name = "sql", - .preinit_legacy = userdb_sql_preinit, + .preinit = userdb_sql_preinit, .init = userdb_sql_init, .deinit = userdb_sql_deinit,