[PATCH 0/3] cms/openssl: add CRL verification support and tests

34 views
Skip to first unread message

Daniel Braunwarth

unread,
Jul 2, 2026, 6:17:09 AM (14 days ago) Jul 2
to swup...@googlegroups.com, Daniel Braunwarth
this series adds CRL support to CMS verification when SWUpdate is built
with the OpenSSL backend.

Today, CMS signatures are validated against the configured trust chain,
but revocation status is not checked. With this series, SWUpdate can be
pointed to a CRL file and reject revoked signer certificates.

Summary:
- add a new runtime option/config key `crl-path`
- load CRLs from PEM (single or multiple CRLs) and DER (single CRL)
- enable CRL verification in the OpenSSL X509 store
- extend docs with usage examples for PEM and DER CRLs
- add OpenSSL CMS test cases for revoked and non-revoked scenarios
- keep per-directory `.gitignore` files trackable for generated fixtures

Test notes:
- The new tests cover:
- CMS verification without CRL
- revoked signer with PEM CRL
- revoked signer with DER CRL
- non-revoked signer with empty CRL

Compatibility:
- behavior is unchanged unless `crl-path` is explicitly set
- functionality is scoped to CMS + OpenSSL builds

Daniel Braunwarth (3):
crypto: openssl: add CRL support for CMS verification
test: add CMS CRL verification tests for OpenSSL
git: don't ignore .gitignore files

.gitignore | 3 +
core/swupdate.c | 13 ++++
crypto/swupdate_cms_verify_openssl.c | 94 ++++++++++++++++++++++++++++
doc/source/signed_images.rst | 22 +++++++
include/swupdate.h | 1 +
test/Makefile | 43 +++++++++++++
test/data/.gitignore | 9 +++
test/data/cms-test-ca/openssl.cnf | 31 +++++++++
test/test_verify.c | 76 ++++++++++++++++++++++
9 files changed, 292 insertions(+)
create mode 100644 test/data/.gitignore
create mode 100644 test/data/cms-test-ca/openssl.cnf

--
2.43.0

Daniel Braunwarth

unread,
Jul 2, 2026, 6:17:14 AM (14 days ago) Jul 2
to swup...@googlegroups.com, Daniel Braunwarth, Franz Heger
Add CMS verification test coverage for OpenSSL CRL handling, including:
verification without CRL, revoked signer with PEM CRL, revoked signer
with DER CRL, and a non-revoked signer with an empty CRL.

Extend test fixture generation in test/Makefile to create CMS signature
artifacts and CRLs, and add test/data/.gitignore entries for generated
fixture files.

Co-developed-by: Franz Heger <franz...@kuka.com>
Signed-off-by: Franz Heger <franz...@kuka.com>
Signed-off-by: Daniel Braunwarth <daniel.b...@kuka.com>
---
test/Makefile | 43 +++++++++++++++++
test/data/.gitignore | 5 ++
test/data/cms-test-ca/openssl.cnf | 31 +++++++++++++
test/test_verify.c | 76 +++++++++++++++++++++++++++++++
4 files changed, 155 insertions(+)
create mode 100644 test/data/.gitignore
create mode 100644 test/data/cms-test-ca/openssl.cnf

diff --git a/test/Makefile b/test/Makefile
index 7fa02dbd..ca66f7b0 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -25,6 +25,9 @@ tests-$(CONFIG_HASH_VERIFY) += test_hash
ifeq ($(CONFIG_SIGALG_RAWRSA),y)
tests-$(CONFIG_SIGNED_IMAGES) += test_verify
endif
+ifeq ($(CONFIG_SSL_IMPL_OPENSSL),y)
+tests-$(CONFIG_SIGALG_CMS) += test_verify
+endif
tests-$(CONFIG_SURICATTA_HAWKBIT) += test_json
tests-$(CONFIG_SURICATTA_HAWKBIT) += test_server_hawkbit
tests-$(CONFIG_MONGOOSE) += test_mongoose_upload
@@ -103,6 +106,12 @@ PREPARE_DATA:

$(obj)/test_verify.o: PREPARE_DATA $(DATADIR)/signature $(DATADIR)/signing-pubkey.pem

