COMMAND
kernel (knfsd)
SYSTEMS AFFECTED
Linux
PROBLEM
Chris Evans found following. A DoS condition exists in the Linux
kernel knfsd server. Remote, unauthenticated users (i.e. those
with neither a directory mounted nor permission to mount one) can
OOPS the host kernel. The OOPS does not bring down the target
host, but it is possible to render the NFS service inoperable
until a reboot.
This issue is caused by signed/unsigned issues. A size check is
performed on an "int" (i.e. signed) variable. This size check
does not check for negative values. As a consequence it is
possible to point an internal buffer pointer _way_ before the
beginning of the buffer. The kernel will try to read a memory
address in limbo, and OOPS. The faulty code is in
kernel/net/sunrpc/svcauth.c, svcauth_unix()
int slen;
...
slen = ntohl(*bufp++); /* gids length */
if (slen > 16 || (len -= slen + 2) < 0)
goto badcred;
...
bufp += (slen - i);
As a general note, signed/unsigned errors like the above are
_almost_ as prevalent as buffer overflows. Once you start looking
for them. Re-auditing previously audited software with
signed/unsigned issues in mind often yields new problems. Note
that the userland sunrpc implementation suffered from a very
similar flaw a while back. No, it certainly wasn't a copy/paste
error.
Some signed/unsigned errors can lead to exploitable conditions
rather than just DoS. The following code fragment should
illustrate this:
func()
{
char name[NAMELEN];
int len = get_from_network();
if (len > NAMELEN)
goto i_dont_think_so;
memcpy(name, get_from_network(), len); /* -1 implicitly converted to
4Gb */
}
An interesting variant on this (that HAS been observed) is
func()
{
char* bufp;
char* bufmax = bufp + 100; /* Or whatever */
unsigned int len = get_from_network();
if (bufp + len > bufmax)
goto i_dont_think_so;
...
bufp += len;
}
On machines with a small 4Gb address space, e.g. Intel x86, a
suitable large value of len will wrap bufp around the address
space, leading to a position outside of the buffer boundary, but
satisfying the "bufp + len <= bufmax" constraint.
Programs which have in their history been found to include
signed/unsigned issues include
- knfsd
- sunrpc part of glibc
- xfs
- libORBIT
- issues at syscall API of various kernels
- issues in networking stacks
SOLUTION
The fix is present in the following kernels
- 2.3.99pre7-pre1
- 2.2.15pre20 (only available as a patch on top of 2.2.15pre19)
- The kernel update recently release by RedHat
Fix is also bwlow:
--- net/sunrpc/svcauth.c.old Tue Apr 18 05:13:47 2000
+++ net/sunrpc/svcauth.c Tue Apr 18 06:36:20 2000
@@ -4,6 +4,9 @@
* The generic interface for RPC authentication on the server side.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ *
+ * CHANGES
+ * 19-Apr-2000 Chris Evans - Security fix
*/
#include <linux/types.h>
@@ -117,7 +120,8 @@
struct svc_buf *resp = &rqstp->rq_resbuf;
struct svc_cred *cred = &rqstp->rq_cred;
u32 *bufp = argp->buf;
- int len = argp->len, slen, i;
+ int len = argp->len;
+ u32 slen, i;
if ((len -= 3) < 0) {
*statp = rpc_garbage_args;
@@ -127,7 +131,7 @@
bufp++; /* length */
bufp++; /* time stamp */
slen = (ntohl(*bufp++) + 3) >> 2; /* machname length */
- if (slen > 64 || (len -= slen) < 0)
+ if (slen > 64 || (len -= slen + 3) < 0)
goto badcred;
bufp += slen; /* skip machname */