dovecot-2.0: imap, pop3: Added back ability to run post-login sc...

dovecot at dovecot.org dovecot at dovecot.org
Sat Dec 12 01:38:38 EET 2009


details:   http://hg.dovecot.org/dovecot-2.0/rev/e9cbc461c525
changeset: 10459:e9cbc461c525
user:      Timo Sirainen <tss at iki.fi>
date:      Fri Dec 11 18:38:33 2009 -0500
description:
imap, pop3: Added back ability to run post-login scripts.

diffstat:

6 files changed, 351 insertions(+), 19 deletions(-)
src/imap/main.c               |    5 +
src/lib-master/master-login.c |  196 +++++++++++++++++++++++++++++++++++++----
src/lib-master/master-login.h |    1 
src/pop3/main.c               |    5 +
src/util/Makefile.am          |    7 +
src/util/script.c             |  156 ++++++++++++++++++++++++++++++++

diffs (truncated from 543 to 300 lines):

diff -r 395b53cebaac -r e9cbc461c525 src/imap/main.c
--- a/src/imap/main.c	Fri Dec 11 18:36:19 2009 -0500
+++ b/src/imap/main.c	Fri Dec 11 18:38:33 2009 -0500
@@ -4,6 +4,7 @@
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
+#include "abspath.h"
 #include "str.h"
 #include "base64.h"
 #include "process-title.h"
@@ -252,6 +253,7 @@ int main(int argc, char *argv[])
 	};
 	enum master_service_flags service_flags = 0;
 	enum mail_storage_service_flags storage_service_flags = 0;
+	const char *postlogin_socket_path;
 
 	if (IS_STANDALONE() && getuid() == 0 &&
 	    net_getpeername(1, NULL, NULL) == 0) {
@@ -273,6 +275,8 @@ int main(int argc, char *argv[])
 					     &argc, &argv, NULL);
 	if (master_getopt(master_service) > 0)
 		return FATAL_DEFAULT;
+	postlogin_socket_path = t_abspath(argv[1]);
+
 	master_service_init_finish(master_service);
 	master_service_set_die_callback(master_service, imap_die);
 
@@ -294,6 +298,7 @@ int main(int argc, char *argv[])
 		} T_END;
 	} else {
 		master_login = master_login_init(master_service, "auth-master",
+						 postlogin_socket_path,
 						 login_client_connected);
 		io_loop_set_running(current_ioloop);
 	}
diff -r 395b53cebaac -r e9cbc461c525 src/lib-master/master-login.c
--- a/src/lib-master/master-login.c	Fri Dec 11 18:36:19 2009 -0500
+++ b/src/lib-master/master-login.c	Fri Dec 11 18:38:33 2009 -0500
@@ -6,12 +6,16 @@
 #include "fdpass.h"
 #include "fd-close-on-exec.h"
 #include "llist.h"
+#include "str.h"
+#include "strescape.h"
 #include "master-service-private.h"
 #include "master-login.h"
 #include "master-login-auth.h"
 
 #include <sys/stat.h>
 #include <unistd.h>
