Skip to content

Commit

Permalink
Item13315: Address usability of the admin password
Browse files Browse the repository at this point in the history
Move the password hashing code from tools/configure into the
onSave handler of the password checker.

This allows the password to be set in plain text through both the CLI
tools/configure as well as with web UI.  It detects if someone has
pasted in an already hashed password, and will not double-encode it.
  • Loading branch information
gac410 committed Mar 25, 2015
1 parent 6290beb commit 8516b13
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 57 deletions.
18 changes: 5 additions & 13 deletions core/lib/Foswiki.spec
Expand Up @@ -740,19 +740,11 @@ $Foswiki::cfg{Htpasswd}{AutoDetect} = $TRUE;
# iterations.
$Foswiki::cfg{Htpasswd}{BCryptCost} = 8;

# **PASSWORD LABEL="Internal Admin Password" EXPERT CHECK_ON_CHANGE="{FeatureAccess}{Configure}" CHECK="also:{FeatureAccess}{Configure}"**
# If set, this password
# permits use of the "internal admin" (sudo) facility. *As it is a "shared password",
# this is no longer recommended per good security practices and is not
# set by default.* If you want to enable the internal admin login, set this field
# to a valid hashed password generated by the apache =htpasswd= command
# Example: Set the internal admin password to 'password'
# <verbatim>
# htpasswd -nb admin password
# admin:$apr1$3xBPRZAV$iqaC9QyWdzC/93os7A9np1
# </verbatim>
# Paste the everything following the ="admin:"= into this field.
# Do not include the =admin:=
# **PASSWORD LABEL="Internal Admin Password" CHECK_ON_CHANGE="{FeatureAccess}{Configure}" CHECK="also:{FeatureAccess}{Configure}" ONSAVE**
# If set, this password # permits use of the _internal admin_ login, and the sudo facility.
# *As it is a "shared password", this is no longer recommended per good security practices.*
# Clear this field to disable use of the internal admin login.
#
$Foswiki::cfg{Password} = '';

#---++ Registration
Expand Down
Expand Up @@ -41,7 +41,6 @@ You should either set the _internal admin_ password, or add users to this list w
my $passed = ''; # Set to true if current user is allowed to use configure

my $curuser = Foswiki::Func::getCanonicalUserID();
print STDERR "CURRENT $curuser\n";

unless ( $Foswiki::cfg{isBOOTSTRAPPING}
|| !$Foswiki::cfg{FeatureAccess}{Configure}
Expand Down
61 changes: 52 additions & 9 deletions core/lib/Foswiki/Configure/Checkers/Password.pm
Expand Up @@ -4,6 +4,9 @@ package Foswiki::Configure::Checkers::Password;
use strict;
use warnings;
use Assert;

use Crypt::PasswdMD5;

use Foswiki::Configure::Checker ();
our @ISA = ('Foswiki::Configure::Checker');

Expand All @@ -30,19 +33,59 @@ sub check_current_value {
&& !$Foswiki::cfg{Password}
&& !$Foswiki::cfg{FeatureAccess}{Configure} );

if (
$Foswiki::cfg{Password}
&& ( $Foswiki::cfg{Password} !~ m/^\$apr1\$/
|| length( $Foswiki::cfg{Password} ) ne 37 )
)
}

=begin TML
---++ =onSave()= handler
When the password is set using the Web or CLI configure interface
hash it with salt following the Apache APR1 password standard.
=cut

sub onSave {
my $this = shift;
my $reporter = shift;

#my ( $key, $value ) = shift;

return unless ( $_[1] ); # If no password, let it through.

# Allow an existing encoded password to go through without re-hashing it.
unless ( $_[1]
&& length( $_[1] ) eq 37
&& $_[1] =~ m/^\$apr1\$/ )
{
$reporter->ERROR(
"This _internal admin_ password does not appear to be a valid password. You will be unable to access the _internal admin_ $Foswiki::cfg{AdminUserWikiName} ($Foswiki::cfg{AdminUserLogin})
using the current configuration. If you want to be able to use the _internal admin_ user, the password should be saved as an \"\$apr1:...\" encoded password. Show the help for more details."
);
my $pw = _setPassword( 'admin', $_[1] );
$_[1] = $pw;
}
}

sub _setPassword {
my ( $login, $passwd ) = @_;
my $salt = '$apr1$';
my @saltchars = ( '.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z' );
foreach my $i ( 0 .. 7 ) {

# generate a salt not only from rand() but also mixing
# in the users login name: unecessary
$salt .= $saltchars[
(
int( rand( $#saltchars + 1 ) ) +
$i +
ord( substr( $login, $i % length($login), 1 ) ) )
% ( $#saltchars + 1 )
];
}

# print STDERR "encoded $passwd as "
# . Crypt::PasswdMD5::apache_md5_crypt( $passwd, substr( $salt, 0, 14 ) )
# . "\n";

return Crypt::PasswdMD5::apache_md5_crypt( $passwd,
substr( $salt, 0, 14 ) );
}
1;
__END__
Foswiki - The Free and Open Source Wiki, http://foswiki.org/
Expand Down
34 changes: 0 additions & 34 deletions core/tools/configure
Expand Up @@ -130,11 +130,6 @@ if ( $action =~ /^get/ && scalar( keys %{ $params->{set} } ) ) {
exit 1;
}

if ( $params->{set}{'{Password}'} ) {
$params->{set}{'{Password}'} =
_setPassword( 'admin', $params->{set}{'{Password}'} );
}

# ---++ Prompt for config values
# * $root - Configuration root
# * $keys - ={Configuration}{Key}{Path}= to a single variable
Expand Down Expand Up @@ -173,9 +168,6 @@ sub _prompt {
}

$reply = '' unless ( defined $reply );
if ( $keys eq '{Password}' && $reply ) {
$reply = _setPassword( 'admin', $reply );
}
return if ( $opt && $reply eq 'none' );
eval "\$Foswiki::cfg$keys='$reply'";
if ($@) {
Expand All @@ -184,32 +176,6 @@ sub _prompt {
}
}

sub _setPassword {
my ( $login, $passwd ) = @_;
my $salt = '$apr1$';
my @saltchars = ( '.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z' );
require Crypt::PasswdMD5;
foreach my $i ( 0 .. 7 ) {

# generate a salt not only from rand() but also mixing
# in the users login name: unecessary
$salt .= $saltchars[
(
int( rand( $#saltchars + 1 ) ) +
$i +
ord( substr( $login, $i % length($login), 1 ) ) )
% ( $#saltchars + 1 )
];
}

#print STDERR "encoded $passwd as "
# . Crypt::PasswdMD5::apache_md5_crypt( $passwd, substr( $salt, 0, 14 ) )
# . "\n";

return Crypt::PasswdMD5::apache_md5_crypt( $passwd,
substr( $salt, 0, 14 ) );
}

sub _reply {
my $response = shift;

Expand Down

0 comments on commit 8516b13

Please sign in to comment.