changeset 613:1906116a62ce HEAD

Finally support for handling each login connection in it's own process. Enabled by default. Also a few bugfixes to master process.
author Timo Sirainen <tss@iki.fi>
date Sat, 16 Nov 2002 07:21:21 +0200
parents 7c91f579ebed
children e60620644af3
files dovecot-example.conf src/login/client.c src/login/client.h src/login/common.h src/login/main.c src/login/master.c src/login/master.h src/login/ssl-proxy.c src/master/login-process.c src/master/settings.c src/master/settings.h
diffstat 11 files changed, 229 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/dovecot-example.conf	Sat Nov 16 07:19:03 2002 +0200
+++ b/dovecot-example.conf	Sat Nov 16 07:21:21 2002 +0200
@@ -57,11 +57,20 @@
 # is if you wish to run the whole imapd without roots.
 #login_chroot = yes
 
-# Number of imap-login processes to use, one or two is enough
-#login_processes_count = 1
+# Should each login be processed in it's own process (yes), or should one
+# login process be allowed to process multiple connections (no)? Yes is more
+# secure, espcially with SSL/TLS enabled. No is faster since there's no need
+# to create processes all the time.
+#login_process_per_connection = yes
+
+# Number of imap-login processes to create. If login_process_per_user is
+# yes, this is the number of extra processes waiting for users to log in.
+#login_processes_count = 3
 
 # Maximum number of connections allowed in login state. When this limit is
-# reached, the oldest connections are dropped.
+# reached, the oldest connections are dropped. If login_process_per_user
+# is no, this is a per-process value, so the absolute maximum number of users
+# logging in actually login_processes_count * max_logging_users.
 #max_logging_users = 256
 
 ##
--- a/src/login/client.c	Sat Nov 16 07:19:03 2002 +0200
+++ b/src/login/client.c	Sat Nov 16 07:21:21 2002 +0200
@@ -271,6 +271,8 @@
         client->last_input = ioloop_time;
 	hash_insert(clients, client, client);
 
+	main_ref();
+
 	client_send_line(client, "* OK " PACKAGE " ready.");
 	return client;
 }
@@ -312,6 +314,8 @@
 	i_free(client->tag);
 	i_free(client->plain_login);
 	i_free(client);
+
+	main_unref();
 	return FALSE;
 }
 
@@ -353,10 +357,9 @@
 	hash_foreach(clients, client_hash_check_idle, NULL);
 }
 
-void clients_init(void)
+unsigned int clients_get_count(void)
 {
-	clients = hash_create(default_pool, 128, NULL, NULL);
-	to_idle = timeout_add(1000, idle_timeout, NULL);
+	return hash_size(clients);
 }
 
 static void client_hash_destroy(void *key, void *value __attr_unused__,
@@ -365,9 +368,20 @@
 	client_destroy(key, NULL);
 }
 
