changeset 7109:e6823d781317 HEAD

Reverted "environment array" changes. It broke overriding imap/pop3 settings from userdb and the performance improvements weren't all that great anyway.
author Timo Sirainen <tss@iki.fi>
date Sat, 05 Jan 2008 01:28:46 +0200
parents fe74d0999e3a
children c3365e886746
files src/auth/main.c src/deliver/auth-client.c src/dict/main.c src/imap/main.c src/lib/env-util.c src/lib/env-util.h src/lib/restrict-access.c src/lib/restrict-access.h src/login-common/main.c src/master/auth-process.c src/master/child-process.c src/master/child-process.h src/master/dict-process.c src/master/login-process.c src/master/mail-process.c src/master/ssl-init.c src/plugins/expire/auth-client.c src/pop3/main.c src/util/rawlog.c
diffstat 19 files changed, 305 insertions(+), 401 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/main.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/auth/main.c	Sat Jan 05 01:28:46 2008 +0200
@@ -209,7 +209,7 @@
 		add_extra_listeners();
 
 	/* Password lookups etc. may require roots, allow it. */
-	restrict_access_by_env(NULL, FALSE);
+	restrict_access_by_env(FALSE);
 }
 
 static void main_init(bool nodaemon)
--- a/src/deliver/auth-client.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/deliver/auth-client.c	Sat Jan 05 01:28:46 2008 +0200
@@ -173,7 +173,7 @@
 				    extra_groups, NULL));
 	}
 
-	restrict_access_by_env(NULL, TRUE);
+	restrict_access_by_env(TRUE);
 	return_value = EX_OK;
 }
 
--- a/src/dict/main.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/dict/main.c	Sat Jan 05 01:28:46 2008 +0200
@@ -51,7 +51,7 @@
 	sql_drivers_init();
 	sql_drivers_register_all();
 
-	restrict_access_by_env(NULL, FALSE);
+	restrict_access_by_env(FALSE);
 }
 
 static void main_init(void)
--- a/src/imap/main.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/imap/main.c	Sat Jan 05 01:28:46 2008 +0200
@@ -157,7 +157,7 @@
 					  TRUE, version);
 	}
 
-	restrict_access_by_env(NULL, !IS_STANDALONE());
+	restrict_access_by_env(!IS_STANDALONE());
 }
 
 static void main_init(void)
--- a/src/lib/env-util.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/lib/env-util.c	Sat Jan 05 01:28:46 2008 +0200
@@ -1,7 +1,6 @@
 /* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "array.h"
 #include "env-util.h"
 
 #include <stdlib.h>
@@ -27,25 +26,3 @@
 	if (pool != NULL)
 		pool_unref(&pool);
 }
-
-void envarr_add(ARRAY_TYPE(const_string) *arr,
-		const char *key, const char *value)
-{
-	const char *str = t_strconcat(key, "=", value, NULL);
-
-	array_append(arr, &str, 1);
-}
-
-void envarr_addi(ARRAY_TYPE(const_string) *arr, const char *key,
-		 unsigned int value)
-{
-	char str[MAX_INT_STRLEN];
-
-	i_snprintf(str, sizeof(str), "%u", value);
-	envarr_add(arr, key, str);
-}
-
-void envarr_addb(ARRAY_TYPE(const_string) *arr, const char *key)
-{
-	envarr_add(arr, key, "1");
-}
--- a/src/lib/env-util.h	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/lib/env-util.h	Sat Jan 05 01:28:46 2008 +0200
@@ -7,12 +7,4 @@
 /* Clear all environment variables. */
 void env_clean(void);
 
-/* Append a string containing key=value to the array */
-void envarr_add(ARRAY_TYPE(const_string) *arr,
-		const char *key, const char *value);
-void envarr_addi(ARRAY_TYPE(const_string) *arr, const char *key,
-		 unsigned int value);
-/* Append a string containing key=1 to the array */
-void envarr_addb(ARRAY_TYPE(const_string) *arr, const char *key);
-
 #endif
--- a/src/lib/restrict-access.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/lib/restrict-access.c	Sat Jan 05 01:28:46 2008 +0200
@@ -1,7 +1,6 @@
 /* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "array.h"
 #include "restrict-access.h"
 #include "env-util.h"
 
@@ -10,54 +9,31 @@
 #include <time.h>
 #include <grp.h>
 
-enum restrict_env {
-	RESTRICT_ENV_USER,
-	RESTRICT_ENV_CHROOT,
-	RESTRICT_ENV_SETUID,
-	RESTRICT_ENV_SETGID,
-	RESTRICT_ENV_SETEXTRAGROUPS,
-	RESTRICT_ENV_GID_FIRST,
-	RESTRICT_ENV_GID_LAST,
-
-	RESTRICT_ENV_COUNT
-};
-
-static const char *restrict_env_strings[RESTRICT_ENV_COUNT] = {
-	"RESTRICT_USER",
-	"RESTRICT_CHROOT",
-	"RESTRICT_SETUID",
-	"RESTRICT_SETGID",
-	"RESTRICT_SETEXTRAGROUPS",
-	"RESTRICT_GID_FIRST",
-	"RESTRICT_GID_LAST"
-};
-
-static void renv_add(ARRAY_TYPE(const_string) *env, enum restrict_env key,
-		     const char *value)
-{
-	envarr_add(env, restrict_env_strings[key], value);
-}
-
-void restrict_access_set_env(ARRAY_TYPE(const_string) *env,
-			     const char *user, uid_t uid, gid_t gid,
+void restrict_access_set_env(const char *user, uid_t uid, gid_t gid,
 			     const char *chroot_dir,
 			     gid_t first_valid_gid, gid_t last_valid_gid,
 			     const char *extra_groups)
 {
 	if (user != NULL && *user != '\0')
-		renv_add(env, RESTRICT_ENV_USER, user);
+		env_put(t_strconcat("RESTRICT_USER=", user, NULL));
 	if (chroot_dir != NULL && *chroot_dir != '\0')
-		renv_add(env, RESTRICT_ENV_CHROOT, chroot_dir);
+		env_put(t_strconcat("RESTRICT_CHROOT=", chroot_dir, NULL));
 
-	renv_add(env, RESTRICT_ENV_SETUID, dec2str(uid));
-	renv_add(env, RESTRICT_ENV_SETGID, dec2str(gid));
-	if (extra_groups != NULL && *extra_groups != '\0')
-		renv_add(env, RESTRICT_ENV_SETEXTRAGROUPS, extra_groups);
+	env_put(t_strdup_printf("RESTRICT_SETUID=%s", dec2str(uid)));
+	env_put(t_strdup_printf("RESTRICT_SETGID=%s", dec2str(gid)));
+	if (extra_groups != NULL && *extra_groups != '\0') {
+		env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=",
+				    extra_groups, NULL));
+	}
 
-	if (first_valid_gid != 0)
-		renv_add(env, RESTRICT_ENV_GID_FIRST, dec2str(first_valid_gid));
-	if (last_valid_gid != 0)
-		renv_add(env, RESTRICT_ENV_GID_LAST, dec2str(last_valid_gid));
+	if (first_valid_gid != 0) {
+		env_put(t_strdup_printf("RESTRICT_GID_FIRST=%s",
+					dec2str(first_valid_gid)));
+	}
+	if (last_valid_gid != 0) {
+		env_put(t_strdup_printf("RESTRICT_GID_LAST=%s",
+					dec2str(last_valid_gid)));
+	}
 }
 
 static gid_t *get_groups_list(unsigned int *gid_count_r)
@@ -77,8 +53,7 @@
 	return gid_list;
 }
 
-static bool drop_restricted_groups(const char *const *env_values,
-				   gid_t *gid_list, unsigned int *gid_count,
+static bool drop_restricted_groups(gid_t *gid_list, unsigned int *gid_count,
 				   bool *have_root_group)
 {
 	/* @UNSAFE */
