changeset 801:86224ff16bf6 HEAD

Drop root privileges earlier. Close syslog more later in imap-master when forking new processes, so that any errors get logged. Make sure that all errors show up in log files - use specific exit status codes if we can't write to log file. Make sure imap and login processes always drop root privileges even if master process didn't ask for it for some reason. putenv() wasn't verified to succeed - luckily we never allowed large user given data there.
author Timo Sirainen <tss@iki.fi>
date Wed, 18 Dec 2002 06:00:01 +0200
parents db1608b43054
children 681cfbf19424
files src/auth/main.c src/imap/main.c src/lib/env-util.c src/lib/failures.c src/lib/failures.h src/lib/lib.c src/lib/process-title.c src/lib/restrict-access.c src/lib/restrict-access.h src/login/main.c src/master/auth-process.c src/master/common.h src/master/imap-process.c src/master/login-process.c src/master/main.c
diffstat 15 files changed, 273 insertions(+), 177 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/main.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/auth/main.c	Wed Dec 18 06:00:01 2002 +0200
@@ -38,33 +38,33 @@
 	}
 }
 
+static void open_logfile(void)
+{
+	if (getenv("IMAP_USE_SYSLOG") != NULL)
+		i_set_failure_syslog("imap-auth", LOG_NDELAY, LOG_MAIL);
+	else {
+		/* log to file or stderr */
+		i_set_failure_file(getenv("IMAP_LOGFILE"), "imap-auth");
+		i_set_failure_timestamp_format(getenv("IMAP_LOGSTAMP"));
+	}
+}
+
+static void drop_privileges(void)
+{
+	/* Log file or syslog opening probably requires roots */
+	open_logfile();
+
+	/* Open /dev/urandom before chrooting */
+	random_init();
+
+	/* Password lookups etc. may require roots, allow it. */
+	restrict_access_by_env(FALSE);
+}
+
 static void main_init(void)
 {
-	const char *logfile;
-
 	lib_init_signals(sig_quit);
 
-	logfile = getenv("IMAP_LOGFILE");
-	if (logfile == NULL) {
-		/* open the syslog immediately so chroot() won't
-		   break logging */
-		openlog("imap-auth", LOG_NDELAY, LOG_MAIL);
-
-		i_set_panic_handler(i_syslog_panic_handler);
-		i_set_fatal_handler(i_syslog_fatal_handler);
-		i_set_error_handler(i_syslog_error_handler);
-		i_set_warning_handler(i_syslog_warning_handler);
-	} else {
-		/* log failures into specified log file */
-		i_set_failure_file(logfile, "imap-auth");
-		i_set_failure_timestamp_format(getenv("IMAP_LOGSTAMP"));
-	}
-
-	/* open /dev/urandom before chrooting */
-	random_init();
-
-	restrict_access_by_env();
-
 	auth_init();
 	cookies_init();
 	login_connections_init();
@@ -100,6 +100,8 @@
 	/* NOTE: we start rooted, so keep the code minimal until
 	   restrict_access_by_env() is called */
 	lib_init();
+	drop_privileges();
+
 	ioloop = io_loop_create(system_pool);
 
 	main_init();
--- a/src/imap/main.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/imap/main.c	Wed Dec 18 06:00:01 2002 +0200
@@ -11,48 +11,60 @@
 #include <stdlib.h>
 #include <syslog.h>
 
+#define IS_STANDALONE() \
+        (getenv("LOGIN_TAG") == NULL)
+
 IOLoop ioloop;
-static char log_prefix[128];
+static char log_prefix[128]; /* syslog() needs this to be permanent */
 
 static void sig_quit(int signo __attr_unused__)
 {
 	io_loop_stop(ioloop);
 }
 
-static void main_init(int use_syslog)
+static void open_logfile(void)
+{
+	const char *user;
+
+	user = getenv("USER");
+	if (user == NULL) user = "??";
+	if (strlen(user) >= sizeof(log_prefix)-6) {
+		/* quite a long user name, cut it */
+		user = t_strndup(user, sizeof(log_prefix)-6-2);
+		user = t_strconcat(user, "..", NULL);
+	}
+	i_snprintf(log_prefix, sizeof(log_prefix), "imap(%s)", user);
+
+	if (getenv("IMAP_USE_SYSLOG") != NULL)
+		i_set_failure_syslog(log_prefix, LOG_NDELAY, LOG_MAIL);
+	else {
+		/* log to file or stderr */
+		i_set_failure_file(getenv("IMAP_LOGFILE"), log_prefix);
+		i_set_failure_timestamp_format(getenv("IMAP_LOGSTAMP"));
+	}
+}
+
+static void drop_privileges(void)
+{
+	/* Log file or syslog opening probably requires roots */
+	open_logfile();
+
+	restrict_access_by_env(!IS_STANDALONE());
+}
+
+static void main_init(void)
 {
 	Client *client;
 	MailStorage *storage;
-	const char *logfile, *mail, *tag;
+	const char *mail;
 	int hin, hout;
 
+	lib_init_signals(sig_quit);
+
 	if (getenv("USER") == NULL)
 		i_fatal("USER environment missing");
 
 	hin = 0; hout = 1;
-
-	lib_init_signals(sig_quit);
-
-	i_snprintf(log_prefix, sizeof(log_prefix), "imap(%s)", getenv("USER"));
-
-	logfile = getenv("IMAP_LOGFILE");
-	if (logfile != NULL) {
-		/* log failures into specified log file */
-		i_set_failure_file(logfile, log_prefix);
-		i_set_failure_timestamp_format(getenv("IMAP_LOGSTAMP"));
-	} else if (use_syslog) {
-		/* open the syslog while we still have access to it */
-		openlog(log_prefix, LOG_NDELAY, LOG_MAIL);
-
-		i_set_panic_handler(i_syslog_panic_handler);
-		i_set_fatal_handler(i_syslog_fatal_handler);
-		i_set_error_handler(i_syslog_error_handler);
-		i_set_warning_handler(i_syslog_warning_handler);
-	}
-
-	/* do the chrooting etc. */
-	restrict_access_by_env();
-
 	rawlog_open(&hin, &hout);
 
 	mail_storage_register_all();
@@ -84,14 +96,13 @@
 
 	client = client_create(hin, hout, storage);
 
-	tag = getenv("LOGIN_TAG");
-	if (tag == NULL || *tag == '\0') {
+	if (IS_STANDALONE()) {
 		client_send_line(client, t_strconcat(
 			"* PREAUTH [CAPABILITY "CAPABILITY_STRING"] "
 			"Logged in as ", getenv("USER"), NULL));
 	} else {
-		client_send_line(client,
-				 t_strconcat(tag, " OK Logged in.", NULL));
+		client_send_line(client, t_strconcat(getenv("LOGIN_TAG"),
+						     " OK Logged in.", NULL));
 	}
 }
 
@@ -107,7 +118,7 @@
 	closelog();
 }
 
-int main(int argc, char *argv[], char *envp[])
+int main(int argc __attr_unused__, char *argv[], char *envp[])
 {
 #ifdef DEBUG
 	if (getenv("LOGIN_TAG") != NULL)
@@ -116,10 +127,12 @@
 	/* NOTE: we start rooted, so keep the code minimal until
 	   restrict_access_by_env() is called */
 	lib_init();
+	drop_privileges();
+
         process_title_init(argv, envp);
 	ioloop = io_loop_create(system_pool);
 
-	main_init(argc >= 2 && strcmp(argv[1], "-s") == 0);
+	main_init();
         io_loop_run(ioloop);
 	main_deinit();
 
--- a/src/lib/env-util.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/lib/env-util.c	Wed Dec 18 06:00:01 2002 +0200
@@ -35,7 +35,8 @@
 	if (pool == NULL)
 		pool = pool_create("Environment", 1024, FALSE);
 
-	putenv(p_strdup(pool, env));
+	if (putenv(p_strdup(pool, env)) < 0)
+		i_fatal("Environment full, can't add: %s", env);
 }
 
 void env_clean(void)
--- a/src/lib/failures.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/lib/failures.c	Wed Dec 18 06:00:01 2002 +0200
@@ -35,7 +35,7 @@
 
 static void default_panic_handler(const char *format, va_list args)
 	__attr_noreturn__;
-static void default_fatal_handler(const char *format, va_list args)
+static void default_fatal_handler(int status, const char *format, va_list args)
 	__attr_noreturn__;
 
 static void default_error_handler(const char *format, va_list args);
@@ -43,7 +43,7 @@
 
 /* Initialize working defaults */
 static FailureFunc panic_handler __attr_noreturn__ = default_panic_handler;
-static FailureFunc fatal_handler __attr_noreturn__ = default_fatal_handler;
+static FatalFailureFunc fatal_handler __attr_noreturn__ = default_fatal_handler;
 static FailureFunc error_handler = default_error_handler;
 static FailureFunc warning_handler = default_warning_handler;
 
@@ -80,7 +80,7 @@
 	abort();
 }
 