+void clients_destroy_all(void)
+{
+	hash_foreach(clients, client_hash_destroy, NULL);
+}
+
+void clients_init(void)
+{
+	clients = hash_create(default_pool, 128, NULL, NULL);
+	to_idle = timeout_add(1000, idle_timeout, NULL);
+}
+
 void clients_deinit(void)
 {
-	hash_foreach(clients, client_hash_destroy, NULL);
+	clients_destroy_all();
 	hash_destroy(clients);
 
 	timeout_remove(to_idle);
--- a/src/login/client.h	Sat Nov 16 07:19:03 2002 +0200
+++ b/src/login/client.h	Sat Nov 16 07:21:21 2002 +0200
@@ -37,6 +37,9 @@
 int client_read(Client *client);
 void client_input(void *context, int fd, IO io);
 
+unsigned int clients_get_count(void);
+void clients_destroy_all(void);
+
 void clients_init(void);
 void clients_deinit(void);
 
--- a/src/login/common.h	Sat Nov 16 07:19:03 2002 +0200
+++ b/src/login/common.h	Sat Nov 16 07:21:21 2002 +0200
@@ -7,8 +7,10 @@
 typedef struct _Client Client;
 typedef struct _AuthRequest AuthRequest;
 
-extern IOLoop ioloop;
 extern int disable_plaintext_auth;
 extern unsigned int max_logging_users;
 
+void main_ref(void);
+void main_unref(void);
+
 #endif
--- a/src/login/main.c	Sat Nov 16 07:19:03 2002 +0200
+++ b/src/login/main.c	Sat Nov 16 07:21:21 2002 +0200
@@ -10,13 +10,33 @@
 #include "ssl-proxy.h"
 
 #include <stdlib.h>
+#include <unistd.h>
 #include <syslog.h>
 
-IOLoop ioloop;
 int disable_plaintext_auth;
 unsigned int max_logging_users;
 
+static IOLoop ioloop;
 static IO io_imap, io_imaps;
+static int main_refcount;
+static int process_per_connection, closing_down;
+
+void main_ref(void)
+{
+	main_refcount++;
+}
+
+void main_unref(void)
+{
+	if (--main_refcount == 0) {
+		/* nothing to do, quit */
+		io_loop_stop(ioloop);
+	} else if (closing_down && clients_get_count() == 0) {
+		/* last login finished, close all communications
+		   to master process */
+		master_close();
+	}
+}
 
 static void sig_quit(int signo __attr_unused__)
 {
@@ -33,6 +53,23 @@
 	if (fd == -1)
 		return;
 
+	if (process_per_connection) {
+		if (close(listen_fd) < 0)
+			i_fatal("can't close() listen handle");
+
+		if (io_imap != NULL) {
+			io_remove(io_imap);
+			io_imap = NULL;
+		}
+		if (io_imaps != NULL) {
+			io_remove(io_imaps);
+			io_imaps = NULL;
+		}
+
+		closing_down = TRUE;
+		master_notify_finished();
+	}
+
 	(void)client_create(fd, &addr);
 }
 
@@ -79,10 +116,14 @@
 	}
 
 	disable_plaintext_auth = getenv("DISABLE_PLAINTEXT_AUTH") != NULL;
+        process_per_connection = getenv("PROCESS_PER_CONNECTION") != NULL;
 
 	value = getenv("MAX_LOGGING_USERS");
 	max_logging_users = value == NULL ? 0 : strtoul(value, NULL, 10);
 
+        closing_down = FALSE;
+	main_refcount = 0;
+
 	/* Initialize SSL proxy before dropping privileges so it can read
 	   the certificate and private key file. */
 	ssl_proxy_init();
--- a/src/login/master.c	Sat Nov 16 07:19:03 2002 +0200
+++ b/src/login/master.c	Sat Nov 16 07:21:21 2002 +0200
@@ -5,6 +5,9 @@
 #include "network.h"
 #include "fdpass.h"
 #include "master.h"
+#include "client.h"
+
+#include <unistd.h>
 
 typedef struct _WaitingRequest WaitingRequest;
 
@@ -86,6 +89,29 @@
 	push_request(req.id, callback, context);
 }
 
+void master_notify_finished(void)
+{
+	MasterRequest req;
+
+	memset(&req, 0, sizeof(req));
+
+	/* sending -1 as fd does the notification */
+	if (fd_send(LOGIN_MASTER_SOCKET_FD,
+		    -1, &req, sizeof(req)) != sizeof(req))
+		i_fatal("fd_send() failed: %m");
+}
+
+void master_close(void)
+{
+	clients_destroy_all();
+
+	(void)close(LOGIN_MASTER_SOCKET_FD);
+	io_remove(io_master);
+	io_master = NULL;
+
+	main_unref();
+}
+
 static void master_input(void *context __attr_unused__, int fd,
 			 IO io __attr_unused__)
 {
@@ -94,8 +120,8 @@
 	ret = net_receive(fd, master_buf + master_pos,
 			  sizeof(master_buf) - master_pos);
 	if (ret < 0) {
-		/* master died, kill ourself too */
-		io_loop_stop(ioloop);
+		/* master died, kill all clients logging in */
+		master_close();
 		return;
 	}
 
@@ -110,6 +136,8 @@
 
 void master_init(void)
 {
+	main_ref();
+
 	requests = NULL;
 	next_request = &requests;
 
@@ -126,5 +154,7 @@
 		i_free(requests);
 		requests = next;
 	}
-	io_remove(io_master);
+
+	if (io_master != NULL)
+		io_remove(io_master);
 }
