mirror of
https://github.com/webmin/webmin.git
synced 2025-07-20 16:48:46 +00:00
Start of work on password change API for use by roundcube / etc
This commit is contained in:
@ -29,10 +29,11 @@ $user || &errordie("User $ARGV[0] does not exist");
|
||||
$| = 1;
|
||||
if ($askold) {
|
||||
# Ask for the old password
|
||||
&foreign_require("useradmin");
|
||||
print "(current) UNIX password: ";
|
||||
$old = <STDIN>;
|
||||
$old =~ s/\r|\n//g;
|
||||
&unix_crypt($old, $user->{'pass'}) eq $user->{'pass'} ||
|
||||
&useradmin::validate_password($old, $user->{'pass'}) ||
|
||||
&errordie("Old password is incorrect");
|
||||
}
|
||||
|
||||
|
51
passwd/change_passwd.cgi
Executable file
51
passwd/change_passwd.cgi
Executable file
@ -0,0 +1,51 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Change a user's password knowing the old one. For user only via anonymous
|
||||
# API calls.
|
||||
|
||||
require './passwd-lib.pl';
|
||||
&ReadParse();
|
||||
print "Content-type: text/plain\n\n";
|
||||
|
||||
# Validate inputs
|
||||
my $err = &apply_rate_limit($ENV{'REMOTE_ADDR'});
|
||||
&error_exit($err) if ($err);
|
||||
$in{'user'} || &error_exit("Missing user parameter");
|
||||
$in{'old'} || &error_exit("Missing old parameter");
|
||||
$in{'new'} || &error_exit("Missing new parameter");
|
||||
$ENV{'ANONYMOUS_USER'} || &error_exit("Can only be called in anonymous mode");
|
||||
$ENV{'REQUEST_METHOD'} eq 'POST' ||
|
||||
&error_exit("Passwords can only be submitted via POST");
|
||||
&foreign_installed("useradmin") ||
|
||||
&error_exit("Users and Groups module is not supported on this OS");
|
||||
|
||||
# Validate user and pass
|
||||
my $err = &apply_rate_limit($in{'user'});
|
||||
&error_exit($err) if ($err);
|
||||
&foreign_require("useradmin");
|
||||
my $user = &find_user($in{'user'});
|
||||
$user || &error_exit("User does not exist");
|
||||
&useradmin::validate_password($in{'old'}, $user->{'pass'}) ||
|
||||
&error_exit("Incorrect password");
|
||||
my $err = &useradmin::check_password_restrictions(
|
||||
$in{'pass'}, $in{'user'}, $user);
|
||||
&error_exit("Invalid password : $err") if ($err);
|
||||
|
||||
# Do the change
|
||||
&clear_rate_limit($ENV{'REMOTE_ADDR'});
|
||||
&clear_rate_limit($in{'user'});
|
||||
eval {
|
||||
local $main::error_must_die = 1;
|
||||
&change_password($user, $in{'pass'}, 1);
|
||||
};
|
||||
if ($@) {
|
||||
&error_exit($@);
|
||||
}
|
||||
else {
|
||||
print "OK: Password changed for $in{'user'}\n";
|
||||
}
|
||||
|
||||
sub error_exit
|
||||
{
|
||||
print "FAILED: ",join("", @_),"\n";
|
||||
exit(0);
|
||||
}
|
@ -14,6 +14,9 @@ BEGIN { push(@INC, ".."); };
|
||||
use WebminCore;
|
||||
&init_config();
|
||||
%access = &get_module_acl();
|
||||
$rate_limit_file = "$module_var_directory/rate-limit";
|
||||
$rate_limit_timeout = 10*60; # 10 minutes
|
||||
$rate_limit_max = 10;
|
||||
|
||||
=head2 can_edit_passwd(&user)
|
||||
|
||||
@ -80,7 +83,6 @@ sub find_user
|
||||
local $mod;
|
||||
foreach $mod ([ "useradmin", "user-lib.pl" ],
|
||||
[ "ldap-useradmin", "ldap-useradmin-lib.pl" ],
|
||||
# [ "nis", "nis-lib.pl" ],
|
||||
) {
|
||||
next if (!&foreign_installed($mod->[0], 1));
|
||||
&foreign_require($mod->[0], $mod->[1]);
|
||||
@ -146,5 +148,49 @@ if ($others) {
|
||||
}
|
||||
}
|
||||
|
||||
# apply_rate_limit(key)
|
||||
# Delays for some amount of time based on the key, to prevent brute force attacks
|
||||
sub apply_rate_limit
|
||||
{
|
||||
my ($key) = @_;
|
||||
my $now = time();
|
||||
my %rate;
|
||||
&lock_file($rate_limit_file);
|
||||
&read_file($rate_limit_file, \%rate);
|
||||
$rate{$key."_last"} ||= $now;
|
||||
if ($now - $rate{$key."_last"} > $rate_limit_timeout) {
|
||||
# Time since blocking for this key started as expired
|
||||
delete($rate{$key});
|
||||
delete($rate{$key."_last"});
|
||||
}
|
||||
my $rv;
|
||||
if ($rate{$key} > $rate_limit_max) {
|
||||
$rv = "Too many failures for $key";
|
||||
}
|
||||
else {
|
||||
sleep($rate{$key} ** 2);
|
||||
$rate{$key}++;
|
||||
}
|
||||
&write_file($rate_limit_file, \%rate);
|
||||
&unlock_file($rate_limit_file);
|
||||
return $rv;
|
||||
}
|
||||
|
||||
# clear_rate_limit(key)
|
||||
# After a successful operation, clear any rate limits for the given key
|
||||
sub clear_rate_limit
|
||||
{
|
||||
my ($key) = @_;
|
||||
my %rate;
|
||||
&lock_file($rate_limit_file);
|
||||
&read_file($rate_limit_file, \%rate);
|
||||
delete($rate{$key});
|
||||
delete($rate{$key."_last"});
|
||||
&write_file($rate_limit_file, \%rate);
|
||||
&unlock_file($rate_limit_file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
1;
|
||||
|
||||
|
Reference in New Issue
Block a user