@@ -86,9 +61,9 @@
 	const char *env;
 	unsigned int i, used;
 
-	env = env_values[RESTRICT_ENV_GID_FIRST];
+	env = getenv("RESTRICT_GID_FIRST");
 	first_valid = env == NULL ? 0 : (gid_t)strtoul(env, NULL, 10);
-	env = env_values[RESTRICT_ENV_GID_LAST];
+	env = getenv("RESTRICT_GID_LAST");
 	last_valid = env == NULL ? (gid_t)-1 : (gid_t)strtoul(env, NULL, 10);
 
 	for (i = 0, used = 0; i < *gid_count; i++) {
@@ -118,20 +93,19 @@
 	return group->gr_gid;
 }
 
-static void fix_groups_list(const char *const *env_values, gid_t egid,
+static void fix_groups_list(const char *extra_groups, gid_t egid,
 			    bool preserve_existing, bool *have_root_group)
 {
 	gid_t *gid_list;
-	const char *const *tmp, *extra_groups, *empty = NULL;
+	const char *const *tmp, *empty = NULL;
 	unsigned int gid_count;
 
-	extra_groups = env_values[RESTRICT_ENV_SETEXTRAGROUPS];
 	tmp = extra_groups == NULL ? &empty :
 		t_strsplit_spaces(extra_groups, ", ");
 
 	if (preserve_existing) {
 		gid_list = get_groups_list(&gid_count);
-		if (!drop_restricted_groups(env_values, gid_list, &gid_count,
+		if (!drop_restricted_groups(gid_list, &gid_count,
 					    have_root_group) &&
 		    *tmp == NULL) {
 			/* nothing dropped, no extra groups to grant. */
@@ -161,40 +135,17 @@
 	}
 }
 
-void restrict_access_by_env(ARRAY_TYPE(const_string) *envarr,
-			    bool disallow_root)
+void restrict_access_by_env(bool disallow_root)
 {
-	const char *env_values[RESTRICT_ENV_COUNT], *const *envs, *env;
-	const char *home = NULL;
-	unsigned int i, j, count, len;
+	const char *env;
 	gid_t gid;
 	uid_t uid;
 	bool is_root, have_root_group, preserve_groups = FALSE;
 
-	if (envarr == NULL) {
-		/* use environment */
-		for (i = 0; i < RESTRICT_ENV_COUNT; i++)
-			env_values[i] = getenv(restrict_env_strings[i]);
-		home = getenv("HOME");
-	} else {
-		envs = array_get(envarr, &count);
-		memset(env_values, 0, sizeof(env_values));
-		for (i = 0; i < count; i++) {
-			for (j = 0; j < RESTRICT_ENV_COUNT; j++) {
-				len = strlen(restrict_env_strings[j]);
-				if (strncmp(envs[i], restrict_env_strings[j],
-					    len) == 0 &&
-				    envs[i][len] == '=')
-					env_values[j] = envs[i] + len + 1;
-			}
-			if (strncmp(envs[i], "HOME=", 5) == 0)
-				home = envs[i] + 5;
-		}
-	}
 	is_root = geteuid() == 0;
 
 	/* set the primary group */
-	env = env_values[RESTRICT_ENV_SETGID];
+	env = getenv("RESTRICT_SETGID");
 	gid = env == NULL || *env == '\0' ? (gid_t)-1 :
 		(gid_t)strtoul(env, NULL, 10);
 	have_root_group = gid == 0;
@@ -207,7 +158,7 @@
 	}
 
 	/* set system user's groups */
-	env = env_values[RESTRICT_ENV_USER];
+	env = getenv("RESTRICT_USER");
 	if (env != NULL && *env != '\0' && is_root) {
 		if (initgroups(env, gid) < 0) {
 			i_fatal("initgroups(%s, %s) failed: %m",
@@ -218,18 +169,20 @@
 
 	/* add extra groups. if we set system user's groups, drop the
 	   restricted groups at the same time. */
+	env = getenv("RESTRICT_SETEXTRAGROUPS");
 	if (is_root) {
 		T_FRAME(
-			fix_groups_list(env_values, gid, preserve_groups,
+			fix_groups_list(env, gid, preserve_groups,
 					&have_root_group);
 		);
 	}
 
 	/* chrooting */
-	env = env_values[RESTRICT_ENV_CHROOT];
+	env = getenv("RESTRICT_CHROOT");
 	if (env != NULL && *env != '\0') {
 		/* kludge: localtime() must be called before chroot(),
 		   or the timezone isn't known */
+		const char *home = getenv("HOME");
 		time_t t = 0;
 		(void)localtime(&t);
 
@@ -249,7 +202,7 @@
 	}
 
 	/* uid last */
-	env = env_values[RESTRICT_ENV_SETUID];
+	env = getenv("RESTRICT_SETUID");
 	uid = env == NULL || *env == '\0' ? 0 : (uid_t)strtoul(env, NULL, 10);
 	if (uid != 0) {
 		if (setuid(uid) != 0) {
@@ -267,7 +220,7 @@
 		}
 	}
 
-	env = env_values[RESTRICT_ENV_GID_FIRST];
+	env = getenv("RESTRICT_GID_FIRST");
 	if ((!have_root_group || (env != NULL && atoi(env) != 0)) && uid != 0) {
 		if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) {
 			if (gid == 0)
@@ -277,4 +230,13 @@
 				dec2str(getgid()), dec2str(getegid()));
 		}
 	}
+
+	/* clear the environment, so we don't fail if we get back here */
+	env_put("RESTRICT_USER=");
+	env_put("RESTRICT_CHROOT=");
+	env_put("RESTRICT_SETUID=");
+	env_put("RESTRICT_SETGID=");
+	env_put("RESTRICT_SETEXTRAGROUPS=");
+	env_put("RESTRICT_GID_FIRST=");
+	env_put("RESTRICT_GID_LAST=");
 }
--- a/src/lib/restrict-access.h	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/lib/restrict-access.h	Sat Jan 05 01:28:46 2008 +0200
@@ -3,17 +3,14 @@
 
 /* set environment variables so they can be read with
    restrict_access_by_env() */
-void restrict_access_set_env(ARRAY_TYPE(const_string) *env,
-			     const char *user, uid_t uid, gid_t gid,
+void restrict_access_set_env(const char *user, uid_t uid, gid_t gid,
 			     const char *chroot_dir,
 			     gid_t first_valid_gid, gid_t last_valid_gid,
 			     const char *extra_groups);
 
 /* 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. If env=NULL, the real
-   environment is used. */
-void restrict_access_by_env(ARRAY_TYPE(const_string) *env,
-			    bool disallow_root);
+   environment settings and we have root uid or gid. */
+void restrict_access_by_env(bool disallow_root);
 
 #endif
--- a/src/login-common/main.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/login-common/main.c	Sat Jan 05 01:28:46 2008 +0200
@@ -275,7 +275,7 @@
 
 	/* Refuse to run as root - we should never need it and it's
 	   dangerous with SSL. */
-	restrict_access_by_env(NULL, TRUE);
+	restrict_access_by_env(TRUE);
 
 	/* make sure we can't fork() */
 	restrict_process_size((unsigned int)-1, 1);
--- a/src/master/auth-process.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/master/auth-process.c	Sat Jan 05 01:28:46 2008 +0200
@@ -378,25 +378,18 @@
 }
 
 static void
-socket_settings_env_put(ARRAY_TYPE(const_string) *env,
-			const char *env_base, struct socket_settings *set)
+socket_settings_env_put(const char *env_base, struct socket_settings *set)
 {
 	if (!set->used)
 		return;
 
-	envarr_add(env, env_base, set->path);
-	if (set->mode != 0) {
-		envarr_add(env, t_strdup_printf("%s_MODE", env_base),
-			   t_strdup_printf("%o", set->mode));
-	}
-	if (*set->user != '\0') {
-		envarr_add(env, t_strdup_printf("%s_USER", env_base),
-			   set->user);
-	}
-	if (*set->group != '\0') {
-		envarr_add(env, t_strdup_printf("%s_GROUP", env_base),
-			   set->group);
-	}
+	env_put(t_strdup_printf("%s=%s", env_base, set->path));
+	if (set->mode != 0)
+		env_put(t_strdup_printf("%s_MODE=%o", env_base, set->mode));
+	if (*set->user != '\0')
+		env_put(t_strdup_printf("%s_USER=%s", env_base, set->user));
+	if (*set->group != '\0')
+		env_put(t_strdup_printf("%s_GROUP=%s", env_base, set->group));
 }
 
 static int connect_auth_socket(struct auth_process_group *group,
@@ -418,8 +411,7 @@
 	return 0;
 }
 
-static void auth_set_environment(ARRAY_TYPE(const_string) *env,
-				 struct auth_settings *set)
+static void auth_set_environment(struct auth_settings *set)
 {
 	struct auth_socket_settings *as;
 	struct auth_passdb_settings *ap;
@@ -428,46 +420,46 @@
 	int i;
 
 	/* setup access environment */
-	restrict_access_set_env(env, set->user, set->uid, set->gid, set->chroot,
+	restrict_access_set_env(set->user, set->uid, set->gid, set->chroot,
 				0, 0, NULL);
 
 	/* set other environment */
-	envarr_addb(env, "DOVECOT_MASTER");
-	envarr_add(env, "AUTH_NAME", set->name);
-	envarr_add(env, "MECHANISMS", set->mechanisms);
-	envarr_add(env, "REALMS", set->realms);
-	envarr_add(env, "DEFAULT_REALM", set->default_realm);
-	envarr_add(env, "USERNAME_CHARS", set->username_chars);
-	envarr_add(env, "ANONYMOUS_USERNAME", set->anonymous_username);
-	envarr_add(env, "USERNAME_TRANSLATION", set->username_translation);
-	envarr_add(env, "USERNAME_FORMAT", set->username_format);
-	envarr_add(env, "MASTER_USER_SEPARATOR", set->master_user_separator);
-	envarr_addi(env, "CACHE_SIZE", set->cache_size);
-	envarr_addi(env, "CACHE_TTL", set->cache_ttl);
-	envarr_addi(env, "CACHE_NEGATIVE_TTL", set->cache_negative_ttl);
+	env_put("DOVECOT_MASTER=1");
+	env_put(t_strconcat("AUTH_NAME=", set->name, NULL));
+	env_put(t_strconcat("MECHANISMS=", set->mechanisms, NULL));
+	env_put(t_strconcat("REALMS=", set->realms, NULL));
+	env_put(t_strconcat("DEFAULT_REALM=", set->default_realm, NULL));
+	env_put(t_strconcat("USERNAME_CHARS=", set->username_chars, NULL));
+	env_put(t_strconcat("ANONYMOUS_USERNAME=",
+			    set->anonymous_username, NULL));
+	env_put(t_strconcat("USERNAME_TRANSLATION=",
+			    set->username_translation, NULL));
+	env_put(t_strconcat("USERNAME_FORMAT=", set->username_format, NULL));
+	env_put(t_strconcat("MASTER_USER_SEPARATOR=",
+			    set->master_user_separator, NULL));
+	env_put(t_strdup_printf("CACHE_SIZE=%u", set->cache_size));
+	env_put(t_strdup_printf("CACHE_TTL=%u", set->cache_ttl));
+	env_put(t_strdup_printf("CACHE_NEGATIVE_TTL=%u",
+				set->cache_negative_ttl));
 
 	for (ap = set->passdbs, i = 1; ap != NULL; ap = ap->next, i++) {
-		envarr_add(env, t_strdup_printf("PASSDB_%u_DRIVER", i),
-			   ap->driver);
+		env_put(t_strdup_printf("PASSDB_%u_DRIVER=%s", i, ap->driver));
 		if (ap->args != NULL) {
-			envarr_add(env, t_strdup_printf("PASSDB_%u_ARGS", i),
-				   ap->args);
+			env_put(t_strdup_printf("PASSDB_%u_ARGS=%s",
+						i, ap->args));
 		}
 		if (ap->deny)
-			envarr_addb(env, t_strdup_printf("PASSDB_%u_DENY", i));
+			env_put(t_strdup_printf("PASSDB_%u_DENY=1", i));
                 if (ap->pass)
-			envarr_addb(env, t_strdup_printf("PASSDB_%u_PASS", i));
-		if (ap->master) {
-			envarr_addb(env,
-				    t_strdup_printf("PASSDB_%u_MASTER", i));
-		}
+                        env_put(t_strdup_printf("PASSDB_%u_PASS=1", i));
+		if (ap->master)
+                        env_put(t_strdup_printf("PASSDB_%u_MASTER=1", i));
 	}
 	for (au = set->userdbs, i = 1; au != NULL; au = au->next, i++) {
-		envarr_add(env, t_strdup_printf("USERDB_%u_DRIVER", i),
-			   au->driver);
+		env_put(t_strdup_printf("USERDB_%u_DRIVER=%s", i, au->driver));
 		if (au->args != NULL) {
-			envarr_add(env, t_strdup_printf("USERDB_%u_ARGS", i),
-				   au->args);
+			env_put(t_strdup_printf("USERDB_%u_ARGS=%s",
+						i, au->args));
 		}
 	}
 
@@ -476,31 +468,34 @@
 			continue;
 
 		str = t_strdup_printf("AUTH_%u", i);
-		socket_settings_env_put(env, str, &as->client);
-		socket_settings_env_put(env, t_strconcat(str, "_MASTER", NULL),
+		socket_settings_env_put(str, &as->client);
+		socket_settings_env_put(t_strconcat(str, "_MASTER", NULL),
 					&as->master);
 	}
 
 	if (set->verbose)
-		envarr_addb(env, "VERBOSE");
+		env_put("VERBOSE=1");
 	if (set->debug)
-		envarr_addb(env, "VERBOSE_DEBUG");
+		env_put("VERBOSE_DEBUG=1");
 	if (set->debug_passwords)
-		envarr_addb(env, "VERBOSE_DEBUG_PASSWORDS");
+		env_put("VERBOSE_DEBUG_PASSWORDS=1");
 	if (set->ssl_require_client_cert)
-		envarr_addb(env, "SSL_REQUIRE_CLIENT_CERT");
+		env_put("SSL_REQUIRE_CLIENT_CERT=1");
 	if (set->ssl_username_from_cert)
-		envarr_addb(env, "SSL_USERNAME_FROM_CERT");
+		env_put("SSL_USERNAME_FROM_CERT=1");
 	if (set->ntlm_use_winbind)
-		envarr_addb(env, "NTLM_USE_WINBIND");
+		env_put("NTLM_USE_WINBIND=1");
 	if (*set->krb5_keytab != '\0') {
 		/* Environment used by Kerberos 5 library directly */
-		envarr_add(env, "KRB5_KTNAME", set->krb5_keytab);
+		env_put(t_strconcat("KRB5_KTNAME=", set->krb5_keytab, NULL));
 	}
-	if (*set->gssapi_hostname != '\0')
-		envarr_add(env, "GSSAPI_HOSTNAME", set->gssapi_hostname);
-	envarr_add(env, "WINBIND_HELPER_PATH", set->winbind_helper_path);
-	envarr_addi(env, "FAILURE_DELAY", set->failure_delay);
+	if (*set->gssapi_hostname != '\0') {
+		env_put(t_strconcat("GSSAPI_HOSTNAME=",
+				    set->gssapi_hostname, NULL));
+	}
+	env_put(t_strconcat("WINBIND_HELPER_PATH=",
+			    set->winbind_helper_path, NULL));
+	env_put(t_strdup_printf("FAILURE_DELAY=%u", set->failure_delay));
 
 	restrict_process_size(set->process_size, (unsigned int)-1);
 }
@@ -512,7 +507,6 @@
 	struct log_io *log;
 	pid_t pid;
 	int fd[2], log_fd, i;
-	ARRAY_TYPE(const_string) env;
 
 	/* see if this is a connect socket */
 	as = group->set->sockets;
@@ -572,7 +566,7 @@
 	if (dup2(log_fd, 2) < 0)
 		i_fatal("dup2(stderr) failed: %m");
 
-	child_process_init_env(&env);
+	child_process_init_env();
 
 	if (group->listen_fd != 3) {
 		if (dup2(group->listen_fd, 3) < 0)
@@ -583,24 +577,23 @@
 	for (i = 0; i <= 2; i++)
 		fd_close_on_exec(i, FALSE);
 
-        auth_set_environment(&env, group->set);
+        auth_set_environment(group->set);
 
-	envarr_add(&env, "AUTH_WORKER_PATH",
-		   t_strdup_printf("%s/auth-worker.%s",
-				   *group->set->chroot != '\0' ? "" :
-				   group->set->parent->defaults->base_dir,
-				   dec2str(getpid())));
-	envarr_addi(&env, "AUTH_WORKER_MAX_COUNT",
-		    group->set->worker_max_count);
-	envarr_addi(&env, "AUTH_WORKER_MAX_REQUEST_COUNT",
-		    group->set->worker_max_request_count);
+	env_put(t_strdup_printf("AUTH_WORKER_PATH=%s/auth-worker.%s",
+				*group->set->chroot != '\0' ? "" :
+				group->set->parent->defaults->base_dir,
+				dec2str(getpid())));
+	env_put(t_strdup_printf("AUTH_WORKER_MAX_COUNT=%u",
+				group->set->worker_max_count));
+	env_put(t_strdup_printf("AUTH_WORKER_MAX_REQUEST_COUNT=%u",
+				group->set->worker_max_request_count));
 
 	/* make sure we don't leak syslog fd, but do it last so that
 	   any errors above will be logged */
 	closelog();
 
 	executable = group->set->executable;
-	client_process_exec(executable, "", &env);
+	client_process_exec(executable, "");
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
 	return -1;
 }
@@ -611,7 +604,6 @@
 	const char *prefix, *executable;
 	pid_t pid;
 	int log_fd, i;
-	ARRAY_TYPE(const_string) env;
 
 	log_fd = log_create_pipe(&log, 0);
 	if (log_fd < 0)
@@ -659,15 +651,15 @@
 		fd_close_on_exec(i, FALSE);
 	fd_close_on_exec(4, FALSE);
 
-	child_process_init_env(&env);
-        auth_set_environment(&env, process->group->set);
+	child_process_init_env();
+        auth_set_environment(process->group->set);
 
 	/* make sure we don't leak syslog fd, but do it last so that
 	   any errors above will be logged */
 	closelog();
 
 	executable = t_strconcat(process->group->set->executable, " -w", NULL);
-	client_process_exec(executable, "", &env);
+	client_process_exec(executable, "");
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
 	return -1;
 }
--- a/src/master/child-process.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/master/child-process.c	Sat Jan 05 01:28:46 2008 +0200
@@ -2,7 +2,6 @@
 
 #include "common.h"
 #include "lib-signals.h"
-#include "array.h"
 #include "hash.h"
 #include "env-util.h"
 #include "syslog-util.h"
@@ -41,32 +40,32 @@
 	hash_remove(processes, POINTER_CAST(pid));
 }
 
-void child_process_init_env(ARRAY_TYPE(const_string) *env)
+void child_process_init_env(void)
 {
 	int facility;
 
-	t_array_init(env, 128);
+	/* remove all environment, we don't need them */
+	env_clean();
 
 	/* we'll log through master process */
-	envarr_addb(env, "LOG_TO_MASTER");
+	env_put("LOG_TO_MASTER=1");
 	if (env_tz != NULL)
-		envarr_add(env, "TZ", env_tz);
+		env_put(t_strconcat("TZ=", env_tz, NULL));
 
 	if (settings_root == NULL ||
 	    !syslog_facility_find(settings_root->defaults->syslog_facility,
 				  &facility))
 		facility = LOG_MAIL;
-	envarr_addi(env, "SYSLOG_FACILITY", facility);
+	env_put(t_strdup_printf("SYSLOG_FACILITY=%d", facility));
 
 	if (settings_root != NULL && !settings_root->defaults->version_ignore)
-		envarr_add(env, "DOVECOT_VERSION", PACKAGE_VERSION);
+		env_put("DOVECOT_VERSION="PACKAGE_VERSION);
 #ifdef DEBUG
-	if (gdb) envarr_addb(env, "GDB");
+	if (gdb) env_put("GDB=1");
 #endif
 }
 
-void client_process_exec(const char *cmd, const char *title,
-			 ARRAY_TYPE(const_string) *env)
+void client_process_exec(const char *cmd, const char *title)
 {
 	const char *executable, *p, **argv;
 
@@ -82,9 +81,7 @@
 	p = strrchr(argv[0], '/');
 	if (p != NULL) argv[0] = p+1;
 
-	(void)array_append_space(env); /* NULL-terminate */
-	execve(executable, (char **)argv,
-	       (char **)array_idx_modifiable(env, 0));
+	execv(executable, (char **)argv);
 }
 
 static const char *get_exit_status_message(enum fatal_exit_status status)
--- a/src/master/child-process.h	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/master/child-process.h	Sat Jan 05 01:28:46 2008 +0200
@@ -30,9 +30,8 @@
 void child_process_add(pid_t pid, struct child_process *process);
 void child_process_remove(pid_t pid);
 
-void child_process_init_env(ARRAY_TYPE(const_string) *env);
-void client_process_exec(const char *cmd, const char *title,
-			 ARRAY_TYPE(const_string) *env);
+void child_process_init_env(void);
+void client_process_exec(const char *cmd, const char *title);
 
 void child_process_set_destroy_callback(enum process_type type,
 					child_process_destroy_callback_t *cb);
--- a/src/master/dict-process.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/master/dict-process.c	Sat Jan 05 01:28:46 2008 +0200
@@ -36,7 +36,6 @@
 	unsigned int i, count;
 	int log_fd;
 	pid_t pid;
-	ARRAY_TYPE(const_string) env;
 
 	log_fd = log_create_pipe(&log, 0);
 	if (log_fd < 0)
@@ -82,22 +81,20 @@
 	for (i = 0; i <= 3; i++)
 		fd_close_on_exec(i, FALSE);
 
-	child_process_init_env(&env);
-	envarr_add(&env, "DICT_LISTEN_FROM_FD", process->path);
+	child_process_init_env();
+	env_put(t_strconcat("DICT_LISTEN_FROM_FD=", process->path, NULL));
 
 	dicts = array_get(&settings_root->dicts, &count);
 	i_assert((count % 2) == 0);
-	for (i = 0; i < count; i += 2) {
-		envarr_add(&env, t_strdup_printf("DICT_%s", dicts[i]),
-			   dicts[i+1]);
-	}
+	for (i = 0; i < count; i += 2)
+		env_put(t_strdup_printf("DICT_%s=%s", dicts[i], dicts[i+1]));
 
 	/* make sure we don't leak syslog fd, but do it last so that
 	   any errors above will be logged */
 	closelog();
 
 	executable = PKG_LIBEXECDIR"/dict";
-	client_process_exec(executable, "", &env);
+	client_process_exec(executable, "");
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
 	return -1;
 }
--- a/src/master/login-process.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/master/login-process.c	Sat Jan 05 01:28:46 2008 +0200
@@ -505,23 +505,22 @@
 	i_free(p);
 }
 
-static void login_process_init_env(ARRAY_TYPE(const_string) *env,
-				   struct login_group *group, pid_t pid)
+static void login_process_init_env(struct login_group *group, pid_t pid)
 {
 	struct settings *set = group->set;
 
-	child_process_init_env(env);
+	child_process_init_env();
 
 	/* setup access environment - needs to be done after
 	   clean_child_process() since it clears environment. Don't set user
 	   parameter since we don't want to call initgroups() for login
 	   processes. */
-	restrict_access_set_env(env, NULL, set->login_uid,
+	restrict_access_set_env(NULL, set->login_uid,
 				set->server->login_gid,
 				set->login_chroot ? set->login_dir : NULL,
 				0, 0, NULL);
 
-	envarr_addb(env, "DOVECOT_MASTER");
+	env_put("DOVECOT_MASTER=1");
 
 	if (!set->ssl_disable) {
 		const char *ssl_key_password;
@@ -529,51 +528,57 @@
 		ssl_key_password = *set->ssl_key_password != '\0' ?
 			set->ssl_key_password : ssl_manual_key_password;
 
-		if (*set->ssl_ca_file != '\0')
-			envarr_add(env, "SSL_CA_FILE", set->ssl_ca_file);
-		envarr_add(env, "SSL_CERT_FILE", set->ssl_cert_file);
-		envarr_add(env, "SSL_KEY_FILE", set->ssl_key_file);
-		envarr_add(env, "SSL_KEY_PASSWORD", ssl_key_password);
-		envarr_add(env, "SSL_PARAM_FILE", SSL_PARAMETERS_FILENAME);
+		if (*set->ssl_ca_file != '\0') {
+			env_put(t_strconcat("SSL_CA_FILE=",
+					    set->ssl_ca_file, NULL));
+		}
+		env_put(t_strconcat("SSL_CERT_FILE=",
+				    set->ssl_cert_file, NULL));
+		env_put(t_strconcat("SSL_KEY_FILE=",
+				    set->ssl_key_file, NULL));
+		env_put(t_strconcat("SSL_KEY_PASSWORD=",
+				    ssl_key_password, NULL));
+		env_put("SSL_PARAM_FILE="SSL_PARAMETERS_FILENAME);
 		if (*set->ssl_cipher_list != '\0') {
-			envarr_add(env, "SSL_CIPHER_LIST",
-				   set->ssl_cipher_list);
+			env_put(t_strconcat("SSL_CIPHER_LIST=",
+					    set->ssl_cipher_list, NULL));
 		}
-		envarr_add(env, "SSL_CERT_USERNAME_FIELD",
-			   set->ssl_cert_username_field);
+		env_put(t_strconcat("SSL_CERT_USERNAME_FIELD=",
+				    set->ssl_cert_username_field, NULL));
 		if (set->ssl_verify_client_cert)
-			envarr_addb(env, "SSL_VERIFY_CLIENT_CERT");
+			env_put("SSL_VERIFY_CLIENT_CERT=1");
 	}
 
 	if (set->disable_plaintext_auth)
-		envarr_addb(env, "DISABLE_PLAINTEXT_AUTH");
+		env_put("DISABLE_PLAINTEXT_AUTH=1");
 	if (set->verbose_proctitle)
-		envarr_addb(env, "VERBOSE_PROCTITLE");
+		env_put("VERBOSE_PROCTITLE=1");
 	if (set->verbose_ssl)
-		envarr_addb(env, "VERBOSE_SSL");
+		env_put("VERBOSE_SSL=1");
 	if (set->server->auths->verbose)
-		envarr_addb(env, "VERBOSE_AUTH");
+		env_put("VERBOSE_AUTH=1");
 
 	if (set->login_process_per_connection) {
-		envarr_addi(env, "PROCESS_PER_CONNECTION", 1);
-		envarr_addi(env, "MAX_CONNECTIONS", 1);
+		env_put("PROCESS_PER_CONNECTION=1");
+		env_put("MAX_CONNECTIONS=1");
 	} else {
-		envarr_addi(env, "MAX_CONNECTIONS",
-			    set->login_max_connections);
+		env_put(t_strdup_printf("MAX_CONNECTIONS=%u",
+					set->login_max_connections));
 	}
 
-	envarr_add(env, "PROCESS_UID", dec2str(pid));
-	envarr_add(env, "GREETING", set->login_greeting);
-	envarr_add(env, "LOG_FORMAT_ELEMENTS", set->login_log_format_elements);
-	envarr_add(env, "LOG_FORMAT", set->login_log_format);
+	env_put(t_strconcat("PROCESS_UID=", dec2str(pid), NULL));
+	env_put(t_strconcat("GREETING=", set->login_greeting, NULL));
+	env_put(t_strconcat("LOG_FORMAT_ELEMENTS=",
+			    set->login_log_format_elements, NULL));
+	env_put(t_strconcat("LOG_FORMAT=", set->login_log_format, NULL));
 	if (set->login_greeting_capability)
-		envarr_addb(env, "GREETING_CAPABILITY");
+		env_put("GREETING_CAPABILITY=1");
 
 	if (group->mail_process_type == PROCESS_TYPE_IMAP) {
-		envarr_add(env, "CAPABILITY_STRING",
-			   *set->imap_capability != '\0' ?
-			   set->imap_capability :
-			   set->imap_generated_capability);
+		env_put(t_strconcat("CAPABILITY_STRING=",
+				    *set->imap_capability != '\0' ?
+				    set->imap_capability :
+				    set->imap_generated_capability, NULL));
 	}
 }
 
@@ -587,7 +592,6 @@
 	ARRAY_TYPE(dup2) dups;
 	unsigned int i, fd_limit, listen_count = 0, ssl_listen_count = 0;
 	int fd[2], log_fd, cur_fd, tmp_fd;
-	ARRAY_TYPE(const_string) env;
 
 	if (group->set->login_uid == 0)
 		i_fatal("Login process must not run as root");
@@ -668,10 +672,10 @@
 	(void)close(fd[0]);
 	(void)close(fd[1]);
 
-	login_process_init_env(&env, group, getpid());
+	login_process_init_env(group, getpid());
 
-	envarr_addi(&env, "LISTEN_FDS", listen_count);
-	envarr_addi(&env, "SSL_LISTEN_FDS", ssl_listen_count);
+	env_put(t_strdup_printf("LISTEN_FDS=%u", listen_count));
+	env_put(t_strdup_printf("SSL_LISTEN_FDS=%u", ssl_listen_count));
 
 	if (!group->set->login_chroot) {
 		/* no chrooting, but still change to the directory */
@@ -697,7 +701,7 @@
 	i_assert(fd_limit > (unsigned int)cur_fd+1);
 	(void)close(cur_fd+1);
 
-	client_process_exec(group->set->login_executable, "", &env);
+	client_process_exec(group->set->login_executable, "");
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
 		       group->set->login_executable);
 	return -1;
@@ -843,20 +847,18 @@
 
 static int login_process_send_env(struct login_process *p)
 {
-	ARRAY_TYPE(const_string) env;
-	const char *const *envs;
+	extern char **environ;
+	char **env;
 	ssize_t len;
-	unsigned int i, count;
 	int ret = 0;
 
 	/* this will clear our environment. luckily we don't need it. */
-	login_process_init_env(&env, p->group, p->pid);
+	login_process_init_env(p->group, p->pid);
 
-	envs = array_get(&env, &count);
-	for (i = 0; i < count; i++) {
-		len = strlen(envs[i]);
+	for (env = environ; *env != NULL; env++) {
+		len = strlen(*env);
 
-		if (o_stream_send(p->output, envs[i], len) != len ||
+		if (o_stream_send(p->output, *env, len) != len ||
 		    o_stream_send(p->output, "\n", 1) != 1) {
 			ret = -1;
 			break;
@@ -882,16 +884,11 @@
 
 static bool login_process_init_group(struct login_process *p)
 {
-	int ret;
-
 	p->group->refcount++;
 	p->group->processes++;
 	p->group->listening_processes++;
 
-	T_FRAME(
-		ret = login_process_send_env(p);
-	);
-	if (ret < 0) {
+	if (login_process_send_env(p) < 0) {
 		i_error("login: Couldn't send environment");
 		return FALSE;
 	}
--- a/src/master/mail-process.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/master/mail-process.c	Sat Jan 05 01:28:46 2008 +0200
@@ -235,22 +235,14 @@
 	return str_c(str);
 }
 
-static const char *ns_env_key(const char *name, unsigned int i)
-{
-	return t_strdup_printf("NAMESPACE_%u_%s", i, name);
-}
-
 static void
-env_add_namespace(ARRAY_TYPE(const_string) *env,
-		  struct namespace_settings *ns, const char *default_location,
-		  const struct var_expand_table *table,
-		  const char **first_location_r)
+env_put_namespace(struct namespace_settings *ns, const char *default_location,
+		  const struct var_expand_table *table)
 {
 	const char *location;
 	unsigned int i;
 	string_t *str;
 
-	*first_location_r = NULL;
 	if (default_location == NULL)
 		default_location = "";
 
@@ -258,132 +250,142 @@
 		location = *ns->location != '\0' ? ns->location :
 			default_location;
 		location = expand_mail_env(location, table);
-		if (*first_location_r == NULL)
-			*first_location_r = location;
-		envarr_add(env, t_strdup_printf("NAMESPACE_%u", i), location);
+		env_put(t_strdup_printf("NAMESPACE_%u=%s", i, location));
 
-		if (ns->separator != NULL)
-			envarr_add(env, ns_env_key("SEP", i), ns->separator);
-		if (ns->type != NULL)
-			envarr_add(env, ns_env_key("TYPE", i), ns->type);
+		if (ns->separator != NULL) {
+			env_put(t_strdup_printf("NAMESPACE_%u_SEP=%s",
+						i, ns->separator));
+		}
+		if (ns->type != NULL) {
+			env_put(t_strdup_printf("NAMESPACE_%u_TYPE=%s",
+						i, ns->type));
+		}
 		if (ns->prefix != NULL) {
 			/* expand variables, eg. ~%u/ can be useful */
 			str = t_str_new(256);
+			str_printfa(str, "NAMESPACE_%u_PREFIX=", i);
 			var_expand(str, ns->prefix, table);
-			envarr_add(env, ns_env_key("PREFIX", i), str_c(str));
+			env_put(str_c(str));
 		}
 		if (ns->inbox)
-			envarr_addb(env, ns_env_key("INBOX", i));
+			env_put(t_strdup_printf("NAMESPACE_%u_INBOX=1", i));
 		if (ns->hidden)
-			envarr_addb(env, ns_env_key("HIDDEN", i));
+			env_put(t_strdup_printf("NAMESPACE_%u_HIDDEN=1", i));
 		if (ns->list)
-			envarr_addb(env, ns_env_key("LIST", i));
+			env_put(t_strdup_printf("NAMESPACE_%u_LIST=1", i));
 		if (ns->subscriptions)
-			envarr_addb(env, ns_env_key("SUBSCRIPTIONS", i));
+			env_put(t_strdup_printf("NAMESPACE_%u_SUBSCRIPTIONS=1",
+						i));
 	}
 }
 
 static void
-mail_process_set_environment(ARRAY_TYPE(const_string) *env,
-			     struct settings *set, const char *mail,
+mail_process_set_environment(struct settings *set, const char *mail,
 			     const struct var_expand_table *var_expand_table,
-			     bool dump_capability,
-			     const char **first_location_r)
+			     bool dump_capability)
 {
 	const char *const *envs;
 	string_t *str;
 	unsigned int i, count;
 
-	(void)umask(set->umask);
-
-	envarr_add(env, "MAIL_CACHE_FIELDS", set->mail_cache_fields);
-	envarr_add(env, "MAIL_NEVER_CACHE_FIELDS",
-		   set->mail_never_cache_fields);
-	envarr_addi(env, "MAIL_CACHE_MIN_MAIL_COUNT",
-		    set->mail_cache_min_mail_count);
-	envarr_addi(env, "MAILBOX_IDLE_CHECK_INTERVAL",
-		    set->mailbox_idle_check_interval);
-	envarr_addi(env, "MAIL_MAX_KEYWORD_LENGTH",
-		    set->mail_max_keyword_length);
+	env_put(t_strconcat("MAIL_CACHE_FIELDS=",
+			    set->mail_cache_fields, NULL));
+	env_put(t_strconcat("MAIL_NEVER_CACHE_FIELDS=",
+			    set->mail_never_cache_fields, NULL));
+	env_put(t_strdup_printf("MAIL_CACHE_MIN_MAIL_COUNT=%u",
+				set->mail_cache_min_mail_count));
+	env_put(t_strdup_printf("MAILBOX_IDLE_CHECK_INTERVAL=%u",
+				set->mailbox_idle_check_interval));
+	env_put(t_strdup_printf("MAIL_MAX_KEYWORD_LENGTH=%u",
+				set->mail_max_keyword_length));
 
 	if (set->protocol == MAIL_PROTOCOL_IMAP) {
-		envarr_addi(env, "IMAP_MAX_LINE_LENGTH",
-			    set->imap_max_line_length);
+		env_put(t_strdup_printf("IMAP_MAX_LINE_LENGTH=%u",
+					set->imap_max_line_length));
 		if (*set->imap_capability != '\0') {
-			envarr_add(env, "IMAP_CAPABILITY",
-				   set->imap_capability);
+			env_put(t_strconcat("IMAP_CAPABILITY=",
+					    set->imap_capability, NULL));
 		}
-		envarr_add(env, "IMAP_CLIENT_WORKAROUNDS",
-			   set->imap_client_workarounds);
-		envarr_add(env, "IMAP_LOGOUT_FORMAT", set->imap_logout_format);
+		env_put(t_strconcat("IMAP_CLIENT_WORKAROUNDS=",
+				    set->imap_client_workarounds, NULL));
+		env_put(t_strconcat("IMAP_LOGOUT_FORMAT=",
+				    set->imap_logout_format, NULL));
 	}
 	if (set->protocol == MAIL_PROTOCOL_POP3) {
-		envarr_add(env, "POP3_CLIENT_WORKAROUNDS",
-			   set->pop3_client_workarounds);
-		envarr_add(env, "POP3_LOGOUT_FORMAT", set->pop3_logout_format);
+		env_put(t_strconcat("POP3_CLIENT_WORKAROUNDS=",
+				    set->pop3_client_workarounds, NULL));
+		env_put(t_strconcat("POP3_LOGOUT_FORMAT=",
+				    set->pop3_logout_format, NULL));
 		if (set->pop3_no_flag_updates)
-			envarr_addb(env, "POP3_NO_FLAG_UPDATES");
+			env_put("POP3_NO_FLAG_UPDATES=1");
 		if (set->pop3_reuse_xuidl)
-			envarr_addb(env, "POP3_REUSE_XUIDL");
+			env_put("POP3_REUSE_XUIDL=1");
 		if (set->pop3_enable_last)
-			envarr_addb(env, "POP3_ENABLE_LAST");
+			env_put("POP3_ENABLE_LAST=1");
 		if (set->pop3_lock_session)
-			envarr_addb(env, "POP3_LOCK_SESSION");
+			env_put("POP3_LOCK_SESSION=1");
 	}
 
 	/* We care about POP3 UIDL format in all process types */
-	envarr_add(env, "POP3_UIDL_FORMAT", set->pop3_uidl_format);
+	env_put(t_strconcat("POP3_UIDL_FORMAT=", set->pop3_uidl_format, NULL));
 
 	if (set->mail_save_crlf)
-		envarr_addb(env, "MAIL_SAVE_CRLF");
+		env_put("MAIL_SAVE_CRLF=1");
 	if (set->mmap_disable)
-		envarr_addb(env, "MMAP_DISABLE");
+		env_put("MMAP_DISABLE=1");
 	if (set->dotlock_use_excl)
-		envarr_addb(env, "DOTLOCK_USE_EXCL");
+		env_put("DOTLOCK_USE_EXCL=1");
 	if (set->fsync_disable)
-		envarr_addb(env, "FSYNC_DISABLE");
+		env_put("FSYNC_DISABLE=1");
 	if (set->mail_nfs_storage)
-		envarr_addb(env, "MAIL_NFS_STORAGE");
+		env_put("MAIL_NFS_STORAGE=1");
 	if (set->mail_nfs_index)
-		envarr_addb(env, "MAIL_NFS_INDEX");
+		env_put("MAIL_NFS_INDEX=1");
 	if (set->mailbox_list_index_disable)
-		envarr_addb(env, "MAILBOX_LIST_INDEX_DISABLE");
+		env_put("MAILBOX_LIST_INDEX_DISABLE=1");
 	if (set->maildir_stat_dirs)
-		envarr_addb(env, "MAILDIR_STAT_DIRS");
+		env_put("MAILDIR_STAT_DIRS=1");
 	if (set->maildir_copy_with_hardlinks)
-		envarr_addb(env, "MAILDIR_COPY_WITH_HARDLINKS");
+		env_put("MAILDIR_COPY_WITH_HARDLINKS=1");
 	if (set->maildir_copy_preserve_filename)
-		envarr_addb(env, "MAILDIR_COPY_PRESERVE_FILENAME");
+		env_put("MAILDIR_COPY_PRESERVE_FILENAME=1");
 	if (set->mail_debug)
-		envarr_addb(env, "DEBUG");
+		env_put("DEBUG=1");
 	if (set->mail_full_filesystem_access)
-		envarr_addb(env, "FULL_FILESYSTEM_ACCESS");
+		env_put("FULL_FILESYSTEM_ACCESS=1");
 	if (set->mbox_dirty_syncs)
-		envarr_addb(env, "MBOX_DIRTY_SYNCS");
+		env_put("MBOX_DIRTY_SYNCS=1");
 	if (set->mbox_very_dirty_syncs)
-		envarr_addb(env, "MBOX_VERY_DIRTY_SYNCS");
+		env_put("MBOX_VERY_DIRTY_SYNCS=1");
 	if (set->mbox_lazy_writes)
-		envarr_addb(env, "MBOX_LAZY_WRITES");
+		env_put("MBOX_LAZY_WRITES=1");
 	/* when running dump-capability log still points to stderr,
 	   and io_add()ing it might break (epoll_ctl() gives EPERM) */
 	if (set->shutdown_clients && !dump_capability)
-		envarr_addb(env, "STDERR_CLOSE_SHUTDOWN");
+		env_put("STDERR_CLOSE_SHUTDOWN=1");
+	(void)umask(set->umask);
 
-	envarr_add(env, "LOCK_METHOD", set->lock_method);
-	envarr_add(env, "MBOX_READ_LOCKS", set->mbox_read_locks);
-	envarr_add(env, "MBOX_WRITE_LOCKS", set->mbox_write_locks);
-	envarr_addi(env, "MBOX_LOCK_TIMEOUT", set->mbox_lock_timeout);
-	envarr_addi(env, "MBOX_DOTLOCK_CHANGE_TIMEOUT",
-		    set->mbox_dotlock_change_timeout);
-	envarr_addi(env, "MBOX_MIN_INDEX_SIZE", set->mbox_min_index_size);
+	env_put(t_strconcat("LOCK_METHOD=", set->lock_method, NULL));
+	env_put(t_strconcat("MBOX_READ_LOCKS=", set->mbox_read_locks, NULL));
+	env_put(t_strconcat("MBOX_WRITE_LOCKS=", set->mbox_write_locks, NULL));
+	env_put(t_strdup_printf("MBOX_LOCK_TIMEOUT=%u",
+				set->mbox_lock_timeout));
+	env_put(t_strdup_printf("MBOX_DOTLOCK_CHANGE_TIMEOUT=%u",
+				set->mbox_dotlock_change_timeout));
+	env_put(t_strdup_printf("MBOX_MIN_INDEX_SIZE=%u",
+				set->mbox_min_index_size));
 
-	envarr_addi(env, "DBOX_ROTATE_SIZE", set->dbox_rotate_size);
-	envarr_addi(env, "DBOX_ROTATE_MIN_SIZE", set->dbox_rotate_min_size);
-	envarr_addi(env, "DBOX_ROTATE_DAYS", set->dbox_rotate_days);
+	env_put(t_strdup_printf("DBOX_ROTATE_SIZE=%u",
+				set->dbox_rotate_size));
+	env_put(t_strdup_printf("DBOX_ROTATE_MIN_SIZE=%u",
+				set->dbox_rotate_min_size));
+	env_put(t_strdup_printf("DBOX_ROTATE_DAYS=%u",
+				set->dbox_rotate_days));
 
 	if (*set->mail_plugins != '\0') {
-		envarr_add(env, "MAIL_PLUGIN_DIR", set->mail_plugin_dir);
-		envarr_add(env, "MAIL_PLUGINS", set->mail_plugins);
+		env_put(t_strconcat("MAIL_PLUGIN_DIR=",
+				    set->mail_plugin_dir, NULL));
+		env_put(t_strconcat("MAIL_PLUGINS=", set->mail_plugins, NULL));
 	}
 
 	/* user given environment - may be malicious. virtual_user comes from
@@ -391,15 +393,12 @@
 	   mechanism might allow leaving extra data there. */
 	if ((mail == NULL || *mail == '\0') && *set->mail_location != '\0')
 		mail = expand_mail_env(set->mail_location, var_expand_table);
-	envarr_add(env, "MAIL", mail);
+	env_put(t_strconcat("MAIL=", mail, NULL));
 
-	*first_location_r = NULL;
 	if (set->server->namespaces != NULL) {
-		env_add_namespace(env, set->server->namespaces,
-				  mail, var_expand_table, first_location_r);
+		env_put_namespace(set->server->namespaces,
+				  mail, var_expand_table);
 	}
-	if (*first_location_r == NULL)
-		*first_location_r = mail == NULL ? mail : "";
 
 	str = t_str_new(256);
 	envs = array_get(&set->plugin_envs, &count);
@@ -408,7 +407,8 @@
 		str_truncate(str, 0);
 		var_expand(str, envs[i+1], var_expand_table);
 
-		envarr_add(env, t_str_ucase(envs[i]), str_c(str));
+		env_put(t_strconcat(t_str_ucase(envs[i]), "=",
+				    str_c(str), NULL));
 	}
 }
 
@@ -417,8 +417,7 @@
 	struct server_settings *server = settings_root;
 	const struct var_expand_table *var_expand_table;
 	struct settings *set;
-	const char *executable, *first_location;
-	ARRAY_TYPE(const_string) env;
+	const char *executable;
 
 	if (strcmp(protocol, "ext") == 0) {
 		/* external binary. section contains path for it. */
@@ -452,24 +451,24 @@
 				     getpid(), geteuid());
 
 	/* set up logging */
-	t_array_init(&env, 128);
-	envarr_add(&env, "LOG_TIMESTAMP", set->log_timestamp);
+	env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
 	if (*set->log_path == '\0')
-		envarr_addb(&env, "USE_SYSLOG");
+		env_put("USE_SYSLOG=1");
 	else
-		envarr_add(&env, "LOGFILE", set->log_path);
+		env_put(t_strconcat("LOGFILE=", set->log_path, NULL));
 	if (*set->info_log_path != '\0')
-		envarr_add(&env, "INFOLOGFILE", set->info_log_path);
+		env_put(t_strconcat("INFOLOGFILE=", set->info_log_path, NULL));
 	if (*set->mail_log_prefix != '\0') {
 		string_t *str = t_str_new(256);
 
+		str_append(str, "LOG_PREFIX=");
 		var_expand(str, set->mail_log_prefix, var_expand_table);
-		envarr_add(&env, "LOG_PREFIX", str_c(str));
+		env_put(str_c(str));
 	}
 
-	mail_process_set_environment(&env, set, getenv("MAIL"),
-				     var_expand_table, FALSE, &first_location);
-        client_process_exec(executable, "", &env);
+	mail_process_set_environment(set, getenv("MAIL"), var_expand_table,
+				     FALSE);
+        client_process_exec(executable, "");
 
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
 }
@@ -521,7 +520,7 @@
 {
 	const struct var_expand_table *var_expand_table;
 	const char *p, *addr, *mail, *chroot_dir, *home_dir, *full_home_dir;
-	const char *system_user, *first_location;
+	const char *system_user;
 	struct mail_process_group *process_group;
 	char title[1024];
 	struct log_io *log;
@@ -529,7 +528,6 @@
 	pid_t pid;
 	uid_t uid;
 	gid_t gid;
-	ARRAY_TYPE(const_string) env, restrict_env, *dest_env;
 	ARRAY_DEFINE(extra_args, const char *);
 	unsigned int i, count, left, process_count;
 	int ret, log_fd, nice, chdir_errno;
@@ -702,7 +700,7 @@
 		log_set_prefix(log, str_c(str));
 	}
 
-	child_process_init_env(&env);
+	child_process_init_env();
 
 	/* move the client socket into stdin and stdout fds, log to stderr */
 	if (dup2(dump_capability ? null_fd : socket, 0) < 0)
@@ -717,20 +715,14 @@
 
 	/* setup environment - set the most important environment first
 	   (paranoia about filling up environment without noticing) */
-	if (!set->mail_drop_priv_before_exec)
-		dest_env = &env;
-	else {
-		t_array_init(&restrict_env, 8);
-		dest_env = &restrict_env;
-	}
-	restrict_access_set_env(dest_env, system_user, uid, gid, chroot_dir,
+	restrict_access_set_env(system_user, uid, 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 (dump_capability)
-		envarr_addb(&env, "DUMP_CAPABILITY");
+		env_put("DUMP_CAPABILITY=1");
 
 	if (*home_dir == '\0') {
 		full_home_dir = "";
@@ -780,8 +772,8 @@
 			i_fatal("chdir(/tmp) failed: %m");
 	}
 
-	mail_process_set_environment(&env, set, mail, var_expand_table,
-				     dump_capability, &first_location);
+	mail_process_set_environment(set, mail, var_expand_table,
+				     dump_capability);
 
 	/* extra args. uppercase key value. */
 	args = array_get(&extra_args, &count);
@@ -794,28 +786,31 @@
 		p = strchr(args[i], '=');
 		if (p == NULL) {
 			/* boolean */
-			envarr_addb(&env, t_str_ucase(args[i]));
+			env_put(t_strconcat(t_str_ucase(args[i]), "=1", NULL));
 
 		} else {
 			/* key=value */
-			envarr_add(&env,
-				   t_str_ucase(t_strdup_until(args[i], p)),
-				   p + 1);
+			env_put(t_strconcat(t_str_ucase(
+				t_strdup_until(args[i], p)), p, NULL));
 		}
 	}
 
 	if (nfs_check) {
 		/* ideally we should check all of the namespaces,
 		   but for now don't bother. */
-		nfs_warn_if_found(first_location, full_home_dir);
+		const char *mail_location = getenv("NAMESPACE_1");
+
+		if (mail_location == NULL)
+			mail_location = getenv("MAIL");
+		nfs_warn_if_found(mail_location, full_home_dir);
 	}
 
-	envarr_addb(&env, "LOGGED_IN");
-	envarr_add(&env, "HOME", home_dir);
-	envarr_add(&env, "USER", user);
+	env_put("LOGGED_IN=1");
+	env_put(t_strconcat("HOME=", home_dir, NULL));
+	env_put(t_strconcat("USER=", user, NULL));
 
 	addr = net_ip2addr(remote_ip);
-	envarr_add(&env, "IP", addr);
+	env_put(t_strconcat("IP=", addr, NULL));
 
 	if (!set->verbose_proctitle)
 		title[0] = '\0';
@@ -831,9 +826,9 @@
 	closelog();
 
 	if (set->mail_drop_priv_before_exec)
-		restrict_access_by_env(&restrict_env, TRUE);
+		restrict_access_by_env(TRUE);
 
-	client_process_exec(set->mail_executable, title, &env);
+	client_process_exec(set->mail_executable, title);
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
 		       set->mail_executable);
 
--- a/src/master/ssl-init.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/master/ssl-init.c	Sat Jan 05 01:28:46 2008 +0200
@@ -31,7 +31,6 @@
 	struct log_io *log;
 	pid_t pid;
 	int log_fd;
-	ARRAY_TYPE(const_string) env;
 
 	log_fd = log_create_pipe(&log, 10);
 	if (log_fd == -1)
@@ -61,9 +60,9 @@
 	if (dup2(log_fd, 2) < 0)
 		i_fatal("dup2(stderr) failed: %m");
 
-	child_process_init_env(&env);
+	child_process_init_env();
 	client_process_exec(t_strconcat(binpath, " "SSL_PARAMETERS_PERM_PATH,
-					NULL), "", &env);
+					NULL), "");
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", binpath);
 }
 
--- a/src/plugins/expire/auth-client.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/plugins/expire/auth-client.c	Sat Jan 05 01:28:46 2008 +0200
@@ -143,7 +143,7 @@
 		conn->current_uid = uid;
 	}
 
-	restrict_access_by_env(NULL, FALSE);
+	restrict_access_by_env(FALSE);
 	conn->return_value = 1;
 }
 
--- a/src/pop3/main.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/pop3/main.c	Sat Jan 05 01:28:46 2008 +0200
@@ -177,7 +177,7 @@
 					  TRUE, version);
 	}
 
-	restrict_access_by_env(NULL, !IS_STANDALONE());
+	restrict_access_by_env(!IS_STANDALONE());
 }
 
 static int main_init(void)
--- a/src/util/rawlog.c	Fri Jan 04 04:38:03 2008 +0200
+++ b/src/util/rawlog.c	Sat Jan 05 01:28:46 2008 +0200
@@ -309,7 +309,7 @@
 	}
 	(void)close(sfd[1]);
 
-	restrict_access_by_env(NULL, TRUE);
+	restrict_access_by_env(TRUE);
 
 	process_title_set(t_strdup_printf("[%s:%s rawlog]", getenv("USER"),
 					  dec2str(getppid())));