[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