]>
git.proxmox.com Git - pve-common.git/blob - src/PVE/Subscription.pm
1 package PVE
::Subscription
;
5 use Digest
::MD5
qw(md5_hex md5_base64);
15 # How long the local key is valid for in between remote checks
16 our $localkeydays = 15;
17 # How many days to allow after local key expiry before blocking
18 # access if connection cannot be made
19 my $allowcheckfaildays = 5;
21 my $shared_key_data = "kjfdlskfhiuewhfk947368";
35 my ($info, $server_id) = @_;
37 foreach my $f (qw(status checktime key)) {
39 die "Missing field '$f'\n";
43 if ($info->{checktime
} > time()) {
44 die "Last check time in future.\n";
47 return undef if $info->{status
} ne 'Active';
49 foreach my $f (keys %$saved_fields) {
50 next if !$saved_fields->{$f};
52 die "Missing field '$f'\n";
57 foreach my $hwid (split(/,/, $info->{validdirectory
})) {
58 if ($hwid eq $server_id) {
63 die "Server ID does not match\n" if !$found;
68 sub check_subscription
{
69 my ($key, $server_id, $proxy) = @_;
71 my $whmcsurl = "https://shop.maurer-it.com";
73 my $uri = "$whmcsurl/modules/servers/licensing/verify.php";
75 my $check_token = time() . md5_hex
(rand(8999999999) + 1000000000) . $key;
80 domain
=> 'www.proxmox.com',
82 check_token
=> $check_token,
85 my $req = HTTP
::Request-
>new('POST' => $uri);
86 $req->header('Content-Type' => 'application/x-www-form-urlencoded');
87 # We use a temporary URI object to format
88 # the application/x-www-form-urlencoded content.
89 my $url = URI-
>new('http:');
90 $url->query_form(%$params);
91 my $content = $url->query;
92 $req->header('Content-Length' => length($content));
93 $req->content($content);
95 my $ua = LWP
::UserAgent-
>new(protocols_allowed
=> ['https'], timeout
=> 30);
98 $ua->proxy(['https'], $proxy);
103 my $response = $ua->request($req);
104 my $code = $response->code;
107 my $msg = $response->message || 'unknown';
108 die "Invalid response from server: $code $msg\n";
111 my $raw = $response->decoded_content;
114 while ($raw =~ m/<(.*?)>([^<]+)<\/\
1>/g
) {
115 my ($k, $v) = ($1, $2);
116 next if !($k eq 'md5hash' || defined($saved_fields->{$k}));
119 $subinfo->{checktime
} = time();
120 $subinfo->{key
} = $key;
122 if ($subinfo->{message
}) {
123 $subinfo->{message
} =~ s/^Directory Invalid$/Invalid Server ID/;
126 my $emd5sum = md5_hex
($shared_key_data . $check_token);
127 if ($subinfo->{status
} && $subinfo->{status
} eq 'Active') {
128 if (!$subinfo->{md5hash
} || ($subinfo->{md5hash
} ne $emd5sum)) {
129 die "MD5 Checksum Verification Failed\n";
133 delete $subinfo->{md5hash
};
135 check_fields
($subinfo, $server_id);
140 sub read_subscription
{
141 my ($server_id, $filename, $fh) = @_;
143 my $info = { status
=> 'Invalid' };
145 my $key = <$fh>; # first line is the key
150 my $csum = <$fh>; # second line is a checksum
153 while (defined(my $line = <$fh>)) {
157 if ($key && $csum && $data) {
164 my $json_text = decode_base64
($data);
165 $localinfo = decode_json
($json_text);
166 my $newcsum = md5_base64
($localinfo->{checktime
} . $data . $shared_key_data);
167 die "checksum failure\n" if $csum ne $newcsum;
169 check_fields
($localinfo, $server_id);
171 my $age = time() - $localinfo->{checktime
};
173 my $maxage = ($localkeydays + $allowcheckfaildays)*60*60*24;
174 die "subscription info too old\n"
175 if ($localinfo->{status
} eq 'Active') && ($age > $maxage);
179 $info->{message
} = $err;
188 sub update_apt_auth
{
189 my ($key, $server_id) = @_;
191 my $auth = { 'enterprise.proxmox.com' => { login
=> $key, password
=> $server_id } };
192 PVE
::INotify
::update_file
('apt-auth', $auth);
195 sub write_subscription
{
196 my ($server_id, $filename, $fh, $info) = @_;
198 if ($info->{status
} eq 'New') {
199 PVE
::Tools
::safe_print
($filename, $fh, "$info->{key}\n");
201 my $json = encode_json
($info);
202 my $data = encode_base64
($json);
203 my $csum = md5_base64
($info->{checktime
} . $data . $shared_key_data);
205 my $raw = "$info->{key}\n$csum\n$data";
207 PVE
::Tools
::safe_print
($filename, $fh, $raw);
210 update_apt_auth
($info->{key
}, $server_id);