2000-05-15
|
Pluggable Authentication Modules, or PAM, represent a standard whereby different authentication mechanisms can be utilized without needing to replace system binaries. Instead, a shared object can be created, and its utilization be dynamically selected based on the contents of an easily configured file. In this manner, different sites can select the authentication mechanism most appropriate for them, without sacrificing functionality, or needing to depend on unsupported software. Solaris and Linux both support PAM, albeit with slight differences. With the popularity of these two operating systems, many PAM modules are freely available. This makes it easy for the integration of new authentication mechanisms into your environment, should you be interested in trying something other than crypt(3). In this, our second article in the PAM series, we will gain an understanding of the PAM interface and how it operates, and then use a PAM authentication module, implementing X9.9 token based authentication, as a more serious model. PAM represents both a module standard, as well as a programming interface standard. We'll be discussing actual module implementation, as versus application implementation, and leave that for a future article. PAM modules themselves are shared objects, and are compiled like any shared object or library. The thing that makes a PAM module is the existence of a certain set of functions. These functions map to the different roles a module can take. As we saw in the previous PAM article, modules implement code to perform one or more of 4 classes of functions. auth, for authentication, account, for account management, session, for opening and closing sessions, and password, for doing password management. These each correspond to well known entry points inside the PAM module.
Extensive documentation exists for all of these functions, both in man page format, as well as an excellent set of documents on kernel.org. All of the documentation is well written, and concise, but sorely lacking in examples. The easiest way to get a good grasp of PAM is via an example. To that end, I elected to write what turned out to be a fairly complex module implementing X9.9 based authentication. The module itself only implements authentication -- another application exists for creating and populating the card database. I'll use the pam_sm_authenticate() function for an example, and leave deciphering the rest of the module to those interested in improving it for actual use; while it works well in my environment, it's sorely lacking in a few areas. We'll discuss those after going over the PAM specific portions. As mentioned, this module solely implements authentication. Therefore it need only contain an pam_sm_authenticate() function. I usually choose to implement stubs for all pam_sm_* functions, in order to prevent any issues should I make a mistake in a configuration file. They all simply return PAM_SUCCESS.
Line 100 The pam_sm_authenticate() function receives four arguments. The pam_handle_t *pamh is a reference to a function initialized when the PAM enabled application calls pam_start. It contains information associated with the process and user. flags contains optional information regarding the behavior of the authentication operation. Should it be silent and not generate any messages, or allow a user without a password to successfully log in? Finally, argc and argv are options passed via the configuration file. Line 108 To obtain the user asking for authentication, we call the pam_get_user() function, which takes the pam_handle_t passed in to the call to pam_sm_authenticate(), a character pointer, which is used to return the user name, and a prompt. The prompt is utilized if the username has not already been obtained prior to this call; this will be the prompt issued to the user. Normally, this information is already known at the time of the pam_get_user() call, and the prompt is not issued. The information returned by this function is not free()'d, as its merely a reference, and will be free()'d upon the application calling pam_end(); Line 112 If we were unable to obtain information about the user we're attempting to authenticate, or if the number of challenges remaining is 0, return an authentication error. I tend to install this module as 'sufficient', so failure here is not catastrophic -- users who have no card information will simply be authenticated using normal means. Lines 114-119 Get and issue a challenge to the user. Get the user's response. Lines 121-129 We need to perform the same operation the user's token did. We therefore encrypt the challenge with the user's DES key. Once we use the key, we overwrite it so it doesn't live in memory longer than it needs to, and free the entire user block. We then format the encrypted data to match the format the user will enter. Lines 131-133 If the two match up, we authenticate the user. The information being returned is a set of hex digits, so case is irrelevant. We only explicitly return success. This limits the possibility of a logic error resulting in a security issue to a single if() statement. The bulk of the code for the module lies in the support functions written to obtain information from the ndbm DB the user information is stored in, the challenge routines, and the encryption wrappers. In addition, I supplied a simple (and pretty messy) database initialization program. It will create a DES key for the user specified, printing out the random key in both hex and octal formats. Its called x99control. It takes 3 arguments at the moment; -l will cause it to list all the entries in the database, along with the associated key in hex and octal, and the remaining challenges for that key. -c will set a challenge count associated with a card. Setting this to -1 will result in the card never expiring. It is 65000 challenges by default (a fairly excessive number). Finally, the -d option, which takes a username as an argument, will delete the specified user from the database. This program should only be runnable by root. To install the module, simply place it somewhere on the filesystem; /lib/security is fairly standard on Linux, and /usr/lib/security on Solaris. My entry for the module on Solaris looks like this (/etc/pam.conf):
And on Linux (/etc/pam.d/su):
Installing it as "sufficient" dictates that if it succeeds, it is enough to log a user in, and additional checks will not take place. If the x99 module fails, however, it will run modules following it, until they too fail, or succeed, authenticating the user. The pam_x9.9 module is available at pam_x9.9-0.1.tar.gz. License information is in the LICENSE file -- for all purposes, it's a BSD style license. Type 'make' and follow the directions. I intended on using the libdes library, which is popular, but there were issues with in under Solaris. Instead, I elected to use the DES code from SSH2, which has a somewhat cleaner interface. You'll need to have at least the sshcrypt lib built. It expects the ssh root directory to be at the same level as the pam_x9.9-0.1 directory. It's a bit of a nuisance, and once I track down the issues with libdes, I will move to that. SSH is available at ssh.org. The Makefile is configured to use ssh-2.0.13. You'll need to manually install the shared library and x99control program once they're built. Please install and test CAREFULLY. I'd suggest testing it with something like 'su' first. Prior to configuring it, open a window or virtual console as root, and keep it open while testing. Once it seems to be working, then configure it for other things if you like. This code has been tested on Solaris 7, and RedHat Linux 6.2. I use a CryptoCard RB-1 token. I have also successfully used the SafeWord Gold and Platinum Cards from Secure Computing as well as the DESGold card from Enigma Logic, now part of Secure Computing. The module should work with any X9.9 card, including the SNK card, the SecurePilot DES Token, and numerous others. I'll be continuing to work on this code. Plans include moving the x99control to be a PAM application (fodder for a future article), cleaning up key management to allow easier dissemination of key information, as well as a way to import keys into the database. Figuring out what's wrong with libdes, and moving to that instead of the SSH2 DES routines might be nice as well, although the SSH2 interface is somewhat nicer. I'd like to support the 'event synchronous' mode these cards support -- I have had little luck in tracking down the algorithm details. If you have the information and are willing to share it with me, I'd appreciate it. Finally, the build environment is a complete and utter mess. Its simple enough I think autoconf would be overkill, but there is plenty of room for improvement. Finally, I need to mention how useful the snk code, by William LeFebvre, in the logdaemon package was initially. The first work I did with getting X9.9 authentication working was using his code and logdaemon, and they served well for a long time.
|
|
|