예제 #1
0
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);
}
예제 #2
0
파일: script.c 프로젝트: bsmr-dovecot/core
static void
exec_child(struct master_service_connection *conn, const char *const *args)
{
	unsigned int i, socket_count;

	if (dup2(conn->fd, STDIN_FILENO) < 0)
		i_fatal("dup2() failed: %m");
	if (dup2(conn->fd, STDOUT_FILENO) < 0)
		i_fatal("dup2() failed: %m");

	/* close all fds */
	socket_count = master_service_get_socket_count(master_service);
	for (i = 0; i < socket_count; i++) {
		if (close(MASTER_LISTEN_FD_FIRST + i) < 0)
			i_error("close(listener) failed: %m");
	}
	if (close(MASTER_STATUS_FD) < 0)
		i_error("close(status) failed: %m");
	if (close(conn->fd) < 0)
		i_error("close(conn->fd) failed: %m");

	for (; *args != NULL; args++)
		array_append(&exec_args, args, 1);
	array_append_zero(&exec_args);

	env_clean();
	args = array_idx(&exec_args, 0);
	execvp_const(args[0], args);
}
예제 #3
0
static void client_connected(struct master_service_connection *conn)
{
	enum mail_storage_service_flags flags =
		MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS;
	string_t *instr, *keys;
	const char **args, *key, *value, *error, *version_line, *data_line;
	struct mail_storage_service_ctx *service_ctx;
	struct mail_storage_service_input input;
	struct mail_storage_service_user *user;
	char buf[1024];
	unsigned int i, socket_count;
	int fd = -1;
	ssize_t ret;

	alarm(SCRIPT_LOGIN_READ_TIMEOUT_SECS);

	net_set_nonblock(conn->fd, FALSE);
	instr = t_str_new(1024);
	ret = fd_read(conn->fd, buf, sizeof(buf), &fd);
	while (ret > 0) {
		str_append_n(instr, buf, ret);
		if (buf[ret-1] == '\n' &&
		    strchr(str_c(instr), '\n')[1] != '\0') {
			str_truncate(instr, str_len(instr)-1);
			break;
		}

		ret = read(conn->fd, buf, sizeof(buf));
	}

	version_line = str_c(instr);
	data_line = strchr(version_line, '\n');
	if (data_line != NULL)
		version_line = t_strdup_until(version_line, data_line++);
	else
		version_line = NULL;

	if (ret > 0 || version_line != NULL) {
		if (version_line == NULL ||
		    !version_string_verify(version_line, "script-login",
				SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR)) {
			i_fatal("Client not compatible with this binary "
				"(connecting to wrong socket?)");
		}
	}

	if (ret <= 0) {
		if (ret < 0)
			i_fatal("read() failed: %m");
		else
			i_fatal("read() failed: disconnected");
	}
	if (fd == -1)
		i_fatal("client fd not received");

	alarm(0);

	/* put everything to environment */
	env_clean();
	keys = t_str_new(256);
	args = t_strsplit_tab(data_line);

	if (str_array_length(args) < 3)
		i_fatal("Missing input fields");

	i = 0;
	memset(&input, 0, sizeof(input));
	input.module = "mail"; /* need to get mail_uid, mail_gid */
	input.service = "script-login";
	(void)net_addr2ip(args[i++], &input.local_ip);
	(void)net_addr2ip(args[i++], &input.remote_ip);
	input.username = args[i++];
	input.userdb_fields = args + i;

	env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&input.local_ip), NULL));
	env_put(t_strconcat("IP=", net_ip2addr(&input.remote_ip), NULL));
	env_put(t_strconcat("USER="******"%s ", key);
		}
	}
	env_put(t_strconcat(ENV_USERDB_KEYS"=", str_c(keys), NULL));

	master_service_init_log(master_service,
		t_strdup_printf("script-login(%s): ", input.username));

	if (drop_to_userdb_privileges) {
		service_ctx = mail_storage_service_init(master_service, NULL, flags);
		if (mail_storage_service_lookup(service_ctx, &input, &user, &error) <= 0)
			i_fatal("%s", error);
		mail_storage_service_restrict_setenv(service_ctx, user);
		/* we can't exec anything in a chroot */
		env_remove("RESTRICT_CHROOT");
		restrict_access_by_env(getenv("HOME"), TRUE);
	}

	if (dup2(fd, STDIN_FILENO) < 0)
		i_fatal("dup2() failed: %m");
	if (dup2(fd, STDOUT_FILENO) < 0)
		i_fatal("dup2() failed: %m");
	if (close(fd) < 0)
		i_fatal("close() failed: %m");
	if (conn->fd != SCRIPT_COMM_FD) {
		if (dup2(conn->fd, SCRIPT_COMM_FD) < 0)
			i_fatal("dup2() failed: %m");
		if (close(conn->fd) < 0)
			i_fatal("close() failed: %m");
	}

	/* close all listener sockets */
	socket_count = master_service_get_socket_count(master_service);
	for (i = 0; i < socket_count; i++) {
		if (close(MASTER_LISTEN_FD_FIRST + i) < 0)
			i_error("close(listener) failed: %m");
	}
	if (close(MASTER_STATUS_FD) < 0)
		i_error("close(status) failed: %m");

	execvp_const(exec_args[0], exec_args);
}
static void exec_child
(const char *bin_path, const char *const *args, const char *const *envs,
	int in_fd, int out_fd, int *extra_fds, bool drop_stderr)
{
	ARRAY_TYPE(const_string) exec_args;

	/* Setup stdin/stdout */

	if ( in_fd < 0 ) {
		in_fd = open("/dev/null", O_RDONLY);

		if ( in_fd == -1 )
			i_fatal("open(/dev/null) failed: %m");
	}
	if ( out_fd < 0 ) {
		out_fd = open("/dev/null", O_WRONLY);

		if ( out_fd == -1 )
			i_fatal("open(/dev/null) failed: %m");
	}

	if ( dup2(in_fd, STDIN_FILENO) < 0 )
		i_fatal("dup2(stdin) failed: %m");
	if ( dup2(out_fd, STDOUT_FILENO) < 0 )
		i_fatal("dup2(stdout) failed: %m");

	if ( close(in_fd) < 0 )
		i_error("close(in_fd) failed: %m");
	if ( (out_fd != in_fd) && close(out_fd) < 0 )
		i_error("close(out_fd) failed: %m");

	/* Drop stderr if requested */
	if ( drop_stderr ) {
		int err_fd = open("/dev/null", O_WRONLY);
		if ( err_fd == -1 )
			i_fatal("open(/dev/null) failed: %m");
		if ( dup2(err_fd, STDERR_FILENO) < 0 )
			i_fatal("dup2(stderr) failed: %m");
		if ( close(err_fd) < 0 )
			i_error("close(err_fd) failed: %m");
	}

	/* Setup extra fds */
	if ( extra_fds != NULL ) {
		for (; *extra_fds != -1; extra_fds += 2 ) {
			if ( dup2(extra_fds[0], extra_fds[1]) < 0 )
				i_fatal("dup2(extra_fd=%d) failed: %m", extra_fds[1]);
			if ( close(extra_fds[0]) < 0 )
				i_error("close(extra_fd=%d) failed: %m", extra_fds[1]);
		}
	}

	/* Compose argv */

	t_array_init(&exec_args, 16);
	array_append(&exec_args, &bin_path, 1);
	if ( args != NULL ) {
		for (; *args != NULL; args++)
			array_append(&exec_args, args, 1);
	}
	(void)array_append_space(&exec_args);

	/* Setup environment */

	env_clean();
	if ( envs != NULL ) {
		for (; *envs != NULL; envs++)
			env_put(*envs);
	}

	/* Execute */

	args = array_idx(&exec_args, 0);
	execvp_const(args[0], args);
}
예제 #5
0
파일: chressh.c 프로젝트: surjit/chroot
int
main(const int argc, char * const argv[])
/*@globals environ, errno@*/
/*@modifies environ, errno@*/
{
    struct passwd *pw;
    char **target_env = environ;
    static char *empty_env[] = { NULL };
    environ = empty_env;
    (void) umask(~(mode_t)0);  /* (no file perms, if signalled to dump core)*/

    /* check that calling UID exists, is not root, and shell matches */
    if (NULL != (pw = getpwuid(getuid()))
	&& 0 != pw->pw_uid
	&& 0 == memcmp(pw->pw_shell,CHROOTING_SHELL,sizeof(CHROOTING_SHELL)) ) {

	/* require commands of the form: "sh" "-c" "command args args args" */
	if (3 == argc && 0 == memcmp(argv[1], "-c", 3)) {
	    const char *chroot_dir = (const char *)pw->pw_dir;
	    const char *home = "/";
	    struct stat st;
	    if (0 == memcmp(pw->pw_dir, GROUP_CHROOT_DIR,
			    sizeof(GROUP_CHROOT_DIR)-1)
		&& getgroups(0, (gid_t *)NULL) > 1) {
		chroot_dir = GROUP_CHROOT_DIR;
		home = (const char *)pw->pw_dir+(sizeof(GROUP_CHROOT_DIR)-2);
		if (*home != '/' && *++home != '/') {
		    home = "/";
		}
	    }
	    if (!(   0 == stat(chroot_dir, &st)
		  && pw->pw_uid != st.st_uid  /* (not caller; typically root) */
		  && 0 == (st.st_mode & (S_IWGRP|S_IWOTH)))) {
		fatal(argc, pw, chroot_dir);
	    }
	    openlog("chressh", LOG_NDELAY|LOG_PID, LOG_AUTHPRIV);
	    if (/*@-superuser@*/
		   0 == chroot(chroot_dir)
		/*@=superuser@*/
		&& 0 == setuid(getuid())
		&& 0 != setuid(0)
		&& 0 == chdir(home)) {

		char **target_argv;
		size_t len;
		errno = 0;  /* (reset errno after expected setuid(0) failure) */

		environ = target_env = env_clean(target_env, pw, home);
		target_argv = shell_parse(argv[2]);
		if (NULL == target_argv || NULL == target_argv[0]) {
		    fatal(argc, pw, NULL);
		}
		(void) umask((mode_t)UMASK);

		if (0 == strcmp(target_argv[0], "scp")) {
		    if (0 == set_limits(NPROC_SCP)) {
			(void) execve(CHROOTED_CMD_DIR "scp",
				      target_argv, target_env);
		    }
		}
		else if (0 == strcmp(target_argv[0], "rsync")) {
		    if (0 == filter_args_rsync(target_argv)
			&& 0 == set_limits(NPROC_RSYNC)) {
			(void) execve(CHROOTED_CMD_DIR "rsync",
				      target_argv, target_env);
		    }
		}
		else if (0 == strcmp(target_argv[0], "unison")) {
		    if (0 == filter_args_unison(target_argv)
			&& 0 == set_limits(NPROC_UNISON)) {
			(void) execve(CHROOTED_CMD_DIR "unison",
				      target_argv, target_env);
		    }
		}
		else {
		    if (  11 <= (len = strlen(target_argv[0]))
			&& 0 == memcmp(target_argv[0]+len-11, "sftp-server", 11)
			&& 0 == set_limits(NPROC_SFTP_SERVER)) {
			/*('chressh -c /usr/local/libexec/sftp-server')*/
			/*(only tests for "sftp-server" suffix, which is fine)*/
			/*(discard additional args to sftp-server, if present)*/
			char sftp_server[] = "sftp-server";
			char *target_argv_static[] = { sftp_server, NULL };
			(void) execve(CHROOTED_CMD_DIR "sftp-server",
				      target_argv_static, target_env);
		    }
		}


#if 0  /* which code do you think is clearer?  above or below? */

		switch ((len = strlen(target_argv[0]))) {
		  case  3:
		    if (0 == memcmp(target_argv[0], "scp", 3)
			&& 0 == set_limits(NPROC_SCP)) {
			(void) execve(CHROOTED_CMD_DIR "scp",
				      target_argv, target_env);
		    }
		    break;
		  case  5:
		    if (0 == memcmp(target_argv[0], "rsync", 5)
			&& 0 == filter_args_rsync(target_argv)
			&& 0 == set_limits(NPROC_RSYNC)) {
			(void) execve(CHROOTED_CMD_DIR "rsync",
				      target_argv, target_env);
		    }
		    break;
		  case  6:
		    if (0 == memcmp(target_argv[0], "unison", 6)
			&& 0 == filter_args_unison(target_argv)
			&& 0 == set_limits(NPROC_UNISON)) {
			(void) execve(CHROOTED_CMD_DIR "unison",
				      target_argv, target_env);
		    }
		    break;
		  default:
		    if (  11 <= len
			&& 0 == memcmp(target_argv[0]+len-11, "sftp-server", 11)
			&& 0 == set_limits(NPROC_SFTP_SERVER)) {
			/*('chressh -c /usr/local/libexec/sftp-server')*/
			/*(only tests for "sftp-server" suffix, which is fine)*/
			/*(discard additional args to sftp-server, if present)*/
			char sftp_server[] = "sftp-server";
			char *target_argv_static[] = { sftp_server, NULL };
			(void) execve(CHROOTED_CMD_DIR "sftp-server",
				      target_argv_static, target_env);
		    }
		    break;
		}
#endif


	    }
	}

      #ifdef PASSWD_PROGRAM
	/* If login attempt (argc == 1), use PASSWD_PROGRAM as 'shell'
	 * (*argv[0] == '-', too, for login shells on most (all?) systems)
	 * (privileges intentionally dropped even if passwd_program setuid)
	 */
	else if (1 == argc && 0 == setuid(getuid()) && 0 != setuid(0)) {
	    char passwd_program[] = PASSWD_PROGRAM;
	    char *target_argv[] = { passwd_program, NULL };
	    errno = 0;  /* (reset errno after expected setuid(0) failure) */
	    target_env = env_clean(target_env, pw, NULL);
	    (void) execve(PASSWD_PROGRAM, target_argv, target_env);
	}
      #endif

    }

    fatal(argc, pw, NULL);
    return 0; /*(UNREACHED)*/
}