diff --git a/Makefile.in b/Makefile.in
index 82d2327..c2e0b45 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -107,14 +107,14 @@ am_tac_plus_OBJECTS = acct.$(OBJEXT) authen.$(OBJEXT) author.$(OBJEXT) \
 	parse.$(OBJEXT) programs.$(OBJEXT) pw.$(OBJEXT) \
 	pwlib.$(OBJEXT) regexp.$(OBJEXT) report.$(OBJEXT) \
 	sendauth.$(OBJEXT) sendpass.$(OBJEXT) tac_plus.$(OBJEXT) \
-	utils.$(OBJEXT) $(am__objects_1)
+	utils.$(OBJEXT) crypt-md5.$(OBJEXT) $(am__objects_1)
 tac_plus_OBJECTS = $(am_tac_plus_OBJECTS)
 am__DEPENDENCIES_1 =
 tac_plus_DEPENDENCIES = $(am__DEPENDENCIES_1)
 tac_plus_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
 	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(tac_plus_LDFLAGS) \
 	$(LDFLAGS) -o $@
-am_tac_pwd_OBJECTS = tac_pwd.$(OBJEXT)
+am_tac_pwd_OBJECTS = tac_pwd.$(OBJEXT) crypt-md5.$(OBJEXT) md5.$(OBJEXT)
 tac_pwd_OBJECTS = $(am_tac_pwd_OBJECTS)
 tac_pwd_LDADD = $(LDADD)
 tac_pwd_DEPENDENCIES = $(am__DEPENDENCIES_1)
diff --git a/choose_authen.c b/choose_authen.c
index cf50399..1fd1f06 100644
--- a/choose_authen.c
+++ b/choose_authen.c
@@ -127,7 +127,7 @@ choose_login(struct authen_data *data, struct authen_type *type)
 #endif	/* SKEY */
 	}
 
-	/* Not an skey user. Must be none, des, cleartext or file password */
+	/* Not an skey user. Must be none, des, md5, cleartext or file password */
 	type->authen_func = default_fn;
 	strcpy(type->authen_name, "default_fn");
 	return(CHOOSE_OK);
diff --git a/config.c b/config.c
index 10aa9da..3359637 100644
--- a/config.c
+++ b/config.c
@@ -52,7 +52,7 @@
    <host_decl>		:=	host = <string> {
 					key = <string>
 					prompt = <string>
-					enable = (file|skey|cleartext|des|
+					enable = (file|skey|cleartext|des|md5|
 						  nopassword) <filename/string>
 				}
 
@@ -66,6 +66,7 @@
 				skey |
 				cleartext <password> |
 				des <password> |
+				md5 <password> |
 				PAM |
 				nopassword
 
@@ -80,6 +81,7 @@
 #endif
 				pap	= cleartext <string> |
 				pap	= des <string> |
+				pap	= md5 <string> |
 				opap	= cleartext <string> |
 				global	= cleartext <string> |
 				msg	= <string>
@@ -856,7 +858,7 @@ parse_host(void)
 {
     HOST *h;
     HOST *host = (HOST *)tac_malloc(sizeof(HOST));
-    char buf[MAX_INPUT_LINE_LEN];
+    char buf[MAX_INPUT_LINE_LEN+1];
 
     memset(host, 0, sizeof(HOST));
 
@@ -903,6 +905,7 @@ parse_host(void)
 
 	    case S_cleartext:
 	    case S_des:
+	    case S_md5:
 		sprintf(buf, "%s ", sym_buf);
 		sym_get();
 		/* XXX: naughty, this should check the size */
@@ -919,7 +922,7 @@ parse_host(void)
 		break;
 
 	    default:
-		parse_error("expecting 'cleartext' or 'des' keyword after "
+		parse_error("expecting 'cleartext', 'des' or 'md5' keyword after "
 			    "'enable =' on line %d", sym_line);
 	    }
 	    sym_get();
@@ -1106,6 +1109,7 @@ parse_user(void)
 	    case S_file:
 	    case S_cleartext:
 	    case S_des:
+	    case S_md5:
 		sprintf(buf, "%s ", sym_buf);
 		sym_get();
 		strcat(buf, sym_buf);
@@ -1120,7 +1124,7 @@ parse_user(void)
 #ifdef HAVE_PAM
 			    "'PAM', "
 #endif
-			    "or 'des' keyword after 'login =' on line %d",
+			    "'des', or 'md5' keyword after 'login =' on line %d",
 			    sym_line);
 	    }
 	    sym_get();