-static void default_fatal_handler(const char *format, va_list args)
+static void default_fatal_handler(int status, const char *format, va_list args)
 {
 	write_prefix();
 
@@ -88,7 +88,10 @@
 	vfprintf(log_fd, printf_string_fix_format(format), args);
 	fputc('\n', log_fd);
 
-	exit(98);
+	if (fflush(log_fd) < 0 && status == FATAL_DEFAULT)
+		status = FATAL_LOGWRITE;
+
+	exit(status);
 }
 
 static void default_error_handler(const char *format, va_list args)
@@ -103,7 +106,8 @@
         fputc('\n', log_fd);
 	t_pop();
 
-	fflush(log_fd);
+	if (fflush(log_fd) < 0)
+		exit(FATAL_LOGWRITE);
 
 	errno = old_errno;
 }
@@ -120,7 +124,8 @@
 	fputc('\n', log_fd);
 	t_pop();
 
-	fflush(log_fd);
+	if (fflush(log_fd) < 0)
+		exit(FATAL_LOGWRITE);
 
 	errno = old_errno;
 }
@@ -139,7 +144,16 @@
 	va_list args;
 
 	va_start(args, format);
-	fatal_handler(format, args);
+	fatal_handler(FATAL_DEFAULT, format, args);
+	va_end(args);
+}
+
+void i_fatal_status(int status, const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	fatal_handler(status, format, args);
 	va_end(args);
 }
 
