11 use Crypt
::OpenSSL
::RSA
;
13 my $min_ticket_lifetime = -60*5; # allow 5 minutes time drift
14 my $max_ticket_lifetime = 60*60*2; # 2 hours
16 my $basedir = "/etc/proxmox";
18 my $pmg_api_cert_fn = "$basedir/pmg-api.pem";
20 # this is just a secret accessable by all API servers
21 # and is used for CSRF prevention
22 my $pmg_csrf_key_fn = "$basedir/pmg-csrf.key";
24 my $authprivkeyfn = "$basedir/pmg-authkey.key";
25 my $authpubkeyfn = "$basedir/pmg-authkey.pub";
27 # only write output if something fails
33 my $record_output = sub {
39 PVE
::Tools
::run_command
($cmd, outfunc
=> $record_output,
40 errfunc
=> $record_output);
51 sub generate_api_cert
{
52 my ($nodename, $force) = @_;
54 if (-f
$pmg_api_cert_fn) {
55 return $pmg_api_cert_fn if !$force;
56 unlink $pmg_api_cert_fn;
59 my $gid = getgrnam('www-data') ||
60 die "user www-data not in group file\n";
62 my $tmp_fn = "$pmg_api_cert_fn.tmp$$";
64 my $cmd = ['openssl', 'req', '-batch', '-x509', '-newkey', 'rsa:4096',
65 '-nodes', '-keyout', $tmp_fn, '-out', $tmp_fn,
66 '-subj', "/CN=$nodename/",
71 chown(0, $gid, $tmp_fn) || die "chown failed - $!\n";
72 chmod(0640, $tmp_fn) || die "chmod failed - $!\n";
73 rename($tmp_fn, $pmg_api_cert_fn) || die "rename failed - $!\n";
77 die "unable to generate pmg api cert '$pmg_api_cert_fn':\n$err";
80 return $pmg_api_cert_fn;
83 sub generate_csrf_key
{
85 return if -f
$pmg_csrf_key_fn;
87 my $gid = getgrnam('www-data') ||
88 die "user www-data not in group file\n";
90 my $tmp_fn = "$pmg_csrf_key_fn.tmp$$";
91 my $cmd = ['openssl', 'genrsa', '-out', $tmp_fn, '2048'];
95 chown(0, $gid, $tmp_fn) || die "chown failed - $!\n";
96 chmod(0640, $tmp_fn) || die "chmod failed - $!\n";
97 rename($tmp_fn, $pmg_csrf_key_fn) || die "rename failed - $!\n";
101 die "unable to generate pmg csrf key '$pmg_csrf_key_fn':\n$@";
104 return $pmg_csrf_key_fn;
107 sub generate_auth_key
{
109 return if -f
"$authprivkeyfn";
112 run_silent_cmd
(['openssl', 'genrsa', '-out', $authprivkeyfn, '2048']);
114 run_silent_cmd
(['openssl', 'rsa', '-in', $authprivkeyfn, '-pubout', '-out', $authpubkeyfn]);
117 die "unable to generate pmg auth key:\n$@" if $@;
120 my $pve_auth_priv_key;
123 return $pve_auth_priv_key if $pve_auth_priv_key;
125 my $input = PVE
::Tools
::file_get_contents
($authprivkeyfn);
127 $pve_auth_priv_key = Crypt
::OpenSSL
::RSA-
>new_private_key($input);
129 return $pve_auth_priv_key;
132 my $pve_auth_pub_key;
135 return $pve_auth_pub_key if $pve_auth_pub_key;
137 my $input = PVE
::Tools
::file_get_contents
($authpubkeyfn);
139 $pve_auth_pub_key = Crypt
::OpenSSL
::RSA-
>new_public_key($input);
141 return $pve_auth_pub_key;
144 my $csrf_prevention_secret;
145 my $get_csrfr_secret = sub {
146 if (!$csrf_prevention_secret) {
147 my $input = PVE
::Tools
::file_get_contents
($pmg_csrf_key_fn);
148 $csrf_prevention_secret = Digest
::SHA
::sha1_base64
($input);
149 print "SECRET:$csrf_prevention_secret\n";
151 return $csrf_prevention_secret;
155 sub verify_csrf_prevention_token
{
156 my ($username, $token, $noerr) = @_;
158 my $secret = &$get_csrfr_secret();
160 return PVE
::Ticket
::verify_csrf_prevention_token
(
161 $secret, $username, $token, $min_ticket_lifetime,
162 $max_ticket_lifetime, $noerr);
165 sub assemble_csrf_prevention_token
{
168 my $secret = &$get_csrfr_secret();
170 return PVE
::Ticket
::assemble_csrf_prevention_token
($secret, $username);
173 sub assemble_ticket
{
176 my $rsa_priv = get_privkey
();
178 return PVE
::Ticket
::assemble_rsa_ticket
($rsa_priv, 'PMG', $username);
182 my ($ticket, $noerr) = @_;
184 my $rsa_pub = get_pubkey
();
186 return PVE
::Ticket
::verify_rsa_ticket
(
187 $rsa_pub, 'PMG', $ticket, undef,
188 $min_ticket_lifetime, $max_ticket_lifetime, $noerr);
192 # - they do not contain the username in plain text
193 # - they are restricted to a specific resource path (example: '/vms/100')
194 sub assemble_vnc_ticket
{
195 my ($username, $path) = @_;
197 my $rsa_priv = get_privkey
();
199 my $secret_data = "$username:$path";
201 return PVE
::Ticket
::assemble_rsa_ticket
(
202 $rsa_priv, 'PMGVNC', undef, $secret_data);
205 sub verify_vnc_ticket
{
206 my ($ticket, $username, $path, $noerr) = @_;
208 my $rsa_pub = get_pubkey
();
210 my $secret_data = "$username:$path";
212 return PVE
::Ticket
::verify_rsa_ticket
(
213 $rsa_pub, 'PMGVNC', $ticket, $secret_data, -20, 40, $noerr);