diff -ruN u-boot/README u-boot-cvs-work/README
--- u-boot/README	2004-08-03 09:59:52.000000000 +0300
+++ u-boot-cvs-work/README	2004-09-14 10:01:09.062404632 +0300
@@ -1560,6 +1560,39 @@
 		/ other_activity_monitor it works automatically from
 		general timer_interrupt().
 
+- Factory Defaults:
+		CONFIG_FACTORY_DEFAULTS
+
+		Defining this enables a "factory" command which returns
+		the board to a pristine state (after asking for
+		confirmation). It performed by a board specific function
+		
+		  void factory_defaults(void);
+
+		Typically this means the resetting of the environment
+		with the exception of any set-once variables line
+		ethernet addresses, serial numbers and the like.
+
+		The remaining non volatile storage should be
+		erased for security reasons and the board should be reset.
+
+- Password Access:
+		CONFIG_PASSWORD_ACCESSS
+
+		Define this to secure access to the device by means
+		of an md5 encrypted password with the same format as
+		a unix system uses.
+
+		The auto boot sequence is unaffected but access to 
+		the command line is secured.
+
+		The password is stored to the "password" variable
+		and can be changed by the command "passwd".
+
+		If you happen to forget the password a "factory"
+		password (after confirmation) resets the device to 
+		the factory defaults.
+
 - General:
 
 		In the target system modem support is enabled when a
@@ -2097,6 +2130,16 @@
 		Add the "loopw" memory command. This only takes effect if
 		the memory commands are activated globally (CFG_CMD_MEM).
 
+- CONFIG_CRC32_VERIFY
+		Add a verify option to the crc32 command.
+		The syntax is:
+		  
+		=> crc32 -v <address> <count> <crc32>
+
+		Where address/count indicate a memory area
+		and crc32 is the correct crc32 which the 
+		area should have.
+
 Building the Software:
 ======================
 
@@ -2403,6 +2446,9 @@
 		  ethernet is encapsulated/received over 802.1q
 		  VLAN tagged frames.
 
+   password	- The MD5 encrypted access password;
+   		  see CONFIG_PASSWORD_ACCESS
+
 The following environment variables may be used and automatically
 updated by the network boot commands ("bootp" and "rarpboot"),
 depending the information provided by your boot server:
diff -ruN u-boot/common/Makefile u-boot-cvs-work/common/Makefile
--- u-boot/common/Makefile	2004-08-03 09:59:54.000000000 +0300
+++ u-boot-cvs-work/common/Makefile	2004-09-08 11:27:30.000000000 +0300
@@ -28,7 +28,7 @@
 AOBJS	=
 
 COBJS	= main.o ACEX1K.o altera.o bedbug.o \
-	  cmd_ace.o cmd_autoscript.o \
+	  cmd_ace.o cmd_access.o cmd_autoscript.o \
 	  cmd_bdinfo.o cmd_bedbug.o cmd_bmp.o cmd_boot.o cmd_bootm.o \
 	  cmd_cache.o cmd_console.o \
 	  cmd_date.o cmd_dcr.o cmd_diag.o cmd_doc.o cmd_dtt.o \