@@ -1139,6 +1143,7 @@ parse_user(void)
 
 	    case S_cleartext:
 	    case S_des:
+	    case S_md5:
 		sprintf(buf, "%s ", sym_buf);
 		sym_get();
 		strcat(buf, sym_buf);
@@ -1146,7 +1151,7 @@ parse_user(void)
 		break;
 
 	    default:
-		parse_error("expecting 'cleartext', or 'des' keyword after "
+		parse_error("expecting 'cleartext', 'des', or 'md5' keyword after "
 			    "'pap =' on line %d", sym_line);
 	    }
 	    sym_get();
@@ -1167,6 +1172,7 @@ parse_user(void)
 		case S_file:
 		case S_cleartext:
 		case S_des:
+		case S_md5:
 		    sprintf(buf, "%s ", sym_buf);
 		    sym_get();
 		    strcat(buf, sym_buf);
@@ -1192,7 +1198,7 @@ parse_user(void)
 #ifdef SKEY
 				"'skey', "
 #endif
-				"or 'des' keyword after 'enable =' on line %d",
+				"'des' or 'md5' keyword after 'enable =' on line %d",
 				sym_line);
 	    }
 	    sym_get();
@@ -1282,7 +1288,7 @@ parse_user(void)
 	default:
 	    if (STREQ(sym_buf, "password")) {
 		fprintf(stderr,
-			"\npassword = <string> is obsolete. Use login = des <string>\n");
+			"\npassword = <string> is obsolete. Use login = md5 <string>\n");
 	    }
 	    parse_error("Unrecognised keyword %s for user on line %d",
 			sym_buf, sym_line);
