Compare commits

..

7 Commits

Author SHA1 Message Date
morganamilo
c7cb6e486b libalpm: fix importing keys from keyserver
commit 95a7d416ce 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.
2025-08-01 09:42:02 +01:00
morganamilo
a0be6f0829 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.
2025-08-01 09:41:59 +01:00
morganamilo
fff9296478 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)
2025-08-01 09:41:56 +01:00
morganamilo
f42b93dd7f 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)
<bertptrs@archlinux.org>" 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) <bertptrs@archlinux.org>" (38100C24376CD5F6ED4FF4B46918400C2703040C)
2025-08-01 06:15:06 +01:00
morganamilo
beb6bd80cd libalpm: set errno when key is missing from keyring
There was no fitting error type for this so add ALPM_ERR_KEY_MISSING.
2025-08-01 04:09:26 +01:00
morganamilo
48a784dde8 libalpm: fix -U when pkg exists but not sig 2025-06-21 23:39:57 +10:00
morganamilo
67e3a7be36 libalpm: fix regression in downloader
Fixes failure to finalize download path if the package file already
exists but the .sig file does not.

This patch also moves .sig.part files which should be done for
completeness although it's probably rare/inconsequential for them to
exist.

Hopefully this is now the right approach now. The logic is as follows:

  Check if dest_name or temp_name exists and try to move whichever
  does.

  If neither exist assume we're just downloading sig files and don't
  error.

  Figure out the .sig base filename.

  Try to move the .sig file if one was needed and if that fails try
  move the .sig.part file.

The patch leaves the logging as is. Maybe we should check if moves fail
for reasons other than non existence and log it properly. Though this is
probably rare and pacman will error out later anyway.

Fixes #256
2025-05-30 14:48:14 +01:00
13 changed files with 109 additions and 70 deletions

View File

@@ -292,9 +292,6 @@ Upgrade Options (apply to '-S' and '-U')[[UO]]
exclamation mark. Subsequent matches will override previous ones. A leading
literal exclamation mark or backslash needs to be escaped.
*\--preremove <package>::
Uninstall 'package' before performing the requested installation. Multiple
packages can be specified by separating them with commas.
Query Options (apply to '-Q')[[QO]]
-----------------------------------

View File

@@ -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 */
@@ -300,6 +304,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,

View File

@@ -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 : "<from .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) {
@@ -766,6 +768,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;
}

View File

@@ -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;
}
}

View File

