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 0e333cf
Show file tree
Hide file tree
Showing 7 changed files with 413 additions and 1 deletion.
18 changes: 18 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**} \[*\--drop_backends*\] \[*--\include_backend_backend BACKEND*\] \[*--\include_backend_backend BACKEND*\]

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

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

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

Rebuild the key storage:

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

Recreate the public key storage. Update to the latest format and drop
unreadable keys. This will fail if there are public keys in other
storage backends.

Adding **\--drop_backends** will delete those keys during rebuilding.

Adding **\--include_backend BACKEND** will keep the keys from the given
backend and add it to the configured one during rebuilding.
The parameter can be given more than once. Valid values for the backends
are **rpmdb**, **fs**, **openpgp**. This can be used to convert from
one key storage to another.

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 0e333cf

Please sign in to comment.