changeset 1275:af685269ead0 HEAD

login: Wait until we're connected to auth process before executing command from client. inetd usage: --group=name can now specify which login group to use. Default is the binary name before '-' character (ie. imap or pop3).
author Timo Sirainen <tss@iki.fi>
date Wed, 05 Mar 2003 00:38:07 +0200
parents f7fc5d52ac7c
children 3607a2b4f011
files src/imap-login/client-authenticate.c src/imap-login/client.c src/imap-login/client.h src/login-common/Makefile.am src/login-common/auth-connection.c src/login-common/client-common.h src/login-common/main.c src/login-common/master.c src/login-common/master.h src/master/login-process.c src/pop3-login/client.c src/pop3-login/client.h
diffstat 12 files changed, 244 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap-login/client-authenticate.c	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/imap-login/client-authenticate.c	Wed Mar 05 00:38:07 2003 +0200
@@ -130,6 +130,7 @@
 			      master_callback, &error)) {
 	case -1:
 		/* login failed */
+                client->authenticating = FALSE;
 		client_auth_abort(client, error);
 		break;
 
@@ -143,6 +144,7 @@
 	default:
 		/* success, we should be able to log in. if we fail, just
 		   disconnect the client. */
+                client->authenticating = FALSE;
 		client_send_tagline(client, "OK Logged in.");
 	}
 }
@@ -183,6 +185,7 @@
 			io_remove(client->common.io);
 			client->common.io = NULL;
 		}
+                client->authenticating = TRUE;
 		return TRUE;
 	} else {
 		client_send_tagline(client, t_strconcat(
@@ -204,6 +207,7 @@
 			      master_callback, &error)) {
 	case -1:
 		/* login failed */
+                client->authenticating = FALSE;
 		client_auth_abort(client, error);
 		break;
 
@@ -214,6 +218,7 @@
 	default:
 		/* success, we should be able to log in. if we fail, just
 		   disconnect the client. */
+                client->authenticating = FALSE;
 		client_send_tagline(client, "OK Logged in.");
 	}
 }
@@ -303,6 +308,7 @@
 			io_remove(client->common.io);
 		client->common.io = io_add(client->common.fd, IO_READ,
 					   client_auth_input, client);
+                client->authenticating = TRUE;
 	} else {
 		client_send_tagline(client, t_strconcat(
 			"NO Authentication failed: ", error, NULL));
--- a/src/imap-login/client.c	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/imap-login/client.c	Wed Mar 05 00:38:07 2003 +0200
@@ -12,6 +12,7 @@
 #include "imap-parser.h"
 #include "client.h"
 #include "client-authenticate.h"
+#include "auth-connection.h"
 #include "ssl-proxy.h"
 
 /* max. size of one parameter in line */
@@ -184,10 +185,13 @@
 	return FALSE;
 }
 
-static void client_handle_input(struct imap_client *client)
+static int client_handle_input(struct imap_client *client)
 {
 	struct imap_arg *args;
 
+	if (client->authenticating)
+		return FALSE; /* wait until authentication is finished */
+
 	if (client->cmd_finished) {
 		/* clear the previous command from memory. don't do this
 		   immediately after handling command since we need the
@@ -199,7 +203,7 @@
 		/* remove \r\n */
 		if (client->skip_line) {
 			if (!client_skip_line(client))
-				return;
+				return TRUE;
                         client->skip_line = FALSE;
 		}
 
@@ -209,23 +213,23 @@
 	if (client->cmd_tag == NULL) {
                 client->cmd_tag = imap_parser_read_word(client->parser);
 		if (client->cmd_tag == NULL)
-			return; /* need more data */
+			return FALSE; /* need more data */
 	}
 
 	if (client->cmd_name == NULL) {
                 client->cmd_name = imap_parser_read_word(client->parser);
 		if (client->cmd_name == NULL)
-			return; /* need more data */
+			return FALSE; /* need more data */
 	}
 
 	switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
 	case -1:
 		/* error */
 		client_destroy(client, NULL);
-		return;
+		return TRUE;
 	case -2:
 		/* not enough data */
-		return;
+		return FALSE;
 	}
 	client->skip_line = TRUE;
 
@@ -236,13 +240,14 @@
 				"* BYE Too many invalid IMAP commands.");
 			client_destroy(client, "Disconnected: "
 				       "Too many invalid commands");
-			return;
+			return FALSE;
 		} 
 		client_send_tagline(client,
 			"BAD Error in IMAP command received by server.");
 	}
 
 	client->cmd_finished = TRUE;
+	return TRUE;
 }
 
 int client_read(struct imap_client *client)