+ifeq ($(CONFIG_SSL_IMPL_OPENSSL),y)
+ifeq ($(CONFIG_SIGALG_CMS),y)
+$(obj)/test_verify.o: $(DATADIR)/signature.cms $(DATADIR)/cms-ca.cert.pem $(DATADIR)/cms-ca.crl.pem $(DATADIR)/cms-ca.crl.der $(DATADIR)/cms-ca.crl.empty.pem
+endif
+endif
+
.INTERMEDIATE: $(DATADIR)/signature
$(DATADIR)/signature: $(DATADIR)/to-be-signed $(DATADIR)/signing-secret.pem
$(if $(Q),@echo " SIGN $@")
@@ -118,6 +127,40 @@ $(DATADIR)/signing-secret.pem:
$(if $(Q),@echo " GEN $@")
$(Q)openssl genrsa -out $@ 2>/dev/null

+CMS_TEST_CA_DIR := $(DATADIR)/cms-test-ca
+CMS_TEST_CA_CONFIG := $(CMS_TEST_CA_DIR)/openssl.cnf
+
+$(DATADIR)/signature.cms $(DATADIR)/cms-ca.cert.pem $(DATADIR)/cms-ca.crl.pem $(DATADIR)/cms-ca.crl.der $(DATADIR)/cms-ca.crl.empty.pem &: $(DATADIR)/to-be-signed $(CMS_TEST_CA_CONFIG) | PREPARE_DATA
+ $(if $(Q),@echo " GEN CMS/CRL test data")
+ $(Q)mkdir -p $(CMS_TEST_CA_DIR)
+ $(Q)find $(CMS_TEST_CA_DIR) -mindepth 1 ! -name openssl.cnf -exec rm -rf {} +
+ $(Q)mkdir -p $(CMS_TEST_CA_DIR)/newcerts
+ $(Q)printf "1000\n" > $(CMS_TEST_CA_DIR)/serial
+ $(Q)printf "1000\n" > $(CMS_TEST_CA_DIR)/crlnumber
+ $(Q)touch $(CMS_TEST_CA_DIR)/index.txt
+ $(Q)openssl req -x509 -newkey rsa:2048 -nodes \
+ -keyout $(DATADIR)/cms-ca.key.pem -out $(DATADIR)/cms-ca.cert.pem \
+ -days 365 -sha256 -subj "/CN=SWUpdate Test CA" \
+ -addext "basicConstraints=critical,CA:true" \
+ -addext "keyUsage=critical,keyCertSign,cRLSign" > /dev/null 2>&1
+ $(Q)openssl req -new -newkey rsa:2048 -nodes \
+ -keyout $(DATADIR)/cms-signer.key.pem -out $(DATADIR)/cms-signer.csr.pem \
+ -subj "/CN=SWUpdate Test Signer" > /dev/null 2>&1
+ $(Q)openssl ca -batch -config $(CMS_TEST_CA_CONFIG) \
+ -in $(DATADIR)/cms-signer.csr.pem -out $(DATADIR)/cms-signer.cert.pem \
+ -days 365 > /dev/null 2>&1
+ $(Q)openssl cms -sign -in $(DATADIR)/to-be-signed -out $(DATADIR)/signature.cms \
+ -signer $(DATADIR)/cms-signer.cert.pem -inkey $(DATADIR)/cms-signer.key.pem \
+ -outform DER -nosmimecap -binary > /dev/null 2>&1
+ $(Q)openssl ca -gencrl -config $(CMS_TEST_CA_CONFIG) \
+ -out $(DATADIR)/cms-ca.crl.empty.pem > /dev/null 2>&1
+ $(Q)openssl ca -config $(CMS_TEST_CA_CONFIG) \
+ -revoke $(DATADIR)/cms-signer.cert.pem > /dev/null 2>&1
+ $(Q)openssl ca -gencrl -config $(CMS_TEST_CA_CONFIG) \
+ -out $(DATADIR)/cms-ca.crl.pem > /dev/null 2>&1
+ $(Q)openssl crl -in $(DATADIR)/cms-ca.crl.pem -out $(DATADIR)/cms-ca.crl.der \
+ -outform DER > /dev/null 2>&1
+
ifeq ($(CONFIG_PKCS11),y)
$(obj)/test_crypt_pkcs11.o: $(DATADIR)/softshm

