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

Installation / verification should not pass if the (sub)key(s) has been revoked or expired #1598

Closed
dmantipov opened this issue Mar 24, 2021 · 35 comments

Comments

@dmantipov
Copy link
Contributor

Shouldn't RPM treat the revoked (sub)key(s) as no longer valid? I'm trying to fix the simple use case with the only revoked subkey. IOW after importing:

sec  rsa4096/D8D1E0ECD0EE67F7
     created: 2021-03-24  expires: 2023-03-24  usage: C   
     trust: ultimate      validity: ultimate
The following key was revoked on 2021-03-24 by RSA key D8D1E0ECD0EE67F7 Dmitry Antipov <[email protected]>
ssb  rsa3072/03CB9273F10DB1D4
     created: 2021-03-24  revoked: 2021-03-24  usage: S   
[ultimate] (1). Dmitry Antipov <[email protected]>
[ultimate] (2)  CloudLinux, Inc. <[email protected]>

the package previously signed as:

Signature   : RSA/SHA256, Wed Mar 24 12:16:55 2021, Key ID 03cb9273f10db1d4

should not pass verification:

$ rpm -K foo-1.0-1.x86_64.rpm 
foo-1.0-1.x86_64.rpm: digests SIGNATURES NOT OK

and warning should be issued during an installation:

$ rpm -i foo-1.0-1.x86_64.rpm 
warning: foo-1.0-1.x86_64.rpm: Header V4 RSA/SHA256 Signature, key ID f10db1d4: NOKEY
@dmantipov
Copy link
Contributor Author

Note dmantipov@0f8654d is not pretended to be correct in general and likely may handle the only particular case. I just want to make sure that I'm moving in the right direction.

@pmatilai
Copy link
Member

Revocation is one of the many unimplemented things in rpm's OpenPGP support.

In other words, you're not seeing a bug as such, it's just not implemented at all, much like expiration is not.

@DemiMarie
Copy link
Contributor

Revocation is one of the many unimplemented things in rpm's OpenPGP support.

In other words, you're not seeing a bug as such, it's just not implemented at all, much like expiration is not.

Given the complexity of a full implementation, I wonder if we would be better off ditching OpenPGP entirely in RPMv6. Something like signify would be trivial to implement, and we can just add new tags whenever we need to support new algorithms.

@dmantipov
Copy link
Contributor Author

Given the complexity of a full implementation, I wonder if we would be better off ditching OpenPGP entirely in RPMv6.

What about offloading this to external library like gpgme?

@pmatilai
Copy link
Member

@DemiMarie , please stop throwing around RPMv6 so liberally. See the project description, it is not going to be the grand rewrite of everything but a strictly limited scope revision of what we have now, and bringing it up on every other ticket here can easily give people wrong ideas. There will eventually be a draft to comment on but until then the subject is not really open for discussion.

@dmantipov
Copy link
Contributor Author

it is not going to be the grand rewrite of everything

So is it worth spending time trying to improve current OpenPGP support? Not sure about complete, RFC4880-compilant implementation, but revocation and expiration at least?

@pmatilai
Copy link
Member

pmatilai commented Mar 25, 2021

So is it worth spending time trying to improve current OpenPGP support?

Yes it is. Just note that the problem with revocation and expiry is not so much parsing it out of PGP than the actual interaction with the rest of rpm.

@dmantipov dmantipov changed the title Installation / verification should not pass if the (sub)key(s) has been revoked Installation / verification should not pass if the (sub)key(s) has been revoked or expired Mar 27, 2021
@dmantipov
Copy link
Contributor Author

actual interaction with the rest of rpm

What about adding configure-time option, say, --enable-enforced-signatures? If configured and compiled with this one, RPM should refuse to install the package if no signature at all or (sub)key(s) has been revoked or expired. This may be useful for the distributions where paranoid security checks are essential.

@pmatilai
Copy link
Member

There's already an enforcing mode for signature checking at install time, and that's a point where revocation and expiry checks would seem fairly obvious. The open questions come after that.

@dmantipov
Copy link
Contributor Author

There's already an enforcing mode for signature checking at install time