@@ -272,10 +277,19 @@
 	if (!client_read(client))
 		return;
 
+	if (!auth_is_connected()) {
+		/* we're not yet connected to auth process -
+		   don't allow any commands */
+		client_send_line(client,
+			"* OK Waiting for authentication process to respond..");
+		client->input_blocked = TRUE;
+		return;
+	}
+
 	client_ref(client);
 
 	o_stream_cork(client->output);
-	client_handle_input(client);
+	while (client_handle_input(client)) ;
 
 	if (client_unref(client))
 		o_stream_flush(client->output);
@@ -454,6 +468,22 @@
 	return hash_size(clients);
 }
 
+static void client_hash_check_io(void *key, void *value __attr_unused__,
+				 void *context __attr_unused__)
+{
+	struct imap_client *client = key;
+
+	if (client->input_blocked) {
+		client->input_blocked = FALSE;
+		client_input(client);
+	}
+}
+
+void clients_notify_auth_process(void)
+{
+	hash_foreach(clients, client_hash_check_io, NULL);
+}
+
 static void client_hash_destroy(void *key, void *value __attr_unused__,
 				void *context __attr_unused__)
 {
--- a/src/imap-login/client.h	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/imap-login/client.h	Wed Mar 05 00:38:07 2003 +0200
@@ -25,6 +25,8 @@
 	unsigned int tls:1;
 	unsigned int cmd_finished:1;
 	unsigned int skip_line:1;
+	unsigned int input_blocked:1;
+	unsigned int authenticating:1;
 	unsigned int destroyed:1;
 };
 
@@ -41,9 +43,6 @@
 int client_read(struct imap_client *client);
 void client_input(void *context);
 
-unsigned int clients_get_count(void);
-void clients_destroy_all(void);
-
 void clients_init(void);
 void clients_deinit(void);
 
--- a/src/login-common/Makefile.am	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/login-common/Makefile.am	Wed Mar 05 00:38:07 2003 +0200
@@ -17,7 +17,7 @@
 noinst_HEADERS = \
 	auth-common.h \
 	auth-connection.h \
+	client-common.h \
 	common.h \
-	client-common.h \
 	master.h \
 	ssl-proxy.h
--- a/src/login-common/auth-connection.c	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/login-common/auth-connection.c	Wed Mar 05 00:38:07 2003 +0200
@@ -6,6 +6,7 @@
 #include "network.h"
 #include "istream.h"
 #include "ostream.h"
+#include "client-common.h"
 #include "auth-connection.h"
 
 #include <unistd.h>
@@ -130,6 +131,9 @@
 	hash_foreach(conn->requests, request_hash_remove, NULL);
 
         auth_connection_unref(conn);
+
+	if (auth_is_connected())
+		clients_notify_auth_process();
 }
 
 static void auth_connection_unref(struct auth_connection *conn)
@@ -200,8 +204,11 @@
 	conn->available_auth_mechs = handshake->auth_mechanisms;
 	conn->handshake_received = TRUE;
 
-	auth_waiting_handshake_count--;
+        auth_waiting_handshake_count--;
 	update_available_auth_mechs();
+
+	if (auth_is_connected())
+		clients_notify_auth_process();
 }
 
 static void auth_handle_reply(struct auth_connection *conn,
--- a/src/login-common/client-common.h	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/login-common/client-common.h	Wed Mar 05 00:38:07 2003 +0200
@@ -20,6 +20,7 @@
 struct client *client_create(int fd, struct ip_addr *ip, int ssl);
 
 unsigned int clients_get_count(void);
+void clients_notify_auth_process(void);
 void clients_destroy_all(void);
 
 void clients_init(void);
--- a/src/login-common/main.c	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/login-common/main.c	Wed Mar 05 00:38:07 2003 +0200
@@ -64,7 +64,8 @@
 	}
 
 	closing_down = TRUE;
-	master_notify_finished();
+	if (!is_inetd)
+		master_notify_finished();
 }
 
 static void sig_quit(int signo __attr_unused__)
@@ -77,13 +78,6 @@
 	struct ip_addr ip;
 	int fd;
 