@@ -168,7 +182,7 @@
         panic_handler = func;
 }
 
-void i_set_fatal_handler(FailureFunc func __attr_noreturn__)
+void i_set_fatal_handler(FatalFailureFunc func __attr_noreturn__)
 {
 	if (func == NULL)
 		func = default_fatal_handler;
@@ -195,10 +209,10 @@
         abort();
 }
 
-void i_syslog_fatal_handler(const char *fmt, va_list args)
+void i_syslog_fatal_handler(int status, const char *fmt, va_list args)
 {
 	vsyslog(LOG_CRIT, fmt, args);
-	exit(98);
+	exit(status);
 }
 
 void i_syslog_error_handler(const char *fmt, va_list args)
@@ -211,18 +225,34 @@
 	vsyslog(LOG_WARNING, fmt, args);
 }
 
+void i_set_failure_syslog(const char *ident, int options, int facility)
+{
+	openlog(ident, options, facility);
+
+	i_set_panic_handler(i_syslog_panic_handler);
+	i_set_fatal_handler(i_syslog_fatal_handler);
+	i_set_error_handler(i_syslog_error_handler);
+	i_set_warning_handler(i_syslog_warning_handler);
+}
+
 void i_set_failure_file(const char *path, const char *prefix)
 {
 	if (log_fd != NULL && log_fd != stderr)
 		(void)fclose(log_fd);
 
-	log_fd = fopen(path, "a");
-	if (log_fd == NULL)
-		i_fatal("Can't open log file %s: %m", path);
-	fd_close_on_exec(fileno(log_fd), TRUE);
-
 	i_free(log_prefix);
 	log_prefix = i_strconcat(prefix, ": ", NULL);
+
+	if (path == NULL)
+		log_fd = stderr;
+	else {
+		log_fd = fopen(path, "a");
+		if (log_fd == NULL) {
+			i_fatal_status(FATAL_LOGOPEN,
+				       "Can't open log file %s: %m", path);
+		}
+		fd_close_on_exec(fileno(log_fd), TRUE);
+	}
 }
 
 void i_set_failure_timestamp_format(const char *fmt)