diff --git a/crypt-md5.c b/crypt-md5.c
new file mode 100644
index 0000000..560b014
--- /dev/null
+++ b/crypt-md5.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include "md5.h"
+#include "crypt-md5.h"
+
+/*
+ * UNIX password
+ */
+
+char *
+crypt_md5(char *passwd, const char *pw, const char *salt)
+{
+	MD5_CTX	ctx,ctx1;
+	unsigned long l;
+	int sl, pl;
+	u_int i;
+	u_char final[MD5_SIZE];
+	static const char *sp, *ep;
+	static char *p;
+	static const char *magic = "$1$";
+
+	/* Refine the Salt first */
+	sp = salt;
+
+	/* If it starts with the magic string, then skip that */
+	if(!strncmp(sp, magic, strlen(magic)))
+		sp += strlen(magic);
+
+	/* 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;
+
+	MD5Init(&ctx);
+
+	/* The password first, since that is what is most unknown */
+	MD5Update(&ctx, (u_char *)pw, strlen(pw));
+
+	/* Then our magic string */
+	MD5Update(&ctx, (u_char *)magic, strlen(magic));
+
+	/* Then the raw salt */
+	MD5Update(&ctx, (u_char *)sp, (u_int)sl);
+
+	/* Then just as many characters of the MD5(pw,salt,pw) */
+	MD5Init(&ctx1);
+	MD5Update(&ctx1, (u_char *)pw, strlen(pw));
+	MD5Update(&ctx1, (u_char *)sp, (u_int)sl);
+	MD5Update(&ctx1, (u_char *)pw, strlen(pw));
+	MD5Final(final, &ctx1);
+	for(pl = (int)strlen(pw); pl > 0; pl -= MD5_SIZE)
+		MD5Update(&ctx, (u_char *)final,
+		    (u_int)(pl > MD5_SIZE ? MD5_SIZE : pl));
+
+	/* Don't leave anything around in vm they could use. */
+	memset(final, 0, sizeof(final));
+
+	/* Then something really weird... */
+	for (i = strlen(pw); i; i >>= 1)
+		if(i & 1)
+		    MD5Update(&ctx, (u_char *)final, 1);
+		else
+		    MD5Update(&ctx, (u_char *)pw, 1);
+
+	/* Now make the output string */
+	strcpy(passwd, magic);
+	strncat(passwd, sp, (u_int)sl);
+	strcat(passwd, "$");
+
+	MD5Final(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++) {
+		MD5Init(&ctx1);
+		if(i & 1)
+			MD5Update(&ctx1, (u_char *)pw, strlen(pw));
+		else
+			MD5Update(&ctx1, (u_char *)final, MD5_SIZE);
+
+		if(i % 3)
+			MD5Update(&ctx1, (u_char *)sp, (u_int)sl);
+
+		if(i % 7)
+			MD5Update(&ctx1, (u_char *)pw, strlen(pw));
+
+		if(i & 1)
+			MD5Update(&ctx1, (u_char *)final, MD5_SIZE);
+		else
+			MD5Update(&ctx1, (u_char *)pw, strlen(pw));
+		MD5Final(final, &ctx1);
+	}
+
+	p = passwd + strlen(passwd);
+
+	l = (final[ 0]<<16) | (final[ 6]<<8) | final[12];
+	_crypt_to64(p, l, 4); p += 4;
+	l = (final[ 1]<<16) | (final[ 7]<<8) | final[13];
+	_crypt_to64(p, l, 4); p += 4;
+	l = (final[ 2]<<16) | (final[ 8]<<8) | final[14];
+	_crypt_to64(p, l, 4); p += 4;
+	l = (final[ 3]<<16) | (final[ 9]<<8) | final[15];
+	_crypt_to64(p, l, 4); p += 4;
+	l = (final[ 4]<<16) | (final[10]<<8) | final[ 5];
+	_crypt_to64(p, l, 4); p += 4;
+	l = final[11];
+	_crypt_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);
+}
+
+
+static const char itoa64[] =		/* 0 ... 63 => ascii - 64 */
+	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+void
+_crypt_to64(char *s, u_long v, int n)
+{
+	while (--n >= 0) {
+		*s++ = itoa64[v&0x3f];
+		v >>= 6;
+	}
+}
diff --git a/crypt-md5.h b/crypt-md5.h
new file mode 100644
index 0000000..e9de22a
--- /dev/null
+++ b/crypt-md5.h
@@ -0,0 +1,42 @@
+/* LINTLIBRARY */
+/*
+ * Copyright (c) 1999
+ *      Mark Murray.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MARK MURRAY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL MARK MURRAY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libcrypt/crypt.h,v 1.8.32.1.4.1 2010/06/14 02:09:06 kensmith Exp $
+ *
+ */
+#ifndef _CRYPT_MD5_H
+#define _CRYPT_MD5_H
+
+/* magic sizes */
+#define MD5_SIZE 16
+#define CRYPT_MD5_SIZE 120
+
+char *crypt_md5(char *passwd, const char *pw, const char *salt);
+
+void _crypt_to64(char *s, u_long v, int n);
+
+
+#endif /* _CRYPT_MD5_H */
\ No newline at end of file
diff --git a/parse.c b/parse.c
index d2ece0b..51e0493 100644
--- a/parse.c
+++ b/parse.c
@@ -79,6 +79,7 @@ parser_init(void)
     declare("default", S_default);
     declare("deny", S_deny);
     declare("des", S_des);
+    declare("md5", S_md5);
     declare("enable", S_enable);
 #ifdef UENABLE
     declare("enableacl", S_enableacl);
@@ -211,6 +212,8 @@ codestring(int type)
 	return("nopassword");
     case S_des:
 	return("des");
+    case S_md5:
+	return("md5");
     case S_svc:
 	return("service");
     case S_default:
diff --git a/parse.h b/parse.h
index 95677f5..292c2e8 100644
--- a/parse.h
+++ b/parse.h
@@ -90,3 +90,4 @@
 # define S_pam		49
 #endif
 #define	S_syslog	50
