sftp-server: add a chroot option

Dirk-Willem van Gulik dirkx at webweaving.org
Wed Apr 1 20:15:54 AEDT 2026


FWIIW - would love this go in !  

And this works cleanly for me on 14.3-RELEASE (fairly trivial setup where there is no shell or other access).

Dw

> On 1 Apr 2026, at 09:01, Eloi Benoist-Vanderbeken <eloi.benoist-vanderbeken at synacktiv.com> wrote:
> 
> Hi list,
> 
> Any news on this? It's a pretty simple patch and it should be harmless as it is very similar to ssh ChrootDirectory option.
> We can still think about the namespace option in the future but this implementation already offers a real security and usability advantage for most (all ?) of the platforms at almost no cost.
> 
> Kind regards,
> -- 
> Eloi Benoist-Vanderbeken
> Synacktiv
> +33 (0)6 67 92 63 35
> 
> Hi Jochen,
> 
>> If I understand correctly, you have to create a "fully equipped" chroot 
>> tree (with copies of all used libraries, $CHROOT/etc/passwd and 
>> $CHROOT/etc/group for proper "ls -l" output, maybe a $CHROOT/dev/log 
>> with the syslogd doing an extra LISTEN on it so as to have working 
>> logging, yadda yadda), anyway.
> 
> No, not at all, I call chroot when the process is initialized, so
> sftp-server already had the opportunity to open whatever it needs and now 
> only sees what the sftp user should be able to access (and not the 
> sftp-server executable nor /etc).
> 
> It's almost the same than the ChrootDirectory option with internal-sftp.
> That's also why I proposed it.

> Am 25.02.26 um 12:31 schrieb Eloi Benoist-Vanderbeken:
>> [...] I would like to add an option to chroot the sftp-server.
>> I am well aware that I could use ChrootDirectory with internal-sftp
>> but that doesn't work for me. [...]
> 
> If I understand correctly, you have to create a "fully equipped" chroot 
> tree (with copies of all used libraries, $CHROOT/etc/passwd and 
> $CHROOT/etc/group for proper "ls -l" output, maybe a $CHROOT/dev/log 
> with the syslogd doing an extra LISTEN on it so as to have working 
> logging, yadda yadda), anyway. If so, wouldn't wrapping the (unchanged) 
> sftp-server executable/process with the OS' chroot(1) command do the 
> trick already?
diff --git a/sftp-server.c b/sftp-server.c
index b98c3cd41..d7677d199 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -1888,7 +1888,8 @@ sftp_server_usage(void)
 	extern char *__progname;
 
 	fprintf(stderr,
-	    "usage: %s [-ehR] [-d start_directory] [-f log_facility] "
+	    "usage: %s [-ehR] [-C chroot_directory] "
+	    "[-d start_directory] [-f log_facility] "
 	    "[-l log_level]\n\t[-P denied_requests] "
 	    "[-p allowed_requests] [-u umask]\n"
 	    "       %s -Q protocol_feature\n",
@@ -1902,7 +1903,7 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw)
 	int i, r, in, out, ch, skipargs = 0, log_stderr = 0;
 	ssize_t len, olen;
 	SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
-	char *cp, *homedir = NULL, uidstr[32], buf[4*4096];
+	char *cp, *homedir = NULL, *chrootdir = NULL, uidstr[32], buf[4*4096];
 	long mask;
 
 	extern char *optarg;
@@ -1914,7 +1915,7 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw)
 	pw = pwcopy(user_pw);
 
 	while (!skipargs && (ch = getopt(argc, argv,
-	    "d:f:l:P:p:Q:u:cehR")) != -1) {
+	    "d:f:l:P:p:Q:u:C:cehR")) != -1) {
 		switch (ch) {
 		case 'Q':
 			if (strcasecmp(optarg, "requests") != 0) {
@@ -1937,6 +1938,9 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw)
 			 */
 			skipargs = 1;
 			break;
+		case 'C':
+			chrootdir = optarg;
+			break;
 		case 'e':
 			log_stderr = 1;
 			break;
@@ -2022,6 +2026,16 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw)
 	if ((oqueue = sshbuf_new()) == NULL)
 		fatal_f("sshbuf_new failed");
 
+	if (chrootdir != NULL) {
+		if (chdir(chrootdir) != 0) {
+			fatal_f("chdir to \"%s\" failed: %s", chrootdir,
+			    strerror(errno));
+		}
+		if (chroot(chrootdir) != 0)
+			fatal_f("chroot to \"%s\" failed: %s", chrootdir,
+			    strerror(errno));
+	}
+
 	if (homedir != NULL) {
 		if (chdir(homedir) != 0) {
 			error("chdir to \"%s\" failed: %s", homedir,



More information about the openssh-unix-dev mailing list