Bug 2913 - Reading PEM keys might fail if they decrypt to garbage with zero-length passprahse with new OpenSSL 1.1.0i
Summary: Reading PEM keys might fail if they decrypt to garbage with zero-length passp...
Status: CLOSED FIXED
Alias: None
Product: Portable OpenSSH
Classification: Unclassified
Component: ssh (show other bugs)
Version: 7.7p1
Hardware: Other Linux
: P5 enhancement
Assignee: Assigned to nobody
URL:
Keywords: patch
Depends on:
Blocks: V_7_9
  Show dependency treegraph
 
Reported: 2018-10-03 20:31 AEST by Jakub Jelen
Modified: 2021-04-23 15:10 AEST (History)
3 users (show)

See Also:


Attachments
proposed patch (1.14 KB, patch)
2018-10-03 20:31 AEST, Jakub Jelen
no flags Details | Diff
patch v2 (1.34 KB, patch)
2018-10-03 21:09 AEST, Jakub Jelen
no flags Details | Diff
tweaked patch; avoid -Wsign-compare problem (1.12 KB, patch)
2018-10-09 17:25 AEDT, Damien Miller
no flags Details | Diff
correct diff (965 bytes, patch)
2018-10-10 12:35 AEDT, Damien Miller
dtucker: ok+
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jakub Jelen 2018-10-03 20:31:37 AEST
Created attachment 3183 [details]
proposed patch

Some encrypted PEM keys stopped working from OpenSSH with the new OpenSSL 1.1.0i, while they still can be used from OpenSSL. The example key is attached to the Red Hat bugzilla [1].

After some analysis done by Tomas, there is a change in OpenSSL that supports zero-length passprahse, that are passed by default to the PEM decryption methods. In a rare case, the padding is decrypted successfully with this passprahse, the garbage is passed further which results in fail in OpenSSH.

The correct solution is to create a password callback that returns -1 to overwrite the default passphrase callback as attached in the patch.

With this patch, the ssh tools correctly ask for the passphrase, rather than failing with invalid key error.

For more information, see the attached bugzilla [1].

[1] https://bugzilla.redhat.com/show_bug.cgi?id=1632902
Comment 1 Jakub Jelen 2018-10-03 21:09:36 AEST
Created attachment 3184 [details]
patch v2

Ok, the solution is a bit more complicated. We need to shield also the zero-lenght passphrases and otherwise pass the passprase from userdata. With the attached patch I am able to decrypt existing keys as well as these broken ones without any issues.
Comment 2 Damien Miller 2018-10-04 14:09:24 AEST
Why is this not being considered a bug in OpenSSL?
Comment 3 Tomas Mraz 2018-10-04 16:18:36 AEST
Because zero length encryption keys are technically possible and so it is not a bug to try to decrypt encrypted data with zero length keys.

Basically openssh here depended on undocumented behavior of openssl and this patch makes it not to depend on it anymore.
Comment 4 Damien Miller 2018-10-04 17:50:07 AEST
It might be undocumented behaviour of OpenSSL (given the state of the OpenSSL documentation, especially historically, most behaviours are), but it's been a stable one for >20 years and is demonstrably being depended upon by consumers of the API.
Comment 5 Tomas Mraz 2018-10-04 19:04:34 AEST
I do not disagree that such change on a stable release cannot be seen as functionality change breaking API/ABI. On the other hand if such change was done on release which does not promise to keep the API/ABI stable it would definitely be acceptable anyway.

So I do not see why this patch should not be applied regardless of whether the openssl change in patchlevel release is OK or not.
Comment 6 Damien Miller 2018-10-05 21:42:48 AEST
FWIW I don't see the new OpenSSL behaviour documented either. Are you sure it's an intentioned change?

I'm sure OpenSSH isn't the only application that depended on the old behaviour. Wouldn't it be more sensible for OpenSSL to deal with zero-length passwords internally; by checking for a plaintext key first and then attempting to decrypt with a zero-length password?
Comment 7 Tomas Mraz 2018-10-06 00:16:14 AEST
What OpenSSH does is very specific - it depends on returning some particular error codes when zero length password is passed to OpenSSL key loading function to distinguish between invalid keys vs. encrypted keys. I would be very surprised this would be done elsewhere unless they actually copied the key loading logic from OpenSSH.

Anyway I can report to OpenSSL that this change introduced OpenSSH regression and see what they have to say about it.
Comment 8 Tomas Mraz 2018-10-06 00:18:38 AEST
Btw. this is the issue that triggered this change in OpenSSL:

https://github.com/openssl/openssl/issues/4716
Comment 9 Tomas Mraz 2018-10-06 00:25:52 AEST
OpenSSL issue:

https://github.com/openssl/openssl/issues/7355
Comment 10 Damien Miller 2018-10-08 14:57:43 AEDT
Does the patch attached to bug 2901 avoid this?
Comment 11 Jakub Jelen 2018-10-08 17:26:59 AEDT
(In reply to Damien Miller from comment #10)
> Does the patch attached to bug 2901 avoid this?

You can try that yourself with the key attached to the bug referenced in the bug description.

It does not solve the problem. Running the same test with your patch gives to following:

$ SSH_AUTH_SOCK= ./ssh -i invalid_key -F /dev/null localhost 
error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag
error:0907B00D:PEM routines:PEM_read_bio_PrivateKey:ASN1 lib
Load key "invalid_key": invalid format

The reason why is that had_passphrase is 0 (passphrase is zero-length) and therefore we return SSH_ERR_INVALID_FORMAT from the translate_libcrypto_error().
Comment 12 Damien Miller 2018-10-09 17:25:15 AEDT
Created attachment 3190 [details]
tweaked patch; avoid -Wsign-compare problem
Comment 13 Jakub Jelen 2018-10-09 18:46:52 AEDT
(In reply to Damien Miller from comment #12)
> Created attachment 3190 [details]
> tweaked patch; avoid -Wsign-compare problem

This looks like wrong patch (this one is from #2912).
Comment 14 Damien Miller 2018-10-10 12:35:14 AEDT
Created attachment 3192 [details]
correct diff
Comment 15 Damien Miller 2018-10-11 10:37:42 AEDT
This has been committed and will be in OpenSSH 7.9
Comment 16 Damien Miller 2021-04-23 15:10:20 AEST
closing resolved bugs as of 8.6p1 release