@@ -231,10 +261,6 @@
         log_stamp_format = i_strdup(fmt);
 }
 
-void failures_init(void)
-{
-}
-
 void failures_deinit(void)
 {
 	if (log_fd != NULL && log_fd != stderr) {
--- a/src/lib/failures.h	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/lib/failures.h	Wed Dec 18 06:00:01 2002 +0200
@@ -1,34 +1,51 @@
 #ifndef __FAILURES_H
 #define __FAILURES_H
 
+/* Default exit status codes that we could use. */
+typedef enum {
+	FATAL_LOGOPEN	= 80, /* Can't open log file */
+	FATAL_LOGWRITE  = 81, /* Can't write to log file */
+	FATAL_OUTOFMEM	= 82, /* Out of memory */
+	FATAL_EXEC	= 83, /* exec() failed */
+
+	FATAL_DEFAULT	= 89
+} FatalExitStatus;
+
 #define DEFAULT_FAILURE_STAMP_FORMAT "%b %d %H:%M:%S "
 
 typedef void (*FailureFunc) (const char *, va_list);
+typedef void (*FatalFailureFunc) (int status, const char *, va_list);
 
 void i_panic(const char *format, ...) __attr_format__(1, 2) __attr_noreturn__;
 void i_fatal(const char *format, ...) __attr_format__(1, 2) __attr_noreturn__;
 void i_error(const char *format, ...) __attr_format__(1, 2);
 void i_warning(const char *format, ...) __attr_format__(1, 2);
 
+void i_fatal_status(int status, const char *format, ...)
+	__attr_format__(2, 3) __attr_noreturn__;
+
 /* Change failure handlers. Make sure they don't modify errno. */
 void i_set_panic_handler(FailureFunc func __attr_noreturn__);
-void i_set_fatal_handler(FailureFunc func __attr_noreturn__);
+void i_set_fatal_handler(FatalFailureFunc func __attr_noreturn__);
 void i_set_error_handler(FailureFunc func);
 void i_set_warning_handler(FailureFunc func);
 
-/* send failures to syslog() */
+/* Send failures to syslog() */
 void i_syslog_panic_handler(const char *fmt, va_list args) __attr_noreturn__;
-void i_syslog_fatal_handler(const char *fmt, va_list args) __attr_noreturn__;
+void i_syslog_fatal_handler(int status, const char *fmt, va_list args)
+	__attr_noreturn__;
 void i_syslog_error_handler(const char *fmt, va_list args);
 void i_syslog_warning_handler(const char *fmt, va_list args);
 
-/* send failures to specified log file instead of stderr. */
+/* Open syslog and set failure handlers to use it. */
+void i_set_failure_syslog(const char *ident, int options, int facility);
+
+/* Send failures to specified log file instead of stderr. */
 void i_set_failure_file(const char *path, const char *prefix);
 
-/* prefix failures with a timestamp. fmt is in strftime() format. */
+/* Prefix failures with a timestamp. fmt is in strftime() format. */
 void i_set_failure_timestamp_format(const char *fmt);
 
-void failures_init(void);
 void failures_deinit(void);
 
 #endif
--- a/src/lib/lib.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/lib/lib.c	Wed Dec 18 06:00:01 2002 +0200
@@ -44,7 +44,6 @@
 	/* standard way to get rand() return different values. */
 	srand((unsigned int) time(NULL));
 
-	failures_init();
 	data_stack_init();
 	imem_init();
 }
--- a/src/lib/process-title.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/lib/process-title.c	Wed Dec 18 06:00:01 2002 +0200
@@ -48,12 +48,12 @@
 		;
 
 	if ((p = malloc((i + 1) * sizeof(char *))) == NULL)
-		i_fatal("malloc() failed: %m");
+		i_panic("malloc() failed: %m");
 	environ = p;
 
 	for (i = 0; envp[i] != NULL; i++) {
 		if ((environ[i] = malloc(strlen(envp[i]) + 1)) == NULL)
-			i_fatal("malloc() failed: %m");
+			i_panic("malloc() failed: %m");
 
 		strcpy(environ[i], envp[i]);
 	}
--- a/src/lib/restrict-access.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/lib/restrict-access.c	Wed Dec 18 06:00:01 2002 +0200
@@ -42,7 +42,7 @@
 	env_put(t_strdup_printf("RESTRICT_SETGID=%ld", (long) gid));
 }
 
