Submitted By: Robert Connolly <robert at linuxfromscratch dot org> (ashes)
Date: 2006-12-10
Initial Package Version: 4.0.18.1
Upstream Status: Not Submitted
Origin: http://www.openwall.com/crypt/contrib/shadow-4.0.3-crypt_blowfish.diff.gz
Description: Use this patch with the Glibc blowfish patch (also from openwall).

You must run:

aclocal &&
autoconf &&
autoheader

diff -Naur shadow-4.0.18.1.orig/configure.in shadow-4.0.18.1/configure.in
--- shadow-4.0.18.1.orig/configure.in	2006-08-03 10:17:21.000000000 +0000
+++ shadow-4.0.18.1/configure.in	2006-12-10 20:25:40.000000000 +0000
@@ -218,6 +218,22 @@
 AC_ARG_WITH(libpam,
 	[AC_HELP_STRING([--with-libpam], [use libpam for PAM support @<:@default=yes if found@:>@])],
 	[with_libpam=$withval], [with_libpam=yes])
+AC_ARG_WITH(random,
+	[AC_HELP_STRING([--with-random=FILE], [read randomness from FILE @<:@default=/dev/random@:>@])],
+    [ RANDOM_FILE="$withval" ],
+    [
+	dnl Check for random device
+	AC_CHECK_FILE("/dev/random",
+	    [
+		RANDOM_FILE="/dev/random";
+	    ]
+	)
+    ]
+)
+if test -n "$RANDOM_FILE" ; then
+	AC_SUBST(RANDOM_FILE)
+	AC_DEFINE_UNQUOTED([RANDOM_FILE], ["$RANDOM_FILE"], [Defined to path of file to read randomness from])
+fi
 AC_ARG_WITH(selinux,
 	[AC_HELP_STRING([--with-selinux], [use SELinux support @<:@default=autodetect@:>@])],
 	[with_selinux=$withval], [with_selinux=yes])
@@ -258,8 +274,10 @@
 AM_CONDITIONAL(ENABLE_REGENERATE_MAN, test x$enable_man != xno)
 
 AC_SUBST(LIBCRYPT)
-AC_CHECK_LIB(crypt, crypt, [LIBCRYPT=-lcrypt],
-	[AC_MSG_ERROR([crypt() not found])])
+AC_CHECK_LIB(crypt, crypt_gensalt,
+	[AC_DEFINE(HAVE_CRYPT_GENSALT, 1, [Defined if crypt_gensalt() is in libcrypt])]
+		[LIBCRYPT="-lcrypt"],
+	[AC_MSG_ERROR([crypt_gensalt() not found])])
 
 AC_SUBST(LIBAUDIT)
 if test "$with_audit" = "yes"; then
diff -Naur shadow-4.0.18.1.orig/etc/login.defs shadow-4.0.18.1/etc/login.defs
--- shadow-4.0.18.1.orig/etc/login.defs	2006-07-16 09:00:20.000000000 +0000
+++ shadow-4.0.18.1/etc/login.defs	2006-12-10 20:02:50.000000000 +0000
@@ -242,13 +242,6 @@
 PASS_ALWAYS_WARN	yes
 
 #
-# Number of significant characters in the password for crypt().
-# Default is 8, don't change unless your crypt() is better.
-# Ignored if MD5_CRYPT_ENAB set to "yes".
-#
-#PASS_MAX_LEN		8
-
-#
 # Require password before chfn/chsh can make any changes.
 #
 CHFN_AUTH		yes
@@ -269,14 +262,63 @@
 #LOGIN_STRING		"%s's Password: "
 
 #
