From 1dc6d558115746f50e78ba8d84915a56ae66ec5f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 16:01:54 -0700 Subject: [PATCH] tftpd: handle restricted user namespaces on Linux If the tftpd process lacks the privilege to set its uid, gid, or groups, then assume it is already restricted in the way the administrator requested, if and only if EPERM is returned. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index f6d0565..9abf009 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -374,6 +374,7 @@ int main(int argc, char **argv) int spec_umask = 0; int c; int setrv; + int die; int waittime = 900; /* Default time to wait for a connect */ const char *user = "nobody"; /* Default user */ char *p, *ep; @@ -974,21 +975,30 @@ int main(int argc, char **argv) exit(EX_IOERR); } - /* Set up the supplementary group access list if possible */ - /* /etc/group still need to be accessible at this point */ + /* Set up the supplementary group access list if possible + /etc/group still need to be accessible at this point. + If we get EPERM, this is already a restricted process, e.g. + using user namespaces on Linux. */ + setrv = -1; + die = 0; #ifdef HAVE_INITGROUPS setrv = initgroups(user, pw->pw_gid); - if (setrv) { + if (setrv && errno != EPERM) { syslog(LOG_ERR, "cannot set groups for user %s", user); - exit(EX_OSERR); + die = EX_OSERR; } -#else +#endif #ifdef HAVE_SETGROUPS - if (setgroups(0, NULL)) { - syslog(LOG_ERR, "cannot clear group list"); + if (setrv) { + setrv = setgroups(0, NULL); + if (setrv && errno != EPERM) { + syslog(LOG_ERR, "cannot clear group list"); + die = EX_OSERR; + } } #endif -#endif + if (die) + exit(die); /* Chroot and drop privileges */ if (secure) { @@ -1000,11 +1010,15 @@ int main(int argc, char **argv) chdir("/"); /* Cygwin chroot() bug workaround */ #endif } + #ifdef HAVE_SETREGID setrv = setregid(pw->pw_gid, pw->pw_gid); #else setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); #endif + if (setrv && errno == EPERM) { + setrv = 0; /* Already restricted */ + } #ifdef HAVE_SETREUID setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid); @@ -1013,6 +1027,9 @@ int main(int argc, char **argv) setrv = setrv || setuid(pw->pw_uid) || (geteuid() != pw->pw_uid && seteuid(pw->pw_uid)); #endif + if (setrv && errno == EPERM) { + setrv = 0; /* Already restricted */ + } if (setrv) { syslog(LOG_ERR, "cannot drop privileges: %m");