]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: Implemented support for Postfix's "TCP map" sockets for user existence lookups.
authorTimo Sirainen <tss@iki.fi>
Mon, 9 Jan 2012 14:35:22 +0000 (16:35 +0200)
committerTimo Sirainen <tss@iki.fi>
Mon, 9 Jan 2012 14:35:22 +0000 (16:35 +0200)
src/auth/Makefile.am
src/auth/auth-postfix-connection.c [new file with mode: 0644]
src/auth/auth-postfix-connection.h [new file with mode: 0644]
src/auth/main.c

index 1d56427c00fb849b66cc622b3629e0d0557ef781..8e399e841decee73078b002a028f2d50cea6e59a 100644 (file)
@@ -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 (file)
index 0000000..86bb8ab
--- /dev/null
@@ -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 <unistd.h>
+
+#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 (file)
index 0000000..df9d862
--- /dev/null
@@ -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
+
index 47338705f3e23b046f210ef9429b7088dd8e7cd4..05da762188434af9a0aaec9f4349a376ef6524f6 100644 (file)
@@ -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 <unistd.h>
 #include <sys/stat.h>
@@ -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;