From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id 6A05E1FF0AA for ; Fri, 04 Sep 2026 09:17:54 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id AC0AE2158E; Fri, 04 Sep 2026 09:17:51 +0200 (CEST) Message-ID: <1ff988d4-7185-45ee-ac69-4887fb953741@proxmox.com> Date: Fri, 4 Sep 2026 09:17:42 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Beta Subject: Re: [PATCH manager v3] fix #6735: api: pci: allow mdevscan access via mapping permissions To: Elias Huhsovitz , pve-devel@lists.proxmox.com References: <20260903121143.145841-1-e.huhsovitz@proxmox.com> Content-Language: en-US From: Dominik Csapak In-Reply-To: <20260903121143.145841-1-e.huhsovitz@proxmox.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1788506258809 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.562 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: 7FKG24KMQZFYZW6KEPBAS526VJP33BPO X-Message-ID-Hash: 7FKG24KMQZFYZW6KEPBAS526VJP33BPO X-MailFrom: d.csapak@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: one comment, aside from that Reviewed-by: Dominik Csapak Tested-by: Dominik Csapak On 9/3/26 2:11 PM, Elias Huhsovitz wrote: > The mdevscan endpoint requires Sys.Audit or Sys.Modify on '/'. This > blocks non-admin users from listing mediated device types for a PCI > mapping, even when they hold Mapping.Use on that mapping. > > Check the permission in the API handler based on the parameter type: For > a raw PCI ID, require Sys.Audit or Sys.Modify on '/'. For a mapping, > require Sys.Audit or Sys.Modify on '/', or fall back to requiring > Mapping.Use, Mapping.Modify or Mapping.Audit on the specific mapping > path. > > Set the endpoint permission to 'user => all' so the handler performs the > type-dependent check. This keeps raw PCI IDs, which are not valid ACL > paths, out of the declarative ACL evaluation. > > Signed-off-by: Elias Huhsovitz > --- > v2: https://lore.proxmox.com/pve-devel/20260827112525.154445-1-e.huhsovitz@proxmox.com/ > v1: https://lore.proxmox.com/pve-devel/20260824112610.148089-1-e.huhsovitz@proxmox.com/ > > Changes v2->v3 > -------------- > * re-introduce else-statement in API handler > * move permissions checks into respective logical branches > (previously sepate sesection before core code) > * update commit message > > Changes v1->v2 > -------------- > * Allow all users in the declarative API permissions. > * Implement ACL check in API handler by calling > check_any and raise_perm_exc. > * Remove else statement in API handler, since return statement > provides implicit branching > * update commit message > > Changes > ------- > PVE/API2/Hardware/PCI.pm | 44 ++++++++++++++++++++++++++++++---------- > 1 file changed, 33 insertions(+), 11 deletions(-) > > diff --git a/PVE/API2/Hardware/PCI.pm b/PVE/API2/Hardware/PCI.pm > index 36b9741b..bfcd1fc5 100644 > --- a/PVE/API2/Hardware/PCI.pm > +++ b/PVE/API2/Hardware/PCI.pm > @@ -3,10 +3,12 @@ package PVE::API2::Hardware::PCI; > use strict; > use warnings; > > +use PVE::Exception qw(raise raise_perm_exc); > use PVE::JSONSchema qw(get_standard_option); > > use PVE::QemuServer::PCI::Mdev; > use PVE::RESTHandler; > +use PVE::RPCEnvironment; > > use base qw(PVE::RESTHandler); > > @@ -180,7 +182,11 @@ __PACKAGE__->register_method({ > protected => 1, > proxyto => "node", > permissions => { > - check => ['perm', '/', ['Sys.Audit', 'Sys.Modify'], any => 1], > + description => > + "For a PCI ID, requires 'Sys.Audit' or 'Sys.Modify' on '/'. For a mapping," > + . " requires the same global permissions, or 'Mapping.Use', 'Mapping.Modify'" > + . ", or 'Mapping.Audit' on '/mapping/pci/'.", > + user => 'all', > }, > parameters => { > additionalProperties => 0, > @@ -222,20 +228,37 @@ __PACKAGE__->register_method({ > code => sub { > my ($param) = @_; > > - if ($param->{'pci-id-or-mapping'} =~ > - m/^(?:[0-9a-fA-F]{4}:)?[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]$/ > - ) { > - return PVE::QemuServer::PCI::Mdev::get_mdev_types($param->{'pci-id-or-mapping'}); # PCI ID > + my $id = $param->{'pci-id-or-mapping'}; > + my $is_pci_id = > + $id =~ m/^(?:[0-9a-fA-F]{4}:)?[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]$/; > + > + my $rpcenv = PVE::RPCEnvironment::get(); > + my $authuser = $rpcenv->get_user(); > + > + my $has_global_perms = > + $rpcenv->check_any($authuser, '/', ['Sys.Audit', 'Sys.Modify'], 1); > + > + if ($is_pci_id) { > + raise_perm_exc("/, " . join("|", ['Sys.Audit', 'Sys.Modify'])) > + if !$has_global_perms; > + I guess this list needs to be a proper list (not a reference) otherwise this comes out as: ARRAY(0x64e525f764e8) (check with the perl cli: `perl -e "print join('|', ['foo', 'bar']);"` ) IMO 3 good ways to solve: * use ('Sys.Audit', 'Sys.Modify') * factor that list out and use $var->@* * simply write out the string (this way is even longer than manually writing "/, Sys.Audit|Sys.Modify" > + return PVE::QemuServer::PCI::Mdev::get_mdev_types($id); > } else { > - my $mapping = $param->{'pci-id-or-mapping'}; > + if (!$has_global_perms) { > + $rpcenv->check_any( > + $authuser, > + "/mapping/pci/$id", > + ['Mapping.Use', 'Mapping.Modify', 'Mapping.Audit'], > + ); > + } > > my $types = {}; > - my $devices = PVE::Mapping::PCI::find_on_current_node($mapping); > + my $devices = PVE::Mapping::PCI::find_on_current_node($id); > for my $device ($devices->@*) { > - my $id = $device->{path}; > - next if $id =~ m/;/; # mdev not supported for multifunction devices > + my $dev_id = $device->{path}; > + next if $dev_id =~ m/;/; # mdev not supported for multifunction devices > > - my $device_types = PVE::QemuServer::PCI::Mdev::get_mdev_types($id); > + my $device_types = PVE::QemuServer::PCI::Mdev::get_mdev_types($dev_id); > > for my $type_definition ($device_types->@*) { > my $type = $type_definition->{type}; > @@ -247,6 +270,5 @@ __PACKAGE__->register_method({ > > return [sort { $a->{type} cmp $b->{type} } values($types->%*)]; > } > - > }, > });