Further Reading

Understanding and Deploying LDAP Directory Services > 20. Developing New Applications > Example 1: A Password-Resetting Utility

<  BACK CONTINUE  >
153021169001182127177100019128036004029190136140232051053054012002228051030153151027027

Example 1: A Password-Resetting Utility

Because end users often forget their passwords, one of the most common tasks for help desk personnel is to assign new passwords. Organizations that have deployed a directory service need to enable their help desk personnel to easily reset passwords stored in the directory. In this section, we present a command-line utility called setpwd that resets the password in a user 's directory entry to a randomly chosen one.

Directory Use

The setpwd utility uses the directory service in a simple way. All interaction with the LDAP service is performed using a special help desk identity that is given permission to modify the userPassword attribute in all person entries. Given a person's user ID that is supplied on its command line, the setpwd application locates the person's directory entry via an LDAP search operation. If one entry is found, a random password is generated and the existing userPassword values in the entry are replaced with the new password.

Because this is a simple directory application, there are no significant application architecture issues to consider. The setpwd application does, however, avoid reading unnecessary attributes from the user entries. In addition, setpwd uses only one LDAP connection even when it resets several users' passwords.

The Source Code

The setpwd utility is written in C, and it uses the Netscape Directory SDK for C (which provides a standard LDAPv3 C API). Nearly all the functions that are part of the LDAP C API begin with the prefix ldap_ . The code was compiled and tested under the Sun Solaris 2.6 UNIX operating system, although it should be easy to port to other systems and implementations of the LDAP C API. The entire source code for setpwd consists of one file that is called, logically enough, setpwd.c . We present the code in pieces to aid understanding.

The beginning of the setpwd.c file is shown in Listing 20.2. Notice the inclusion of the standard LDAP header file on line 9.

Listing 20.2 The setpwd.c prelude

1. /* 2. * A simple password resetting program for use by a helpdesk. 3. */ 4. #include <stdio.h> 5. #include <string.h> 6. #include <stdlib.h> 7. #include < ctype .h> 8. #include <time.h> 9. #include <ldap.h> 10. #define LONGEST_WORD 100 11. /* 12. * Global variables : 13. */ 14. char *base = "dc=airius,dc=com"; 15. char *host = "directory.airius.com"; 16. int port = LDAP_PORT; 17. char *binddn = "cn=HelpDesk, dc=airius, dc=com"; 18. char *bindpwd = "secret#password"; 19. char *wordfile = "/usr/dict/words"; 20. int quiet = 0; /* setenv QUIET to suppress chatter */ 21. /* 22. * Function prototypes : 23. */ 24. static int resetpwd( LDAP *ld, char *base, char *userid ); 25. static char *userid2dn( LDAP *ld, char *base, char * userid ); 26. static void print_ldap_error( LDAP *ld, int lderr, char *s ); 27. static char *randompwd( void ); 28. static char * randomword( void );

Some of LDAP- related global variables are defined and set on lines 14 “19. The distinguished name (DN) and password used to bind to the directory s ervice are hard-coded into this application (lines 17 and 18), which is unusual but convenient for the help desk personnel who are the audience for this application. The wordfile variable defined on line 19 has nothing to do with LDAP; it is used by setpwd 's random password generation code (described later).

The setpwd main() function is shown in Listing 20.3.

Listing 20.3 The setpwd.c main () function

1. int 2. main( int argc, char **argv ) 3. { 4. LDAP *ld; 5. int i, rc; 6. quiet = (( getenv( "QUIET" ) != NULL )); 7. if ( argc < 2 ) { 8. fprintf( stderr, "usage: %s userid \n", argv[ 0 ] ); 9. return( 2 ); 10. } 11. /* 12. * Connect and bind to the directory server. 13. */ 14. if ( !quiet ) puts( "Contacting LDAP server " ); 15. if (( ld = ldap_init( host, port )) == NULL ) { 16. perror( host ); 17. return( 1 ); 18. } 19. if (( rc = ldap_simple_bind_s( ld, binddn, bindpwd )) 20. != LDAP_SUCCESS ) { 21. print_ldap_error( ld, rc, binddn ); 22. ldap_unbind( ld ); 23. return( 1 ); 24. } 25. /* 26. * Process userid arguments, setting random passwords 27. * for each one. 28. */ 29. rc = 0; 30. srandom( time( NULL )); 31. for ( i = 1; i < argc; ++i ) { 32. if ( !quiet && i > 1 ) { 33. putchar ( '\n' ); 34. } 35. rc = resetpwd( ld, base, argv[ i ] ); 36. } 37. /* 38. * close LDAP server connection and exit. 39. */ 40. ldap_unbind( ld ); 41. return( rc ); 42. }

