xorl %eax, %eax

CVE-2009-3623: Linux kernel NFSv4 NULL Pointer Dereference

leave a comment »

So, this was reported to oss-security mailing list by Eugene Teo. However, the bug was discovered and reported by Bruce Fields as you can read in the equivalent GIT commit. The vulnerability affects Linux kernel releases between 2.6.31-rc1 and 2.6.32-rc1 and in my opinion, this vulnerability is a result of a design/logic flaw. As you can read from Eugene Teo’s email, rpcauth_lookup_credcache() assumes that the given authentication flavor has credentials cache without performing any checks. Here is a code snippet from 2.6.31 release of the Linux kernel that demonstrates this (as seen in net/sunrpc/auth.c):

/*
 * Look up a process' credentials in the authentication cache
 */
struct rpc_cred *
rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
                int flags)
{
        LIST_HEAD(free);
        struct rpc_cred_cache *cache = auth->au_credcache;
        struct hlist_node *pos;
        struct rpc_cred *cred = NULL,
                        *entry, *new;
        unsigned int nr;

        nr = hash_long(acred->uid, RPC_CREDCACHE_HASHBITS);
     ...
        hlist_for_each_entry_rcu(entry, pos, &cache->hashtable[nr], cr_hash) {
     ...
                cred = get_rpccred(entry);
     ...
        return cred;
}

By that small snippet you easily guess what is the purpose of this routine. Now, the bug was introduced in this commit. One of the first things that someone notices here is the introduction of a new function named lookup_cb_cred().

static struct rpc_cred *lookup_cb_cred(struct nfs4_callback *cb)
{
       struct auth_cred acred = {
               .machine_cred = 1
       };

       /*
        * Note in the gss case this doesn't actually have to wait for a
        * gss upcall (or any calls to the client); this just creates a
        * non-uptodate cred which the rpc state machine will fill in with
        * a refresh_upcall later.
        */
       return rpcauth_lookup_credcache(cb->cb_client->cl_auth, &acred,
                                                       RPCAUTH_LOOKUP_NEW);
}

Obviously, this is a simple wrapper around rpcauth_lookup_credcache() that uses ‘cb->cb_client->cl_auth’ NFSv4 callback routine as an argument to it along with the ‘RPCAUTH_LOOKUP_NEW’ constant which is defined in include/linux/sunrpc/auth.h like this:

/* Flags for rpcauth_lookupcred() */
#define RPCAUTH_LOOKUP_NEW              0x01    /* Accept an uninitialised cred */

The flaw is that a user that attempts to mount some shared directory with NFSv4 and auth_null RPC (which is used to declare a NULL NFS service authentication procedure) will result in a NULL pointer dereference in rpcauth_lookup_credcache() since it requires an ‘auth->au_credcache’ member but on NULL authentication this is not used and thus, it is NULL. The processing of that pointer later in rpcauth_lookup_credcache() results in a NULL pointer dereference vulnerability.
This was fixed by adding a new pointer to lookup_cb_cred() that will be used to store the callback function like this:

        struct auth_cred acred = {
                .machine_cred = 1
        };
+       struct rpc_auth *auth = cb->cb_client->cl_auth;
 
        /*
         * Note in the gss case this doesn't actually have to wait for a

And the buggy call to rpcauth_lookup_credcache() was also changed to use a callback function located at ‘cb->cb_client->cl_auth->au_ops->lookup_cred’ like this:

         * non-uptodate cred which the rpc state machine will fill in with
         * a refresh_upcall later.
         */
-       return rpcauth_lookup_credcache(cb->cb_client->cl_auth, &acred,
-                                                       RPCAUTH_LOOKUP_NEW);
+       return auth->au_ops->lookup_cred(auth, &acred, RPCAUTH_LOOKUP_NEW);
 }

This might seem weird at first, but when a user utilizes the auth_null option, the equivalent code in net/sunrpc/auth_null.c sets lookup_cred() callback to this:

/*
 * Lookup NULL creds for current process
 */
static struct rpc_cred *
nul_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
{
        return get_rpccred(&null_cred);
}
      ...
const struct rpc_authops authnull_ops = {
      ...
        .lookup_cred    = nul_lookup_cred,
};

Which means that the RPC authentication event would be handled by that routine.

Written by xorl

October 24, 2009 at 12:14

Posted in bugs, linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s