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

Exempt src.rpm packages from file signature business #3470

Merged
merged 4 commits into from
Dec 10, 2024

Conversation

dmnks
Copy link
Contributor

@dmnks dmnks commented Nov 26, 2024

Don't add file signatures to source packages and if they happen to be present (i.e. built with an older rpm version), ignore them when unpacking (by not running plugins at all for source packages).

AC (taken from original bug):

  • IMA signature don't make any sense on src.rpm files - so Fedora shouldn't be signing them, and rpm shouldn't let them
  • Even if the assertion above fails and you somehow end up with an src.rpm with IMA signatures on it, rpm should not barf up on it

@dmnks dmnks requested a review from a team as a code owner November 26, 2024 12:37
@dmnks dmnks requested review from ffesti and removed request for a team November 26, 2024 12:37
@dmnks dmnks changed the title Excempt src.rpm packages from file signature business Exempt src.rpm packages from file signature business Nov 26, 2024
lib/transaction.cc Outdated Show resolved Hide resolved
sign/rpmgensig.cc Outdated Show resolved Hide resolved
@ffesti ffesti removed their request for review November 28, 2024 14:45
@dmnks
Copy link
Contributor Author

dmnks commented Dec 4, 2024

Should be fixed now, based on the above discussion.

Note that I'm not a huge fan of the additional NULL checks in all the plugin hook functions (first commit in the series), it just makes it more noisy, and we could just initialize an empty rpmPlugins struct with rpmpluginsNew() in fsmPlugins() instead of returning NULL. But then, it's probably safer to have the NULL checks in place.

@dmnks
Copy link
Contributor Author

dmnks commented Dec 4, 2024

Argh, what a strange CI failure, looking now...

@dmnks
Copy link
Contributor Author

dmnks commented Dec 4, 2024

A silly memory leak in my code! Fixing now...

@dmnks
Copy link
Contributor Author

dmnks commented Dec 4, 2024

... aaand fixed.

@pmatilai
Copy link
Member

pmatilai commented Dec 5, 2024

Note that I'm not a huge fan of the additional NULL checks in all the plugin hook functions (first commit in the series), it just makes it more noisy, and we could just initialize an empty rpmPlugins struct with rpmpluginsNew() in fsmPlugins() instead of returning NULL. But then, it's probably safer to have the NULL checks in place.

Hmm. I thought we wouldn't need any new NULL checks because I thought that's what happens with --noplugins anyway, but apparently --noplugins ends up initializing an empty rpmPlugins struct, and just falling through with that.

Adding NULL checks isn't the end of the world, just a little annoying indeed.

One observation from a closer look is that we also have various per-te plugin hooks in psm, and those shouldn't be called on an src.rpm either. I don't think they get called currently ... yup, because the src.rpm unpack sneaks its way around it. It's weird that you can add src.rpm's to a transaction but then not do anything with them there. Except okay, check dependencies, that is why it's permitted.

So with all that, perhaps the easiest way is probably to temporarily replace ts->plugins with an empty set during rpmInstallSourcePackage(). It's a bit dirty of course, a cleaner way would be passing plugins through psm to fsm, but that requires changing a whole lot of stuff on the way and we prefer a small backportable patch for this.

Another possibility might be checking for src.rpm centrally from RPMPLUGINS_SET_HOOK_FUNC, we could pass a te/NULL to it and let it "return" NULL hookFunc for any src.rpm.

Hmm 🤔

@dmnks
Copy link
Contributor Author

dmnks commented Dec 5, 2024

Hmm. I thought we wouldn't need any new NULL checks because I thought that's what happens with --noplugins anyway, but apparently --noplugins ends up initializing an empty rpmPlugins struct, and just falling through with that.

Oh, I didn't realize that. That seems like a good precedent for doing just that, instead of messing around with NULL checks just to accommodate for this very use case.

