Skip to content

Commit 5fab6aa

Browse files
authored
Player software: verify package (#2893)
* Player software: verify chromeos package on upload. xibosignageltd/xibo-private#926
1 parent 58bfa79 commit 5fab6aa

File tree

10 files changed

+84
-12
lines changed

10 files changed

+84
-12
lines changed

Dockerfile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ RUN LC_ALL=C.UTF-8 DEBIAN_FRONTEND=noninteractive apt update && apt upgrade -y &
7070
libapache2-mod-xsendfile \
7171
netcat \
7272
iputils-ping \
73+
gnupg \
7374
php8.2 \
7475
libapache2-mod-php8.2 \
7576
php8.2-gd \
@@ -90,6 +91,7 @@ RUN LC_ALL=C.UTF-8 DEBIAN_FRONTEND=noninteractive apt update && apt upgrade -y &
9091
php8.2-phar \
9192
php8.2-opcache \
9293
php8.2-mongodb \
94+
php8.2-gnupg \
9395
tzdata \
9496
msmtp \
9597
openssl \
@@ -173,7 +175,8 @@ ENV CMS_DEV_MODE=false \
173175
MEMCACHED_PORT=11211 \
174176
CMS_USAGE_REPORT=true \
175177
XTR_ENABLED=true \
176-
GIT_COMMIT=$GIT_COMMIT
178+
GIT_COMMIT=$GIT_COMMIT \
179+
GNUPGHOME=/var/www/.gnupg
177180

178181
# Expose port 80
179182
EXPOSE 80
@@ -228,7 +231,8 @@ RUN mkdir -p /var/www/cms/library/temp && \
228231
/etc/periodic/15min/cms-db-backup && \
229232
mkdir -p /run/apache2 && \
230233
ln -sf /usr/bin/msmtp /usr/sbin/sendmail && \
231-
chmod 777 /tmp
234+
chmod 777 /tmp && \
235+
chown -R www-data:www-data /var/www/.gnupg
232236

233237
# Expose volume mount points
234238
VOLUME /var/www/cms/library

Dockerfile.ci

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ RUN LC_ALL=C.UTF-8 DEBIAN_FRONTEND=noninteractive apt update && apt upgrade -y &
5555
libapache2-mod-xsendfile \
5656
netcat \
5757
iputils-ping \
58+
gnupg \
5859
php8.2 \
5960
libapache2-mod-php8.2 \
6061
php8.2-gd \
@@ -75,6 +76,7 @@ RUN LC_ALL=C.UTF-8 DEBIAN_FRONTEND=noninteractive apt update && apt upgrade -y &
7576
php8.2-phar \
7677
php8.2-opcache \
7778
php8.2-mongodb \
79+
php8.2-gnupg \
7880
tzdata \
7981
msmtp \
8082
openssl \
@@ -98,7 +100,8 @@ ADD docker/ /
98100
# Adjust file permissions as appropriate
99101
RUN chmod +x /entrypoint.sh /usr/local/bin/httpd-foreground /usr/local/bin/wait-for-command.sh \
100102
/etc/periodic/15min/cms-db-backup && \
101-
chmod 777 /tmp
103+
chmod 777 /tmp && \
104+
chown -R www-data:www-data /var/www/.gnupg
102105

103106
# Update the PHP.ini file
104107
RUN sed -i "s/error_reporting = .*$/error_reporting = E_ERROR | E_WARNING | E_PARSE/" /etc/php/8.2/apache2/php.ini && \
@@ -152,7 +155,8 @@ ENV CMS_DEV_MODE=true \
152155
MEMCACHED_PORT=11211 \
153156
CMS_USAGE_REPORT=false \
154157
XTR_ENABLED=true \
155-
GIT_COMMIT=$GIT_COMMIT
158+
GIT_COMMIT=$GIT_COMMIT \
159+
GNUPGHOME=/var/www/.gnupg
156160

157161
# Expose port 80
158162
EXPOSE 80

Dockerfile.dev

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ RUN LC_ALL=C.UTF-8 DEBIAN_FRONTEND=noninteractive apt update && apt upgrade -y &
2222
libapache2-mod-xsendfile \
2323
netcat \
2424
iputils-ping \
25+
gnupg \
2526
php8.2 \
2627
libapache2-mod-php8.2 \
2728
php8.2-gd \
@@ -42,6 +43,7 @@ RUN LC_ALL=C.UTF-8 DEBIAN_FRONTEND=noninteractive apt update && apt upgrade -y &
4243
php8.2-phar \
4344
php8.2-opcache \
4445
php8.2-mongodb \
46+
php8.2-gnupg \
4547
tzdata \
4648
msmtp \
4749
openssl \
@@ -65,7 +67,8 @@ ADD docker/ /
6567
# Adjust file permissions as appropriate
6668
RUN chmod +x /entrypoint.sh /usr/local/bin/httpd-foreground /usr/local/bin/wait-for-command.sh \
6769
/etc/periodic/15min/cms-db-backup && \
68-
chmod 777 /tmp
70+
chmod 777 /tmp && \
71+
chown -R www-data:www-data /var/www/.gnupg
6972

7073
# Update the PHP.ini file
7174
RUN sed -i "s/error_reporting = .*$/error_reporting = E_ERROR | E_WARNING | E_PARSE/" /etc/php/8.2/apache2/php.ini && \
@@ -121,7 +124,8 @@ ENV CMS_DEV_MODE=true \
121124
MEMCACHED_PORT=11211 \
122125
CMS_USAGE_REPORT=true \
123126
XTR_ENABLED=false \
124-
GIT_COMMIT=$GIT_COMMIT
127+
GIT_COMMIT=$GIT_COMMIT \
128+
GNUPGHOME=/var/www/.gnupg
125129

126130
# Expose port 80
127131
EXPOSE 80

composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"ext-json": "1",
3333
"ext-soap": "1",
3434
"ext-zip": "1",
35-
"ext-fileinfo": "*"
35+
"ext-fileinfo": "1"
3636
}
3737
},
3838
"minimum-stability": "dev",
@@ -66,7 +66,6 @@
6666
"james-heinrich/getid3": "^1.9",
6767
"onelogin/php-saml": "4.1.*",
6868
"infostars/picofeed": "dev-westphal/php8",
69-
"xibosignage/xibo-xmr": "0.*",
7069
"tedivm/stash": "^v0.17.6",
7170
"phenx/php-font-lib": "^0.5.0",
7271
"symfony/event-dispatcher": "^4.1",