-	if (!auth_is_connected()) {
-		/* we're not yet connected to auth process -
-		   don't accept client connections. FIXME: eats CPU if
-		   none of the other login processes accept it either.. */
-		return;
-	}
-
 	fd = net_accept(LOGIN_LISTEN_FD, &ip, NULL);
 	if (fd < 0) {
 		if (fd < -1)
@@ -102,13 +96,6 @@
 	struct ip_addr ip;
 	int fd, fd_ssl;
 
-	if (!auth_is_connected()) {
-		/* we're not yet connected to auth process -
-		   don't accept client connections. FIXME: eats CPU if
-		   none of the other login processes accept it either.. */
-		return;
-	}
-
 	fd = net_accept(LOGIN_SSL_LISTEN_FD, &ip, NULL);
 	if (fd < 0) {
 		if (fd < -1)
@@ -205,7 +192,7 @@
 
 		/* initialize master last - it sends the "we're ok"
 		   notification */
-		master_init(LOGIN_MASTER_SOCKET_FD);
+		master_init(LOGIN_MASTER_SOCKET_FD, TRUE);
 	}
 }
 
@@ -228,9 +215,9 @@
 
 int main(int argc __attr_unused__, char *argv[], char *envp[])
 {
-	const char *name;
+	const char *name, *group_name;
 	struct ip_addr ip;
-	int fd = -1, master_fd = -1;
+	int i, fd = -1, master_fd = -1;
 
 	is_inetd = getenv("DOVECOT_MASTER") == NULL;
 
@@ -244,8 +231,19 @@
 
 	if (is_inetd) {
 		/* running from inetd. create master process before
-		   dropping privileges */
-		master_fd = master_connect();
+		   dropping privileges. */
+		group_name = strrchr(argv[0], '/');
+		group_name = group_name == NULL ? argv[0] : group_name+1;
+		group_name = t_strcut(group_name, '-');
+
+		for (i = 1; i < argc; i++) {
+			if (strncmp(argv[i], "--group=", 8) == 0) {
+				group_name = argv[1]+8;
+				break;
+			}
+		}
+
+		master_fd = master_connect(group_name);
 	}
 
 	name = strrchr(argv[0], '/');
@@ -253,30 +251,34 @@
 
 	process_title_init(argv, envp);
 	ioloop = io_loop_create(system_pool);
+	main_init();
 
 	if (is_inetd) {
-		master_init(master_fd);
-
 		if (net_getsockname(1, &ip, NULL) < 0) {
 			i_fatal("%s can be started only through dovecot "
 				"master process, inetd or equilevant", argv[0]);
 		}
 
-		if (argc < 2 || strcmp(argv[1], "--ssl") != 0)
-			fd = 1;
-		else
-			fd = ssl_proxy_new(fd, &ip);
+		fd = 1;
+		for (i = 1; i < argc; i++) {
+			if (strcmp(argv[i], "--ssl") == 0) {
+				fd = ssl_proxy_new(fd, &ip);
+				if (fd == -1)
+					i_fatal("SSL initialization failed");
+			} else if (strncmp(argv[i], "--group=", 8) != 0)
+				i_fatal("Unknown parameter: %s", argv[i]);
+		}
+
+		master_init(master_fd, FALSE);
 	}
 
-	if (fd != -1 || !is_inetd) {
-		main_init();
+	main_close_listen();
 
-		if (fd != -1)
-			(void)client_create(fd, &ip, TRUE);
+	if (fd != -1)
+		(void)client_create(fd, &ip, TRUE);
 
-		io_loop_run(ioloop);
-		main_deinit();
-	}
+	io_loop_run(ioloop);
+	main_deinit();
 
 	io_loop_destroy(ioloop);
 	lib_deinit();
--- a/src/login-common/master.c	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/login-common/master.c	Wed Mar 05 00:38:07 2003 +0200
@@ -7,6 +7,7 @@
 #include "fdpass.h"
 #include "istream.h"
 #include "env-util.h"
+#include "write-full.h"
 #include "master.h"
 #include "client-common.h"
 
@@ -133,7 +134,7 @@
 	i_stream_unref(input);
 }
 
-int master_connect(void)
+int master_connect(const char *group_name)
 {
 	const char *path = PKG_RUNDIR"/master";
 	int i, fd = -1;
@@ -164,6 +165,19 @@
 	if (fd == -1)
 		i_fatal("Couldn't use/create UNIX socket %s", path);
 
+	if (group_name[0] == '\0')
+		i_fatal("No login group name set");
+
+	if (strlen(group_name) >= 256)
+		i_fatal("Login group name too large: %s", group_name);
+
+	/* group_name length is now guaranteed to be in range of 1..255 so we
+	   can send <length byte><name> */
+	group_name = t_strdup_printf("%c%s", (unsigned char)strlen(group_name),
+				     group_name);
+	if (write_full(fd, group_name, strlen(group_name)) < 0)
+		i_fatal("write_full(master_fd) failed: %m");
+
 	master_read_env(fd);
 	return fd;
 }
@@ -189,7 +203,7 @@
 	master_pos = 0;
 }
 