After some simple argument checking and initialization, the code begins to interact with the LDAP SDK and the directory service. The code on lines 15 “18 obtains an LDAP session handle for our LDAP server host and port. Note that the ldap_init() function call does not open a connection to the LDAP server. A connection to the LDAP server is opened the first time an LDAP request is made.

Such a request is made by the code on lines 19 “24, which binds to the LDAP server using the help desk DN and password. The ldap_simple_bind_s() call connects to the directory server (if the session handle is not already connected, as is the case here), issues an LDAP bind request, and waits for the server response. A return code of LDAP_SUCCESS indicates that the DN and password were accepted and that the authentication was successful.

The code on lines 25 “36 moves through the arguments provided on the command line and passes each one to the resetpwd() function, which is described next . The ldap_unbind() call included in the clean-up code on lines 37 “42 does two things: It closes the LDAP connection and disposes of the memory and any other resources consumed by the LDAP session handle.

The code for the resetpwd() function, which is responsible for resetting one user's password, is shown in Listing 20.4.

Listing 20.4 The setpwd.c resetpwd () function

1. /* 2. * resetpwd(): set an entry's password to a random string. 3. * 4. * Returns 0 on success and 1 on failure. 5. */ 6. static int 7. resetpwd( LDAP *ld, char *base, char *userid ) 8. { 9. char *dn, *pwd, *vals[2]; 10. int rc; 11. LDAPMod mod, *mods[2]; 12. /* 13. * Find the entry. 14. */ 15. if ( !quiet ) printf( "Finding entry %s \n", userid ); 16. if (( dn = userid2dn( ld, base, userid )) == NULL ) { 17. return( 1 ); 18. } 19. if (( pwd = randompwd() == NULL ) { 20. return( 1 ); 21. } 22. /* 23. * Replace the userPassword attribute. 24. */ 25. if ( !quiet ) printf( "Modifying entry %s \n", userid ); 26. vals[0] = pwd; 27. vals[1] = NULL; 28. mod.mod_values = vals; 29. mod.mod_type = "userPassword"; 30. mod.mod_op = LDAP_MOD_REPLACE; 31. mods[0] = &mod; 32. mods[1] = NULL; 33. if (( rc = ldap_modify_s( ld, dn, mods )) == LDAP_SUCCESS ) { 34. rc = 0; 35. printf( "Password reset. %s's new password is: %s\n", 36. userid, pwd ); 37. if ( !quiet ) { 38. printf( "Don't forget to remind %s to reset " 39. "his/her password right away!\n", userid ); 40. } 41. } else { 42. rc = 1; 43. print_ldap_error( ld, rc, "modify" ); 44. } 45. free( pwd ); 46. ldap_memfree( dn ); 47. return( rc ); 48. }

The first task performed by this code is to find the user's directory entry (we need to know the DN to be able to modify the user's entry). The code on lines 12 “18 calls a utility function named userid2dn() (described next) to find and return the user's DN given his or her user ID. The code on lines 19 “21 calls another utility function called randompwd() (described later), which generates a new password for the user.

The remainder of the resetpwd() code (lines 22 “48) accomplishes the task of replacing the userPassword attribute in the user's entry with the newly generated password. After setting up a one-element array of modifications, the ldap_modify_s() LDAP SDK function is called to direct the server to make the password change.

The source code for the userid2dn() utility function is shown in Listing 20.5. This function uses an LDAP subtree search operation to find a user's directory entry given his or her user ID.

Listing 20.5 The setpwd.c userid2dn () function

