MD5 support for Webmin users using modules

This commit is contained in:
Jamie Cameron
2008-03-25 20:57:29 +00:00
parent 839a41bf2e
commit 49c852df0c
7 changed files with 88 additions and 31 deletions

View File

@@ -46,4 +46,4 @@ Updated the user interface to use the Webmin UI library.
Fixed the display of modules granted to groups.
Added a per-user option to opt out of forced password changes after a certain number of days.
A human-readable description of the password restrictions regular expression can be entered, for use in error messages.
Webmin users can now be given temporary passwords, which they are forced to change at the next login.
Webmin users can now be given temporary passwords, which they are forced to change at the next login. Thanks to GE Medical Systems for supporting this feature.

View File

@@ -4,6 +4,7 @@
do '../web-lib.pl';
&init_config();
do '../ui-lib.pl';
do 'md5-lib.pl';
%access = &get_module_acl();
$access{'switch'} = 0 if (&is_readonly_mode());
@@ -658,14 +659,17 @@ else {
}
# encrypt_password(password, [salt])
# Encrypts a Webmin user password
sub encrypt_password
{
local ($pass, $salt) = @_;
if ($gconfig{'md5pass'}) {
$salt ||= '$1$'.substr(time(), -8);
return crypt($pass, $salt);
# Use MD5 encryption
$salt ||= '$1$'.substr(time(), -8).'$xxxxxxxxxxxxxxxxxxxxxx';
return &encrypt_md5($pass, $salt);
}
else {
# Use Unix DES
&seed_random();
$salt ||= chr(int(rand(26))+65).chr(int(rand(26))+65);
return &unix_crypt($pass, $salt);
@@ -797,7 +801,7 @@ local $use_md5 = &md5_perl_module();
if (!$hash_session_id_cache{$sid}) {
if ($use_md5) {
# Take MD5 hash
$hash_session_id_cache{$sid} = &encrypt_md5($sid);
$hash_session_id_cache{$sid} = &hash_md5_session($sid);
}
else {
# Unix crypt
@@ -807,9 +811,9 @@ if (!$hash_session_id_cache{$sid}) {
return $hash_session_id_cache{$sid};
}
# encrypt_md5(string)
# hash_md5_session(string)
# Returns a string encrypted in MD5 format
sub encrypt_md5
sub hash_md5_session
{
local $passwd = $_[0];
local $use_md5 = &md5_perl_module();
@@ -860,18 +864,6 @@ $rv .= &to64($l, 2);
return $rv;
}
sub to64
{
local ($v, $n) = @_;
local @itoa64 = split(//, "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
local $r;
while(--$n >= 0) {
$r .= $itoa64[$v & 0x3f];
$v >>= 6;
}
return $r;
}
# Returns a Perl module for MD5 hashing, or undef if none
sub md5_perl_module
{

1
acl/md5-lib.pl Symbolic link
View File

@@ -0,0 +1 @@
../useradmin/md5-lib.pl

View File

@@ -2963,7 +2963,8 @@ elsif ($canmode == 0) {
}
elsif ($canmode == 1) {
# Attempt Webmin authentication
if ($users{$webminuser} eq &unix_crypt($pass, $users{$webminuser})) {
if ($users{$webminuser} eq
&password_crypt($pass, $users{$webminuser})) {
# Password is valid .. but check for expiry
local $lc = $lastchanges{$user};
if ($config{'pass_maxdays'} && $lc && !$nochange{$user}) {
@@ -3066,7 +3067,8 @@ elsif ($config{'passwd_file'}) {
if (/^\s*(\S+):/ && $1 eq $_[0]) {
$_ = <FILE>;
if (/^\s*password\s*=\s*(\S+)\s*$/) {
$rv = $1 eq &unix_crypt($_[1], $1) ? 1 : 0;
$rv = $1 eq &password_crypt($_[1], $1) ?
1 : 0;
}
last;
}
@@ -3079,7 +3081,7 @@ elsif ($config{'passwd_file'}) {
local $u = $l[$config{'passwd_uindex'}];
local $p = $l[$config{'passwd_pindex'}];
if ($u eq $_[0]) {
$rv = $p eq &unix_crypt($_[1], $p) ? 1 : 0;
$rv = $p eq &password_crypt($_[1], $p) ? 1 : 0;
if ($config{'passwd_cindex'} ne '' && $rv) {
# Password may have expired!
local $c = $l[$config{'passwd_cindex'}];
@@ -3105,7 +3107,7 @@ elsif ($config{'passwd_file'}) {
# Fallback option - check password returned by getpw*
local @uinfo = getpwnam($_[0]);
if ($uinfo[1] ne '' && &unix_crypt($_[1], $uinfo[1]) eq $uinfo[1]) {
if ($uinfo[1] ne '' && &password_crypt($_[1], $uinfo[1]) eq $uinfo[1]) {
return 1;
}
@@ -4161,6 +4163,22 @@ if (!defined($logout_time_cache{$user,$sid})) {
return $logout_time_cache{$user,$sid};
}
# password_crypt(password, salt)
# If the salt looks like MD5 and we have a library for it, perform MD5 hashing
# of a password. Otherwise, do Unix crypt.
sub password_crypt
{
local ($pass, $salt) = @_;
if ($salt =~ /^\$1\$/ && $use_md5) {
return &encrypt_md5($pass, $salt);
}
else {
return &unix_crypt($pass, $salt);
}
}
# unix_crypt(password, salt)
# Performs standard Unix hashing for a password
sub unix_crypt
{
local ($pass, $salt) = @_;
@@ -4591,19 +4609,31 @@ if (!$hash_session_id_cache{$sid}) {
return $hash_session_id_cache{$sid};
}
# encrypt_md5(string)
# encrypt_md5(string, [salt])
# Returns a string encrypted in MD5 format
sub encrypt_md5
{
local $passwd = $_[0];
local ($passwd, $salt) = @_;
local $magic = '$1$';
if ($salt =~ /^\$1\$(.{8})/) {
# Extract actual salt from already encrypted password
$salt = $1;
}
# Add the password
local $ctx = eval "new $use_md5";
$ctx->add($passwd);
if ($salt) {
$ctx->add($magic);
$ctx->add($salt);
}
# Add some more stuff from the hash of the password and salt
local $ctx1 = eval "new $use_md5";
$ctx1->add($passwd);
if ($salt) {
$ctx1->add($salt);
}
$ctx1->add($passwd);
local $final = $ctx1->digest();
for($pl=length($passwd); $pl>0; $pl-=16) {
@@ -4624,6 +4654,18 @@ for($i=length($passwd); $i; $i >>= 1) {
}
$final = $ctx->digest();
if ($salt) {
# This loop exists only to waste time
for($i=0; $i<1000; $i++) {
$ctx1 = eval "new $use_md5";
$ctx1->add($i & 1 ? $passwd : $final);
$ctx1->add($salt) if ($i % 3);
$ctx1->add($passwd) if ($i % 7);
$ctx1->add($i & 1 ? $final : $passwd);
$final = $ctx1->digest();
}
}
# Convert the 16-byte final string into a readable form
local $rv;
local @final = map { ord($_) } split(//, $final);
@@ -4640,7 +4682,13 @@ $rv .= &to64($l, 4);
$l = $final[11];
$rv .= &to64($l, 2);
return $rv;
# Add salt if needed
if ($salt) {
return $magic.$salt.'$'.$rv;
}
else {
return $rv;
}
}
sub to64

View File

@@ -5,6 +5,10 @@
# are not installed, or undef if they are
sub check_md5
{
# On some systems, the crypt function just works!
return undef if (&unix_crypt_supports_md5());
# Try Perl modules
eval "use MD5";
if (!$@) {
eval "use Digest::MD5";
@@ -27,6 +31,11 @@ if ($salt =~ /^\$1\$(.{8})/) {
$salt = $1;
}
# Use built-in crypt support for MD5, if we can
if (&unix_crypt_supports_md5()) {
return &unix_crypt($passwd, $magic.$salt.'$xxxxxxxxxxxxxxxxxxxxxx');
}
# Add the password, magic and salt
local $cls = "MD5";
eval "use MD5";
@@ -95,6 +104,14 @@ $rv .= &to64($l, 2);
return $rv;
}
# unix_crypt_supports_md5()
# Returns 1 if the built-in crypt() function can already do MD5
sub unix_crypt_supports_md5
{
return &unix_crypt('test', '$1$A9wB3O18$zaZgqrEmb9VNltWTL454R/') eq
'$1$A9wB3O18$zaZgqrEmb9VNltWTL454R/';
}
@itoa64 = split(//, "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
sub to64
{

View File

@@ -111,11 +111,10 @@ else {
$gconfig{'loginbanner'} = $in{'banner'};
}
if ($in{'md5pass'}) {
# MD5 enabled .. but is it supported by crypt?
if (&unix_crypt('test', '$1$A9wB3O18$zaZgqrEmb9VNltWTL454R/') ne
'$1$A9wB3O18$zaZgqrEmb9VNltWTL454R/') {
&error($text{'session_emd5'});
}
# MD5 enabled .. but is it supported by this system?
&foreign_require("acl", "acl-lib.pl");
$need = &acl::check_md5();
$need && &error(&text('session_emd5mod', "<tt>$need</tt>"));
}
$gconfig{'md5pass'} = $in{'md5pass'};
&write_file("$config_directory/config", \%gconfig);

View File

@@ -543,7 +543,7 @@ session_pmode1=Always allow users with expired passwords
session_pmode2=Prompt users with expired passwords to enter a new one
session_md5off=Use standard Unix <tt>crypt</tt> encryption for Webmin passwords
session_md5on=Use MD5 encryption for Webmin passwords (allows long passwords)
session_emd5=MD5 encryption cannot be used, as Perl does not have built-in <tt>crypt</tt> MD5 support on your system
session_emd5mod=MD5 encryption cannot be used, as Perl $1 module is not installed.
session_blocklock=Also lock users with failed logins
assignment_title=Reassign Modules