all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Oguz Bektas <o.bektas@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [RFC qemu-server 4/4] api: use common helper for checking root privileges
Date: Wed,  5 Jan 2022 16:22:15 +0100	[thread overview]
Message-ID: <20220105152215.1307583-5-o.bektas@proxmox.com> (raw)
In-Reply-To: <20220105152215.1307583-1-o.bektas@proxmox.com>

to allow API users with the 'Sys.Root' permission to call these
endpoints.

Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
 PVE/API2/Qemu.pm | 74 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 49 insertions(+), 25 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 6992f6f..e846a1e 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -352,7 +352,7 @@ my $cloudinitoptions = {
 my $check_vm_create_serial_perm = sub {
     my ($rpcenv, $authuser, $vmid, $pool, $param) = @_;
 
-    return 1 if $authuser eq 'root@pam';
+    return 1 if PVE::Tools::check_for_root("/vms/$vmid");
 
     foreach my $opt (keys %{$param}) {
 	next if $opt !~ m/^serial\d+$/;
@@ -370,7 +370,7 @@ my $check_vm_create_serial_perm = sub {
 my $check_vm_create_usb_perm = sub {
     my ($rpcenv, $authuser, $vmid, $pool, $param) = @_;
 
-    return 1 if $authuser eq 'root@pam';
+    return 1 if PVE::Tools::check_for_root("/vms/$vmid");
 
     foreach my $opt (keys %{$param}) {
 	next if $opt !~ m/^usb\d+$/;
@@ -388,7 +388,7 @@ my $check_vm_create_usb_perm = sub {
 my $check_vm_modify_config_perm = sub {
     my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
 
-    return 1 if $authuser eq 'root@pam';
+    return 1 if PVE::Tools::check_for_root("/vms/$vmid");
 
     foreach my $opt (@$key_list) {
 	# some checks (e.g., disk, serial port, usb) need to be done somewhere
@@ -1105,6 +1105,8 @@ my $update_vm_api  = sub {
 
     my $background_delay = extract_param($param, 'background_delay');
 
+    my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
     if (defined(my $cipassword = $param->{cipassword})) {
 	# Same logic as in cloud-init (but with the regex fixed...)
 	$param->{cipassword} = PVE::Tools::encrypt_pw($cipassword)
@@ -1119,7 +1121,7 @@ my $update_vm_api  = sub {
 
     my $skiplock = extract_param($param, 'skiplock');
     raise_param_exc({ skiplock => "Only root may use this option." })
-	if $skiplock && $authuser ne 'root@pam';
+	if $skiplock && !$is_root;
 
     my $delete_str = extract_param($param, 'delete');
 
@@ -1340,7 +1342,7 @@ my $update_vm_api  = sub {
 		} elsif ($opt =~ m/^serial\d+$/) {
 		    if ($val eq 'socket') {
 			$rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
-		    } elsif ($authuser ne 'root@pam') {
+		    } elsif (!$is_root) {
 			die "only root can delete '$opt' config for real devices\n";
 		    }
 		    PVE::QemuConfig->add_to_pending_delete($conf, $opt, $force);
@@ -1348,7 +1350,7 @@ my $update_vm_api  = sub {
 		} elsif ($opt =~ m/^usb\d+$/) {
 		    if ($val =~ m/spice/) {
 			$rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
-		    } elsif ($authuser ne 'root@pam') {
+		    } elsif (!$is_root) {
 			die "only root can delete '$opt' config for real devices\n";
 		    }
 		    PVE::QemuConfig->add_to_pending_delete($conf, $opt, $force);
@@ -1392,14 +1394,14 @@ my $update_vm_api  = sub {
 		} elsif ($opt =~ m/^serial\d+/) {
 		    if ((!defined($conf->{$opt}) || $conf->{$opt} eq 'socket') && $param->{$opt} eq 'socket') {
 			$rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
-		    } elsif ($authuser ne 'root@pam') {
+		    } elsif (!$is_root) {
 			die "only root can modify '$opt' config for real devices\n";
 		    }
 		    $conf->{pending}->{$opt} = $param->{$opt};
 		} elsif ($opt =~ m/^usb\d+/) {
 		    if ((!defined($conf->{$opt}) || $conf->{$opt} =~ m/spice/) && $param->{$opt} =~ m/spice/) {
 			$rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
-		    } elsif ($authuser ne 'root@pam') {
+		    } elsif (!$is_root) {
 			die "only root can modify '$opt' config for real devices\n";
 		    }
 		    $conf->{pending}->{$opt} = $param->{$opt};
@@ -1644,9 +1646,11 @@ __PACKAGE__->register_method({
 	my $authuser = $rpcenv->get_user();
 	my $vmid = $param->{vmid};
 
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
 	my $skiplock = $param->{skiplock};
 	raise_param_exc({ skiplock => "Only root may use this option." })
-	    if $skiplock && $authuser ne 'root@pam';
+	    if $skiplock && !$is_root;
 
 	my $early_checks = sub {
 	    # test if VM exists
@@ -2291,10 +2295,12 @@ __PACKAGE__->register_method({
 	my $machine = extract_param($param, 'machine');
 	my $force_cpu = extract_param($param, 'force-cpu');
 
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
 	my $get_root_param = sub {
 	    my $value = extract_param($param, $_[0]);
 	    raise_param_exc({ "$_[0]" => "Only root may use this option." })
-		if $value && $authuser ne 'root@pam';
+		if $value && !$is_root;
 	    return $value;
 	};
 
@@ -2436,17 +2442,19 @@ __PACKAGE__->register_method({
 	my $node = extract_param($param, 'node');
 	my $vmid = extract_param($param, 'vmid');
 
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
 	my $skiplock = extract_param($param, 'skiplock');
 	raise_param_exc({ skiplock => "Only root may use this option." })
-	    if $skiplock && $authuser ne 'root@pam';
+	    if $skiplock && !$is_root;
 
 	my $keepActive = extract_param($param, 'keepActive');
 	raise_param_exc({ keepActive => "Only root may use this option." })
-	    if $keepActive && $authuser ne 'root@pam';
+	    if $keepActive && !$is_root;
 
 	my $migratedfrom = extract_param($param, 'migratedfrom');
 	raise_param_exc({ migratedfrom => "Only root may use this option." })
-	    if $migratedfrom && $authuser ne 'root@pam';
+	    if $migratedfrom && !$is_root;
 
 
 	my $storecfg = PVE::Storage::config();
@@ -2513,9 +2521,11 @@ __PACKAGE__->register_method({
 
 	my $vmid = extract_param($param, 'vmid');
 
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
 	my $skiplock = extract_param($param, 'skiplock');
 	raise_param_exc({ skiplock => "Only root may use this option." })
-	    if $skiplock && $authuser ne 'root@pam';
+	    if $skiplock && !$is_root;
 
 	die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid);
 
@@ -2580,13 +2590,15 @@ __PACKAGE__->register_method({
 	my $node = extract_param($param, 'node');
 	my $vmid = extract_param($param, 'vmid');
 
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
 	my $skiplock = extract_param($param, 'skiplock');
 	raise_param_exc({ skiplock => "Only root may use this option." })
-	    if $skiplock && $authuser ne 'root@pam';
+	    if $skiplock && !$is_root;
 
 	my $keepActive = extract_param($param, 'keepActive');
 	raise_param_exc({ keepActive => "Only root may use this option." })
-	    if $keepActive && $authuser ne 'root@pam';
+	    if $keepActive && !$is_root;
 
 	my $storecfg = PVE::Storage::config();
 
@@ -2735,13 +2747,15 @@ __PACKAGE__->register_method({
 	my $node = extract_param($param, 'node');
 	my $vmid = extract_param($param, 'vmid');
 
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
 	my $todisk = extract_param($param, 'todisk') // 0;
 
 	my $statestorage = extract_param($param, 'statestorage');
 
 	my $skiplock = extract_param($param, 'skiplock');
 	raise_param_exc({ skiplock => "Only root may use this option." })
-	    if $skiplock && $authuser ne 'root@pam';
+	    if $skiplock && !$is_root;
 
 	die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid);
 
@@ -2811,13 +2825,15 @@ __PACKAGE__->register_method({
 
 	my $vmid = extract_param($param, 'vmid');
 
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
 	my $skiplock = extract_param($param, 'skiplock');
 	raise_param_exc({ skiplock => "Only root may use this option." })
-	    if $skiplock && $authuser ne 'root@pam';
+	    if $skiplock && !$is_root;
 
 	my $nocheck = extract_param($param, 'nocheck');
 	raise_param_exc({ nocheck => "Only root may use this option." })
-	    if $nocheck && $authuser ne 'root@pam';
+	    if $nocheck && !$is_root;
 
 	my $to_disk_suspended;
 	eval {
@@ -2883,9 +2899,11 @@ __PACKAGE__->register_method({
 
 	my $vmid = extract_param($param, 'vmid');
 
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
 	my $skiplock = extract_param($param, 'skiplock');
 	raise_param_exc({ skiplock => "Only root may use this option." })
-	    if $skiplock && $authuser ne 'root@pam';
+	    if $skiplock && !$is_root;
 
 	PVE::QemuServer::vm_sendkey($vmid, $skiplock, $param->{key});
 
@@ -3392,6 +3410,8 @@ __PACKAGE__->register_method({
 
 	my $storecfg = PVE::Storage::config();
 
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
 	my $move_updatefn =  sub {
 	    my $conf = PVE::QemuConfig->load_config($vmid);
 	    PVE::QemuConfig->check_lock($conf);
@@ -3671,7 +3691,7 @@ __PACKAGE__->register_method({
 	    raise_param_exc({ 'target-vmid' => $msg, 'storage' => $msg });
 	} elsif ($target_vmid) {
 	    $rpcenv->check_vm_perm($authuser, $target_vmid, undef, ['VM.Config.Disk'])
-		if $authuser ne 'root@pam';
+		if !$is_root;
 
 	    raise_param_exc({ 'target-vmid' => "must be different than source VMID to reassign disk" })
 		if $vmid eq $target_vmid;
@@ -3910,15 +3930,17 @@ __PACKAGE__->register_method({
 
 	my $vmid = extract_param($param, 'vmid');
 
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
 	raise_param_exc({ force => "Only root may use this option." })
-	    if $param->{force} && $authuser ne 'root@pam';
+	    if $param->{force} && !$is_root;
 
 	raise_param_exc({ migration_type => "Only root may use this option." })
-	    if $param->{migration_type} && $authuser ne 'root@pam';
+	    if $param->{migration_type} && !$is_root;
 
 	# allow root only until better network permissions are available
 	raise_param_exc({ migration_network => "Only root may use this option." })
-	    if $param->{migration_network} && $authuser ne 'root@pam';
+	    if $param->{migration_network} && !$is_root;
 
 	# test if VM exists
 	my $conf = PVE::QemuConfig->load_config($vmid);
@@ -4099,8 +4121,10 @@ __PACKAGE__->register_method({
 	my $sizestr = extract_param($param, 'size');
 
 	my $skiplock = extract_param($param, 'skiplock');
+	my $is_root = PVE::Tools::check_for_root("/vms/$vmid");
+
         raise_param_exc({ skiplock => "Only root may use this option." })
-            if $skiplock && $authuser ne 'root@pam';
+            if $skiplock && !$is_root;
 
         my $storecfg = PVE::Storage::config();
 
-- 
2.30.2





  parent reply	other threads:[~2022-01-05 15:22 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-01-05 15:22 [pve-devel] [RFC access-control common container qemu-server 0/4] #2582: Sys.Root privilege Oguz Bektas
2022-01-05 15:22 ` [pve-devel] [RFC access-control 1/4] add " Oguz Bektas
2022-01-05 15:22 ` [pve-devel] [RFC common 2/4] tools: add 'check_for_root' helper Oguz Bektas
2022-01-10 13:45   ` Fabian Grünbichler
2022-01-05 15:22 ` [pve-devel] [PATCH container 3/4] fix #2582: api: use common helper for checking root privileges Oguz Bektas
2022-01-05 15:22 ` Oguz Bektas [this message]
2022-01-10 13:45   ` [pve-devel] [RFC qemu-server 4/4] " Fabian Grünbichler
2022-01-10 13:45 ` [pve-devel] [RFC access-control common container qemu-server 0/4] #2582: Sys.Root privilege Fabian Grünbichler

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220105152215.1307583-5-o.bektas@proxmox.com \
    --to=o.bektas@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal