--- makepasswd-1.10.orig/makepasswd
+++ makepasswd-1.10/makepasswd
@@ -4,7 +4,7 @@
# Program information.
#
-$Program = 'mkpasswd';
+$Program = 'makepasswd';
$Version = '1.10';
$Author = 'Rob Levin <levin@openproject.net>';
$Date = "Monday, 7 April 1999 at 22:56 (UCT)";
@@ -19,6 +19,8 @@
use Getopt::Long;
use FileHandle;
use integer;
+use bytes;
+use Crypt::OpenSSL::Random;
#
# Set default values for options ("" to indicate not-specified).
@@ -28,6 +30,7 @@
$Clear = "";
$Count = "";
$Crypt = 0;
+$CryptMd5 = 0;
$CryptSalt = "";
$MaxChars = "";
$MinChars = "";
@@ -43,8 +46,8 @@
#
$Error = 0;
-$CharMin = 6;
-$CharMax = 8;
+$CharMin = 8;
+$CharMax = 10;
$CharFormat = $CharMax + 3;
$CountUsed = 1;
$PasswordRepeat = 1;
@@ -91,9 +94,11 @@
&GetOptions
(
'chars=i' => \$Chars,
+ 'clear=s' => \$OldClear,
'clearfrom=s' => \$Clear,
'count=i' => \$Count,
'crypt!' => \$Crypt,
+ 'crypt-md5!' => \$CryptMd5,
'cryptsalt=i' => \$CryptSalt,
'help' => \$ShowHelp,
'maxchars=i' => \$MaxChars,
@@ -115,6 +120,17 @@
};
#
+# If password generation option was specified with the old --clear,
+# warn the user and exit
+#
+$OldClear ne "" and do
+{
+ print STDERR "$Program: Option --clear is no longer present \n".
+ "please use --clearfrom and supply a file for it.\n";
+ $Error = 1;
+};
+
+#
# If password generation option was specified with --clearfrom, flag it.
#
@@ -134,10 +150,10 @@
"--chars --minchars --maxchars --count --string.\n";
$Error = 1;
};
- $Crypt or do
+ $Crypt or $CryptMd5 or do
{
print STDERR "$Program: Option --clearfrom may not be specified ".
- "without option --crypt.\n";
+ "without option --crypt or --crypt-md5.\n";
$Error = 1;
};
open CLEARFROM, "$Clear" or do
@@ -256,9 +272,9 @@
# If --crypt is not set or --cryptsalt is set, disallow this parameter.
#
- $Crypt or do
+ $Crypt or $CryptMd5 or do
{
- print STDERR "$Program: To use --repeatpass, --crypt must also be set.\n";
+ print STDERR "$Program: To use --repeatpass, --crypt or --crypt-md5 must also be set.\n";
$Error = 1;
};
$CryptSalt and do
@@ -425,12 +441,12 @@
exit(0);
#
-# sub Help: Display help information on STDERR.
+# sub Help: Display help information on STDOUT.
#
sub Help
{
- print STDERR
+ print
"$Program v$Version, a utility to generate and/or encrypt passwords.
Copyright (c) $Copyright by $Author. All rights are reserved by
@@ -441,42 +457,51 @@
Format: $Program [option...]
-For low (nonzero) values of --rerandom, tap the CONTROL key at random
-intervals if the program seems to stall. The entropy base for /dev/random
-is depleted easily.
-
Options are:
--chars=N Generate passwords with exactly N characters (do not use with
options --minchars and --maxchars).
--clearfrom=FILE Use a clear password from FILE instead of generating passwords.
- Requires the --crypt option; may not be used with options
- --chars, --maxchars, --minchars, --count, --string,
- --nocrypt. Trailing newlines are ignored, other
- whitespace is not.
+ Requires the --crypt or --crypt-md5 option; may not be
+ used with options --chars, --maxchars, --minchars,
+ --count, --string, --nocrypt. Trailing newlines are
+ ignored, other whitespace is not.
--count=N Produce a total of N passwords (the default is one).
--crypt Produce encrypted passwords.
+--crypt-md5 Produce encrypted passwords using the MD5 digest (hash)
+ algorithm.
--cryptsalt=N Use crypt() salt N, a positive number <= 4096. If random
seeds are desired, specify a zero value (the default).
--help Ignore other operands and produce only this help display.
---maxchars=N Generate passwords with at most N characters (default=8).
---minchars=N Generate passwords with at least N characters (default=6).
+--maxchars=N Generate passwords with at most N characters (default=10).
+--minchars=N Generate passwords with at least N characters (default=8).
--nocrypt Do not encrypt the generated password(s) (the default).
--noverbose Display no labels on output (the default).
--randomseed=N Use random number seed N, between 0 and 2^32 inclusive. A zero
- value results in a real-random seed.
+ value results in a real-random seed. This option
+ generates predictable passwords, and should normally
+ be avoided.
--rerandom=N Set the random seed value every N values used. Specify zero
to use a single seed value (the default). Specify
one to get true-random passwords, but plan on hitting
the CONTROL key a lot while it's running. ;)
---repeatpass=N Use each password N times (4096 maximum, --crypt must be set
- and --cryptsalt may not be set).
+--repeatpass=N Use each password N times (4096 maximum, --crypt or
+ --crypt-md5 must be set and --cryptsalt may not be set).
--string=STRING Use the characters in STRING to generate random passwords.
--verbose Display labelling information on output.
";
}
#
+# sub NumBits(N): Number of significant bits in N.
+#
+
+sub NumBits
+{
+ return length(sprintf('%b', $_[0]));
+}
+
+#
# sub Random(A, B): Produce a random integer from A to B, inclusive.
#
@@ -492,7 +517,42 @@
{
$RerandomNow = $RerandomCount;
};
- my $RandomOutputVal=rand($_[1]-$_[0]+1);
+ my $RandomOutputVal;
+ if ($RandSeed)
+ {
+ $RandomOutputVal = rand($_[1]-$_[0]+1);
+ }
+ else
+ {
+ # Annoyingly, there's no Perl binding for
+ # BN_pseudo_rand_range, so we have to emulate it. We
+ # repeatedly generate n-bit pseudo-random byte sequences
+ # (with n = number of significant bits in range) until we
+ # get something less than range.
+ my $range = $_[1]-$_[0]+1;
+ my $bits = NumBits($range);
+ my $bytes = ($bits - 1) / 8 + 1;
+ my $max = 1 << ($bytes * 8);
+ $max -= $max % $range;
+ while (1)
+ {
+ my $buf = Crypt::OpenSSL::Random::random_pseudo_bytes($bytes);
+ my $val = 0;
+ for my $byte (unpack('C*', $buf))
+ {
+ $val = ($val << 8) + $byte;
+ }
+ if ($val < $max)
+ {
+ # Using the modulus is OK here; we're
+ # working with a byte stream, so the
+ # low-order bits are no worse than any of
+ # the others.
+ $RandomOutputVal = $val % $range;
+ last;
+ }
+ }
+ }
$RandomOutputVal=$RandomOutputVal+$_[0];
$RandomOutputVal =~ s/\..*$//;
$RandomOutputVal;
@@ -506,19 +566,25 @@
{
my $i;
my $SeedOutput = $RandSeed;
- $SeedOutput or do
+ if ($SeedOutput)
{
- open(RANDOMSEED, "</dev/random") or die "Could not access a seed value.\n";
- $SeedOutput=0;
- for (my $i=0; $i < 4; $i++)
+ srand $SeedOutput;
+ }
+ else
+ {
+ open(RANDOMSEED, "<:unix", "/dev/urandom") or die "Could not access a seed value.\n";
+ $SeedOutput='';
+ my $i=0;
+ while ($i < 32)
{
- $Char=getc(RANDOMSEED);
- $NumChar=ord($Char);
- $SeedOutput = ($SeedOutput*256)+$NumChar;
+ my $r = read(RANDOMSEED, $SeedOutput, 32 - $i, $i);
+ die "read /dev/urandom failed: $!\n" unless defined $r;
+ $i += $r;
}
close(RANDOMSEED);
+ Crypt::OpenSSL::Random::random_seed($SeedOutput);
+ Crypt::OpenSSL::Random::random_status() or die "OpenSSL PRNG could not be sufficiently seeded.\n";
};
- srand $SeedOutput;
}
#
@@ -588,6 +654,82 @@
}
#
+# sub Md5Base64Char(A): Base-64-encode a character from an MD5 digest.
+#
+
+sub Md5Base64Char
+{
+ my $map64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+ my $char = shift;
+ $char = 0 if $char < 0;
+ $char = 63 if $char > 63;
+ substr($map64, $char, 1);
+}
+
+#
+# sub MakeMd5Salt(A, B): Generate a crypt salt string from a number from 0
+# through 4095.
+#
+
+sub MakeMd5Salt
+{
+ my $md5 = Digest::MD5->new();
+ $md5->add(time);
+ $md5->add($$);
+ $md5->add($_[0]);
+ $md5->add($_[1]);
+ my $digest = $md5->digest;
+ $digest = substr($digest, 0, 8);
+ my $salt;
+ for my $char (map { ord($_) & 077 } split //, $digest)
+ {
+ $salt .= Md5Base64Char($char);
+ }
+ $salt;
+}
+
+#
+# sub CryptMd5Password(A, B): Encrypt the password provided using the
+# MD5 digest algorithm; keep a running list of codes used as long as B is
+# true.
+#
+
+sub CryptMd5Password
+{
+ my $password = $_[0];
+ eval "use Crypt::PasswdMD5";
+ if ($@)
+ {
+ print STDERR "$Program: Could not load the Crypt::PasswdMD5 library, cannot use --crypt-md5\n".
+ "This may be due to an invalid or incomplete Perl installation\n.";
+ exit 1;
+ };
+
+ my $ThisSeed = $SeedValue;
+ if ($ThisSeed)
+ {
+ $ThisSeed--;
+ }
+ else
+ {
+ $_[1] or do
+ {
+ %UsedSeed = ();
+ };
+ $ThisSeed = Random(0, 4095);
+ do
+ {
+ $ThisSeed = Random(0, 4095);
+ }
+ until not exists $UsedSeed{$ThisSeed};
+ $UsedSeed{$ThisSeed} = $ThisSeed;
+ }
+
+ my $salt = MakeMd5Salt($password, $ThisSeed);
+ unix_md5_crypt($password, $salt);
+}
+
+#
# sub ProcessPassword(A): Process the password provided.
#
@@ -600,6 +742,7 @@
$Password = MakePassword();
$Crypt and $PaddedPass = sprintf "%-$CharFormat"."s", $Password;
+ $CryptMd5 and $PaddedPass = sprintf "%-$CharFormat"."s", $Password;
$Verbose and do
{
$PassLabel="Password=";
@@ -616,6 +759,12 @@
print "$PassLabel"."$PaddedPass"."$CryptLabel"."$CryptedPass\n";
$Verbose and $PaddedPass = $EmptyPassword;
}
+ elsif ($CryptMd5)
+ {
+ $CryptedPass = CryptMd5Password($Password);
+ print "$PassLabel"."$PaddedPass"."$CryptLabel"."$CryptedPass\n";
+ $Verbose and $PaddedPass = $EmptyPassword;
+ }
else
{
print "$PassLabel"."$Password\n";