Skip to content

Commit

Permalink
Implement rpmkeys --rebuild
Browse files Browse the repository at this point in the history
This rebuildis the public key storage. If _keyring is set to a new value
and there are keys left in an old store this fails with an error message.

The keys can either be included by using one or more instances of
--include_backend or dropped with --drop_backends.

Keys are stored in the latest format. Keys no longer accepted are
dropped.

Resolves: rpm-software-management#3347
  • Loading branch information
ffesti committed Dec 13, 2024
1 parent e53a2d5 commit 511d68f
Show file tree
Hide file tree
Showing 7 changed files with 405 additions and 1 deletion.
10 changes: 10 additions & 0 deletions docs/man/rpmkeys.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ The general forms of rpm digital signature commands are

**rpmkeys** {**-e\|\--erase\|-d\|\--delete**} *FINGERPRINT \...*

**rpmkeys** {**\--rebuild**}

**rpmkeys** {**-K\|\--checksig**} *PACKAGE\_FILE \...*

The **\--checksig** option checks all the digests and signatures
Expand Down Expand Up @@ -64,6 +66,14 @@ Erase the key(s) designated by *FINGERPRINT*. For example:

**rpmkeys** **\--erase 771b18d3d7baa28734333c424344591e1964c5fc**

Rebuild the key storage:

**rpmkeys** **\--rebuild**

If the storage backend was changed the keys will be moved. Outdated or
no longer supported keys will be dropped. Keys will be stored in the newest
storage format.

SEE ALSO
========

Expand Down
2 changes: 2 additions & 0 deletions include/rpm/rpmts.h
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,8 @@ rpmtsi rpmtsiInit(rpmts ts);
*/
rpmte rpmtsiNext(rpmtsi tsi, rpmElementTypes types);

rpmRC rpmtsRebuildKeystore(rpmts ts, char ** loadBackends, int flags);

#ifdef __cplusplus
}
#endif
Expand Down
96 changes: 96 additions & 0 deletions lib/keystore.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "system.h"

#include <filesystem>
#include <string>
#include <set>

#include <fcntl.h>
#include <unistd.h>
Expand All @@ -18,11 +20,13 @@
#include <rpm/rpmtypes.h>

#include "rpmts_internal.hh"
#include "rpmmacro_internal.hh"

#include "debug.h"

using std::string;
using namespace rpm;
namespace fs = std::filesystem;

static int makePubkeyHeader(rpmts ts, rpmPubkey key, Header * hdrp);

Expand Down Expand Up @@ -118,6 +122,42 @@ rpmRC rpm::check_backends(rpmtxn txn, rpmts ts)
return rc;
}

rpmRC rebuild_dir(rpmtxn txn, keystore * store, rpmKeyring keys)
{

auto macros = rpm::macros();
std::string keyringpath = rpm::expand_path({"%{_keyringpath}"});
std::string path = rpm::expand_path({rpmtxnRootDir(txn), keyringpath});
std::string oldpath = path + ".rpmold/";
std::error_code ec = {};
rpmRC rc = RPMRC_OK;
macros.push("_keyringpath", "", (keyringpath + ".tmp/").c_str(), 0);

rpmPubkey key = NULL;
auto iter = rpmKeyringInitIterator(keys, 0);
while ((rc == RPMRC_OK) && (key = rpmKeyringIteratorNext(iter))) {
rc = store->import_key(txn, key, 0, 0);
}
rpmKeyringIteratorFree(iter);

if (rc != RPMRC_OK)
goto exit;

/* Ignore errors from (re)moving old directory as it might not exist */
fs::rename(path, oldpath, ec);
fs::rename(path + ".tmp", path, ec);
if (ec) {
rpmlog(RPMLOG_ERR, _("can't move new key store to %s: %s.\n"), path.c_str(), ec.message().c_str());
rc = RPMRC_FAIL;
}
fs::remove_all(oldpath);

exit:
macros.pop("_keyringpath");

return rc;
}

/*****************************************************************************/

rpmRC keystore_fs::load_keys(rpmtxn txn, rpmKeyring keyring)
Expand Down Expand Up @@ -171,6 +211,11 @@ rpmRC keystore_fs::import_key(rpmtxn txn, rpmPubkey key, int replace, rpmFlags f
return rc;
}

rpmRC keystore_fs::rebuild(rpmtxn txn, rpmKeyring keys)
{
return rebuild_dir(txn, this, keys);
}

/*****************************************************************************/

