1 # Functions for MD5 and SHA1 password encryption
4 # Returns a perl module name if the needed perl module(s) for MD5 encryption
5 # are not installed, or undef if they are
8 # On some systems, the crypt function just works!
9 return undef if (&unix_crypt_supports_md5());
14 eval "use Digest::MD5";
22 # encrypt_md5(string, [salt])
23 # Returns a string encrypted in MD5 format
26 local $passwd = $_[0];
29 if ($salt =~ /^\$1\$([^\$]+)/) {
30 # Extract actual salt from already encrypted password
33 if ($salt !~ /^[a-z0-9\/]{8}$/i) {
37 $salt ||= substr(time(), -8);
39 # Use built-in crypt support for MD5, if we can
40 if (&unix_crypt_supports_md5()) {
41 return crypt($passwd, $magic.$salt.'$xxxxxxxxxxxxxxxxxxxxxx');
44 # Add the password, magic and salt
49 eval "use Digest::MD5";
51 &error("Missing MD5 or Digest::MD5 perl modules");
54 local $ctx = eval "new $cls";
59 # Add some more stuff from the hash of the password and salt
60 local $ctx1 = eval "new $cls";
64 local $final = $ctx1->digest();
65 for($pl=length($passwd); $pl>0; $pl-=16) {
66 $ctx->add($pl > 16 ? $final : substr($final, 0, $pl));
69 # This piece of code seems rather pointless, but it's in the C code that
70 # does MD5 in PAM so it has to go in!
73 for($i=length($passwd); $i; $i >>= 1) {
78 $ctx->add(substr($passwd, $j, 1));
81 $final = $ctx->digest();
83 # This loop exists only to waste time
84 for($i=0; $i<1000; $i++) {
85 $ctx1 = eval "new $cls";
86 $ctx1->add($i & 1 ? $passwd : $final);
87 $ctx1->add($salt) if ($i % 3);
88 $ctx1->add($passwd) if ($i % 7);
89 $ctx1->add($i & 1 ? $final : $passwd);
90 $final = $ctx1->digest();
93 # Convert the 16-byte final string into a readable form
94 local $rv = $magic.$salt.'$';
95 local @final = map { ord($_) } split(//, $final);
96 $l = ($final[ 0]<<16) + ($final[ 6]<<8) + $final[12];
98 $l = ($final[ 1]<<16) + ($final[ 7]<<8) + $final[13];
100 $l = ($final[ 2]<<16) + ($final[ 8]<<8) + $final[14];
102 $l = ($final[ 3]<<16) + ($final[ 9]<<8) + $final[15];
104 $l = ($final[ 4]<<16) + ($final[10]<<8) + $final[ 5];
112 # unix_crypt_supports_md5()
113 # Returns 1 if the built-in crypt() function can already do MD5
114 sub unix_crypt_supports_md5
116 my $hash = '$1$A9wB3O18$zaZgqrEmb9VNltWTL454R/';
117 my $newhash = eval { crypt('test', $hash) };
118 return $newhash eq $hash;
121 @itoa64 = split(//, "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
127 $r .= $itoa64[$v & 0x3f];
135 eval "use Digest::SHA1";
136 return $@ ? "Digest::SHA1" : undef;
139 # encrypt_sha1(password)
140 # Encrypts a password in SHA1 format
144 local $sh = eval "use Digest::SHA1 qw(sha1_base64);return sha1_base64(\$pass);";
148 # encrypt_sha1_hash(password, salt)
149 # Hashes a combined salt+password with SHA1, and returns it in hex. Used on OSX
150 sub encrypt_sha1_hash
152 local ($pass, $salt) = @_;
157 # Returns an missing Perl module if blowfish is not available, undef if OK
160 eval "use Crypt::Eksblowfish::Bcrypt";
161 return $@ ? "Crypt::Eksblowfish::Bcrypt" : undef;
164 # encrypt_blowfish(password, [salt])
165 # Returns a string encrypted in blowfish format, suitable for /etc/shadow
168 local ($passwd, $salt) = @_;
169 local ($plain, $base64);
170 eval "use Crypt::Eksblowfish::Bcrypt";
171 if ($salt !~ /^\$2a\$/) {
172 # Invalid salt for Blowfish
176 # Generate a 22-character base-64 format salt
178 while(length($base64) < 22) {
179 $plain .= chr(int(rand()*96)+32);
180 $base64 = Crypt::Eksblowfish::Bcrypt::en_base64($plain);
182 $base64 = substr($base64, 0, 22);
183 $salt = '$2a$'.'08'.'$'.$base64;
185 return Crypt::Eksblowfish::Bcrypt::bcrypt($passwd, $salt);
188 # unix_crypt_supports_sha512()
189 # Returns 1 if the built-in crypt() function can already do SHA512
190 sub unix_crypt_supports_sha512
192 my $hash = '$6$Tk5o/GEE$zjvXhYf/dr5M7/jan3pgunkNrAsKmQO9r5O8sr/Cr1hFOLkWmsH4iE9hhqdmHwXd5Pzm4ubBWTEjtMeC.h5qv1';
193 my $newhash = eval { crypt('test', $hash) };
194 return $newhash eq $hash;
198 # Returns undef if SHA512 hashing is supported, or an error message if not
201 return &unix_crypt_supports_sha512() ? undef : 'Crypt::SHA';
204 # encrypt_sha512(password, [salt])
205 # Hashes a password, possibly with the give salt, with SHA512
208 local ($passwd, $salt) = @_;
209 $salt ||= '$6$'.substr(time(), -8).'$';
210 return crypt($passwd, $salt);
213 # validate_password(password, hash)
214 # Compares a password with a hash to see if they match, returns 1 if so,
215 # 0 otherwise. Tries all supported hashing schemes.
216 sub validate_password
218 local ($passwd, $hash) = @_;
221 local $chash = eval {
222 local $main::error_must_die = 1;
223 &unix_crypt($passwd, $hash);
225 return 1 if ($chash eq $hash);
229 local $mhash = &encrypt_md5($passwd, $hash);
230 return 1 if ($mhash eq $hash);
234 if (!&check_blowfish()) {
235 local $mhash = &encrypt_blowfish($passwd, $hash);
236 return 1 if ($mhash eq $hash);
240 if (!&check_sha512()) {
241 local $shash = &encrypt_sha512($passwd, $hash);
242 return 1 if ($shash eq $hash);
245 # Some other hashing, maybe supported by crypt
246 local $ohash = eval { crypt($passwd, $hash) };
247 return 1 if ($ohash eq $hash);