-void restrict_access_by_env(void)
+void restrict_access_by_env(int disallow_root)
 {
 	const char *env;
 	gid_t gid;
@@ -89,9 +89,16 @@
 	if (uid != 0) {
 		if (setuid(uid) != 0)
 			i_fatal("setuid(%ld) failed: %m", (long) uid);
+	}
 
-		/* just extra verification */
+	/* verify that we actually dropped the privileges */
+	if (uid != 0 || disallow_root) {
 		if (setuid(0) == 0)
 			i_fatal("We couldn't drop root privileges");
 	}
+
+	if (gid != 0 || disallow_root) {
+		if (getgid() == 0 || getegid() == 0 || setgid(0) == 0)
+			i_fatal("We couldn't drop root group privileges");
+	}
 }
--- a/src/lib/restrict-access.h	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/lib/restrict-access.h	Wed Dec 18 06:00:01 2002 +0200
@@ -6,7 +6,9 @@
 void restrict_access_set_env(const char *user, uid_t uid, gid_t gid,
 			     const char *chroot_dir);
 
-/* chroot, setuid() and setgid() based on environment variables */
-void restrict_access_by_env(void);
+/* chroot, setuid() and setgid() based on environment variables.
+   If disallow_roots is TRUE, we'll kill ourself if we didn't have the
+   environment settings and we have root uid or gid. */
+void restrict_access_by_env(int disallow_root);
 
 #endif
--- a/src/login/main.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/login/main.c	Wed Dec 18 06:00:01 2002 +0200
@@ -115,28 +115,37 @@
 	}
 }
 
