Testing NSS modules in glibc

The Name Service Switch (NSS) is the feature of many C libraries, including the standard Linux one (GNU libc) and the standard Solaris one, that allows name lookup routines to be implemented via plugins. "Name lookup" here refers to things like usernames, host names, etc. When you run ls -l, and the ls command maps numeric user IDs to usernames, the names are provided by any module listed in the Name Service Switch's configuration. This could be the files module that looks at local files like /etc/passwd, the ldap module that talks to a corporate LDAP server, etc.

One of my half-forgotten side projects involves writing a new NSS module. NSS is configured via the file /etc/nsswitch.conf, which is systemwide configuration. If I want to test my NSS module, I could install it globally and reconfigure my system. But I started wondering if there was a way to load an NSS module just for a single process under my control. Since the process is running in my user account, I should be able to reconfigure it, without affecting the rest of the system.

Turns out that it's possible in a somewhat hackish way. There's an internal glibc function called __nss_configure_lookup that overrides NSS settings. If you're writing your own test program, you can just call, e.g., __nss_configure_lookup("passwd", "files") to force all user lookups to go through libnss_files. If you're using an existing program, you can shoehorn this in by use of an LD_PRELOAD:

#include <nss.h>
#include <stdlib.h>

static void __attribute__((constructor))
nsstest_ctor(void)
{
        const char *db = getenv("NSSTEST_DB"), *config = getenv("NSSTEST_CONFIG");
        if (db && config)
                __nss_configure_lookup(db, config);
}

Compile with gcc -fPIC -shared -o nsstest.so nsstest.c. Then you can do things like this:

howe-and-ser-moving:/tmp geofft$ ls -ld ~
drwxr-xr-x 355 geofft root 34816 Apr 17 21:25 /afs/athena.mit.edu/user/g/e/geofft
howe-and-ser-moving:/tmp geofft$ LD_PRELOAD=./nsstest.so NSSTEST_DB=passwd NSSTEST_CONFIG=files ls -ld ~
drwxr-xr-x 355 40490 root 34816 Apr 17 21:25 /afs/athena.mit.edu/user/g/e/geofft

Since my account isn't in the files database on this machine (it's in hesiod), my user ID can no longer be looked up if I restrict passwd lookups to the files database. A more straightforward way of testing is using the getent command that ships with glibc, which lets you ask for a specific entry in a specific NSS database. For instance, both files and hesiod have entries for the root user:

howe-and-ser-moving:/tmp geofft$ LD_PRELOAD=./nsstest.so NSSTEST_DB=passwd NSSTEST_CONFIG=files getent passwd root
root:x:0:0:root:/root:/bin/bash
howe-and-ser-moving:/tmp geofft$ LD_PRELOAD=./nsstest.so NSSTEST_DB=passwd NSSTEST_CONFIG=hesiod getent passwd root
root:*:0:101:Wizard A Root,,,:/mit/root:/bin/csh

If you're writing your own NSS library, you'll also need to set LD_LIBRARY_PATH to point to the directory where it lives, since NSS configuration just takes names, not full paths.

18 April 2015