diff -ruN u-boot/common/cmd_access.c u-boot-cvs-work/common/cmd_access.c
--- u-boot/common/cmd_access.c	1970-01-01 02:00:00.000000000 +0200
+++ u-boot-cvs-work/common/cmd_access.c	2004-09-08 11:30:17.000000000 +0300
@@ -0,0 +1,229 @@
+/*
+ * (C) Copyright 2004
+ * Pantelis Antoniou, Intracom S.A., panto@intracom.gr
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * Password functions & factory defaults
+ */
+#include <common.h>
+#include <command.h>
+
+/* Note no controlling CFG_CMD_xxx; it is enterily board controlled */
+
+#ifdef CONFIG_FACTORY_DEFAULTS
+
+extern void factory_defaults(void);
+
+static int prompt_factory_defaults(void)
+{
+	char c;
+
+	puts("WARNING: Factory settings will be restored; Confirm [Y/n] ");
+	c = getc();
+	putc(c);
+	putc('\n');
+	if (c != 'Y' && c != 'y')
+		return 1;
+
+	puts("Restoring factory defaults.... ");
+	factory_defaults();
+	puts("\n");
+
+	return 0;
+}
+
+int do_factory(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	return prompt_factory_defaults();
+}
+
+U_BOOT_CMD(
+	factory,    1,    1,     do_factory,
+	"factory - restore factory defaults\n",
+	NULL
+);
+#endif
+
+#ifdef CONFIG_PASSWORD_ACCESS
+
+static int i64c(int i)
+{
+	if (i <= 0)
+		return ('.');
+	if (i == 1)
+		return ('/');
+	if (i >= 2 && i < 12)
+		return ('0' - 2 + i);
+	if (i >= 12 && i < 38)
+		return ('A' - 12 + i);
+	if (i >= 38 && i < 63)
+		return ('a' - 38 + i);
+	return ('z');
+}
+
+static char *make_salt(void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+	bd_t *bd = gd->bd;
+	static unsigned long x;
+	static char result[3];
+
+	/* make semi random salt */
+	x += get_timer(0) + bd->bi_baudrate +
+		bd->bi_enetaddr[3] + bd->bi_enetaddr[4] + bd->bi_enetaddr[5];
+	result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
+	result[1] = i64c(((x >> 12) ^ x) & 077);
+	result[2] = '\0';
+	return result;
+}
+
+static const char *ask_pass(void)
+{
+	int c;
+	static char buf[20];	/* only 8 chars significant */
+	char *s, *e;
+
+	s = buf;
+	e = buf + sizeof(buf) - 1;
+	while ((c = getc()) != '\r') {
+		switch (c) {
+			case '\b':	/* backspace */
+				if (s > buf) {
+					puts("\b \b");	/* erase star */
+					s--;
+				}
+				break;
+			case 'U' - 'A' + 1:	/* Ctrl-U erase all */
+				while (s > buf) {
+					puts("\b \b");	/* erase star */
+					s--;
+				}
+				break;
+			default:
+				if (s >= e)	/* no more */
+					break;
+				*s++ = (char)c;
+				putc('*');
+				break;
+		}
+	}
+	putc('\n');
+	*s = '\0';
+
+	return buf;
+}
+
+int do_passwd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	char pass[20];
+	const char *s;
+
+	puts("Enter new password: ");
+	s = ask_pass();
+	strcpy(pass, s);
+	puts("Confirm new password: ");
+	s = ask_pass();
+	if (strcmp(s, pass) != 0) {
+		puts("Confirmation failed.\n");
+		return 1;
+	}
+	/* clearing password? */
+	if (pass[0] == '\0') {
+		setenv("password", NULL);
+		return 0;
+	}
+
+	/* and set it */
+	setenv("password", (char *)md5_crypt(pass, make_salt()));
+	
+	puts("Note: To survive reboot do a 'saveenv'\n");
+
+	return 0;
+}
+
+U_BOOT_CMD(
+	passwd ,    1,    1,     do_passwd,
+	"passwd  - Change access password\n",
+	NULL
+);
+
+const char *get_stored_password(void)
+{
+	const char *stored;
+
+	stored = getenv("password");
+	if (stored == NULL)	/* no password; default is access permitted */
+		return NULL;
+
+	return stored;
+}
+
+void password_access(void)
+{
+	const char *stored, *encrypted, *unencrypted; 
+	int i, j = 0;
+
+	stored = get_stored_password();
+	if (stored == NULL)	/* no password; default is access permitted */
+		return;
+
+	puts("\nPlease enter password to continue\n"
+#ifdef CONFIG_FACTORY_DEFAULTS
+		"  or enter 'factory' to reset to defaults\n"
+#endif
+		"\n");
+
+	for (;;) {
+		puts("Access Password: ");
+		unencrypted = ask_pass();
+		if (unencrypted && unencrypted[0] != '\0') {
+
+#ifdef CONFIG_FACTORY_DEFAULTS
+			if (strcmp(unencrypted, "factory") == 0) {
+				if (prompt_factory_defaults() == 0) {
+					/* and re-read password */
+					stored = get_stored_password();
+					if (stored == NULL)	/* no password; default is access permitted */
+						return;
+				}
+				continue;
+			}
+#endif
+			encrypted = md5_crypt(unencrypted, stored);
+			if (strcmp(encrypted, stored) == 0)
+				break;
+		}
+
+		puts("Password incorrect\n");
+		/* delay 3 seconds for 3 consecutive errors */
+		if (++j > 3) {
+			j = 0;
+			for (i = 0; i < 3000; i++)
+				udelay (1000);
+		}
+	}
+
+	/* access granted */
+	puts("\n");
+}
+
+#endif
diff -ruN u-boot/common/main.c u-boot-cvs-work/common/main.c
--- u-boot/common/main.c	2004-04-23 23:32:06.000000000 +0300
+++ u-boot-cvs-work/common/main.c	2004-09-08 10:00:11.000000000 +0300
@@ -449,6 +449,13 @@
 	}
 #endif
 