1. /* 2. * userid2dn(): Given an entry's userid, return its DN. 3. * 4. * The DN returned should be freed by passing it to 5. * ldap_memfree(). If no entry is found, NULL is returned. 6. */ 7. static char * 8. userid2dn( LDAP *ld, char *base, char *userid ) 9. { 10. int rc; 11. char *filter, *dn = NULL; 12. char *attrs[] = { LDAP_NO_ATTRS, NULL }; 13. LDAPMessage *e, *res = NULL; 14. if (( filter = (char *)malloc( strlen( userid ) + 7 )) 15. == NULL ) { 16. perror( "malloc" ); 17. return( NULL ); 18. } 19. sprintf( filter, "(uid=%s)", userid ); 20. rc = ldap_search_s( ld, base, LDAP_SCOPE_SUBTREE, 21. filter, attrs, 0, &res ); 22. free( filter ); 23. if ( rc != LDAP_SUCCESS ) { 24. print_ldap_error( ld, rc, "search" ); 25. } else if (( e = ldap_first_entry( ld, res )) == NULL ) { 26. fprintf( stderr, "No match for %s\n", userid ); 27. } else if ( ldap_next_entry( ld, e ) != NULL ) { 28. fprintf( stderr, "%d matches for %s\n", 29. ldap_count_entries( ld, res ), userid ); 30. } else { 31. dn = ldap_get_dn( ld, e ); /* found just one! */ 32. } 33. ldap_msgfree( res ); 34. return( dn ); 35. }

The code on lines 14 “19 creates an LDAP search filter that looks like ( uid= ID ), where ID is the user ID provided on the setpwd command line. The actual search is done by the ldap_search_s() LDAP SDK call that appears on lines 20 and 21. Notice that the attrs parameter, which is an array of attribute types to be returned with each entry found during the search, contains one type called LDAP_NO_ATTRS ( attrs is initialized on line 12). The special LDAP_NO_ATTRS macro, defined by the LDAP API, is used to indicate that no attribute types should be returned. This is desirable because it is wasteful to ask for attributes we do not need, and in this instance we are interested in obtaining only the DN of the entry. If we were to pass NULL for the attrs parameter, every attribute in the entry would be returned. The code on lines 23 “35 checks to see if exactly one entry was found; if so, it returns the DN of the entry. If more than one entry is found, or if no entries are found, an error is reported .

The print_ldap_error() utility function is shown in Listing 20.6. This simple function is called from several places in setpwd.c to report LDAP-related errors in a consistent way.

Listing 20.6 The setpwd.c print_ldap_error () function

1. /* 2. * Display an LDAP error message. 3. */ 4. void 5. print_ldap_error( LDAP *ld, int lderr, char *s ) 6. { 7. fprintf( stderr, "%s: %s\n", s, ldap_err2string( lderr )); 8. }

The randompwd() function, which is called from the resetpwd() function we already looked at, is shown in Listing 20.7. This code does not perform any LDAP-related functions. It uses a file that contains a series of words (one per line) and the standard random() number generator function to select a new password. The actual algorithm used could easily be altered to match a specific organization's security policy or the preferences of help desk technical staff.

Listing 20.7 The setpwd.c randompwd () function