Is it controlled by the command-line option? I've found only --nodigest and --nosignature, both meaning an opposite to what we're talking about here.

@pmatilai
Copy link
Member

See

rpm/macros.in

Line 672 in 375bac6

# Enforced package verification level

(that stuff really needs proper docs, sigh...)

@dmantipov
Copy link
Contributor Author

dmantipov commented Mar 29, 2021

(that stuff really needs proper docs, sigh...)

Is it intended to describe mechanism or policy? It seems that these two are mixed through the whole code base in an obfuscating and weird way. For example, what's expected to happen if someone try --nosignature install of a package build with %_pkgverify_level all?

@dmantipov
Copy link
Contributor Author

Could someone please briefly review two patches above? Thanks.

@DemiMarie
Copy link
Contributor

Could someone please briefly review two patches above? Thanks.

Revocation signatures are only valid if they are a valid signature of the key being revoked, and are made by either the key being revoked or a key that it has designated as valid for revocation.

@dmantipov
Copy link
Contributor Author

dmantipov commented Mar 31, 2021

Well, it seems it would be helpful to have some advice here. In my local setup, packets analysis code detects the following,
in that order:

   PGPTAG_PUBLIC_KEY                    ; [1] public key id saved

   PGPTAG_SIGNATURE
     <unknown 33>
     PGPSUBTYPE_SIG_CREATE_TIME
     PGPSUBTYPE_REVOKE_REASON           ; [2] revoke reason
     PGPSUBTYPE_ISSUER_KEYID            ; [3] key id match saved at [1]

   PGPTAG_USER_ID

   PGPTAG_SIGNATURE
     <unknown 33>
     PGPSUBTYPE_SIG_CREATE_TIME
     PGPSUBTYPE_KEY_FLAGS
     PGPSUBTYPE_KEY_EXPIRE_TIME
     PGPSUBTYPE_PREFER_SYMKEY
     PGPSUBTYPE_PREFER_HASH
     PGPSUBTYPE_PREFER_COMPRESS
     PGPSUBTYPE_FEATURES
     PGPSUBTYPE_KEYSERVER_PREFERS
     PGPSUBTYPE_ISSUER_KEYID            ; key id match saved at [1]

   PGPTAG_USER_ID

   PGPTAG_SIGNATURE
     <unknown 33>
     PGPSUBTYPE_SIG_CREATE_TIME
     PGPSUBTYPE_KEY_FLAGS
     PGPSUBTYPE_KEY_EXPIRE_TIME
     PGPSUBTYPE_PREFER_SYMKEY
     PGPSUBTYPE_PREFER_HASH
     PGPSUBTYPE_PREFER_COMPRESS
     PGPSUBTYPE_FEATURES
     PGPSUBTYPE_KEYSERVER_PREFERS
     PGPSUBTYPE_ISSUER_KEYID            ; key id match saved at [1]

   PGPTAG_PUBLIC_SUBKEY                 ; subkey saved for later analysis

   PGPTAG_SIGNATURE
     <unknown 33>
     PGPSUBTYPE_SIG_CREATE_TIME
     PGPSUBTYPE_KEY_FLAGS
     PGPSUBTYPE_KEY_EXPIRE_TIME
     PGPSUBTYPE_ISSUER_KEYID            ; key id match saved at [1]
     PGPSUBTYPE_EMBEDDED_SIG

   PGPTAG_SIGNATURE
     <unknown 33>
     PGPSUBTYPE_SIG_CREATE_TIME
     PGPSUBTYPE_SIGNER_USERID
     PGPSUBTYPE_ISSUER_KEYID            ; key id match saved at [1]

So, if [2] is detected and key id at [3] matches key id saved at [1], can I assume that the key (and so all subkeys) is revoked?

@DemiMarie
Copy link
Contributor

DemiMarie commented Mar 31, 2021