+#ifdef CONFIG_PASSWORD_ACCESS
+	{
+		extern void password_access(void);
+		password_access();
+	}
+#endif
+
 	/*
 	 * Main Loop for Monitor Command Processing
 	 */
diff -ruN u-boot/include/common.h u-boot-cvs-work/include/common.h
--- u-boot/include/common.h	2004-08-03 09:59:55.000000000 +0300
+++ u-boot-cvs-work/include/common.h	2004-09-07 18:07:01.000000000 +0300
@@ -568,4 +568,26 @@
 void	show_boot_progress (int status);
 #endif
 
+/***************************************************************/
+
+/* Structure to save state of computation between the single steps.  */
+struct md5_ctx_t {
+	u32 A;
+	u32 B;
+	u32 C;
+	u32 D;
+	u32 total[2];
+	u32 buflen;
+	u8 buffer[128];
+};
+
+extern void md5_begin(void *md5_ctx);
+extern void md5_hash(const void *buffer, int length, void *md5_ctx);
+extern void md5_end(void *resbuf, void *md5_ctx);
+
+const char *md5_crypt(const char *pw, const char *salt);
+
+#define HASH_MD5_SIZE	16
+
+
 #endif	/* __COMMON_H_ */
diff -ruN u-boot/lib_generic/Makefile u-boot-cvs-work/lib_generic/Makefile
--- u-boot/lib_generic/Makefile	2003-08-29 23:58:07.000000000 +0300
+++ u-boot-cvs-work/lib_generic/Makefile	2004-09-07 17:49:25.000000000 +0300
@@ -28,7 +28,7 @@
 OBJS	= bzlib.o bzlib_crctable.o bzlib_decompress.o \
 	  bzlib_randtable.o bzlib_huffman.o \
 	  crc32.o ctype.o display_options.o ldiv.o \
-	  string.o vsprintf.o zlib.o
+	  md5.o string.o vsprintf.o zlib.o
 
 $(LIB):	.depend $(OBJS)
 	$(AR) crv $@ $(OBJS)
