changeset 2311:4558d1b196fd HEAD

Added --exec-mail option to master. It can be used to parse Dovecot config file and exec() imap/pop3 process directly. Moved --inetd option into environment as it's only for internal use.
author Timo Sirainen <tss@iki.fi>
date Fri, 09 Jul 2004 22:59:02 +0300
parents 1d03eceb6ae6
children cc753e275197
files src/login-common/master.c src/master/mail-process.c src/master/mail-process.h src/master/main.c src/master/master-settings.c src/master/master-settings.h
diffstat 6 files changed, 247 insertions(+), 180 deletions(-) [+]
line wrap: on
line diff
--- a/src/login-common/master.c	Fri Jul 09 22:22:15 2004 +0300
+++ b/src/login-common/master.c	Fri Jul 09 22:59:02 2004 +0300
@@ -104,7 +104,7 @@
 
 static void master_exec(int fd)
 {
-	char *argv[] = { "dovecot", "--inetd", NULL };
+	char *argv[] = { "dovecot", NULL };
 
 	switch (fork()) {
 	case -1:
@@ -117,6 +117,7 @@
 		if (setsid() < 0)
 			i_fatal("setsid() failed: %m");
 
+		env_put("DOVECOT_INETD=1");
 		execv(SBINDIR"/dovecot", argv);
 		i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
 			       SBINDIR"/dovecot");
--- a/src/master/mail-process.c	Fri Jul 09 22:22:15 2004 +0300
+++ b/src/master/mail-process.c	Fri Jul 09 22:59:02 2004 +0300
@@ -75,10 +75,9 @@
 }
 
 static const struct var_expand_table *
-get_var_expand_table(enum process_type process_type,
+get_var_expand_table(const char *protocol,
 		     const char *user, const char *home,
-		     const struct ip_addr *local_ip,
-		     const struct ip_addr *remote_ip, pid_t pid)
+		     const char *local_ip, const char *remote_ip, pid_t pid)
 {
 	static struct var_expand_table static_tab[] = {
 		{ 'u', NULL },
@@ -100,10 +99,10 @@
 	tab[1].value = t_strcut(user, '@');
 	tab[2].value = strchr(user, '@');
 	if (tab[2].value != NULL) tab[2].value++;
-	tab[3].value = t_str_ucase(process_names[process_type]);
+	tab[3].value = t_str_ucase(protocol);
 	tab[4].value = home;
-	tab[5].value = net_ip2addr(local_ip);
-	tab[6].value = net_ip2addr(remote_ip);
+	tab[5].value = local_ip;
+	tab[6].value = remote_ip;
 	tab[7].value = dec2str(pid);
 
 	return tab;
@@ -180,130 +179,10 @@
 	}
 }
 