@@ -1118,31 +1118,34 @@ static int finalize_download_locations(alpm_list_t *payloads, const char *localp
filename = payload->tempfile_name;
}
/* if neither file exists then the download failed and logged an error for us */
if(!filename) {
returnvalue = -1;
continue;
}
if(filename) {
int ret = move_file(filename, localpath);
int ret = move_file(filename, localpath);
if(ret == -1) {
/* ignore error if the file already existed - only signature file was downloaded */
if(payload->mtime_existing_file == 0) {
_alpm_log(payload->handle, ALPM_LOG_ERROR, _("could not move %s into %s (%s)\n"),
filename, localpath, strerror(errno));
returnvalue = -1;
if(ret == -1) {
if(payload->mtime_existing_file == 0) {
_alpm_log(payload->handle, ALPM_LOG_ERROR, _("could not move %s into %s (%s)\n"),
filename, localpath, strerror(errno));
returnvalue = -1;
}
}
}
if (payload->download_signature) {
const char sig_suffix[] = ".sig";
char *sig_filename = NULL;
size_t sig_filename_len = strlen(filename) + sizeof(sig_suffix);
MALLOC(sig_filename, sig_filename_len, continue);
snprintf(sig_filename, sig_filename_len, "%s%s", filename, sig_suffix);
move_file(sig_filename, localpath);
FREE(sig_filename);
char *sig_filename;
int ret;
filename = payload->destfile_name ? payload->destfile_name : payload->tempfile_name;
sig_filename = _alpm_get_fullpath("", filename, ".sig");
ASSERT(sig_filename, RET_ERR(payload->handle, ALPM_ERR_MEMORY, -1));
ret = move_file(sig_filename, localpath);
free(sig_filename);
if(ret == -1) {
sig_filename = _alpm_get_fullpath("", filename, ".sig.part");
ASSERT(sig_filename, RET_ERR(payload->handle, ALPM_ERR_MEMORY, -1));
move_file(sig_filename, localpath);
free(sig_filename);
}
}
}
return returnvalue;
@@ -1296,7 +1299,7 @@ download_signature:
return ret;
}
static char *filecache_find_url(alpm_handle_t *handle, const char *url)
static const char *url_basename(const char *url)
{
const char *filebase = strrchr(url, '/');
@@ -1309,7 +1312,7 @@ static char *filecache_find_url(alpm_handle_t *handle, const char *url)
return NULL;
}
return _alpm_filecache_find(handle, filebase);
return filebase;
}
int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
@@ -1331,9 +1334,26 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
for(i = urls; i; i = i->next) {
char *url = i->data;
char *filepath = NULL;
const char *urlbase = url_basename(url);
if(urlbase) {
/* attempt to find the file in our pkgcache */
filepath = _alpm_filecache_find(handle, urlbase);
if(filepath && (handle->siglevel & ALPM_SIG_PACKAGE)) {
char *sig_filename = _alpm_get_fullpath("", urlbase, ".sig");
/* if there's no .sig file then forget about the pkg file and go for download */
if(!_alpm_filecache_exists(handle, sig_filename)) {
free(filepath);
filepath = NULL;
}
free(sig_filename);
}
}
/* attempt to find the file in our pkgcache */
char *filepath = filecache_find_url(handle, url);
if(filepath) {
/* the file is locally cached so add it to the output right away */
alpm_list_append(fetched, filepath);

View File

@@ -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:
@@ -130,6 +134,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");

View File

@@ -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);
@@ -371,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) {
@@ -792,7 +798,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 +806,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));
@@ -821,8 +828,11 @@ 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");
key_invalid = 1;
__attribute__((fallthrough));
case ALPM_SIGSTATUS_VALID:
_alpm_log(handle, ALPM_LOG_DEBUG, "signature is valid\n");
switch(siglist->results[num].validity) {
case ALPM_SIGVALIDITY_FULL:
@@ -846,9 +856,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;
@@ -864,7 +877,7 @@ int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path,
free(siglist);
}
return ret;
return key_invalid ? -2 : ret;
}
/**
@@ -897,7 +910,6 @@ int _alpm_process_siglist(alpm_handle_t *handle, const char *identifier,
const char *name = result->key.uid ? result->key.uid : result->key.fingerprint;
switch(result->status) {
case ALPM_SIGSTATUS_VALID:
case ALPM_SIGSTATUS_KEY_EXPIRED:
switch(result->validity) {
case ALPM_SIGVALIDITY_FULL:
break;
@@ -923,6 +935,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

View File

@@ -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;
}
}
@@ -1053,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,

View File

@@ -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:

View File

@@ -150,7 +150,6 @@ int config_free(config_t *oldconfig)
FREELIST(oldconfig->noupgrade);
FREELIST(oldconfig->noextract);
FREELIST(oldconfig->overwrite_files);
FREELIST(oldconfig->preremove);
free(oldconfig->configfile);
free(oldconfig->sysroot);
free(oldconfig->rootdir);

View File

@@ -134,8 +134,6 @@ typedef struct __config_t {
/* our connection to libalpm */
alpm_handle_t *handle;
alpm_list_t *preremove;
alpm_list_t *explicit_adds;
alpm_list_t *explicit_removes;
@@ -216,8 +214,7 @@ enum {
OP_REFRESH,
OP_ASSUMEINSTALLED,
OP_DISABLEDLTIMEOUT,
OP_DISABLESANDBOX,
OP_PREREMOVE,
OP_DISABLESANDBOX
};
/* clean method */

View File

@@ -196,8 +196,6 @@ static void usage(int op, const char * const myname)
addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
addlist(_(" --ignoregroup <grp>\n"
" ignore a group upgrade (can be used more than once)\n"));
addlist(_(" --preremove <pkg>\n"));
addlist(_(" uninstall <pkg> before performing installation\n"));
__attribute__((fallthrough));
case PM_OP_REMOVE:
addlist(_(" -d, --nodeps skip dependency version checks (-dd to skip all checks)\n"));
@@ -772,9 +770,6 @@ static int parsearg_upgrade(int opt)
case OP_IGNOREGROUP:
parsearg_util_addlist(&(config->ignoregrp));
break;
case OP_PREREMOVE:
parsearg_util_addlist(&(config->preremove));
break;
case OP_DOWNLOADONLY:
case 'w':
config->op_s_downloadonly = 1;
@@ -987,7 +982,6 @@ static int parseargs(int argc, char *argv[])
{"color", required_argument, 0, OP_COLOR},
{"disable-download-timeout", no_argument, 0, OP_DISABLEDLTIMEOUT},
{"disable-sandbox", no_argument, 0, OP_DISABLESANDBOX},
{"preremove", required_argument, 0, OP_PREREMOVE},
{0, 0, 0, 0}
};

View File

@@ -717,21 +717,6 @@ static int sync_trans(alpm_list_t *targets)
}
}
for(i = config->preremove; i; i = i->next) {
alpm_pkg_t *pkg;
const char *pname = i->data;
alpm_db_t *db_local = alpm_get_localdb(config->handle);
if((pkg = alpm_db_get_pkg(db_local, pname)) != NULL) {
if(alpm_remove_pkg(config->handle, pkg) == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, "'%s': %s\n", pname, alpm_strerror(err));
retval = 1;
}
config->explicit_removes = alpm_list_add(config->explicit_removes, pkg);
}
}
if(retval) {
trans_release();
return retval;