diff -ruN u-boot/lib_generic/md5.c u-boot-cvs-work/lib_generic/md5.c
--- u-boot/lib_generic/md5.c	1970-01-01 02:00:00.000000000 +0200
+++ u-boot-cvs-work/lib_generic/md5.c	2004-09-07 18:06:41.000000000 +0300
@@ -0,0 +1,650 @@
+/*
+ *  Based on who knows who...
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef USE_HOSTCC	/* Shut down "ANSI does not permit..." warnings */
+#include <common.h>	/* to get command definitions like CFG_CMD_JFFS2 */
+#endif
+
+
+/*
+ * md5sum.c - Compute MD5 checksum of files or strings according to the
+ *            definition of MD5 in RFC 1321 from April 1992.
+ *
+ * Copyright (C) 1995-1999 Free Software Foundation, Inc.
+ * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+ *
+ *
+ * June 29, 2001        Manuel Novoa III
+ *
+ * Added MD5SUM_SIZE_VS_SPEED configuration option.
+ *
+ * Current valid values, with data from my system for comparison, are:
+ *   (using uClibc and running on linux-2.4.4.tar.bz2)
+ *                     user times (sec)  text size (386)
+ *     0 (fastest)         1.1                6144
+ *     1                   1.4                5392
+ *     2                   3.0                5088
+ *     3 (smallest)        5.1                4912
+ */
+
+# define MD5SUM_SIZE_VS_SPEED 2
+
+/* Handle endian-ness */
+#ifdef __LITTLE_ENDIAN
+#  define SWAP(n) (n)
+# else
+#  define SWAP(n) ((n << 24) | ((n&65280)<<8) | ((n&16711680)>>8) | (n>>24))
+# endif
+
+# if MD5SUM_SIZE_VS_SPEED == 0
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (RFC 1321, 3.1: Step 1)  */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */  };
+# endif	/* MD5SUM_SIZE_VS_SPEED == 0 */
+
+/* Initialize structure containing state of computation.
+ * (RFC 1321, 3.3: Step 3)
+ */
+extern void md5_begin(void *md5_ctx)
+{
+	struct md5_ctx_t *ctx = md5_ctx;
+
+	ctx->A = 0x67452301;
+	ctx->B = 0xefcdab89;
+	ctx->C = 0x98badcfe;
+	ctx->D = 0x10325476;
+
+	ctx->total[0] = ctx->total[1] = 0;
+	ctx->buflen = 0;
+}
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ * and defined in the RFC 1321.  The first function is a little bit optimized
+ * (as found in Colin Plumbs public domain implementation).
+ * #define FF(b, c, d) ((b & c) | (~b & d))
+ */
+# define FF(b, c, d) (d ^ (b & (c ^ d)))
+# define FG(b, c, d) FF (d, b, c)
+# define FH(b, c, d) (b ^ c ^ d)
+# define FI(b, c, d) (c ^ (b | ~d))
+
+/* Starting with the result of former calls of this function (or the
+ * initialization function update the context for the next LEN bytes
+ * starting at BUFFER.
+ * It is necessary that LEN is a multiple of 64!!!
+ */
+static void md5_hash_block(const void *buffer, int len, struct md5_ctx_t *ctx)
+{
+	u32 correct_words[16];
+	const u32 *words = buffer;
+	int nwords = len / sizeof(u32);
+	const u32 *endp = words + nwords;
+
+# if MD5SUM_SIZE_VS_SPEED > 0
+	static const u32 C_array[] = {
+		/* round 1 */
+		0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+		0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+		0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+		0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+		/* round 2 */
+		0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+		0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
+		0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+		0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+		/* round 3 */
+		0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+		0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+		0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
+		0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+		/* round 4 */
+		0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+		0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+		0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+		0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+	};
+
+	static const char P_array[] = {
+#  if MD5SUM_SIZE_VS_SPEED > 1
+		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	/* 1 */
+#  endif	/* MD5SUM_SIZE_VS_SPEED > 1 */
+		1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,	/* 2 */
+		5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,	/* 3 */
+		0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9	/* 4 */
+	};
+
+#  if MD5SUM_SIZE_VS_SPEED > 1
+	static const char S_array[] = {
+		7, 12, 17, 22,
+		5, 9, 14, 20,
+		4, 11, 16, 23,
+		6, 10, 15, 21
+	};
+#  endif	/* MD5SUM_SIZE_VS_SPEED > 1 */
+# endif
+
+	u32 A = ctx->A;
+	u32 B = ctx->B;
+	u32 C = ctx->C;
+	u32 D = ctx->D;
+
+	/* First increment the byte count.  RFC 1321 specifies the possible
+	   length of the file up to 2^64 bits.  Here we only compute the
+	   number of bytes.  Do a double word increment.  */
+	ctx->total[0] += len;
+	if (ctx->total[0] < len)
+		++ctx->total[1];
+
+	/* Process all bytes in the buffer with 64 bytes in each round of
+	   the loop.  */
+	while (words < endp) {
+		u32 *cwp = correct_words;
+		u32 A_save = A;
+		u32 B_save = B;
+		u32 C_save = C;
+		u32 D_save = D;
+
+# if MD5SUM_SIZE_VS_SPEED > 1
+#  define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+		const u32 *pc;
+		const char *pp;
+		const char *ps;
+		int i;
+		u32 temp;
+
+		for (i = 0; i < 16; i++) {
+			cwp[i] = SWAP(words[i]);
+		}
+		words += 16;
+
+#  if MD5SUM_SIZE_VS_SPEED > 2
+		pc = C_array;
+		pp = P_array;
+		ps = S_array - 4;
+
+		for (i = 0; i < 64; i++) {
+			if ((i & 0x0f) == 0)
+				ps += 4;
+			temp = A;
+			switch (i >> 4) {
+			case 0:
+				temp += FF(B, C, D);
+				break;
+			case 1:
+				temp += FG(B, C, D);
+				break;
+			case 2:
+				temp += FH(B, C, D);
+				break;
+			case 3:
+				temp += FI(B, C, D);
+			}
+			temp += cwp[(int) (*pp++)] + *pc++;
+			CYCLIC(temp, ps[i & 3]);
+			temp += B;
+			A = D;
+			D = C;
+			C = B;
+			B = temp;
+		}
+#  else
+		pc = C_array;
+		pp = P_array;
+		ps = S_array;
+
+		for (i = 0; i < 16; i++) {
+			temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+			CYCLIC(temp, ps[i & 3]);
+			temp += B;
+			A = D;
+			D = C;
+			C = B;
+			B = temp;
+		}
+
+		ps += 4;
+		for (i = 0; i < 16; i++) {
+			temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+			CYCLIC(temp, ps[i & 3]);
+			temp += B;
+			A = D;
+			D = C;
+			C = B;
+			B = temp;
+		}
+		ps += 4;
+		for (i = 0; i < 16; i++) {
+			temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+			CYCLIC(temp, ps[i & 3]);
+			temp += B;
+			A = D;
+			D = C;
+			C = B;
+			B = temp;
+		}
+		ps += 4;
+		for (i = 0; i < 16; i++) {
+			temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+			CYCLIC(temp, ps[i & 3]);
+			temp += B;
+			A = D;
+			D = C;
+			C = B;
+			B = temp;
+		}
+
+#  endif	/* MD5SUM_SIZE_VS_SPEED > 2 */
+# else
+		/* First round: using the given function, the context and a constant
+		   the next context is computed.  Because the algorithms processing
+		   unit is a 32-bit word and it is determined to work on words in
+		   little endian byte order we perhaps have to change the byte order
+		   before the computation.  To reduce the work for the next steps
+		   we store the swapped words in the array CORRECT_WORDS.  */
+
+#  define OP(a, b, c, d, s, T)	\
+      do	\
+        {	\
+	  a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T;	\
+	  ++words;	\
+	  CYCLIC (a, s);	\
+	  a += b;	\
+        }	\
+      while (0)
+
+		/* It is unfortunate that C does not provide an operator for
+		   cyclic rotation.  Hope the C compiler is smart enough.  */
+		/* gcc 2.95.4 seems to be --aaronl */
+#  define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+		/* Before we start, one word to the strange constants.
+		   They are defined in RFC 1321 as
+
+		   T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+		 */
+
+#  if MD5SUM_SIZE_VS_SPEED == 1
+		const u32 *pc;
+		const char *pp;
+		int i;
+#  endif	/* MD5SUM_SIZE_VS_SPEED */
+
+		/* Round 1.  */
+#  if MD5SUM_SIZE_VS_SPEED == 1
+		pc = C_array;
+		for (i = 0; i < 4; i++) {
+			OP(A, B, C, D, 7, *pc++);
+			OP(D, A, B, C, 12, *pc++);
+			OP(C, D, A, B, 17, *pc++);
+			OP(B, C, D, A, 22, *pc++);
+		}
+#  else
+		OP(A, B, C, D, 7, 0xd76aa478);
+		OP(D, A, B, C, 12, 0xe8c7b756);
+		OP(C, D, A, B, 17, 0x242070db);
+		OP(B, C, D, A, 22, 0xc1bdceee);
+		OP(A, B, C, D, 7, 0xf57c0faf);
+		OP(D, A, B, C, 12, 0x4787c62a);
+		OP(C, D, A, B, 17, 0xa8304613);
+		OP(B, C, D, A, 22, 0xfd469501);
+		OP(A, B, C, D, 7, 0x698098d8);
+		OP(D, A, B, C, 12, 0x8b44f7af);
+		OP(C, D, A, B, 17, 0xffff5bb1);
+		OP(B, C, D, A, 22, 0x895cd7be);
+		OP(A, B, C, D, 7, 0x6b901122);
+		OP(D, A, B, C, 12, 0xfd987193);
+		OP(C, D, A, B, 17, 0xa679438e);
+		OP(B, C, D, A, 22, 0x49b40821);
+#  endif	/* MD5SUM_SIZE_VS_SPEED == 1 */
+
+		/* For the second to fourth round we have the possibly swapped words
+		   in CORRECT_WORDS.  Redefine the macro to take an additional first
+		   argument specifying the function to use.  */
+#  undef OP
+#  define OP(f, a, b, c, d, k, s, T)	\
+      do	\
+	{	\
+	  a += f (b, c, d) + correct_words[k] + T;	\
+	  CYCLIC (a, s);	\
+	  a += b;	\
+	}	\
+      while (0)
+
+		/* Round 2.  */
+#  if MD5SUM_SIZE_VS_SPEED == 1
+		pp = P_array;
+		for (i = 0; i < 4; i++) {
+			OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++);
+			OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++);
+			OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++);
+			OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++);
+		}
+#  else
+		OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
+		OP(FG, D, A, B, C, 6, 9, 0xc040b340);
+		OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
+		OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+		OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
+		OP(FG, D, A, B, C, 10, 9, 0x02441453);
+		OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
+		OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+		OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
+		OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
+		OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
+		OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
+		OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
+		OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+		OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
+		OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+#  endif	/* MD5SUM_SIZE_VS_SPEED == 1 */
+
+		/* Round 3.  */
+#  if MD5SUM_SIZE_VS_SPEED == 1
+		for (i = 0; i < 4; i++) {
+			OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++);
+			OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++);
+			OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++);
+			OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++);
+		}
+#  else
+		OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
+		OP(FH, D, A, B, C, 8, 11, 0x8771f681);
+		OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
+		OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
+		OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
+		OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+		OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+		OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
+		OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
+		OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
+		OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
+		OP(FH, B, C, D, A, 6, 23, 0x04881d05);
+		OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
+		OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
+		OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+		OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
+#  endif	/* MD5SUM_SIZE_VS_SPEED == 1 */
+
+		/* Round 4.  */
+#  if MD5SUM_SIZE_VS_SPEED == 1
+		for (i = 0; i < 4; i++) {
+			OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++);
+			OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++);
+			OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++);
+			OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++);
+		}
+#  else
+		OP(FI, A, B, C, D, 0, 6, 0xf4292244);
+		OP(FI, D, A, B, C, 7, 10, 0x432aff97);
+		OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
+		OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
+		OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
+		OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+		OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
+		OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
+		OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+		OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+		OP(FI, C, D, A, B, 6, 15, 0xa3014314);
+		OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
+		OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
+		OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
+		OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+		OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
+#  endif	/* MD5SUM_SIZE_VS_SPEED == 1 */
+# endif	/* MD5SUM_SIZE_VS_SPEED > 1 */
+
+		/* Add the starting values of the context.  */
+		A += A_save;
+		B += B_save;
+		C += C_save;
+		D += D_save;
+	}
+
+	/* Put checksum in context given as argument.  */
+	ctx->A = A;
+	ctx->B = B;
+	ctx->C = C;
+	ctx->D = D;
+}
+
+/* Starting with the result of former calls of this function (or the
+ * initialization function update the context for the next LEN bytes
+ * starting at BUFFER.
+ * It is NOT required that LEN is a multiple of 64.
+ */
+
+static void md5_hash_bytes(const void *buffer, int len, struct md5_ctx_t *ctx)
+{
+	/* When we already have some bits in our internal buffer concatenate
+	   both inputs first.  */
+	if (ctx->buflen != 0) {
+		int left_over = ctx->buflen;
+		int add = 128 - left_over > len ? len : 128 - left_over;
+
+		memcpy(&ctx->buffer[left_over], buffer, add);
+		ctx->buflen += add;
+
+		if (left_over + add > 64) {
+			md5_hash_block(ctx->buffer, (left_over + add) & ~63, ctx);
+			/* The regions in the following copy operation cannot overlap.  */
+			memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+				   (left_over + add) & 63);
+			ctx->buflen = (left_over + add) & 63;
+		}
+
+		buffer = (const char *) buffer + add;
+		len -= add;
+	}
+
+	/* Process available complete blocks.  */
+	if (len > 64) {
+		md5_hash_block(buffer, len & ~63, ctx);
+		buffer = (const char *) buffer + (len & ~63);
+		len &= 63;
+	}
+
+	/* Move remaining bytes in internal buffer.  */
+	if (len > 0) {
+		memcpy(ctx->buffer, buffer, len);
+		ctx->buflen = len;
+	}
+}
+
+extern void md5_hash(const void *buffer, int length, void *md5_ctx)
+{
+	if (length % 64 == 0) {
+		md5_hash_block(buffer, length, md5_ctx);
+	} else {
+		md5_hash_bytes(buffer, length, md5_ctx);
+	}
+}
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ * in first 16 bytes following RESBUF.  The result is always in little
+ * endian byte order, so that a byte-wise output yields to the wanted
+ * ASCII representation of the message digest.
+ *
+ * IMPORTANT: On some systems it is required that RESBUF is correctly
+ * aligned for a 32 bits value.
+ */
+extern void md5_end(void *resbuf, void *md5_ctx)
+{
+	struct md5_ctx_t *ctx = md5_ctx;
+
+	/* Take yet unprocessed bytes into account.  */
+	u32 bytes = ctx->buflen;
+	int pad;
+
+	/* Now count remaining bytes.  */
+	ctx->total[0] += bytes;
+	if (ctx->total[0] < bytes)
+		++ctx->total[1];
+
+	pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+# if MD5SUM_SIZE_VS_SPEED > 0
+	memset(&ctx->buffer[bytes], 0, pad);
+	ctx->buffer[bytes] = 0x80;
+# else
+	memcpy(&ctx->buffer[bytes], fillbuf, pad);
+# endif	/* MD5SUM_SIZE_VS_SPEED > 0 */
+
+	/* Put the 64-bit file length in *bits* at the end of the buffer.  */
+	*(u32 *) & ctx->buffer[bytes + pad] = SWAP(ctx->total[0] << 3);
+	*(u32 *) & ctx->buffer[bytes + pad + 4] =
+		SWAP(((ctx->total[1] << 3) | (ctx->total[0] >> 29)));
+
+	/* Process last bytes.  */
+	md5_hash_block(ctx->buffer, bytes + pad + 8, ctx);
+
+	/* Put result from CTX in first 16 bytes following RESBUF.  The result is
+	 * always in little endian byte order, so that a byte-wise output yields
+	 * to the wanted ASCII representation of the message digest.
+	 *
+	 * IMPORTANT: On some systems it is required that RESBUF is correctly
+	 * aligned for a 32 bits value.
+	 */
+	((u32 *) resbuf)[0] = SWAP(ctx->A);
+	((u32 *) resbuf)[1] = SWAP(ctx->B);
+	((u32 *) resbuf)[2] = SWAP(ctx->C);
+	((u32 *) resbuf)[3] = SWAP(ctx->D);
+}
+
+/*********************************************************************************************/
+
+static const char __md5__magic[] = "$1$";	/* This string is magic for this algorithm.  Having 
+						   it this way, we can get better later on */
+static const unsigned char __md5_itoa64[] =		/* 0 ... 63 => ascii - 64 */
+	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void md5_to64( char *s, unsigned long v, int n)
+{
+	while (--n >= 0) {
+		*s++ = __md5_itoa64[v&0x3f];
+		v >>= 6;
+	}
+}
+
+/*
+ * UNIX password
+ *
+ * Use MD5 for what it is best at...
+ */
+
+const char *md5_crypt(const char *pw, const char *salt)
+{
+	/* Static stuff */
+	static const char *sp, *ep;
+	static char passwd[120], *p;
+	unsigned char	final[17];	/* final[16] exists only to aid in looping */
+	int sl,pl,i,__md5__magic_len,pw_len;
+	struct md5_ctx_t ctx,ctx1;
+	unsigned long l;
+
+	/* Refine the Salt first */
+	sp = salt;
+
+	/* If it starts with the magic string, then skip that */
+	__md5__magic_len = strlen(__md5__magic);
+	if(!strncmp(sp,__md5__magic,__md5__magic_len))
+		sp += __md5__magic_len;
+
+	/* It stops at the first '$', max 8 chars */
+	for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++)
+		continue;
+
+	/* get the length of the true salt */
+	sl = ep - sp;
+
+	md5_begin(&ctx);
+
+	/* The password first, since that is what is most unknown */
+	pw_len = strlen(pw);
+	md5_hash(pw,pw_len,&ctx);
+
+	/* Then our magic string */
+	md5_hash(__md5__magic,__md5__magic_len,&ctx);
+
+	/* Then the raw salt */
+	md5_hash(sp,sl,&ctx);
+
+	/* Then just as many characters of the MD5(pw,salt,pw) */
+	md5_begin(&ctx1);
+	md5_hash(pw,pw_len,&ctx1);
+	md5_hash(sp,sl,&ctx1);
+	md5_hash(pw,pw_len,&ctx1);
+	md5_end(final,&ctx1);
+	for(pl = pw_len; pl > 0; pl -= 16)
+		md5_hash(final,pl>16 ? 16 : pl,&ctx);
+
+	/* Don't leave anything around in vm they could use. */
+	memset(final,0,sizeof final);
+
+	/* Then something really weird... */
+	for (i = pw_len; i ; i >>= 1)
+		md5_hash(((i&1) ? final : (const unsigned char *) pw), 1, &ctx);
+
+	/* Now make the output string */
+	strcpy(passwd,__md5__magic);
+	strncat(passwd,sp,sl);
+	strcat(passwd,"$");
+
+	md5_end(final,&ctx);
+
+	/*
+	 * and now, just to make sure things don't run too fast
+	 * On a 60 Mhz Pentium this takes 34 msec, so you would
+	 * need 30 seconds to build a 1000 entry dictionary...
+	 */
+	for(i=0;i<1000;i++) {
+		md5_begin(&ctx1);
+		if(i & 1)
+			md5_hash(pw,pw_len,&ctx1);
+		else
+			md5_hash(final,16,&ctx1);
+
+		if(i % 3)
+			md5_hash(sp,sl,&ctx1);
+
+		if(i % 7)
+			md5_hash(pw,pw_len,&ctx1);
+
+		if(i & 1)
+			md5_hash(final,16,&ctx1);
+		else
+			md5_hash(pw,pw_len,&ctx1);
+		md5_end(final,&ctx1);
+	}
+
+	p = passwd + strlen(passwd);
+
+	final[16] = final[5];
+	for ( i=0 ; i < 5 ; i++ ) {
+		l = (final[i]<<16) | (final[i+6]<<8) | final[i+12];
+		md5_to64(p,l,4); p += 4;
+	}
+	l = final[11];
+	md5_to64(p,l,2); p += 2;
+	*p = '\0';
+
+	/* Don't leave anything around in vm they could use. */
+	memset(final,0,sizeof final);
+
+	return passwd;
+}