-int create_mail_process(struct login_group *group, int socket,
-			const struct ip_addr *local_ip,
-			const struct ip_addr *remote_ip,
-			struct auth_master_reply *reply, const char *data)
+static void
+mail_process_set_environment(struct settings *set, const char *mail,
+			     const struct var_expand_table *var_expand_table)
 {
-	struct settings *set = group->set;
-	const struct var_expand_table *var_expand_table;
-	const char *argv[4];
-	const char *addr, *mail, *user, *chroot_dir, *home_dir, *full_home_dir;
-	const char *executable, *p;
-	struct log_io *log;
-	char title[1024];
-	string_t *str;
-	pid_t pid;
-	int i, err, ret, log_fd;
-
-	// FIXME: per-group
-	if (mail_process_count == set->max_mail_processes) {
-		i_error("Maximum number of mail processes exceeded");
-		return FALSE;
-	}
-
-	if (!validate_uid_gid(set, reply->uid, reply->gid,
-			      data + reply->virtual_user_idx))
-		return FALSE;
-
-	user = data + reply->virtual_user_idx;
-	mail = data + reply->mail_idx;
-	home_dir = data + reply->home_idx;
-	chroot_dir = data + reply->chroot_idx;
-
-	if (*chroot_dir == '\0' && set->mail_chroot != NULL)
-		chroot_dir = set->mail_chroot;
-
-	if (*chroot_dir != '\0' && !validate_chroot(set, chroot_dir)) {
-		i_error("Invalid chroot directory '%s' (user %s) "
-			"(see valid_chroot_dirs in config file)",
-			chroot_dir, user);
-		return FALSE;
-	}
-
-	log_fd = log_create_pipe(&log);
-
-	pid = fork();
-	if (pid < 0) {
-		i_error("fork() failed: %m");
-		(void)close(log_fd);
-		return FALSE;
-	}
-
-	var_expand_table =
-		get_var_expand_table(group->process_type, user, home_dir,
-				     local_ip, remote_ip,
-				     pid != 0 ? pid : getpid());
-	str = t_str_new(128);
-
-	if (pid != 0) {
-		/* master */
-		var_expand(str, set->mail_log_prefix, var_expand_table);
-		log_set_prefix(log, str_c(str));
-
-		mail_process_count++;
-		PID_ADD_PROCESS_TYPE(pid, group->process_type);
-		(void)close(log_fd);
-		return TRUE;
-	}
-
-	str_append(str, "master-");
-	var_expand(str, set->mail_log_prefix, var_expand_table);
-	log_set_prefix(log, str_c(str));
-
-	child_process_init_env();
-
-	/* move the client socket into stdin and stdout fds, log to stderr */
-	if (dup2(socket, 0) < 0)
-		i_fatal("dup2(stdin) failed: %m");
-	if (dup2(socket, 1) < 0)
-		i_fatal("dup2(stdout) failed: %m");
-	if (dup2(log_fd, 2) < 0)
-		i_fatal("dup2(stderr) failed: %m");
-
-	for (i = 0; i < 3; i++)
-		fd_close_on_exec(i, FALSE);
-
-	/* setup environment - set the most important environment first
-	   (paranoia about filling up environment without noticing) */
-	restrict_access_set_env(data + reply->system_user_idx,
-				reply->uid, reply->gid, chroot_dir,
-				set->first_valid_gid, set->last_valid_gid,
-				set->mail_extra_groups);
-
-	restrict_process_size(group->set->mail_process_size, (unsigned int)-1);
-
-	if (*home_dir == '\0')
-		ret = -1;
-	else {
-		full_home_dir = *chroot_dir == '\0' ? home_dir :
-			t_strconcat(chroot_dir, "/", home_dir, NULL);
-		/* NOTE: if home directory is NFS-mounted, we might not
-		   have access to it as root. Change the effective UID
-		   temporarily to make it work. */
-		if (reply->uid != master_uid && seteuid(reply->uid) < 0)
-			i_fatal("seteuid(%s) failed: %m", dec2str(reply->uid));
-		ret = chdir(full_home_dir);
-		if (reply->uid != master_uid && seteuid(master_uid) < 0)
-			i_fatal("seteuid(%s) failed: %m", dec2str(master_uid));
-
-		/* If user's home directory doesn't exist and we're not
-		   trying to chroot anywhere, fallback to /tmp as the mails
-		   could be stored elsewhere. */
-		if (ret < 0 && (errno != ENOENT || *chroot_dir != '\0')) {
-			i_fatal("chdir(%s) failed with uid %s: %m",
-				full_home_dir, dec2str(reply->uid));
-		}
-	}
-	if (ret < 0) {
-		/* We still have to change to some directory where we have
-		   rx-access. /tmp should exist everywhere. */
-		if (chdir("/tmp") < 0)
-			i_fatal("chdir(/tmp) failed: %m");
-	}
-
-	env_put("LOGGED_IN=1");
-	env_put(t_strconcat("HOME=", home_dir, NULL));
 	env_put(t_strconcat("MAIL_CACHE_FIELDS=",
 			    set->mail_cache_fields, NULL));
 	env_put(t_strconcat("MAIL_NEVER_CACHE_FIELDS=",
@@ -350,25 +229,209 @@
 	env_put(t_strdup_printf("MBOX_DOTLOCK_CHANGE_TIMEOUT=%u",
 				set->mbox_dotlock_change_timeout));
 
-	if (group->set->mail_use_modules &&
-	    group->set->mail_modules != NULL &&
-	    *group->set->mail_modules != '\0') {
+	if (set->mail_use_modules &&
+	    set->mail_modules != NULL && *set->mail_modules != '\0') {
 		env_put(t_strconcat("MODULE_DIR=",
-				    group->set->mail_modules, NULL));
+				    set->mail_modules, NULL));
 	}
 
 	/* user given environment - may be malicious. virtual_user comes from
 	   auth process, but don't trust that too much either. Some auth
 	   mechanism might allow leaving extra data there. */
-	if (*mail == '\0' && set->default_mail_env != NULL)
+	if ((mail == NULL || *mail == '\0') && set->default_mail_env != NULL)
 		mail = expand_mail_env(set->default_mail_env, var_expand_table);
+	env_put(t_strconcat("MAIL=", mail, NULL));
 
 	if (set->server->namespaces != NULL) {
 		env_put_namespace(set->server->namespaces,
 				  mail, var_expand_table);
 	}
+}
 
-	env_put(t_strconcat("MAIL=", mail, NULL));
+static void mail_process_exec_set(struct settings *set, const char *title)
+{
+	const char *executable, *p, *argv[4];
+	int i;
+
+	/* very simple argument splitting. */
+	i = 0;
+	argv[i++] = executable = t_strcut(set->mail_executable, ' ');
+	argv[i] = strchr(set->mail_executable, ' ');
+	if (argv[i] != NULL) {
+		argv[i]++;
+		i++;
+	}
+	if (title[0] != '\0')
+		argv[i++] = title;
+	argv[i] = NULL;
+
+	/* hide the path, it's ugly */
+	p = strrchr(argv[0], '/');
+	if (p != NULL) argv[0] = p+1;
+
+	execv(executable, (char **) argv);
+}
+
+void mail_process_exec(const char *protocol, const char *section)
+{
+	struct server_settings *server = settings_root;
+	const struct var_expand_table *var_expand_table;
+	struct settings *set;
+
+	if (section != NULL) {
+		for (; server != NULL; server = server->next) {
+			if (strcmp(server->name, section) == 0)
+				break;
+		}
+		if (server == NULL)
+			i_fatal("Section not found: '%s'", section);
+	}
+
+	if (strcmp(protocol, "imap") == 0)
+		set = server->imap;
+	else if (strcmp(protocol, "pop3") == 0)
+		set = server->pop3;
+	else
+		i_fatal("Unknown protocol: '%s'", protocol);
+
+	var_expand_table =
+		get_var_expand_table(protocol, getenv("USER"), getenv("HOME"),
+				     getenv("TCPLOCALIP"),
+				     getenv("TCPREMOTEIP"), getpid());
+
+	mail_process_set_environment(set, getenv("MAIL"), var_expand_table);
+	mail_process_exec_set(set, "");
+
+	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
+		       set->mail_executable);
+}
+
+int create_mail_process(struct login_group *group, int socket,
+			const struct ip_addr *local_ip,
+			const struct ip_addr *remote_ip,
+			struct auth_master_reply *reply, const char *data)
+{
+	struct settings *set = group->set;
+	const struct var_expand_table *var_expand_table;
+	const char *addr, *mail, *user, *chroot_dir, *home_dir, *full_home_dir;
+	char title[1024];
+	struct log_io *log;
+	string_t *str;
+	pid_t pid;
+	int i, err, ret, log_fd;
+
+	// FIXME: per-group
+	if (mail_process_count == set->max_mail_processes) {
+		i_error("Maximum number of mail processes exceeded");
+		return FALSE;
+	}
+
+	if (!validate_uid_gid(set, reply->uid, reply->gid,
+			      data + reply->virtual_user_idx))
+		return FALSE;
+
+	user = data + reply->virtual_user_idx;
+	mail = data + reply->mail_idx;
+	home_dir = data + reply->home_idx;
+	chroot_dir = data + reply->chroot_idx;
+
+	if (*chroot_dir == '\0' && set->mail_chroot != NULL)
+		chroot_dir = set->mail_chroot;
+
+	if (*chroot_dir != '\0' && !validate_chroot(set, chroot_dir)) {
+		i_error("Invalid chroot directory '%s' (user %s) "
+			"(see valid_chroot_dirs in config file)",
+			chroot_dir, user);
+		return FALSE;
+	}
+
+	log_fd = log_create_pipe(&log);
+
+	pid = fork();
+	if (pid < 0) {
+		i_error("fork() failed: %m");
+		(void)close(log_fd);
+		return FALSE;
+	}
+
+	var_expand_table =
+		get_var_expand_table(process_names[group->process_type],
+				     user, home_dir,
+				     net_ip2addr(local_ip),
+				     net_ip2addr(remote_ip),
+				     pid != 0 ? pid : getpid());
+	str = t_str_new(128);
+
+	if (pid != 0) {
+		/* master */
+		var_expand(str, set->mail_log_prefix, var_expand_table);
+		log_set_prefix(log, str_c(str));
+
+		mail_process_count++;
+		PID_ADD_PROCESS_TYPE(pid, group->process_type);
+		(void)close(log_fd);
+		return TRUE;
+	}
+
+	str_append(str, "master-");
+	var_expand(str, set->mail_log_prefix, var_expand_table);
+	log_set_prefix(log, str_c(str));
+
+	child_process_init_env();
+
+	/* move the client socket into stdin and stdout fds, log to stderr */
+	if (dup2(socket, 0) < 0)
+		i_fatal("dup2(stdin) failed: %m");
+	if (dup2(socket, 1) < 0)
+		i_fatal("dup2(stdout) failed: %m");
+	if (dup2(log_fd, 2) < 0)
+		i_fatal("dup2(stderr) failed: %m");
+
+	for (i = 0; i < 3; i++)
+		fd_close_on_exec(i, FALSE);
+
+	/* setup environment - set the most important environment first
+	   (paranoia about filling up environment without noticing) */
+	restrict_access_set_env(data + reply->system_user_idx,
+				reply->uid, reply->gid, chroot_dir,
+				set->first_valid_gid, set->last_valid_gid,
+				set->mail_extra_groups);
+
+	restrict_process_size(set->mail_process_size, (unsigned int)-1);
+
+	if (*home_dir == '\0')
+		ret = -1;
+	else {
+		full_home_dir = *chroot_dir == '\0' ? home_dir :
+			t_strconcat(chroot_dir, "/", home_dir, NULL);
+		/* NOTE: if home directory is NFS-mounted, we might not
+		   have access to it as root. Change the effective UID
+		   temporarily to make it work. */
+		if (reply->uid != master_uid && seteuid(reply->uid) < 0)
+			i_fatal("seteuid(%s) failed: %m", dec2str(reply->uid));
+		ret = chdir(full_home_dir);
+		if (reply->uid != master_uid && seteuid(master_uid) < 0)
+			i_fatal("seteuid(%s) failed: %m", dec2str(master_uid));
+
+		/* If user's home directory doesn't exist and we're not
+		   trying to chroot anywhere, fallback to /tmp as the mails
+		   could be stored elsewhere. */
+		if (ret < 0 && (errno != ENOENT || *chroot_dir != '\0')) {
+			i_fatal("chdir(%s) failed with uid %s: %m",
+				full_home_dir, dec2str(reply->uid));
+		}
+	}
+	if (ret < 0) {
+		/* We still have to change to some directory where we have
+		   rx-access. /tmp should exist everywhere. */
+		if (chdir("/tmp") < 0)
+			i_fatal("chdir(/tmp) failed: %m");
+	}
+
+        mail_process_set_environment(set, mail, var_expand_table);
+
+	env_put("LOGGED_IN=1");
+	env_put(t_strconcat("HOME=", home_dir, NULL));
 	env_put(t_strconcat("USER=", data + reply->virtual_user_idx, NULL));
 
 	addr = net_ip2addr(remote_ip);
@@ -391,23 +454,7 @@
 	if (set->mail_drop_priv_before_exec)
 		restrict_access_by_env(TRUE);
 
-	/* very simple argument splitting. */
-	i = 0;
-	argv[i++] = executable = t_strcut(group->set->mail_executable, ' ');
-	argv[i] = strchr(group->set->mail_executable, ' ');
-	if (argv[i] != NULL) {
-		argv[i]++;
-		i++;
-	}
-	if (title[0] != '\0')
-		argv[i++] = title;
-	argv[i] = NULL;
-
-	/* hide the path, it's ugly */
-	p = strrchr(argv[0], '/');
-	if (p != NULL) argv[0] = p+1;
-
-	execv(executable, (char **) argv);
+	mail_process_exec_set(set, title);
 	err = errno;
 
 	for (i = 0; i < 3; i++)
@@ -415,7 +462,7 @@
 
 	errno = err;
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
-		       group->set->mail_executable);
+		       set->mail_executable);
 
 	/* not reached */
 	return FALSE;
