sftp-server: add a flag to call unveil on starting directory
s-k2 at caipora.net
s-k2 at caipora.net
Thu Jan 29 07:54:26 AEDT 2026
Dear list,
I would like to discuss whether it is possible to add an optional flag
to sftp-server that uses unveil to restrict filesystem access to the
starting directory and paths beneath it. Greping the mailing list
surprisingly revealed no discussion about unveil usage so far, thus I'll
try my luck here.
Such a flag would make it possible to apply restrictions for specific
SSH keys, for example by using them for just a specific file access
task. With the patch below, I can restrict the SFTP access of my music
player, while other keys retain normal access permissions, using an
authorized_keys file like this:
restrict="/usr/libexec/sftp-server -d /home/sk/music -U" MUSIC-DEV-KEY
ssh-ed25519 UNRESTRICTED-ACCESS-KEY
Would it be possible to add that feature? I know that this could also be
accomplished with an additional user and appropriate group permissions.
However, using different keys seems much easier to me and encourages
narrowing down restrictions to specific use cases.
While reading through the code, I noticed three extension commands that
are affected when a user activates that flag. All of them fail because
/etc/passwd is no longer accessible:
- "users-groups-by-id at openssh.com"
- "expand-path at openssh.com" (only when ~user is used)
- "home-directory"
In my oppinion that is not a real problem, as the user explicitly adds a
flag to limit filesystem access. I would argue against calling unveil
for the passwd file as well, because a user would not expect this to be
accessible anymore. Instead, I mention that in the man page.
I would be glad if you could share your opinions about that flag!
Kind regards,
Stefan
diff -u -r ssh-10.2/sftp-server.8 ssh/sftp-server.8
--- ssh-10.2/sftp-server.8 Tue Jul 27 16:14:25 2021
+++ ssh/sftp-server.8 Mon Jan 26 03:51:04 2026
@@ -31,7 +31,7 @@
.Sh SYNOPSIS
.Nm sftp-server
.Bk -words
-.Op Fl ehR
+.Op Fl ehRU
.Op Fl d Ar start_directory
.Op Fl f Ar log_facility
.Op Fl l Ar log_level
@@ -138,6 +138,13 @@
.Xr umask 2
to be applied to newly-created files and directories, instead of the
user's default mask.
+.It Fl U
+Restrict filesystem access to the starting directory and all paths beneath
+it using the
+.Xr unveil 2
+system call. Extended SFTP commands that read the
+.Xr passwd 5
+file may fail.
.El
.Pp
On some systems,
diff -u -r ssh-10.2/sftp-server.c ssh/sftp-server.c
--- ssh-10.2/sftp-server.c Tue Sep 2 11:26:21 2025
+++ ssh/sftp-server.c Mon Jan 26 03:57:27 2026
@@ -1859,7 +1859,7 @@
extern char *__progname;
fprintf(stderr,
- "usage: %s [-ehR] [-d start_directory] [-f log_facility] "
+ "usage: %s [-ehRU] [-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",
@@ -1870,7 +1870,7 @@
int
sftp_server_main(int argc, char **argv, struct passwd *user_pw)
{
- int i, r, in, out, ch, skipargs = 0, log_stderr = 0;
+ int i, r, in, out, ch, skipargs = 0, log_stderr = 0, unveil_cwd = 0;
ssize_t len, olen;
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
char *cp, *homedir = NULL, uidstr[32], buf[4*4096];
@@ -1884,7 +1884,7 @@
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:cehRU")) != -1) {
switch (ch) {
case 'Q':
if (strcasecmp(optarg, "requests") != 0) {
@@ -1946,6 +1946,9 @@
fatal("Invalid umask \"%s\"", optarg);
(void)umask((mode_t)mask);
break;
+ case 'U':
+ unveil_cwd = 1;
+ break;
case 'h':
default:
sftp_server_usage();
@@ -1981,6 +1984,13 @@
error("chdir to \"%s\" failed: %s", homedir,
strerror(errno));
}
+ }
+
+ if (unveil_cwd) {
+ if (unveil(".", "rwc") != 0)
+ error("unveil cwd failed: %s", strerror(errno));
+ if (unveil(NULL, NULL) != 0)
+ error("blocking unveil failed: %s", strerror(errno));
}
for (;;) {
More information about the openssh-unix-dev
mailing list