[PATCH] improved chroot handling

Luc I. Suryo luc at suryo.com
Thu Jun 27 09:17:47 EST 2002


Tony,

it is maybe me but the code:

	char emptydir[] = "/var/tmp/sshd.XXXXXXXXXX";

is hard coded...and we want to use what is defined by

	_PATH_PRIVSEP_CHROOT_DIR 

yes? 

and should not one make sure that there is no overflow in
emptydir??? malloc/free/strlen and that kinda of stuff 


just me quick 25c :)
 

> There are a couple of niggles with the sandboxing of the unprivileged
> child in the privsep code: the empty directory causes namespace pollution,
> and it requires care to ensure that it is set up properly and remains set
> up properly. The patch below (against the portable OpenSSH, although the
> patch against the OpenBSD version is very similar) replaces the fixed
> empty directory with one that is created on demand and is immediately
> removed after the child process has chdir()ed and chroot()ed into it.
> This ensures that the directory is in a known-safe state and that no-one
> (not even root) can mess it up.
> 
> Index: pathnames.h
> ===================================================================
> RCS file: /home/ncvs/src/crypto/openssh-portable/pathnames.h,v
> retrieving revision 1.1.1.1
> diff -u -r1.1.1.1 pathnames.h
> --- pathnames.h	24 Jun 2002 22:46:13 -0000	1.1.1.1
> +++ pathnames.h	26 Jun 2002 17:58:59 -0000
> @@ -145,11 +145,6 @@
>  #define _PATH_SFTP_SERVER		"/usr/libexec/sftp-server"
>  #endif
>  
> -/* chroot directory for unprivileged user when UsePrivilegeSeparation=yes */
> -#ifndef _PATH_PRIVSEP_CHROOT_DIR
> -#define _PATH_PRIVSEP_CHROOT_DIR	"/var/empty"
> -#endif
> -
>  #ifndef _PATH_LS
>  #define _PATH_LS			"ls"
>  #endif
> Index: sshd.c
> ===================================================================
> RCS file: /home/ncvs/src/crypto/openssh-portable/sshd.c,v
> retrieving revision 1.1.1.1
> diff -u -r1.1.1.1 sshd.c
> --- sshd.c	24 Jun 2002 22:46:20 -0000	1.1.1.1
> +++ sshd.c	26 Jun 2002 18:00:25 -0000
> @@ -545,14 +545,9 @@
>  	memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
>  	endpwent();
>  
> -	/* Change our root directory*/
> -	if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1)
> -		fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR,
> -		    strerror(errno));
> -	if (chdir("/") == -1)
> -		fatal("chdir(\"/\"): %s", strerror(errno));
> -
> -	/* Drop our privileges */
> +	/* Change our root directory and drop privileges */
> +	if (chroot(".") < 0)
> +		fatal("chroot(): %s\n", strerror(errno));
>  	debug3("privsep user:group %u:%u", (u_int)pw->pw_uid,
>  	    (u_int)pw->pw_gid);
>  	do_setusercontext(pw);
> @@ -561,6 +556,7 @@
>  static Authctxt*
>  privsep_preauth(void)
>  {
> +	char emptydir[] = "/var/tmp/sshd.XXXXXXXXXX";
>  	Authctxt *authctxt = NULL;
>  	int status;
>  	pid_t pid;
> @@ -570,12 +566,31 @@
>  	/* Store a pointer to the kex for later rekeying */
>  	pmonitor->m_pkex = &xxx_kex;
>  
> +	/*
> +	 * We create a safe environment for the child by creating an empty
> +	 * directory into which the child chroots, and the parent prevents
> +	 * others from fooling around with it by removing the directory. We do
> +	 * it this way because the child can't remove its own current working
> +	 * directory (except on some systems by giving an absolute path to
> +	 * rmdir, but it is highly dependent on the OS and filesystem). We
> +	 * create the directory in /var/tmp in order that we are more likely
> +	 * to get a well-behaved disk filesystem.
> +	 */
> +	if (mkdtemp(emptydir) == NULL)
> +		fatal("mkdtemp(\"%s\"): %s", emptydir, strerror(errno));
> +
>  	pid = fork();
>  	if (pid == -1) {
>  		fatal("fork of unprivileged child failed");
>  	} else if (pid != 0) {
>  		debug2("Network child is on pid %ld", (long)pid);
>  
> +		/* Wait for the child to chdir then remove the directory */
> +		if (read(pmonitor->m_recvfd, &status, 1) < 0)
> +			fatal("read(): %s", strerror(errno));
> +		if (rmdir(emptydir) < 0)
> +			fatal("rmdir(\"%s\"): %s", emptydir, strerror(errno));
> +
>  		close(pmonitor->m_recvfd);
>  		authctxt = monitor_child_preauth(pmonitor);
>  		close(pmonitor->m_sendfd);
> @@ -591,6 +606,10 @@
>  	} else {
>  		/* child */
>  
> +		if (chdir(emptydir) == -1)
> +			fatal("chdir(\"%s\"): %s", emptydir, strerror(errno));
> +		if (write(pmonitor->m_sendfd, &status, 1) < 0)
> +			fatal("write(): %s", strerror(errno));
>  		close(pmonitor->m_sendfd);
>  
>  		/* Demote the child */
> @@ -1008,10 +1027,6 @@
>  		if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL)
>  			fatal("Privilege separation user %s does not exist",
>  			    SSH_PRIVSEP_USER);
> -		if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) ||
> -		    (S_ISDIR(st.st_mode) == 0))
> -			fatal("Missing privilege separation directory: %s",
> -			    _PATH_PRIVSEP_CHROOT_DIR);
>  	}
>  
>  	/* Configuration looks good, so exit if in test mode. */
> 
> 
> Tony.
> -- 
> f.a.n.finch <dot at dotat.at> http://dotat.at/
> FISHER GERMAN BIGHT: WESTERLY VEERING NORTHWESTERLY 4 OR 5, OCCASIONALLY 6.
> SHOWERS. MODERATE OR GOOD.
> _______________________________________________
> openssh-unix-dev at mindrot.org mailing list
> http://www.mindrot.org/mailman/listinfo/openssh-unix-dev

--- End of dot at dotat.at's quote ---

-- 
Kind regards,
Luc Suryo



More information about the openssh-unix-dev mailing list