--- a/src/master/mail-process.h	Fri Jul 09 22:22:15 2004 +0300
+++ b/src/master/mail-process.h	Fri Jul 09 22:59:02 2004 +0300
@@ -4,6 +4,8 @@
 struct login_group;
 struct auth_master_reply;
 
+void mail_process_exec(const char *protocol, const char *section);
+
 int create_mail_process(struct login_group *group, int socket,
 			const struct ip_addr *local_ip,
 			const struct ip_addr *remote_ip,
--- a/src/master/main.c	Fri Jul 09 22:22:15 2004 +0300
+++ b/src/master/main.c	Fri Jul 09 22:59:02 2004 +0300
@@ -99,7 +99,7 @@
         login_processes_destroy_all();
         auth_processes_destroy_all();
 
-	if (!master_settings_read(configfile))
+	if (!master_settings_read(configfile, FALSE))
 		i_warning("Invalid configuration, keeping old one");
 	else {
 		listen_fds_close(old_set);
@@ -482,9 +482,13 @@
 int main(int argc, char *argv[])
 {
 	/* parse arguments */
+	const char *exec_protocol = NULL, *exec_section = NULL;
 	int foreground = FALSE;
 	int i;
 
+#ifdef DEBUG
+	gdb = getenv("GDB") != NULL;
+#endif
 	lib_init();
 
 	master_uid = geteuid();
@@ -498,13 +502,14 @@
 			i++;
 			if (i == argc) i_fatal("Missing config file argument");
 			configfile = argv[i];
-		} else if (strcmp(argv[i], "--inetd") == 0) {
-			/* starting through inetd. */
-			inetd_login_fd = dup(0);
-			if (inetd_login_fd == -1)
-				i_fatal("dup(0) failed: %m");
-			fd_close_on_exec(inetd_login_fd, TRUE);
-			foreground = TRUE;
+		} else if (strcmp(argv[i], "--exec-mail") == 0) {
+			/* <protocol> [<server section>]
+			   read configuration and execute mail process */
+			i++;
+			if (i == argc) i_fatal("Missing protocol argument");
+			exec_protocol = argv[i];
+			if (i+1 != argc) 
+				exec_section = argv[i++];
 		} else if (strcmp(argv[i], "--version") == 0) {
 			printf("%s\n", VERSION);
 			return 0;
@@ -514,18 +519,28 @@
 		}
 	}
 
+	if (getenv("DOVECOT_INETD") != NULL) {
+		/* starting through inetd. */
+		inetd_login_fd = dup(0);
+		if (inetd_login_fd == -1)
+			i_fatal("dup(0) failed: %m");
+		fd_close_on_exec(inetd_login_fd, TRUE);
+		foreground = TRUE;
+	}
+
 	/* read and verify settings before forking */
 	master_settings_init();
-	if (!master_settings_read(configfile))
+	if (!master_settings_read(configfile, exec_protocol != NULL))
 		exit(FATAL_DEFAULT);
-	open_fds();
 
-#ifdef DEBUG
-	gdb = getenv("GDB") != NULL;
-#endif
-	/* we don't need any environment */
+	if (exec_protocol != NULL)
+		mail_process_exec(exec_protocol, exec_section);
+
+	/* we don't need any environment anymore */
 	env_clean();
 
+	open_fds();
+
 	if (!foreground)
 		daemonize(settings_root->defaults);
 
--- a/src/master/master-settings.c	Fri Jul 09 22:22:15 2004 +0300
+++ b/src/master/master-settings.c	Fri Jul 09 22:59:02 2004 +0300
@@ -908,7 +908,7 @@
 	return FALSE;
 }
 