-void master_init(int fd)
+void master_init(int fd, int notify)
 {
 	main_ref();
 
@@ -200,9 +214,11 @@
         master_pos = 0;
 	io_master = io_add(master_fd, IO_READ, master_input, NULL);
 
-	/* just a note to master that we're ok. if we die before,
-	   master should shutdown itself. */
-	master_notify_finished();
+	if (notify) {
+		/* just a note to master that we're ok. if we die before,
+		   master should shutdown itself. */
+		master_notify_finished();
+	}
 }
 
 void master_deinit(void)
--- a/src/login-common/master.h	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/login-common/master.h	Wed Mar 05 00:38:07 2003 +0200
@@ -17,9 +17,9 @@
 void master_close(void);
 
 /* inetd: Connect to existing master process, or create new one. */
-int master_connect(void);
+int master_connect(const char *group_name);
 
-void master_init(int fd);
+void master_init(int fd, int notify);
 void master_deinit(void);
 
 #endif
--- a/src/master/login-process.c	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/master/login-process.c	Wed Mar 05 00:38:07 2003 +0200
@@ -67,6 +67,7 @@
 
 static void login_process_destroy(struct login_process *p);
 static void login_process_unref(struct login_process *p);
+static int login_process_init_group(struct login_process *p);
 
 static void login_group_create(struct login_settings *login_set)
 {
@@ -142,16 +143,76 @@
 	}
 
 	p->listening = FALSE;
-	p->group->listening_processes--;
+
+	if (p->group != NULL) {
+		p->group->listening_processes--;
+		p->prev_nonlisten = p->group->newest_nonlisten_process;
+
+		if (p->group->newest_nonlisten_process != NULL)
+			p->group->newest_nonlisten_process->next_nonlisten = p;
+		p->group->newest_nonlisten_process = p;
+
+		if (p->group->oldest_nonlisten_process == NULL)
+			p->group->oldest_nonlisten_process = p;
+	}
+}
 
-	p->prev_nonlisten = p->group->newest_nonlisten_process;
+static struct login_group *login_group_process_find(const char *name)
+{
+	struct login_group *group;
+	struct login_settings *login;
+
+	if (login_groups == NULL) {
+		for (login = set->logins; login != NULL; login = login->next)
+			login_group_create(login);
+	}
+
+	for (group = login_groups; group != NULL; group = group->next) {
+		if (strcmp(group->set->name, name) == 0)
+			return group;
+	}
+
+	return NULL;
+}
 
-	if (p->group->newest_nonlisten_process != NULL)
-		p->group->newest_nonlisten_process->next_nonlisten = p;
-	p->group->newest_nonlisten_process = p;
+static int login_process_read_group(struct login_process *p)
+{
+	struct login_group *group;
+	const char *name;
+	char buf[256];
+	unsigned int len;
+	ssize_t ret;
+
+	/* read length */
+	ret = read(p->fd, buf, 1);
+	if (ret != 1)
+		len = 0;
+	else {
+		len = buf[0];
+		if (len >= sizeof(buf)) {
+			i_error("login: Process name length too large");
+			return FALSE;
+		}
 
-	if (p->group->oldest_nonlisten_process == NULL)
-		p->group->oldest_nonlisten_process = p;
+		ret = read(p->fd, buf, len);
+	}
+
+	if (ret < 0)
+		i_error("login: read() failed: %m");
+	else if (len == 0 || (size_t)ret != len)
+		i_error("login: Process name wasn't sent");
+	else {
+		name = t_strndup(buf, len);
+		group = login_group_process_find(name);
+		if (group == NULL) {
+			i_error("login: Unknown process group '%s'", name);
+			return FALSE;
+		}
+
+		p->group = group;
+		return login_process_init_group(p);
+	}
+	return FALSE;
 }
 
 static void login_process_input(void *context)
@@ -160,7 +221,15 @@
 	struct auth_process *auth_process;
 	struct login_auth_request *authreq;
 	struct master_login_request req;
-	int client_fd, ret;
+	int client_fd;
+	ssize_t ret;
+
+	if (p->group == NULL) {
+		/* we want to read the group */
+		if (!login_process_read_group(p))
+			login_process_destroy(p);
+		return;
+	}
 
 	ret = fd_read(p->fd, &req, sizeof(req), &client_fd);
 	if (ret != sizeof(req)) {
@@ -222,7 +291,6 @@
 	struct login_process *p;
 
 	i_assert(pid != 0);
-	i_assert(group != NULL);
 
 	p = i_new(struct login_process, 1);
 	p->group = group;
@@ -238,13 +306,18 @@
 	PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_LOGIN);
 	hash_insert(processes, POINTER_CAST(pid), p);
 
