NSS Overview

NSS Overview

NSS (Name Service Switch) is a feature first introduced by Sun and has subsequently been picked up by most all UNIX systems since, but generally with differing interfaces.

What NSS does is provide a common "funnel" through which simple system database lookup operations are fed. This funnel gives a system administrator runtime configurable control over these operations, including the ability to extend the implementation through plug-in runtime modules.

Our NSS implementation deliberately matches the external interface of the FreeBSD approach. Thus the C library provides nsdispatch(3C) and the header <nsswitch.h>.

The file /etc/nsswitch.conf defines the NSS configuration for the system, and its syntax is generally compatible in all NSS implementations. See nsswitch.conf(5) for details not covered in this overview. /etc/nsswitch.conf has a line-oriented syntax, ignoring empty lines and #-to-line-end style comments. It takes any number of entries, each of which are single lines (which can be extended to multiple lines by ending the current line with a backslash), each specifying the handling of a single database.

           database :
           database : module_list

module_list: module module_list module

module: name name [ acceptance_list ]

acceptance_list: acceptance acceptance_list acceptance

acceptance: status = action TRYAGAIN = action TRYAGAIN = FOREVER TRYAGAIN = decimal_number



In the above grammar, all-caps words are literals, although their case is ignored in the configuration file.

White space is ignored except as it serves to end and separate tokens.

At present the databases implemented to use NSS are "iaf", "group", "passwd", and "shadow". The standard module names are "compat", "files", and "nis", each of which is provided as built-in for these databases. Valid database and module names begin with a letter and are followed by any number of letters, digits, or underscore; they cannot be "forever" or the status or action keywords.

When nsdispatch(3C) is called, looking to perform a specified operation on a specified database, it searches for a matching database entry from /etc/nsswitch.conf. If found, it then runs through each of the modules listed for that database in order (left to right) checking whether they provide a method of the same name. Note that case is significant for the method strings, but is ignored for the databases.

As nsdispatch(3C) finds a module with a matching method, the method is called with three arguments, matching this typedef found in <nsswitch.h>:

   typedef int (*nss_method)(void *rv, void *mdata, va_list ap);

The first argument is a copy of nsdispatch's first argument, nominally the "return value" for the original lookup operation. The second argument is some method-specific data provided to nsdispatch along with the method address. The third argument is a handle on nsdispatch's trailing variable arguments, whose number and type are known by convention for each method. See nsswitch.conf(5) for details on these arguments for the implemented databases.

The method returns a integer value matching one of these constants found in <nsswitch.h>:

      NS_SUCCESS   operation worked
      NS_UNAVAIL   database (or a necessary resource) not available
      NS_NOTFOUND  no match found for the lookup
      NS_TRYAGAIN  resource temporarily unavailable; feel free to retry
      NS_RETURN    stop searching

If the value returned is acceptable, nsdispatch is done and returns that same value. Also note that if no match to the database/method pair is found, nsdispatch returns NS_NOTFOUND.

The default acceptance is to return for NS_SUCCESS or NS_RETURN only. This is specified as (NS_SUCCESS | NS_RETURN) as the above value are distinct bits. This combination is named NS_TERMINATE in <nsswitch.h>.

If an acceptance list is specified for the current module, then for any status with a "return" action, nsdispatch will do so; for those with a "continue" action, it will move on to the next module in the list. The default action for "success" is "return"; for all others, it's "continue".

For the special status of "tryagain" one can also specify a number of immediate retries for nsdispatch to perform. As long as the limit is not yet reached -- and "forever" means there is no limit -- nsdispatch will retry the same module's method with the same arguments as long as it returns NS_TRYAGAIN. (Note that this can readily result in an infinite loop, or at least a CPU hog if the blocking resource is not available again very soon.)

For each module specified in /etc/nsswitch.conf, nsdispatch will attempt to dlopen(3C) a runtime shared library with the pathname /usr/lib/nss/, where "module" is the name specified. If the dlopen succeeds, nsdispatch then tries to call a routine named nss_method_register in that library. This function is expected to provide an array of structures listing the database/method pairs provided by this module. See nsdispatch(3C) for further details.

As mentioned above, the iaf, group, passwd, and shadow database lookup APIs are implemented using NSS. Each of these provide an "override" method to use for the modules "compat", "files", and "nis". So, even if one were to provide runtime shared library modules in /usr/lib/nss/ named,, or, they would not have any effect for these databases.

Small NSS Example

Say you wanted to write your own NSS module to have the system use ahead of the built-in ones for the passwd database. First off, you'd need to know what methods are expected to be called for passwd and with what arguments. This is described in nsswitch.conf(5).

Let's keep it simple, and say that all we want to do is to deny the existence of the UID 2 (usually "bin"). This will not change the ownership of any files or the like, it merely will prevent a lookup in the passwd database from getting a match for the UID of 2.

This means minimally, that we need to provide a "getpwuid_r" method for the passwd database. Compile the following code as a runtime shared library -- see nsswitch.conf(5) to understand the arguments passed to the method for getpwuid_r -- and copy the result into the NSS modules directory:

   $ cat mod.c
   #include <pwd.h>
   #include <nsswitch.h>
   #include <stdarg.h>
   #include <string.h>

static int method(void *rv, void *mdata, va_list ap) { uid_t uid = va_arg(ap, uid_t); struct passwd **ptr = rv;

/* skip over next three variable args */ (void)va_arg(ap, struct passwd *); (void)va_arg(ap, char *); (void)va_arg(ap, size_t); *va_arg(ap, int *) = 0; *ptr = 0; return uid == 2 ? NS_NOTFOUND : NS_UNAVAIL; }

ns_mtab * nss_module_register(const char *modname, unsigned int *plen, nss_module_unregister_fn *fptr) { static ns_mtab mtab = {"passwd", "getpwuid_r", &method, 0};

*plen = 1; /* one ns_mtab item */ *fptr = 0; /* no cleanup needed */ return &mtab; } $ cc -KPIC -G -o mod.c $ cp /usr/lib/nss

Now change /etc/nsswitch.conf so that an NS_NOTFOUND return is accepted as the final answer for this module, but otherwise processes passwd operations as it does normally:

   $ grep ^passwd /etc/nsswitch.conf
   passwd: mod [notfound=return] compat

Now, try something like

   $ ls -l /usr/bin/cat
   -r-xr-xr-x    1 2        bin            6632 Oct 12  1999 /usr/bin/cat

You can see that you've managed to prevent ls from translating the UID of 2 owning /usr/bin/cat to "bin" as it would have normally. Congratulations.

Now you probably want to put the system back the way it was.

If you now check /var/adm/log/osmlog -- where syslog() messages are sent -- for NSS diagnostics, you'll likely see lines like the following:

   $ grep NSSWITCH: /var/adm/log/osmlog
   Feb 27 15:33:42 syslog: NSSWITCH: mod, passwd, setpwent, not found
   Feb 27 15:42:08 sendmail[17412]: NSSWITCH: mod, passwd, endpwent, not found

and, possibly quite a few of these.

This is because we've installed an incomplete module -- one lacking a full set of methods for the database. Normally when you write a "real" NSS module, you include all the methods, as otherwise you can get inconsistent system behavior.

© 2004 The SCO Group, Inc. All rights reserved.
UnixWare 7 Release 7.1.4 - 22 April 2004