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

Conversation

ffesti
Copy link
Contributor

@ffesti ffesti commented Nov 28, 2024

This rebuild the public key storage. If _keyring is set to a new value
keys from old storage are moved there and the old stores are deleted.
Keys are stored in the latest format. Keys no longer accepted are
dropped.

Resolves: #3347

@ffesti ffesti requested a review from a team as a code owner November 28, 2024 09:10
@ffesti ffesti requested review from pmatilai and removed request for a team November 28, 2024 09:10
lib/keystore.cc Outdated
auto macros = rpm::macros();
auto [mrc, keyringpath] = macros.expand("%{_keyringpath}");
while ('/' == keyringpath.back())
keyringpath.pop_back();
Copy link
Member

Choose a reason for hiding this comment

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

Just use expand_path(), it'll do this for you. There's actually bug in this version: it'd crash if the path only contained /'s.

lib/keystore.cc Outdated
keyringpath.pop_back();
std::string path = rpm::expand_path({rpmtxnRootDir(txn), keyringpath});
std::string tmppath = keyringpath + ".tmp/";
std::string oldpath = keyringpath + ".rpmold/";
Copy link
Member

Choose a reason for hiding this comment

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

Include the rpmtxnRootDir() in these already, as it is you just need to duplicate that work multiple times in the below, making it harder to follow.

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.

lib/rpmts.cc Outdated

keystore_openpgp_cert_d ks_opengpg = {};
rpmKeyring keyring_openpgp = rpmKeyringNew();
ks_opengpg.load_keys(txn, keyring_openpgp);
Copy link
Member

@pmatilai pmatilai Nov 28, 2024

Choose a reason for hiding this comment

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

Um. Why should we load up all possible keystore types? Only one can possibly be active at any time, and if there happens to be other info laying around I don't think we want anything to do with it, because who knows what put it there.

lib/rpmts.cc Outdated
rpmPubkey key = NULL;
const unsigned char * pkt = NULL;
size_t pktlen = 0;
char *lints = NULL;
Copy link
Member

Choose a reason for hiding this comment

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

pkt, pktlen and lints belong inside the while() loop.

@pmatilai
Copy link
Member

The above isn't a "full review", just remarks on stuff that immediately caught my eye.

An observation, not on this PR but this keystore thing in general: like we discussed, the keystore not having any internal state seemed weird and wrong. In retrospective it indeed is. The keystore should take the keyring path as an optional argument and remember it, macro values changing after initialization "obviously" must not affect an existing keystore. I remember thinking about that but then somehow conflating it with the txn root directory. And this PR pays the price for that mistake - seems obvious now but... oh well.

Another, sort of related observation is that if temporary macros like the %_keyringpath here are a common use-case, we could arrange the cleanup to occur automatically with the macro context handle destructor.

@pmatilai
Copy link
Member

pmatilai commented Dec 2, 2024

But okay, there's no point in doing detailed review while there are higher level questions open. I was expecting to see a similar 1:1 rebuild system with optional conversion as the rpmdb has, but this merges multiple sources into one. In all rpm versions, it's only been possible to have one keystore configured at a time, and if we're pulling in anything else then we're making a decision to trust those keys on behalf of the user (because currently imported == trusted). That seems very wrong.

@ffesti
Copy link
Contributor Author

ffesti commented Dec 2, 2024

Well, having multiple keystores can happen quite easily. Technically we will always a rpmdb - so there can be gpg-pubkey packages in there no matter what you configured in the macros file. Also an update of rpm that is changing the keystore might pull in a new keys right away - ending up with keys in two different keystores.

I do agree we want to be a bit more conservative with just adding random keys from somewhere. But should at least look for other stores and report them. May be require --force or something to actually use them.

@pmatilai
Copy link
Member

pmatilai commented Dec 2, 2024

We'll need to sort this out in the ticket, this isn't review territory really.

Merge the keys directly in the keyring
We need to both delete the old key and add the new one.
Simplify rpmtxnImportPubkey
No functional change
Don't make stuffing this into a rpmPubkey object a one-way street.
This will become handy later
Allow checking which one is selected by name.
Give an warning if they contain public keys. This allows the user to
detect misconfigurations or missing conversion from one backend to
another.
@ffesti
Copy link
Contributor Author

ffesti commented Dec 13, 2024

OK, added check for dangling pubkeys in old keystores for both normal operation and rebuilding the keystore.
Added --include_backend and --drop_backends to the rpmkeys --rebuild.

Patch is still missing man page and may be docs updates. I'll add those as soon as the CLI is green lighted.

This rebuildis the public key storage. If _keyring is set to a new value
and there are keys left in an old store this fails with an error message.

The keys can either be included by using one or more instances of
--include_backend or dropped with --drop_backends.

Keys are stored in the latest format. Keys no longer accepted are
dropped.

Resolves: rpm-software-management#3347
@ffesti
Copy link
Contributor Author

ffesti commented Dec 13, 2024

Updated the rpmkeys man page.

@ffesti ffesti requested a review from pmatilai December 17, 2024 09:09
@pmatilai
Copy link
Member

pmatilai commented Dec 17, 2024

Let's split this into smaller pieces to move it forward. There are useful commits in here that we can merge already, eg I think supporting key merge in the keyring directly makes more sense than hiding it inside the import. Basically, split commits 1-6 to another PR, just a few quick review-remarks:

  • The commit messages could be more elaborate (these look like just something quickly thrown there while working on something else, which was probably exactly the case 😅 ). Eg, the RPMKEYRING_REPLACE fix, is that a regression or what?
  • Make rpmPubkeyRawData() return the pointer via return, and length via the parameter. That's the common style in rpm API and returning void for something that can fail is just weird and clunky for a caller who wants to check the return.
  • rpmKeyringIsEmpty() would be more general purpose as rpmKeyringSize() to return number of keys in that keyring. It'll serve the empty check just as well.
  • The keystore name should probably be a property of the class, set from the constructor of the base class. It seems fair that the keystores know who they are, and then you don't need separate methods for each.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Move installed gpg keys to the currently configured storage
2 participants