Well, it seems it would be helpful to have some advice here. In my local setup, packets analysis code detects the following,
in that order:

   PGPTAG_PUBLIC_KEY                    ; [1] public key id saved

   PGPTAG_SIGNATURE
     <unknown 33>
     PGPSUBTYPE_SIG_CREATE_TIME
     PGPSUBTYPE_REVOKE_REASON           ; [2] revoke reason
     PGPSUBTYPE_ISSUER_KEYID            ; [3] key id match saved at [1]

   PGPTAG_USER_ID

   PGPTAG_SIGNATURE
     <unknown 33>
     PGPSUBTYPE_SIG_CREATE_TIME
     PGPSUBTYPE_KEY_FLAGS
     PGPSUBTYPE_KEY_EXPIRE_TIME
     PGPSUBTYPE_PREFER_SYMKEY
     PGPSUBTYPE_PREFER_HASH
     PGPSUBTYPE_PREFER_COMPRESS
     PGPSUBTYPE_FEATURES
     PGPSUBTYPE_KEYSERVER_PREFERS
     PGPSUBTYPE_ISSUER_KEYID            ; key id match saved at [1]

   PGPTAG_USER_ID

   PGPTAG_SIGNATURE
     <unknown 33>
     PGPSUBTYPE_SIG_CREATE_TIME
     PGPSUBTYPE_KEY_FLAGS
     PGPSUBTYPE_KEY_EXPIRE_TIME
     PGPSUBTYPE_PREFER_SYMKEY
     PGPSUBTYPE_PREFER_HASH
     PGPSUBTYPE_PREFER_COMPRESS
     PGPSUBTYPE_FEATURES
     PGPSUBTYPE_KEYSERVER_PREFERS
     PGPSUBTYPE_ISSUER_KEYID            ; key id match saved at [1]

   PGPTAG_PUBLIC_SUBKEY                 ; subkey saved for later analysis

   PGPTAG_SIGNATURE
     <unknown 33>
     PGPSUBTYPE_SIG_CREATE_TIME
     PGPSUBTYPE_KEY_FLAGS
     PGPSUBTYPE_KEY_EXPIRE_TIME
     PGPSUBTYPE_ISSUER_KEYID            ; key id match saved at [1]
     PGPSUBTYPE_EMBEDDED_SIG

   PGPTAG_SIGNATURE
     <unknown 33>
     PGPSUBTYPE_SIG_CREATE_TIME
     PGPSUBTYPE_SIGNER_USERID
     PGPSUBTYPE_ISSUER_KEYID            ; key id match saved at [1]

So, if [2] is detected and key id at [3] matches key id saved at [1], can I assume that the key (and so all subkeys) is revoked?

Only if [2] is a valid signature of [1], made by [1]. And you should use fingerprints, not key IDs, if possible. Key IDs are not guaranteed to be unique.

@dmantipov
Copy link
Contributor Author

I'll investigate how to dig for fingerprints; here is the version with key IDs.

@DemiMarie
Copy link
Contributor

I'll investigate how to dig for fingerprints; here is the version with key IDs.

Thanks! In addition to the fingerprint vs key ID issue, this still needs a cryptographic signature check.

@dmantipov
Copy link
Contributor Author

this still needs a cryptographic signature check

Is it enough to get zero from pgpVerifySignature()?

@mlschroe
Copy link
Contributor

mlschroe commented Jul 1, 2021

Note the following text from the gpgv manpage:

gpgv2  assumes  that  all keys in the keyring are trustworthy.  That does also mean that it does not check for expired or revoked keys.

So we're in good company ;-)

@DemiMarie
Copy link
Contributor

Revocation checking requires a proper keystore, which RPM does not have. Expiration checking “merely” requires checking the expiration date of the self-signature.

@cgwalters
Copy link
Contributor

Related discussion of this over here ostreedev/ostree#2260

@mlschroe
Copy link
Contributor

mlschroe commented Jul 2, 2021

I don't think we need to support key revokation in rpm. My understanding is that the revokation handling in gpg is done that way because gpg can only merge new key material and never deletes existing data. But that's not the way rpm works, as it does not merge key material.

