From beb6bd80cd1463d4569a332c581ad57cecc9ede8 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Fri, 1 Aug 2025 04:09:26 +0100 Subject: [PATCH 1/5] libalpm: set errno when key is missing from keyring There was no fitting error type for this so add ALPM_ERR_KEY_MISSING. --- lib/libalpm/alpm.h | 2 ++ lib/libalpm/be_package.c | 1 + lib/libalpm/error.c | 2 ++ lib/libalpm/sync.c | 1 + 4 files changed, 6 insertions(+) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 96e5e643..ee967dbb 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -300,6 +300,8 @@ typedef enum _alpm_errno_t { ALPM_ERR_SIG_MISSING, /** Signatures are invalid */ ALPM_ERR_SIG_INVALID, + /** Keys are missing from keyring */ + ALPM_ERR_KEY_MISSING, /* Dependencies */ /** Dependencies could not be satisfied */ ALPM_ERR_UNSATISFIED_DEPS, diff --git a/lib/libalpm/be_package.c b/lib/libalpm/be_package.c index 0fecbcb5..a7ceb8a2 100644 --- a/lib/libalpm/be_package.c +++ b/lib/libalpm/be_package.c @@ -766,6 +766,7 @@ int SYMEXPORT alpm_pkg_load(alpm_handle_t *handle, const char *filename, int ful if(fail) { _alpm_log(handle, ALPM_LOG_ERROR, _("required key missing from keyring\n")); + handle->pm_errno = ALPM_ERR_KEY_MISSING; free(sigpath); return -1; } diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c index 58842eac..e59bdb5f 100644 --- a/lib/libalpm/error.c +++ b/lib/libalpm/error.c @@ -130,6 +130,8 @@ const char SYMEXPORT *alpm_strerror(alpm_errno_t err) return _("missing PGP signature"); case ALPM_ERR_SIG_INVALID: return _("invalid PGP signature"); + case ALPM_ERR_KEY_MISSING: + return _("PGP key missing from keyring"); /* Dependencies */ case ALPM_ERR_UNSATISFIED_DEPS: return _("could not satisfy dependencies"); diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c index 087c48de..63f5d500 100644 --- a/lib/libalpm/sync.c +++ b/lib/libalpm/sync.c @@ -975,6 +975,7 @@ static int check_keyring(alpm_handle_t *handle) EVENT(handle, &event); if(fail) { _alpm_log(handle, ALPM_LOG_ERROR, _("required key missing from keyring\n")); + handle->pm_errno = ALPM_ERR_KEY_MISSING; return -1; } } From f42b93dd7f23e63e06a7ccc197189602bfae4a09 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Fri, 1 Aug 2025 05:16:16 +0100 Subject: [PATCH 2/5] libalpm: improve error message for expired keys If the user does not update their system for a while some of the package keys may expire. Pacman gives this message: error: simdjson: signature from "Bert Peters (packager key) " is unknown trust While this is technically true it masks the real issue of the key being expired. This commit changes that error message to: error: simdjson: key "Bert Peters (packager key) " (38100C24376CD5F6ED4FF4B46918400C2703040C) --- lib/libalpm/signing.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c index e5ddd2d0..d6526514 100644 --- a/lib/libalpm/signing.c +++ b/lib/libalpm/signing.c @@ -821,8 +821,10 @@ int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, size_t num; for(num = 0; !ret && num < siglist->count; num++) { switch(siglist->results[num].status) { - case ALPM_SIGSTATUS_VALID: case ALPM_SIGSTATUS_KEY_EXPIRED: + _alpm_log(handle, ALPM_LOG_DEBUG, "key is expired\n"); + /* fallthrough */ + case ALPM_SIGSTATUS_VALID: _alpm_log(handle, ALPM_LOG_DEBUG, "signature is valid\n"); switch(siglist->results[num].validity) { case ALPM_SIGVALIDITY_FULL: @@ -896,8 +898,12 @@ int _alpm_process_siglist(alpm_handle_t *handle, const char *identifier, alpm_sigresult_t *result = siglist->results + i; const char *name = result->key.uid ? result->key.uid : result->key.fingerprint; switch(result->status) { - case ALPM_SIGSTATUS_VALID: case ALPM_SIGSTATUS_KEY_EXPIRED: + _alpm_log(handle, ALPM_LOG_ERROR, + _("%s: key \"%s\" (%s) is expired\n"), + identifier, name, result->key.fingerprint); + break; + case ALPM_SIGSTATUS_VALID: switch(result->validity) { case ALPM_SIGVALIDITY_FULL: break; From fff9296478e88b862e7fd3269ae260f2217164bd Mon Sep 17 00:00:00 2001 From: morganamilo Date: Fri, 1 Aug 2025 06:20:20 +0100 Subject: [PATCH 3/5] libalpm: improve error message for expired keys (cont) Continuation of last commit. Changes: error: failed to commit transaction (invalid or corrupted package (PGP signature)) To: error: failed to commit transaction (package signature has missing or invalid PGP key) --- lib/libalpm/alpm.h | 4 ++++ lib/libalpm/be_package.c | 8 +++++--- lib/libalpm/be_sync.c | 1 + lib/libalpm/error.c | 4 ++++ lib/libalpm/signing.c | 13 +++++++++---- lib/libalpm/sync.c | 1 + src/pacman/callback.c | 15 +++++++++++---- 7 files changed, 35 insertions(+), 11 deletions(-) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index ee967dbb..bd57d866 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -242,6 +242,8 @@ typedef enum _alpm_errno_t { ALPM_ERR_DB_INVALID, /** Database has an invalid signature */ ALPM_ERR_DB_INVALID_SIG, + /** Database is signed by an invalid key */ + ALPM_ERR_DB_INVALID_KEY, /** The localdb is in a newer/older format than libalpm expects */ ALPM_ERR_DB_VERSION, /** Failed to write to the database */ @@ -285,6 +287,8 @@ typedef enum _alpm_errno_t { ALPM_ERR_PKG_INVALID_CHECKSUM, /** Package has an invalid signature */ ALPM_ERR_PKG_INVALID_SIG, + /** Package is signed by an invalid key */ + ALPM_ERR_PKG_INVALID_KEY, /** Package does not have a signature */ ALPM_ERR_PKG_MISSING_SIG, /** Cannot open the package file */ diff --git a/lib/libalpm/be_package.c b/lib/libalpm/be_package.c index a7ceb8a2..3da80e24 100644 --- a/lib/libalpm/be_package.c +++ b/lib/libalpm/be_package.c @@ -341,15 +341,17 @@ int _alpm_pkg_validate_internal(alpm_handle_t *handle, /* even if we don't have a sig, run the check code if level tells us to */ if(level & ALPM_SIG_PACKAGE) { const char *sig = syncpkg ? syncpkg->base64_sig : NULL; + int ret; _alpm_log(handle, ALPM_LOG_DEBUG, "sig data: %s\n", sig ? sig : ""); if(!has_sig && !(level & ALPM_SIG_PACKAGE_OPTIONAL)) { handle->pm_errno = ALPM_ERR_PKG_MISSING_SIG; return -1; } - if(_alpm_check_pgp_helper(handle, pkgfile, sig, + ret = _alpm_check_pgp_helper(handle, pkgfile, sig, level & ALPM_SIG_PACKAGE_OPTIONAL, level & ALPM_SIG_PACKAGE_MARGINAL_OK, - level & ALPM_SIG_PACKAGE_UNKNOWN_OK, sigdata)) { - handle->pm_errno = ALPM_ERR_PKG_INVALID_SIG; + level & ALPM_SIG_PACKAGE_UNKNOWN_OK, sigdata); + if(ret) { + handle->pm_errno = ret == -1 ? ALPM_ERR_PKG_INVALID_SIG : ALPM_ERR_PKG_INVALID_KEY; return -1; } if(validation && has_sig) { diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c index 648a8bd6..25320687 100644 --- a/lib/libalpm/be_sync.c +++ b/lib/libalpm/be_sync.c @@ -126,6 +126,7 @@ static int sync_db_validate(alpm_db_t *db) db->status &= ~DB_STATUS_VALID; db->status |= DB_STATUS_INVALID; db->handle->pm_errno = ALPM_ERR_DB_INVALID_SIG; + db->handle->pm_errno = ret == -1 ? ALPM_ERR_PKG_INVALID_SIG : ALPM_ERR_PKG_INVALID_KEY; return 1; } } diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c index e59bdb5f..f004b03d 100644 --- a/lib/libalpm/error.c +++ b/lib/libalpm/error.c @@ -72,6 +72,8 @@ const char SYMEXPORT *alpm_strerror(alpm_errno_t err) return _("invalid or corrupted database"); case ALPM_ERR_DB_INVALID_SIG: return _("invalid or corrupted database (PGP signature)"); + case ALPM_ERR_DB_INVALID_KEY: + return _("database signature has missing or invalid PGP key"); case ALPM_ERR_DB_VERSION: return _("database is incorrect version"); case ALPM_ERR_DB_WRITE: @@ -115,6 +117,8 @@ const char SYMEXPORT *alpm_strerror(alpm_errno_t err) return _("invalid or corrupted package (checksum)"); case ALPM_ERR_PKG_INVALID_SIG: return _("invalid or corrupted package (PGP signature)"); + case ALPM_ERR_PKG_INVALID_KEY: + return _("package signature has missing or invalid PGP key"); case ALPM_ERR_PKG_MISSING_SIG: return _("package missing required signature"); case ALPM_ERR_PKG_OPEN: diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c index d6526514..c6694e7b 100644 --- a/lib/libalpm/signing.c +++ b/lib/libalpm/signing.c @@ -792,7 +792,7 @@ char *_alpm_sigpath(alpm_handle_t *handle, const char *path) * @param marginal whether signatures with marginal trust are acceptable * @param unknown whether signatures with unknown trust are acceptable * @param sigdata a pointer to storage for signature results - * @return 0 on success, -1 on error (consult pm_errno or sigdata) + * @return 0 on success, -1 on error, -2 on key error (consult pm_errno or sigdata) */ int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, const char *base64_sig, int optional, int marginal, int unknown, @@ -800,6 +800,7 @@ int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, { alpm_siglist_t *siglist; int ret; + int key_invalid = 0; CALLOC(siglist, 1, sizeof(alpm_siglist_t), RET_ERR(handle, ALPM_ERR_MEMORY, -1)); @@ -823,7 +824,8 @@ int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, switch(siglist->results[num].status) { case ALPM_SIGSTATUS_KEY_EXPIRED: _alpm_log(handle, ALPM_LOG_DEBUG, "key is expired\n"); - /* fallthrough */ + key_invalid = 1; + __attribute__((fallthrough)); case ALPM_SIGSTATUS_VALID: _alpm_log(handle, ALPM_LOG_DEBUG, "signature is valid\n"); switch(siglist->results[num].validity) { @@ -848,9 +850,12 @@ int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, break; } break; - case ALPM_SIGSTATUS_SIG_EXPIRED: case ALPM_SIGSTATUS_KEY_UNKNOWN: case ALPM_SIGSTATUS_KEY_DISABLED: + case ALPM_SIGSTATUS_SIG_EXPIRED: + _alpm_log(handle, ALPM_LOG_DEBUG, "key is not valid\n"); + key_invalid = 1; + __attribute__((fallthrough)); case ALPM_SIGSTATUS_INVALID: _alpm_log(handle, ALPM_LOG_DEBUG, "signature is not valid\n"); ret = -1; @@ -866,7 +871,7 @@ int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, free(siglist); } - return ret; + return key_invalid ? -2 : ret; } /** diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c index 63f5d500..2159817e 100644 --- a/lib/libalpm/sync.c +++ b/lib/libalpm/sync.c @@ -1054,6 +1054,7 @@ static int check_validity(alpm_handle_t *handle, _("%s: missing required signature\n"), v->pkg->name); break; case ALPM_ERR_PKG_INVALID_SIG: + case ALPM_ERR_PKG_INVALID_KEY: _alpm_process_siglist(handle, v->pkg->name, v->siglist, v->siglevel & ALPM_SIG_PACKAGE_OPTIONAL, v->siglevel & ALPM_SIG_PACKAGE_MARGINAL_OK, diff --git a/src/pacman/callback.c b/src/pacman/callback.c index 77874760..523b1cd4 100644 --- a/src/pacman/callback.c +++ b/src/pacman/callback.c @@ -529,10 +529,17 @@ void cb_question(void *ctx, alpm_question_t *question) case ALPM_QUESTION_CORRUPTED_PKG: { alpm_question_corrupted_t *q = &question->corrupted; - q->remove = yesno(_("File %s is corrupted (%s).\n" - "Do you want to delete it?"), - q->filepath, - alpm_strerror(q->reason)); + if(q->reason == ALPM_ERR_PKG_INVALID_KEY || q->reason == ALPM_ERR_DB_INVALID_KEY) { + q->remove = yesno(_("Can't get PGP key for file %s (%s)\n" + "Do you want to delete it?"), + q->filepath, + alpm_strerror(q->reason)); + } else { + q->remove = yesno(_("File %s is corrupted (%s).\n" + "Do you want to delete it?"), + q->filepath, + alpm_strerror(q->reason)); + } } break; case ALPM_QUESTION_IMPORT_KEY: From a0be6f0829ab171ad2f9528413041ea32573e602 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Fri, 1 Aug 2025 07:46:23 +0100 Subject: [PATCH 4/5] libalpm: reimport expired keys If the user does not update for a while some of the keys in the keyring may expire. Pacman does not import new versions of these keys because they are already in the keying. This leads to users needing to first update archlinux-keyring to get the new keys. --- lib/libalpm/signing.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c index c6694e7b..19aeaa5a 100644 --- a/lib/libalpm/signing.c +++ b/lib/libalpm/signing.c @@ -233,9 +233,14 @@ int _alpm_key_in_keychain(alpm_handle_t *handle, const char *fpr) _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n"); ret = 0; } else if(gpg_err_code(gpg_err) == GPG_ERR_NO_ERROR) { - _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup success, key exists\n"); - handle->known_keys = alpm_list_add(handle->known_keys, strdup(fpr)); - ret = 1; + if(key->expired) { + _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup success, but key is expired\n"); + ret = 0; + } else { + _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup success, key exists\n"); + handle->known_keys = alpm_list_add(handle->known_keys, strdup(fpr)); + ret = 1; + } } else { _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(gpg_err)); } @@ -268,7 +273,7 @@ static int key_import_wkd(alpm_handle_t *handle, const char *email, const char * CHECK_ERR(); mode = gpgme_get_keylist_mode(ctx); - mode |= GPGME_KEYLIST_MODE_LOCATE; + mode |= GPGME_KEYLIST_MODE_LOCATE_EXTERNAL; gpg_err = gpgme_set_keylist_mode(ctx, mode); CHECK_ERR(); @@ -279,7 +284,7 @@ static int key_import_wkd(alpm_handle_t *handle, const char *email, const char * if(fpr && _alpm_key_in_keychain(handle, fpr)) { ret = 0; } else { - _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed: WKD imported wrong fingerprint\n"); + _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed: WKD imported wrong fingerprint or key expired\n"); } } gpgme_key_unref(key); @@ -903,11 +908,6 @@ int _alpm_process_siglist(alpm_handle_t *handle, const char *identifier, alpm_sigresult_t *result = siglist->results + i; const char *name = result->key.uid ? result->key.uid : result->key.fingerprint; switch(result->status) { - case ALPM_SIGSTATUS_KEY_EXPIRED: - _alpm_log(handle, ALPM_LOG_ERROR, - _("%s: key \"%s\" (%s) is expired\n"), - identifier, name, result->key.fingerprint); - break; case ALPM_SIGSTATUS_VALID: switch(result->validity) { case ALPM_SIGVALIDITY_FULL: @@ -934,6 +934,16 @@ int _alpm_process_siglist(alpm_handle_t *handle, const char *identifier, identifier, name); break; } + break; + case ALPM_SIGSTATUS_KEY_EXPIRED: + _alpm_log(handle, ALPM_LOG_ERROR, + _("%s: key \"%s\" (%s) is expired\n"), + identifier, name, result->key.fingerprint); + + if(_alpm_key_import(handle, result->key.uid, result->key.fingerprint) == 0) { + retry = 1; + } + break; case ALPM_SIGSTATUS_KEY_UNKNOWN: /* ensure this key is still actually unknown; we may have imported it From c7cb6e486b75e74e2eb76dff256b529f54bf254d Mon Sep 17 00:00:00 2001 From: morganamilo Date: Fri, 1 Aug 2025 09:20:19 +0100 Subject: [PATCH 5/5] libalpm: fix importing keys from keyserver commit 95a7d416ce50446339c10c59b9ea25125a816adf broke key_search_keyserver() by removing the `ret = 1` at the end of the function, causing the caller to allways think the funciton failed even when it did not. Due to WKD being the primary method to import keys this was unnoticed. --- lib/libalpm/signing.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c index 19aeaa5a..32aba2e1 100644 --- a/lib/libalpm/signing.c +++ b/lib/libalpm/signing.c @@ -376,6 +376,7 @@ static int key_search_keyserver(alpm_handle_t *handle, const char *fpr, pgpkey->expires = key->subkeys->expires; pgpkey->length = key->subkeys->length; pgpkey->revoked = key->subkeys->revoked; + ret = 1; gpg_error: if(ret != 1) {