passdb-sql.c revision 4042b17026279498eb37922ee3316a50144c51a4
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (C) 2004 Timo Sirainen, Alex Howansky */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "config.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#undef HAVE_CONFIG_H
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#ifdef PASSDB_SQL
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "common.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "str.h"
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen#include "strescape.h"
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen#include "var-expand.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "password-scheme.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "db-sql.h"
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen#include "passdb.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen#include <stdlib.h>
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen#include <string.h>
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstruct passdb_sql_request {
a425baf62f30f4b62250227ff97b4b01c4b01affTimo Sirainen struct auth_request *auth_request;
42dbeeb3462895b03e7633dbc59e8e191199734bTimo Sirainen enum passdb_credentials credentials;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen union {
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen verify_plain_callback_t *verify_plain;
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen lookup_credentials_callback_t *lookup_credentials;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen } callback;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen char password[1];
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen};
a425baf62f30f4b62250227ff97b4b01c4b01affTimo Sirainen
1a266561b099269bef75eee1a3742e61130ef780Timo Sirainenstatic struct sql_connection *passdb_sql_conn;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstatic void result_save_extra_fields(struct sql_result *result,
14175321ddb88619015866978c05a27786ca4814Timo Sirainen struct passdb_sql_request *sql_request,
14175321ddb88619015866978c05a27786ca4814Timo Sirainen struct auth_request *auth_request)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen{
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen unsigned int i, fields_count;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen const char *name, *value;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen string_t *str;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen fields_count = sql_result_get_fields_count(result);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (fields_count == 1)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen str = NULL;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen for (i = 0; i < fields_count; i++) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen name = sql_result_get_field_name(result, i);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen value = sql_result_get_field_value(result, i);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (value == NULL)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen continue;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (strcmp(name, "password") == 0)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen continue;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (strcmp(name, "nodelay") == 0) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* don't delay replying to client of the failure */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen auth_request->no_failure_delay = TRUE;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen continue;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (str == NULL)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen str = str_new(auth_request->pool, 64);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (strcmp(name, "nologin") == 0) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* user can't actually login - don't keep this
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen reply for master */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen auth_request->no_login = TRUE;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (str_len(str) > 0)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen str_append_c(str, '\t');
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen str_append(str, name);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen } else if (strcmp(name, "proxy") == 0) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* we're proxying authentication for this user. send
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen password back if using plaintext authentication. */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen auth_request->proxy = TRUE;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (str_len(str) > 0)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen str_append_c(str, '\t');
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen str_append(str, name);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (*sql_request->password != '\0') {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen str_printfa(str, "\tpass=%s",
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen sql_request->password);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen } else {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (str_len(str) > 0)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen str_append_c(str, '\t');
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen str_printfa(str, "%s=%s", name, value);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (str != NULL)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen auth_request->extra_fields = str_c(str);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen}
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenstatic void sql_query_callback(struct sql_result *result, void *context)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen{
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen struct passdb_sql_request *sql_request = context;
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen struct auth_request *auth_request = sql_request->auth_request;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen const char *user, *password, *scheme;
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen int ret, idx;
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen user = auth_request->user;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen password = NULL;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen ret = sql_result_next_row(result);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (ret < 0) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_error("sql(%s): Password query failed: %s",
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen get_log_prefix(auth_request),
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen sql_result_get_error(result));
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen } else if (ret == 0) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (verbose) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen i_info("sql(%s): Unknown user",
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen get_log_prefix(auth_request));
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen } else if ((idx = sql_result_find_field(result, "password")) < 0) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen i_error("sql(%s): Password query didn't return password",
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen get_log_prefix(auth_request));
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen } else {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen password = t_strdup(sql_result_get_field_value(result, idx));
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen result_save_extra_fields(result, sql_request, auth_request);
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (ret > 0) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* make sure there was only one row returned */
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (sql_result_next_row(result) > 0) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen i_error("sql(%s): Password query returned multiple "
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen "matches", get_log_prefix(auth_request));
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen password = NULL;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen scheme = password_get_scheme(&password);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (scheme == NULL) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen scheme = passdb_sql_conn->set.default_pass_scheme;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen i_assert(scheme != NULL);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (sql_request->credentials != -1) {
3d2d1ba0a14521c7320370d8cafb9a95b49d333dTimo Sirainen passdb_handle_credentials(sql_request->credentials,
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen user, password, scheme,
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen sql_request->callback.lookup_credentials,
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen auth_request);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen return;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen /* verify plain */
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (password == NULL) {
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen sql_request->callback.verify_plain(PASSDB_RESULT_USER_UNKNOWN,
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen auth_request);
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen return;
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen }
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen ret = password_verify(sql_request->password, password, scheme, user);
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen if (ret < 0) {
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen i_error("sql(%s): Unknown password scheme %s",
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen get_log_prefix(auth_request), scheme);
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen } else if (ret == 0) {
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen if (verbose) {
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen i_info("sql(%s): Password mismatch",
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen get_log_prefix(auth_request));
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen sql_request->callback.verify_plain(ret > 0 ? PASSDB_RESULT_OK :
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen PASSDB_RESULT_PASSWORD_MISMATCH,
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen auth_request);
553667c748977991590854426255e1c34a615f24Timo Sirainen}
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainenstatic void sql_lookup_pass(struct passdb_sql_request *sql_request)
553667c748977991590854426255e1c34a615f24Timo Sirainen{
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen string_t *query;
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen query = t_str_new(512);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen var_expand(query, passdb_sql_conn->set.password_query,
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen auth_request_get_var_expand_table(sql_request->auth_request,
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen str_escape));
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen sql_query(passdb_sql_conn->db, str_c(query),
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen sql_query_callback, sql_request);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen}
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstatic void sql_verify_plain(struct auth_request *request, const char *password,
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen verify_plain_callback_t *callback)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen struct passdb_sql_request *sql_request;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen sql_request = i_malloc(sizeof(struct passdb_sql_request) +
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen strlen(password));
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen sql_request->auth_request = request;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen sql_request->credentials = -1;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen sql_request->callback.verify_plain = callback;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen strcpy(sql_request->password, password);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen sql_lookup_pass(sql_request);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen}
bf3fb941cc1deb06786449b89c77d9a56a07c251Timo Sirainen
9511a40d933181045343110c8101b75887062aaeTimo Sirainenstatic void sql_lookup_credentials(struct auth_request *request,
9511a40d933181045343110c8101b75887062aaeTimo Sirainen enum passdb_credentials credentials,
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen lookup_credentials_callback_t *callback)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen struct passdb_sql_request *sql_request;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen sql_request = i_new(struct passdb_sql_request, 1);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen sql_request->auth_request = request;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen sql_request->credentials = credentials;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen sql_request->callback.lookup_credentials = callback;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen sql_lookup_pass(sql_request);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen}
static void passdb_sql_preinit(const char *args)
{
passdb_sql_conn = db_sql_init(args);
}
static void passdb_sql_init(const char *args __attr_unused__)
{
db_sql_connect(passdb_sql_conn);
}
static void passdb_sql_deinit(void)
{
db_sql_unref(passdb_sql_conn);
}
struct passdb_module passdb_sql = {
passdb_sql_preinit,
passdb_sql_init,
passdb_sql_deinit,
sql_verify_plain,
sql_lookup_credentials
};
#endif