docker/var/www/.gnupg/private-keys-v1.d/.gitkeep

Whitespace-only changes.

docker/var/www/.gnupg/pubring.kbx

2.45 KB
Binary file not shown.

docker/var/www/.gnupg/trustdb.gpg

1.21 KB
Binary file not shown.

lib/Entity/PlayerVersion.php

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/*
3-
* Copyright (C) 2024 Xibo Signage Ltd
3+
* Copyright (C) 2025 Xibo Signage Ltd
44
*
55
* Xibo - Digital Signage - https://xibosignage.com
66
*
@@ -32,7 +32,9 @@
3232
use Xibo\Service\LogServiceInterface;
3333
use Xibo\Storage\StorageServiceInterface;
3434
use Xibo\Support\Exception\DuplicateEntityException;
35+
use Xibo\Support\Exception\GeneralException;
3536
use Xibo\Support\Exception\InvalidArgumentException;
37+
use Xibo\Support\Exception\NotFoundException;
3638

3739
/**
3840
* Class PlayerVersion
@@ -223,10 +225,53 @@ public function unpack(string $libraryFolder): static
223225
if ($this->type === 'chromeOS') {
224226
$this->getLog()->debug('add: handling chromeOS upload');
225227

226-
// TODO: check the signature of the file to make sure it comes from a verified source.
228+
$fullFileName = $libraryFolder . 'playersoftware/' . $this->fileName;
229+
230+
// Check the signature of the file to make sure it comes from a verified source.
231+
try {
232+
$this->getLog()->debug('unpack: loading gnupg to verify the signature');
233+
234+
$gpg = new \gnupg();
235+
$gpg->seterrormode(\gnupg::ERROR_EXCEPTION);
236+
$info = $gpg->verify(
237+
file_get_contents($fullFileName),
238+
false,
239+
);
240+
241+
if ($info === false
242+
|| $info[0]['fingerprint'] !== '10415C506BE63E70BAF1D58BC1EF165A0F880F75'
243+
|| $info[0]['status'] !== 0
244+
|| $info[0]['summary'] !== 0
245+
) {
246+
$this->getLog()->error('unpack: unable to verify GPG. file = ' . $this->fileName);
247+
throw new GeneralException();
248+
}
249+
250+
$this->getLog()->debug('unpack: signature verified');
251+
252+
// Signature verified, move the file, so we can decrypt it.
253+
rename($fullFileName, $libraryFolder . 'playersoftware/' . $this->versionId . '.gpg');
254+
255+
$this->getLog()->debug('unpack: using the shell to decrypt the file');
256+
257+
// Go to the shell to decrypt it.
258+
shell_exec('gpg --decrypt --output ' . $libraryFolder . 'playersoftware/' . $this->versionId
259+
. ' ' . $libraryFolder . 'playersoftware/' . $this->versionId . '.gpg');
260+
261+
// Was this successful?
262+
if (!file_exists($libraryFolder . 'playersoftware/' . $this->versionId)) {
263+
throw new NotFoundException('Not found after decryption');
264+
}
265+
266+
// Rename the GPG file back to its original name.
267+
rename($libraryFolder . 'playersoftware/' . $this->versionId . '.gpg', $fullFileName);
268+
} catch (\Exception $e) {
269+
$this->getLog()->error('unpack: ' . $e->getMessage());
270+
throw new InvalidArgumentException(__('Package file unsupported or invalid'));
271+
}
227272

228273
$zip = new \ZipArchive();
229-
if (!$zip->open($libraryFolder . 'playersoftware/' . $this->fileName)) {
274+
if (!$zip->open($libraryFolder . 'playersoftware/' . $this->versionId)) {
230275
throw new InvalidArgumentException(__('Unable to open ZIP'));
231276
}
232277

@@ -273,6 +318,9 @@ public function unpack(string $libraryFolder): static
273318
$manifest = Str::replace('assets/icons/192x192.png', $this->config->uri('img/192x192.png'), $manifest);
274319

275320
file_put_contents($folder . '/manifest.json', $manifest);
321+
322+
// Unlink our decrypted file
323+
unlink($libraryFolder . 'playersoftware/' . $this->versionId);
276324
}
277325

278326
return $this;

lib/Helper/Environment.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,14 @@ public static function checkSimpleXml()
319319
return extension_loaded('simplexml');
320320
}
321321

322+
/**
323+
* @return bool
324+
*/
325+
public static function checkGnu()
326+
{
327+
return extension_loaded('gnupg');
328+
}
329+
322330
/**
323331
* @param $url
324332
* @return bool

lib/Service/ConfigService.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/*
3-
* Copyright (C) 2024 Xibo Signage Ltd
3+
* Copyright (C) 2025 Xibo Signage Ltd
44
*
55
* Xibo - Digital Signage - https://xibosignage.com
66
*
@@ -743,6 +743,11 @@ public function checkEnvironment()
743743
__('SimpleXML is used to parse RSS feeds and other XML data sources')
744744
);
745745

746+
$this->testItem($rows, __('GNUPG'),
747+
Environment::checkGnu(),
748+
__('checkGnu is used to verify the integrity of Player Software versions uploaded to the CMS')
749+
);
750+
746751
$this->envTested = true;
747752

748753
return $rows;

0 commit comments

Comments
 (0)