Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement rpmkeys --rebuild #3474

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
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
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
19 changes: 18 additions & 1 deletion include/rpm/rpmkeyring.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ extern "C" {
typedef enum rpmKeyringModifyMode_e {
RPMKEYRING_ADD = 1,
RPMKEYRING_REPLACE = 2,
RPMKEYRING_DELETE = 3
RPMKEYRING_DELETE = 3,
RPMKEYRING_MERGE = 4,
} rpmKeyringModifyMode;


Expand All @@ -39,6 +40,13 @@ rpmKeyring rpmKeyringNew(void);
*/
rpmKeyring rpmKeyringFree(rpmKeyring keyring);

/** \ingroup rpmkeyring
* Check if there are no keys in the keyring.
* @param keyring keyring handle
* @return 1 if keyring is empy, 0 otherwise
*/
int rpmKeyringIsEmpty(rpmKeyring keyring);

/** \ingroup rpmkeyring
* Add a public key to keyring.
* @param keyring keyring handle
Expand Down Expand Up @@ -133,6 +141,15 @@ rpmPubkey rpmPubkeyFree(rpmPubkey key);
*/
rpmPubkey rpmPubkeyLink(rpmPubkey key);


/** \ingroup rpmkeyring
* Return pubkey as raw bytes
* @param key Pubkey
* @param pkt key data
* @param pktlen Length of key data
*/
void rpmPubkeyRawData(rpmPubkey key, unsigned const char ** pkt, size_t * pktlen);

/** \ingroup rpmkeyring
* Return base64 encoding of pubkey
* @param key Pubkey
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
117 changes: 117 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 @@ -96,6 +100,63 @@ static rpmRC write_key_to_disk(rpmPubkey key, string & dir, string & filename, i
return rc;
}

rpmRC rpm::check_backends(rpmtxn txn, rpmts ts)
{
rpmRC rc = RPMRC_OK;

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}) {
if (ks->get_name() == ts->keystore->get_name())
continue;
rpmKeyring keyring = rpmKeyringNew();
ks->load_keys(txn, keyring);
if (!rpmKeyringIsEmpty(keyring)) {
rpmlog(RPMLOG_WARNING, _("there are public keys in the %s backend which is not the one configured (%s); use rpmkeys --rebuild to integrate or discard them\n"), ks->get_name().c_str(), ts->keystore->get_name().c_str());
rc = RPMRC_FAIL;
}
rpmKeyringFree(keyring);
}
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will stop on first error, which seems contrary to the goal of weeding out bad keys with warning.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... Also this suggests that there shoudn't perhaps be a separate keystore-specific rebuild operation, ie it should just be handled by the rpmts-level rebuild - if we use the rpmdb backend as a model, that doesn't have a backend-specific rebuild operation either.

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

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

Expand Down Expand Up @@ -150,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 @@ -235,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 @@ -336,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
10 changes: 10 additions & 0 deletions lib/keystore.hh
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,50 @@

namespace rpm {

rpmRC check_backends(rpmtxn txn, rpmts ts);

class keystore {
public:
virtual std::string get_name() { return "None"; };
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;
};

class keystore_fs : public keystore {
public:
virtual std::string get_name() { return "fs"; };
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 = "");
};

class keystore_rpmdb : public keystore {
public:
virtual std::string get_name() { return "rpmdb"; };
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);
};

class keystore_openpgp_cert_d : public keystore {
public:
virtual std::string get_name() { return "openpgp"; };
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
Loading
Loading