[pve-devel] r4864 - pve-access-control/trunk

svn-commits at proxmox.com svn-commits at proxmox.com
Fri Jul 2 09:39:26 CEST 2010


Author: dietmar
Date: 2010-07-02 07:39:26 +0000 (Fri, 02 Jul 2010)
New Revision: 4864

Modified:
   pve-access-control/trunk/AccessControl.pm
   pve-access-control/trunk/ChangeLog
Log:
2010-07-02  Proxmox Support Team  <support at proxmox.com>

	* AccessControl.pm (lock_user_config): renamed from lock_config,
	because we will have more then one config file (auth.conf, shadow
	password, ...)
	(modify_user): check for exceptions after lock_user_config()
	(delete_user): check for exceptions after lock_user_config(),
	raise invalid characters exception
	(delete_group): check for exceptions after lock_user_config(),
	raise invalid characters exception
	(modify_role): check for exceptions after lock_user_config()
	(delete_role): check for exceptions after lock_user_config(),
	raise invalid characters exception

2010-07-01  Proxmox Support Team  <support at proxmox.com>

	* AccessControl.pm: implemented file locking functionality for all
	processes that make modifications to configuration file (Seth) -
	code for lock_file() was copied from QemuServer.pm.



Modified: pve-access-control/trunk/AccessControl.pm
===================================================================
--- pve-access-control/trunk/AccessControl.pm	2010-07-01 12:57:26 UTC (rev 4863)
+++ pve-access-control/trunk/AccessControl.pm	2010-07-02 07:39:26 UTC (rev 4864)
@@ -8,12 +8,16 @@
 use Crypt::OpenSSL::Random;
 use Crypt::OpenSSL::RSA;
 use MIME::Base64;
+use Fcntl ':flock';
 use Authen::PAM qw(:constants);
 use Data::Dumper; # fixme: remove
 
 # $authdir must be writable by root only!
 my $authdir = "/etc/pve/auth";
-my $userconfigfn = "/etc/pve/user.cfg";
+my $confdir = "/etc/pve";
+my $userconfigfile = "user.cfg";
+my $userconfigpath = "$confdir/$userconfigfile";
+my $userconfiglock = "$confdir/.lock-$userconfigfile";
 
 my $ticket_lifetime = 3600*2; # 2 hours
 
@@ -21,6 +25,71 @@
     return $authdir;
 }
 
