Mostly Content-Free Weblog by Nalin Dahyabhai
Mon, 19 Feb 2007
More on Offline Authentication

Thought about the offline authentication problem and pam_krb5 some more. If we just let the helper contact the KDC, we double the number of round trips made between the client and the KDC unless we provide some way for the helper to pass the obtained credentials back to the calling process, which isn't good. If we don't do that, then we may cost the user in terms of failed logins, which could trigger a KDC-enforced lockout policy.

At least we've got another test release before we feature freeze for FC7, so we've got time to think about this some more.

[/development] permanent link
Oh, and Before I Forget

In summary, Connectathon totally ruled.

[/development] permanent link
glibc's nsswitch subsystem; Recommendations for Prospective Module Writers

This is a digest of some notes I've had for a while, recorded here in case someone else finds them useful. (Let me know if I'm wrong on any of this.) No, there's not really much of a point to this.

When applications on Linux systems want to get information about a user, they frequently call either getpwnam() or getpwuid(), passing in the user's name or UID, respectively. Likewise, for groups, getgrnam() and getgrgid().

When that happens, libc consults /etc/nsswitch.conf for the configuration which describes how it will answer the application's question. Absent any control flags, it will often look like this:

passwd: files hesiod nis ldap

For each module listed, libc constructs a soname (libnss_foo.so.version), uses dlopen() to load the named library, and calls into a function provided by that library. On most systems (including all of the platforms Fedora builds on), version is hard-coded to 2, but on a few, it's 1.

Exactly which function the shared library should export, and what its signature should be, varies based on which function the application actually called, but there's a pattern.

getpwnam(),getpwnam_r() → _nss_foo_getpwnam_r()

getpwuid(),getpwuid_r() → _nss_foo_getpwuid_r()

The exported functions don't return a pointer to some global result structure. Rather, they fill in a passed-in structure and return a result code. The function also gets a chunk of memory which it can use to store items of which the structure will hold a pointer (for example, all of the string members of a struct passwd).

For functions which only return one result (getpwnam, getpwuid, and so on), libc by default stops at the first function which returns a success. For enumeration functions, libc generally iterates through all of the configured modules. There's more detail in the glibc info pages, but that's the basic idea. Note that the details of how this fits together varies among libc implementations.

Building a good module can be tricky.

The calling application may be multithreaded, so your module needs to be thread-safe. But it may also not be multithreaded, so your module can't be dependent on threading functions. This isn't so difficult as it might sound because certain threading functions are also available, in no-op stub form, to non-threaded applications. So long as you limit yourself to these, you'll be fine in both cases.

pthread_mutex_lock()/pthread_mutex_unlock()

pthread_cond_init()/pthread_cond_destroy()

pthread_cond_broadcast()

pthread_cond_signal()

pthread_cond_timedwait()

pthread_cond_wait()

These days, you also have thread-local storage.

Because your module may be loaded by many, many processes (including otherwise lightweight programs such as "ls" or "find"), keep your code size and list of external dependencies small. Ideally, link to nothing other than libc itself.

One popular strategy for achieving this is to make your module a stub which contacts a local daemon, and to have that daemon do all of your heavy lifting. This is the approach being considered by at least one module named nss-mysql, and is the one used in Doug Nazar's nss_ldap2, Arthur de Jong's nss_ldapd, and (probably the best-known example) Samba's winbindd. Crutcher and I even took a stab at it a while back with splatbind.

A slightly less popular strategy is to use linker flags and static libraries to pull all of your module's dependencies into the module itself. It's what we do with nss_db in Fedora, and made far easier because upstream Berkeley DB packaging includes support for this sort of scenario.

Your module may be called while the system is not yet fully "up", and again while the system is in the midst of shutting down. If you depend on the network, keep in mind that it may not be there.

The calling application may fork() between calls into your module. You must handle this gracefully, including making sure that parent and child don't get tripped up by the fact that they're now sharing an open connection to every resource the parent used to have.

Take care that the right thing happens when the calling application calls exec(), whether or not it's preceded by fork().

If you call into libc's nsswitch subsystem (which happens most often when a library which you're using needs to resolve a hostname), and especially if you offer hostname resolution services, be mindful that you may end up inadvertently calling yourself, either directly or indirectly with the help of a daemon such as nscd.

[/development] permanent link
RSS
Powered by Blosxom
Validate XHTML Validate CSS