-# Only works if compiled with MD5_CRYPT defined:
-# If set to "yes", new passwords will be encrypted using the MD5-based
-# algorithm compatible with the one used by recent releases of FreeBSD.
-# It supports passwords of unlimited length and longer salt strings.
-# Set to "no" if you need to copy encrypted passwords to other systems
-# which don't understand the new algorithm.  Default is "no".
+# Each password entry contains a prefix that specifies the hashing algorithm
+# used to create the remaining characters/bytes. Use this setting to specify
+# which hashing algorithm is used to create new passwords.
+#
+# The default here is to use the Blowfish-based algorithm, (which currently
+# requires you to be running a patched version of glibc). To use the
+# slightly more compatible MD5-based algorithm, you would set this to $1$.
+# To be completely backwards compatible and use the traditional DES-based
+# hashing, you should set this value to an empty string, but be warned,
+# passwords using this algorithm offer very little security.
+#
+CRYPT_PREFIX	"$2a$"
+
+#
+# For hashing algorithms that can alter their complexity, use this setting
+# to achieve a balance between the security of the password and performance
+# on the host system.
+#
+# This value is interpreted by each algorithm in specific ways. With the
+# Blowfish algorithm, it specifies the number of rounds as a base-2
+# alogarithm of the actual iteration count, so 12 actually refers to 2^12.
+# Altering the value to 11 would therefore halve the number of iterations
+# used to 2^11.
+#
+# Make sure that if you alter the above setting, this setting is also
+# appropriate. For algorithms that have fixed iteration counts, or to
+# enforce the use of a low default value, use a setting of 0.
+#
+CRYPT_ROUNDS	12
+
+#
+# All algorithms require varying amounts of random bytes known as salt. For
+# example the DES-based algorithm requires only 12-bits, (1½ bytes), whereas
+# the Blowfish-based algorithm requires 128-bits, (16 bytes).
+#
+# If an algorithm doesn't receive enough salt, more will be collected from
+# /dev/urandom, a byte at a time until it's satisfied. If you know how much
+# is enough to satisfy even the most hungry of algorithms locally available,
+# setting it here will speed up the generation of passwords.
+#
+# A maximum is also provided to enforce an upper limit on this to prevent a
+# wayward algorithm munching all the randomness unnecessarily.
+#
+CRYPT_MINSALT	16
+CRYPT_MAXSALT	32
+
+#
+# Number of significant characters in the password for crypt(). MD5 can
+# effectively cope with unlimited length passwords, but a limit of ~127 is
+# reasonable. Blowfish can handle up to 72 characters, and the DES algorithm
+# can only handle 8.
+#
+# This setting is used in some of the obscure checks, and also to inform the
+# user on how big their new password should be, so it should be set in
+# accordance to the choice of algorithm.
 #
-#MD5_CRYPT_ENAB	no
+PASS_MAX_LEN	72
 
 #
 # List of groups to add to the user's supplementary group set
diff -Naur shadow-4.0.18.1.orig/lib/getdef.c shadow-4.0.18.1/lib/getdef.c
--- shadow-4.0.18.1.orig/lib/getdef.c	2006-06-24 13:17:18.000000000 +0000
+++ shadow-4.0.18.1/lib/getdef.c	2006-12-10 20:02:50.000000000 +0000
@@ -51,6 +51,12 @@
 	{"CONSOLE_GROUPS", NULL},
 	{"CONSOLE", NULL},
 	{"CREATE_HOME", NULL},
+#ifdef HAVE_CRYPT_GENSALT
+	{ "CRYPT_MAXSALT", NULL },
+	{ "CRYPT_MINSALT", NULL },
+	{ "CRYPT_PREFIX", NULL },
+	{ "CRYPT_ROUNDS", NULL },
+#endif /* HAVE_CRYPT_GENSALT */
 	{"DEFAULT_HOME", NULL},
 	{"ENV_PATH", NULL},
 	{"ENV_SUPATH", NULL},
@@ -67,6 +73,9 @@
 	{"LOG_UNKFAIL_ENAB", NULL},
 	{"MAIL_DIR", NULL},
 	{"MAIL_FILE", NULL},
+#ifndef HAVE_CRYPT_GENSALT
+ 	{ "MD5_CRYPT_ENAB", NULL },
+#endif /* ! HAVE_CRYPT_GENSALT */
 	{"PASS_MAX_DAYS", NULL},
 	{"PASS_MIN_DAYS", NULL},
 	{"PASS_WARN_AGE", NULL},
diff -Naur shadow-4.0.18.1.orig/libmisc/obscure.c shadow-4.0.18.1/libmisc/obscure.c
--- shadow-4.0.18.1.orig/libmisc/obscure.c	2006-02-07 22:38:49.000000000 +0000
+++ shadow-4.0.18.1/libmisc/obscure.c	2006-12-10 20:02:50.000000000 +0000
@@ -233,8 +233,10 @@
 	   Example: "password$%^&*123".  So check it again, this time
 	   truncated to the maximum length.  Idea from npasswd.  --marekm */
 
+#ifndef HAVE_CRYPT_GENSALT
 	if (getdef_bool ("MD5_CRYPT_ENAB"))
 		return NULL;	/* unlimited password length */
+#endif
 
 	maxlen = getdef_num ("PASS_MAX_LEN", 8);
 	if (oldlen <= maxlen && newlen <= maxlen)