+sub lock_user_config {
+    my ($code, @param) = @_;
+
+    my $parent = ( caller(1) )[3];
+
+    lock_file ($userconfiglock, $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.
+
+my $lock_handles =  {};
+
+sub lock_file {
+    my ($filename, $text, $code, @param) = @_;
+
+    my $timeout = 10;
+
+    my $res;
+
+    eval {
+
+        local $SIG{ALRM} = sub { die "got timeout\n"; };
+
+        alarm ($timeout);
+
+        if (!$lock_handles->{$$}->{$filename}) {
+            $lock_handles->{$$}->{$filename} = new IO::File (">>$filename") ||
+                die "can't open lock for $text '$filename' - $!\n";
+        }
+
+        if (!flock ($lock_handles->{$$}->{$filename}, LOCK_EX|LOCK_NB)) {
+            print STDERR "trying to aquire lock...";
+            if (!flock ($lock_handles->{$$}->{$filename}, LOCK_EX)) {
+                print STDERR " failed\n";
+                die "can't aquire lock for $text '$filename' - $!\n";
+            }
+            print STDERR " OK\n";
+        }
+        alarm (0);
+
+        $res = &$code (@param);
+    };
+
+    my $err = $@;
+
+    alarm (0);
+
+    if ($lock_handles->{$$}->{$filename}) {
+        my $fh = $lock_handles->{$$}->{$filename};
+        $lock_handles->{$$}->{$filename} = undef;
+        close ($fh);
+    }
+
+    if ($err) {
+        $@ = $err;
+        return undef;
+    }
+
+    $@ = undef;
+
+    return $res;
+}
+
+
 sub file_set_contents {
     my ($filename, $data, $perm)  = @_;
 
@@ -435,81 +504,94 @@
 sub modify_user {
 
     my ($username, $opts) = @_;
+
+    lock_user_config (sub {
     
-    my $domain;
+	my $domain;
 
-    ($username, undef, $domain) = verify_username ($username);
+	($username, undef, $domain) = verify_username ($username);
 
-    die "modify failed: username '$username' contains invalid characters\n" if !$username;
-    
-    my $usercfg = load_user_config();
+	die "username '$username' contains invalid characters\n" if !$username;
+	
+	my $usercfg = load_user_config();
 
-    if ($opts->{create}) {
+	if ($opts->{create}) {
 
-	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};
+	    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};
 
-	store_shadow_passwd() if !$domain && $opts->{passwd};
+	    store_shadow_passwd() if !$domain && $opts->{passwd};
 
-	enable_user ($username, $usercfg);
+	    enable_user ($username, $usercfg);
 
-	if ($opts->{groups}) {
-	    foreach my $group (split_list ($opts->{groups})) {
-		if ($usercfg->{groups}->{$group}) {
-		    add_user_group ($username,$usercfg,$group);
-		} else {
-		    warn "ignore group '$group' - no such group\n";
-		    next;
+	    if ($opts->{groups}) {
+		foreach my $group (split_list ($opts->{groups})) {
+		    if ($usercfg->{groups}->{$group}) {
+			add_user_group ($username,$usercfg,$group);
+		    } else {
+			warn "ignore group '$group' - no such group\n";
+			next;
+		    }
 		}
 	    }
-	}
 
-    } else {
+	} else {
 
-	die "user '$username' does not exist\n" if !$usercfg->{users}->{$username};
-	store_shadow_passwd() if !$domain && $opts->{passwd};
-	enable_user ($username,$usercfg) if $opts->{unlock} &&
-	    !$usercfg->{users}->{$username}->{enabled};
-	disable_user ($username,$usercfg) if $opts->{lock} &&
-	    $usercfg->{users}->{$username}->{enabled};
-	delete_user_group ($username,$usercfg) if (!$opts->{append} && $opts->{groups});
-	if ($opts->{groups}) {
-	    foreach my $group (split_list ($opts->{groups})) {
-		if ($usercfg->{groups}->{$group}) {
-		    add_user_group ($username,$usercfg,$group);
-		} else {
-		    warn "ignore group '$group' - no such group\n";
-		    next;
+	    die "user '$username' does not exist\n" if !$usercfg->{users}->{$username};
+	    store_shadow_passwd() if !$domain && $opts->{passwd};
+	    enable_user ($username,$usercfg) if $opts->{unlock} &&
+		!$usercfg->{users}->{$username}->{enabled};
+	    disable_user ($username,$usercfg) if $opts->{lock} &&
+		$usercfg->{users}->{$username}->{enabled};
+	    delete_user_group ($username,$usercfg) if (!$opts->{append} && $opts->{groups});
+	    if ($opts->{groups}) {
+		foreach my $group (split_list ($opts->{groups})) {
+		    if ($usercfg->{groups}->{$group}) {
+			add_user_group ($username,$usercfg,$group);
+		    } else {
+			warn "ignore group '$group' - no such group\n";
+			next;
+		    }
 		}
 	    }
+
+	    change_password ($username,$usercfg) if $opts->{password};
+	    name_user ($username,$usercfg) if $opts->{name};
+	    comment_user ($username,$usercfg) if $opts->{comment};
 	}
 
-	change_password ($username,$usercfg) if $opts->{password};
-	name_user ($username,$usercfg) if $opts->{name};
-	comment_user ($username,$usercfg) if $opts->{comment};
-    }
+	save_user_config ($usercfg);
+    });
 
-    save_user_config ($usercfg);
+    my $err = $@;
+
+    die "modify user failed: $err" if $err;
 }
 
 sub delete_user {
     
     my ($username) = @_;
+    
+    lock_user_config (sub {
 
-    ($username, undef, undef) = verify_username ($username);
+	($username, undef, undef) = verify_username ($username);
+	die "username '$username' contains invalid characters\n" if !$username;
 
-    my $usercfg = load_user_config();
+	my $usercfg = load_user_config();
 
-    delete ($usercfg->{users}->{$username})
-        if $usercfg->{users}->{$username};
+	delete ($usercfg->{users}->{$username})
+	    if $usercfg->{users}->{$username};
 
-    delete_user_group ($username,$usercfg);
-    delete_user_acl ($username,$usercfg);
+	delete_user_group ($username,$usercfg);
+	delete_user_acl ($username,$usercfg);
+	save_user_config ($usercfg);
+    });
 
-    save_user_config ($usercfg);
+    my $err = $@;
 
+    die "delete user failed: $err" if $err;
 }   
 
 sub store_shadow_passwd {
@@ -589,31 +671,47 @@
 sub add_group {
 
     my ($group) = @_;
+
+    lock_user_config (sub {
     
-    my $usercfg = load_user_config();
+	my $usercfg = load_user_config();
 
-    die "add group failed: '$group' - invalid characters in group name\n" if (!verify_groupname ($group));
+	die "'$group' - invalid characters in group name\n" if (!verify_groupname ($group));
+	
+	die "group '$group' already exists\n" 
+	    if $usercfg->{groups}->{$group};
+
+	$usercfg->{groups}->{$group} = {};
+
+	save_user_config ($usercfg);
     
-    die "group '$group' already exists\n" 
-	if $usercfg->{groups}->{$group};
+    });
 
-    $usercfg->{groups}->{$group} = {};
+    my $err = $@;
 
-    save_user_config ($usercfg);
-   
+    die "add group failed: $err" if $err;
 }
 
 sub delete_group {
     
     my ($group) = @_;
 
-    my $usercfg = load_user_config();
+    lock_user_config (sub {
 
-    delete ($usercfg->{groups}->{$group})
-        if $usercfg->{groups}->{$group};
+	die "'$group' - invalid characters in group name\n" if (!verify_groupname ($group));
 
-    save_user_config ($usercfg);
+	my $usercfg = load_user_config();
 
+	delete ($usercfg->{groups}->{$group})
+	    if $usercfg->{groups}->{$group};
+
+	save_user_config ($usercfg);
+
+    });
+
+    my $err = $@;
+
+    die "delete group failed: $err" if $err;
 }
 
 my $valid_privs = {
@@ -656,44 +754,60 @@
 };
 
 sub modify_role {
-
     my ($role, $opts) = @_;
-    my $usercfg = load_user_config();
 
-    die "modify role '$role' failed - invalid characters in role name\n" if (!verify_rolename ($role));
+    lock_user_config (sub {
+	
+	my $usercfg = load_user_config();
 
-    if ($opts->{create}) {
-	die "add role '$role' failed - role already exists\n" if $usercfg->{roles}->{$role};	
-	$usercfg->{roles}->{$role} = {};
-    }
+	die "invalid characters in role name\n" if !verify_rolename ($role);
 
-    die "modify role '$role' failed - role does not exist\n" if !$usercfg->{roles}->{$role};
+	if ($opts->{create}) {
+	    die "can't add role '$role' - role already exists\n" if $usercfg->{roles}->{$role};	
+	    $usercfg->{roles}->{$role} = {};
+	}
 
-    $usercfg->{roles}->{$role} = {} if !$opts->{append};
-    if ($opts->{privs}) {
-	foreach my $priv (split_list ($opts->{privs})) {
-	    if ($usercfg->{roles}->{$role} && defined ($valid_privs->{$priv})) {
-		$usercfg->{roles}->{$role}->{$priv} = 1;
-	    } else {
-		warn "modify role - ignore invalid priviledge '$priv'\n";
-	    } 
-	}	
-    }
+	die "role '$role' does not exist\n" if !$usercfg->{roles}->{$role};
 
-    save_user_config ($usercfg);
+	$usercfg->{roles}->{$role} = {} if !$opts->{append};
+	if ($opts->{privs}) {
+	    foreach my $priv (split_list ($opts->{privs})) {
+		if ($usercfg->{roles}->{$role} && defined ($valid_privs->{$priv})) {
+		    $usercfg->{roles}->{$role}->{$priv} = 1;
+		} else {
+		    warn "modify role - ignore invalid priviledge '$priv'\n";
+		} 
+	    }	
+	}
+
+	save_user_config ($usercfg);
+    });
+
+    my $err = $@;
+
+    die "modify role failed: $err" if $err;
 }
 
 sub delete_role {
     
     my ($role) = @_;
 
-    my $usercfg = load_user_config();
+    lock_user_config (sub {
 
-    delete ($usercfg->{roles}->{$role})
-        if $usercfg->{roles}->{$role};
+	my $usercfg = load_user_config();
 
-    save_user_config ($usercfg);
+	die "invalid characters in role name\n" if !verify_rolename ($role);
 
+	delete ($usercfg->{roles}->{$role})
+	    if $usercfg->{roles}->{$role};
+
+	save_user_config ($usercfg);
+
+    });
+
+    my $err = $@;
+
+    die "delete role '$role' failed: $err" if $err;
 }
 
 sub split_list {
@@ -916,8 +1030,8 @@
 
     my $cfg = {};
 
-    my $fh = IO::File->new ($userconfigfn, 'r');
-    $cfg = parse_config ($userconfigfn, $fh);
+    my $fh = IO::File->new ($userconfigpath, 'r');
+    $cfg = parse_config ($userconfigpath, $fh);
     $fh->close() if $fh;
 
     $user_config_cache = $cfg;
@@ -1013,7 +1127,7 @@
 	}
     }
     
-    file_set_contents($userconfigfn, $data, 0644);
+    file_set_contents($userconfigpath, $data, 0644);
 }
 
 sub roles {

Modified: pve-access-control/trunk/ChangeLog
===================================================================
--- pve-access-control/trunk/ChangeLog	2010-07-01 12:57:26 UTC (rev 4863)
+++ pve-access-control/trunk/ChangeLog	2010-07-02 07:39:26 UTC (rev 4864)
@@ -1,3 +1,23 @@
+2010-07-02  Proxmox Support Team  <support at proxmox.com>
+
+	* AccessControl.pm (lock_user_config): renamed from lock_config,
+	because we will have more then one config file (auth.conf, shadow
+	password, ...)
+	(modify_user): check for exceptions after lock_user_config()
+	(delete_user): check for exceptions after lock_user_config(),
+	raise invalid characters exception
+	(delete_group): check for exceptions after lock_user_config(),
+	raise invalid characters exception
+	(modify_role): check for exceptions after lock_user_config()
+	(delete_role): check for exceptions after lock_user_config(),
+	raise invalid characters exception
+
+2010-07-01  Proxmox Support Team  <support at proxmox.com>
+
+	* AccessControl.pm: implemented file locking functionality for all
+	processes that make modifications to configuration file (Seth) -
+	code for lock_file() was copied from QemuServer.pm.
+
 2010-06-29  Proxmox Support Team  <support at proxmox.com>
 
 	* pveum: new roleadd/rolemod/roledel (Seth)



More information about the pve-devel mailing list