Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ PKG_CHECK_MODULES(LIBXML2 libxml-2.0 REQUIRED)
FIND_PACKAGE(CURL 7.52.0 REQUIRED)

IF (USE_GPGME)
ADD_DEFINITIONS(-DUSE_GPGME)
PKG_SEARCH_MODULE(GPGME gpgme)
IF (NOT GPGME_FOUND)
FIND_PACKAGE(Gpgme REQUIRED)
Expand Down
47 changes: 40 additions & 7 deletions librepo/gpg_gpgme.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,19 +279,52 @@ lr_gpg_check_signature_fd(int signature_fd,

// Example of signature usage could be found in gpgme git repository
// in the gpgme/tests/run-verify.c
//
// For key rotation support: If the data is signed with multiple keys and at least
// one valid, non-expired signature exists, we should succeed. We only fail if ALL
// signatures are either invalid or expired.
gboolean found_valid_signature = FALSE;
gboolean found_expired_key = FALSE;
gboolean found_expired_sig = FALSE;
for (; sig; sig = sig->next) {
if ((sig->summary & GPGME_SIGSUM_VALID) || // Valid
(sig->summary & GPGME_SIGSUM_GREEN) || // Valid
(sig->summary == 0 && sig->status == GPG_ERR_NO_ERROR)) // Valid but key is not certified with a trusted signature
// Check if this signature is from an expired key or is itself expired
if (sig->summary & GPGME_SIGSUM_KEY_EXPIRED) {
g_debug("%s: Skipping signature with expired key", __func__);
found_expired_key = TRUE;
continue;
}
if (sig->summary & GPGME_SIGSUM_SIG_EXPIRED) {
g_debug("%s: Skipping expired signature", __func__);
found_expired_sig = TRUE;
continue;
}
if ((sig->summary & GPGME_SIGSUM_VALID) || // Valid
(sig->summary & GPGME_SIGSUM_GREEN) || // Valid
(sig->summary == 0 && sig->status == GPG_ERR_NO_ERROR))
// Valid but key is not certified with a trusted signature
{
gpgme_release(context);
return TRUE;
found_valid_signature = TRUE;
break;
}
}

gpgme_release(context);
g_debug("%s: Bad GPG signature", __func__);
g_set_error(err, LR_GPG_ERROR, LRE_BADGPG, "Bad GPG signature");

if (found_valid_signature) {
return TRUE;
}

// No valid signature found - report appropriate error
if (found_expired_key) {
g_debug("%s: GPG signature verification failed - key has expired", __func__);
g_set_error(err, LR_GPG_ERROR, LRE_BADGPG, "GPG signature verification failed - key has expired");
} else if (found_expired_sig) {
g_debug("%s: GPG signature verification failed - signature has expired", __func__);
g_set_error(err, LR_GPG_ERROR, LRE_BADGPG, "GPG signature verification failed - signature has expired");
} else {
g_debug("%s: Bad GPG signature", __func__);
g_set_error(err, LR_GPG_ERROR, LRE_BADGPG, "Bad GPG signature");
}
return FALSE;
}

Expand Down
18 changes: 18 additions & 0 deletions tests/test_data/expired_test/expired_key.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQENBAAAB+MBCADlMUlDwWp8ME7QY1ta4ZksRK8wRyYEvxCCzJLA3MrMIOUydAkQ
jTG2GEJJxYubF28Z48lhohhkxp1VOMNosaD2HzAJRc90fS6dhsqj3kbrJebxM56I
d7BKY+2sNE0rb3+MH6xUCrhOmhMaM9jWWbrJHpS5+GpWSfNDPyUJhHBGfAUWhfc8
6S3DanpRaz22krU0ZrehGLet8DPRaU6QLBuKDCt9WBGyrQpcYBdXTXqSB8sYDKhg
oz6F37jloS19hr7HKTFzT9Xwy7nirW+/JQXRS/YQvXkPu+zpFukHrFU0i4it57g6
835KDMxRIa7k2rpKaXX8JGaOIy5pDbVEB9gpABEBAAG0JmV4cGlyZWQgdGVzdCBr
ZXkgPGV4cGlyZWRAZXhhbXBsZS5vcmc+iQFYBBMBCgBCFiEEwjdX5ZlIYBxrPLaf
Xf+LndwbPB8FAgAAB+MDGy8EBQleDKwNBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4H
AheAAAoJEF3/i53cGzwfdusIAIW1Pf9X/wvP+BxOEUc7eb7INMEk2tYDb4W9Y8GR
hV/4iJ74Ld29zWrxYdjNevbXalhX4I3mDz0VeuCSxVxVBkORun5a/d9LdyjnKBdS
3D2QGqjmREg4D7tJf9reaiSNvtDlamf8c5Vs54Ce+Wcv02OvTWX4gF+B9M8QV0He
Fj/hINgRYDaMCDV6lMf3IkKV1CjUE9N6ZsuhSfkXRLDKLTM9prM+QhhZCwGPMlV2
UF1Dw/TEb7nRHvgWMqotJdWYv5bmCtTfEej31u5BGP7qgD4VXeaXC8YhTPpZupIo
Ku36bs1JFuNC1PmylB9w4kqWC7UctXcLWzlRlfXujjMaGyA=
=EnBs
-----END PGP PUBLIC KEY BLOCK-----
9 changes: 9 additions & 0 deletions tests/test_data/expired_test/expired_key_config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
%echo Generating expired test key
Key-Type: RSA
Key-Length: 2048
Name-Real: expired test key
Name-Email: [email protected]
Expire-Date: 2020-01-01
%no-protection
%commit
%echo done
1 change: 1 addition & 0 deletions tests/test_data/expired_test/test_data.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is test data for expired signature testing.
11 changes: 11 additions & 0 deletions tests/test_data/expired_test/test_data.txt.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN PGP SIGNATURE-----

