view src/lib/env-util.c @ 21322:5ab8dc1a4a6f

global: Change string position/length from unsigned int to size_t Mainly to avoid truncating >4GB strings, which might potentially cause some security holes. Normally there are other limits, which prevent such excessive strings from being created in the first place. I'm sure this didn't find everything. Maybe everything could be found with compiler warnings. -Wconversion kind of does it, but it gives way too many unnecessary warnings. These were mainly found with: grep " = strlen" egrep "unsigned int.*(size|len)"
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 12 Dec 2016 07:19:55 +0200
parents 0f22db71df7a
children 2e2563132d5f
line wrap: on
line source

/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "env-util.h"

#ifdef __APPLE__
#  include <crt_externs.h>
#endif

struct env_backup {
	pool_t pool;
	const char **strings;
};

static pool_t env_pool = NULL;

void env_put(const char *env)
{
	if (env_pool == NULL) {
		env_pool = pool_alloconly_create(MEMPOOL_GROWING"Environment",
						 2048);
	}
	if (putenv(p_strdup(env_pool, env)) != 0)
		i_fatal("putenv(%s) failed: %m", env);
}

void env_remove(const char *name)
{
#ifdef HAVE_UNSETENV
#ifdef UNSETENV_RET_INT
	if (unsetenv(name) < 0)
		i_fatal("unsetenv(%s) failed: %m", name);
#else
	unsetenv(name);
#endif
#else
	extern char **environ;
	size_t len;
	char **envp;

	len = strlen(name);
	for (envp = environ; *envp != NULL; envp++) {
		if (strncmp(name, *envp, len) == 0 &&
		    (*envp)[len] == '=') {
			do {
				envp[0] = envp[1];
			} while (*++envp != NULL);
			break;
		}
	}
#endif
}

void env_clean(void)
{
#ifdef HAVE_CLEARENV
	if (clearenv() < 0)
		i_fatal("clearenv() failed");
#else
	char ***environ_p = env_get_environ_p();

	/* Try to clear the environment.

	   a) environ = NULL crashes on OS X.
	   b) *environ = NULL doesn't work on FreeBSD 7.0.
	   c) environ = emptyenv doesn't work on Haiku OS
	   d) environ = calloc() should work everywhere
	*/
	*environ_p = calloc(1, sizeof(**environ_p));
#endif
	if (env_pool != NULL)
		p_clear(env_pool);
}

static void env_clean_except_real(const char *const preserve_envs[])
{
	ARRAY_TYPE(const_string) copy;
	const char *value, *const *envp;
	unsigned int i;

	t_array_init(&copy, 16);
	for (i = 0; preserve_envs[i] != NULL; i++) {
		const char *key = preserve_envs[i];

		value = getenv(key);
		if (value != NULL) {
			value = t_strconcat(key, "=", value, NULL);
			array_append(&copy, &value, 1);
		}
	}

	/* Note that if the original environment was set with env_put(), the
	   environment strings will be invalid after env_clean(). That's why
	   we t_strconcat() them above. */
	env_clean();

	array_foreach(&copy, envp)
		env_put(*envp);
}

void env_clean_except(const char *const preserve_envs[])
{
	T_BEGIN {
		env_clean_except_real(preserve_envs);
	} T_END;
}

struct env_backup *env_backup_save(void)
{
	char **environ = *env_get_environ_p();
	struct env_backup *env;
	unsigned int i, count;
	pool_t pool;

	i_assert(environ != NULL);

	for (count = 0; environ[count] != NULL; count++) ;

	pool = pool_alloconly_create("saved environment", 4096);
	env = p_new(pool, struct env_backup, 1);
	env->pool = pool;
	env->strings = p_new(pool, const char *, count + 1);
	for (i = 0; i < count; i++)
		env->strings[i] = p_strdup(pool, environ[i]);
	return env;
}

void env_backup_restore(struct env_backup *env)
{
	unsigned int i;

	env_clean();
	for (i = 0; env->strings[i] != NULL; i++)
		env_put(env->strings[i]);
}

void env_backup_free(struct env_backup **_env)
{
	struct env_backup *env = *_env;

	*_env = NULL;
	pool_unref(&env->pool);
}

char ***env_get_environ_p(void)
{
#ifdef __APPLE__
	return _NSGetEnviron();
#else
	extern char **environ;

	return &environ;
#endif
}

void env_deinit(void)
{
	if (env_pool != NULL)
		pool_unref(&env_pool);
}