-int master_settings_read(const char *path)
+int master_settings_read(const char *path, int nochecks)
 {
 	struct settings_parse_ctx ctx;
 	struct server_settings *server, *prev;
@@ -949,7 +949,7 @@
 		if (!settings_is_active(server->imap))
 			server->imap = NULL;
 		else {
-			if (!settings_verify(server->imap))
+			if (!nochecks && !settings_verify(server->imap))
 				return FALSE;
 			server->defaults = server->imap;
 		}
@@ -957,7 +957,7 @@
 		if (!settings_is_active(server->pop3))
 			server->pop3 = NULL;
 		else {
-			if (!settings_verify(server->pop3))
+			if (!nochecks && !settings_verify(server->pop3))
 				return FALSE;
 			if (server->defaults == NULL)
 				server->defaults = server->pop3;
@@ -976,14 +976,16 @@
 				return FALSE;
 			}
 
-			for (; auth != NULL; auth = auth->next) {
-				if (!auth_settings_verify(auth))
-					return FALSE;
-			}
-                        ns = server->namespaces;
-			for (; ns != NULL; ns = ns->next) {
-				if (!namespace_settings_verify(ns))
-					return FALSE;
+			if (!nochecks) {
+				for (; auth != NULL; auth = auth->next) {
+					if (!auth_settings_verify(auth))
+						return FALSE;
+				}
+				ns = server->namespaces;
+				for (; ns != NULL; ns = ns->next) {
+					if (!namespace_settings_verify(ns))
+						return FALSE;
+				}
 			}
 			prev = server;
 		}
--- a/src/master/master-settings.h	Fri Jul 09 22:22:15 2004 +0300
+++ b/src/master/master-settings.h	Fri Jul 09 22:59:02 2004 +0300
@@ -171,7 +171,7 @@
 
 extern struct server_settings *settings_root;
 
-int master_settings_read(const char *path);
+int master_settings_read(const char *path, int nochecks);
 
 void master_settings_init(void);
 void master_settings_deinit(void);