[pve-devel] r4878 - in pve-access-control/trunk: . test
svn-commits at proxmox.com
svn-commits at proxmox.com
Wed Jul 7 09:51:09 CEST 2010
Author: dietmar
Date: 2010-07-07 07:51:09 +0000 (Wed, 07 Jul 2010)
New Revision: 4878
Added:
pve-access-control/trunk/test/auth-test.pl
Modified:
pve-access-control/trunk/AccessControl.pm
pve-access-control/trunk/ChangeLog
pve-access-control/trunk/pveum
Log:
* AccessControl.pm: implemented shadowauthentication (add/modify/delete/verify)
with file locking (Seth)
(encrypt_pw): use SHA256 to crypt passwords
(save_shadow_config): change mode to 0600, store to /etc/pve/auth/shadow.cfg
* test/auth-test.pl: program for testing authentication methods (Seth)
* pveum (read_password): added confirm password
Modified: pve-access-control/trunk/AccessControl.pm
===================================================================
--- pve-access-control/trunk/AccessControl.pm 2010-07-07 06:08:37 UTC (rev 4877)
+++ pve-access-control/trunk/AccessControl.pm 2010-07-07 07:51:09 UTC (rev 4878)
@@ -1,6 +1,7 @@
package PVE::AccessControl;
use strict;
+use Encode;
use POSIX;
use IO::Select;
use IO::File;
@@ -9,6 +10,7 @@
use Crypt::OpenSSL::RSA;
use MIME::Base64;
use Fcntl ':flock';
+use Digest::SHA;
use Authen::PAM qw(:constants);
use Data::Dumper; # fixme: remove
@@ -18,6 +20,9 @@
my $userconfigfile = "user.cfg";
my $userconfigpath = "$confdir/$userconfigfile";
my $userconfiglock = "$confdir/.lock-$userconfigfile";
+my $shadowconfigfile = "shadow.cfg";
+my $shadowconfigpath = "$authdir/$shadowconfigfile";
+my $shadowconfiglock = "$authdir/.lock-$shadowconfigfile";
my $ticket_lifetime = 3600*2; # 2 hours
@@ -33,6 +38,14 @@
lock_file($userconfiglock, $parent, $code, @param);
}
+sub lock_shadow_config {
+ my ($code, @param) = @_;
+
+ my $parent = ( caller(1) )[3];
+
+ lock_file($shadowconfiglock, $parent, $code, @param);
+}
+
# flock: we use one file handle per process, so lock file
# can be called multiple times and succeeds for the same process.
@@ -408,9 +421,17 @@
sub authenticate_user_pve {
my ($username, $password) = @_;
- # fixme: add delay if auth failed
-
- die "authenticate_user_pve: not implemented";
+ my $shadow_cfg = load_shadow_config();
+
+ if ($shadow_cfg->{users}->{$username}) {
+ my $encpw = crypt($password, $shadow_cfg->{users}->{$username}->{shadow});
+ if ($encpw ne $shadow_cfg->{users}->{$username}->{shadow}) {
+ sleep(4);
+ die "SHADOW auth failed\n";
+ }
+ } else {
+ die "SHADOW password not set\n";
+ }
}
sub authenticate_user_pam {
@@ -521,9 +542,10 @@
die "user '$username' already exists\n"
if $usercfg->{users}->{$username};
- warn "ignore password - can't set password on auth domain '$domain'\n" if $domain && $opts->{passwd};
+ warn "ignore password - can't set password on auth domain '$domain'\n" if $domain && $opts->{password};
+ my $pass_plain = $opts->{password};
- store_shadow_passwd() if !$domain && $opts->{passwd};
+ store_shadow_password($username,$opts->{password}) if !$domain && $opts->{password};
enable_user($username, $usercfg);
@@ -539,9 +561,9 @@
}
} else {
-
+ my $pw = $opts->{password};
die "user '$username' does not exist\n" if !$usercfg->{users}->{$username};
- store_shadow_passwd() if !$domain && $opts->{passwd};
+ store_shadow_password($username,$opts->{password}) if !$domain && $opts->{password};
enable_user($username, $usercfg) if $opts->{unlock} &&
!$usercfg->{users}->{$username}->{enabled};
disable_user($username, $usercfg) if $opts->{lock} &&
@@ -573,16 +595,17 @@
sub delete_user {
my ($username) = @_;
+ my $domain;
lock_user_config(sub {
- ($username, undef, undef) = verify_username($username);
+ ($username, undef, $domain) = verify_username($username);
my $usercfg = load_user_config();
delete ($usercfg->{users}->{$username})
if $usercfg->{users}->{$username};
-
+ delete_shadow_password($username) if !$domain;
delete_user_group($username, $usercfg);
delete_user_acl($username, $usercfg);
save_user_config($usercfg);
@@ -591,22 +614,43 @@
my $err = $@;
die "delete user failed: $err" if $err;
-}
+}
-sub store_shadow_passwd {
+sub delete_shadow_password {
- warn "store shadow password not implemented yet\n";
- # fixme: store password somewhere into a protected file
+ my ($username) = @_;
+ lock_shadow_config(sub {
+ my $shadow_cfg = load_shadow_config();
+ delete ($shadow_cfg->{users}->{$username})
+ if $shadow_cfg->{users}->{$username};
+ save_shadow_config($shadow_cfg);
+ });
+ die $@ if $@;
+}
+sub store_shadow_password {
+
+ my ($username,$password) = @_;
+ lock_shadow_config(sub {
+ my $shadow_cfg = load_shadow_config();
+ $shadow_cfg->{users}->{$username}->{shadow} = encrypt_pw($password);
+ save_shadow_config($shadow_cfg);
+ });
+ die $@ if $@;
}
+sub encrypt_pw {
+ my ($pw) = @_;
+
+ my $time = substr (Digest::SHA::sha1_base64 (time), 0, 8);
+ return crypt (encode("utf8", $pw), "\$5\$$time\$");
+}
+
sub add_user_group {
my ($username, $usercfg, $group) = @_;
$usercfg->{users}->{$username}->{groups}->{$group} = 1;
$usercfg->{groups}->{$group}->{$username} = 1;
-
-
}
sub delete_user_group {
@@ -618,7 +662,6 @@
delete ($usercfg->{groups}->{$group}->{$username})
if $usercfg->{groups}->{$group}->{$username};
}
-
}
sub delete_user_acl {
@@ -1018,11 +1061,37 @@
userconfig_force_defaults($cfg);
-# print "GOT:$filename:$fh: " . Dumper($cfg) . "\n";
-
return $cfg;
}
+sub parse_shadow {
+ my ($filename, $fh) = @_;
+
+ my $shadow = {};
+
+ die "MODE: '$/'" if !$/;
+
+ if ($fh) {
+ while (defined (my $line = <$fh>)) {
+ chomp $line;
+
+ next if $line =~ m/^\s*$/; # skip empty lines
+
+ my @data;
+
+ foreach my $d (split (/:/, $line)) {
+ $d =~ s/^\s+//;
+ $d =~ s/\s+$//;
+ push @data, $d
+ }
+ my ($username,$crypt_pass) = @data;
+ $shadow->{users}->{$username}->{shadow} = $crypt_pass;
+ }
+ }
+
+ return $shadow;
+}
+
my $user_config_cache;
sub load_user_config {
my ($reload) = @_;
@@ -1040,6 +1109,38 @@
return $user_config_cache;
}
+my $shadow_config_cache;
+sub load_shadow_config {
+ my ($reload) = @_;
+
+ return $shadow_config_cache if !$reload && defined($shadow_config_cache);
+
+ my $cfg = {};
+
+ my $fh = IO::File->new ($shadowconfigpath, 'r');
+ $cfg = parse_shadow($shadowconfigpath, $fh);
+ $fh->close() if $fh;
+
+ $shadow_config_cache = $cfg;
+
+ return $shadow_config_cache;
+}
+
+sub save_shadow_config {
+ my ($cfg) = @_;
+
+ $user_config_cache = undef; # force reload
+
+ my $data = '';
+
+ foreach my $user (keys %{$cfg->{users}}) {
+ my $crypt_pass = $cfg->{users}->{$user}->{shadow};
+ $data .= "$user:$crypt_pass:\n";
+ }
+
+ file_set_contents($shadowconfigpath, $data, 0600);
+}
+
sub save_user_config {
my ($cfg) = @_;
Modified: pve-access-control/trunk/ChangeLog
===================================================================
--- pve-access-control/trunk/ChangeLog 2010-07-07 06:08:37 UTC (rev 4877)
+++ pve-access-control/trunk/ChangeLog 2010-07-07 07:51:09 UTC (rev 4878)
@@ -1,3 +1,14 @@
+2010-07-07 Proxmox Support Team <support at proxmox.com>
+
+ * AccessControl.pm: implemented shadowauthentication (add/modify/delete/verify)
+ with file locking (Seth)
+ (encrypt_pw): use SHA256 to crypt passwords
+ (save_shadow_config): change mode to 0600, store to /etc/pve/auth/shadow.cfg
+
+ * test/auth-test.pl: program for testing authentication methods (Seth)
+
+ * pveum (read_password): added confirm password
+
2010-07-05 Proxmox Support Team <support at proxmox.com>
* AccessControl.pm (modify_user): remove call to change_password()
Modified: pve-access-control/trunk/pveum
===================================================================
--- pve-access-control/trunk/pveum 2010-07-07 06:08:37 UTC (rev 4877)
+++ pve-access-control/trunk/pveum 2010-07-07 07:51:09 UTC (rev 4878)
@@ -44,7 +44,9 @@
my $term = new Term::ReadLine ('pveum');
my $attribs = $term->Attribs;
$attribs->{redisplay_function} = $attribs->{shadow_redisplay};
- my $input = $term->readline('password: ');
+ my $input = $term->readline('Enter new password: ');
+ my $conf = $term->readline('Retype new password: ');
+ die "Passwords do not match." if ($input ne $conf);
return $input;
}
Added: pve-access-control/trunk/test/auth-test.pl
===================================================================
--- pve-access-control/trunk/test/auth-test.pl (rev 0)
+++ pve-access-control/trunk/test/auth-test.pl 2010-07-07 07:51:09 UTC (rev 4878)
@@ -0,0 +1,23 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Term::ReadLine;
+use PVE::AccessControl;
+
+my $username = shift;
+die "Username missing" if !$username;
+sub read_password {
+
+ my $term = new Term::ReadLine ('pveum');
+ my $attribs = $term->Attribs;
+ $attribs->{redisplay_function} = $attribs->{shadow_redisplay};
+ my $input = $term->readline('password: ');
+ return $input;
+}
+
+my $password = read_password();
+PVE::AccessControl::authenticate_user($username,$password);
+
+print "Authentication Successful!!\n";
+
+exit (0);
More information about the pve-devel
mailing list