[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Modifications to the pam_unix module



Hello all,

I've been working on updating the pam_unix module to make it a viable, more
efficient replacement for pam_pwdb.  Per Andrew's advice, I'll be submitting
my patches to the list so that interested parties might have an opportunity
to provide feedback.  My first patch, attached below, enables full support
for MD5 passwords on systems where this is implemented as part of crypt(3).

After switching to pam_unix on our system (roughly 5,000 users at present),
authentications which previously took several seconds now appear almost
instantaneous.

Right now I'm working on cleaning up pam_unix_auth and making it more
configurable (basically, copying the list of pam_pwdb options...)  When that
patch is ready, I will likewise post it here.  I also plan to update
pam_unix_passwd down the line to permit stacking with the cracklib module.

If you're currently using pam_unix at your site, I would appreciate knowing
whether you find that these patches work for you.

By the by, does anyone know where there's a working archive of this list?
archive.redhat.com appears to be suffering from an upgrade right now...

-Steve Langasek
postmodern programmer
diff -uNr pam-0.66.orig/modules/pam_unix/CHANGELOG pam-0.66/modules/pam_unix/CHANGELOG
--- pam-0.66.orig/modules/pam_unix/CHANGELOG	Fri Feb 12 01:40:20 1999
+++ pam-0.66/modules/pam_unix/CHANGELOG	Sun Jun  6 12:07:17 1999
@@ -1,3 +1,3 @@
-
-This is an empty changelog file
-
+- Wed 26 May 1999 Steve Langasek <vorlon@netexpress.net>
+	Changed pam_unix_passwd to take advantage of md5 support in system
+	crypt() if available.
diff -uNr pam-0.66.orig/modules/pam_unix/Makefile pam-0.66/modules/pam_unix/Makefile
--- pam-0.66.orig/modules/pam_unix/Makefile	Fri Feb 12 01:40:20 1999
+++ pam-0.66/modules/pam_unix/Makefile	Sun Jun  6 12:07:17 1999
@@ -10,6 +10,9 @@
 # do you want shadow?
 USE_SHADOW=-D"HAVE_SHADOW_H"
 
+# does crypt support md5 hashing?
+USE_MD5=-D"HAVE_CRYPT_MD5"
+
 # do you want cracklib?
 ifeq ($(HAVE_CRACKLIB),yes)
 USE_CRACKLIB=-D"USE_CRACKLIB"
@@ -23,7 +26,7 @@
 
 ########################################################################
 
-CFLAGS += $(USE_SHADOW) $(USE_CRACKLIB) $(USE_LCKPWDF) $(NEED_LCKPWDF)
+CFLAGS += $(USE_SHADOW) $(USE_CRACKLIB) $(USE_LCKPWDF) $(NEED_LCKPWDF) $(USE_MD5)
 LDLIBS = -lcrypt
 
 ifdef DYNAMIC
@@ -45,8 +48,8 @@
 LIBAUTHSRC = pam_unix_auth.c support.c
 LIBSESSOBJ = pam_unix_sess.o
 LIBSESSSRC = pam_unix_sess.c
-LIBPASSWDSRC = pam_unix_passwd.c
-LIBPASSWDOBJ = pam_unix_passwd.o
+LIBPASSWDSRC = pam_unix_passwd.c md5.c
+LIBPASSWDOBJ = pam_unix_passwd.o md5.o
 LIBACCOUNTSRC = pam_unix_acct.c
 LIBACCOUNTOBJ = pam_unix_acct.o
 LIBOBJ = $(LIBAUTHOBJ) $(LIBSESSOBJ) $(LIBPASSWDOBJ) $(LIBACCOUNTOBJ)
diff -uNr pam-0.66.orig/modules/pam_unix/README pam-0.66/modules/pam_unix/README
--- pam-0.66.orig/modules/pam_unix/README	Fri Dec 18 16:30:45 1998
+++ pam-0.66/modules/pam_unix/README	Sun Jun  6 12:07:17 1999
@@ -1,4 +1,4 @@
-This is the README for pam_unix in Linux-PAM-0.53.
+This is the README for pam_unix in Linux-PAM-0.66.
 --------------------------------------------------
 
 pam_unix comes as four separate modules:
@@ -31,6 +31,14 @@
 	with the argument
 
 		"strict=false"
+
+	the "hash" argument is also accepted, allowing the administrator to
+	specify the algorithm used to hash passwords, e.g.,
+
+		"hash=md5"
+
+	currently recognized values are "md5" and "crypt".  This is only
+	supported on systems where crypt() accepts alternate salts.
 
 	invalid arguments are logged to syslog.
 
diff -uNr pam-0.66.orig/modules/pam_unix/md5.c pam-0.66/modules/pam_unix/md5.c
--- pam-0.66.orig/modules/pam_unix/md5.c	Wed Dec 31 18:00:00 1969
+++ pam-0.66/modules/pam_unix/md5.c	Sun Jun  6 12:07:17 1999
@@ -0,0 +1,254 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ */
+
+#include <string.h>
+#include "md5.h"
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len)	/* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32 t;
+    do {
+	t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(uint32 *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301U;
+    ctx->buf[1] = 0xefcdab89U;
+    ctx->buf[2] = 0x98badcfeU;
+    ctx->buf[3] = 0x10325476U;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned const char *buf, unsigned len)
+{
+    uint32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+	ctx->bits[1]++;		/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    memcpy(p, buf, len);
+	    return;
+	}
+	memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32 *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32 *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+	/* Two lots of padding:  Pad the first block to 64 bytes */
+	memset(p, 0, count);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32 *) ctx->in);
+
+	/* Now fill the next block with 56 bytes */
+	memset(ctx->in, 0, 56);
+    } else {
+	/* Pad block to 56 bytes */
+	memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((uint32 *) ctx->in)[14] = ctx->bits[0];
+    ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (uint32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32 buf[4], uint32 const in[16])
+{
+    register uint32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d,  in[0] + 0xd76aa478U,  7);
+    MD5STEP(F1, d, a, b, c,  in[1] + 0xe8c7b756U, 12);
+    MD5STEP(F1, c, d, a, b,  in[2] + 0x242070dbU, 17);
+    MD5STEP(F1, b, c, d, a,  in[3] + 0xc1bdceeeU, 22);
+    MD5STEP(F1, a, b, c, d,  in[4] + 0xf57c0fafU,  7);
+    MD5STEP(F1, d, a, b, c,  in[5] + 0x4787c62aU, 12);
+    MD5STEP(F1, c, d, a, b,  in[6] + 0xa8304613U, 17);
+    MD5STEP(F1, b, c, d, a,  in[7] + 0xfd469501U, 22);
+    MD5STEP(F1, a, b, c, d,  in[8] + 0x698098d8U,  7);
+    MD5STEP(F1, d, a, b, c,  in[9] + 0x8b44f7afU, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U,  7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22);
+
+    MD5STEP(F2, a, b, c, d,  in[1] + 0xf61e2562U,  5);
+    MD5STEP(F2, d, a, b, c,  in[6] + 0xc040b340U,  9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14);
+    MD5STEP(F2, b, c, d, a,  in[0] + 0xe9b6c7aaU, 20);
+    MD5STEP(F2, a, b, c, d,  in[5] + 0xd62f105dU,  5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U,  9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14);
+    MD5STEP(F2, b, c, d, a,  in[4] + 0xe7d3fbc8U, 20);
+    MD5STEP(F2, a, b, c, d,  in[9] + 0x21e1cde6U,  5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U,  9);
+    MD5STEP(F2, c, d, a, b,  in[3] + 0xf4d50d87U, 14);
+    MD5STEP(F2, b, c, d, a,  in[8] + 0x455a14edU, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U,  5);
+    MD5STEP(F2, d, a, b, c,  in[2] + 0xfcefa3f8U,  9);
+    MD5STEP(F2, c, d, a, b,  in[7] + 0x676f02d9U, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20);
+
+    MD5STEP(F3, a, b, c, d,  in[5] + 0xfffa3942U,  4);
+    MD5STEP(F3, d, a, b, c,  in[8] + 0x8771f681U, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23);
+    MD5STEP(F3, a, b, c, d,  in[1] + 0xa4beea44U,  4);
+    MD5STEP(F3, d, a, b, c,  in[4] + 0x4bdecfa9U, 11);
+    MD5STEP(F3, c, d, a, b,  in[7] + 0xf6bb4b60U, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U,  4);
+    MD5STEP(F3, d, a, b, c,  in[0] + 0xeaa127faU, 11);
+    MD5STEP(F3, c, d, a, b,  in[3] + 0xd4ef3085U, 16);
+    MD5STEP(F3, b, c, d, a,  in[6] + 0x04881d05U, 23);
+    MD5STEP(F3, a, b, c, d,  in[9] + 0xd9d4d039U,  4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16);
+    MD5STEP(F3, b, c, d, a,  in[2] + 0xc4ac5665U, 23);
+
+    MD5STEP(F4, a, b, c, d,  in[0] + 0xf4292244U,  6);
+    MD5STEP(F4, d, a, b, c,  in[7] + 0x432aff97U, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15);
+    MD5STEP(F4, b, c, d, a,  in[5] + 0xfc93a039U, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U,  6);
+    MD5STEP(F4, d, a, b, c,  in[3] + 0x8f0ccc92U, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15);
+    MD5STEP(F4, b, c, d, a,  in[1] + 0x85845dd1U, 21);
+    MD5STEP(F4, a, b, c, d,  in[8] + 0x6fa87e4fU,  6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10);
+    MD5STEP(F4, c, d, a, b,  in[6] + 0xa3014314U, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21);
+    MD5STEP(F4, a, b, c, d,  in[4] + 0xf7537e82U,  6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10);
+    MD5STEP(F4, c, d, a, b,  in[2] + 0x2ad7d2bbU, 15);
+    MD5STEP(F4, b, c, d, a,  in[9] + 0xeb86d391U, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+#endif
diff -uNr pam-0.66.orig/modules/pam_unix/md5.h pam-0.66/modules/pam_unix/md5.h
--- pam-0.66.orig/modules/pam_unix/md5.h	Wed Dec 31 18:00:00 1969
+++ pam-0.66/modules/pam_unix/md5.h	Sun Jun  6 12:07:17 1999
@@ -0,0 +1,27 @@
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __alpha
+typedef unsigned int uint32;
+#else
+typedef unsigned long uint32;
+#endif
+
+struct MD5Context {
+    uint32 buf[4];
+    uint32 bits[2];
+    unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *);
+void MD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void MD5Final(unsigned char digest[16], struct MD5Context *);
+void MD5Transform(uint32 buf[4], uint32 const in[16]);
+
+/*
+* This is needed to make RSAREF happy on some MS-DOS compilers.
+*/
+
+typedef struct MD5Context MD5_CTX;
+
+#endif /* MD5_H */
diff -uNr pam-0.66.orig/modules/pam_unix/pam_unix_auth.c pam-0.66/modules/pam_unix/pam_unix_auth.c
--- pam-0.66.orig/modules/pam_unix/pam_unix_auth.c	Fri Feb 12 02:06:46 1999
+++ pam-0.66/modules/pam_unix/pam_unix_auth.c	Sun Jun  6 12:07:23 1999
@@ -66,7 +66,7 @@
 
 static const char rcsid[] = "$Id: pam_unix_auth.c,v 1.1.2.4 1999/02/12 08:06:46 gafton Exp $ pam_unix authentication functions. alex@bach.cis.temple.edu";
 
-/* Define function phototypes */
+/* Define function prototypes */
 
 extern char *crypt(const char *key, const char *salt);	/* This should have 
 							   been in unistd.h
@@ -191,9 +191,9 @@
 			return PAM_AUTH_ERR;
 	}
 				
-	pp = crypt(p, salt);
+	pp = p ? crypt(p, salt) : NULL;
 	
-	if ( strcmp( pp, salt ) == 0 ) 
+	if ( pp && strcmp( pp, salt ) == 0 ) 
 		return	PAM_SUCCESS;
 
   	return PAM_AUTH_ERR;
diff -uNr pam-0.66.orig/modules/pam_unix/pam_unix_passwd.c pam-0.66/modules/pam_unix/pam_unix_passwd.c
--- pam-0.66.orig/modules/pam_unix/pam_unix_passwd.c	Mon Dec 28 11:37:40 1998
+++ pam-0.66/modules/pam_unix/pam_unix_passwd.c	Sun Jun  6 12:07:23 1999
@@ -79,6 +79,8 @@
 #include <shadow.h>
 #endif
 
+#include "md5.h"
+
 #define MAX_PASSWD_TRIES 3
 #define OLD_PASSWORD_PROMPT "Password: "
 #define NEW_PASSWORD_PROMPT "New password: "
@@ -96,6 +98,11 @@
 #define PPW_TOOEARLY 32
 #define PPW_ERROR 64
 
+/* Password hash types available. For now, this includes only standard
+   crypt and md5, but the list may be expanded as needed. */
+#define HASH_CRYPT 0
+#define HASH_MD5 1
+
 #ifndef DO_TEST
 #define STATIC static
 #else
@@ -103,7 +110,7 @@
 #endif
 /* Sets a password for the specified user to the specified password
    Returns flags PPW_*, or'd. */
-STATIC int _do_setpass(char *forwho, char *towhat, int flags);
+STATIC int _do_setpass(char *forwho, char *towhat, int crypt_type, int flags);
 /* Gets a password for the specified user
    Returns flags PPW_*, or'd. */
 STATIC int _do_getpass(char *forwho, char **theirpass);
@@ -111,7 +118,7 @@
    'entered' should not be crypt()'d or anything (it should be as the
    user entered it...), 'listed' should be as it is listed in the
    password database file */
-STATIC int _do_checkpass(const char *entered, char *listed);
+STATIC int _do_checkpass(const char *entered, const char *listed);
 
 /* sends a one-way message to the user, either error or info... */
 STATIC int conv_sendmsg(struct pam_conv *aconv, const char *message, int style);
@@ -119,6 +126,10 @@
 STATIC int conv_getitem(struct pam_conv *aconv, char *message, int style,
 			  char **result);
 
+/* mangles an integer into the password file's special printable character
+   format. */
+STATIC char i64c(int i);
+
 PAM_EXTERN
 int pam_sm_chauthtok(	pam_handle_t *pamh, 
 			int flags,
@@ -283,6 +294,9 @@
   int fascist = 1; /* Be fascist by default.  If compiled with cracklib,
                       call cracklib.  Otherwise just check length... */
 
+  int crypt_type = HASH_CRYPT; /* What type of salt do we use for
+                                  encrypting? Standard unix crypt is the
+                                  default. */
   char argbuf[256],argval[256];
   int i;
 
@@ -319,15 +333,23 @@
      }
 
      /* For PC functionality use "strict" -- historically "fascist" */
-     if(!strcmp(argbuf,"strict") || !strcmp(argbuf, "fascist"))
-
+     if(!strcmp(argbuf,"strict") || !strcmp(argbuf, "fascist")) {
        if(!strcmp(argval,"true"))
          fascist = 1;
        else if(!strcmp(argval,"false"))
          fascist = 0;
        else
          return PAM_SERVICE_ERR;
-     else {
+     } else if(!strcmp(argbuf,"hash")) {
+       if(!strcmp(argval,"crypt"))	/* Default. */
+         crypt_type = HASH_CRYPT;
+#ifdef HAVE_CRYPT_MD5
+       else if(!strcmp(argval,"md5"))
+         crypt_type = HASH_MD5;
+#endif
+	else
+          return PAM_SERVICE_ERR;
+     } else {
        _pam_log(LOG_ERR,"Unknown option: %s",argbuf);
        return PAM_SERVICE_ERR;
      }
@@ -363,7 +385,7 @@
       PAM_FAIL_CHECK;
       curpass = resp->resp;
       free (resp);
-      if(_do_checkpass(curpass?curpass:"",miscptr)) {
+      if(_do_checkpass(curpass?curpass:"",(const char *)miscptr)) {
         int abortme = 0;
 
         /* password is incorrect... */
@@ -435,7 +457,7 @@
     if(newpass) {
 #ifdef USE_CRACKLIB
       if(fascist && getuid()) 
-        cmiscptr = FascistCheck(newpass,CRACKLIB_DICTS);
+        cmiscptr = FascistCheck(newpass,(char *)CRACKLIB_DICTS);
 #else
       if(fascist && getuid() && strlen(newpass) < 6)
 	cmiscptr = "You must choose a longer password";
@@ -496,7 +518,7 @@
   /* From now on, we are bound and determined to get their password
      changed :-) */
   pam_set_item(pamh, PAM_AUTHTOK, (void *)newpass);
-  retval = _do_setpass(usrname,newpass,pflags);
+  retval = _do_setpass(usrname,newpass,crypt_type,pflags);
 #ifdef DEBUG
     fprintf(stderr,"retval was %d\n",retval);
 #endif
@@ -510,51 +532,82 @@
 }
 
 /* _do_checkpass() returns 0 on success, non-0 on failure */
-STATIC int _do_checkpass(const char *entered, char *listed)
+STATIC int _do_checkpass(const char *entered, const char *listed)
 {
-  char salt[3];
   if ((strlen(listed) == 0) &&(strlen(entered) == 0)) {
     /* no password in database; no password entered */
     return (0);
   }
-  salt[0]=listed[0]; salt[1]=listed[1]; salt[2]='\0';
-  return strcmp(crypt(entered,salt),listed);
-}
+/* This guarantees that the salt is not inappropriately truncated when
+   dealing with alternative hashing algorithms, but are there possibly
+   crypt() implementations that *fail* if not passed a two-byte salt?  Not
+   a problem on Linux, but we should try to be cross-platform. -SRL */
 
-STATIC char mksalt(int seed) {
-  int num = seed % 64;
+  return entered ? strcmp(crypt(entered,listed),listed) : -1;
+}
 
-  if (num < 26)
-    return 'a' + num;
-  else if (num < 52)
-    return 'A' + (num - 26);
-  else if (num < 62)
-    return '0' + (num - 52);
-  else if (num == 63)
-    return '.';
-  else
-    return '/';
+STATIC char i64c(int i) {
+  if (i < 0)
+    return ('.');
+  else if (i > 63)
+    return ('z');
+  if (i == 0)
+    return ('.');
+  if (i == 1)
+    return ('/');
+  if (i >= 2 && i <= 11)
+    return ('0' - 2 + i);
+  if (i >= 12 && i <= 37)
+    return ('A' - 12 + i);
+  if (i >= 38 && i <= 63)
+    return ('a' - 38 + i);
+  return ('\0');
 }
 
-STATIC int _do_setpass(char *forwho, char *towhat,int flags)
+STATIC int _do_setpass(char *forwho, char *towhat,int crypt_type, int flags)
 {
   struct passwd *pwd=NULL, *tmpent=NULL;
   FILE *pwfile,*opwfile;
-  char thesalt[3];
+  struct timeval tv;
+  MD5_CTX ctx;
+  unsigned char tmp[16];
+  unsigned char thesalt[16];
+  char *cp = (char *)thesalt;
+  int i;
   int retval=0;
-  struct timeval time1;
   int err = 0;
 #ifdef HAVE_SHADOW_H
   struct spwd *spwdent=NULL, *stmpent=NULL;
   int oldmask;
 #endif
+
   if(flags & PPW_SHADOW) { retval |= PPW_SHADOW; }
-  gettimeofday(&time1, NULL);
-  srand(time1.tv_usec);
-  thesalt[0]=mksalt(rand());
-  thesalt[1]=mksalt(rand());
-  thesalt[2]='\0';
-  
+
+  MD5Init(&ctx);
+  gettimeofday(&tv, (struct timezone *) 0);
+  MD5Update(&ctx, (void *) &tv, sizeof tv);
+  i = getpid();
+  MD5Update(&ctx, (void *) &i, sizeof i);
+  i = clock();
+  MD5Update(&ctx, (void *) &i, sizeof i);
+  MD5Update(&ctx, thesalt, sizeof thesalt);
+  MD5Final(tmp, &ctx);
+
+  if(crypt_type == HASH_CRYPT) {
+    thesalt[0]=i64c(tmp[0] & 077);
+    thesalt[1]=i64c(tmp[1] & 077);
+    thesalt[2]='\0';
+  }
+#ifdef HAVE_CRYPT_MD5
+  else if(crypt_type == HASH_MD5) {
+    strcpy(cp, "$1$");  /* magic for the MD5 */
+    cp += strlen(cp);
+    for (i = 0; i < 8; i++)
+      *cp++ = i64c(tmp[i] & 077);
+    *cp = '\0';
+  }
+#endif
+
   /* lock the entire password subsystem */
 #ifdef USE_LCKPWDF
   lckpwdf();
@@ -587,7 +640,7 @@
     stmpent=fgetspent(opwfile);
     while(stmpent) {
       if(!strcmp(stmpent->sp_namp,forwho)) {
-	stmpent->sp_pwdp = crypt(towhat,thesalt);
+	stmpent->sp_pwdp = towhat ? crypt(towhat,thesalt) : NULL;
 	stmpent->sp_lstchg = time(NULL)/(60*60*24);
 #ifdef DEBUG
 	fprintf(stderr,"Set password %s for %s\n",stmpent->sp_pwdp,
@@ -629,7 +682,7 @@
     tmpent=fgetpwent(opwfile);
     while(tmpent) {
       if(!strcmp(tmpent->pw_name,forwho)) {
-	tmpent->pw_passwd = crypt(towhat,thesalt);
+	tmpent->pw_passwd = towhat ? crypt(towhat,thesalt) : NULL;
       }
       if (putpwent(tmpent,pwfile)) {
 	fprintf(stderr, "error writing entry to password file: %s\n",
@@ -667,7 +720,7 @@
   tmpent=fgetpwent(opwfile);
   while(tmpent) {
     if(!strcmp(tmpent->pw_name,forwho)) {
-      tmpent->pw_passwd = crypt(towhat,thesalt);
+      tmpent->pw_passwd = towhat ? crypt(towhat,thesalt) : NULL;
     }
     if (putpwent(tmpent,pwfile)) {
 	fprintf(stderr, "error writing entry to shadow file: %s\n",

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index] []