-
Notifications
You must be signed in to change notification settings - Fork 372
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 merging of new key material when importing pubkeys #3083
Conversation
The call to pgpPubKeyLint() modified the rc code, which the following code assumed to be RPMRC_FAIL. Also, the return code of the database operation was always overwritten with RPMRC_OK.
This is useful for API users that do not re-read the keys from the database.
No functual changes.
Oh, nice. Didn't look at details yet but the functionality is pretty desperately needed indeed. @nwalfield , thoughts from rpm-sequoia POV (or otherwise)? |
I'll take a look the start of next week. From a very high-level perspective: yes, we want to merge certificates. So, thanks a lot for working on this @mlschroe! |
@mlschroe , I didn't quite expect you to start so actively hacking on it, more like terminal care and hence the name. If you intend to continue developing it, I'm okay with renaming it to something else than "legacy". Only it can't be "internal" anymore because it's not 😅 |
I started fixing some problems I found in the code and then got carried away... |
maybe "pgp" would be a working name for the backend... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks fine to me!
@nwalfield, if you can afford a few cycles to look at this before I hit merge, I'd appreciate...
About that "legacy" name: how about changing it to "traditional"? It doesn't sound so negative and also suggests that the code is somewhat not state of the art. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for working on this. The most important question I have regards the proposed semantics. You have "Add", "Replace", and "Delete." I don't understand the point of "Replace." Do you have a use case for when you want to replace and not merge? Why not "AddOrMerge" and "Delete"?
typedef enum rpmKeyringModifyMode_e { | ||
RPMKEYRING_ADD = 1, | ||
RPMKEYRING_REPLACE = 2, | ||
RPMKEYRING_DELETE = 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please explain the semantics of these different operations. My feeling is that there are only two relevant operations: ADD_OR_MERGE
and DELETE
. Does your ADD
fail if the certificate is already present? Does REPLACE
do a merge or not (looking at the code, it doesn't appear to)? If it doesn't, why not? (What's the use case?) Doesn't not merging mean that you potentially lose old binding signatures? That seems problematic to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is about if you do the merge in the keyring code or in the import code. I played with both options and then settled in doing the merge call in the import code and then replacing the old pubkey with the merged pubkey.
It's up to the merge code if it wants to keep all binding signatures. The rpmpgp_legacy merge code does not throw away any pgp package.
include/rpm/rpmkeyring.h
Outdated
* @param key Pubkey | ||
* @param key pubkey handle | ||
* @param mode mode of operation | ||
* @return 0 on success, -1 on error, 1 if key already present (add) or not found (delete) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please make this comment a bit more verbose, please. I get the idea, but the semantics aren't entirely clear to me.
rpmRC rc; | ||
|
||
pthread_rwlock_rdlock(&oldkey->lock); | ||
pthread_rwlock_rdlock(&newkey->lock); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these locks have a locking order? If not, we should define one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know. I'm not even sure that we need to lock them but found it more safe. Panu?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we have a defined locking order for anything in rpm. The locks on they keys are probably a bit hokey to begin with because librpm and threads is playing with fire anyway. But, it'd seem unsafe to drop since we have them elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A simple locking order would be to lock according to the memory address. But, that is probably out of scope for this PR.
include/rpm/rpmpgp.h
Outdated
* @param pkts2len length of the buffer with second certificates | ||
* @param pktsm[out] merged certificates (malloced) | ||
* @param pktsmlen[out] length of merged certificates | ||
* @param flags merge flags (currently not implemented) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably say: must be 0
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also drop the flags. I added them because we could use them in the future to change the merge operation. E.g. have a flag to ignore all non-self signatures or similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather have a flags arg we never end up using than not having it when we need it. Rpm is traditionally terribly sloppy with these, but indeed it'd be good to state the only legit value now is 0 so people don't end up passing random stuff that hurts us later (been there).
* @param pktsm[out] merged certificates (malloced) | ||
* @param pktsmlen[out] length of merged certificates | ||
* @param flags merge flags (currently not implemented) | ||
* @return RPMRC_OK on success |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please clarify the semantics. What exactly is merging? Does it mean the union of the keys or also the union of self-signatures? (I hope it means the latter.) What if the two certificates are not the same? What if a buffer contains multiple certificates? What if a certificate is invalid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pgp backend MUST return an error if a buffer contains multiple certificates or is invalid.
How the merging is done is up to the backend implementer. It SHOULD build the union of all pgp packets in some sane order and remove all duplicates.
* @param flags merge flags (currently not implemented) | ||
* @return RPMRC_OK on success | ||
*/ | ||
rpmRC pgpPubkeyMerge(const uint8_t *pkts1, size_t pkts1len, const uint8_t *pkts2, size_t pkts2len, uint8_t **pktsm, size_t *pktsmlen, int flags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit unfortunate that this interface works with buffers and not rpmKeyring
s. Is there a reason we can't use rpmKeyring
s here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the other functions use buffers as well (e.g. pgpParsePkts, pgpPubKeyCertLen, ...)
pthread_rwlock_rdlock(&oldkey->lock); | ||
pthread_rwlock_rdlock(&newkey->lock); | ||
rc = pgpPubkeyMerge(oldkey->pkt.data(), oldkey->pkt.size(), newkey->pkt.data(), newkey->pkt.size(), &mergedpkt, &mergedpktlen, 0); | ||
if (rc == RPMRC_OK && (mergedpktlen != oldkey->pkt.size() || memcmp(mergedpkt, oldkey->pkt.data(), mergedpktlen) != 0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unfortunate. It assumes that there is a canonical form. Sequoia will first canonicalize the certificates, and then merge them. It's able to detect that nothing new was added, but may not emit the same bytes. I'd rather have pgpPubkeyMerge
return "unchanged" in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems I forgot to press the "Comment" button after I typed my reply...
There was no straightforward option with the RPMRC codes, so I went with the memcmp as a way to detect that nothing was changed. We can certainly hijack a RPMRC code or use a different means
But note that the rpm-sequoia code can also just duplicate the first certificate if the sequoia library returns an "unchanged" status...
include/rpm/rpmpgp.h
Outdated
@@ -509,6 +509,19 @@ int pgpSignatureType(pgpDigParams sig); | |||
*/ | |||
char *pgpIdentItem(pgpDigParams digp); | |||
|
|||
/** \ingroup rpmpgp | |||
* Merge the PGP packets of two public keys | |||
* @param pkts1 OpenPGP pointer to a buffer of first certificates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you really mean this function should handle multiple certificates, i.e., an OpenPGP keyring? If so, then we only need a single buffer. Otherwise, s/certificates/certificate/
(likewise below).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a copy/paste error ;-)
} | ||
|
||
if (!rc && replace) { | ||
/* find and delete the old pubkey entry */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the use case for these semantics? (Deleting the old version of a certificate and replacing it with a new version.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above.
Sorry for the delay. I'm in grant writing mode as our funding from STF ends at the end of this year, and so far we haven't gotten any other sponsors---public, commercial, or otherwise. |
@nwalfieldthanks for the review! |
Looks to me the issues are mostly cosmetical, like a mention of copy-paste error. @mlschroe , can you fixup the review remarks so we can merge, or are you planning further work on this? |
I'll do the requested changes to the comments and force push. After that it should be in a state that can be merged. |
This allows us to replace or delete keys from the keyring.
Use this method in rpmtsImportPubkey() to check if we already have that key. This is the place where we will implement key merging in the next commits.
The new rpmPubkeyMerge function will merge the certificate material of two pubkeys describing the same key. This is currently only implemented in the "legcay" backend.
Ok, I think this is now ready to be merged. |
Ack, lets get it merged then, been open for comments long enough for sure. |
Thanks a lot for working on this @mlschroe and @nwalfield for the review! This topic has become a bit of a hot potato in recent months. |
Just FWIW, it'd be nice to get this into 4.20 too, but the backport is more involved just a plain cherry-pick so I figured it's best I don't mess with it just before heading off to vacation. But, patches would be cheerfully accepted 😇 |
FYI, I've implemented this in |
Thank you all for working on this. As I understand the discussion, the feature should already be released and available? I am testing it in a F41 container: $ rpm -q rpm
rpm-4.20.0-1.fc41.x86_64
$ rpm -q rpm-sequoia
rpm-sequoia-1.7.0-2.fc41.x86_64 And this is my reproducer: $ dnf install faketime diffutils
# Download some keys
$ curl https://raw.githubusercontent.com/xsuchy/distribution-gpg-keys/64392d70990254bb10876d68b451c0242dde4f95/keys/copr/copr-agriffis-neovim-nightly.gpg > old.gpg
$ curl https://raw.githubusercontent.com/xsuchy/distribution-gpg-keys/83ac2d8fd74a595687b0ecbb04081864cf59da01/keys/copr/copr-agriffis-neovim-nightly.gpg > new.gpg
# Pretend we imported an old key 5 years ago
$ faketime '-5years' rpm --import old.gpg
# See information about the key
$ rpm -q gpg-pubkey
gpg-pubkey-e99d6ad1-64d2612c
gpg-pubkey-453d6413-5c7aa779
$ rpm -qi gpg-pubkey-453d6413-5c7aa779 > old
# Import a new key
$ rpm --import new.gpg
# None of the information changed
$ rpm -q gpg-pubkey
gpg-pubkey-e99d6ad1-64d2612c
gpg-pubkey-453d6413-5c7aa779
$ rpm -qi gpg-pubkey-453d6413-5c7aa779 > new
$ diff old new
$ So it doesn't seem to work. Am I testing the feature correctly? |
The rpm-side feature only exists in git master, not any released rpm version. |
This currently only makes a difference if the "legacy" backend is used.