diff -uNr Linux-PAM-0.75/doc/modules/pam_userdb.sgml Linux-PAM-0.75/doc/modules/pam_userdb.sgml
--- Linux-PAM-0.75/doc/modules/pam_userdb.sgml	Tue Jun 20 18:11:05 2000
+++ Linux-PAM-0.75/doc/modules/pam_userdb.sgml	Tue Jul 10 00:14:23 2001
@@ -50,6 +50,8 @@
 <tt/icase/;
 <tt/dump/;
 <tt/db=XXXX/;
+<tt/use_authtok/;
+<tt/unknown_ok/;
 
 <tag><bf>Description:</bf></tag>
 
@@ -59,7 +61,7 @@
 so caution must be exercised over the access rights to the DB database itself..
 
 The module will read the password from the user using the conversation mechanism. If
-you are using this module on top of another authetication module (like <tt/pam_pwdb/;)
+you are using this module on top of another authentication module (like <tt/pam_pwdb/;)
 then you should tell that module to read the entered password from the PAM_AUTHTOK field, which is set by this module.
 
 <p>
@@ -85,6 +87,18 @@
 needed filename extension for you, so you should use something like <tt>/etc/foodata</tt>
 instead of <tt>/etc/foodata.db</tt>.
 
+<item> <tt/use_authtok/ - 
+use the authentication token previously obtained by another that did the
+conversation with the application. If this token can not be obtained then
+the module will try to converse again. This option can be used for stacking
+different modules that need to deal with the authentication tokens.
+
+<item>
+<tt/unknown_ok/ -
+do not return error when checking for a user that is not in the database.
+This can be used to stack more than one pam_userdb module that will check a
+username/password pair in more than a database.
+
 </itemize>
 
 <tag><bf>Examples/suggested usage:</bf></tag>
diff -uNr Linux-PAM-0.75/modules/pam_userdb/pam_userdb.h Linux-PAM-0.75/modules/pam_userdb/pam_userdb.h
--- Linux-PAM-0.75/modules/pam_userdb/pam_userdb.h	Tue Jun 20 18:12:09 2000
+++ Linux-PAM-0.75/modules/pam_userdb/pam_userdb.h	Tue Jul 10 00:14:23 2001
@@ -7,9 +7,12 @@
 #include <security/pam_appl.h>
 
 /* argument parsing */
-#define PAM_DEBUG_ARG		0x0001
-#define PAM_ICASE_ARG		0x0002
-#define PAM_DUMP_ARG		0x0004
+#define	PAM_DEBUG_ARG		(1 <<  0)
+#define	PAM_ICASE_ARG		(1 <<  1)
+#define	PAM_DUMP_ARG		(1 <<  2)
+#define	PAM_USE_AUTHTOK_ARG	(1 <<  3)
+#define	PAM_UNKNOWN_OK_ARG	(1 <<  4)
+#define	PAM_KEY_ONLY		(1 <<  5)
 
 /* Useful macros */
 #define  x_strdup(s)  ( (s) ? strdup(s):NULL )
diff -uNr Linux-PAM-0.75/modules/pam_userdb/README Linux-PAM-0.75/modules/pam_userdb/README
--- Linux-PAM-0.75/modules/pam_userdb/README	Tue Jun 20 18:12:09 2000
+++ Linux-PAM-0.75/modules/pam_userdb/README	Tue Jul 10 00:14:23 2001
@@ -1,6 +1,7 @@
 pam_userdb:
 	Look up users in a .db database and verify their password against
-	what is contained in that database.
+	what is contained in that database.  The database will have been
+	created using db_load.
 
 RECOGNIZED ARGUMENTS:
 	debug		write a message to syslog indicating success or
@@ -8,7 +9,9 @@
 
 	db=[path]	use the [path] database for performing lookup. There
 			is no default; the module will return PAM_IGNORE if
-			no database is provided.
+			no database is provided.  Newer versions of DB will
+			automatically append ".db" to whatever pathname you
+			supply here, so the path should not include it.
 			
 	icase		make the password verification to be case insensitive
 			(ie when working with registration numbers and such)
