diff --git a/docs/man/rpmkeys.8.md b/docs/man/rpmkeys.8.md index 1b27f06247..468eec5c65 100644 --- a/docs/man/rpmkeys.8.md +++ b/docs/man/rpmkeys.8.md @@ -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 @@ -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 ======== diff --git a/include/rpm/rpmts.h b/include/rpm/rpmts.h index 86ca62e0ef..6814d5c9b9 100644 --- a/include/rpm/rpmts.h +++ b/include/rpm/rpmts.h @@ -754,6 +754,8 @@ rpmtsi rpmtsiInit(rpmts ts); */ rpmte rpmtsiNext(rpmtsi tsi, rpmElementTypes types); +rpmRC rpmtsRebuildKeystore(rpmts ts); + #ifdef __cplusplus } #endif diff --git a/lib/keystore.cc b/lib/keystore.cc index a64ef07d6a..65b64f7deb 100644 --- a/lib/keystore.cc +++ b/lib/keystore.cc @@ -1,6 +1,8 @@ #include "system.h" +#include #include +#include #include #include @@ -18,11 +20,13 @@ #include #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); @@ -96,6 +100,50 @@ static rpmRC write_key_to_disk(rpmPubkey key, string & dir, string & filename, i return rc; } +rpmRC rebuild_dir(rpmtxn txn, keystore * store, rpmKeyring keys) +{ + + auto macros = rpm::macros(); + auto [mrc, keyringpath] = macros.expand("%{_keyringpath}"); + while ('/' == keyringpath.back()) + keyringpath.pop_back(); + std::string path = rpm::expand_path({rpmtxnRootDir(txn), keyringpath}); + std::string tmppath = keyringpath + ".tmp/"; + std::string oldpath = keyringpath + ".rpmold/"; + std::error_code ec = {}; + rpmRC rc = RPMRC_OK; + + macros.push("_keyringpath", "", tmppath.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(rpm::join_path({std::string(rpmtxnRootDir(txn)), keyringpath}), + rpm::join_path({rpmtxnRootDir(txn), oldpath}), ec); + fs::rename(rpm::join_path({std::string(rpmtxnRootDir(txn)), + keyringpath + ".tmp"}), + rpm::join_path({rpmtxnRootDir(txn), keyringpath}), ec); + if (ec) { + rpmlog(RPMLOG_ERR, _("can't move new key store to %s: %s.\n"), rpm::join_path({rpmtxnRootDir(txn), keyringpath}).c_str(), ec.message().c_str()); + rc = RPMRC_FAIL; + } + fs::remove_all(rpm::join_path({rpmtxnRootDir(txn), oldpath})); + + exit: + macros.pop("_keyringpath"); + + return rc; +} + + /*****************************************************************************/ @@ -150,6 +198,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) @@ -235,6 +288,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) @@ -330,6 +389,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 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); diff --git a/lib/keystore.hh b/lib/keystore.hh index 53fd53530a..bf1b69cef4 100644 --- a/lib/keystore.hh +++ b/lib/keystore.hh @@ -13,6 +13,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; }; @@ -22,6 +23,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 = ""); @@ -32,6 +34,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); @@ -42,6 +45,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 */ diff --git a/lib/rpmts.cc b/lib/rpmts.cc index 649c10cb46..5a749bae19 100644 --- a/lib/rpmts.cc +++ b/lib/rpmts.cc @@ -9,6 +9,8 @@ #include #include +#include + #include #include /* rpmReadPackage etc */ #include @@ -31,6 +33,7 @@ #include "rpmts_internal.hh" #include "rpmte_internal.hh" #include "rpmlog_internal.hh" +#include "rpmmacro_internal.hh" #include "misc.hh" #include "rpmtriggers.hh" @@ -38,6 +41,7 @@ using std::string; using namespace rpm; +namespace fs = std::filesystem; /** * Iterator across transaction elements, forward on install, backward on erase. @@ -395,6 +399,77 @@ rpmRC rpmtxnDeletePubkey(rpmtxn txn, rpmPubkey key) return rc; } +rpmRC rpmtsRebuildKeystore(rpmts ts) +{ + rpmRC rc = RPMRC_OK; + rpmtxn txn = rpmtxnBegin(ts, RPMTXN_WRITE); + rpmVSFlags oflags = rpmtsVSFlags(ts); + + ts->keyring = rpmKeyringFree(ts->keyring); + rpmtsOpenDB(ts, O_RDWR); + + /* XXX keyring wont load if sigcheck disabled, force it temporarily */ + rpmtsSetVSFlags(ts, (oflags & ~RPMVSF_MASK_NOSIGNATURES)); + + rpmKeyring keyring = rpmKeyringNew(); + + keystore_fs ks_fs = {}; + rpmKeyring keyring_fs = rpmKeyringNew(); + ks_fs.load_keys(txn, keyring); + + keystore_rpmdb ks_rpmdb = {}; + rpmKeyring keyring_rpmdb = rpmKeyringNew(); + ks_rpmdb.load_keys(txn, keyring_rpmdb); + + keystore_openpgp_cert_d ks_opengpg = {}; + rpmKeyring keyring_openpgp = rpmKeyringNew(); + ks_opengpg.load_keys(txn, keyring_openpgp); + + for (auto kr: {keyring_fs, keyring_rpmdb, keyring_openpgp}) { + rpmKeyringIterator iter = rpmKeyringInitIterator(kr, 0); + rpmPubkey key = NULL; + const unsigned char * pkt = NULL; + size_t pktlen = 0; + char *lints = NULL; + while ((key = rpmKeyringIteratorNext(iter)) != 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); + } + + keystore * ks = getKeystore(ts); + rc = ks->rebuild(txn, keyring); + + if (dynamic_cast(ks)) { + 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); + } + + rpmKeyringFree(keyring); + rpmKeyringFree(keyring_fs); + rpmKeyringFree(keyring_rpmdb); + rpmKeyringFree(keyring_openpgp); + rpmtxnEnd(txn); + rpmtsSetVSFlags(ts, oflags); + + return rc; +} + rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen) { rpmRC rc = RPMRC_FAIL; diff --git a/tests/rpmsigdig.at b/tests/rpmsigdig.at index 63bef22c15..ad1b87a6d0 100644 --- a/tests/rpmsigdig.at +++ b/tests/rpmsigdig.at @@ -1846,3 +1846,124 @@ rpm -qp --qf "[%{filenames}:%{filesignatures}\n]" /data/RPMS/imatest-1.0-1.fc34. [ignore]) RPMTEST_CLEANUP + +AT_SETUP([keyring rebuild rpmdb]) +AT_KEYWORDS([rpmkeys signature]) +RPMDB_INIT + +runroot rpmkeys \ + --define "_keyring rpmdb" \ + --import /data/keys/rpm.org-rsa-2048-test.pub +runroot rpmkeys \ + --define "_keyring rpmdb" \ + --import /data/keys/rpm.org-ed25519-test.pub +runroot rpmkeys \ + --define "_keyring fs" \ + --import /data/keys/rpm.org-rsa-2048-test.pub +runroot rpmkeys \ + --define "_keyring openpgp" \ + --import /data/keys/rpm.org-rsa-2048-add-subkey.asc +runroot rpmkeys \ + --define "_keyring fs" \ + --import /data/keys/alice.asc + +RPMTEST_CHECK([[ +runroot rpmkeys --define "_keyring rpmdb" --rebuild +runroot rpm -qa gpg-pubkey +runroot rpmkeys --define "_keyring rpmdb" --list +echo "===============================================" +runroot rpmkeys --define "_keyring fs" --list +runroot rpmkeys --define "_keyring openpgp" --list +runroot rpmkeys --define "_keyring rpmdb" -Kv /data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm | grep "Header OpenPGP" +]], +[0], +[gpg-pubkey-771b18d3d7baa28734333c424344591e1964c5fc-58e63918 +gpg-pubkey-152bb32fd9ca982797e835cfb0645aec757bf69e-661d22a8 +gpg-pubkey-b6542f92f30650c36b6f41bcb3a771bfeb04e625-62521e00 +771b18d3d7baa28734333c424344591e1964c5fc rpm.org RSA testkey public key +152bb32fd9ca982797e835cfb0645aec757bf69e rpm.org ed25519 testkey public key +b6542f92f30650c36b6f41bcb3a771bfeb04e625 Alice public key +=============================================== + Header OpenPGP V4 EdDSA/SHA512 signature, key fingerprint: 771b18d3d7baa28734333c424344591e1964c5fc: OK +], +[]) + +RPMTEST_CLEANUP + +AT_SETUP([keyring rebuild fs]) +AT_KEYWORDS([rpmkeys signature]) +RPMDB_INIT + +runroot rpmkeys \ + --define "_keyring rpmdb" \ + --import /data/keys/rpm.org-rsa-2048-test.pub +runroot rpmkeys \ + --define "_keyring rpmdb" \ + --import /data/keys/rpm.org-ed25519-test.pub +runroot rpmkeys \ + --define "_keyring fs" \ + --import /data/keys/rpm.org-rsa-2048-test.pub +runroot rpmkeys \ + --define "_keyring openpgp" \ + --import /data/keys/rpm.org-rsa-2048-add-subkey.asc +runroot rpmkeys \ + --define "_keyring fs" \ + --import /data/keys/alice.asc + +RPMTEST_CHECK([[ +runroot rpmkeys --define "_keyring fs" --rebuild +runroot rpmkeys --define "_keyring fs" --list +echo "===============================================" +runroot rpmkeys --define "_keyring rpmdb" --list +runroot rpmkeys --define "_keyring openpgp" --list +runroot rpmkeys --define "_keyring fs" -Kv /data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm | grep "Header OpenPGP" +]], +[0], +[771b18d3d7baa28734333c424344591e1964c5fc rpm.org RSA testkey public key +152bb32fd9ca982797e835cfb0645aec757bf69e rpm.org ed25519 testkey public key +b6542f92f30650c36b6f41bcb3a771bfeb04e625 Alice public key +=============================================== + Header OpenPGP V4 EdDSA/SHA512 signature, key fingerprint: 771b18d3d7baa28734333c424344591e1964c5fc: OK +], +[]) + +RPMTEST_CLEANUP + +AT_SETUP([keyring rebuild openpgp]) +AT_KEYWORDS([rpmkeys signature]) +RPMDB_INIT + +runroot rpmkeys \ + --define "_keyring rpmdb" \ + --import /data/keys/rpm.org-rsa-2048-test.pub +runroot rpmkeys \ + --define "_keyring rpmdb" \ + --import /data/keys/rpm.org-ed25519-test.pub +runroot rpmkeys \ + --define "_keyring fs" \ + --import /data/keys/rpm.org-rsa-2048-test.pub +runroot rpmkeys \ + --define "_keyring openpgp" \ + --import /data/keys/rpm.org-rsa-2048-add-subkey.asc +runroot rpmkeys \ + --define "_keyring fs" \ + --import /data/keys/alice.asc + +RPMTEST_CHECK([[ +runroot rpmkeys --define "_keyring openpgp" --rebuild +runroot rpmkeys --define "_keyring openpgp" --list +echo "===============================================" +runroot rpmkeys --define "_keyring fs" --list +runroot rpmkeys --define "_keyring rpmdb" --list +runroot rpmkeys --define "_keyring openpgp" -Kv /data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm | grep "Header OpenPGP" +]], +[0], +[771b18d3d7baa28734333c424344591e1964c5fc rpm.org RSA testkey public key +152bb32fd9ca982797e835cfb0645aec757bf69e rpm.org ed25519 testkey public key +b6542f92f30650c36b6f41bcb3a771bfeb04e625 Alice public key +=============================================== + Header OpenPGP V4 EdDSA/SHA512 signature, key fingerprint: 771b18d3d7baa28734333c424344591e1964c5fc: OK +], +[]) + +RPMTEST_CLEANUP diff --git a/tools/rpmkeys.cc b/tools/rpmkeys.cc index 89a2a7131e..96a1a7254f 100644 --- a/tools/rpmkeys.cc +++ b/tools/rpmkeys.cc @@ -15,6 +15,7 @@ enum modes { MODE_DELKEY = (1 << 2), MODE_LISTKEY = (1 << 3), MODE_EXPORTKEY = (1 << 4), + MODE_REBUILD = (1 << 5), }; static int mode = 0; @@ -35,6 +36,8 @@ static struct poptOption keyOptsTable[] = { N_("Erase keys from RPM keyring"), NULL }, { "list", 'l', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_LISTKEY, N_("list keys from RPM keyring"), NULL }, + { "rebuild", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_REBUILD, + N_("rebuild the keyring - convert to current backend"), NULL }, POPT_TABLEEND }; @@ -149,7 +152,8 @@ int main(int argc, char *argv[]) args = (ARGV_const_t) poptGetArgs(optCon); - if (args == NULL && mode != MODE_LISTKEY && mode != MODE_EXPORTKEY) + if (args == NULL && mode != MODE_LISTKEY && mode != MODE_EXPORTKEY && + mode != MODE_REBUILD) argerror(_("no arguments given")); ts = rpmtsCreate(); @@ -183,6 +187,11 @@ int main(int argc, char *argv[]) ec = matchingKeys(ts, args, printKey); break; } + case MODE_REBUILD: + { + ec = rpmtsRebuildKeystore(ts); + break; + } default: argerror(_("only one major mode may be specified")); }