3.2 Programming with Directory Services

As a programmer, you frequently need to deal with directory information, whether you realize it or not. Your application uses Directory Services each time it looks up a host entry or authenticates a password. The Open Directory architecture unifies what used to be a random collection of flat files in /etc . The good news is that the flat files still work. The other good news is that there is a brave new world just beyond those flat files. So, while all your old Unix code should work with the Open Directory architecture, you should look for new ways to accomplish old tasks , especially if you can continue writing portable code.

To get at directory information, Unix applications typically go through the C library using such functions as gethostent( ) . The C library connects to lookupd , a thin shim that is the doorway to the DirectoryService daemon. The DirectoryService daemon consults the available plug-ins until it finds the one that can answer the directory query.

3.2.1 Working with Passwords

One traditional route to user and password information was through the getpw* family of functions. However, those functions are not ideal for working with systems that support multiple directories (flat files, NetInfo, LDAP, etc.). Also, in the interest of thwarting dictionary attacks against password files, many operating systems have stopped returning encrypted passwords through those APIs. Many Unix and Linux systems simply return an " x " when you invoke a function like getpwnam( ) . However, those systems can return an encrypted password through functions like getspnam( ) , which consult shadow password entries, and can generally be invoked by the root user only. Example 3-1 shows the typical usage of such an API, where the user enters her plaintext password, and the program encrypts it and then compares it against the encrypted password stored in the system.

Example 3-1. Using getpwnam( ) to retrieve an encrypted password
 /*  * getpw* no longer returns a crypted password.  *  * Compile with gcc checkpass.c -o checkpass  * Run with: ./checkpass  */ #include <pwd.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[])  {   const char *user = NULL;   struct passwd *pwd;   /* Set the user name if it was supplied on the command    * line.  Bail out if we don't end up with a user name.    */   if (argc == 2)     user = argv[1];   if(!user)    {     fprintf(stderr, "Usage: checkpass <username>\n");     exit(1);   }   /* Fetch the password entry. */   if (pwd = getpwnam(user))    {     char *password = (char *) getpass("Enter your password: ");     /* Encrypt the password using the encrypted password as salt.        * See crypt(3) for complete details.      */     char *crypted  = (char *) crypt(password, pwd->pw_passwd);     /* Are the two encrypted passwords identical? */     if (strcmp(pwd->pw_passwd, crypted) == 0)        printf("Success.\n");     else      {       printf("Bad password: %s != %s\n", pwd->pw_passwd, crypted);       return 1;     }   }    else    {     fprintf(stderr, "Could not find password for %s.\n", user);     return 1;   }   return 0; } 

As of Mac OS X Panther, your code no longer has a chance to look at an encrypted password. There are no functions such as getspnam( ) , and if you invoke a function like getpwnam( ) , you will get one or more asterisks as the result. For example:

 $  ./checkpass bjepson  Enter your password:  Bad password: ******** != **yRnqib5QSRI 

There are some circumstances where you can obtain an encrypted password, but this is not the default behavior of Mac OS X Panther. See the getpwent(3) manpage for complete details.


Instead of retrieving and comparing encrypted passwords, you should go through the Linux-PAM APIs. Since Linux-PAM is included with (or available for) many flavors of Unix, you can use it to write portable code. Example 3-2 shows a simple program that uses Linux-PAM to prompt a user for his password.

Example 3-2. Using Linux-PAM to authenticate a user
 /*  * Use Linux-PAM to check passwords.  *  * Compile with gcc pam_example.c -o pam_example -lpam  * Run with: ./pam_example <username>  */ #include <stdio.h> #include <pam/pam_appl.h> #include <pam/pam_misc.h> int main(int argc, char *argv[])  {   int retval;   static struct pam_conv pam_conv;   pam_conv.conv = misc_conv;   pam_handle_t *pamh = NULL;   const char *user = NULL;   /* Set the username if it was supplied on the command    * line. Bail out if we don't end up with a username.    */   if (argc == 2)     user = argv[1];   if(!user)    {     fprintf(stderr, "Usage: pam_example <username>\n");     exit(1);   }   /* Initialize Linux-PAM. */   retval = pam_start("pam_example", user, &pam_conv, &pamh);   if (retval != PAM_SUCCESS)    {     fprintf(stderr, "Could not start pam: %s\n",          pam_strerror(pamh, retval));     exit(1);   }   /* Try to authenticate the user. This could cause Linux-PAM    * to prompt the user for a password.    */   retval = pam_authenticate(pamh, 0);   if (retval == PAM_SUCCESS)      printf("Success.\n");   else      fprintf(stderr, "Failure: %s\n", pam_strerror(pamh, retval));   /* Shutdown Linux-PAM. Return with an error if     * something goes wrong.    */   return pam_end(pamh, retval) == PAM_SUCCESS ? 0 : 1; } 

In order for this to work, you must create a file called pam_sample in /etc/pam.d with the following contents (the filename must match the first argument to pam_start( ) ):

 auth       required  pam_securityserver.so account    required  pam_permit.so password   required  pam_deny.so 

Be careful when making any changes in the /etc/pam.d directory. If you change one of the files that is consulted for system login, you may lock yourself out of the system. For more information on Linux-PAM, see the pam(8) manpage.



Mac OS X Panther for Unix Geeks
Mac OS X Panther for Unix Geeks
ISBN: 0596006071
EAN: 2147483647
Year: 2003
Pages: 212

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net