+static void open_logfile(void)
+{
+	if (getenv("IMAP_USE_SYSLOG") != NULL)
+		i_set_failure_syslog("imap-login", LOG_NDELAY, LOG_MAIL);
+	else {
+		/* log to file or stderr */
+		i_set_failure_file(getenv("IMAP_LOGFILE"), "imap-login");
+		i_set_failure_timestamp_format(getenv("IMAP_LOGSTAMP"));
+	}
+}
+
+static void drop_privileges(void)
+{
+	/* Log file or syslog opening probably requires roots */
+	open_logfile();
+
+	/* Initialize SSL proxy so it can read certificate and private
+	   key file. */
+	ssl_proxy_init();
+
+	/* Refuse to run as root - we should never need it and it's
+	   dangerous with SSL. */
+	restrict_access_by_env(TRUE);
+}
+
 static void main_init(void)
 {
-	const char *logfile, *value;
+	const char *value;
 
 	lib_init_signals(sig_quit);
 
-	logfile = getenv("IMAP_LOGFILE");
-	if (logfile == NULL) {
-		/* open the syslog immediately so chroot() won't
-		   break logging */
-		openlog("imap-login", LOG_NDELAY, LOG_MAIL);
-
-		i_set_panic_handler(i_syslog_panic_handler);
-		i_set_fatal_handler(i_syslog_fatal_handler);
-		i_set_error_handler(i_syslog_error_handler);
-		i_set_warning_handler(i_syslog_warning_handler);
-	} else {
-		/* log failures into specified log file */
-		i_set_failure_file(logfile, "imap-login");
-		i_set_failure_timestamp_format(getenv("IMAP_LOGSTAMP"));
-	}
-
 	disable_plaintext_auth = getenv("DISABLE_PLAINTEXT_AUTH") != NULL;
 	process_per_connection = getenv("PROCESS_PER_CONNECTION") != NULL;
         verbose_proctitle = getenv("VERBOSE_PROCTITLE") != NULL;
@@ -147,12 +156,6 @@
         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();
-
-	restrict_access_by_env();
-
 	auth_connection_init();
 	master_init();
 	clients_init();
@@ -204,7 +207,9 @@
 	/* NOTE: we start rooted, so keep the code minimal until
 	   restrict_access_by_env() is called */
 	lib_init();
-        process_title_init(argv, envp);
+	drop_privileges();
+
+	process_title_init(argv, envp);
 	ioloop = io_loop_create(system_pool);
 
 	main_init();
--- a/src/master/auth-process.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/master/auth-process.c	Wed Dec 18 06:00:01 2002 +0200
@@ -13,6 +13,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <pwd.h>
+#include <syslog.h>
 #include <sys/stat.h>
 
 typedef struct _WaitingRequest WaitingRequest;
@@ -255,13 +256,17 @@
 
 	restrict_process_size(config->process_size);
 
+	/* make sure we don't leak syslog fd, but do it last so that
+	   any errors above will be logged */
+	closelog();
+
 	/* hide the path, it's ugly */
 	argv[0] = strrchr(config->executable, '/');
 	if (argv[0] == NULL) argv[0] = config->executable; else argv[0]++;
 
 	execv(config->executable, (char **) argv);
 
-	i_fatal("execv(%s) failed: %m", argv[0]);
+	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", argv[0]);
 	return -1;
 }
 
--- a/src/master/common.h	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/master/common.h	Wed Dec 18 06:00:01 2002 +0200
@@ -37,7 +37,8 @@
 				      const char *system_user,
 				      const char *virtual_user,
 				      uid_t uid, gid_t gid, const char *home,
-				      int chroot, const char *env[]);
+				      int chroot, const char *mail,
+				      const char *login_tag);
 void imap_process_destroyed(pid_t pid);
 
 /* misc */
--- a/src/master/imap-process.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/master/imap-process.c	Wed Dec 18 06:00:01 2002 +0200
@@ -8,9 +8,9 @@
 
 #include <stdlib.h>
 #include <unistd.h>
-#include <sys/stat.h>
+#include <grp.h>
 #include <syslog.h>
-#include <grp.h>
+#include <sys/stat.h>
 
 static unsigned int imap_process_count = 0;
 
@@ -105,12 +105,13 @@
 				      const char *system_user,
 				      const char *virtual_user,
 				      uid_t uid, gid_t gid, const char *home,