iQEzBAABCgAdFiEEwjdX5ZlIYBxrPLafXf+LndwbPB8FAgAAB+MACgkQXf+Lndwb
PB/A3Qf/WMN0GRFeFVxOcGWkKEygs5FD3LI1pNdEV08gAhP3sFC4t55bTsLx7tsd
gpNxcW/VkW0hc7FgZT4uYoJVAlr01sPHslbNI4HZA6P6OTVtMHLrRH2ZZ9ae0d2z
xJc+fqiYxhRfbC8ERjDVt0HWiw1TxWKXb5scw80mxmkdaD6K1pTU1o+Qu90Q+IUT
g04hKSw1+WRT6UVcqsuR00eUDw01W5tcZNJ72X0rp0GCLC9JU51o8ZtKTmliKRTc
+PE5khUhwHnTV19Zp2JYLfGrFtAmvR3e5mpcRuRe7qYwZFXFBtkKR93hS3IpK9kl
Ac4ZWncPlkUPlIMq3Hiha9WvXZ712w==
=Szsq
-----END PGP SIGNATURE-----
80 changes: 80 additions & 0 deletions tests/test_gpg.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,85 @@ START_TEST(test_gpg_check_import_padded)
END_TEST


START_TEST(test_gpgme_expired_signature)
{
// This test verifies that expired GPG signatures are properly rejected
// using the gpgme backend.

#ifndef USE_GPGME
// Skip this test when not using gpgme backend
return;
#endif

gboolean ret;
char *valid_key_path, *valid_data_path, *valid_signature_path;
char *tmp_home_path;
GError *tmp_err = NULL;

tmp_home_path = lr_gettmpdir();

// First test with valid key/signature (sanity check)
valid_key_path = lr_pathconcat(test_globals.testdata_dir,
"repo_yum_01/repodata/repomd.xml.key.asc", NULL);
valid_data_path = lr_pathconcat(test_globals.testdata_dir,
"repo_yum_01/repodata/repomd.xml", NULL);
valid_signature_path = lr_pathconcat(test_globals.testdata_dir,
"repo_yum_01/repodata/repomd.xml.asc", NULL);

// Import the valid key first
ret = lr_gpg_import_key(valid_key_path, tmp_home_path, &tmp_err);
ck_assert(ret);
ck_assert_ptr_null(tmp_err);

// This should pass with a valid signature (sanity check)
ret = lr_gpg_check_signature(valid_signature_path,
valid_data_path,
tmp_home_path,
&tmp_err);
ck_assert_msg(ret, "Valid signature should pass: %s",
(tmp_err && tmp_err->message) ? tmp_err->message : "");
ck_assert_ptr_null(tmp_err);

// Clean up for expired key test
lr_remove_dir(tmp_home_path);
tmp_home_path = lr_gettmpdir();

// Now test with expired key
char *expired_key_path = lr_pathconcat(test_globals.testdata_dir, "expired_test/expired_key.asc", NULL);
char *expired_data_path = lr_pathconcat(test_globals.testdata_dir, "expired_test/test_data.txt", NULL);
char *expired_signature_path = lr_pathconcat(test_globals.testdata_dir, "expired_test/test_data.txt.asc", NULL);

// Import the expired key
ret = lr_gpg_import_key(expired_key_path, tmp_home_path, &tmp_err);
ck_assert(ret);
ck_assert_ptr_null(tmp_err);

// This should FAIL with expired signature - gpgme will reject it
ret = lr_gpg_check_signature(expired_signature_path,
expired_data_path,
tmp_home_path,
&tmp_err);
ck_assert_msg(!ret, "Expired signature should be rejected");
ck_assert_ptr_nonnull(tmp_err);

// Just verify that we get an appropriate error message (no specific text required)
ck_assert_msg(tmp_err->message != NULL, "Error message should not be NULL");
g_debug("GPG verification failed as expected: %s", tmp_err->message);

// Cleanup
lr_remove_dir(tmp_home_path);
lr_free(valid_key_path);
lr_free(valid_data_path);
lr_free(valid_signature_path);
lr_free(expired_key_path);
lr_free(expired_data_path);
lr_free(expired_signature_path);
g_free(tmp_home_path);
if (tmp_err) g_error_free(tmp_err);
}
END_TEST


Suite *
gpg_suite(void)
{
Expand All @@ -300,6 +379,7 @@ gpg_suite(void)
tcase_add_test(tc, test_gpg_check_armored_key_import_test_export);
tcase_add_test(tc, test_gpg_check_binary_key_import_test_export);
tcase_add_test(tc, test_gpg_check_import_padded);
tcase_add_test(tc, test_gpgme_expired_signature);
suite_add_tcase(s, tc);
return s;
}