@@ -16,8 +19,26 @@
 	dump		dump all the entries in the database to the log (eek,
 			don't do this by default!)
 
+	use_authtok	use the authentication token previously obtained by another
+			that did the conversation with the application. If this 
+			token can not be obtained then the module will try to converse
+			again. This option can be used for stacking different modules
+			that need to deal with the authentication tokens.
+
+	unknown_ok	do not return error when checking for a user that is not
+			in the database. This can be used to stack more than one
+			pam_userdb module that will check a username/password pair
+			in more than a database.
+
+	key_only	the username and password are concatenated together in the
+			database hash as 'username-password' with a random value.
+			if the concatenation of the username and password with a
+			dash in the middle returns any result, the user is valid.
+			this is useful in cases	where the username may not be
+			unique but the username and password pair are.
+
 MODULE SERVICES PROVIDED:
-	auth		_authetication and _setcred (blank)
+	auth		_authentication and _setcred (blank)
 
 EXAMPLE USE:
 	auth  sufficient pam_userdb.so icase db=/tmp/dbtest.db
diff -uNr Linux-PAM-0.75/modules/pam_userdb/conv.c Linux-PAM-0.75/modules/pam_userdb/conv.c
--- Linux-PAM-0.75/modules/pam_userdb/conv.c	Tue Jun 20 18:12:09 2000
+++ Linux-PAM-0.75/modules/pam_userdb/conv.c	Tue Jul 10 00:14:23 2001
@@ -5,8 +5,6 @@
 /* $Id */
 /* Copyright at the end of the file */
 
-#define _BSD_SOURCE
-
 #include <stdlib.h>
 #include <string.h>
 
@@ -24,7 +22,7 @@
 		    struct pam_response **response)
 {
     int retval;
-    const struct pam_conv *conv;
+    const struct pam_conv *conv = NULL;
 
     retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
     if (retval == PAM_SUCCESS)
@@ -50,7 +48,7 @@
     struct pam_message msg[2],*pmsg[2];
     struct pam_response *resp;
     int retval;
-    char * token = NULL;
+    char *token = NULL;
     
     pmsg[0] = &msg[0];
     msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
@@ -61,7 +59,7 @@
     retval = converse(pamh, pmsg, &resp);
 
     if (resp != NULL) {
-	const char * item;
+	const char *item;
 	/* interpret the response */
 	if (retval == PAM_SUCCESS) {     /* a good conversation */
 	    token = x_strdup(resp[0].resp);
@@ -74,8 +72,7 @@
 	retval = pam_set_item(pamh, PAM_AUTHTOK, token);
 	token = _pam_delete(token);   /* clean it up */
 	if ( (retval != PAM_SUCCESS) ||
-	     (retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&item))
-	     != PAM_SUCCESS ) {
+	     (retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&item)) != PAM_SUCCESS ) {
 	    return retval;
 	}
 	
diff -uNr Linux-PAM-0.75/modules/pam_userdb/pam_userdb.c Linux-PAM-0.75/modules/pam_userdb/pam_userdb.c
--- Linux-PAM-0.75/modules/pam_userdb/pam_userdb.c	Mon Dec  4 10:02:16 2000
+++ Linux-PAM-0.75/modules/pam_userdb/pam_userdb.c	Tue Jul 10 00:14:23 2001
@@ -18,10 +18,13 @@
 #include <fcntl.h>
 #include <errno.h>
 
-#include "pam_userdb.h"
-
-#ifdef HAVE_NDBM_H
-# include <ndbm.h>
+#ifdef HAVE_LIBDB
+# define DB_DBM_HSEARCH    1 /* use the dbm interface */
+# if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ > 1)))
+#  include <db3/db.h>
+# else
+#  include <db.h>
+# endif
 #else
 # ifdef HAVE_DB_H
 #  define DB_DBM_HSEARCH    1 /* use the dbm interface */
@@ -31,6 +34,8 @@
 # endif
 #endif
 
+#include "pam_userdb.h"
+
 /*
  * here, we make a definition for the externally accessible function
  * in this file (this definition is required for static a module
@@ -72,11 +77,18 @@
 	      ctrl |= PAM_ICASE_ARG;
 	  else if (!strcasecmp(*argv, "dump"))
 	      ctrl |= PAM_DUMP_ARG;
+	  else if (!strcasecmp(*argv, "use_authtok"))
+	      ctrl |= PAM_USE_AUTHTOK_ARG;
+	  else if (!strcasecmp(*argv, "unknown_ok"))
+	      ctrl |= PAM_UNKNOWN_OK_ARG;
           else if (!strncasecmp(*argv,"db=", 3)) {
 	      database = strdup((*argv) + 3);
 	      if (database == NULL)
 		  _pam_log(LOG_ERR, "pam_parse: could not parse argument \"%s\"",
 			   *argv);
+	  }
+	  else if (!strcasecmp(*argv,"key_only")) {
+	    ctrl |= PAM_KEY_ONLY;
 	  } else {
                _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
           }
@@ -99,50 +111,73 @@
 {
     DBM *dbm;
     datum key, data;
+    char *key_only_buf = NULL;
+    int key_only_len = 0;
+
+    if (ctrl & PAM_KEY_ONLY) {
+      /* set up a buffer for the username and password pair */
+      key_only_len = strlen(user) + strlen(pass) + 2;
+      key_only_buf = alloca(key_only_len);
+      /* check to make sure that we have allocated the memory */
+      if (key_only_buf == NULL) {
+	_pam_log(LOG_CRIT, "no memory for key_only");
+	return -2;
+      }
+      /* clear the buffer */
+      memset(key_only_buf, 0, key_only_len);
+      /* add the user */
+      strcpy(key_only_buf, user);
+      /* add the "-" */
+      strcat(key_only_buf, "-");
+      /* add the password */
+      strcat(key_only_buf, pass);
+    }
 
     /* Open the DB file. */
     dbm = dbm_open(database, O_RDONLY, 0644);
     if (dbm == NULL) {
-	_pam_log(LOG_ERR, "user_lookup: could not open database `%s'",
-		 database);
+	_pam_log(LOG_ERR, "%s: could not open database `%s'", __FUNCTION__, database);
 	return -2;
     }
 
     if (ctrl &PAM_DUMP_ARG) {
 	_pam_log(LOG_INFO, "Database dump:");
-	for (key = dbm_firstkey(dbm);  key.dptr != NULL;
-	     key = dbm_nextkey(dbm)) {
+	for (key = dbm_firstkey(dbm);  key.dptr != NULL; key = dbm_nextkey(dbm)) {
 	    data = dbm_fetch(dbm, key);
 	    _pam_log(LOG_INFO, "key[len=%d] = `%s', data[len=%d] = `%s'",
 		     key.dsize, key.dptr, data.dsize, data.dptr);
 	}
     } 
     /* do some more init work */
-
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
-    key.dptr = x_strdup(user);
-    key.dsize = strlen(user);
-    user = NULL;
 
-    if (key.dptr) {
-	data = dbm_fetch(dbm, key);
-	memset(key.dptr, 0, key.dsize);
-	free(key.dptr);
+    /* use the key_only_buf if it exists. */
+    if (key_only_buf) {
+      (const char *) key.dptr = key_only_buf;
+      key.dsize = key_only_len;
+    } else {
+      (const char *) key.dptr = user;
+      key.dsize = strlen(user);
     }
-
+    
+    data = dbm_fetch(dbm, key);  
     if (ctrl & PAM_DEBUG_ARG) {
-	_pam_log(LOG_INFO, "password in database is [%p]`%s', len is %d",
+	_pam_log(LOG_INFO, "password in database is [%p] `%s', len is %d",
 		 data.dptr, (char *) data.dptr, data.dsize);
     }
-
     if (data.dptr != NULL) {
 	int compare = 0;
 	/* bingo, got it */
-	if (ctrl & PAM_ICASE_ARG)
+	if (ctrl & PAM_KEY_ONLY) {
+	  /* if we're in key only mode then matching
+	     the key is enough to return success */
+	  compare = 0;
+	} else if (ctrl & PAM_ICASE_ARG) {
 	    compare = strncasecmp(pass, data.dptr, data.dsize);
-	else
+	} else {
 	    compare = strncmp(pass, data.dptr, data.dsize);
+	}
 	dbm_close(dbm);
 	if (compare == 0)
 	    return 0; /* match */
@@ -150,19 +185,69 @@
 	    return -1; /* wrong */
     } else {
 	if (ctrl & PAM_DEBUG_ARG) {    
-	    _pam_log(LOG_INFO, "error returned by dbm_fetch: %s",
-		     strerror(errno));
+	    _pam_log(LOG_INFO, "error returned by dbm_fetch: %s", strerror(errno));
 	}
 	dbm_close(dbm);
-	/* probably we should check dbm_error() here */
+	/* probably we should check dbm_error() here */
 	return 1; /* not found */
     }
-
-    /* NOT REACHED */
+	    
+    if (dbm != NULL)
+	dbm_close(dbm);
+    /* we don't know what the heck happend */
     return -2;
 }
 
-/* --- authentication management functions (only) --- */
+/*
+ * Checks if a user has an entry in the database
+ *
+ * return values:
+ *	 1  = User not found
+ *	 0  = OK
+ * 	-1  = System error
+ */
+static int valid_user(const char *user)
+{
+    DBM *dbm;
+    datum key, data;
+
+    /* Open the DB file. */
+    dbm = dbm_open(database, O_RDONLY, 0644);
+    if (dbm == NULL) {
+	_pam_log(LOG_ERR, "%s: could not open database `%s'", __FUNCTION__, database);
+	return -1;
+    }
+
+    if (ctrl &PAM_DUMP_ARG) {
+	_pam_log(LOG_INFO, "Database dump:");
+	for (key = dbm_firstkey(dbm);  key.dptr != NULL; key = dbm_nextkey(dbm)) {
+	    data = dbm_fetch(dbm, key);
+	    _pam_log(LOG_INFO, "key[len=%d] = `%s', data[len=%d] = `%s'",
+		     key.dsize, key.dptr, data.dsize, data.dptr);
+	}
+    } 
+    /* do some more init work */
+    memset(&key, 0, sizeof(key));
+    memset(&data, 0, sizeof(data));
+    (const char *) key.dptr = user;
+    key.dsize = strlen(user);
+    
+    data = dbm_fetch(dbm, key);  
+
+    if (data.dptr != NULL) {
+	dbm_close(dbm);
+	return 0;
+    }
+
+    if (ctrl & PAM_DEBUG_ARG) {    
+	_pam_log(LOG_INFO, "error returned by dbm_fetch: %s", strerror(errno));
+    }
+    if (dbm != NULL)
+	dbm_close(dbm);
+    return 1; /* not found */
+}
+
+/* --- authentication management functions --- */
 
 PAM_EXTERN
 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
@@ -175,6 +260,12 @@
      /* parse arguments */
      ctrl = _pam_parse(argc, argv);
 
+     /* Do we have a database ? */
+     if (database == NULL) {
+	 _pam_log(LOG_ERR, "no database supplied; must use db=<database_path> arg");
+	 return PAM_SERVICE_ERR;
+     }
+     
      /* Get the username */
      retval = pam_get_user(pamh, &username, NULL);
      if ((retval != PAM_SUCCESS) || (!username)) {
@@ -183,19 +274,21 @@
         return PAM_SERVICE_ERR;
      }
      
-     /* Converse just to be sure we have the password */
-     retval = conversation(pamh);
-     if (retval != PAM_SUCCESS) {
-	 _pam_log(LOG_ERR, "could not obtain password for `%s'",
-		  username);
-	 return -2;
+     if ((ctrl & PAM_USE_AUTHTOK_ARG) == 0) {
+	 /* Converse just to be sure we have the password */
+	 retval = conversation(pamh);
+	 if (retval != PAM_SUCCESS) {
+	     _pam_log(LOG_ERR, "could not obtain password for `%s'",
+		      username);
+	     return PAM_CONV_ERR;
+	 }
      }
      
      /* Get the password */
-     retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&password);
+     retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &password);
      if (retval != PAM_SUCCESS) {
 	 _pam_log(LOG_ERR, "Could not retrive user's password");
-	 return -2;
+	 return PAM_AUTHTOK_ERR;
      }
      
      if (ctrl & PAM_DEBUG_ARG)
@@ -210,15 +303,16 @@
 	     return PAM_SERVICE_ERR;    
 	 case -1:
 	     /* incorrect password */
-	     _pam_log(LOG_WARNING,
-		      "user `%s' denied access (incorrect password)",
-		      username);
+	     _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password)", username);
 	     return PAM_AUTH_ERR;
 	 case 1:
 	     /* the user does not exist in the database */
 	     if (ctrl & PAM_DEBUG_ARG)
