X-Git-Url: https://git.proxmox.com/?p=pve-access-control.git;a=blobdiff_plain;f=PVE%2FAccessControl.pm;h=f0c37c7651a6eda6d51ced1bda18022d9ff63068;hp=768703124594957b6d240fd2e5cb4429dd6b5171;hb=3f62bdbea65c55ad6465a512b624ecea2dddee9e;hpb=7b395f990d138c831589b202df6e61ee0999aab3 diff --git a/PVE/AccessControl.pm b/PVE/AccessControl.pm index 7687031..f0c37c7 100644 --- a/PVE/AccessControl.pm +++ b/PVE/AccessControl.pm @@ -213,6 +213,69 @@ sub verify_vnc_ticket { return undef; } +sub assemble_spice_ticket { + my ($username, $vmid, $node) = @_; + + my $rsa_priv = get_privkey(); + + my $timestamp = sprintf("%08x", time()); + + my $randomstr = "PVESPICE:$timestamp:$vmid:$node:" . rand(10); + + # this should be uses as one-time password + # max length is 60 chars (spice limit) + # we pass this to qemu set_pasword and limit lifetime there + # keep this secret + my $ticket = Digest::SHA::sha1_hex($rsa_priv->sign($randomstr)); + + # Note: spice proxy connects with HTTP, so $proxyticket is exposed to public + # we use a signature/timestamp to make sure nobody can fake such ticket + # an attacker can use this $proxyticket, but he will fail because $ticket is + # private. + # The proxy need to be able to extract/verify the ticket + # Note: data needs to be lower case only, because virt-viewer needs that + # Note: RSA signature are too long (>=256 charaters) and makes problems with remote-viewer + + my $secret = &$get_csrfr_secret(); + my $plain = "pvespiceproxy:$timestamp:$vmid:$node"; + + # produces 40 characters + my $sig = unpack("H*", Digest::SHA::sha1($plain, &$get_csrfr_secret())); + + #my $sig = unpack("H*", $rsa_priv->sign($plain)); # this produce too long strings (512) + + my $proxyticket = $plain . "::" . $sig; + + return ($ticket, $proxyticket); +} + +sub verify_spice_connect_url { + my ($connect_str) = @_; + + # Note: we pass the spice ticket as 'host', so the + # spice viewer connects with "$ticket:$port" + + return undef if !$connect_str; + + if ($connect_str =~m/^pvespiceproxy:([a-z0-9]{8}):(\d+):(\S+)::([a-z0-9]{40}):(\d+)$/) { + my ($timestamp, $vmid, $node, $hexsig, $port) = ($1, $2, $3, $4, $5, $6); + my $ttime = hex($timestamp); + my $age = time() - $ttime; + + # use very limited lifetime - is this enough? + return undef if !(($age > -20) && ($age < 40)); + + my $plain = "pvespiceproxy:$timestamp:$vmid:$node"; + my $sig = unpack("H*", Digest::SHA::sha1($plain, &$get_csrfr_secret())); + + if ($sig eq $hexsig) { + return ($vmid, $node, $port); + } + } + + return undef; +} + sub check_user_exist { my ($usercfg, $username, $noerr) = @_; @@ -959,4 +1022,36 @@ sub check_permissions { return 1; } +sub add_vm_to_pool { + my ($vmid, $pool) = @_; + + my $addVMtoPoolFn = sub { + my $usercfg = cfs_read_file("user.cfg"); + if (my $data = $usercfg->{pools}->{$pool}) { + $data->{vms}->{$vmid} = 1; + $usercfg->{vms}->{$vmid} = $pool; + cfs_write_file("user.cfg", $usercfg); + } + }; + + lock_user_config($addVMtoPoolFn, "can't add VM $vmid to pool '$pool'"); +} + +sub remove_vm_from_pool { + my ($vmid) = @_; + + my $delVMfromPoolFn = sub { + my $usercfg = cfs_read_file("user.cfg"); + if (my $pool = $usercfg->{vms}->{$vmid}) { + if (my $data = $usercfg->{pools}->{$pool}) { + delete $data->{vms}->{$vmid}; + delete $usercfg->{vms}->{$vmid}; + cfs_write_file("user.cfg", $usercfg); + } + } + }; + + lock_user_config($delVMfromPoolFn, "pool cleanup for VM $vmid failed"); +} + 1;