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.
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());
}
# 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);
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
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();
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
{
--- /dev/null
+../useradmin/md5-lib.pl
\ No newline at end of file
}
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}) {
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;
}
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'}];
# 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;
}
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) = @_;
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) {
}
$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);
$l = $final[11];
$rv .= &to64($l, 2);
-return $rv;
+# Add salt if needed
+if ($salt) {
+ return $magic.$salt.'$'.$rv;
+ }
+else {
+ return $rv;
+ }
}
sub to64
# 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";
$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";
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
{
$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);
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