static int acquire_write_lock(rpmtxn txn)
Expand Down Expand Up @@ -256,6 +301,12 @@ rpmRC keystore_openpgp_cert_d::import_key(rpmtxn txn, rpmPubkey key, int replace
return rc;
}

rpmRC keystore_openpgp_cert_d::rebuild(rpmtxn txn, rpmKeyring keys)
{
return rebuild_dir(txn, this, keys);
}


/*****************************************************************************/

rpmRC keystore_rpmdb::load_keys(rpmtxn txn, rpmKeyring keyring)
Expand Down Expand Up @@ -357,6 +408,51 @@ rpmRC keystore_rpmdb::import_key(rpmtxn txn, rpmPubkey key, int replace, rpmFlag
return rc;
}

rpmRC keystore_rpmdb::rebuild(rpmtxn txn, rpmKeyring keys)
{
rpmRC rc = RPMRC_OK;
std::set<unsigned int> packages = {};
Header h = NULL;
rpmPubkey key = NULL;

auto iter = rpmKeyringInitIterator(keys, 0);
while ((key = rpmKeyringIteratorNext(iter))) {
if (makePubkeyHeader(rpmtxnTs(txn), key, &h) != 0) {
rpmlog(RPMLOG_ERR, _("can't create header for key %s\n"),
rpmPubkeyFingerprintAsHex(key));
rc = RPMRC_FAIL;
break;
}
if ((rc = rpmtsImportHeader(txn, h, 0)) != RPMRC_OK) {
headerFree(h);
rpmlog(RPMLOG_ERR, _("can't add header to rpmdb for key %s\n"),
rpmPubkeyFingerprintAsHex(key));
break;
}
packages.insert(headerGetInstance(h));
headerFree(h);
}
rpmKeyringIteratorFree(iter);

if (rc != RPMRC_OK) {
return rc;
}

rpmdbMatchIterator mi = rpmtsInitIterator(rpmtxnTs(txn), RPMDBI_NAME, "gpg-pubkey", 0);
while ((h = rpmdbNextIterator(mi)) != NULL) {
if (!packages.count(headerGetInstance(h))) {
rpmRC rrc = rpmdbRemove(rpmtsGetRdb(rpmtxnTs(txn)), headerGetInstance(h)) ?
RPMRC_FAIL : RPMRC_OK;
if (rc != RPMRC_OK) {
rpmlog(RPMLOG_WARNING, "can't remove key %s", headerGetString(h, RPMTAG_NEVR));
rc = rrc;
}
}
}
rpmdbFreeIterator(mi);
return rc;
}

static void addGpgProvide(Header h, const char *n, const char *v)
{
rpmsenseFlags pflags = (RPMSENSE_KEYRING|RPMSENSE_EQUAL);
Expand Down
4 changes: 4 additions & 0 deletions lib/keystore.hh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public:
virtual rpmRC load_keys(rpmtxn txn, rpmKeyring keyring) = 0;
virtual rpmRC import_key(rpmtxn txn, rpmPubkey key, int replace = 1, rpmFlags flags = 0) = 0;
virtual rpmRC delete_key(rpmtxn txn, rpmPubkey key) = 0;
virtual rpmRC rebuild(rpmtxn txn, rpmKeyring keys) = 0;

virtual ~keystore() = default;
};
Expand All @@ -26,6 +27,7 @@ public:
virtual rpmRC load_keys(rpmtxn txn, rpmKeyring keyring);
virtual rpmRC import_key(rpmtxn txn, rpmPubkey key, int replace = 1, rpmFlags flags = 0);
virtual rpmRC delete_key(rpmtxn txn, rpmPubkey key);
virtual rpmRC rebuild(rpmtxn txn, rpmKeyring keys);

private:
rpmRC delete_key(rpmtxn txn, const std::string & keyid, const std::string & newname = "");
Expand All @@ -37,6 +39,7 @@ public:
virtual rpmRC load_keys(rpmtxn txn, rpmKeyring keyring);
virtual rpmRC import_key(rpmtxn txn, rpmPubkey key, int replace = 1, rpmFlags flags = 0);
virtual rpmRC delete_key(rpmtxn txn, rpmPubkey key);
virtual rpmRC rebuild(rpmtxn txn, rpmKeyring keys);

