Files
bpfire/html/cgi-bin/chpasswd.cgi
Vincent Li 993dbbbd75 chpasswd.cgi: Fixes bug12755
commit a461fd70445aec9dfa34bf9c5a29a85e0ad0e2fe
Author: Adolf Belka <adolf.belka@ipfire.org>
Date:   Sat May 10 12:30:56 2025 +0200

    chpasswd.cgi: Fixes bug12755 - v3 with password verification correction

    - v3 version based on feedback from @Michael to use the status value returned from
       using the htpasswd command.
    - Also simplified the whole section to carry out the change if the status is 0, ie all
       went well, otherwise give an error but without identifying if the error is in the
       username or the password. This makes it more secure as any attacker only knows it
       failed and doesn't know if any part of the authentication was correct or not.
    - Changed the error messages in line with this so the language file changes are in the
       other part of this patch set submission.
    - Tested out on my vm test bed and worked fine. If the username was incorrect or the
       password was incorrect or both were incorrect the same error message is given. If
       both are correct then the update is carried out.

    Fixes: bug12755
    Tested-by: Adolf Belka <adolf.belka@ipfire.org>
    Signed-off-by: Adolf Belka <adolf.belka@ipfire.org>
    Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>

commit 9c0dab3d3ca807e836823253aced80a14bc1970a
Author: Michael Tremer <michael.tremer@ipfire.org>
Date:   Wed May 7 09:06:12 2025 +0000

    chpasswd.cgi: Add missing $

    Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>

commit 4c39e38f90fea60ef62e07267fd84f1b89de0297
Author: Adolf Belka <adolf.belka@ipfire.org>
Date:   Tue May 6 16:10:11 2025 +0200

    chpasswd.cgi: Make swroot refs the same as for other cgi files

    - This uses the swroot definition from general-functions.pl and makes the definition
       the same as used in the majority of other IPFire cgi files.

    Tested-by: Adolf Belka <adolf.belka@ipfire.org>
    Signed-off-by: Adolf Belka <adolf.belka@ipfire.org>
    Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>

commit 6c1549ff7a9c8e3f9f17a29a6b169fce175fea42
Author: Adolf Belka <adolf.belka@ipfire.org>
Date:   Tue May 6 16:10:09 2025 +0200

    chpasswd.cgi: Fixes bug12755 - proxy auth password problem longer than 8 chars

    - The existing version of the perl module Apache::Htpasswd was using the crypt hash for
       the password hashing, which is very insecure. The only alternative with this module
       is the md5 and sha1 hashes which are also considered weak now.
    - The module was last updated in Nov 2012 and there is no alternative module available.
    - This patch replaces that perl module with using the apache htpasswd program. This can
       be set to use the bcrypt hash which is considered secure. This is used for the
       generation of the root and admin passwords during the IPFire install.
    - Tested out on my vm testbed system and the password for a specific user name was
       changed successfully without any restriction to the length of the password.
    - Existing passwords with the existing md5 or crypt options will still work as htpasswd
       can manage different encoding hashes in the one file.

    Fixes: bug12755
    Tested-by: Adolf Belka <adolf.belka@ipfire.org>
    Signed-off-by: Adolf Belka <adolf.belka@ipfire.org>
    Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>

Signed-off-by: Vincent Li <vincent.mc.li@gmail.com>
2025-10-03 22:09:46 +00:00

281 lines
7.9 KiB
Perl