-				      int chroot, const char *env[])
+				      int chroot, const char *mail,
+				      const char *login_tag)
 {
-	static char *argv[] = { NULL, "-s", NULL, NULL };
+	static char *argv[] = { NULL, NULL, NULL };
 	char host[MAX_IP_LEN], title[1024];
 	pid_t pid;
-	int i, j, err, found_mail;
+	int i, j, err;
 
 	if (imap_process_count == set_max_imap_processes) {
 		i_error("Maximum number of imap processes exceeded");
@@ -151,28 +152,11 @@
 	}
 	(void)close(socket);
 
-	/* setup environment */
-        found_mail = FALSE;
-	for (; env[0] != NULL && env[1] != NULL; env += 2) {
-		if (strcmp(env[0], "MAIL") == 0) {
-			if (env[1] == NULL || *env[1] == '\0')
-				continue;
-
-			found_mail = TRUE;
-		}
+	/* setup environment - set the most important environment first
+	   (paranoia about filling up environment without noticing) */
+	restrict_access_set_env(system_user, uid, gid, chroot ? home : NULL);
+	restrict_process_size(set_imap_process_size);
 
-		env_put(t_strconcat(env[0], "=", env[1], NULL));
-	}
-
-	if (!found_mail && set_default_mail_env != NULL) {
-		const char *mail;
-
-		mail = expand_mail_env(set_default_mail_env,
-				       virtual_user, home);
-		env_put(t_strconcat("MAIL=", mail, NULL));
-	}
-
-	env_put(t_strconcat("USER=", virtual_user, NULL));
 	env_put(t_strconcat("HOME=", home, NULL));
 	env_put(t_strconcat("MAIL_CACHE_FIELDS=", set_mail_cache_fields, NULL));
 	env_put(t_strconcat("MAIL_NEVER_CACHE_FIELDS=",
@@ -200,16 +184,27 @@
 	if (set_mbox_read_dotlock)
 		env_put("MBOX_READ_DOTLOCK=1");
 
-	if (set_verbose_proctitle && net_ip2host(ip, host) == 0) {
-		i_snprintf(title, sizeof(title), "[%s %s]", virtual_user, host);
-		argv[2] = title;
+	/* 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 == NULL && set_default_mail_env != NULL) {
+		mail = expand_mail_env(set_default_mail_env,
+				       virtual_user, home);
+		env_put(t_strconcat("MAIL=", mail, NULL));
 	}
 
-	/* setup access environment - needs to be done after
-	   clean_child_process() since it clears environment */
-	restrict_access_set_env(system_user, uid, gid, chroot ? home : NULL);
+	env_put(t_strconcat("MAIL=", mail, NULL));
+	env_put(t_strconcat("USER=", virtual_user, NULL));
+	env_put(t_strconcat("LOGIN_TAG=", login_tag, NULL));
 
-	restrict_process_size(set_imap_process_size);
+	if (set_verbose_proctitle && net_ip2host(ip, host) == 0) {
+		i_snprintf(title, sizeof(title), "[%s %s]", virtual_user, host);
+		argv[1] = title;
+	}
+
+	/* make sure we don't leak syslog fd, but do it last so that
+	   any errors above will be logged */
+	closelog();
 
 	/* hide the path, it's ugly */
 	argv[0] = strrchr(set_imap_executable, '/');
@@ -221,7 +216,7 @@
 	for (i = 0; i < 3; i++)
 		(void)close(i);
 
-	i_fatal("execv(%s) failed: %m", set_imap_executable);
+	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", set_imap_executable);
 
 	/* not reached */
 	return 0;
--- a/src/master/login-process.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/master/login-process.c	Wed Dec 18 06:00:01 2002 +0200
@@ -53,18 +53,10 @@
 
 static void auth_callback(AuthCookieReplyData *cookie_reply, void *context)
 {
-	const char *env[] = {
-		"MAIL", NULL,
-		"LOGIN_TAG", NULL,
-		NULL
-	};
 	LoginAuthRequest *request = context;
         LoginProcess *process;
 	MasterReply reply;
 
-	env[1] = cookie_reply->mail;
-	env[3] = request->login_tag;
-
 	if (cookie_reply == NULL || !cookie_reply->success)
 		reply.result = MASTER_RESULT_FAILURE;
 	else {
@@ -75,7 +67,9 @@
 						   cookie_reply->uid,
 						   cookie_reply->gid,
 						   cookie_reply->home,
-						   cookie_reply->chroot, env);
+						   cookie_reply->chroot,
+						   cookie_reply->mail,
+						   request->login_tag);
 	}
 
 	/* reply to login */
@@ -331,13 +325,17 @@
 
 	restrict_process_size(set_login_process_size);
 
+	/* make sure we don't leak syslog fd, but do it last so that
+	   any errors above will be logged */
+	closelog();
+
 	/* hide the path, it's ugly */
 	argv[0] = strrchr(set_login_executable, '/');
 	if (argv[0] == NULL) argv[0] = set_login_executable; else argv[0]++;
 
 	execv(set_login_executable, (char **) argv);
 
-	i_fatal("execv(%s) failed: %m", argv[0]);
+	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", argv[0]);
 	return -1;
 }
 
--- a/src/master/main.c	Wed Dec 18 04:23:13 2002 +0200
+++ b/src/master/main.c	Wed Dec 18 06:00:01 2002 +0200
@@ -54,10 +54,11 @@
 	/* set the failure log */
 	if (set_log_path != NULL)
 		env_put(t_strconcat("IMAP_LOGFILE=", set_log_path, NULL));
+	else
+		env_put("IMAP_USE_SYSLOG=1");
+
 	if (set_log_timestamp != NULL)
 		env_put(t_strconcat("IMAP_LOGSTAMP=", set_log_timestamp, NULL));
-
-	closelog();
 }
 
 static void sig_quit(int signo __attr_unused__)
@@ -76,10 +77,29 @@
         auth_processes_destroy_all();
 }
 