private:
rpmRC delete_key(rpmtxn txn, const std::string & keyid, unsigned int newinstance = 0);
Expand All @@ -48,6 +51,7 @@ public:
virtual rpmRC load_keys(rpmtxn txn, rpmKeyring keyring);
virtual rpmRC import_key(rpmtxn txn, rpmPubkey key, int replace = 1, rpmFlags flags = 0);
virtual rpmRC delete_key(rpmtxn txn, rpmPubkey key);
virtual rpmRC rebuild(rpmtxn txn, rpmKeyring keys);
};

}; /* namespace */
Expand Down
93 changes: 93 additions & 0 deletions lib/rpmts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include <fcntl.h>
#include <errno.h>

#include <filesystem>

#include <rpm/rpmtypes.h>
#include <rpm/rpmlib.h> /* rpmReadPackage etc */
#include <rpm/rpmmacro.h>
Expand All @@ -31,13 +33,15 @@
#include "rpmts_internal.hh"
#include "rpmte_internal.hh"
#include "rpmlog_internal.hh"
#include "rpmmacro_internal.hh"
#include "misc.hh"
#include "rpmtriggers.hh"

#include "debug.h"

using std::string;
using namespace rpm;
namespace fs = std::filesystem;

/**
* Iterator across transaction elements, forward on install, backward on erase.
Expand Down Expand Up @@ -396,6 +400,95 @@ rpmRC rpmtxnDeletePubkey(rpmtxn txn, rpmPubkey key)
return rc;
}

rpmRC rpmtsRebuildKeystore(rpmts ts, char ** loadBackends, int flags)
{
rpmRC rc = RPMRC_OK;
rpmtxn txn = rpmtxnBegin(ts, RPMTXN_WRITE);
rpmVSFlags oflags = rpmtsVSFlags(ts);

rpmtsOpenDB(ts, O_RDWR);

/* XXX keyring wont load if sigcheck disabled, force it temporarily */
rpmtsSetVSFlags(ts, (oflags & ~RPMVSF_MASK_NOSIGNATURES));

keystore * ts_keystore = getKeystore(ts);
rpmKeyring keyring = rpmKeyringNew();

keystore_fs ks_fs = {};
keystore_rpmdb ks_rpmdb = {};
keystore_openpgp_cert_d ks_opengpg = {};

for (keystore * ks: std::vector<keystore *>{&ks_fs, &ks_rpmdb, &ks_opengpg}) {
rpmKeyring kr = rpmKeyringNew();
ks->load_keys(txn, kr);

char ** name = loadBackends;
if (name) {
for (;*name; name++) {
if (!strcmp(*name, ks->get_name().c_str()))
break;

}
}

/* backend not to be included */
if (ks->get_name() != ts->keystore->get_name() && (!name || !*name)) {
if (!rpmKeyringIsEmpty(kr) && !flags) {
rpmlog(RPMLOG_ERR, _("public key backend %s is not empty\n"),
ks->get_name().c_str());
rpmKeyringFree(kr);
rc = RPMRC_FAIL;
goto exit;
}
rpmKeyringFree(kr);
continue;
}

rpmKeyringIterator iter = rpmKeyringInitIterator(kr, 0);
rpmPubkey key = NULL;
while ((key = rpmKeyringIteratorNext(iter)) != NULL) {
const unsigned char * pkt = NULL;
size_t pktlen = 0;
char *lints = NULL;

rpmPubkeyRawData(key, &pkt, &pktlen);
int lrc = pgpPubKeyLint(pkt, pktlen, &lints) ;
if (lints) {
if (lrc != RPMRC_OK)
rpmlog(RPMLOG_WARNING, "dropping public key %s:\n", rpmPubkeyFingerprintAsHex(key));
rpmlog(RPMLOG_WARNING, "%s\n", lints);
free(lints);
if (lrc != RPMRC_OK)
continue;
}
rpmKeyringModify(keyring, key, RPMKEYRING_MERGE);
}
rpmKeyringIteratorFree(iter);
rpmKeyringFree(kr);
}

rc = ts_keystore->rebuild(txn, keyring);

if (dynamic_cast<keystore_rpmdb*>(ts_keystore)) {
fs::remove_all(rpm::expand_path({rpmtxnRootDir(txn),
"%{_keyringpath}"}));
} else {
/* remove all gpg-pubkey packages */
rpmKeyring kr = rpmKeyringNew();
ks_rpmdb.rebuild(txn, kr);
rpmKeyringFree(kr);
}

exit:

ts->keyring = rpmKeyringFree(ts->keyring);
rpmKeyringFree(keyring);
rpmtxnEnd(txn);
rpmtsSetVSFlags(ts, oflags);

return rc;
}

rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen)
{
rpmRC rc = RPMRC_FAIL;
Expand Down
Loading

0 comments on commit 511d68f

Please sign in to comment.