From 6e19c1602c1118938d556453ee33357ea0006d3d Mon Sep 17 00:00:00 2001 From: Florian Festi Date: Thu, 31 Oct 2024 10:43:01 +0100 Subject: [PATCH] Implement openpgp.cert.d based keystore This does implement the layout on the file system and the write lock of the openpgp.cert.d proposal according to https://www.ietf.org/archive/id/draft-nwjw-openpgp-cert-d-00.html but not the Trust root, Petname mapping or Trusted introducers. Resolves: #3341 --- lib/keystore.cc | 89 ++++++++++++++++++++++++++++++++++++++++++++++ lib/keystore.hh | 7 ++++ lib/rpmts.cc | 2 ++ tests/rpmsigdig.at | 77 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) diff --git a/lib/keystore.cc b/lib/keystore.cc index 9037af8112..da4e12e289 100644 --- a/lib/keystore.cc +++ b/lib/keystore.cc @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include @@ -148,6 +150,93 @@ rpmRC keystore_fs::import_key(rpmtxn txn, rpmPubkey key, int replace, rpmFlags f return rc; } +/*****************************************************************************/ + +static int acquire_write_lock(rpmtxn txn) +{ + char * keyringpath = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", NULL); + char * lockpath = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", "writelock"); + int fd = -1; + + if (rpmMkdirs(NULL, keyringpath)) { + rpmlog(RPMLOG_ERR, _("failed to create keyring directory %s: %s\n"), + keyringpath, strerror(errno)); + goto exit; + } + + if ((fd = open(lockpath, O_WRONLY|O_CREAT)) == -1) { + rpmlog(RPMLOG_ERR, _("Can't create writelock for keyring at %s: %s\n"), keyringpath, strerror(errno)); + } else if (flock(fd, LOCK_EX|LOCK_NB)) { + rpmlog(RPMLOG_ERR, _("Can't acquire writelock for keyring at %s\n"), keyringpath); + close(fd); + fd = -1; + } + + exit: + free(keyringpath); + free(lockpath); + return fd; +} + +static void free_write_lock(int fd) +{ + flock(fd, LOCK_UN); +} + +rpmRC keystore_openpgp_cert_d::load_keys(rpmtxn txn, rpmKeyring keyring) +{ + return load_keys_from_glob(txn, keyring, "%{_keyringpath}/*/*"); +} + +rpmRC keystore_openpgp_cert_d::delete_key(rpmtxn txn, rpmPubkey key) +{ + rpmRC rc = RPMRC_NOTFOUND; + int lock_fd = -1; + + if ((lock_fd = acquire_write_lock(txn)) == -1) + return RPMRC_FAIL; + + string fp = rpmPubkeyFingerprintAsHex(key); + string dir = fp.substr(0, 2); + string filename = fp.substr(2); + char * filepath = rpmGetPath(rpmtxnRootDir(txn), "%{_keyringpath}/", dir.c_str(), "/", filename.c_str(), NULL); + char * dirpath = rpmGetPath(rpmtxnRootDir(txn), "%{_keyringpath}/", dir.c_str(), NULL); + + if (!access(filepath, F_OK)) + rc = unlink(filepath) ? RPMRC_FAIL : RPMRC_OK; + /* delete directory if empty */ + rmdir(dirpath); + + free(filepath); + free(dirpath); + free_write_lock(lock_fd); + return rc; +} + +rpmRC keystore_openpgp_cert_d::import_key(rpmtxn txn, rpmPubkey key, int replace, rpmFlags flags) +{ + rpmRC rc = RPMRC_NOTFOUND; + int lock_fd = -1; + + if ((lock_fd = acquire_write_lock(txn)) == -1) + return RPMRC_FAIL; + + string fp = rpmPubkeyFingerprintAsHex(key); + string dir = fp.substr(0, 2); + string filename = fp.substr(2); + char *dirpath = rpmGetPath(rpmtxnRootDir(txn), "%{_keyringpath}/", dir.c_str(), NULL); + string dirstr = dirpath; + + rc = write_key_to_disk(key, dirstr, filename, replace, flags); + + free_write_lock(lock_fd); + free(dirpath); + + return rc; +} + +/*****************************************************************************/ + rpmRC keystore_rpmdb::load_keys(rpmtxn txn, rpmKeyring keyring) { Header h; diff --git a/lib/keystore.hh b/lib/keystore.hh index 2511b5f95b..53fd53530a 100644 --- a/lib/keystore.hh +++ b/lib/keystore.hh @@ -37,6 +37,13 @@ private: rpmRC delete_key(rpmtxn txn, const std::string & keyid, unsigned int newinstance = 0); }; +class keystore_openpgp_cert_d : public keystore { +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); +}; + }; /* namespace */ #endif /* _KEYSTORE_H */ diff --git a/lib/rpmts.cc b/lib/rpmts.cc index 758b893354..e01ddf92f9 100644 --- a/lib/rpmts.cc +++ b/lib/rpmts.cc @@ -275,6 +275,8 @@ static keystore *getKeystore(rpmts ts) ts->keystore = new keystore_fs(); } else if (rstreq(krtype, "rpmdb")) { ts->keystore = new keystore_rpmdb(); + } else if (rstreq(krtype, "openpgp")) { + ts->keystore = new keystore_openpgp_cert_d(); } else { /* Fall back to using rpmdb if unknown, for now at least */ rpmlog(RPMLOG_WARNING, diff --git a/tests/rpmsigdig.at b/tests/rpmsigdig.at index ec6ad3a0b5..aa5eb95957 100644 --- a/tests/rpmsigdig.at +++ b/tests/rpmsigdig.at @@ -264,6 +264,83 @@ runroot rpmkeys -Kv /data/RPMS/hello-2.0-1.x86_64.rpm /data/RPMS/hello-1.0-1.i38 RPMTEST_CLEANUP +AT_SETUP([rpmkeys key update (openpgp)]) +AT_KEYWORDS([rpmkeys signature]) +RPMDB_INIT +# root's .rpmmacros used to keep this build prefix independent +echo "%_keyring openpgp" >> "${RPMTEST}"/root/.rpmmacros +RPMTEST_CHECK([ +runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub +runroot rpmkeys -Kv /data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm +], +[1], +[/data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm: + Header OpenPGP V4 EdDSA/SHA512 signature, key ID 6323c42711450b6c: NOKEY + Header SHA256 digest: OK + Payload SHA256 digest: OK +], +[]) + +RPMTEST_CHECK([ +runroot_other touch /usr/lib/sysimage/rpm/pubkeys/writelock +runroot_other flock -x /usr/lib/sysimage/rpm/pubkeys/writelock -c "rpmkeys --import /data/keys/rpm.org-rsa-2048-add-subkey.asc" +runroot_other rm /usr/lib/sysimage/rpm/pubkeys/writelock +], +[0], +[], +[error: Can't acquire writelock for keyring at /usr/lib/sysimage/rpm/pubkeys +error: /data/keys/rpm.org-rsa-2048-add-subkey.asc: key 1 import failed. +]) + + +RPMTEST_CHECK([ +runroot rpmkeys --list | wc -l +runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-add-subkey.asc +runroot rpmkeys --list | wc -l +], +[0], +[1 +1 +], +[]) + +RPMTEST_CHECK([ +runroot rpmkeys -Kv /data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm +], +[0], +[/data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm: + Header OpenPGP V4 EdDSA/SHA512 signature, key fingerprint: 771b18d3d7baa28734333c424344591e1964c5fc: OK + Header SHA256 digest: OK + Payload SHA256 digest: OK +], +[]) + +RPMTEST_CHECK([ +runroot rpmkeys --delete abcd gimmekey 1111aaaa2222bbbb +], +[1], +[], +[error: invalid key id: abcd +error: invalid key id: gimmekey +error: key not found: 1111aaaa2222bbbb +]) + +RPMTEST_CHECK([ +runroot rpmkeys --delete 1964c5fc +], +[0], +[], +[]) + +RPMTEST_CHECK([ +runroot rpmkeys --list | wc -l +], +[0], +[0 +], +[]) +RPMTEST_CLEANUP + AT_SETUP([rpmkeys -Kv 1]) AT_KEYWORDS([rpmkeys digest]) RPMDB_INIT