Here's another patch. This one isn't much different from the last patch, except it tries a little harder to make sure that the required fields are returned by the query (like the pgsql module does). One question about how t_strdup works. How and who should memory returned by t_strdup be freed? My patch has a function my_get_str() which returns the value of t_strdup, and then I do this: user.uid = atoi(my_get_str(res, row, "uid")); /* XXX leak */ user.gid = atoi(my_get_str(res, row, "gid")); /* XXX leak */ Will this result in a memory leak? Matt diff -u -r --new-file work/dovecot-0.99.10/doc/dovecot-mysql.conf work.patched/dovecot-0.99.10/doc/dovecot-mysql.conf --- work/dovecot-0.99.10/doc/dovecot-mysql.conf Wed Dec 31 16:00:00 1969 +++ work.patched/dovecot-0.99.10/doc/dovecot-mysql.conf Tue Sep 30 12:24:00 2003 @@ -0,0 +1,70 @@ +# For the mysql passdb module, you'll need a database with a table that +# contains fields for at least the userid and password. If you want to +# use the user@domain syntax, you might want to have a separate domain +# field as well. +# +# If your users all have the same uig/gid, and have predictable home +# directories, you can use the static userdb module to generate the home +# dir based on the userid and domain. In this case, you won't need fields +# for home, uid, or gid in the database. +# +# If you prefer to use the mysql userdb module, you'll want to add fields +# for home, uid, and gid. Here is an example table: +# +# CREATE TABLE users ( +# userid VARCHAR(128) NOT NULL, +# password VARCHAR(64) NOT NULL, +# home VARCHAR(256) NOT NULL, +# uid INTEGER NOT NULL, +# gid INTEGER NOT NULL, +# active CHAR(1) DEFAULT 'Y' NOT NULL +# ); + +db_host = localhost +db_port = 3306 +#db_unix_socket = /var/tmp/mysql.sock +db = users +db_user = dovecot-db +db_passwd = opensesame +db_client_flags = 0 + +# Default password scheme. +# +# Currently supported schemes include PLAIN, PLAIN-MD5, DIGEST-MD5, and CRYPT. +# +#default_pass_scheme = PLAIN-MD5 + +# Query to retrieve the password. +# +# The query should return one row, one column. If more than one row or column +# is returned, authentication will automatically fail. +# +# Available substitutions: +# %u = entire userid +# %n = user part of user@domain +# %d = domain part of user@domain +# +# Example: +# password_query = SELECT password FROM users WHERE userid = '%n' AND domain = '%d' +# password_query = SELECT password FROM users WHERE userid = '%u' AND active = 'Y' +# +#password_query = SELECT password FROM users WHERE userid = '%u' + +# Query to retrieve the user information. +# +# The query must return only one row. The columns to return are: +# home - Home directory +# mail - MAIL environment +# system_user - System user name (for initgroups()) +# uid - System UID +# gid - System GID +# +# Either home or mail is required. uid and gid are required. If more than one +# row is returned or there's missing fields, login will automatically fail. +# +# Examples +# user_query = SELECT home, uid, gid FROM users WHERE userid = '%n' AND domain = '%d' +# user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u' +# user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u' +# +#user_query = SELECT home, uid, gid FROM users WHERE userid = '%u' diff -u -r --new-file work/dovecot-0.99.10/src/auth/Makefile.in work.patched/dovecot-0.99.10/src/auth/Makefile.in --- work/dovecot-0.99.10/src/auth/Makefile.in Thu Jun 26 09:15:11 2003 +++ work.patched/dovecot-0.99.10/src/auth/Makefile.in Tue Sep 30 12:24:00 2003 @@ -99,10 +99,10 @@ dovecot_auth_LDADD = ../lib-settings/libsettings.a ../lib/liblib.a $(AUTH_LIBS) $(RAND_LIBS) $(MODULE_LIBS) -dovecot_auth_SOURCES = auth-module.c db-ldap.c db-pgsql.c db-passwd-file.c login-connection.c main.c master-connection.c md5crypt.c mech.c mech-anonymous.c mech-cyrus-sasl2.c mech-plain.c mech-digest-md5.c mycrypt.c passdb.c passdb-ldap.c passdb-passwd.c passdb-passwd-file.c passdb-pam.c passdb-shadow.c passdb-vpopmail.c passdb-pgsql.c password-scheme.c userdb.c userdb-ldap.c userdb-passwd.c userdb-passwd-file.c userdb-static.c userdb-vpopmail.c userdb-pgsql.c +dovecot_auth_SOURCES = auth-module.c db-ldap.c db-pgsql.c db-mysql.c db-passwd-file.c login-connection.c main.c master-connection.c md5crypt.c mech.c mech-anonymous.c mech-cyrus-sasl2.c mech-plain.c mech-digest-md5.c mycrypt.c passdb.c passdb-ldap.c passdb-passwd.c passdb-passwd-file.c passdb-pam.c passdb-shadow.c passdb-vpopmail.c passdb-pgsql.c passdb-mysql.c password-scheme.c userdb.c userdb-ldap.c userdb-passwd.c userdb-passwd-file.c userdb-static.c userdb-vpopmail.c userdb-pgsql.c userdb-mysql.c -noinst_HEADERS = auth-login-interface.h auth-master-interface.h auth-mech-desc.h auth-module.h db-ldap.h db-pgsql.h db-passwd-file.h common.h login-connection.h master-connection.h md5crypt.h mech.h mycrypt.h passdb.h password-scheme.h userdb.h userdb-vpopmail.h +noinst_HEADERS = auth-login-interface.h auth-master-interface.h auth-mech-desc.h auth-module.h db-ldap.h db-pgsql.h db-mysql.h db-passwd-file.h common.h login-connection.h master-connection.h md5crypt.h mech.h mycrypt.h passdb.h password-scheme.h userdb.h userdb-vpopmail.h mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_HEADER = ../../config.h @@ -116,17 +116,17 @@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ dovecot_auth_OBJECTS = auth-module.$(OBJEXT) db-ldap.$(OBJEXT) \ -db-pgsql.$(OBJEXT) db-passwd-file.$(OBJEXT) login-connection.$(OBJEXT) \ +db-pgsql.$(OBJEXT) db-mysql.$(OBJEXT) db-passwd-file.$(OBJEXT) login-connection.$(OBJEXT) \ main.$(OBJEXT) master-connection.$(OBJEXT) md5crypt.$(OBJEXT) \ mech.$(OBJEXT) mech-anonymous.$(OBJEXT) mech-cyrus-sasl2.$(OBJEXT) \ mech-plain.$(OBJEXT) mech-digest-md5.$(OBJEXT) mycrypt.$(OBJEXT) \ passdb.$(OBJEXT) passdb-ldap.$(OBJEXT) passdb-passwd.$(OBJEXT) \ passdb-passwd-file.$(OBJEXT) passdb-pam.$(OBJEXT) \ passdb-shadow.$(OBJEXT) passdb-vpopmail.$(OBJEXT) \ -passdb-pgsql.$(OBJEXT) password-scheme.$(OBJEXT) userdb.$(OBJEXT) \ +passdb-pgsql.$(OBJEXT) passdb-mysql.$(OBJEXT) password-scheme.$(OBJEXT) userdb.$(OBJEXT) \ userdb-ldap.$(OBJEXT) userdb-passwd.$(OBJEXT) \ userdb-passwd-file.$(OBJEXT) userdb-static.$(OBJEXT) \ -userdb-vpopmail.$(OBJEXT) userdb-pgsql.$(OBJEXT) +userdb-vpopmail.$(OBJEXT) userdb-pgsql.$(OBJEXT) userdb-mysql.$(OBJEXT) dovecot_auth_DEPENDENCIES = ../lib-settings/libsettings.a \ ../lib/liblib.a dovecot_auth_LDFLAGS = @@ -295,6 +295,13 @@ ../../src/lib/strfuncs.h ../../src/lib/network.h \ ../../src/lib/str.h ../../src/lib-settings/settings.h \ db-pgsql.h +db-mysql.o: db-mysql.c ../../config.h common.h ../../src/lib/lib.h \ + ../../src/lib/compat.h ../../src/lib/macros.h \ + ../../src/lib/failures.h ../../src/lib/data-stack.h \ + ../../src/lib/mempool.h ../../src/lib/imem.h \ + ../../src/lib/strfuncs.h ../../src/lib/network.h \ + ../../src/lib/str.h ../../src/lib-settings/settings.h \ + db-mysql.h login-connection.o: login-connection.c common.h ../../src/lib/lib.h \ ../../config.h ../../src/lib/compat.h ../../src/lib/macros.h \ ../../src/lib/failures.h ../../src/lib/data-stack.h \ @@ -388,6 +395,14 @@ ../../src/lib/str.h ../../src/lib/strescape.h \ ../../src/lib/var-expand.h password-scheme.h db-pgsql.h \ passdb.h mech.h auth-login-interface.h +passdb-mysql.o: passdb-mysql.c ../../config.h common.h \ + ../../src/lib/lib.h ../../src/lib/compat.h \ + ../../src/lib/macros.h ../../src/lib/failures.h \ + ../../src/lib/data-stack.h ../../src/lib/mempool.h \ + ../../src/lib/imem.h ../../src/lib/strfuncs.h \ + ../../src/lib/str.h ../../src/lib/strescape.h \ + ../../src/lib/var-expand.h password-scheme.h db-mysql.h \ + passdb.h mech.h auth-login-interface.h passdb-shadow.o: passdb-shadow.c ../../config.h common.h \ ../../src/lib/lib.h ../../src/lib/compat.h \ ../../src/lib/macros.h ../../src/lib/failures.h \ @@ -428,6 +443,13 @@ ../../src/lib/imem.h ../../src/lib/strfuncs.h \ ../../src/lib/str.h ../../src/lib/strescape.h \ ../../src/lib/var-expand.h db-pgsql.h userdb.h +userdb-mysql.o: userdb-mysql.c ../../config.h common.h \ + ../../src/lib/lib.h ../../src/lib/compat.h \ + ../../src/lib/macros.h ../../src/lib/failures.h \ + ../../src/lib/data-stack.h ../../src/lib/mempool.h \ + ../../src/lib/imem.h ../../src/lib/strfuncs.h \ + ../../src/lib/str.h ../../src/lib/strescape.h \ + ../../src/lib/var-expand.h db-mysql.h userdb.h userdb-static.o: userdb-static.c ../../config.h common.h \ ../../src/lib/lib.h ../../src/lib/compat.h \ ../../src/lib/macros.h ../../src/lib/failures.h \ diff -u -r --new-file work/dovecot-0.99.10/src/auth/db-mysql.c work.patched/dovecot-0.99.10/src/auth/db-mysql.c --- work/dovecot-0.99.10/src/auth/db-mysql.c Wed Dec 31 16:00:00 1969 +++ work.patched/dovecot-0.99.10/src/auth/db-mysql.c Tue Sep 30 12:24:00 2003 @@ -0,0 +1,181 @@ +/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */ + +#include "config.h" +#undef HAVE_CONFIG_H + +#if defined(PASSDB_MYSQL) || defined(USERDB_MYSQL) + +#include "common.h" +#include "network.h" +#include "str.h" +#include "settings.h" +#include "db-mysql.h" + +#include <limits.h> +#include <stddef.h> +#include <stdlib.h> + +#define DEF(type, name) { type, #name, offsetof(struct mysql_settings, name) } + +static struct setting_def setting_defs[] = { + DEF(SET_STR, db_host), + DEF(SET_STR, db_port), + DEF(SET_STR, db_unix_socket), + DEF(SET_STR, db), + DEF(SET_STR, db_user), + DEF(SET_STR, db_passwd), + DEF(SET_STR, db_client_flags), + DEF(SET_STR, password_query), + DEF(SET_STR, user_query), + DEF(SET_STR, default_pass_scheme) +}; + +struct mysql_settings default_mysql_settings = { + MEMBER(db_host) "localhost", + MEMBER(db_port) "0", + MEMBER(db_unix_socket) "/var/tmp/mysql.sock", + MEMBER(db) "email_accounts", + MEMBER(db_user) "dovecot", + MEMBER(db_passwd) "changeme", + MEMBER(db_client_flags) "0", + MEMBER(password_query) "SELECT password FROM users WHERE userid = '%u'", + MEMBER(user_query) "SELECT home, uid, gid FROM users WHERE userid = '%u'", + MEMBER(default_pass_scheme) "PLAIN-MD5" +}; + +static struct mysql_connection *mysql_connections = NULL; + +static int mysql_conn_open(struct mysql_connection *conn); +static void mysql_conn_close(struct mysql_connection *conn); + +void db_mysql_query(struct mysql_connection *conn, const char *query, + struct mysql_request *request) +{ + MYSQL_RES *res; + int failed; + + if (!conn->connected) { + if (!mysql_conn_open(conn)) { + request->callback(conn, request, NULL); + return; + } + } + + if (verbose_debug) + i_info("MYSQL: Performing query: %s", query); + + if (mysql_query(conn->mysql, query)) + i_info("MYSQL: Error executing query \"%s\": %s", query, + mysql_error(conn->mysql)); + + if ((res = mysql_store_result(conn->mysql))) + failed = FALSE; + else { + i_info("MYSQL: Error retrieving results: %s", + mysql_error(conn->mysql)); + failed = TRUE; + } + + request->callback(conn, request, failed ? NULL : res); + mysql_free_result(res); + i_free(request); +} + +static int mysql_conn_open(struct mysql_connection *conn) +{ + if (conn->connected) + return TRUE; + + if (conn->mysql == NULL) { + conn->mysql = mysql_init(NULL); + if (conn->mysql == NULL) { + i_error("MYSQL: mysql_init failed"); + return FALSE; + } + + if (!mysql_real_connect(conn->mysql, conn->set.db_host, + conn->set.db_user, conn->set.db_passwd, + conn->set.db, + atoi(conn->set.db_port), + conn->set.db_unix_socket, + strtoul(conn->set.db_client_flags, + NULL, 10))) { + i_error("MYSQL: Can't connect to database %s: %s", + conn->set.db, mysql_error(conn->mysql)); + return FALSE; + } + } + + conn->connected = TRUE; + return TRUE; +} + +static void mysql_conn_close(struct mysql_connection *conn) +{ + conn->connected = FALSE; + + if (conn->mysql != NULL) { + mysql_close(conn->mysql); + conn->mysql = NULL; + } +} + +static struct mysql_connection *mysql_conn_find(const char *config_path) +{ + struct mysql_connection *conn; + + for (conn = mysql_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 mysql_connection *conn = context; + + return parse_setting_from_defs(conn->pool, setting_defs, + &conn->set, key, value); +} + +struct mysql_connection *db_mysql_init(const char *config_path) +{ + struct mysql_connection *conn; + pool_t pool; + + conn = mysql_conn_find(config_path); + if (conn != NULL) { + conn->refcount++; + return conn; + } + + pool = pool_alloconly_create("mysql_connection", 1024); + conn = p_new(pool, struct mysql_connection, 1); + conn->pool = pool; + + conn->refcount = 1; + + conn->config_path = p_strdup(pool, config_path); + conn->set = default_mysql_settings; + settings_read(config_path, parse_setting, conn); + + (void)mysql_conn_open(conn); + + conn->next = mysql_connections; + mysql_connections = conn; + return conn; +} + +void db_mysql_unref(struct mysql_connection *conn) +{ + if (--conn->refcount > 0) + return; + + mysql_conn_close(conn); + pool_unref(conn->pool); +} + +#endif diff -u -r --new-file work/dovecot-0.99.10/src/auth/db-mysql.h work.patched/dovecot-0.99.10/src/auth/db-mysql.h --- work/dovecot-0.99.10/src/auth/db-mysql.h Wed Dec 31 16:00:00 1969 +++ work.patched/dovecot-0.99.10/src/auth/db-mysql.h Tue Sep 30 12:24:00 2003 @@ -0,0 +1,51 @@ +#ifndef __DB_MYSQL_H +#define __DB_MYSQL_H + +#include <mysql.h> + +struct mysql_connection; +struct mysql_request; + +typedef void mysql_query_callback_t(struct mysql_connection *conn, + struct mysql_request *request, + MYSQL_RES *res); + +struct mysql_settings { + const char *db_host; + const char *db_port; + const char *db_unix_socket; + const char *db; + const char *db_user; + const char *db_passwd; + const char *db_client_flags; + const char *password_query; + const char *user_query; + const char *default_pass_scheme; +}; + +struct mysql_connection { + struct mysql_connection *next; + + pool_t pool; + int refcount; + + char *config_path; + struct mysql_settings set; + + MYSQL *mysql; + + unsigned int connected:1; +}; + +struct mysql_request { + mysql_query_callback_t *callback; + void *context; +}; + +void db_mysql_query(struct mysql_connection *conn, const char *query, + struct mysql_request *request); + +struct mysql_connection *db_mysql_init(const char *config_path); +void db_mysql_unref(struct mysql_connection *conn); + +#endif diff -u -r --new-file work/dovecot-0.99.10/src/auth/master-connection.c work.patched/dovecot-0.99.10/src/auth/master-connection.c --- work/dovecot-0.99.10/src/auth/master-connection.c Mon May 26 08:27:13 2003 +++ work.patched/dovecot-0.99.10/src/auth/master-connection.c Tue Sep 30 12:24:00 2003 @@ -55,7 +55,7 @@ reply.virtual_user_idx = reply_add(buf, user->virtual_user); reply.mail_idx = reply_add(buf, user->mail); - p = strstr(user->home, "/./"); + p = user->home ? strstr(user->home, "/./") : NULL; if (p == NULL) { reply.home_idx = reply_add(buf, user->home); reply.chroot_idx = reply_add(buf, NULL); diff -u -r --new-file work/dovecot-0.99.10/src/auth/passdb-mysql.c work.patched/dovecot-0.99.10/src/auth/passdb-mysql.c --- work/dovecot-0.99.10/src/auth/passdb-mysql.c Wed Dec 31 16:00:00 1969 +++ work.patched/dovecot-0.99.10/src/auth/passdb-mysql.c Tue Sep 30 12:24:00 2003 @@ -0,0 +1,171 @@ +/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */ + +#include "config.h" +#undef HAVE_CONFIG_H + +#ifdef PASSDB_MYSQL + +#include "common.h" +#include "str.h" +#include "strescape.h" +#include "var-expand.h" +#include "password-scheme.h" +#include "db-mysql.h" +#include "passdb.h" + +#include <mysql.h> +#include <stdlib.h> +#include <string.h> + +struct passdb_mysql_connection { + struct mysql_connection *conn; +}; + +struct passdb_mysql_request { + struct mysql_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_mysql_connection *passdb_mysql_conn; + +static void mysql_handle_request(struct mysql_connection *conn, + struct mysql_request *request, MYSQL_RES *res) +{ + struct passdb_mysql_request *mysql_request = + (struct passdb_mysql_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 (mysql_num_rows(res) == 0) { + if (verbose) + i_info("mysql(%s): Unknown user", user); + } else if (mysql_num_rows(res) > 1) { + i_error("mysql(%s): Multiple matches for user", user); + } else if (mysql_num_fields(res) != 1) { + i_error("mysql(%s): Password query returned " + "more than one field", user); + } else { + MYSQL_ROW row; + + row = mysql_fetch_row(res); + if (row) + password = t_strdup(row[0]); + } + } + + scheme = password_get_scheme(&password); + if (scheme == NULL) { + scheme = conn->set.default_pass_scheme; + i_assert(scheme != NULL); + } + + if (mysql_request->credentials != -1) { + passdb_handle_credentials(mysql_request->credentials, + user, password, scheme, + mysql_request->callback.lookup_credentials, + auth_request); + return; + } + + /* verify plain */ + if (password == NULL) { + mysql_request->callback.verify_plain(PASSDB_RESULT_USER_UNKNOWN, + auth_request); + return; + } + + ret = password_verify(mysql_request->password, password, + scheme, user); + if (ret < 0) + i_error("mysql(%s): Unknown password scheme %s", user, scheme); + else if (ret == 0) { + if (verbose) + i_info("mysql(%s): Password mismatch", user); + } + + mysql_request->callback.verify_plain(ret > 0 ? PASSDB_RESULT_OK : + PASSDB_RESULT_PASSWORD_MISMATCH, + auth_request); +} + +static void mysql_lookup_pass(struct auth_request *auth_request, + struct mysql_request *mysql_request) +{ + struct mysql_connection *conn = passdb_mysql_conn->conn; + const char *query; + string_t *str; + + str = t_str_new(512); + var_expand(str, conn->set.password_query, + str_escape(auth_request->user), NULL); + query = str_c(str); + + mysql_request->callback = mysql_handle_request; + mysql_request->context = auth_request; + + db_mysql_query(conn, query, mysql_request); +} + +static void +mysql_verify_plain(struct auth_request *request, const char *password, + verify_plain_callback_t *callback) +{ + struct passdb_mysql_request *mysql_request; + + mysql_request = i_malloc(sizeof(struct passdb_mysql_request) + + strlen(password)); + mysql_request->credentials = -1; + mysql_request->callback.verify_plain = callback; + strcpy(mysql_request->password, password); + + mysql_lookup_pass(request, &mysql_request->request); +} + +static void mysql_lookup_credentials(struct auth_request *request, + enum passdb_credentials credentials, + lookup_credentials_callback_t *callback) +{ + struct passdb_mysql_request *mysql_request; + + mysql_request = i_new(struct passdb_mysql_request, 1); + mysql_request->credentials = credentials; + mysql_request->callback.lookup_credentials = callback; + + mysql_lookup_pass(request, &mysql_request->request); +} + +static void passdb_mysql_init(const char *args) +{ + struct mysql_connection *conn; + + passdb_mysql_conn = i_new(struct passdb_mysql_connection, 1); + passdb_mysql_conn->conn = conn = db_mysql_init(args); +} + +static void passdb_mysql_deinit(void) +{ + db_mysql_unref(passdb_mysql_conn->conn); + i_free(passdb_mysql_conn); +} + +struct passdb_module passdb_mysql = { + passdb_mysql_init, + passdb_mysql_deinit, + + mysql_verify_plain, + mysql_lookup_credentials +}; + +#endif diff -u -r --new-file work/dovecot-0.99.10/src/auth/passdb.c work.patched/dovecot-0.99.10/src/auth/passdb.c --- work/dovecot-0.99.10/src/auth/passdb.c Sun May 18 05:26:28 2003 +++ work.patched/dovecot-0.99.10/src/auth/passdb.c Tue Sep 30 12:24:00 2003 @@ -110,6 +110,10 @@ if (strcasecmp(name, "pgsql") == 0) passdb = &passdb_pgsql; #endif +#ifdef PASSDB_MYSQL + if (strcasecmp(name, "mysql") == 0) + passdb = &passdb_mysql; +#endif #ifdef HAVE_MODULES passdb_module = passdb != NULL ? NULL : auth_module_open(name); if (passdb_module != NULL) { diff -u -r --new-file work/dovecot-0.99.10/src/auth/passdb.h work.patched/dovecot-0.99.10/src/auth/passdb.h --- work/dovecot-0.99.10/src/auth/passdb.h Thu Mar 20 07:46:33 2003 +++ work.patched/dovecot-0.99.10/src/auth/passdb.h Tue Sep 30 12:24:00 2003 @@ -58,6 +58,7 @@ extern struct passdb_module passdb_vpopmail; extern struct passdb_module passdb_ldap; extern struct passdb_module passdb_pgsql; +extern struct passdb_module passdb_mysql; void passdb_init(void); void passdb_deinit(void); diff -u -r --new-file work/dovecot-0.99.10/src/auth/userdb-mysql.c work.patched/dovecot-0.99.10/src/auth/userdb-mysql.c --- work/dovecot-0.99.10/src/auth/userdb-mysql.c Wed Dec 31 16:00:00 1969 +++ work.patched/dovecot-0.99.10/src/auth/userdb-mysql.c Tue Sep 30 12:24:00 2003 @@ -0,0 +1,160 @@ +/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */ + +#include "config.h" +#undef HAVE_CONFIG_H + +#ifdef USERDB_MYSQL + +#include "common.h" +#include "str.h" +#include "strescape.h" +#include "var-expand.h" +#include "db-mysql.h" +#include "userdb.h" + +#include <mysql.h> +#include <stdlib.h> +#include <string.h> + +struct userdb_mysql_connection { + struct mysql_connection *conn; +}; + +struct userdb_mysql_request { + struct mysql_request request; + userdb_callback_t *userdb_callback; + + char username[1]; /* variable width */ +}; + +static struct userdb_mysql_connection *userdb_mysql_conn; + +static int is_result_valid(MYSQL_RES *res) +{ + int i, n_fields, found; + MYSQL_FIELD *fields; + + if (res == NULL) { + i_error("MYSQL: Query failed"); + return FALSE; + } + + if (mysql_num_rows(res) == 0) { + if (verbose) + i_error("MYSQL: Authenticated user not found"); + return FALSE; + } + + n_fields = mysql_num_fields(res); + fields = mysql_fetch_fields(res); + + /* Make sure the 'uid' field exists. */ + for (found = 0, i = 0; i < n_fields; i++) + if (strcmp("uid", fields[i].name) == 0) { + found = 1; + break; + } + + if (!found) { + i_error("MYSQL: User query did not return 'uid' field"); + return FALSE; + } + + /* Make sure the 'gid' field exists. */ + for (found = 0, i = 0; i < n_fields; i++) + if (strcmp("gid", fields[i].name) == 0) { + found = 1; + break; + } + + if (!found) { + i_error("MYSQL: User query did not return 'gid' field"); + return FALSE; + } + + return TRUE; +} + +static const char *my_get_str(MYSQL_RES *res, MYSQL_ROW row, const char *field) +{ + int i, n_fields; + unsigned long *lengths; + MYSQL_FIELD *fields; + + n_fields = mysql_num_fields(res); + lengths = mysql_fetch_lengths(res); + fields = mysql_fetch_fields(res); + for (i = 0; i < n_fields; i++) + if (strcmp(field, fields[i].name) == 0) + return (const char *) lengths[i] == 0 ? + NULL : t_strdup(row[i]); + + return NULL; +} + +static void mysql_handle_request(struct mysql_connection *conn __attr_unused__, + struct mysql_request *request, MYSQL_RES *res) +{ + struct userdb_mysql_request *urequest = + (struct userdb_mysql_request *) request; + struct user_data user; + MYSQL_ROW row; + + if (res != NULL && is_result_valid(res) && + (row = mysql_fetch_row(res))) { + memset(&user, 0, sizeof(user)); + user.virtual_user = urequest->username; + user.system_user = my_get_str(res, row, "system_user"); + user.home = my_get_str(res, row, "home"); + user.mail = my_get_str(res, row, "mail"); + user.uid = atoi(my_get_str(res, row, "uid")); /* XXX leak */ + user.gid = atoi(my_get_str(res, row, "gid")); /* XXX leak */ + urequest->userdb_callback(&user, request->context); + } else { + urequest->userdb_callback(NULL, request->context); + } +} + +static void userdb_mysql_lookup(const char *user, userdb_callback_t *callback, + void *context) +{ + struct mysql_connection *conn = userdb_mysql_conn->conn; + struct userdb_mysql_request *request; + const char *query; + string_t *str; + + str = t_str_new(512); + var_expand(str, conn->set.user_query, str_escape(user), NULL); + query = str_c(str); + + request = i_malloc(sizeof(struct userdb_mysql_request) + strlen(user)); + request->request.callback = mysql_handle_request; + request->request.context = context; + request->userdb_callback = callback; + strcpy(request->username, user); + + db_mysql_query(conn, query, &request->request); +} + +static void userdb_mysql_init(const char *args) +{ + struct mysql_connection *conn; + + userdb_mysql_conn = i_new(struct userdb_mysql_connection, 1); + userdb_mysql_conn->conn = conn = db_mysql_init(args); +} + +static void userdb_mysql_deinit(void) +{ + db_mysql_unref(userdb_mysql_conn->conn); + i_free(userdb_mysql_conn); +} + +struct userdb_module userdb_mysql = { + userdb_mysql_init, + userdb_mysql_deinit, + + userdb_mysql_lookup +}; + +#endif diff -u -r --new-file work/dovecot-0.99.10/src/auth/userdb.c work.patched/dovecot-0.99.10/src/auth/userdb.c --- work/dovecot-0.99.10/src/auth/userdb.c Sun May 18 05:26:28 2003 +++ work.patched/dovecot-0.99.10/src/auth/userdb.c Tue Sep 30 12:24:00 2003 @@ -49,6 +49,10 @@ if (strcasecmp(name, "pgsql") == 0) userdb = &userdb_pgsql; #endif +#ifdef USERDB_MYSQL + if (strcasecmp(name, "mysql") == 0) + userdb = &userdb_mysql; +#endif #ifdef HAVE_MODULES userdb_module = userdb != NULL ? NULL : auth_module_open(name); if (userdb_module != NULL) { diff -u -r --new-file work/dovecot-0.99.10/src/auth/userdb.h work.patched/dovecot-0.99.10/src/auth/userdb.h --- work/dovecot-0.99.10/src/auth/userdb.h Sun May 18 05:26:28 2003 +++ work.patched/dovecot-0.99.10/src/auth/userdb.h Tue Sep 30 12:24:00 2003 @@ -29,6 +29,7 @@ extern struct userdb_module userdb_vpopmail; extern struct userdb_module userdb_ldap; extern struct userdb_module userdb_pgsql; +extern struct userdb_module userdb_mysql; void userdb_init(void); void userdb_deinit(void);