From: Timo Sirainen Date: Mon, 9 Jan 2012 14:35:22 +0000 (+0200) Subject: auth: Implemented support for Postfix's "TCP map" sockets for user existence lookups. X-Git-Tag: 2.1.rc4~77 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=27abb44a48d7a4eeb30c3efc1e0619f7fa06bf00;p=thirdparty%2Fdovecot%2Fcore.git auth: Implemented support for Postfix's "TCP map" sockets for user existence lookups. --- diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am index 1d56427c00..8e399e841d 100644 --- a/src/auth/Makefile.am +++ b/src/auth/Makefile.am @@ -60,6 +60,7 @@ auth_SOURCES = \ auth-cache.c \ auth-client-connection.c \ auth-master-connection.c \ + auth-postfix-connection.c \ mech-otp-skey-common.c \ mech-plain-common.c \ auth-penalty.c \ @@ -121,6 +122,7 @@ headers = \ auth-client-connection.h \ auth-common.h \ auth-master-connection.h \ + auth-postfix-connection.h \ mech-otp-skey-common.h \ mech-plain-common.h \ auth-penalty.h \ diff --git a/src/auth/auth-postfix-connection.c b/src/auth/auth-postfix-connection.c new file mode 100644 index 0000000000..86bb8aba21 --- /dev/null +++ b/src/auth/auth-postfix-connection.c @@ -0,0 +1,248 @@ +/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */ + +#include "auth-common.h" +#include "ioloop.h" +#include "network.h" +#include "istream.h" +#include "ostream.h" +#include "llist.h" +#include "str.h" +#include "strescape.h" +#include "str-sanitize.h" +#include "master-service.h" +#include "userdb.h" +#include "auth-postfix-connection.h" + +#include + +#define MAX_INBUF_SIZE 1024 +#define MAX_OUTBUF_SIZE (1024*50) + +struct auth_postfix_connection { + struct auth_postfix_connection *prev, *next; + struct auth *auth; + int refcount; + + int fd; + char *path; + struct istream *input; + struct ostream *output; + struct io *io; + + unsigned int destroyed:1; +}; + +static void postfix_input(struct auth_postfix_connection *conn); +static void auth_postfix_connection_ref(struct auth_postfix_connection *conn); +static void auth_postfix_connection_destroy(struct auth_postfix_connection **_conn); +static void auth_postfix_connection_unref(struct auth_postfix_connection **_conn); + +static struct auth_postfix_connection *auth_postfix_connections; + +static int +postfix_input_auth_request(struct auth_postfix_connection *conn, + const char *username, + struct auth_request **request_r, const char **error_r) +{ + struct auth_request *auth_request; + + auth_request = auth_request_new_dummy(); + auth_request->id = 1; + auth_request->context = conn; + auth_postfix_connection_ref(conn); + + if (!auth_request_set_username(auth_request, username, error_r)) { + *request_r = auth_request; + return FALSE; + } + (void)auth_request_import_info(auth_request, "service", "postfix"); + + auth_request_init(auth_request); + *request_r = auth_request; + return TRUE; +} + +static void +user_callback(enum userdb_result result, struct auth_request *auth_request) +{ + struct auth_postfix_connection *conn = auth_request->context; + struct auth_stream_reply *reply = auth_request->userdb_reply; + string_t *str; + const char *value; + + if (auth_request->userdb_lookup_failed) + result = USERDB_RESULT_INTERNAL_FAILURE; + + str = t_str_new(128); + switch (result) { + case USERDB_RESULT_INTERNAL_FAILURE: + if (auth_request->userdb_lookup_failed) + value = auth_stream_reply_find(reply, "reason"); + else + value = NULL; + str_printfa(str, "400 %s", + value != NULL ? value: "Internal failure"); + break; + case USERDB_RESULT_USER_UNKNOWN: + str_append(str, "500 User not found"); + break; + case USERDB_RESULT_OK: + str_append(str, "200 1"); + break; + } + + if (conn->auth->set->debug) + i_debug("postfix out: %s", str_c(str)); + + str_append_c(str, '\n'); + (void)o_stream_send(conn->output, str_data(str), str_len(str)); + + i_assert(conn->io == NULL); + if (!conn->destroyed) + conn->io = io_add(conn->fd, IO_READ, postfix_input, conn); + + auth_request_unref(&auth_request); + auth_postfix_connection_unref(&conn); +} + +static bool +postfix_input_user(struct auth_postfix_connection *conn, const char *username) +{ + struct auth_request *auth_request; + const char *error; + + io_remove(&conn->io); + if (!postfix_input_auth_request(conn, username, + &auth_request, &error)) { + auth_request_log_info(auth_request, "postfix", "%s", error); + user_callback(USERDB_RESULT_USER_UNKNOWN, auth_request); + } else { + auth_request_set_state(auth_request, AUTH_REQUEST_STATE_USERDB); + auth_request_lookup_user(auth_request, user_callback); + } + return TRUE; +} + +static bool +auth_postfix_input_line(struct auth_postfix_connection *conn, const char *line) +{ + if (conn->auth->set->debug) + i_debug("postfix in: %s", line); + + if (strncasecmp(line, "get ", 4) == 0) + return postfix_input_user(conn, line + 4); + + i_error("BUG: Unknown command in postfix socket: %s", + str_sanitize(line, 80)); + return FALSE; +} + +static void postfix_input(struct auth_postfix_connection *conn) +{ + char *line; + bool ret; + + switch (i_stream_read(conn->input)) { + case 0: + return; + case -1: + /* disconnected */ + auth_postfix_connection_destroy(&conn); + return; + case -2: + /* buffer full */ + i_error("BUG: Postfix sent us more than %d bytes", + (int)MAX_INBUF_SIZE); + auth_postfix_connection_destroy(&conn); + return; + } + + while ((line = i_stream_next_line(conn->input)) != NULL) { + T_BEGIN { + ret = auth_postfix_input_line(conn, line); + } T_END; + if (!ret) { + auth_postfix_connection_destroy(&conn); + return; + } + } +} + +struct auth_postfix_connection * +auth_postfix_connection_create(struct auth *auth, int fd) +{ + struct auth_postfix_connection *conn; + + conn = i_new(struct auth_postfix_connection, 1); + conn->refcount = 1; + conn->fd = fd; + conn->auth = auth; + conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); + conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); + conn->io = io_add(fd, IO_READ, postfix_input, conn); + DLLIST_PREPEND(&auth_postfix_connections, conn); + return conn; +} + +static void +auth_postfix_connection_destroy(struct auth_postfix_connection **_conn) +{ + struct auth_postfix_connection *conn = *_conn; + + *_conn = NULL; + if (conn->destroyed) + return; + conn->destroyed = TRUE; + + DLLIST_REMOVE(&auth_postfix_connections, conn); + + if (conn->input != NULL) + i_stream_close(conn->input); + if (conn->output != NULL) + o_stream_close(conn->output); + if (conn->io != NULL) + io_remove(&conn->io); + if (conn->fd != -1) { + if (close(conn->fd) < 0) + i_error("close(%s): %m", conn->path); + conn->fd = -1; + } + + master_service_client_connection_destroyed(master_service); + auth_postfix_connection_unref(&conn); +} + +static void auth_postfix_connection_ref(struct auth_postfix_connection *conn) +{ + i_assert(conn->refcount > 0); + + conn->refcount++; +} + +static void +auth_postfix_connection_unref(struct auth_postfix_connection **_conn) +{ + struct auth_postfix_connection *conn = *_conn; + + *_conn = NULL; + i_assert(conn->refcount > 0); + + if (--conn->refcount > 0) + return; + + if (conn->input != NULL) + i_stream_unref(&conn->input); + if (conn->output != NULL) + o_stream_unref(&conn->output); + i_free(conn); +} + +void auth_postfix_connections_destroy_all(void) +{ + struct auth_postfix_connection *conn; + + while (auth_postfix_connections != NULL) { + conn = auth_postfix_connections; + auth_postfix_connection_destroy(&conn); + } +} diff --git a/src/auth/auth-postfix-connection.h b/src/auth/auth-postfix-connection.h new file mode 100644 index 0000000000..df9d862185 --- /dev/null +++ b/src/auth/auth-postfix-connection.h @@ -0,0 +1,10 @@ +#ifndef AUTH_POSTFIX_CONNECTION_H +#define AUTH_POSTFIX_CONNECTION_H + +struct auth_postfix_connection * +auth_postfix_connection_create(struct auth *auth, int fd); + +void auth_postfix_connections_destroy_all(void); + +#endif + diff --git a/src/auth/main.c b/src/auth/main.c index 47338705f3..05da762188 100644 --- a/src/auth/main.c +++ b/src/auth/main.c @@ -26,6 +26,7 @@ #include "auth-worker-client.h" #include "auth-master-connection.h" #include "auth-client-connection.h" +#include "auth-postfix-connection.h" #include #include @@ -37,7 +38,8 @@ enum auth_socket_type { AUTH_SOCKET_CLIENT, AUTH_SOCKET_LOGIN_CLIENT, AUTH_SOCKET_MASTER, - AUTH_SOCKET_USERDB + AUTH_SOCKET_USERDB, + AUTH_SOCKET_POSTFIX }; struct auth_socket_listener { @@ -118,6 +120,8 @@ auth_socket_type_get(const char *path) return AUTH_SOCKET_MASTER; else if (strcmp(suffix, "userdb") == 0) return AUTH_SOCKET_USERDB; + else if (strcmp(suffix, "postmap") == 0) + return AUTH_SOCKET_POSTFIX; else return AUTH_SOCKET_CLIENT; } @@ -230,8 +234,6 @@ static void main_init(void) auth_worker_server_init(); auths_init(); auth_request_handler_init(); - auth_master_connections_init(); - auth_client_connections_init(); if (worker) { /* workers have only a single connection from the master @@ -263,6 +265,7 @@ static void main_deinit(void) auth_client_connections_destroy_all(); auth_master_connections_destroy_all(); + auth_postfix_connections_destroy_all(); if (auth_worker_client != NULL) auth_worker_client_destroy(&auth_worker_client); @@ -321,6 +324,9 @@ static void client_connected(struct master_service_connection *conn) (void)auth_master_connection_create(auth, conn->fd, l->path, &l->st, TRUE); break; + case AUTH_SOCKET_POSTFIX: + (void)auth_postfix_connection_create(auth, conn->fd); + break; case AUTH_SOCKET_LOGIN_CLIENT: (void)auth_client_connection_create(auth, conn->fd, TRUE); break;