--- a/src/login/master.h	Sat Nov 16 07:19:03 2002 +0200
+++ b/src/login/master.h	Sat Nov 16 07:21:21 2002 +0200
@@ -10,6 +10,12 @@
 			 unsigned char cookie[AUTH_COOKIE_SIZE], IPADDR *ip,
 			 MasterCallback callback, void *context);
 
+/* Notify master that we're not listening for new connections anymore. */
+void master_notify_finished(void);
+
+/* Close connection to master process */
+void master_close(void);
+
 void master_init(void);
 void master_deinit(void);
 
--- a/src/login/ssl-proxy.c	Sat Nov 16 07:19:03 2002 +0200
+++ b/src/login/ssl-proxy.c	Sat Nov 16 07:21:21 2002 +0200
@@ -125,6 +125,8 @@
 		io_remove(proxy->io_plain);
 
 	i_free(proxy);
+
+	main_unref();
 	return FALSE;
 }
 
@@ -230,26 +232,6 @@
 	proxy->io_plain = io_add(proxy->fd_ssl, IO_WRITE, plain_output, proxy);
 }
 
-static GNUTLS_STATE initialize_state(void)
-{
-	GNUTLS_STATE state;
-
-	gnutls_init(&state, GNUTLS_SERVER);
-
-	gnutls_protocol_set_priority(state, protocol_priority);
-	gnutls_cipher_set_priority(state, cipher_priority);
-	gnutls_compression_set_priority(state, comp_priority);
-	gnutls_kx_set_priority(state, kx_priority);
-	gnutls_mac_set_priority(state, mac_priority);
-
-	gnutls_cred_set(state, GNUTLS_CRD_CERTIFICATE, x509_cred);
-
-	/*gnutls_certificate_server_set_request(state, GNUTLS_CERT_REQUEST);*/
-
-	gnutls_dh_set_prime_bits(state, DH_BITS);
-	return state;
-}
-
 static void ssl_handshake(void *context, int fd __attr_unused__,
 			  IO io __attr_unused__)
 {
@@ -284,6 +266,26 @@
 	}
 }
 