I don't think it makes sense to have a revoked key in the database at all, you might as well just delete the key from the database. So we could state that it's up to the layer above rpm that manages the keys to handle this (libzypp does handle key updates, I don't know about dnf).

But I do think rpm should check the expiry date of a key. We could make it configurable how rpm deals with an expired key.

@DemiMarie
Copy link
Contributor

I don't think it makes sense to have a revoked key in the database at all, you might as well just delete the key from the database. So we could state that it's up to the layer above rpm that manages the keys to handle this (libzypp does handle key updates, I don't know about dnf).

Perhaps a better option would be to replace the revoked key with an invalid stub entry, so future attempts to re-add the key fail. This also lets us provide better error messages to the user.

But I do think rpm should check the expiry date of a key. We could make it configurable how rpm deals with an expired key.

Agreed.

@subsr97
Copy link

subsr97 commented Jul 3, 2021

@dmantipov Is there a CVE associated with this vulnerability?
I'm asking so that I can keep an eye out for the fix.

Also, on a different note, any idea if package managers that reply on rpm are vulnerable as well? Yum and Zypper for instance.

@sidhpurwala-huzaifa
Copy link

An additional thing, once a key is revoked by a distro (for whatever reason), they usually sign new rpms with the new key. However it does not mean that the older rpms signed by the old key are no longer secure to use. Unless of-course the old key has been compromised by the attacker and they sign malicious rpms with that.

So if revokation makes all the installed rpms, seem to be signed with the wrong key, than that could be a problem.

Therefore there is some amount of onus on the administrator/user as well.

@sidhpurwala-huzaifa
Copy link

An additional thing, once a key is revoked by a distro (for whatever reason), they usually sign new rpms with the new key. However it does not mean that the older rpms signed by the old key are no longer secure to use. Unless of-course the old key has been compromised by the attacker and they sign malicious rpms with that.

I mean the rpms signed before the key was revoked.

So if revokation makes all the installed rpms, seem to be signed with the wrong key, than that could be a problem.

Therefore there is some amount of onus on the administrator/user as well.

@ffesti
Copy link
Contributor

ffesti commented Jul 5, 2021

@dmantipov Is there a CVE associated with this vulnerability?
I'm asking so that I can keep an eye out for the fix.

Also, on a different note, any idea if package managers that reply on rpm are vulnerable as well? Yum and Zypper for instance.

OK, as there is some confusion here: There is no CVE (AFAIK) and there should not be a CVE. This is not a vulnerability. This is a basic misunderstanding on how rpm works.
RPM by design works on the local system only and does not look things up on the internet. It does not make decisions on its own but relies the user or other tools to be told what needs to be done - including adding or removing keys. The RPM way of no longer trusting a key is to remove it from the RPM DB. This works just fine.

This does not mean that the current situation does not leave things to be desired as withdrawing a key requires quite some effort like issuing an updated that removes the key or using some sort of automation for local setups.

But the topic is much more complicated than just adding support for GPG revocation keys to RPM. First the actual key look up and check needs to go into the updater level (e.g. dnf and zypper) as they are dealing with things on the network. More important than removing a key is probably a way to add a new one when the current one is no longer trusted. Just breaking (automatic) updates for everyone is not a great solution. And there are probably more things to consider. Some are already mentioned above.

@misterzed88
Copy link

I agree with @ffesti about key revokation being more complex than what it seems like. When you revoke, you don't want to invalidate the signatures created before the revokation. That would require every existing package to be re-signed with the new key, which would be very disruptive.

But how can you trust the signature date? An attacker could create a new signature with a forged date. Therefore you need to add support for trusted third-party timestamping to the signatures. Unless I am mistaken, there is no such support in GPG:
https://dev.gnupg.org/T4108
https://dev.gnupg.org/T4537

The problem is not specific to RPM and package signatures. Git signatures, for example, also have the same issue:
https://petertodd.org/2016/opentimestamps-git-integration

Short term, the simplest way to deal with a compromised key, is to remove it from the RPM DB, just as @ffesti suggested. There should be a tested and planned process for how to do this, for example by sending an emergency update, perhaps signed with the disaster recovery key (in Red Hat case). It would also require all packages are re-signed with a new key, since the old ones will be invalidated after the key removal.

Long term, adding support for trusted timestamps would make key revokation much easier.

@DemiMarie
Copy link
Contributor

DemiMarie commented Jul 5, 2021

I agree with @ffesti about key revokation being more complex than what it seems like. When you revoke, you don't want to invalidate the signatures created before the revokation. That would require every existing package to be re-signed with the new key, which would be very disruptive.

But how can you trust the signature date? An attacker could create a new signature with a forged date. Therefore you need to add support for trusted third-party timestamping to the signatures. Unless I am mistaken, there is no such support in GPG:
https://dev.gnupg.org/T4108
https://dev.gnupg.org/T4537

The problem is not specific to RPM and package signatures. Git signatures, for example, also have the same issue:
https://petertodd.org/2016/opentimestamps-git-integration

Short term, the simplest way to deal with a compromised key, is to remove it from the RPM DB, just as @ffesti suggested. There should be a tested and planned process for how to do this, for example by sending an emergency update, perhaps signed with the disaster recovery key (in Red Hat case). It would also require all packages are re-signed with a new key, since the old ones will be invalidated after the key removal.

Long term, adding support for trusted timestamps would make key revokation much easier.

Perhaps the best solution is to ensure (by appropriate use of HSMs) that the key cannot be leaked.

@misterzed88
Copy link

misterzed88 commented Jul 5, 2021

Perhaps the best solution is to ensure (by appropriate use of HSMs) that the key cannot be leaked.

Yes, that comes a long way to mitigating the problem and is hopefully already used by the major distributions.

But the risk is not completely eliminated, since the usage of the HSM itself may have become compromised. An attacker may have gained access to a system with HSM access and issued malicious signatures. If this should happen, a key replacement is most probably warranted.

Fedora has actually been through this before, over a decade ago. (Showing one possible way to manage the situation without GPG key revokation support in RPM):
https://fedoraproject.org/wiki/Enabling_new_signing_key

@DemiMarie
Copy link
Contributor

But the risk is not completely eliminated, since the usage of the HSM itself may have become compromised. An attacker may have gained access to a system with HSM access and issued malicious signatures. If this should happen, a key replacement is most probably warranted.

Absolutely! That said, I imagine any decent HSM can perform internal time-stamping, in which case only signatures before a certain point need to be invalidated.

@erlenmayr
Copy link

There is another issue that is not covered by revocation at all. A software package is obsolete as soon as a new version of the package is signed, especially if there is a known vulnerability in the old version. However, the signature of the vulnerable version obviously stays valid. If the security of updates were just based on package signatures, an attacker could just give you an old package version with a known vulnerability.
Signing on the package level is a nice extra feature, but it is only a piece in a working security concept. It is not even covering the update process. Even if key revocation worked (which in general it does not, Google does not even use it for TLS any more), a valid signature on the package level is no guarantee that the update is correct.
This is of course solved by signing of update lists in short intervals, which makes signatures on the package level unnecessary in the first place.

@sidhpurwala-huzaifa
Copy link

There is another issue that is not covered by revocation at all. A software package is obsolete as soon as a new version of the package is signed, especially if there is a known vulnerability in the old version. However, the signature of the vulnerable version obviously stays valid. If the security of updates were just based on package signatures, an attacker could just give you an old package version with a known vulnerability.
Signing on the package level is a nice extra feature, but it is only a piece in a working security concept. It is not even covering the update process. Even if key revocation worked (which in general it does not, Google does not even use it for TLS any more), a valid signature on the package level is no guarantee that the update is correct.
This is of course solved by signing of update lists in short intervals, which makes signatures on the package level unnecessary in the first place.

The signature on an RPM by a vendor/distributor implies that the package has indeed been provided by the vendor and an MITM attacker has not tampered with it. It does not prove that the binaries in the package are non-malicious or free from known/unknown security defects, those are different security problems. Most likely someone who trusts his vendors signing keys would also trust that the vendor is providing him with known good binaries.

@pmatilai
Copy link
Member

To conclude, neither revocation or expiry is particularly meaningful in the rpm context, only a very limited subset of OpenPGP spec is relevant to rpm. Revoking has been discussed at length here already, and while expiry is far simpler on the outset, tick of the clock will not remove expired software from the system so checking for it at the door would only make things weirder, security would not be improved in the slightest.

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

No branches or pull requests

10 participants