Chrootdirectory / Chroot - not working in OpenSSH sftp directives in \etc\sshd_config or using a custom shell script - MS OpenSSH build has a workaround
Peter Board
p_board@hotmail.com
Wed Mar 12 06:40:46 GMT 2025
Hi Cygwin Developers,
In the source code for the session.c Cygwin is using the standard OpenSSH source code, which checks for both the user ID of 0 and a permissions for who can write to the new folder.
Chroot mounting test that I can't get Cygwin to pass
if (st.st_uid != 0 || (st.st_mode & 022) != 0)
I have tried mapping the SYSTEM user and Group via the \etc\passwd and \etc\group files, but I am unable to get a successful setting that will allow Chroot.exe or \etc\sshd_config directive for rehoming an SFTP connection to work.
Match User username
ChrootDirectory F:\sftproot
ForceCommand internal-sftp
In the MS OpenSSH source code, they have switched Windows to just doing a basic directory exists check. Would it be possible to implement the same check in the Cygwin source code so that sftp root rehoming works again? I believe it was broken after OpenSSH 8.6 from my research, I have an older Cygwin setup based on OpenSSH 8.3 and the Chroot directive for SFTP root rehoming works fine.
session.c source code
/*
* Chroot into a directory after checking it for safety: all path components
* must be root-owned directories with strict permissions.
*/
static void
safely_chroot(const char *path, uid_t uid)
{
const char *cp;
char component[PATH_MAX];
struct stat st;
if (!path_absolute(path))
fatal("chroot path does not begin at root");
if (strlen(path) >= sizeof(component))
fatal("chroot path too long");
#ifdef WINDOWS
/* ensure chroot path exists and is a directory */
if (stat(path, &st) != 0)
fatal("%s: stat(\"%s\"): %s", __func__,
path, strerror(errno));
if (!S_ISDIR(st.st_mode))
fatal("chroot path %s is not a directory",
path);
#else
/*
* Descend the path, checking that each component is a
* root-owned directory with strict permissions.
*/
for (cp = path; cp != NULL;) {
if ((cp = strchr(cp, '/')) == NULL)
strlcpy(component, path, sizeof(component));
else {
cp++;
memcpy(component, path, cp - path);
component[cp - path] = '\0';
}
debug3_f("checking '%s'", component);
if (stat(component, &st) != 0)
fatal_f("stat(\"%s\"): %s",
component, strerror(errno));
if (st.st_uid != 0 || (st.st_mode & 022) != 0)
fatal("bad ownership or modes for chroot "
"directory %s\"%s\"",
cp == NULL ? "" : "component ", component);
if (!S_ISDIR(st.st_mode))
fatal("chroot path %s\"%s\" is not a directory",
cp == NULL ? "" : "component ", component);
}
#endif
if (chdir(path) == -1)
fatal("Unable to chdir to chroot path \"%s\": "
"%s", path, strerror(errno));
if (chroot(path) == -1)
fatal("chroot(\"%s\"): %s", path, strerror(errno));
if (chdir("/") == -1)
fatal_f("chdir(/) after chroot: %s", strerror(errno));
verbose("Changed root directory to \"%s\"", path);
}
Regards,
Peter Board
More information about the Cygwin
mailing list