+static GNUTLS_STATE initialize_state(void)
+{
+	GNUTLS_STATE state;
+
+	gnutls_init(&state, GNUTLS_SERVER);
+
+	gnutls_protocol_set_priority(state, protocol_priority);
+	gnutls_cipher_set_priority(state, cipher_priority);
+	gnutls_compression_set_priority(state, comp_priority);
+	gnutls_kx_set_priority(state, kx_priority);
+	gnutls_mac_set_priority(state, mac_priority);
+
+	gnutls_cred_set(state, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+	/*gnutls_certificate_server_set_request(state, GNUTLS_CERT_REQUEST);*/
+
+	gnutls_dh_set_prime_bits(state, DH_BITS);
+	return state;
+}
+
 int ssl_proxy_new(int fd)
 {
         SSLProxy *proxy;
@@ -316,6 +318,7 @@
 	if (!ssl_proxy_destroy(proxy))
 		return -1;
 
+        main_ref();
 	return sfd[1];
 }
 
--- a/src/master/login-process.c	Sat Nov 16 07:19:03 2002 +0200
+++ b/src/master/login-process.c	Sat Nov 16 07:21:21 2002 +0200
@@ -14,15 +14,19 @@
 #include <unistd.h>
 #include <syslog.h>
 
-typedef struct {
+typedef struct _LoginProcess LoginProcess;
+
+struct _LoginProcess {
+	LoginProcess *prev_nonlisten, *next_nonlisten;
 	int refcount;
 
 	pid_t pid;
 	int fd;
 	IO io;
 	OBuffer *outbuf;
+	unsigned int listening:1;
 	unsigned int destroyed:1;
-} LoginProcess;
+};
 
 typedef struct {
 	LoginProcess *process;
@@ -36,7 +40,10 @@
 
 static int auth_id_counter;
 static Timeout to;
-static HashTable *processes = NULL;
+
+static HashTable *processes;
+static LoginProcess *oldest_nonlisten_process, *newest_nonlisten_process;
+static unsigned int listening_processes;
 
 static void login_process_destroy(LoginProcess *p);
 static void login_process_unref(LoginProcess *p);
@@ -103,6 +110,28 @@
 		return;
 	}
 
+	if (client_fd == -1) {
+		/* just a notification that the login process isn't
+		   listening for new connections anymore */
+		if (!p->listening) {
+			i_error("login: received another \"not listening\" "
+				"notification");
+		} else {
+			p->listening = FALSE;
+			listening_processes--;
+
+			p->prev_nonlisten = newest_nonlisten_process;
+
+			if (newest_nonlisten_process != NULL)
+				newest_nonlisten_process->next_nonlisten = p;
+			newest_nonlisten_process = p;
+
+			if (oldest_nonlisten_process == NULL)
+                                oldest_nonlisten_process = p;
+		}
+		return;
+	}
+
 	/* login process isn't trusted, validate all data to make sure
 	   it's not trying to exploit us */
 	if (!VALIDATE_STR(req.login_tag)) {
@@ -125,7 +154,7 @@
 	if (auth_process == NULL) {
 		i_error("login: Authentication process %u doesn't exist",
 			req.auth_process);
-		auth_callback(NULL, &authreq);
+		auth_callback(NULL, authreq);
 	} else {
 		auth_process_request(auth_process, authreq->auth_id, req.cookie,
 				     auth_callback, authreq);
@@ -142,26 +171,50 @@
 	p->refcount = 1;
 	p->pid = pid;
 	p->fd = fd;
+	p->listening = TRUE;
 	p->io = io_add(fd, IO_READ, login_process_input, p);
 	p->outbuf = o_buffer_create_file(fd, default_pool,
 					 sizeof(MasterReply)*10,
 					 IO_PRIORITY_DEFAULT, FALSE);
 
 	hash_insert(processes, POINTER_CAST(pid), p);
+        listening_processes++;
 	return p;
 }
 
+void login_process_remove_from_lists(LoginProcess *p)
+{
+	if (p == oldest_nonlisten_process)
+		oldest_nonlisten_process = p->next_nonlisten;
+	else
+		p->prev_nonlisten->next_nonlisten = p->next_nonlisten;
+
+	if (p == newest_nonlisten_process)
+		newest_nonlisten_process = p->prev_nonlisten;
+	else
+		p->next_nonlisten->prev_nonlisten = p->prev_nonlisten;
+
+	p->next_nonlisten = p->prev_nonlisten = NULL;
+}
+
 static void login_process_destroy(LoginProcess *p)
 {
 	if (p->destroyed)
 		return;
 	p->destroyed = TRUE;
 
+	if (p->listening)
+		listening_processes--;
+
 	o_buffer_close(p->outbuf);
 	io_remove(p->io);
 	(void)close(p->fd);
 
+	if (!p->listening)
+		login_process_remove_from_lists(p);
+
 	hash_remove(processes, POINTER_CAST(p->pid));
+
 	login_process_unref(p);
 }
 
@@ -180,6 +233,12 @@
 	pid_t pid;
 	int fd[2];
 
+	if (set_login_process_per_connection &&
+	    hash_size(processes)-listening_processes >= set_max_logging_users) {
+		if (oldest_nonlisten_process != NULL)
+			login_process_destroy(oldest_nonlisten_process);
+	}
+
 	if (set_login_uid == 0)
 		i_fatal("Login process must not run as root");
 
@@ -249,8 +308,13 @@
 	if (set_disable_plaintext_auth)
 		putenv("DISABLE_PLAINTEXT_AUTH=1");
 
-	putenv((char *) t_strdup_printf("MAX_LOGGING_USERS=%d",
-					set_max_logging_users));
+	if (set_login_process_per_connection) {
+		putenv("PROCESS_PER_CONNECTION=1");
+		putenv("MAX_LOGGING_USERS=1");
+	} else {
+		putenv((char *) t_strdup_printf("MAX_LOGGING_USERS=%d",
+						set_max_logging_users));
+	}
 
 	/* hide the path, it's ugly */
 	argv[0] = strrchr(set_login_executable, '/');
@@ -280,14 +344,17 @@
 {
 	/* create max. one process every second, that way if it keeps
 	   dying all the time we don't eat all cpu with fork()ing. */
-	if (hash_size(processes) < set_login_processes_count)
+	if (listening_processes < set_login_processes_count)
                 (void)create_login_process();
 }
 
 void login_processes_init(void)
 {
         auth_id_counter = 0;
-        processes = hash_create(default_pool, 128, NULL, NULL);
+	listening_processes = 0;
+	oldest_nonlisten_process = newest_nonlisten_process = NULL;
+
+	processes = hash_create(default_pool, 128, NULL, NULL);
 	to = timeout_add(1000, login_processes_start_missing, NULL);
 }
 
--- a/src/master/settings.c	Sat Nov 16 07:19:03 2002 +0200
+++ b/src/master/settings.c	Sat Nov 16 07:21:21 2002 +0200
@@ -39,6 +39,8 @@
 	{ "login_user",		SET_STR, &set_login_user },
 	{ "login_dir",		SET_STR, &set_login_dir },
 	{ "login_chroot",	SET_BOOL,&set_login_chroot },
+	{ "login_process_per_connection",
+				SET_BOOL,&set_login_process_per_connection },
 	{ "login_processes_count",
 				SET_INT, &set_login_processes_count },
 	{ "max_logging_users",	SET_INT, &set_max_logging_users },
@@ -88,7 +90,8 @@
 char *set_login_dir = PKG_RUNDIR;
 
 int set_login_chroot = TRUE;
-unsigned int set_login_processes_count = 1;
+int set_login_process_per_connection = TRUE;
+unsigned int set_login_processes_count = 3;
 unsigned int set_max_logging_users = 256;
 
 uid_t set_login_uid; /* generated from set_login_user */
@@ -176,11 +179,18 @@
 	if (access(set_login_dir, X_OK) < 0)
 		i_fatal("Can't access login directory %s: %m", set_login_dir);
 
+	if (set_max_imap_processes < 1)
+		i_fatal("max_imap_processes must be at least 1");
 	if (set_login_processes_count < 1)
 		i_fatal("login_processes_count must be at least 1");
-	if (set_first_valid_uid < set_last_valid_uid)
+	if (set_max_logging_users < 1)
+		i_fatal("max_logging_users must be at least 1");
+
+	if (set_last_valid_uid != 0 &&
+	    set_first_valid_uid > set_last_valid_uid)
 		i_fatal("first_valid_uid can't be larger than last_valid_uid");
-	if (set_first_valid_gid < set_last_valid_gid)
+	if (set_last_valid_gid != 0 &&
+	    set_first_valid_gid > set_last_valid_gid)
 		i_fatal("first_valid_gid can't be larger than last_valid_gid");
 
 	auth_settings_verify();
--- a/src/master/settings.h	Sat Nov 16 07:19:03 2002 +0200
+++ b/src/master/settings.h	Sat Nov 16 07:21:21 2002 +0200
@@ -20,6 +20,7 @@
 extern char *set_login_user;
 extern char *set_login_dir;
 extern int set_login_chroot;
+extern int set_login_process_per_connection;
 extern unsigned int set_login_processes_count;
 extern unsigned int set_max_logging_users;