-		 _pam_log(LOG_NOTICE, "user `%s' not found in the database",
-			  username);
+		 _pam_log(LOG_NOTICE, "user `%s' not found in %s",
+			  username, database);
+	     if (ctrl & PAM_UNKNOWN_OK_ARG) {
+		 return PAM_IGNORE;
+	     }	 
 	     return PAM_USER_UNKNOWN;
 	 case 0:
 	     /* Otherwise, the authentication looked good */
@@ -226,12 +320,10 @@
 	     return PAM_SUCCESS;
 	 default:
 	     /* we don't know anything about this return value */
-	     _pam_log(LOG_ERR,
-		      "internal module error (retval = %d, user = `%s'",
+	     _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
 		      retval, username);
 	     return PAM_SERVICE_ERR;
      }
-
      /* should not be reached */
      return PAM_IGNORE;
 }
diff -uNr Linux-PAM-0.75/modules/pam_userdb/libdbfound.sh pam-0.75/modules/pam_userdb/libdbfound.sh
--- Linux-PAM-0.75/modules/pam_userdb/libdbfound.sh	Wed Dec 31 19:00:00 1969
+++ pam-0.75/modules/pam_userdb/libdbfound.sh	Thu Oct 19 18:08:56 2000
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+if [ -f "/usr/include/ndbm.h" ]; then
+    echo "-DUSE_NDBM_H"
+    exit 0
+fi
+
+if [ -f "/usr/include/db1/ndbm.h" ]; then
+    echo "-I/usr/include/db1 -DUSE_DB1_NDBM_H"
+    exit 0
+fi
+
+list=`/bin/ls /lib/libdb.so.* /lib/libdb1.so.* 2> /dev/null`
+if [ -n "$list" ]; then
+    echo ""
+    exit 0
+fi
+
+echo "none"
+exit 0
diff -uNr Linux-PAM-0.75/modules/pam_userdb/pam_userdb.c pam-0.75/modules/pam_userdb/pam_userdb.c
--- Linux-PAM-0.75/modules/pam_userdb/pam_userdb.c	Mon Oct  1 14:54:30 2001
+++ pam-0.75/modules/pam_userdb/pam_userdb.c	Mon Oct  1 12:07:05 2001
@@ -116,15 +116,13 @@
 
     if (ctrl & PAM_KEY_ONLY) {
       /* set up a buffer for the username and password pair */
-      key_only_len = strlen(user) + strlen(pass) + 2;
-      key_only_buf = alloca(key_only_len);
+      key_only_len = strlen(user) + 1 + strlen(pass);
+      key_only_buf = alloca(key_only_len + 1);
       /* check to make sure that we have allocated the memory */
       if (key_only_buf == NULL) {
 	_pam_log(LOG_CRIT, "no memory for key_only");
 	return -2;
       }
-      /* clear the buffer */
-      memset(key_only_buf, 0, key_only_len);
       /* add the user */
       strcpy(key_only_buf, user);
       /* add the "-" */