diff --git a/test/data/.gitignore b/test/data/.gitignore
new file mode 100644
index 00000000..ae4bfdfc
--- /dev/null
+++ b/test/data/.gitignore
@@ -0,0 +1,5 @@
+*.pem
+*.der
+*.cms
+cms-test-ca/*
+!cms-test-ca/openssl.cnf
diff --git a/test/data/cms-test-ca/openssl.cnf b/test/data/cms-test-ca/openssl.cnf
new file mode 100644
index 00000000..0eb9d10f
--- /dev/null
+++ b/test/data/cms-test-ca/openssl.cnf
@@ -0,0 +1,31 @@
+# SPDX-FileCopyrightText: 2026 Daniel Braunwarth <daniel.b...@kuka.com>
+#
+# SPDX-License-Identifier: CC0-1.0
+
+[ ca ]
+default_ca = swupdate_test_ca
+[ swupdate_test_ca ]
+dir = test/data/cms-test-ca
+database = $dir/index.txt
+new_certs_dir = $dir/newcerts
+certificate = test/data/cms-ca.cert.pem
+private_key = test/data/cms-ca.key.pem
+serial = $dir/serial
+crlnumber = $dir/crlnumber
+default_md = sha256
+policy = policy_any
+x509_extensions = usr_cert
+copy_extensions = copy
+default_crl_days = 365
+[ policy_any ]
+commonName = supplied
+organizationName = optional
+organizationalUnitName = optional
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+emailAddress = optional
+[ usr_cert ]
+basicConstraints = critical,CA:FALSE
+keyUsage = critical,digitalSignature
+extendedKeyUsage = emailProtection
diff --git a/test/test_verify.c b/test/test_verify.c
index 1ed6793c..d72f3967 100644
--- a/test/test_verify.c
+++ b/test/test_verify.c
@@ -21,6 +21,7 @@
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
+#include <string.h>
#include <cmocka.h>

#include "swupdate_crypto.h"
@@ -44,11 +45,86 @@ static void test_verify_pkcs15(void **state)
assert_int_equal(error, 0);
}

+#if defined(CONFIG_SIGALG_CMS) && defined(CONFIG_SSL_IMPL_OPENSSL)
+static void test_verify_cms_without_crl(void **state)
+{
+ int error;
+ struct swupdate_cfg config;
+
+ (void)state;
+
+ memset(&config, 0, sizeof(config));
+ error = swupdate_dgst_init(&config, DATADIR "cms-ca.cert.pem");
+ assert_int_equal(error, 0);
+
+ error = swupdate_verify_file(config.dgst, DATADIR "signature.cms",
+ DATADIR "to-be-signed", NULL);
+ assert_int_equal(error, 0);
+}
+
+static void test_verify_cms_with_revoked_signer_crl(void **state)
+{
+ int error;
+ struct swupdate_cfg config;
+
+ (void)state;
+
+ memset(&config, 0, sizeof(config));
+ strlcpy(config.crlfname, DATADIR "cms-ca.crl.pem", sizeof(config.crlfname));
+ error = swupdate_dgst_init(&config, DATADIR "cms-ca.cert.pem");
+ assert_int_equal(error, 0);
+
+ error = swupdate_verify_file(config.dgst, DATADIR "signature.cms",
+ DATADIR "to-be-signed", NULL);
+ assert_int_not_equal(error, 0);
+}
+
+static void test_verify_cms_with_revoked_signer_der_crl(void **state)
+{
+ int error;
+ struct swupdate_cfg config;
+
+ (void)state;
+
+ memset(&config, 0, sizeof(config));
+ strlcpy(config.crlfname, DATADIR "cms-ca.crl.der", sizeof(config.crlfname));
+ error = swupdate_dgst_init(&config, DATADIR "cms-ca.cert.pem");
+ assert_int_equal(error, 0);
+
+ error = swupdate_verify_file(config.dgst, DATADIR "signature.cms",
+ DATADIR "to-be-signed", NULL);
+ assert_int_not_equal(error, 0);
+}
+
+static void test_verify_cms_with_nonrevoked_crl(void **state)
+{
+ int error;
+ struct swupdate_cfg config;
+
+ (void)state;
+
+ memset(&config, 0, sizeof(config));
+ strlcpy(config.crlfname, DATADIR "cms-ca.crl.empty.pem", sizeof(config.crlfname));
+ error = swupdate_dgst_init(&config, DATADIR "cms-ca.cert.pem");
+ assert_int_equal(error, 0);
+
+ error = swupdate_verify_file(config.dgst, DATADIR "signature.cms",
+ DATADIR "to-be-signed", NULL);
+ assert_int_equal(error, 0);
+}
+#endif
+
int main(void)
{
swupdate_crypto_init();
static const struct CMUnitTest verify_tests[] = {
cmocka_unit_test(test_verify_pkcs15),
+#if defined(CONFIG_SIGALG_CMS) && defined(CONFIG_SSL_IMPL_OPENSSL)
+ cmocka_unit_test(test_verify_cms_without_crl),
+ cmocka_unit_test(test_verify_cms_with_revoked_signer_crl),
+ cmocka_unit_test(test_verify_cms_with_revoked_signer_der_crl),
+ cmocka_unit_test(test_verify_cms_with_nonrevoked_crl),
+#endif
};
return cmocka_run_group_tests_name("verify", verify_tests, NULL, NULL);
}
--
2.43.0

Daniel Braunwarth

unread,
Jul 2, 2026, 6:17:15 AM (14 days ago) Jul 2
to swup...@googlegroups.com, Daniel Braunwarth
Otherwise it's not possible to ignored generated files in
subdirecotires, as mentioned on top of the file.

Signed-off-by: Daniel Braunwarth <daniel.b...@kuka.com>
---
.gitignore | 3 +++
test/data/.gitignore | 4 ++++
2 files changed, 7 insertions(+)

diff --git a/.gitignore b/.gitignore
index 8c490245..e3fc844c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -128,6 +128,9 @@ web-app/swupdate-www.tar.gz
!.gitlab-ci.yml
!.github

+# Git
+!.gitignore
+
# swupdateclient
Pipfile
Pipfile.lock
diff --git a/test/data/.gitignore b/test/data/.gitignore
index ae4bfdfc..6312ff1d 100644
--- a/test/data/.gitignore
+++ b/test/data/.gitignore
@@ -1,3 +1,7 @@
+# SPDX-FileCopyrightText: 2026 Daniel Braunwarth <daniel.b...@kuka.com>
+#
+# SPDX-License-Identifier: CC0-1.0
+
*.pem
*.der
*.cms
--
2.43.0

Daniel Braunwarth

unread,
Jul 2, 2026, 6:17:15 AM (14 days ago) Jul 2
to swup...@googlegroups.com, Daniel Braunwarth, Franz Heger
Add support to load certificate revocation lists for CMS verification
with the OpenSSL backend.

Wire a new crl-path setting into configuration and CLI parsing, load
CRLs from PEM or DER files into the X509 store, and enable CRL checks
during certificate verification.

Update signed images documentation to describe crl-path usage and add
PEM and DER examples.

Co-developed-by: Franz Heger <franz...@kuka.com>
Signed-off-by: Franz Heger <franz...@kuka.com>
Signed-off-by: Daniel Braunwarth <daniel.b...@kuka.com>
---
core/swupdate.c | 13 ++++
crypto/swupdate_cms_verify_openssl.c | 94 ++++++++++++++++++++++++++++
doc/source/signed_images.rst | 22 +++++++
include/swupdate.h | 1 +
4 files changed, 130 insertions(+)

diff --git a/core/swupdate.c b/core/swupdate.c
index e98301c7..213be587 100644
--- a/core/swupdate.c
+++ b/core/swupdate.c
@@ -100,6 +100,9 @@ static struct option long_options[] = {
{"cert-purpose", required_argument, NULL, '1'},
#if defined(CONFIG_SIGALG_CMS)
{"forced-signer-name", required_argument, NULL, '2'},
+#if defined(CONFIG_SSL_IMPL_OPENSSL)
+ {"crl-path", required_argument, NULL, '9'},
+#endif
#endif
#ifdef CONFIG_SIGALG_GPG
{"gpg-home-dir", required_argument, NULL, '4'},
@@ -173,6 +176,9 @@ static void usage(char *programname)
" --forced-signer-name <cn> : set expected common name of signer certificate\n"
#endif
" --ca-path : path to the Certificate Authority (PEM)\n"
+#if defined(CONFIG_SIGALG_CMS) && defined(CONFIG_SSL_IMPL_OPENSSL)
+ " --crl-path : path to the Certificate Revocation List (CRL, PEM or DER)\n"
+#endif
#ifdef CONFIG_SIGALG_GPG
" For GnuPG only:\n"
" --gpg-home-dir : path where the GPG ring and keys are stored\n"
@@ -383,6 +389,8 @@ static int read_globals_settings(void *elem, void *data)
"public-key-file", sw->publickeyfname);
GET_FIELD_STRING(LIBCFG_PARSER, elem,
"ca-path", sw->publickeyfname);
+ GET_FIELD_STRING(LIBCFG_PARSER, elem,
+ "crl-path", sw->crlfname);
GET_FIELD_STRING(LIBCFG_PARSER, elem,
"aes-key-file", sw->aeskeyfname);
GET_FIELD_STRING(LIBCFG_PARSER, elem,
@@ -841,6 +849,11 @@ int main(int argc, char **argv)
optarg,
sizeof(swcfg.decrypt_provider));
break;
+ case '9':
+ if (optarg) strlcpy(swcfg.crlfname,
+ optarg,
+ sizeof(swcfg.crlfname));
+ break;
#ifdef CONFIG_ENCRYPTED_IMAGES
case 'K':
if (optarg) strlcpy(swcfg.aeskeyfname,
diff --git a/crypto/swupdate_cms_verify_openssl.c b/crypto/swupdate_cms_verify_openssl.c
index d0a0bb02..e202817e 100644
--- a/crypto/swupdate_cms_verify_openssl.c
+++ b/crypto/swupdate_cms_verify_openssl.c
@@ -155,6 +155,91 @@ static X509_STORE *load_cert_chain(const char *file)
return castore;
}

+static void report_crl_err(unsigned long err, const char *crl_file)
+{
+ char err_buf[256] = {0};
+
+ ERR_error_string_n(err, err_buf, sizeof(err_buf));
+ ERROR("Malformed CRL in %s: %s", crl_file, err_buf);
+ ERR_clear_error();
+}
+
+static int add_crl_to_store(X509_STORE *castore, const char *crl_file)
+{
+ BIO *fp = BIO_new_file(crl_file, "rb");
+ if (!fp) {
+ TRACE("Unable to open CRL file %s", crl_file);
+ return 0;
+ }
+
+ int crl_count = 0;
+ X509_CRL *crl = NULL;
+ ERR_clear_error();
+ while ((crl = PEM_read_bio_X509_CRL(fp, NULL, NULL, NULL)) != NULL) {
+ crl_count++;
+ if (X509_STORE_add_crl(castore, crl) != 1) {
+ TRACE("Error adding CRL to store");
+ X509_CRL_free(crl);
+ BIO_free(fp);
+ return 0;
+ }
+
+ X509_CRL_free(crl);
+ }
+
+ unsigned long err = ERR_peek_last_error();
+ BIO_free(fp);
+
+ if (err) {
+ if (!(ERR_GET_LIB(err) == ERR_LIB_PEM &&
+ ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
+ report_crl_err(err, crl_file);
+ return 0;
+ }
+
+ ERR_clear_error();
+ }
+
+ if (crl_count == 0) {
+ fp = BIO_new_file(crl_file, "rb");
+ if (!fp) {
+ TRACE("Unable to reopen CRL file %s", crl_file);
+ return 0;
+ }
+
+ ERR_clear_error();
+ crl = d2i_X509_CRL_bio(fp, NULL);
+ err = ERR_peek_last_error();
+ BIO_free(fp);
+
+ if (!crl) {
+ if (err) {
+ report_crl_err(err, crl_file);
+ } else {
+ ERROR("No CRLs found in %s", crl_file);
+ }
+ return 0;
+ }
+
+ if (X509_STORE_add_crl(castore, crl) != 1) {
+ TRACE("Error adding CRL to store");
+ X509_CRL_free(crl);
+ return 0;
+ }
+
+ X509_CRL_free(crl);
+ crl_count = 1;
+ ERR_clear_error();
+ }
+
+ if (!X509_STORE_set_flags(castore, X509_V_FLAG_CRL_CHECK)) {
+ TRACE("Error setting CRL flags");
+ return 0;
+ }
+
+ return 1;
+}
+
static inline int next_common_name(X509_NAME *subject, int i)
{
return X509_NAME_get_index_by_NID(subject, NID_commonName, i);
@@ -319,6 +404,15 @@ static int openssl_cms_dgst_init(struct swupdate_cfg *sw, const char *keyfile)
goto dgst_init_error;
}

+ if (strlen(sw->crlfname)) {
+ TRACE("Loading CRL from %s", sw->crlfname);
+ if (!add_crl_to_store(dgst->certs, sw->crlfname)) {
+ ERROR("Error loading CRL from %s", sw->crlfname);
+ ret = -EINVAL;
+ goto dgst_init_error;
+ }
+ }
+
#ifndef CONFIG_CMS_IGNORE_CERTIFICATE_PURPOSE
{
static char code_sign_name[] = "Code signing";
diff --git a/doc/source/signed_images.rst b/doc/source/signed_images.rst
index ad11f1a1..d7ec5813 100644
--- a/doc/source/signed_images.rst
+++ b/doc/source/signed_images.rst
@@ -307,6 +307,28 @@ it is not possible to disable the check at runtime.
For RSA and CMS signing, the -k parameter (public key file) is mandatory and the program stops
if the public key is not passed. For CMS signing, CONFIG_SIGALG_CMS needs to be enabled.

+When using CMS with OpenSSL, revocation checking can be enabled by passing
+``--crl-path`` to SWUpdate. The file may contain one or more CRLs in PEM format,
+or a single CRL in DER format. If the signer certificate is revoked,
+verification fails.
+
+::
+
+ swupdate -i image.swu -k /etc/swupdate/ca-chain.pem \
+ --crl-path /etc/swupdate/revocations.pem
+
+For a DER-encoded CRL, pass the binary file directly:
+
+::
+
+ swupdate -i image.swu -k /etc/swupdate/ca-chain.pem \
+ --crl-path /etc/swupdate/revocations.der
+
+Revocation is only checked against the signer certificate. Revoking an
+intermediate CA in the trust chain would require the
+``X509_V_FLAG_CRL_CHECK_ALL`` verification flag, which SWUpdate does not
+currently set.
+
For GPG signing, CONFIG_SIGALG_GPG needs to be enabled. The GPG key will
need to be imported to the device's GnuPG home directory. To do this, the
key will need to be exported:
diff --git a/include/swupdate.h b/include/swupdate.h
index 9f98a29e..d6443e71 100644
--- a/include/swupdate.h
+++ b/include/swupdate.h
@@ -83,6 +83,7 @@ struct swupdate_cfg {
char output[SWUPDATE_GENERAL_STRING_SIZE];
char output_swversions[SWUPDATE_GENERAL_STRING_SIZE];
char publickeyfname[SWUPDATE_GENERAL_STRING_SIZE];
+ char crlfname[SWUPDATE_GENERAL_STRING_SIZE];
char aeskeyfname[SWUPDATE_GENERAL_STRING_SIZE];
char mtdblacklist[SWUPDATE_GENERAL_STRING_SIZE];
char forced_signer_name[SWUPDATE_GENERAL_STRING_SIZE];
--
2.43.0

Stefano Babic

unread,
Jul 12, 2026, 7:24:36 AM (4 days ago) Jul 12
to Daniel Braunwarth, swup...@googlegroups.com, Franz Heger
Hi Franz, Daniel,
Reviewed-by: Stefano Babic <stefan...@swupdate.org>

Stefano Babic

unread,
Jul 12, 2026, 7:26:02 AM (4 days ago) Jul 12
to Daniel Braunwarth, swup...@googlegroups.com, Franz Heger


On 7/2/26 12:14, 'Daniel Braunwarth' via swupdate wrote:
Reviewed-by: Stefano Babic <stefan...@swupdate.org>


Stefano Babic

unread,
Jul 12, 2026, 7:26:19 AM (4 days ago) Jul 12
to Daniel Braunwarth, swup...@googlegroups.com


On 7/2/26 12:14, 'Daniel Braunwarth' via swupdate wrote:
Reviewed-by: Stefano Babic <stefan...@swupdate.org>

Reply all
Reply to author
Forward
0 new messages