From daver@gomerbud.com  Fri Oct 31 19:09:24 2003
Return-Path: <daver@gomerbud.com>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 59C9416A4CE
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 31 Oct 2003 19:09:24 -0800 (PST)
Received: from typhoon.he.net (typhoon.he.net [64.62.229.2])
	by mx1.FreeBSD.org (Postfix) with SMTP id B020643FCB
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 31 Oct 2003 19:09:23 -0800 (PST)
	(envelope-from daver@gomerbud.com)
Received: from tombstone.localnet.gomerbud.com ([12.236.205.48]) by typhoon.he.net for <FreeBSD-gnats-submit@freebsd.org>; Fri, 31 Oct 2003 19:09:14 -0800
Received: by tombstone.localnet.gomerbud.com (Postfix, from userid 1001)
	id D9505575; Fri, 31 Oct 2003 19:12:31 -0800 (PST)
Message-Id: <20031101031231.D9505575@tombstone.localnet.gomerbud.com>
Date: Fri, 31 Oct 2003 19:12:31 -0800 (PST)
From: David P.Reese Jr. <daver@gomerbud.com>
Reply-To: David P.Reese Jr. <daver@gomerbud.com>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: [patch] linux_statfs() can leak fsid's to non-root users
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         58793
>Category:       kern
>Synopsis:       [patch] linux_statfs() can leak fsid's to non-root users
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    anholt
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Oct 31 19:10:20 PST 2003
>Closed-Date:    Mon Nov 17 15:28:17 PST 2003
>Last-Modified:  Mon Nov 17 15:28:17 PST 2003
>Originator:     David P. Reese Jr.
>Release:        FreeBSD 5.1-RELEASE-p10 i386
>Organization:
>Environment:
System: FreeBSD tombstone.localnet.gomerbud.com 5.1-RELEASE-p10 FreeBSD 5.1-RELEASE-p10 #33: Fri Oct 31 17:52:30 PST 2003 root@tombstone.localnet.gomerbud.com:/tmp/obj/usr/src/sys/TOMBSTONE i386
	
>Description:

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.

>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.

>Fix:

Apply this patch to the kernel source:

--- Begin Patch ---
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 ---

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
>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->patched 
State-Changed-By: anholt 
State-Changed-When: Wed Nov 5 15:52:59 PST 2003 
State-Changed-Why:  
Fix committed to -current. 


Responsible-Changed-From-To: freebsd-bugs->anholt 
Responsible-Changed-By: anholt 
Responsible-Changed-When: Wed Nov 5 15:52:59 PST 2003 
Responsible-Changed-Why:  
Fix committed to -current. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=58793 
State-Changed-From-To: patched->closed 
State-Changed-By: anholt 
State-Changed-When: Mon Nov 17 15:18:26 PST 2003 
State-Changed-Why:  
MFC Committed.  Thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=58793 
>Unformatted:
