Navigation Menu

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

Fix IMA signature lengths assumed constant (#1833, RhBug:2018937) #1844

Merged
merged 1 commit into from Dec 13, 2021

Conversation

pmatilai
Copy link
Member

ECDSA signatures can vary in length, but the IMA code assumes constant
lengths and thus may either place invalid signatures on disk from
either truncating or overshooting, and segfault if the stars are just
so.

Luckily the signatures are stored as strings so we can calculate the
actual lengths at runtime and ignore the stored constant length info.
Extend hex2bin() to optionally calculate the lengths and maximum,
and use these for returning IMA data from the rpmfi(les) API.

Additionally update the signing code to store the largest IMA signature
length rather than what happened to be last to be on the safe side.
We can't rely on this value due to invalid packages being out there,
but then we need to calculate the lengths on rpmfiles populate so there's
not a lot to gain anyhow.

Fixes: #1833

@pmatilai pmatilai added the bug label Nov 26, 2021
@nullr0ute
Copy link
Contributor

Should there be tests for this too?

@pmatilai
Copy link
Member Author

In a better world, yes of course. It's just that we don't have any good means to test it, because our test-suite expects to run non-root so applying IMA signatures is a non-starter, and plugins are disabled in the test-suite due to other artifacts. Even testing signing is pending for somebody to sit down and figure how to do it in the environment. One could test the API returns of course, but this stuff isn't exposed to Python bindings, sigh.

@mlschroe
Copy link
Contributor

RSA sigs can also vary in length. Leading zeros of the exponentiation result will be stripped, so there's a 1/256 chance that the sig is smaller than expected.

@pmatilai
Copy link
Member Author

Oh, right. Remember running into those "missing zeros" on occasion...

@pmatilai
Copy link
Member Author

Rebased on top of #1847 for access to the data from Python, test added and commit message adjusted to list RSA as variable length too.

@Conan-Kudo
Copy link
Member

@pmatilai This PR is behaving oddly, it shows me the Python data access commit even though that's already present in git master. Perhaps you need to rebase again?

Copy link
Contributor

@dmnks dmnks left a comment

Choose a reason for hiding this comment

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

LGTM overall but see inline comment.

@@ -579,9 +580,9 @@ const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len)

if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
if (fi->signatures != NULL)
signature = fi->signatures + (fi->signaturelength * ix);
signature = fi->signatures + (fi->signaturemaxlen * ix);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this actually correct, given that the lengths can now vary?

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a proof that this cannot overflow?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this should have just been signaturelengths[ix] but somehow got missed. Or I'm missing something (non)obvious here...

Copy link
Contributor

Choose a reason for hiding this comment

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

No, this is an integer overflow with potentially bad consequences.

Copy link
Member Author

@pmatilai pmatilai Dec 7, 2021

Choose a reason for hiding this comment

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

rpmfi.c is likely one big integer overflow if you go looking, the overall assumption in the codebase (in this file and basically everywhere) is that they don't overflow. Which is why I'm not going to bother with this one either.

Copy link
Contributor

Choose a reason for hiding this comment

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

That said a limit on signature lengths would be another approach.

Copy link
Member Author

Choose a reason for hiding this comment

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

The header is size-capped to 256MB, there's a limit for you.

Copy link
Member Author

Choose a reason for hiding this comment

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

...and here's another: #1858

Copy link
Member Author

Choose a reason for hiding this comment

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

Is this actually correct, given that the lengths can now vary?

So @dmnks actually did point out the flaw in this patch but it got lost in all the noise.

Copy link
Member Author

Choose a reason for hiding this comment

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

...except that this isn't the flaw, only pointing to the general direction. The real flaw is in the part of hex2bin() that's not touched in this patch so it's hard to spot.

lib/rpmfi.c Outdated
@@ -116,7 +116,8 @@ struct rpmfiles_s {
struct fingerPrint_s * fps; /*!< File fingerprint(s). */

int digestalgo; /*!< File digest algorithm */
int signaturelength; /*!< File signature length */
int *signaturelengths; /*!< File signature length */
Copy link
Contributor

Choose a reason for hiding this comment

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

Cosmetic but the comment should be pluralized too 😄

Copy link
Contributor

@DemiMarie DemiMarie left a comment

Choose a reason for hiding this comment

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

There are a few areas where I would like to see a proof of no integer overflow.

@@ -579,9 +580,9 @@ const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len)

if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
if (fi->signatures != NULL)
signature = fi->signatures + (fi->signaturelength * ix);
signature = fi->signatures + (fi->signaturemaxlen * ix);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a proof that this cannot overflow?

/* Figure string sizes + max length for allocation purposes */
if (lengths) {
int maxl = 0;
int *lens = xmalloc(num * sizeof(*lens));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
int *lens = xmalloc(num * sizeof(*lens));
int *lens = xcalloc(num, sizeof(*lens));

This is safer in the event of an overflow.

int i = 0;

while ((s = rpmtdNextString(&td))) {
lens[i] = strlen(s) / 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

What if strlen(s) is odd? That should be an error.

Copy link
Contributor

@dmnks dmnks Dec 6, 2021

Choose a reason for hiding this comment

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

I guess the assumption here is that a hex string will always be even (one byte is represented by two chars), but otherwise yeah, we should probably have a check here as we can't fully control the input.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, these tags should be sanity checked along everything else in the signature header, but that doesn't currently happen for data that is not used for content checking. The same goes for fsverity signatures. Not that it matters a whole lot, the signature will be invalid in that case anyhow.

, RhBug:2018937)

At least ECDSA and RSA signatures can vary in length, but the IMA code
assumes constant lengths and thus may either place invalid signatures on
disk from either truncating or overshooting, and segfault if the stars are
just so.

Luckily the signatures are stored as strings so we can calculate the
actual lengths at runtime and ignore the stored constant length info.
Extend hex2bin() to optionally calculate the lengths and maximum,
and use these for returning IMA data from the rpmfi(les) API.

Additionally update the signing code to store the largest IMA signature
length rather than what happened to be last to be on the safe side.
We can't rely on this value due to invalid packages being out there,
but then we need to calculate the lengths on rpmfiles populate so there's
not a lot to gain anyhow.

Fixes: rpm-software-management#1833
@pmatilai pmatilai merged commit a79d7ae into rpm-software-management:master Dec 13, 2021
@pmatilai
Copy link
Member Author

FWIW, there appear to be other flaws in the patch, as in segfaults in some circumstances. Will fix of course once I manage to reproduce, but just goes to show that reviews should be concentrating on the actual change. Not theory.

@pmatilai
Copy link
Member Author

Yep, this PR was utter garbage. Anybody needing to cherry-pick the IMA fix, be sure to include commit 31e9daf too.

@pmatilai pmatilai deleted the imasiglen-pr branch February 2, 2022 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

segfault installing packages (in very strange edge cases)
6 participants