diff -Naur shadow-4.0.18.1.orig/libmisc/salt.c shadow-4.0.18.1/libmisc/salt.c
--- shadow-4.0.18.1.orig/libmisc/salt.c	2005-08-31 17:24:58.000000000 +0000
+++ shadow-4.0.18.1/libmisc/salt.c	2006-12-10 20:02:50.000000000 +0000
@@ -3,8 +3,13 @@
  *
  * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
  * public domain.
+ * 
+ * Broken by Matt Dainty <madmatt@bits.bris.ac.uk>
+ * Furthermore by Robert Connolly
  */
 
+#define _OW_SOURCE
+
 #include <config.h>
 
 #ident "$Id: salt.c,v 1.10 2005/08/31 17:24:58 kloczek Exp $"
@@ -13,6 +18,95 @@
 #include <stdlib.h>
 #include "prototypes.h"
 #include "defines.h"
+
+#ifdef HAVE_CRYPT_GENSALT
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <crypt.h>
+#include "getdef.h"
+
+/* Soopa-doopa salt generation function. There isn't anything algorithm
+ * specific in here, although it does require the Openwall-patched glibc to
+ * provide the crypt_gensalt() function, as well as make use of
+ * Blowfish-based hashing.
+ *
+ * All parameters can be customised from the /etc/login.defs file
+ *
+ * Written by Matt Dainty <madmatt@bits.bris.ac.uk>
+ */
+char *
+crypt_make_salt(void)
+{
+	char *result, *salt;
+	int fd, offset, minsalt, maxsalt, count;
+
+	minsalt = getdef_num( "CRYPT_MINSALT", 16 );
+	maxsalt = getdef_num( "CRYPT_MAXSALT", 32 );
+
+	if( minsalt > maxsalt ) {
+		fprintf( stderr, "Check the CRYPT_MINSALT and CRYPT_MAXSALT settings!\n" );
+		exit(1);
+	}
+
+	if( ( salt = ( char * ) malloc( maxsalt ) ) == NULL ) {
+		fprintf( stderr, "Can't allocate %d bytes of memory\n", maxsalt );
+		exit(1);
+	}
+
+	if( ( fd = open( RANDOM_FILE, O_RDONLY ) ) < 0 ) {
+		fprintf( stderr, "Can't open %s for reading\n", RANDOM_FILE );
+		free( salt );
+		exit(1);
+	}
+
+	offset = 0;
+	result = NULL;
+
+	while( !result ) {
+		while( offset < minsalt ) {
+			count = read( fd, &salt[offset], minsalt - offset );
+			if( count <= 0 ) {
+				if( errno == EINTR )
+					continue;
+				goto finish;
+			}
+			offset += count;
+		}
+		result = crypt_gensalt( getdef_str( "CRYPT_PREFIX" ),
+					getdef_num( "CRYPT_ROUNDS", 0 ),
+					salt, minsalt );
+
+		if( !result && errno == EINVAL ) {
+			if( minsalt < maxsalt ) {
+				minsalt++;
+			} else {
+				fprintf( stderr, "CRYPT_PREFIX or CRYPT_ROUNDS is set incorrectly\n" );
+				goto finish;
+			}
+		}
+	}
+
+finish:
+	if( salt )
+		free( salt );
+	if( fd )
+		close( fd );
+
+	/* XXX	If we return the salt string as NULL, crypt will currently
+	 * 	segfault, so if have we a NULL salt string, exit here.
+	 * 	Otherwise, every invocation of crypt_make_salt() will have
+	 * 	to check for a NULL return value.
+	 *
+	 * 	This way, I don't muck up any more code! :-)
+	 */
+	if( result )
+		return result;
+
+	exit(1);
+}
+#else /* HAVE_CRYPT_GENSALT */
 #include "getdef.h"
 /*
  * Generate 8 base64 ASCII characters of random salt.  If MD5_CRYPT_ENAB
@@ -44,3 +138,4 @@
 
 	return result;
 }
+#endif /* HAVE_CRYPT_GENSALT */
diff -Naur shadow-4.0.18.1.orig/src/passwd.c shadow-4.0.18.1/src/passwd.c
--- shadow-4.0.18.1.orig/src/passwd.c	2006-07-28 17:40:15.000000000 +0000
+++ shadow-4.0.18.1/src/passwd.c	2006-12-10 20:02:50.000000000 +0000
@@ -228,9 +228,11 @@
 	 * for strength, unless it is the root user. This provides an escape
 	 * for initial login passwords.
 	 */
+#ifndef HAVE_CRYPT_GENSALT
 	if (getdef_bool ("MD5_CRYPT_ENAB"))
 		pass_max_len = 127;
 	else
+#endif
 		pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
 
 	if (!qflg)
