# HG changeset patch # User Timo Sirainen # Date 1047670139 -7200 # Node ID a9ac5865daf8dc422f6dd49e611831d01c68778c # Parent cc9b61b9959e45bc3889fe5e9b943a5b63beddd7 Whops, forgot to add. diff -r cc9b61b9959e -r a9ac5865daf8 src/auth/db-pgsql.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/db-pgsql.c Fri Mar 14 21:28:59 2003 +0200 @@ -0,0 +1,173 @@ +/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */ + +#include "config.h" +#undef HAVE_CONFIG_H + +#if defined(PASSDB_PGSQL) || defined(USERDB_PGSQL) + +#include "common.h" +#include "network.h" +#include "str.h" +#include "settings.h" +#include "db-pgsql.h" + +#include + +#define DEF(type, name) { type, #name, offsetof(struct pgsql_settings, name) } + +static struct setting_def setting_defs[] = { + DEF(SET_STR, connect), + DEF(SET_STR, password_query), + DEF(SET_STR, user_query), + DEF(SET_STR, allowed_chars), + DEF(SET_STR, default_pass_scheme) +}; + +struct pgsql_settings default_pgsql_settings = { + MEMBER(connect) "dbname=virtual user=virtual", + MEMBER(password_query) "SELECT password FROM users WHERE userid = '%u'", + MEMBER(user_query) "SELECT home, uid, gid FROM users WHERE userid = '%u'", + MEMBER(allowed_chars) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-@", + MEMBER(default_pass_scheme) "PLAIN-MD5" +}; + +static struct pgsql_connection *pgsql_connections = NULL; + +static int pgsql_conn_open(struct pgsql_connection *conn); +static void pgsql_conn_close(struct pgsql_connection *conn); + +int db_pgsql_is_valid_username(struct pgsql_connection *conn, + const char *username) +{ + const char *p; + + for (p = username; *p != '\0'; p++) { + if (strchr(conn->set.allowed_chars, *p) == NULL) + return FALSE; + } + + return TRUE; +} + +void db_pgsql_query(struct pgsql_connection *conn, const char *query, + struct pgsql_request *request) +{ + PGresult *res; + int failed; + + if (!conn->connected) { + if (!pgsql_conn_open(conn)) { + request->callback(conn, request, NULL); + return; + } + } + + if (verbose_debug) + i_info("PGSQL: Performing query: %s", query); + + res = PQexec(conn->pg, query); + + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + i_error("PGSQL: Query \"%s\" failed: %s", + query, PQresultErrorMessage(res)); + failed = TRUE; + } else if (PQntuples(res) != 1) { + i_error("PGSQL: Query \"%s\" returned %d rows", + query, PQntuples(res)); + failed = TRUE; + } else { + failed = FALSE; + } + + request->callback(conn, request, failed ? NULL : res); + PQclear(res); + +} + +static int pgsql_conn_open(struct pgsql_connection *conn) +{ + if (conn->connected) + return TRUE; + + if (conn->pg == NULL) { + conn->pg = PQconnectdb(conn->set.connect); + if (PQstatus(conn->pg) != CONNECTION_OK) { + i_error("PGSQL: Can't connect to database %s", + conn->set.connect); + return FALSE; + } + } + + conn->connected = TRUE; + return TRUE; +} + +static void pgsql_conn_close(struct pgsql_connection *conn) +{ + conn->connected = FALSE; + + if (conn->pg != NULL) { + PQfinish(conn->pg); + conn->pg = NULL; + } +} + +static struct pgsql_connection *pgsql_conn_find(const char *config_path) +{ + struct pgsql_connection *conn; + + for (conn = pgsql_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, + void *context) +{ + struct pgsql_connection *conn = context; + + return parse_setting_from_defs(conn->pool, setting_defs, + &conn->set, key, value); +} + +struct pgsql_connection *db_pgsql_init(const char *config_path) +{ + struct pgsql_connection *conn; + pool_t pool; + + conn = pgsql_conn_find(config_path); + if (conn != NULL) { + conn->refcount++; + return conn; + } + + pool = pool_alloconly_create("pgsql_connection", 1024); + conn = p_new(pool, struct pgsql_connection, 1); + conn->pool = pool; + + conn->refcount = 1; + + conn->config_path = p_strdup(pool, config_path); + conn->set = default_pgsql_settings; + settings_read(config_path, parse_setting, conn); + + (void)pgsql_conn_open(conn); + + conn->next = pgsql_connections; + pgsql_connections = conn; + return conn; +} + +void db_pgsql_unref(struct pgsql_connection *conn) +{ + if (--conn->refcount > 0) + return; + + pgsql_conn_close(conn); + pool_unref(conn->pool); +} + +#endif diff -r cc9b61b9959e -r a9ac5865daf8 src/auth/db-pgsql.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/db-pgsql.h Fri Mar 14 21:28:59 2003 +0200 @@ -0,0 +1,49 @@ +#ifndef __DB_PGSQL_H +#define __DB_PGSQL_H + +#include + +struct pgsql_connection; +struct pgsql_request; + +typedef void pgqsl_query_callback_t(struct pgsql_connection *conn, + struct pgsql_request *request, + PGresult *res); + +struct pgsql_settings { + const char *connect; + const char *password_query; + const char *user_query; + const char *allowed_chars; + const char *default_pass_scheme; +}; + +struct pgsql_connection { + struct pgsql_connection *next; + + pool_t pool; + int refcount; + + char *config_path; + struct pgsql_settings set; + + PGconn *pg; + + unsigned int connected:1; +}; + +struct pgsql_request { + pgqsl_query_callback_t *callback; + void *context; +}; + +int db_pgsql_is_valid_username(struct pgsql_connection *conn, + const char *username); + +void db_pgsql_query(struct pgsql_connection *conn, const char *query, + struct pgsql_request *request); + +struct pgsql_connection *db_pgsql_init(const char *config_path); +void db_pgsql_unref(struct pgsql_connection *conn); + +#endif diff -r cc9b61b9959e -r a9ac5865daf8 src/auth/passdb-pgsql.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/passdb-pgsql.c Fri Mar 14 21:28:59 2003 +0200 @@ -0,0 +1,173 @@ +/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */ + +#include "config.h" +#undef HAVE_CONFIG_H + +#ifdef PASSDB_PGSQL + +#include "common.h" +#include "str.h" +#include "var-expand.h" +#include "password-scheme.h" +#include "db-pgsql.h" +#include "passdb.h" + +#include +#include +#include + +struct passdb_pgsql_connection { + struct pgsql_connection *conn; +}; + +struct passdb_pgsql_request { + struct pgsql_request request; + + enum passdb_credentials credentials; + union { + verify_plain_callback_t *verify_plain; + lookup_credentials_callback_t *lookup_credentials; + } callback; + + char password[1]; +}; + +static struct passdb_pgsql_connection *passdb_pgsql_conn; + +static void pgsql_handle_request(struct pgsql_connection *conn, + struct pgsql_request *request, PGresult *res) +{ + struct passdb_pgsql_request *pgsql_request = + (struct passdb_pgsql_request *) request; + struct auth_request *auth_request = request->context; + const char *user, *password, *scheme; + int ret = 0; + + user = auth_request->user; + password = NULL; + + if (res != NULL) { + if (PQntuples(res) == 0) { + if (verbose) + i_info("pgsql(%s): Unknown user", user); + } else if (PQntuples(res) > 1) { + i_error("pgsql(%s): Multiple matches for user", user); + } else if (PQnfields(res) != 1) { + i_error("pgsql(%s): Password query returned " + "more than one field", user); + } else { + password = t_strdup(PQgetvalue(res, 0, 0)); + } + } + + scheme = password_get_scheme(&password); + if (scheme == NULL) { + scheme = conn->set.default_pass_scheme; + i_assert(scheme != NULL); + } + + if (pgsql_request->credentials != -1) { + passdb_handle_credentials(pgsql_request->credentials, + user, password, scheme, + pgsql_request->callback.lookup_credentials, + auth_request); + return; + } + + /* verify plain */ + if (password == NULL) { + pgsql_request->callback.verify_plain(PASSDB_RESULT_USER_UNKNOWN, + auth_request); + return; + } + + ret = password_verify(pgsql_request->password, password, + scheme, user); + if (ret < 0) + i_error("pgsql(%s): Unknown password scheme %s", user, scheme); + else if (ret == 0) { + if (verbose) + i_info("pgsql(%s): Password mismatch", user); + } + + pgsql_request->callback.verify_plain(ret > 0 ? PASSDB_RESULT_OK : + PASSDB_RESULT_PASSWORD_MISMATCH, + auth_request); +} + +static void pgsql_lookup_pass(struct auth_request *auth_request, + struct pgsql_request *pgsql_request) +{ + struct pgsql_connection *conn = passdb_pgsql_conn->conn; + const char *query; + string_t *str; + + str = t_str_new(512); + var_expand(str, conn->set.password_query, auth_request->user, NULL); + query = str_c(str); + + pgsql_request->callback = pgsql_handle_request; + pgsql_request->context = auth_request; + + if (db_pgsql_is_valid_username(conn, auth_request->user)) + db_pgsql_query(conn, query, pgsql_request); + else { + if (verbose) { + i_error("pgsql(%s): Invalid username", + auth_request->user); + } + pgsql_handle_request(conn, pgsql_request, NULL); + } +} + +static void +pgsql_verify_plain(struct auth_request *request, const char *password, + verify_plain_callback_t *callback) +{ + struct passdb_pgsql_request *pgsql_request; + + pgsql_request = i_malloc(sizeof(struct passdb_pgsql_request) + + strlen(password)); + pgsql_request->credentials = -1; + pgsql_request->callback.verify_plain = callback; + strcpy(pgsql_request->password, password); + + pgsql_lookup_pass(request, &pgsql_request->request); +} + +static void pgsql_lookup_credentials(struct auth_request *request, + enum passdb_credentials credentials, + lookup_credentials_callback_t *callback) +{ + struct passdb_pgsql_request *pgsql_request; + + pgsql_request = i_new(struct passdb_pgsql_request, 1); + pgsql_request->credentials = credentials; + pgsql_request->callback.lookup_credentials = callback; + + pgsql_lookup_pass(request, &pgsql_request->request); +} + +static void passdb_pgsql_init(const char *args) +{ + struct pgsql_connection *conn; + + passdb_pgsql_conn = i_new(struct passdb_pgsql_connection, 1); + passdb_pgsql_conn->conn = conn = db_pgsql_init(args); +} + +static void passdb_pgsql_deinit(void) +{ + db_pgsql_unref(passdb_pgsql_conn->conn); + i_free(passdb_pgsql_conn); +} + +struct passdb_module passdb_pgsql = { + passdb_pgsql_init, + passdb_pgsql_deinit, + + pgsql_verify_plain, + pgsql_lookup_credentials +}; + +#endif diff -r cc9b61b9959e -r a9ac5865daf8 src/auth/userdb-pgsql.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/userdb-pgsql.c Fri Mar 14 21:28:59 2003 +0200 @@ -0,0 +1,125 @@ +/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */ + +#include "config.h" +#undef HAVE_CONFIG_H + +#ifdef USERDB_PGSQL + +#include "common.h" +#include "str.h" +#include "var-expand.h" +#include "db-pgsql.h" +#include "userdb.h" + +#include +#include +#include + +struct userdb_pgsql_connection { + struct pgsql_connection *conn; +}; + +struct userdb_pgsql_request { + struct pgsql_request request; + userdb_callback_t *userdb_callback; +}; + +static struct userdb_pgsql_connection *userdb_pgsql_conn; + +static int is_result_valid(PGresult *res) +{ + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + i_error("PGSQL: Query failed"); + return FALSE; + } + + if (PQntuples(res) == 0) { + if (verbose) + i_error("PGSQL: Authenticated user not found"); + return FALSE; + } + + if (PQfnumber(res, "home") == -1) { + i_error("PGSQL: User query did not return 'home' field"); + return FALSE; + } + + if (PQfnumber(res, "uid") == -1) { + i_error("PGSQL: User query did not return 'uid' field"); + return FALSE; + } + + if (PQfnumber(res, "gid") == -1) { + i_error("PGSQL: User query did not return 'gid' field"); + return FALSE; + } + + return TRUE; +} + +static void pgsql_handle_request(struct pgsql_connection *conn __attr_unused__, + struct pgsql_request *request, PGresult *res) +{ + struct userdb_pgsql_request *urequest = + (struct userdb_pgsql_request *) request; + struct user_data user; + + if (res != NULL && is_result_valid(res)) { + memset(&user, 0, sizeof(user)); + user.home = PQgetvalue(res, 0, PQfnumber(res, "home")); + user.uid = atoi(PQgetvalue(res, 0, PQfnumber(res, "uid"))); + user.gid = atoi(PQgetvalue(res, 0, PQfnumber(res, "gid"))); + urequest->userdb_callback(&user, request->context); + } else { + urequest->userdb_callback(NULL, request->context); + } +} + +static void userdb_pgsql_lookup(const char *user, userdb_callback_t *callback, + void *context) +{ + struct pgsql_connection *conn = userdb_pgsql_conn->conn; + struct userdb_pgsql_request *request; + const char *query; + string_t *str; + + str = t_str_new(512); + var_expand(str, conn->set.user_query, user, NULL); + query = str_c(str); + + request = i_new(struct userdb_pgsql_request, 1); + request->request.callback = pgsql_handle_request; + request->request.context = context; + request->userdb_callback = callback; + + if (db_pgsql_is_valid_username(conn, user)) + db_pgsql_query(conn, query, &request->request); + else { + if (verbose) + i_info("pgsql(%s): Invalid username", user); + pgsql_handle_request(conn, &request->request, NULL); + } +} + +static void userdb_pgsql_init(const char *args) +{ + struct pgsql_connection *conn; + + userdb_pgsql_conn = i_new(struct userdb_pgsql_connection, 1); + userdb_pgsql_conn->conn = conn = db_pgsql_init(args); +} + +static void userdb_pgsql_deinit(void) +{ + db_pgsql_unref(userdb_pgsql_conn->conn); + i_free(userdb_pgsql_conn); +} + +struct userdb_module userdb_pgsql = { + userdb_pgsql_init, + userdb_pgsql_deinit, + + userdb_pgsql_lookup +}; + +#endif