- add new applet mkpasswd(1)
function                                             old     new   delta
bb_ask                                                 -     355    +355
mkpasswd_main                                          -     296    +296
.rodata                                           121746  121847    +101
packed_usage                                       24632   24689     +57
static.methods                                         -      21     +21
gmatch                                               229     248     +19
bb_ask_stdin                                           -      11     +11
applet_names                                        1949    1958      +9
applet_main                                         1172    1176      +4
sulogin_main                                         503     505      +2
applet_nameofs                                       586     588      +2
sha256_hash                                          329     327      -2
correct_password                                     208     206      -2
parse_command                                       1442    1439      -3
get_cred_or_die                                      145     141      -4
passwd_main                                         1054    1044     -10
bb_askpass                                           348       -    -348
------------------------------------------------------------------------------
(add/remove: 4/1 grow/shrink: 7/5 up/down: 877/-369)          Total: 508 bytes

diff --git a/include/applets.h b/include/applets.h
index ad39255..9c844ee 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -252,6 +252,7 @@
 //USE_MKE2FS(APPLET_ODDNAME(mkfs.ext3, mke2fs, _BB_DIR_SBIN, _BB_SUID_NEVER, mkfs_ext3))
 USE_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, _BB_DIR_SBIN, _BB_SUID_NEVER, mkfs_minix))
 USE_MKNOD(APPLET(mknod, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_MKPASSWD(APPLET(mkpasswd, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_MKSWAP(APPLET(mkswap, _BB_DIR_SBIN, _BB_SUID_NEVER))
 USE_MKTEMP(APPLET(mktemp, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_MODPROBE(APPLET(modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER))
diff --git a/include/libbb.h b/include/libbb.h
index acae93a..a34e8a1 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1021,9 +1021,10 @@
  * return value: 1: read-only loopdev was setup, 0: rw, < 0: error */
 extern int set_loop(char **devname, const char *file, unsigned long long offset) FAST_FUNC;
 
-
+/* Like bb_ask below, but asks on stdin with no timeout.  */
+char *bb_ask_stdin(const char * prompt) FAST_FUNC;
 //TODO: pass buf pointer or return allocated buf (avoid statics)?
-char *bb_askpass(int timeout, const char * prompt) FAST_FUNC;
+char *bb_ask(const int fd, int timeout, const char * prompt) FAST_FUNC;
 int bb_ask_confirmation(void) FAST_FUNC;
 
 int bb_parse_mode(const char* s, mode_t* theMode) FAST_FUNC;
diff --git a/include/usage.h b/include/usage.h
index 000b864..a174222 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -2618,6 +2618,21 @@
        "$ mknod /dev/fd0 b 2 0\n" \
        "$ mknod -m 644 /tmp/pipe p\n"
 
+#define mkpasswd_trivial_usage \
+       "[OPTIONS] [PASSWORD]"
+#define mkpasswd_full_usage "\n\n" \
+       "Crypts the PASSWORD using crypt(3)\n" \
+     "\nOptions:" \
+     "\n\t-P"USE_GETOPT_LONG(", --password-fd=")"NUM\tread password from fd NUM" \
+     "\n\t-s"USE_GETOPT_LONG(", --stdin")"\t\tuse stdin; like -P0" \
+     "\n\t-m"USE_GETOPT_LONG(", --method=")"TYPE\tEncryption method TYPE" \
+     "\n\t-S"USE_GETOPT_LONG(", --salt=")"SALT\t\tuse SALT" \
+     //"\n\t-l"USE_GETOPT_LONG(", --length=")"LEN\tRandom password with length LEN"
+
+#define mkpasswd_example_usage \
+       "$ mkpasswd -m md5\n" \
+       "$ mkpasswd -l 12\n"
+
 #define mkswap_trivial_usage \
        "DEVICE"
 #define mkswap_full_usage "\n\n" \
diff --git a/libbb/bb_askpass.c b/libbb/bb_askpass.c
index c976497..c0dcf0c 100644
--- a/libbb/bb_askpass.c
+++ b/libbb/bb_askpass.c
@@ -15,7 +15,11 @@
 {
 }
 
-char* FAST_FUNC bb_askpass(int timeout, const char *prompt)
+char* FAST_FUNC bb_ask_stdin(const char *prompt)
+{
+	return bb_ask(STDIN_FILENO, 0, prompt);
+}
+char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
 {
 	/* Was static char[BIGNUM] */
 	enum { sizeof_passwd = 128 };
@@ -30,8 +34,8 @@
 		passwd = xmalloc(sizeof_passwd);
 	memset(passwd, 0, sizeof_passwd);
 
-	tcgetattr(STDIN_FILENO, &oldtio);
-	tcflush(STDIN_FILENO, TCIFLUSH);
+	tcgetattr(fd, &oldtio);
+	tcflush(fd, TCIFLUSH);
 	tio = oldtio;
 	tio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
 	tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP);
@@ -52,7 +56,7 @@
 	ret = NULL;
 	/* On timeout or Ctrl-C, read will hopefully be interrupted,
 	 * and we return NULL */
-	if (read(STDIN_FILENO, passwd, sizeof_passwd - 1) > 0) {
+	if (read(fd, passwd, sizeof_passwd - 1) > 0) {
 		ret = passwd;
 		i = 0;
 		/* Last byte is guaranteed to be 0
diff --git a/libbb/correct_password.c b/libbb/correct_password.c
index 255b048..6301589 100644
--- a/libbb/correct_password.c
+++ b/libbb/correct_password.c
@@ -68,7 +68,7 @@
 		return 1;
 
  fake_it:
-	unencrypted = bb_askpass(0, "Password: ");
+	unencrypted = bb_ask_stdin("Password: ");
 	if (!unencrypted) {
 		return 0;
 	}
diff --git a/loginutils/Config.in b/loginutils/Config.in
index 5f66e86..6efca7e 100644
--- a/loginutils/Config.in
+++ b/loginutils/Config.in
@@ -252,6 +252,13 @@
 	  standard input and uses this information to update a group of
 	  existing users.
 
+config MKPASSWD
+	bool "mkpasswd"
+	default n
+	help
+	  mkpasswd encrypts the given password with the crypt(3) libc function
+	  using the given salt.
+
 config SU
 	bool "su"
 	default n
diff --git a/loginutils/Kbuild b/loginutils/Kbuild
index 3d0d777..616d977 100644
--- a/loginutils/Kbuild
+++ b/loginutils/Kbuild
@@ -11,6 +11,7 @@
 lib-$(CONFIG_CHPASSWD)	+= chpasswd.o
 lib-$(CONFIG_GETTY)	+= getty.o
 lib-$(CONFIG_LOGIN)	+= login.o
+lib-$(CONFIG_MKPASSWD)	+= mkpasswd.o
 lib-$(CONFIG_PASSWD)	+= passwd.o
 lib-$(CONFIG_SU)	+= su.o
 lib-$(CONFIG_SULOGIN)	+= sulogin.o
diff --git a/loginutils/mkpasswd.c b/loginutils/mkpasswd.c
new file mode 100644
index 0000000..442738e
--- /dev/null
+++ b/loginutils/mkpasswd.c
@@ -0,0 +1,71 @@
+/* vi: set sw=4 ts=4 sts=4: */
+/*
+ * mkpasswd - Overfeatured front end to crypt(3)
+ * Copyright (c) 2008 Bernhard Reutner-Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+int mkpasswd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkpasswd_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *chp = NULL, *method = NULL, *salt = NULL;
+	char *encrypted;
+	int fd = STDIN_FILENO;
+	enum {
+		OPT_P = (1 << 0),
+		OPT_s = (1 << 1),
+		OPT_m = (1 << 2),
+		OPT_S = (1 << 3)
+	};
+	static const char methods[] ALIGN1 =
+		/*"des\0"*/"md5\0""sha-256\0""sha-512\0";
+	enum { TYPE_des, TYPE_md5, TYPE_sha256, TYPE_sha512 };
+	unsigned algo = TYPE_des, algobits = 1;
+#if ENABLE_GETOPT_LONG
+	static const char mkpasswd_longopts[] ALIGN1 =
+		"password-fd\0"	Required_argument "P"
+		"stdin\0"		No_argument "s"
+		"method\0"		Required_argument "m"
+		"salt\0"		Required_argument "S"
+	;
+	applet_long_options = mkpasswd_longopts;
+#endif
+	opt_complementary = "?1"; /* at most one non-option argument */
+	getopt32(argv, "P:sm:S:", &chp, &method, &salt);
+	argv += optind;
+	if (option_mask32 & OPT_P)
+		fd = xatoi_u(chp);
+	if (option_mask32 & OPT_m)
+		algo = index_in_strings(methods, method) + 1;
+	if (*argv) /* we have a cleartext passwd */
+		chp = *argv;
+	else
+		chp = bb_ask(fd, 0, "Password: ");
+	if (!salt)
+		salt = xmalloc(128);
+
+	if (algo) {
+		char foo[2];
+		foo[0] = foo[2] = '$';
+		algobits = 4;
+		/* MD5 == "$1$", SHA-256 == "$5$", SHA-512 == "$6$" */
+		if (algo > 1) {
+			algo += 3;
+			algobits = 8;
+		}
+		foo[1] = '0' + (algo);
+		strcpy(salt, foo);
+	}
+	/* The opt_complementary adds a bit of additional noise, which is good
+	   but not strictly needed.  */
+	crypt_make_salt(salt + ((!!algo) * 3), algobits, (int)&opt_complementary);
+	encrypted = pw_encrypt(chp, salt, 1);
+	puts(encrypted);
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(encrypted);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/loginutils/passwd.c b/loginutils/passwd.c
index b156ab5..e3e74ba 100644
--- a/loginutils/passwd.c
+++ b/loginutils/passwd.c
@@ -22,7 +22,7 @@
 	if (myuid && pw->pw_passwd[0]) {
 		char *encrypted;
 
-		orig = bb_askpass(0, "Old password:"); /* returns ptr to static */
+		orig = bb_ask_stdin("Old password:"); /* returns ptr to static */
 		if (!orig)
 			goto err_ret;
 		encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
@@ -35,16 +35,16 @@
 		}
 		if (ENABLE_FEATURE_CLEAN_UP) free(encrypted);
 	}
-	orig = xstrdup(orig); /* or else bb_askpass() will destroy it */
-	newp = bb_askpass(0, "New password:"); /* returns ptr to static */
+	orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */
+	newp = bb_ask_stdin("New password:"); /* returns ptr to static */
 	if (!newp)
 		goto err_ret;
-	newp = xstrdup(newp); /* we are going to bb_askpass() again, so save it */
+	newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */
 	if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
 	 && obscure(orig, newp, pw) && myuid)
 		goto err_ret; /* non-root is not allowed to have weak passwd */
 
-	cp = bb_askpass(0, "Retype password:");
+	cp = bb_ask_stdin("Retype password:");
 	if (!cp)
 		goto err_ret;
 	if (strcmp(cp, newp)) {
diff --git a/loginutils/sulogin.c b/loginutils/sulogin.c
index 892c434..4ffefe9 100644
--- a/loginutils/sulogin.c
+++ b/loginutils/sulogin.c
@@ -51,7 +51,7 @@
 	/* Clear dangerous stuff, set PATH */
 	sanitize_env_if_suid();
 
-// bb_askpass() already handles this
+// bb_ask() already handles this
 //	signal(SIGALRM, catchalarm);
 
 	pwd = getpwuid(0);
@@ -77,7 +77,7 @@
 		int r;
 
 		/* cp points to a static buffer that is zeroed every time */
-		cp = bb_askpass(timeout,
+		cp = bb_ask(STDIN_FILENO, timeout,
 				"Give root password for system maintenance\n"
 				"(or type Control-D for normal startup):");
 
diff --git a/mailutils/mail.c b/mailutils/mail.c
index ab1304a..f309848 100644
--- a/mailutils/mail.c
+++ b/mailutils/mail.c
@@ -228,8 +228,8 @@
 {
 	// either from TTY
 	if (isatty(fd)) {
-		G.user = xstrdup(bb_askpass(0, "User: "));
-		G.pass = xstrdup(bb_askpass(0, "Password: "));
+		G.user = xstrdup(bb_ask_stdin("User: "));
+		G.pass = xstrdup(bb_ask_stdin("Password: "));
 	// or from STDIN
 	} else {
 		FILE *fp = fdopen(fd, "r");