+static const char *get_exit_status_message(FatalExitStatus status)
+{
+	switch (status) {
+	case FATAL_LOGOPEN:
+		return "Can't open log file";
+	case FATAL_LOGWRITE:
+		return "Can't write to log file";
+	case FATAL_OUTOFMEM:
+		return "Out of memory";
+	case FATAL_EXEC:
+		return "exec() failed";
+
+	case FATAL_DEFAULT:
+		return NULL;
+	}
+
+	return NULL;
+}
+
 static void timeout_handler(void *context __attr_unused__,
 			    Timeout timeout __attr_unused__)
 {
-	const char *process_type_name;
+	const char *process_type_name, *msg;
 	pid_t pid;
 	int status, process_type;
 
@@ -104,8 +124,12 @@
 			status = WEXITSTATUS(status);
 			if (status != 0) {
 				login_process_abormal_exit(pid);
-				i_error("child %d (%s) returned error %d",
-					(int)pid, process_type_name, status);
+				msg = get_exit_status_message(status);
+				if (msg != NULL)
+					msg = t_strconcat(" (", msg, ")", NULL);
+				i_error("child %d (%s) returned error %d%s",
+					(int)pid, process_type_name,
+					status, msg);
 			}
 		} else if (WIFSIGNALED(status)) {
 			login_process_abormal_exit(pid);
@@ -182,25 +206,25 @@
 	fd_close_on_exec(imaps_fd, TRUE);
 }
 
+static void open_logfile(void)
+{
+	if (set_log_path == NULL)
+		i_set_failure_syslog("imap-master", LOG_NDELAY, LOG_MAIL);
+	else {
+		/* log to file or stderr */
+		i_set_failure_file(set_log_path, "imap-master");
+		i_set_failure_timestamp_format(set_log_timestamp);
+	}
+}
+
 static void main_init(void)
 {
-	lib_init_signals(sig_quit);
-
 	/* deny file access from everyone else except owner */
         (void)umask(0077);
 
-	if (set_log_path == NULL) {
-		openlog("imap-master", LOG_NDELAY, LOG_MAIL);
+	open_logfile();
 
-		i_set_panic_handler(i_syslog_panic_handler);
-		i_set_fatal_handler(i_syslog_fatal_handler);
-		i_set_error_handler(i_syslog_error_handler);
-		i_set_warning_handler(i_syslog_warning_handler);
-	} else {
-		/* log failures into specified log file */
-		i_set_failure_file(set_log_path, "imap-master");
-		i_set_failure_timestamp_format(set_log_timestamp);
-	}
+	lib_init_signals(sig_quit);
 
 	pids = hash_create(default_pool, 128, NULL, NULL);
 	to = timeout_add(100, timeout_handler, NULL);
@@ -240,7 +264,8 @@
 	if (pid != 0)
 		_exit(0);
 
-	setsid();
+	if (setsid() < 0)
+		i_fatal("setsid() failed: %m");
 }
 
 static void print_help(void)