#!/usr/bin/perl
###############################################################################
# #
# IPFire.org - A linux based firewall #
# Copyright (C) 2007-2025 IPFire Team <info@ipfire.org> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
use CGI qw(param);
require '/var/ipfire/general-functions.pl';
my %cgiparams;
my %mainsettings;
my %proxysettings;
$proxysettings{'NCSA_MIN_PASS_LEN'} = 6;
### Initialize environment
&readhash("${General::swroot}/main/settings", \%mainsettings);
&readhash("${General::swroot}/proxy/advanced/settings", \%proxysettings);
$language = $mainsettings{'LANGUAGE'};
### Initialize language
if ($language =~ /^(\w+)$/) {$language = $1;}
#
# Uncomment this to force a certain language:
# $language='en';
#
require "${General::swroot}/langs/en.pl";
require "${General::swroot}/langs/${language}.pl";
my $userdb = "$General::swroot/proxy/advanced/ncsa/passwd";
&readhash("$General::swroot/ethernet/settings", \%netsettings);
my $success = 0;
&getcgihash(\%cgiparams);
if ($cgiparams{'SUBMIT'} eq $tr{'advproxy chgwebpwd change password'})
{
if ($cgiparams{'USERNAME'} eq '')
{
$errormessage = $tr{'advproxy errmsg no username'};
goto ERROR;
}
if (($cgiparams{'OLD_PASSWORD'} eq '') || ($cgiparams{'NEW_PASSWORD_1'} eq '') || ($cgiparams{'NEW_PASSWORD_2'} eq ''))
{
$errormessage = $tr{'advproxy errmsg no password'};
goto ERROR;
}
if (!($cgiparams{'NEW_PASSWORD_1'} eq $cgiparams{'NEW_PASSWORD_2'}))
{
$errormessage = $tr{'advproxy errmsg passwords different'};
goto ERROR;
}
if (length($cgiparams{'NEW_PASSWORD_1'}) < $proxysettings{'NCSA_MIN_PASS_LEN'})
{
$errormessage = $tr{'advproxy errmsg password length 1'}.$proxysettings{'NCSA_MIN_PASS_LEN'}.$tr{'advproxy errmsg password length 2'};
goto ERROR;
}
# If the htpasswd verification status is 0 then update the database
# otherwise respond with an error message.
if (&General::system("/usr/bin/htpasswd", "-bv", "$userdb", "$cgiparams{'USERNAME'}", "$cgiparams{'OLD_PASSWORD'}") != 0) {
$errormessage = $tr{'advproxy errmsg invalid user/password'};
goto ERROR;
} else {
&General::system("/usr/bin/htpasswd", "-bB", "-C 10", "$userdb", "$cgiparams{'USERNAME'}", "$cgiparams{'NEW_PASSWORD_1'}");
}
$success = 1;
undef %cgiparams;
}
ERROR:
print "Pragma: no-cache\n";
print "Cache-control: no-cache\n";
print "Connection: close\n";
print "Content-type: text/html\n\n";
print <<END
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title></title>
</head>
<body bgcolor="#FFFFFF">
<center>
<form method='post' action='$ENV{'SCRIPT_NAME'}'>
<table width="80%" cellspacing="10" cellpadding="5">
<tr>
<td bgcolor="#FFFFFF" align="center">
<table width="100%" cellspacing="10" cellpadding="10" bordercolor="#9A9A9A" border="1">
<tr>
<td nowrap bgcolor="#993333" align="center" >
<font face="trebuchet ms, helvetica, sans-serif" color="#FFFFFF" size="4">
<b>$tr{'advproxy chgwebpwd change web password'}</b>
</font>
</td>
</tr>
<tr>
<td align="center">
<table width="50%" cellspacing="7" cellpadding="7">
<tr>
<td nowrap bgcolor="#FFFFFF" align="left">
<font face="trebuchet ms, helvetica, sans-serif" color="#666666" size="2">
<b>$tr{'advproxy chgwebpwd username'}:</b>
</font>
</td>
<td ><input type="text" name="USERNAME" value="$cgiparams{'USERNAME'}" size="30"></td>
</tr>
<tr>
<td nowrap bgcolor="#FFFFFF" align="left">
<font face="trebuchet ms, helvetica, sans-serif" color="#666666" size="2">
<b>$tr{'advproxy chgwebpwd old password'}:</b>
</font>
</td>
<td><input type="password" name="OLD_PASSWORD" value="$cgiparams{'OLD_PASSWORD'}" size="30"></td>
</tr>
<tr>
<td nowrap bgcolor="#FFFFFF" align="left">
<font face="trebuchet ms, helvetica, sans-serif" color="#666666" size="2">
<b>$tr{'advproxy chgwebpwd new password'}:</b>
</font>
</td>
<td><input type="password" name="NEW_PASSWORD_1" value="$cgiparams{'NEW_PASSWORD_1'}" size="30"></td>
</tr>
<tr>
<td nowrap bgcolor="#FFFFFF" align="left">
<font face="trebuchet ms, helvetica, sans-serif" color="#666666" size="2">
<b>$tr{'advproxy chgwebpwd new password confirm'}:</b>
</font>
</td>
<td><input type="password" name="NEW_PASSWORD_2" value="$cgiparams{'NEW_PASSWORD_2'}" size="30"></td>
</tr>
</table>
<table width="100%" cellspacing="7" cellpadding="7">
<tr>
<td align="center"><br><input type='submit' name='SUBMIT' value="$tr{'advproxy chgwebpwd change password'}"></td>
</tr>
</table>
</td>
</tr>
END
;
if ($errormessage)
{
print <<END
<tr>
<td nowrap bgcolor="#FF0000" align="center">
<font face="trebuchet ms, helvetica, sans-serif" color="#FFFFFF" size="2">
<b>$tr{'advproxy chgwebpwd ERROR'}</b> $errormessage
</font>
</td>
</tr>
END
;
}
if ($success)
{
print <<END
<tr>
<td nowrap bgcolor="#00C000" align="center">
<font face="trebuchet ms, helvetica, sans-serif" color="#FFFFFF" size="2">
<b>$tr{'advproxy chgwebpwd SUCCESS'}</b> $tr{'advproxy errmsg change success'}
</font>
</td>
</tr>
END
;
}
print <<END
</td>
</tr>
</table>
</table>
</form>
</center>
</body>
</html>
END
;
# -------------------------------------------------------------------
sub readhash
{
my $filename = $_[0];
my $hash = $_[1];
my ($var, $val);
if (-e $filename)
{
open(FILE, $filename) or die "Unable to read file $filename";
while (<FILE>)
{
chop;
($var, $val) = split /=/, $_, 2;
if ($var)
{
$val =~ s/^\'//g;
$val =~ s/\'$//g;
# Untaint variables read from hash
$var =~ /([A-Za-z0-9_-]*)/; $var = $1;
$val =~ /([\w\W]*)/; $val = $1;
$hash->{$var} = $val;
}
}
close FILE;
}
}
# -------------------------------------------------------------------
sub getcgihash
{
my ($hash, $params) = @_;
my $cgi = CGI->new ();
return if ($ENV{'REQUEST_METHOD'} ne 'POST');
if (!$params->{'wantfile'}) {
$CGI::DISABLE_UPLOADS = 1;
$CGI::POST_MAX = 512 * 1024;
} else {
$CGI::POST_MAX = 10 * 1024 * 1024;
}
$cgi->referer() =~ m/^https?\:\/\/([^\/]+)/;
my $referer = $1;
$cgi->url() =~ m/^https?\:\/\/([^\/]+)/;
my $servername = $1;
return if ($referer ne $servername);
### Modified for getting multi-vars, split by |
%temp = $cgi->Vars();
foreach my $key (keys %temp) {
$hash->{$key} = $temp{$key};
$hash->{$key} =~ s/\0/|/g;
$hash->{$key} =~ s/^\s*(.*?)\s*$/$1/;
}
if (($params->{'wantfile'})&&($params->{'filevar'})) {
$hash->{$params->{'filevar'}} = $cgi->upload
($params->{'filevar'});
}
return;
}
# -------------------------------------------------------------------