[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Yet another attempt at /tmp spoof protection


Hi all,

Prompted by Mark's discussion of the transname patch I've put together
an experimental patch to the Linux nfsd that supports something I've named
CDFs for lack of a more appropriate name.  They're not real CDFs, and in
particular, they won't let you manage stuff like shared /etc directories
for diskless clients (it breaks /etc/mtab handling, for instance). I'm
not particularly proud of it since I consider CDFs a kludge by all means,
but as it was feasible with less than 10 minutes of hacking, I thought
I'd put this suggestion forward. As far as I can see, it suffers from none
of the problems Mark found with the transname patch, including the symlink
check some security-aware programs perform (since they usually check
whether the /tmp/foo file is a symlink, not /tmp itself).

It works basically by recognizing and replacing `magic' path components
at lookup time. To provide per-user /tmp directories transparently,
you would have to do something like this:

	# Create per-user temp directories
	mkdir -pm 755 /.tmp/users /.tmp/switch
	for uid in 0 1 2 3 4 5 6 .. 500 501 ...; do
		mkdir -m 755 /.tmp/users/$uid
		chown $uid.users /.tmp/users/$uid
		ln -s /.tmp/users/$uid /.tmp/switch/$uid
	mkdir -pm 1777 /.tmp/users/default;
	ln -s /.tmp/users/default /.tmp/switch/default

	# Prepare NFS-mounted /.tmp/switch directory
	mkdir -pm 755 /.tmp/nfs
	mount localhost:/.tmp/switch /.tmp/nfs -o noac

	# Replace old /tmp directory
	mv /tmp /tmp.old
	ln -s /.tmp/nfs/cdf:uid /tmp

That looks somewhat convoluted, so let me explain how it works: When
a process tries to look up /tmp, it will read the symlink pointing
to /.tmp/nfs/cdf:uid. Looking up the symlink, it will encounter the NFS
mount point (/.tmp/nfs), and ask the NFS server to look up "cdf:uid".
Nfsd will recognize the special name and replace it with the requesting
process' numeric uid (let's say 500) before looking it up. It will
now encounter yet another symlink, this time pointing to /.tmp/users/500
which is the user's private temporary directory. Once the kernel has
obtained this path, subsequent lookups will not have to go through the
server any more.

The /.tmp/switch/default symlink is somewhat special; this is the
fallback name used when nfsd is not able to look up the uid-specific
directory. In the above setting, it provides the usual /tmp semantics
for uids for which you haven't created a specific /.tmp/users directory.
If you choose to omit the default entry, a lookup of cdf:uid for such a
uid will fail, which may or may not be what you want.

Note the noac option when mounting /.tmp/switch over NFS. This is
absolutely crucial, because otherwise the kernel would cache the result
of looking up cdf:uid, leading to random behavior. Also take care of
the export options for /.tmp/switch: using ro is not a problem but doesn't
add much in terms of security. It's up to you whether you enable root
squashing or not; enabling it may have unexpected side effects...

The above setup should also work nicely with setuid applications that
`forget' to yield their privileges before creating or manipulating a file
in /tmp.  Since the kernel uses the fsuid of the process (which is in
effect its euid most of the time) when constructing the RPC credentials
for the NFS call, it will automatically be redirected to its uid-specific
/.tmp/users directory that cannot be booby-trapped by an attacker.

Depending on your boot setup, you may also have to modify your rc files
because nfsd my not yet be running when the first programs try to use
/tmp. In this case, make /tmp a symlink to some other location first,
and flip it to /.tmp/nfs/cdf:uid after nfsd is running.

Oh, I almost forgot to mention this: to enable CDF support in nfsd, you
must edit the Makefile and add -DSUPPORT_CDF to the NFSD_DEFS variable.
The complete source for nfsd-2.2beta23 can be found on 
ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir .

Criticism and suggestions welcome, but please allow for some days before
I find the time to reply.

Happy hacking
<A HREF="; mailx -s 'youve been hacked' $LOGNAME">Test your mailer!</A>
- ------------------------------------------------------------------
diff -ur nfs-server-2.2beta23/ChangeLog nfs-server-2.2beta23/ChangeLog
- --- nfs-server-2.2beta23/ChangeLog	Wed Sep 25 01:22:27 1996
+++ nfs-server-2.2beta23/ChangeLog	Fri Nov 15 11:50:52 1996
@@ -1,3 +1,7 @@
+Fri Nov 15 11:50:28 1996  Olaf Kirch <okir monad swb de>
+	* Added experimental CDF support. See README.CDF for details.
 Wed Sep 25 01:19:25 1996  Olaf Kirch <okir monad swb de>
 	* Fixed a very old bug in the file handle cache. When many
diff -ur nfs-server-2.2beta23/Makefile.in nfs-server-2.2beta23/Makefile.in
- --- nfs-server-2.2beta23/Makefile.in	Fri Sep 20 17:35:50 1996
+++ nfs-server-2.2beta23/Makefile.in	Fri Nov 15 10:43:06 1996
@@ -42,8 +42,13 @@
 #	This is a hack that lets DOS boxes mount NFS volumes with all_squash
 #	enabled and actually write to them, even though their uid/gid does
- -#	not match the non_uid/anon_gid. Ugly, and not officially supported.
+#	not match the anon_uid/anon_gid. Ugly, and not officially supported.
 #	(Don't you call me a hypocrite:)
+#	Experimental support for context-dependent files on LOOKUP.
+#	Currently supported names are cdf:uid and cdf:hostaddr. When a
+#	CDF component cannot be looked up, it will be replaced with
+#	the catch-all name "default".
 #	General debugging enable flag
diff -ur nfs-server-2.2beta23/nfsd.c nfs-server-2.2beta23/nfsd.c
- --- nfs-server-2.2beta23/nfsd.c	Wed Sep 25 00:37:42 1996
+++ nfs-server-2.2beta23/nfsd.c	Fri Nov 15 10:45:37 1996
@@ -85,6 +85,9 @@
 static _PRO (void usage, (FILE *, int)					);
 static _PRO (void terminate, (void)					);
 static _PRO (RETSIGTYPE sigterm, (int sig)				);
+static _PRO (char *cdf_translate, (char *tag)				);
@@ -279,6 +282,24 @@
 	if (auth_fh(rqstp, &(argp->dir), &status, CHK_READ) == NULL)
 		return status;
+	/* This is just some experimental support for context-dependent
+	 * files in order to cope with mindless use of /tmp.
+	 */
+	if (!strncmp(argp->name, "cdf:", 4)) {
+		char		*oldname = argp->name;
+		if ((argp->name = cdf_translate(argp->name + 4)) != NULL) {
+			status = fh_compose(argp, &(dp->file), &sbp, -1, -1);
+		} else
+			status = NFSERR_NOENT;
+		if (status == NFSERR_NOENT) {
+			argp->name = "default";
+			status = fh_compose(argp, &(dp->file), &sbp, -1, -1);
+		}
+		argp->name = oldname;
+	} else
 	status = fh_compose(argp, &(dp->file), &sbp, -1, -1);
 	if (status != NFS_OK)
 		return status;
@@ -294,6 +315,22 @@
 	return (status);
+static char *
+cdf_translate(char *tag)
+	static char	buffer[512];
+	if (tag[0] == 'u' && !strcmp(tag, "uid"))
+		sprintf(buffer, "%d", auth_uid);
+	else if (tag[0] == 'h' && !strcmp(tag, "hostaddr"))
+		sprintf(buffer, "%s", inet_ntoa(nfsclient->clnt_addr));
+	else
+		return NULL;
+	return buffer;
 int nfsd_nfsproc_readlink_2(argp, rqstp)
 nfs_fh *argp;
 struct svc_req	*rqstp;

Version: 2.6.2i


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]