Skip to content

Commit

Permalink
Implement openpgp.cert.d based keystore
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ffesti committed Nov 11, 2024
1 parent 5ee40a5 commit b8599f9
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 0 deletions.
89 changes: 89 additions & 0 deletions lib/keystore.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <string>

#include <fcntl.h>
#include <unistd.h>

#include <rpm/header.h>
#include <rpm/rpmbase64.h>
Expand Down Expand Up @@ -148,6 +149,94 @@ rpmRC keystore_fs::import_key(rpmtxn txn, rpmPubkey key, int replace, rpmFlags f
return rc;
}

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

static rpmRC acquire_write_lock(rpmtxn txn)
{
char * keyringpath = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", NULL);
char * lockpath = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", "writelock");
FILE* fp = NULL;
rpmRC rc = RPMRC_FAIL;

if (rpmMkdirs(NULL, keyringpath)) {
rpmlog(RPMLOG_ERR, _("failed to create keyring directory %s: %s\n"),
keyringpath, strerror(errno));
goto exit;
}

fp = std::fopen(lockpath, "wx");

if (!fp) {
rpmlog(RPMLOG_ERR, _("Can't acquire writelock for keyring at %s\n"), keyringpath);
} else {
rc = RPMRC_OK;
std::fclose(fp);
}

exit:
free(keyringpath);
free(lockpath);
return rc;
}

static void free_write_lock(rpmtxn txn)
{
char * path = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", "writelock");
unlink(path);
free(path);
}

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;

if (acquire_write_lock(txn) != RPMRC_OK)
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(txn);
return rc;
}

rpmRC keystore_openpgp_cert_d::import_key(rpmtxn txn, rpmPubkey key, int replace, rpmFlags flags)
{
rpmRC rc = RPMRC_NOTFOUND;

if ((rc = acquire_write_lock(txn)) == RPMRC_OK) {
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(txn);
free(dirpath);
}

return rc;
}

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

rpmRC keystore_rpmdb::load_keys(rpmtxn txn, rpmKeyring keyring)
{
Header h;
Expand Down
7 changes: 7 additions & 0 deletions lib/keystore.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
2 changes: 2 additions & 0 deletions lib/rpmts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
89 changes: 89 additions & 0 deletions tests/rpmsigdig.at
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,95 @@ 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 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
# ------------------------------
# Test rpmkeys write errors
AT_SETUP([[rpmkeys -K no space left on stdout]])
AT_KEYWORDS([rpmkeys digest])
RPMTEST_CHECK([
RPMDB_INIT[

runroot rpmkeys -Kv /data/RPMS/hello-2.0-1.x86_64.rpm /data/RPMS/hello-1.0-1.i386.rpm >/dev/full
]],255,,[[Error writing to log: No space left on device
]])
RPMTEST_CLEANUP


AT_SETUP([rpmkeys -Kv <reconstructed> 1])
AT_KEYWORDS([rpmkeys digest])
RPMDB_INIT
Expand Down

0 comments on commit b8599f9

Please sign in to comment.