One observation from a closer look is that we also have various per-te plugin hooks in psm, and those shouldn't be called on an src.rpm either. I don't think they get called currently ... yup, because the src.rpm unpack sneaks its way around it. It's weird that you can add src.rpm's to a transaction but then not do anything with them there. Except okay, check dependencies, that is why it's permitted.

Yeah, I don't think any non-fsm hooks get called for src.rpms. Only binary packages are added to the transaction set in rpmInstall() so the code paths eventually separate for binary and source packages (rpmcliTransaction() vs. rpmInstallSource()). The tsm and psm hooks only fire when going through rpmcliTransaction() (rpmtsRun() for the former, rpmpsmRun() for the latter).

Running with --debug confirms this, too (no "calling hook" messages for anything else than init when installing a src.rpm).

So with all that, perhaps the easiest way is probably to temporarily replace ts->plugins with an empty set during rpmInstallSourcePackage().

Why would that be needed, though, assuming the above is true?

Another possibility might be checking for src.rpm centrally from RPMPLUGINS_SET_HOOK_FUNC, we could pass a te/NULL to it and let it "return" NULL hookFunc for any src.rpm.

Right, that would indeed be another way, I'll exercise that as an option 😄

@pmatilai
Copy link
Member

pmatilai commented Dec 5, 2024

Why would that be needed, though, assuming the above is true?

Hmm? rpmInstallSourcePackage() is the route through which any source rpm is "installed" and ends up calling the fsm hooks. So if you swap the ts->plugins for an empty set during that, it should achieve this thing we're trying to do here 😅 But, it does feel kinda dirty.

@dmnks
Copy link
Contributor Author

dmnks commented Dec 5, 2024

Hmm? rpmInstallSourcePackage() is the route through which any source rpm is "installed" and ends up calling the fsm hooks. So if you swap the ts->plugins for an empty set during that, it should achieve this thing we're trying to do here 😅 But, it does feel kinda dirty.

Sure, I was just wondering why you'd suggested that as a solution, instead of what this PR currently does? That said, I agree it would be nicer perhaps 😄

@dmnks
Copy link
Contributor Author

dmnks commented Dec 5, 2024

To clarify a bit:

If we were to go without the NULL checks, we could just return the empty plugins struct in fsmPlugins() instead of returning NULL there. Your suggestion moves that higher in the stack which might be nicer but I don't see any technical reason to prefer one over the other, unless I'm missing something 🤔

@pmatilai
Copy link
Member

pmatilai commented Dec 5, 2024

The issue with fsmPlugins() returning an empty set is that you need to free it too someplace. The one from rpmts gets freed along with the ts itself and cannot be freed from the places where fsmPlugins() is called, whereas the other case has to. Possible, but perhaps a bit murky.

...and hence the suggestion wrt rpmInstallSourcePackage(): creating + freeing it is easily contained. Just ugly otherwise 😅

@dmnks
Copy link
Contributor Author

dmnks commented Dec 5, 2024

Oh, now it makes sense, thanks 😄 Indeed, I haven't thought about freeing it but of course...

@pmatilai
Copy link
Member

pmatilai commented Dec 5, 2024

But yup, handling it in RPMPLUGINS_SET_HOOK_FUNC seems kinda attractive too because it then covers all the plugin hooks, regardless of what gets called just now vs in the future etc. And doesn't have the "hold your nose" factor 😄

Funnily enough, we never really checked if the actual unpacking works.

Use a per-test topdir since the global default ($RPMTEST/build) is
shared among all the tests and thus not exactly tidy.
@dmnks
Copy link
Contributor Author

dmnks commented Dec 9, 2024

Here's a variant that's a blend of all the above options we've discussed. This one makes use of RPMTRANS_FLAG_NOPLUGINS (i.e. same as --noplugins) to turn off the hooks during src.rpm unpacking. Note that this also includes the init/cleanup hooks, which is a nice side effect.