-	p->group->processes++;
-	p->group->listening_processes++;
+	if (p->group != NULL) {
+		p->group->processes++;
+		p->group->listening_processes++;
+	}
 	return p;
 }
 
 static void login_process_remove_from_lists(struct login_process *p)
 {
+	if (p->group == NULL)
+		return;
+
 	if (p == p->group->oldest_nonlisten_process)
 		p->group->oldest_nonlisten_process = p->next_nonlisten;
 	else
@@ -268,7 +341,8 @@
 		i_error("Login process died too early - shutting down");
 		io_loop_stop(ioloop);
 	}
-	if (p->listening)
+
+	if (p->listening && p->group != NULL)
 		p->group->listening_processes--;
 
 	o_stream_close(p->output);
@@ -279,7 +353,9 @@
 	if (!p->listening)
 		login_process_remove_from_lists(p);
 
-	p->group->processes--;
+	if (p->group != NULL)
+		p->group->processes--;
+
 	if (p->pid != 0)
 		hash_remove(processes, POINTER_CAST(p->pid));
 
@@ -421,7 +497,7 @@
 	/* don't start raising the process count if they're dying all
 	   the time */
 	p = hash_lookup(processes, POINTER_CAST(pid));
-	if (p != NULL)
+	if (p != NULL && p->group != NULL)
 		p->group->wanted_processes_count = 0;
 }
 
@@ -516,6 +592,19 @@
 	return ret;
 }
 
+static int login_process_init_group(struct login_process *p)
+{
+	p->group->processes++;
+	p->group->listening_processes++;
+
+	if (login_process_send_env(p) < 0) {
+		i_error("login: Couldn't send environment");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static void inetd_login_accept(void *context __attr_unused__)
 {
         struct login_process *p;
@@ -529,13 +618,8 @@
 		net_set_nonblock(fd, TRUE);
 		fd_close_on_exec(fd, TRUE);
 
-		p = login_process_new(login_groups, ++login_pid_counter, fd);
-		p->initialized = TRUE;;
-
-		if (login_process_send_env(p) < 0) {
-			i_warning("Couldn't send environment to login process");
-			login_process_destroy(p);
-		}
+		p = login_process_new(NULL, ++login_pid_counter, fd);
+		p->initialized = TRUE;
 	}
 }
 
@@ -550,9 +634,6 @@
 		to = timeout_add(1000, login_processes_start_missing, NULL);
 		io_listen = NULL;
 	} else {
-		/* use the first login group for everyone */
-		login_group_create(set->logins);
-
 		to = NULL;
 		io_listen = io_add(inetd_login_fd, IO_READ,
 				   inetd_login_accept, NULL);
--- a/src/pop3-login/client.c	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/pop3-login/client.c	Wed Mar 05 00:38:07 2003 +0200
@@ -11,6 +11,7 @@
 #include "strescape.h"
 #include "client.h"
 #include "client-authenticate.h"
+#include "auth-connection.h"
 #include "ssl-proxy.h"
 
 /* max. length of input command line (spec says 512) */
@@ -157,6 +158,13 @@
 	if (!client_read(client))
 		return;
 
+	if (!auth_is_connected()) {
+		/* we're not yet connected to auth process -
+		   don't allow any commands */
+		client->input_blocked = TRUE;
+		return;
+	}
+
 	client_ref(client);
 
 	o_stream_cork(client->output);
@@ -341,6 +349,22 @@
 	return hash_size(clients);
 }
 
+static void client_hash_check_io(void *key, void *value __attr_unused__,
+				 void *context __attr_unused__)
+{
+	struct pop3_client *client = key;
+
+	if (client->input_blocked) {
+		client->input_blocked = FALSE;
+		client_input(client);
+	}
+}
+
+void clients_notify_auth_process(void)
+{
+	hash_foreach(clients, client_hash_check_io, NULL);
+}
+
 static void client_hash_destroy(void *key, void *value __attr_unused__,
 				void *context __attr_unused__)
 {
--- a/src/pop3-login/client.h	Tue Mar 04 20:51:02 2003 +0200
+++ b/src/pop3-login/client.h	Wed Mar 05 00:38:07 2003 +0200
@@ -20,6 +20,7 @@
 	buffer_t *plain_login;
 
 	unsigned int tls:1;
+	unsigned int input_blocked:1;
 	unsigned int destroyed:1;
 };
 
@@ -35,9 +36,6 @@
 int client_read(struct pop3_client *client);
 void client_input(void *context);
 
-unsigned int clients_get_count(void);
-void clients_destroy_all(void);
-
 void clients_init(void);
 void clients_deinit(void);