1. /* 2. * randompwd(): generate a reasonably good password using some 3. * random words from a file. The password should be easy to 4. * remember and able to be passed on verbally to a user. 5. * 6. * The algorithm we use is this: 7. * a) select two random words from the words file (w1 and w2). 8. * b) pick a random punctuation character (c). 9. * c) pick a random digit 0-9 (d) 10. * d) construct a candidate password as: w1 c w2 d 11. * e) change the case of one of the letters at random 12. */ 13. static char * 14. randompwd( void ) 15. { 16. static char *punctuation = "!@#$%&*-+=,.?"; 17. char *pwd, *w1, *w2, *p; 18. if (( w1 = randomword() == NULL ( w2 = randomword() == NULL ) { 19. if ( w1 != NULL ) { 20. free( w1 ); 21. } 22. return( NULL ); 23. } 24. if (( pwd = (char *)malloc( LONGEST_WORD * 2 + 3 )) != NULL ) { 25. sprintf( pwd, "%s%c%s%d", w1, 26. punctuation[ random() % strlen( punctuation ) ], w2,random() % 10 ); 27. 28. p = pwd + ( random() % strlen( pwd )); 29. if ( isalpha ( *p )) { 30. if ( isupper ( *p )) { 31. *p = tolower ( *p ); 32. } else { 33. *p = toupper( *p ); 34. } 35. } 36. } 37. free( w1 ); 38. free( w2 ); 39. return( pwd ); 40. }

The randompwd() function uses a utility function called randomword() to pick the two words that is uses to construct the password value. The first part of the randomword() function, which consists mainly of initialization tasks, is shown in Listing 20.8.

Listing 20.8 The setpwd.c randomword () function (part 1 of 2)

1. /* 2. * randomword(): return a random, lowercase word. 3. * 4. * Returns a malloc'd string if successful. 5. * Returns NULL on error, e.g., if unable to access 6. * the words file. 7. * 8. * This function assumes that all words in the file are 9. * less than LONGEST_WORD characters long. 10. */ 11. static char * 12. randomword( void ) 13. { 14. static FILE *fp = NULL; 15. static long filesize = 0L; 16. char *word, *p; 17. int len; 18. if ( fp == NULL ) { 19. /* 20. * initialize: open the words file and 21. * determine its size 22. */ 23. if (( fp = fopen( wordfile, "r" )) == NULL 24. fseek( fp, 0L, SEEK_END ) == -1 25. ( filesize = ftell ( fp )) == -1 ) { 26. perror( wordfile ); 27. if ( fp != NULL ) { 28. fclose( fp ); 29. } 30. return( NULL ); 31. } 32. filesize -= LONGEST_WORD; 33. }

The remainder of the randomword() function, which randomly seeks to a location in the words file and returns a word, is shown in Listing 20.9.

Listing 20.9 The setpwd.c randomword () function (part 2 of 2)

1. /* 2. * seek to a random location in the file. 3. */ 4. if ( fseek( fp, random() % filesize, SEEK_SET ) == -1 ) { 5. perror( wordfile ); 6. return( NULL ); 7. } 8. if (( word = (char *)malloc( LONGEST_WORD )) == NULL ) { 9. perror( "malloc" ); 10. return( NULL ); 11. } 12. /* 13. * read the tail (remainder) of one word and then 14. * the entire next word, which is what we will return. 15. */ 16. fgets( word, LONGEST_WORD - 1, fp ); 17. if ( fgets( word, LONGEST_WORD - 1, fp ) == NULL ) { 18. perror( wordfile ); 19. free( word ); 20. return( NULL ); 21. } 22. len = strlen( word ) - 1; 23. if ( word[ len ] == '\n' ) { 24. word[ len ] = '\0'; 25. } 26. for ( p = word; *p != '\0'; ++p ) { 27. if ( isupper( *p )) { 28. *p = tolower( *p ); 29. } 30. } 31. return( word ); 32. }

The Help Desk Staff's Experience

Listing 20.10 shows a sample run of the setpwd application.

Listing 20.10 Using setpwd to reset one user's password

% setpwd scarter Contacting LDAP server Finding entry scarter Modifying entry scarter Password reset. scarter's new password is: Knife,attentive7 Don't forget to remind scarter to reset his/her password right away!

Sam Carter's password was reset to the random password Knife,attentive7 .

Listing 20.11 shows another sample run of the setpwd utility.

Listing 20.11 Using setpwd to reset several users' passwords

% setenv QUIET % setpwd scarter tmorris kvaughan abergin dmiller gfarmer kwinters trigden cschmith jwallace Password reset. scarter's new password is: define+chaRd5 Password reset. tmorris's new password is: cyclades@eT0 Password reset. kvaughan's new password is: sorghum#releVant5 Password reset. abergin's new password is: shinGle?appellant0 Password reset. dmiller's new password is: grackLe,polish2 Password reset. gfarmer's new password is: periClean+problematic5 Password reset. kwinters's new password is: deforest*articulatoRy6 Password reset. trigden's new password is: bombard+decontrolling2 Password reset. cschmith's new password is: pessimist+lIne9 Password reset. jwallace's new password is: paulSon,battle3

In this instance, 10 people's passwords were reset with a single run of the utility. The QUIET environment variable is set prior to running setpwd to reduce the amount of output.

Ideas for Improvement

The setpwd application could be improved in many ways. Here are a few ideas:

  • Increase security by prompting for the help desk password, or by using a different form of authentication (such as Kerberos or public key certificates) instead of embedding the help desk password in the application.

  • Increase security by taking advantage of your directory service's password policy features (if available) to force users to choose a new password the next time they bind to the directory after their password is reset using setpwd .

  • Provide a way for the person who invokes setpwd to optionally specify the new password values.

  • Expand the setpwd application's role by adding features to disable a user's account and change other information in user entries, such as email addresses.

  • Create reusable, shared libraries that contain some of the LDAP-related code included in setpwd . Good candidates include the userid2dn() and print_ldap_error() functions.



Understanding and Deploying LDAP Directory Services,  2002 New Riders Publishing
<  BACK CONTINUE  >

Index terms contained in this section

applications
         developing
                    setpwd example 2nd 3rd 4th 5th 6th 7th 8th 9th 10th 11th 12th 13th 14th 15th 16th 17th 18th 19th 20th 21st 22nd 23rd 24th 25th 26th 27th 28th 29th 30th 31st 32nd 33rd 34th 35th 36th 37th 38th 39th 40th
developing
         applications
                    setpwd example 2nd 3rd 4th 5th 6th 7th 8th 9th 10th 11th 12th 13th 14th 15th 16th 17th 18th 19th 20th 21st 22nd 23rd 24th 25th 26th 27th 28th 29th 30th 31st 32nd 33rd 34th 35th 36th 37th 38th 39th 40th
directories
         applications
                    developing 2nd 3rd 4th 5th 6th 7th 8th 9th 10th 11th 12th 13th 14th 15th 16th 17th 18th 19th 20th 21st 22nd 23rd 24th 25th 26th 27th 28th 29th 30th 31st 32nd 33rd 34th 35th 36th 37th 38th 39th 40th
listings
         setpwd utility
                    prelude (source code) 2nd 3rd 4th
                    sample run 2nd 3rd 4th
                    setpwd.c main() function 2nd 3rd 4th 5th 6th 7th
                    setpwd.c print_ldap_error() function 2nd
                    setpwd.c randompwd() function 2nd 3rd 4th
                    setpwd.c randomword() function 2nd 3rd 4th 5th 6th
                    setpwd.c resetpwd() function 2nd 3rd 4th 5th
                    setpwd.c userid2dn() function 2nd 3rd 4th
passwords
          setpwd example
                    improvement ideas 2nd
                    prelude (listing) 2nd 3rd 4th
                    sample run 2nd 3rd 4th
                    setpwd.c main() function 2nd 3rd 4th 5th 6th 7th
                    setpwd.c print_ldap_error() function 2nd
                    setpwd.c randompwd() function 2nd 3rd 4th
                    setpwd.c randomword() function 2nd 3rd 4th 5th 6th
                    setpwd.c resetpwd() function 2nd 3rd 4th 5th
                    setpwd.c userid2dn() function 2nd 3rd 4th
                    userPassword attribute
setpwd utility example
          improvement ideas 2nd
          prelude (listing) 2nd 3rd 4th
          sample run 2nd 3rd 4th
          setpwd.c main() function 2nd 3rd 4th 5th 6th 7th
          setpwd.c print_ldap_error() function 2nd
          setpwd.c randompwd() function 2nd 3rd 4th
          setpwd.c randomword() function 2nd 3rd 4th 5th 6th
          setpwd.c resetpwd() function 2nd 3rd 4th 5th
          setpwd.c userid2dn() function 2nd 3rd 4th
          userPassword attribute
setpwd.c main() function
          setpwd utility 2nd 3rd 4th 5th
setpwd.c print_ldap_error() function
          setpwd utility 2nd
setpwd.c randompwd() function
          setpwd utility 2nd 3rd 4th
setpwd.c randomword() function
          setpwd utility 2nd 3rd 4th 5th 6th
setpwd.c resetpwd() function
          setpwd utility 2nd 3rd 4th 5th
setpwd.c userid2dn() function
          setpwd utility 2nd 3rd 4th
userPassword attribute
          setpwd utility example

2002, O'Reilly & Associates, Inc.



Understanding and Deploying LDAP Directory Services
Understanding and Deploying LDAP Directory Services (2nd Edition)
ISBN: 0672323168
EAN: 2147483647
Year: 1997
Pages: 245

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