+#define S_md5           51
diff --git a/pwlib.c b/pwlib.c
index 03723c9..fce9206 100644
--- a/pwlib.c
+++ b/pwlib.c
@@ -40,11 +40,14 @@ static int pam_tacacs(int, const struct pam_message **, struct pam_response **,
 		      void *);
 #endif
 
+# include "crypt-md5.h"
+
 /*
  * Generic password verification routines for des, file and cleartext passwords
  */
 static int etc_passwd_file_verify(char *, char *, struct authen_data *);
 static int des_verify(char *, char *);
+static int md5_verify(char *, char *);
 #if HAVE_PAM
 static int pam_verify(char *, char *);
 #endif
@@ -107,7 +110,7 @@ set_expiration_status(char *exp_date, struct authen_data *data)
 
 /*
  * Verify that this user/password is valid.  Works only for cleartext, file,
- * PAM and des passwords.  Return 1 if password is valid.
+ * PAM, des and md5 passwords.  Return 1 if password is valid.
  */
 int
 verify(char *name, char *passwd, struct authen_data *data, int recurse)
@@ -199,6 +202,21 @@ verify(char *name, char *passwd, struct authen_data *data, int recurse)
 	return(data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
     }
 
+    p = tac_find_substring("md5 ", cfg_passwd);
+    if (p) {
+	/* try to verify this md5 password */
+	if (!md5_verify(passwd, p)) {
+	    data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+	    return(0);
+	} else {
+	    data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+	}
+
+	exp_date = cfg_get_expires(name, recurse);
+	set_expiration_status(exp_date, data);
+	return(data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+    }
+
     p = tac_find_substring("file ", cfg_passwd);
     if (p) {
 	return(passwd_file_verify(name, passwd, data, p));
@@ -218,7 +236,7 @@ verify(char *name, char *passwd, struct authen_data *data, int recurse)
 
 /*
  * Verify that this user/password is valid for the matching password data,
- * such as "cleartext foopwd".  Works only for cleartext, des and file
+ * such as "cleartext foopwd".  Works only for cleartext, des, md5 and file
  * passwords and is used only for or by enable().
  * Return 1 if password is valid.  The caller needs to check any expiration
  * dates itself.
@@ -263,6 +281,19 @@ verify_pwd(char *username, char *passwd, struct authen_data *data,
 	return(data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
     }
 
+    p = tac_find_substring("md5 ", cfg_passwd);
+    if (p) {
+        /* try to verify this md5 password */
+        if (!md5_verify(passwd, p)) {
+            data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+            return(0);
+        } else {
+            data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+        }
+
+        return(data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+    }
+
     p = tac_find_substring("file ", cfg_passwd);
     if (p) {
 	if (!passwd_file_verify(username, passwd, data, p)) {
@@ -461,6 +492,67 @@ des_verify(char *users_passwd, char *encrypted_passwd)
     return(0);
 }
 
+/*
+ * verify a provided password against a md5 encrypted one.  return 1 if
+ * verified, 0 otherwise.
+ */
+static int
+md5_verify(char *users_passwd, char *encrypted_passwd)
+{
+    char  pass[CRYPT_MD5_SIZE];
+    char  salt[8];
+    int   salt_len;
+    char  *s, *p;
+
+    if (debug & DEBUG_PASSWD_FLAG)
+	report(LOG_DEBUG, "verify %s %s", users_passwd, encrypted_passwd);
+
+    if (users_passwd == NULL ||
+	*users_passwd == '\0' ||
+	encrypted_passwd == NULL ||
+	*encrypted_passwd == '\0') {
+	if (debug & DEBUG_PASSWD_FLAG)
+	    report(LOG_DEBUG, "verify returns 0");
+	return(0);
+    }
+
+    if ( strncmp(encrypted_passwd, "$1$", 3) ) {
+	if (debug & DEBUG_PASSWD_FLAG)
+	    report(LOG_DEBUG, "verify returns 0");
+
+    }
+
+    /* Extract salt from encrypted passwd */
+    /* Skip $1$ and copy upto next '$' into salt */
+
+    s = encrypted_passwd + 3;
+    p = strchr(s,'$');
+
+    if ( p == NULL || (p-s) <= 0 || (p-s) >= 8  ) {
+	if (debug & DEBUG_PASSWD_FLAG)
+	    report(LOG_DEBUG, "verify returns 0");
+	return(0);
+    }
+
+    strncpy(salt, s, p-s);
+
+    crypt_md5(pass, users_passwd, salt);
+
+    if (debug & DEBUG_PASSWD_FLAG)
+	report(LOG_DEBUG, "%s encrypts to %s", users_passwd, pass);
+
+    if (strcmp(pass, encrypted_passwd) == 0) {
+	if (debug & DEBUG_PASSWD_FLAG)
+	    report(LOG_DEBUG, "Password is correct");
+	return(1);
+    }
+
+    if (debug & DEBUG_PASSWD_FLAG)
+	report(LOG_DEBUG, "Password is incorrect");
+
+    return(0);
+}
+
 #if HAVE_PAM
 static int
 pam_tacacs(int nmsg, const struct pam_message **pmpp, struct pam_response
diff --git a/tac_plus.conf.5.in b/tac_plus.conf.5.in
index b372235..8ba7493 100644
--- a/tac_plus.conf.5.in
+++ b/tac_plus.conf.5.in
@@ -307,6 +307,16 @@ is a utility supplied with
 to assist in performing this encryption.
 .\"
 .TP
+.B md5_string
+Represents the one-way encryption of a password <string>.  For example,
+a password might encrypt to the string $1$Qc1x$WKkfDUmv3O2.RBNBEqRfQ0
+.sp
+.BR tac_pwd (8)
+is a utility supplied with
+.B tac_plus
+to assist in performing this encryption.
+.\"
+.TP
 .B expires
 Causes the <user>'s password to become invalid, starting on the specified
 expiration date.
@@ -368,6 +378,7 @@ format, and skey.
     file <filename>
     cleartext <string>
     des <des_string>
+    md5 <md5_string>
     PAM
     skey
     nopassword
@@ -467,7 +478,7 @@ daemon to the client. This is a security issue if the <key> is ever
 compromised.
 .sp
 enable specifies the enable password.  The <password_spec> may only be
-of type cleartext, des, nopassword or file.  If the daemon was compiled with
+of type cleartext, des, md5, nopassword or file.  If the daemon was compiled with
 per-user enable support (--enable-uenable), the host enable password will be
 evaluated iff the user does not have a personal enable password.
 .sp
@@ -484,7 +495,7 @@ acl = <string>
 enableacl = <string>
 .sp
 In the case of recursion, the first match is returned.
-host enable is cleartext, des, nopassword or file only.
+host enable is cleartext, des, md5, nopassword or file only.
 arap
 chap
 .B expires "May 23 2005"
@@ -503,6 +514,7 @@ chap
 	#endif
                          pap      = cleartext <string> |
                          pap      = des <string> |
+                         pap      = md5 <string> |
                          opap     = cleartext <string> |
                          global   = cleartext <string> |
                          msg      = <string>
@@ -1582,7 +1594,7 @@ user = homer {
     member = no_backbone
 }
 user = fred {
-    login = des mEX027bHtzTlQ
+    login = md5 $1$DXBO$uA334/9VqI65KJra76Y6W1
     name = "Fred Flintstone"
     member = admin
     expires = "May 23 2005"
diff --git a/tac_pwd.8 b/tac_pwd.8
index 8885128..06fd1a0 100644
--- a/tac_pwd.8
+++ b/tac_pwd.8
@@ -2,17 +2,17 @@
 .hys 50
 .TH "tac_pwd" "8" "22 March 2003"
 .SH NAME
-tac_pwd \- generate DES encryption of a password
+tac_pwd \- generate DES or MD5 encryption of a password
 .\"
 .SH SYNOPSIS
 .B tac_pwd
-[\fB\-eh\fP]
+[\fB\-emh\fP]
 [\c
 salt]
 .\"
 .SH DESCRIPTION
 .B tac_pwd 
-prompts for a clear-text password and produces a DES encryption of that
+prompts for a clear-text password and produces a DES or MD5 encryption of that 
 password on stdout which may be used in lieu of the clear-text representation
 in the
 .BR tac_plus.conf (5).
@@ -27,6 +27,10 @@ The DES salt may be provide as a command-line argument.
 Do not echo the plain-text password to the terminal.
 .\"
 .TP
+.B \-m
+Generate an MD5 password hash.
+.\"
+.TP
 .B \-h
 Display help message.
 .\"
diff --git a/tac_pwd.c b/tac_pwd.c
index 71941bd..b4485da 100644
--- a/tac_pwd.c
+++ b/tac_pwd.c
@@ -49,28 +49,34 @@
 # include <termios.h>
 #endif
 
+# include "crypt-md5.h"
+
 void	usage(void);
+char*  hash_pwd_des(char *, char *);
+char*  hash_pwd_md5(char *, char *);
 
 int
 main(int argc, char **argv)
 {
-    char		*crypt();
-    char		buf[24],
-			pass[25],
+    char		pass[25],
 			*salt = NULL;
     char		*result;
     extern char		*optarg;
     extern int		optind;
     char		*prompt = "Password to be encrypted: ";
     int			opt_e = 0,
+    				opt_m = 0,
 			n;
     struct termios	t;
 
-    while ((n = getopt(argc, argv, "eh")) != EOF) {
+    while ((n = getopt(argc, argv, "emh")) != EOF) {
 	switch (n) {
 	case 'e':
 	    opt_e++;
 	    break;
+	case 'm':
+	    opt_m++;
+	    break;
 	case 'h':
 	    usage();
 	    exit(0);
@@ -104,14 +110,28 @@ main(int argc, char **argv)
 	tcsetattr(STDIN_FILENO, TCSANOW, &t);
     }
 
-    if (!salt) {
-	int i, r, r1, r2;
+    if (opt_m) {
+        result = hash_pwd_md5(pass, salt);
+    } else {
+        result = hash_pwd_des(pass, salt);
+    }
+
+    write(1, result, strlen(result));
+    write(1, "\n", 1);
 
-	r1 = r2 = 0;
+    return(0);
+}
+
+char*
+obtain_salt(char *buf, char *salt, int saltlen)
+{
+
+    if (!salt) {
+	int i, r;
 
 	srand(time(0));
 
-	for (i = 0; i <= 1; i++) {
+	for (i = 0; i < saltlen; i++) {
 
 	    r = rand();
 
@@ -129,23 +149,40 @@ main(int argc, char **argv)
 	    if (r > 122)
 		r -= 5;
 
-	    if (i == 0)
-		r1 = r;
-
-	    if (i == 1)
-		r2 = r;
+           buf[i] = r;
 	}
 
-	sprintf(buf, "%c%c", r1, r2);
+       buf[saltlen] = 0;
 	salt = buf;
     }
 
-    result = crypt(pass, salt);
+    return salt;
+}
 
-    write(1, result, strlen(result));
-    write(1, "\n", 1);
+char*
+hash_pwd_des(char *pass, char *salt)
+{
+    char		*crypt();
+    char             saltbuf[4];
 
-    return(0);
+
+    salt = obtain_salt(saltbuf,salt,2); 
+
+    return crypt(pass, salt);
+}
+
+
+
+char*
+hash_pwd_md5(char *pass, char *salt)
+{
+    char             saltbuf[8];
+    static char      phash[CRYPT_MD5_SIZE];
+
+
+    salt = obtain_salt(saltbuf,salt,4);
+
+    return crypt_md5(phash,pass,salt);
 }
 
 void
@@ -153,6 +190,7 @@ usage(void)
 {
     fprintf(stderr, "Usage: tac_pwd [-eh] [<salt>]\n");
     fprintf(stderr, "\t-e\tdo not echo the password\n"
+		    "\t-m\tgenerate MD5 hash\n"
 		    "\t-h\tdisplay this message\n");
 
     return;