This way, we don't need to pass around a te argument to every hook function (this would, annoyingly, also impact a couple of the static fsm functions, e.g. ensureDir()) and just making the function signatures longer as a result. After all, we know that we're processing source packages before calling the hooks via rpmInstallSource() so one could argue that it's more natural to handle it at that level (by disabling the plugins), as opposed to checking for rpmteIsSource() within the hooks themselves.

It's also cleaner than swapping out the plugin list with an empty one temporarily (this would have to be done from within rpmInstallSourcePackage(), i.e. once per every src.rpm, since ts->plugins may only be initialized for the first time in that function). Not impossible, just ugly, as you noted 😄

Lastly, as it often happens, one discovers tasty bugs while looking in dusty corners, which is exactly what happened here: It turns out the combination of binary and source packages (e.g. rpm -i foo.rpm bar.src.rpm) never really worked properly, due to a missed rpmtsEmpty() call, so that's also fixed in this PR 😄

Let me know if you like this approach or not. If not, the other workable alternative is passing around the te argument. I have that variant in a topic branch, too, just didn't like it as much, due to the reasons mentioned.

rpmInstallSourcePackage() grabs the transaction element with the index
of 0, which only works correctly if the src.rpm package is the only one
in the set.  That's not the case if some binary packages have been part
of the same install command in rpmInstall() so we need to make sure the
set is emptied beforehand.

Add a test to go with.
@@ -667,15 +668,23 @@ int rpmInstall(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_t fileArgv)
rpmcliProgressState = 0;
rpmcliProgressTotal = 0;
rpmcliProgressCurrent = 0;

tsflags = rpmtsFlags(ts);
otsflags = rpmtsSetFlags(ts, (tsflags | RPMTRANS_FLAG_NOPLUGINS));
Copy link
Member

@pmatilai pmatilai Dec 10, 2024

Choose a reason for hiding this comment

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

Using NOPLUGINS seems fine, but it needs to be set inside rpmInstallSourcePackage() as that's the lowest level public API we have for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah right, haven't considered that at all. Sigh 😄

@dmnks
Copy link
Contributor Author

dmnks commented Dec 10, 2024

Let's go with the empty-list variant after all. Since we need to do the disabling as low as in rpmInstallSourcePackage(), the flag would be of no use anyway. This way we actually avoid having to touch anything besides that function, isn't that neat? 🥳

@dmnks dmnks force-pushed the ima-srpm-v2 branch 2 times, most recently from 1431cba to d4bf03b Compare December 10, 2024 13:49
File signatures make no sense in source packages as they don't ship
binaries to be installed on the target system, they're just fancy
archives unpacked into %_topdir for packaging purposes.

Disable the respective flags when processing a src.rpm and log a debug
message as a heads-up, but don't skip the rest, header signatures are
still relevant for source rpms, as is the deletion of existing file
signatures.
lib/psm.cc Outdated
@@ -150,6 +150,7 @@ rpmRC rpmInstallSourcePackage(rpmts ts, FD_t fd,
Header h = NULL;
rpmpsm psm = NULL;
rpmte te = NULL;
rpmPlugins plugins = NULL;
Copy link
Member

Choose a reason for hiding this comment

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

I'd call this "origplugins" or something like that to make its purpose clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, fixed now.

Source packages aren't really "installed", just unpacked, and plugins
operate on real transactions by design, so disable the tsm/psm/fsm and
scriptlet hooks for those.  This doesn't include the init and cleanup
hooks but those should be harmless and only do non-destructive actions
anyway.

Do this by simply replacing the plugins list with an empty one for the
duration of src.rpm unpacking.  This also is in line with what we do for
--noplugins.

This fixes, in particular, src.rpm installations done by a regular user
(a fairly common case) on systems equipped with a plugin that needs root
privileges (e.g. the ima plugin), which would otherwise cause a spurious
warning or even failure (see RhBug:2316785).

Reuse the plugin development test, we don't have anything better at the
moment and it does the job well.
@pmatilai pmatilai merged commit f9f124a into rpm-software-management:master Dec 10, 2024
1 check passed
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.

2 participants