| Summary: | [patch] linux_statfs() can leak fsid's to non-root users | ||
|---|---|---|---|
| Product: | Base System | Reporter: | David P.Reese Jr. <daver> |
| Component: | kern | Assignee: | Eric Anholt <anholt> |
| Status: | Closed FIXED | ||
| Severity: | Affects Only Me | ||
| Priority: | Normal | ||
| Version: | 5.1-RELEASE | ||
| Hardware: | Any | ||
| OS: | Any | ||
State Changed From-To: open->patched Fix committed to -current. Responsible Changed From-To: freebsd-bugs->anholt Fix committed to -current. State Changed From-To: patched->closed MFC Committed. Thanks! |
The linux emulator syscalls linux_statfs() and linux_fstatfs() can leak fsid's to non-root users because they don't check the user id of the calling thread. The native syscalls statfs() and fstatfs() do however check the user id of the calling thread and modify the statfs buffer before copyout. While this is not much of a concern with local filesystems, leaking this information about an NFS mount exposes part of the NFS file handle. Fix: Apply this patch to the kernel source: --- Begin Patch --- After rebuilding the Linux module and loading it, we notice that the fsid's are not being leaked to non-root users by using the above program: [daver@tombstone:~/security/freebsd/linux_statfs]$ ./statfs-linux /home f_fsid.val[0] = 0x0 f_fsid.val[1] = 0x0 [daver@tombstone:~/security/freebsd/linux_statfs]$ sudo ./statfs-linux /home f_fsid.val[0] = 0x3dcce3c0 f_fsid.val[1] = 0x3f68df57--gZuuxZFuPsl1OW9Wej1aa7ew2UseYILldqIB5gSfkcW3UCqR Content-Type: text/plain; name="file.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="file.diff" Index: compat/linux/linux_stats.c =================================================================== RCS file: /home/daver/cvs-repos/cvs-freebsd/src/sys/compat/linux/linux_stats.c,v retrieving revision 1.53 diff -u -r1.53 linux_stats.c --- compat/linux/linux_stats.c 29 Apr 2003 17:03:22 -0000 1.53 +++ compat/linux/linux_stats.c 1 Nov 2003 01:46:11 -0000 @@ -267,8 +267,13 @@ linux_statfs.f_bavail = bsd_statfs->f_bavail; linux_statfs.f_ffree = bsd_statfs->f_ffree; linux_statfs.f_files = bsd_statfs->f_files; - linux_statfs.f_fsid.val[0] = bsd_statfs->f_fsid.val[0]; - linux_statfs.f_fsid.val[1] = bsd_statfs->f_fsid.val[1]; + if (suser(td)) { + linux_statfs.f_fsid.val[0] = 0; + linux_statfs.f_fsid.val[1] = 0; + } else { + linux_statfs.f_fsid.val[0] = bsd_statfs->f_fsid.val[0]; + linux_statfs.f_fsid.val[1] = bsd_statfs->f_fsid.val[1]; + } linux_statfs.f_namelen = MAXNAMLEN; return copyout(&linux_statfs, args->buf, sizeof(linux_statfs)); } @@ -311,8 +316,13 @@ linux_statfs.f_bavail = bsd_statfs->f_bavail; linux_statfs.f_ffree = bsd_statfs->f_ffree; linux_statfs.f_files = bsd_statfs->f_files; - linux_statfs.f_fsid.val[0] = bsd_statfs->f_fsid.val[0]; - linux_statfs.f_fsid.val[1] = bsd_statfs->f_fsid.val[1]; + if (suser(td)) { + linux_statfs.f_fsid.val[0] = 0; + linux_statfs.f_fsid.val[1] = 0; + } else { + linux_statfs.f_fsid.val[0] = bsd_statfs->f_fsid.val[0]; + linux_statfs.f_fsid.val[1] = bsd_statfs->f_fsid.val[1]; + } linux_statfs.f_namelen = MAXNAMLEN; error = copyout(&linux_statfs, args->buf, sizeof(linux_statfs)); fdrop(fp, td); --- End Patch --- How-To-Repeat: Compile the following program under both FreeBSD and Linux. --- Begin Program --- #include <stdio.h> #include <stdlib.h> #include <err.h> #ifdef __linux__ #include <sys/vfs.h> #else #include <sys/param.h> #include <sys/mount.h> #endif int main(int argc, char **argv) { struct statfs buf; int error; if (argc != 2) errx(EXIT_FAILURE, "please give me a path"); error = statfs(argv[1], &buf); if (error) err(EXIT_FAILURE, "statfs()"); printf("f_fsid.val[0] = 0x%x\n", buf.f_fsid); printf("f_fsid.val[1] = 0x%x\n", (int)*((int32_t *)&buf.f_fsid + 1)); return (0); } --- End Program --- For convenience, I have provided binaries built under both FreeBSD 5.1 and Slackware Linux 9.0 in order to reproduce the problem at http://gomerbud.com/daver/patches/freebsd/linux_statfs/ Running the FreeBSD version of the program we see something like: [daver@tombstone:~/security/freebsd/linux_statfs]$ ./statfs-freebsd5 /home f_fsid.val[0] = 0x0 f_fsid.val[1] = 0x0 [daver@tombstone:~/security/freebsd/linux_statfs]$ sudo ./statfs-freebsd5 /home f_fsid.val[0] = 0x3dcce3c0 f_fsid.val[1] = 0x3f68df57 Running the Linux version of the program we see something like: [daver@tombstone:~/security/freebsd/linux_statfs]$ ./statfs-linux /home f_fsid.val[0] = 0x3dcce3c0 f_fsid.val[1] = 0x3f68df57 [daver@tombstone:~/security/freebsd/linux_statfs]$ sudo ./statfs-linux /home f_fsid.val[0] = 0x3dcce3c0 f_fsid.val[1] = 0x3f68df57 Thus, the Linux emulator syscalls are not doing the proper credential checking.