+
+#define MASTER_LOGIN_POSTLOGIN_TIMEOUT_MSECS (60*1000)
 
 struct master_login_connection {
 	struct master_login_connection *prev, *next;
@@ -22,17 +26,28 @@ struct master_login_connection {
 	struct ostream *output;
 };
 
+struct master_login_postlogin {
+	struct master_login_client *client;
+
+	int fd;
+	struct io *io;
+	struct timeout *to;
+	string_t *input;
+};
+
 struct master_login {
 	struct master_service *service;
 	master_login_callback_t *callback;
 	struct master_login_connection *conns;
 	struct master_login_auth *auth;
+	char *postlogin_socket_path;
 };
 
 static void master_login_conn_deinit(struct master_login_connection **_conn);
 
 struct master_login *
 master_login_init(struct master_service *service, const char *auth_socket_path,
+		  const char *postlogin_socket_path,
 		  master_login_callback_t *callback)
 {
 	struct master_login *login;
@@ -41,6 +56,7 @@ master_login_init(struct master_service 
 	login->service = service;
 	login->callback = callback;
 	login->auth = master_login_auth_init(auth_socket_path);
+	login->postlogin_socket_path = i_strdup(postlogin_socket_path);
 	service->login_connections = TRUE;
 	return login;
 }
@@ -57,6 +73,7 @@ void master_login_deinit(struct master_l
 
 		master_login_conn_deinit(&conn);
 	}
+	i_free(login->postlogin_socket_path);
 	i_free(login);
 }
 
@@ -126,44 +143,185 @@ master_login_conn_read_request(struct ma
 	return 1;
 }
 
-static void
-master_login_auth_callback(const char *const *auth_args, void *context)
-{
-	struct master_login_client *client = context;
-	struct master_auth_reply reply;
+static void master_login_auth_finish(struct master_login_client *client,
+				     const char *const *auth_args)
+{
 	struct master_login_connection *conn = client->conn;
 	struct master_service *service = conn->login->service;
 	bool close_config;
+
+	close_config = service->master_status.available_count == 0 &&
+		service->service_count_left == 1;
+
+	conn->login->callback(client, auth_args[0], auth_args+1);
+	i_free(client);
+
+	if (close_config) {
+		/* we're dying as soon as this connection closes. */
+		master_service_close_config_fd(service);
+		master_login_conn_deinit(&conn);
+	}
+}
+
+static void master_login_client_free(struct master_login_client *client)
+{
+	if (close(client->fd) < 0)
+		i_error("close(fd_read client) failed: %m");
+	i_free(client);
+}
+
+static void master_login_postlogin_free(struct master_login_postlogin *pl)
+{
+	timeout_remove(&pl->to);
+	io_remove(&pl->io);
+	if (close(pl->fd) < 0)
+		i_error("close(postlogin) failed: %m");
+	str_free(&pl->input);
+	i_free(pl);
+}
+
+static void master_login_postlogin_input(struct master_login_postlogin *pl)
+{
+	struct master_login *login = pl->client->conn->login;
+	char buf[1024];
+	const char **auth_args, **p;
+	unsigned int len;
+	ssize_t ret;
+	int fd = -1;
+
+	while ((ret = fd_read(pl->fd, buf, sizeof(buf), &fd)) > 0) {
+		if (fd != -1) {
+			/* post-login script replaced fd */
+			if (close(pl->client->fd) < 0)
+				i_error("close(client) failed: %m");
+			pl->client->fd = fd;
+		}
+		str_append_n(pl->input, buf, ret);
+	}
+
+	len = str_len(pl->input);
+	if (len > 0 && str_c(pl->input)[len-1] == '\n') {
+		/* finished reading the input */
+		str_truncate(pl->input, len-1);
+	} else {
+		if (ret < 0) {
+			if (errno == EAGAIN)
+				return;
+
+			i_error("fd_read(%s) failed: %m",
+				login->postlogin_socket_path);
+		} else {
+			i_error("fd_read(%s) failed: disconnected",
+				login->postlogin_socket_path);
+		}
+		master_login_client_free(pl->client);
+		master_login_postlogin_free(pl);
+		master_service_client_connection_destroyed(login->service);
+		return;
+	}
+
+	auth_args = t_strsplit(str_c(pl->input), "\t");
+	for (p = auth_args; *p != NULL; p++)
+		*p = str_tabunescape(t_strdup_noconst(*p));
+
+	master_login_auth_finish(pl->client, auth_args);
+	master_login_postlogin_free(pl);
+}
+
+static void master_login_postlogin_timeout(struct master_login_postlogin *pl)
+{
+	struct master_login *login = pl->client->conn->login;
+
+	i_error("%s: Timeout waiting for post-login script to finish, aborting",
+		login->postlogin_socket_path);
+
+	master_login_client_free(pl->client);
+	master_login_postlogin_free(pl);
+	master_service_client_connection_destroyed(login->service);
+}
+
+static int master_login_postlogin(struct master_login_client *client,
+				  const char *const *auth_args)
+{
+	struct master_login *login = client->conn->login;
+	struct master_login_postlogin *pl;
+	string_t *str;
+	unsigned int i;
+	int fd;
+	ssize_t ret;
+
+	fd = net_connect_unix_with_retries(login->postlogin_socket_path, 1000);
+	if (fd == -1) {
+		i_error("net_connect_unix(%s) failed: %m",
+			login->postlogin_socket_path);
+		return -1;
+	}
+
+	str = t_str_new(256);
+	str_printfa(str, "%s\t%s", net_ip2addr(&client->auth_req.local_ip),
+		    net_ip2addr(&client->auth_req.remote_ip));
+	for (i = 0; auth_args[i] != NULL; i++) {
+		str_append_c(str, '\t');
+		str_tabescape_write(str, auth_args[i]);
+	}
+	str_append_c(str, '\n');
+	ret = fd_send(fd, client->fd, str_data(str), str_len(str));
+	if (ret != (ssize_t)str_len(str)) {
+		if (ret < 0) {
+			i_error("write(%s) failed: %m",
+				login->postlogin_socket_path);
+		} else {
+			i_error("write(%s) failed: partial write",
+				login->postlogin_socket_path);
+		}
+		(void)close(fd);
+		return -1;
+	}
+	net_set_nonblock(fd, TRUE);
+
+	pl = i_new(struct master_login_postlogin, 1);
+	pl->client = client;
+	pl->fd = fd;
+	pl->io = io_add(fd, IO_READ, master_login_postlogin_input, pl);
+	pl->to = timeout_add(MASTER_LOGIN_POSTLOGIN_TIMEOUT_MSECS,
+			     master_login_postlogin_timeout, pl);
+	pl->input = str_new(default_pool, 512);
+	return 0;
+}
+
+static void
+master_login_auth_callback(const char *const *auth_args, void *context)
+{
+	struct master_login_client *client = context;
+	struct master_auth_reply reply;
+	struct master_service *service = client->conn->login->service;
 
 	memset(&reply, 0, sizeof(reply));
 	reply.tag = client->auth_req.tag;
 	reply.status = auth_args != NULL ? MASTER_AUTH_STATUS_OK :
 		MASTER_AUTH_STATUS_INTERNAL_ERROR;
 	reply.mail_pid = getpid();
-	o_stream_send(conn->output, &reply, sizeof(reply));
+	o_stream_send(client->conn->output, &reply, sizeof(reply));
 
 	if (auth_args == NULL || auth_args[0] == NULL) {
 		if (auth_args != NULL)
 			i_error("login client: Username missing from auth reply");
-		if (close(client->fd) < 0)
-			i_error("close(fd_read client) failed: %m");
-		i_free(client);
+		master_login_client_free(client);
 		return;
 	}
 
 	i_assert(service->master_status.available_count > 0);
 	service->master_status.available_count--;
 	master_status_update(service);
-	close_config = service->master_status.available_count == 0 &&
-		service->service_count_left == 1;
-
-	conn->login->callback(client, auth_args[0], auth_args+1);
-	i_free(client);
-
-	if (close_config) {
-		/* we're dying as soon as this connection closes. */
-		master_service_close_config_fd(service);
-		master_login_conn_deinit(&conn);
+
+	if (client->conn->login->postlogin_socket_path == NULL)
+		master_login_auth_finish(client, auth_args);
+	else {
+		/* execute post-login scripts before finishing auth */
+		if (master_login_postlogin(client, auth_args) < 0) {


More information about the dovecot-cvs mailing list