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

Re: [Fwd: MD5 passwords]



The attached patches fix the following problems.

1. MD5 password hashing in pam_pwdb module.

   After the fix newly changed passwords will be hashed in a proper
   endianess.  To allow people to use the fixed modules with old passwd/shadow
   files I've kept the broken hashing code as well as the proper.  If the hash
   of user given password doesn't match the stored one then the additional
   attempt to match the password is performed.  In the last attempt the old
   (broken) hashing algorithm is used.  I don't think that the additional
   match introduces a security risk.

   As a result of the fixes the module code has become more slow.  But the
   implemented scheme gives us compatibility with improperly generated
   passwd/shadow entries and do not perform endianess check at the
   compilation time.  The last feature prevents problems with
   cross-compilation of PAM.

2. Tani Hosokawa <unknown@RIVERSTYX.NET> recently reported a vulnerability in
   bugtraq mailing list.  The problem is the ability of a malicious user to
   perform brute force attack against passwords without any traces in log files.
   The attack can be implemented via interrupting `su' or some other program
   before the failed attempt is logged by cleanup function of pam_pwdb module.

   I fixed the vulnerability by ensuring that at least one report about
   failed authentication attempt is printed immediately after password
   matching in pam_pwdb module.

   I also reorganized the code so that authentication attempts with empty
   password are accounted for failure reports in the same way as with nonempty
   passwords.

3. An important one line fix for pam_tally module - a result of a quick look
   at compilation warnings.

Best wishes
					Andrey V.
					Savochkin
diff -ru Linux-PAM-0.67.orig/modules/pam_pwdb/Makefile Linux-PAM-0.67/modules/pam_pwdb/Makefile
--- Linux-PAM-0.67.orig/modules/pam_pwdb/Makefile	Sun Jul 12 09:17:16 1998
+++ Linux-PAM-0.67/modules/pam_pwdb/Makefile	Wed Jun 23 10:06:28 1999
@@ -50,7 +50,7 @@
 LIBDEPS = pam_unix_acct.-c pam_unix_auth.-c pam_unix_passwd.-c \
 	pam_unix_sess.-c pam_unix_pwupd.-c support.-c bigcrypt.-c
 
-PLUS += md5.o md5_crypt.o
+PLUS += md5_good.o md5_broken.o md5_crypt_good.o md5_crypt_broken.o
 CFLAGS += $(EXTRAS)
 
 ifdef DYNAMIC
@@ -73,10 +73,27 @@
 	@echo "*** Building PAM_pwdb module..."
 	@echo
 
-$(CHKPWD): pwdb_chkpwd.o md5.o md5_crypt.o
+$(CHKPWD): pwdb_chkpwd.o md5_good.o md5_broken.o \
+           md5_crypt_good.o md5_crypt_broken.o
 	$(CC) -o $(CHKPWD) $^ -lpwdb
 
 pwdb_chkpwd.o: pwdb_chkpwd.c pam_unix_md.-c bigcrypt.-c
+
+md5_good.o: md5.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -DHIGHFIRST -D'MD5Name(x)=Good##x' \
+            $(TARGET_ARCH) -c $< -o $@
+
+md5_broken.o: md5.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \
+            $(TARGET_ARCH) -c $< -o $@
+
+md5_crypt_good.o: md5_crypt.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Good##x' \
+            $(TARGET_ARCH) -c $< -o $@
+
+md5_crypt_broken.o: md5_crypt.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \
+            $(TARGET_ARCH) -c $< -o $@
 
 dirs:
 ifdef DYNAMIC
diff -ru Linux-PAM-0.67.orig/modules/pam_pwdb/md5.c Linux-PAM-0.67/modules/pam_pwdb/md5.c
--- Linux-PAM-0.67.orig/modules/pam_pwdb/md5.c	Sun Jul 12 09:17:17 1998
+++ Linux-PAM-0.67/modules/pam_pwdb/md5.c	Wed Jun 23 10:09:03 1999
@@ -30,13 +30,13 @@
 #ifndef HIGHFIRST
 #define byteReverse(buf, len)	/* Nothing */
 #else
-void byteReverse(unsigned char *buf, unsigned longs);
+static 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)
+static void byteReverse(unsigned char *buf, unsigned longs)
 {
     uint32 t;
     do {
@@ -53,7 +53,7 @@
  * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
  * initialization constants.
  */
-void MD5Init(struct MD5Context *ctx)
+void MD5Name(MD5Init)(struct MD5Context *ctx)
 {
     ctx->buf[0] = 0x67452301U;
     ctx->buf[1] = 0xefcdab89U;
@@ -68,7 +68,7 @@
  * Update context to reflect the concatenation of another buffer full
  * of bytes.
  */
-void MD5Update(struct MD5Context *ctx, unsigned const char *buf, unsigned len)
+void MD5Name(MD5Update)(struct MD5Context *ctx, unsigned const char *buf, unsigned len)
 {
     uint32 t;
 
@@ -93,7 +93,7 @@
 	}
 	memcpy(p, buf, t);
 	byteReverse(ctx->in, 16);
-	MD5Transform(ctx->buf, (uint32 *) ctx->in);
+	MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
 	buf += t;
 	len -= t;
     }
@@ -102,7 +102,7 @@
     while (len >= 64) {
 	memcpy(ctx->in, buf, 64);
 	byteReverse(ctx->in, 16);
-	MD5Transform(ctx->buf, (uint32 *) ctx->in);
+	MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
 	buf += 64;
 	len -= 64;
     }
@@ -116,7 +116,7 @@
  * 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)
+void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx)
 {
     unsigned count;
     unsigned char *p;
@@ -137,7 +137,7 @@
 	/* 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);
+	MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
 
 	/* Now fill the next block with 56 bytes */
 	memset(ctx->in, 0, 56);
@@ -151,7 +151,7 @@
     ((uint32 *) ctx->in)[14] = ctx->bits[0];
     ((uint32 *) ctx->in)[15] = ctx->bits[1];
 
-    MD5Transform(ctx->buf, (uint32 *) ctx->in);
+    MD5Name(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 */
@@ -176,7 +176,7 @@
  * 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])
+void MD5Name(MD5Transform)(uint32 buf[4], uint32 const in[16])
 {
     register uint32 a, b, c, d;
 
diff -ru Linux-PAM-0.67.orig/modules/pam_pwdb/md5.h Linux-PAM-0.67/modules/pam_pwdb/md5.h
--- Linux-PAM-0.67.orig/modules/pam_pwdb/md5.h	Sun Jul 12 09:17:17 1998
+++ Linux-PAM-0.67/modules/pam_pwdb/md5.h	Wed Jun 23 19:11:57 1999
@@ -13,13 +13,17 @@
     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]);
-int i64c(int i);
+void GoodMD5Init(struct MD5Context *);
+void GoodMD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void GoodMD5Final(unsigned char digest[16], struct MD5Context *);
+void GoodMD5Transform(uint32 buf[4], uint32 const in[16]);
+void BrokenMD5Init(struct MD5Context *);
+void BrokenMD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void BrokenMD5Final(unsigned char digest[16], struct MD5Context *);
+void BrokenMD5Transform(uint32 buf[4], uint32 const in[16]);
 
-char *crypt_md5(const char *pw, const char *salt);
+char *Goodcrypt_md5(const char *pw, const char *salt);
+char *Brokencrypt_md5(const char *pw, const char *salt);
 
 /*
 * This is needed to make RSAREF happy on some MS-DOS compilers.
diff -ru Linux-PAM-0.67.orig/modules/pam_pwdb/md5_crypt.c Linux-PAM-0.67/modules/pam_pwdb/md5_crypt.c
--- Linux-PAM-0.67.orig/modules/pam_pwdb/md5_crypt.c	Sun Jul 12 09:17:17 1998
+++ Linux-PAM-0.67/modules/pam_pwdb/md5_crypt.c	Wed Jun 23 19:13:16 1999
@@ -34,34 +34,12 @@
 }
 
 /*
- * i64c - convert an integer to a radix 64 character
- */
-int 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');
-}
-
-/*
  * UNIX password
  *
  * Use MD5 for what it is best at...
  */
 
-char * crypt_md5(const char *pw, const char *salt)
+char * MD5Name(crypt_md5)(const char *pw, const char *salt)
 {
 	const char *magic = "$1$";
 	/* This string is magic for this algorithm.  Having
@@ -87,25 +65,25 @@
 	/* get the length of the true salt */
 	sl = ep - sp;
 
-	MD5Init(&ctx);
+	MD5Name(MD5Init)(&ctx);
 
 	/* The password first, since that is what is most unknown */
-	MD5Update(&ctx,(unsigned const char *)pw,strlen(pw));
+	MD5Name(MD5Update)(&ctx,(unsigned const char *)pw,strlen(pw));
 
 	/* Then our magic string */
-	MD5Update(&ctx,(unsigned const char *)magic,strlen(magic));
+	MD5Name(MD5Update)(&ctx,(unsigned const char *)magic,strlen(magic));
 
 	/* Then the raw salt */
-	MD5Update(&ctx,(unsigned const char *)sp,sl);
+	MD5Name(MD5Update)(&ctx,(unsigned const char *)sp,sl);
 
 	/* Then just as many characters of the MD5(pw,salt,pw) */
-	MD5Init(&ctx1);
-	MD5Update(&ctx1,(unsigned const char *)pw,strlen(pw));
-	MD5Update(&ctx1,(unsigned const char *)sp,sl);
-	MD5Update(&ctx1,(unsigned const char *)pw,strlen(pw));
-	MD5Final(final,&ctx1);
+	MD5Name(MD5Init)(&ctx1);
+	MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+	MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
+	MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+	MD5Name(MD5Final)(final,&ctx1);
 	for(pl = strlen(pw); pl > 0; pl -= 16)
-		MD5Update(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl);
+		MD5Name(MD5Update)(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl);
 
 	/* Don't leave anything around in vm they could use. */
 	memset(final,0,sizeof final);
@@ -113,16 +91,16 @@
 	/* Then something really weird... */
 	for (j=0,i = strlen(pw); i ; i >>= 1)
 		if(i&1)
-		    MD5Update(&ctx, (unsigned const char *)final+j, 1);
+		    MD5Name(MD5Update)(&ctx, (unsigned const char *)final+j, 1);
 		else
-		    MD5Update(&ctx, (unsigned const char *)pw+j, 1);
+		    MD5Name(MD5Update)(&ctx, (unsigned const char *)pw+j, 1);
 
 	/* Now make the output string */
 	strcpy(passwd,magic);
 	strncat(passwd,sp,sl);
 	strcat(passwd,"$");
 
-	MD5Final(final,&ctx);
+	MD5Name(MD5Final)(final,&ctx);
 
 	/*
 	 * and now, just to make sure things don't run too fast
@@ -130,23 +108,23 @@
 	 * need 30 seconds to build a 1000 entry dictionary...
 	 */
 	for(i=0;i<1000;i++) {
-		MD5Init(&ctx1);
+		MD5Name(MD5Init)(&ctx1);
 		if(i & 1)
-			MD5Update(&ctx1,(unsigned const char *)pw,strlen(pw));
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
 		else
-			MD5Update(&ctx1,(unsigned const char *)final,16);
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
 
 		if(i % 3)
-			MD5Update(&ctx1,(unsigned const char *)sp,sl);
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
 
 		if(i % 7)
-			MD5Update(&ctx1,(unsigned const char *)pw,strlen(pw));
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
 
 		if(i & 1)
-			MD5Update(&ctx1,(unsigned const char *)final,16);
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
 		else
-			MD5Update(&ctx1,(unsigned const char *)pw,strlen(pw));
-		MD5Final(final,&ctx1);
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+		MD5Name(MD5Final)(final,&ctx1);
 	}
 
 	p = passwd + strlen(passwd);
diff -ru Linux-PAM-0.67.orig/modules/pam_pwdb/pam_unix_md.-c Linux-PAM-0.67/modules/pam_pwdb/pam_unix_md.-c
--- Linux-PAM-0.67.orig/modules/pam_pwdb/pam_unix_md.-c	Sun Jul 12 09:17:17 1998
+++ Linux-PAM-0.67/modules/pam_pwdb/pam_unix_md.-c	Wed Jun 23 19:54:10 1999
@@ -25,7 +25,7 @@
 
 #define N_MDS 1
 const static struct cfns cfn_list[N_MDS] = {
-    { "$1$", 3, crypt_md5 },
+    { "$1$", 3, Goodcrypt_md5 },
 };
 
 static char *_pam_md(const char *key, const char *salt)
@@ -50,6 +50,23 @@
 
     x = x_strdup(e);                        /* put e in malloc()ed memory */
     _pam_overwrite(e);                                        /* clean up */
+    return x;                           /* this must be deleted elsewhere */
+}
+
+static char *_pam_md_compat(const char *key, const char *salt)
+{
+    char *x,*e=NULL;
+
+    D(("called with key='%s', salt='%s'", key, salt));
+
+    if ( !strncmp("$1$", salt, 3) ) {
+        e = Brokencrypt_md5(key, salt);
+        x = x_strdup(e);                    /* put e in malloc()ed memory */
+        _pam_overwrite(e);                                    /* clean up */
+    } else {
+        x = x_strdup("*");
+    }
+
     return x;                           /* this must be deleted elsewhere */
 }
 
diff -ru Linux-PAM-0.67.orig/modules/pam_pwdb/pam_unix_passwd.-c Linux-PAM-0.67/modules/pam_pwdb/pam_unix_passwd.-c
--- Linux-PAM-0.67.orig/modules/pam_pwdb/pam_unix_passwd.-c	Sun Jul 12 09:17:16 1998
+++ Linux-PAM-0.67/modules/pam_pwdb/pam_unix_passwd.-c	Wed Jun 23 19:13:40 1999
@@ -45,6 +45,28 @@
 /* Implementation */
 
 /*
+ * i64c - convert an integer to a radix 64 character
+ */
+static int 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');
+}
+
+/*
  * FUNCTION: _pam_unix_chauthtok() 
  *
  * this function works in two passes. The first, when UNIX__PRELIM is
@@ -262,15 +284,15 @@
 	    unsigned char tmp[16];
 	    int i;
 
-	    MD5Init(&ctx);
+	    GoodMD5Init(&ctx);
 	    gettimeofday(&tv, (struct timezone *) 0);
-	    MD5Update(&ctx, (void *) &tv, sizeof tv);
+	    GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
 	    i = getpid();
-	    MD5Update(&ctx, (void *) &i, sizeof i);
+	    GoodMD5Update(&ctx, (void *) &i, sizeof i);
 	    i = clock();
-	    MD5Update(&ctx, (void *) &i, sizeof i);
-	    MD5Update(&ctx, result, sizeof result);
-	    MD5Final(tmp, &ctx);
+	    GoodMD5Update(&ctx, (void *) &i, sizeof i);
+	    GoodMD5Update(&ctx, result, sizeof result);
+	    GoodMD5Final(tmp, &ctx);
 	    strcpy(cp, "$1$");  /* magic for the MD5 */
 	    cp += strlen(cp);
 	    for (i = 0; i < 8; i++)
diff -ru Linux-PAM-0.67.orig/modules/pam_pwdb/support.-c Linux-PAM-0.67/modules/pam_pwdb/support.-c
--- Linux-PAM-0.67.orig/modules/pam_pwdb/support.-c	Sun Dec 27 07:02:39 1998
+++ Linux-PAM-0.67/modules/pam_pwdb/support.-c	Wed Jun 23 10:07:58 1999
@@ -106,7 +106,7 @@
 /* UNIX__SET_DB */	  {  NULL,            _ALL_ON_,            0100000 },
 /* UNIX_DEBUG */          { "debug",          _ALL_ON_,            0200000 },
 /* UNIX_NODELAY */        { "nodelay",        _ALL_ON_,            0400000 },
-/* UNIX_UNIX */	  	  { "unix",           _ALL_ON_^(050000),  01000000 },
+/* UNIX_UNIX */		  { "unix",           _ALL_ON_^(050000),  01000000 },
 /* UNIX_BIGCRYPT */	  { "bigcrypt",       _ALL_ON_^(020000),  02000000 },
 /* UNIX_LIKE_AUTH */	  { "likeauth",       _ALL_ON_,           04000000 },
 };
@@ -311,13 +311,13 @@
 	if ( !quiet && !err ) {   /* under advisement from Sun,may go away */
 
 	    /* log the number of authentication failures */
-	    if ( failure->count != 0 ) {
+	    if ( failure->count > 1 ) {
 		(void) pam_get_item(pamh, PAM_SERVICE
 				    , (const void **)&service);
 		_log_err(LOG_NOTICE
-			 , "%d authentication failure%s; %s(uid=%d) -> "
+			 , "%d more authentication failure%s; %s(uid=%d) -> "
 			 "%s for %s service"
-			 , failure->count, failure->count==1 ? "":"s"
+			 , failure->count-1, failure->count==2 ? "":"s"
 			 , failure->name
 			 , failure->id
 			 , failure->user
@@ -407,6 +407,7 @@
     char *pp;
     char *data_name;
     int retval;
+    int verify_result;
 
     D(("called"));
 
@@ -480,34 +481,47 @@
      * clear text...
      */
 
+    data_name = (char *) malloc(sizeof(FAIL_PREFIX)+strlen(name));
+    if ( data_name == NULL ) {
+	_log_err(LOG_CRIT, "no memory for data-name");
+    }
+    strcpy(data_name, FAIL_PREFIX);
+    strcpy(data_name + sizeof(FAIL_PREFIX)-1, name);
+
     if ( ( !salt ) && ( !p ) ) {
 
 	/* the stored password is NULL */
+        pp = NULL;
+	if ( off(UNIX__NONULL, ctrl ) ) {    /* this means we've succeeded */
+	    verify_result = PAM_SUCCESS;
+	} else {
+	    verify_result = PAM_AUTH_ERR;
+	}
 
-	(void) pwdb_entry_delete(&pwe);
-	(void) pwdb_delete(&pw);
+    } else {
 
-	if ( off(UNIX__NONULL, ctrl ) ) {    /* this means we've succeeded */
-	    return PAM_SUCCESS;
+        pp = _pam_md(p, salt);
+
+        /* the moment of truth -- do we agree with the password? */
+        D(("comparing state of pp[%s] and salt[%s]", pp, salt));
+
+	if ( strcmp( pp, salt ) == 0 ) {
+	    verify_result = PAM_SUCCESS;
 	} else {
-	    return PAM_AUTH_ERR;
+	    _pam_delete(pp);
+	    pp = _pam_md_compat(p, salt);
+	    if ( strcmp( pp, salt ) == 0 ) {
+		verify_result = PAM_SUCCESS;
+	    } else {
+		verify_result = PAM_AUTH_ERR;
+	    }
 	}
-    }
 
-    pp = _pam_md(p, salt);
-    p = NULL;                                /* no longer needed here */
+        p = NULL;                                /* no longer needed here */
 
-    data_name = (char *) malloc(sizeof(FAIL_PREFIX)+strlen(name));
-    if ( data_name == NULL ) {
-	_log_err(LOG_CRIT, "no memory for data-name");
     }
-    strcpy(data_name, FAIL_PREFIX);
-    strcpy(data_name + sizeof(FAIL_PREFIX)-1, name);
-
-    /* the moment of truth -- do we agree with the password? */
-    D(("comparing state of pp[%s] and salt[%s]", pp, salt));
 
-    if ( strcmp( pp, salt ) == 0 ) {
+    if ( verify_result == PAM_SUCCESS ) {
 
 	retval = PAM_SUCCESS;
 	if (data_name) {                     /* reset failures */
@@ -528,7 +542,11 @@
 
 	    if (new != NULL) {
 
-		/* any previous failures for this user ? */
+		new->user = x_strdup(name);
+		new->id = getuid();
+		new->name = x_strdup(getlogin() ? getlogin():"" );
+
+                /* any previous failures for this user ? */
 		pam_get_data(pamh, data_name, (const void **)&old );
 
 		if (old != NULL) {
@@ -537,11 +555,19 @@
 			retval = PAM_MAXTRIES;
 		    }
 		} else {
+                    const char *service=NULL;
+                    (void) pam_get_item(pamh, PAM_SERVICE
+                                        , (const void **)&service);
+                    _log_err(LOG_NOTICE
+                             , "authentication failure; %s(uid=%d) -> "
+                             "%s for %s service"
+                             , new->name
+                             , new->id
+                             , new->user
+                             , service == NULL ? "**unknown**":service
+                        );
 		    new->count = 1;
 		}
-		new->user = x_strdup(name);
-		new->id = getuid();
-		new->name = x_strdup(getlogin() ? getlogin():"" );
 
 		pam_set_data(pamh, data_name, new, _cleanup_failures);
 
--- Linux-PAM-0.67/modules/pam_tally/pam_tally.c.orig	Thu Jun 24 13:11:53 1999
+++ Linux-PAM-0.67/modules/pam_tally/pam_tally.c	Thu Jun 24 13:11:53 1999
@@ -145,7 +145,7 @@
         _pam_log(LOG_ALERT, "Couldn't create %s",filename);
         return PAM_AUTH_ERR;
       }
-      lstat_ret = lstat(fileno(*TALLY),&fileinfo);
+      lstat_ret = fstat(fileno(*TALLY),&fileinfo);
       fclose(*TALLY);
     }
 

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