Bug 1728361

Summary: Cannot register 8.1 FIPS enabled system due to SSL malloc failure
Product: Red Hat Enterprise Linux 8 Reporter: Craig Donnelly <cdonnell>
Component: opensslAssignee: Tomas Mraz <tmraz>
Status: CLOSED CURRENTRELEASE QA Contact: Stanislav Zidek <szidek>
Severity: urgent Docs Contact:
Priority: urgent    
Version: 8.1CC: csnyder, jhnidek, jsefler, jwboyer, pviktori, tmraz
Target Milestone: rcKeywords: Triaged
Target Release: ---Flags: pm-rhel: mirror+
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: openssl-1.1.1c-2.el8 Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2019-08-23 15:27:09 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1724835, 1738779    

Description Craig Donnelly 2019-07-09 19:00:06 UTC
Description of problem:

A RHEL 8.1 FIPS system cannot register to candlepin (prod or stage) due to an SSL failure:

Unable to verify server's identity: [SSL] malloc failure (_ssl.c:877)

/var/log/rhsm/rhsm.log:

2019-07-09 14:48:44,549 [ERROR] subscription-manager:1783:MainThread @managercli.py:215 - Error during registration: [SSL] malloc failure (_ssl.c:877)
2019-07-09 14:48:44,549 [ERROR] subscription-manager:1783:MainThread @managercli.py:216 - [SSL] malloc failure (_ssl.c:877)
Traceback (most recent call last):
  File "/usr/lib64/python3.6/site-packages/subscription_manager/managercli.py", line 1353, in _do_command
    owner_key = self._determine_owner_key(admin_cp)
  File "/usr/lib64/python3.6/site-packages/subscription_manager/managercli.py", line 1501, in _determine_owner_key
    owners = cp.getOwnerList(self.username)
  File "/usr/lib64/python3.6/site-packages/rhsm/connection.py", line 1257, in getOwnerList
    return self.conn.request_get(method)
  File "/usr/lib64/python3.6/site-packages/rhsm/connection.py", line 726, in request_get
    return self._request("GET", method, headers=headers)
  File "/usr/lib64/python3.6/site-packages/rhsm/connection.py", line 752, in _request
    info=info, headers=headers)
  File "/usr/lib64/python3.6/site-packages/rhsm/connection.py", line 583, in _request
    conn.request(request_type, handler, body=body, headers=final_headers)
  File "/usr/lib64/python3.6/http/client.py", line 1254, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib64/python3.6/http/client.py", line 1300, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib64/python3.6/http/client.py", line 1249, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib64/python3.6/http/client.py", line 1036, in _send_output
    self.send(msg)
  File "/usr/lib64/python3.6/http/client.py", line 974, in send
    self.connect()
  File "/usr/lib64/python3.6/http/client.py", line 1415, in connect
    server_hostname=server_hostname)
  File "/usr/lib64/python3.6/ssl.py", line 365, in wrap_socket
    _context=self, _session=session)
  File "/usr/lib64/python3.6/ssl.py", line 773, in __init__
    self.do_handshake()
  File "/usr/lib64/python3.6/ssl.py", line 1033, in do_handshake
    self._sslobj.do_handshake()
  File "/usr/lib64/python3.6/ssl.py", line 645, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL] malloc failure (_ssl.c:877)



Version-Release number of selected component (if applicable):

# rpm -q subscription-manager openssl crypto-policies kernel
subscription-manager-1.25.11-1.el8.x86_64
openssl-1.1.1c-1.el8.x86_64
crypto-policies-20190613-1.git21ffdc8.el8.noarch
kernel-4.18.0-107.el8.x86_64

How reproducible: 100%


Steps to Reproduce:
1. # fips-mode-setup --enable
2. # reboot
3. # subscription-manager register

Actual results:

[root@dhcp-8-30-11 ~]# subscription-manager register
Registering to: subscription.rhsm.redhat.com:443/subscription
Username: testuser
Password: 
Unable to verify server's identity: [SSL] malloc failure (_ssl.c:877)


Expected results:

SUCCESS


Additional info:

# update-crypto-policies --show
FIPS

# cat /etc/crypto-policies/back-ends/openssl.config 
@SECLEVEL=2:kEECDH:kEDH:kPSK:kDHEPSK:kECDHEPSK:-kRSA:-aDSS:-CHACHA20-POLY1305:-3DES:!DES:!RC4:!RC2:!IDEA:-SEED:!eNULL:!aNULL:!MD5:-SHA384:-CAMELLIA:-ARIA:-AESCCM8

# cat /etc/crypto-policies/back-ends/opensslcnf.config
CipherString = @SECLEVEL=2:kEECDH:kEDH:kPSK:kDHEPSK:kECDHEPSK:-kRSA:-aDSS:-CHACHA20-POLY1305:-3DES:!DES:!RC4:!RC2:!IDEA:-SEED:!eNULL:!aNULL:!MD5:-SHA384:-CAMELLIA:-ARIA:-AESCCM8
Ciphersuites = TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_SHA256
MinProtocol = TLSv1.2

# cat /etc/crypto-policies/back-ends/nss.config 
library=
name=Policy
NSS=flags=policyOnly,moduleDB
config="disallow=ALL allow=HMAC-SHA256:HMAC-SHA1:HMAC-SHA384:HMAC-SHA512:SECP256R1:SECP384R1:SECP521R1:aes256-gcm:aes256-cbc:aes128-gcm:aes128-cbc:SHA256:SHA384:SHA512:ECDHE-RSA:ECDHE-ECDSA:DHE-RSA:tls-version-min=tls1.2:dtls-version-min=dtls1.2:DH-MIN=2048:DSA-MIN=2048:RSA-MIN=2048"

# getenforce 
Enforcing

Comment 1 Craig Donnelly 2019-07-09 19:01:31 UTC
Python version:

# rpm -q platform-python
platform-python-3.6.8-11.el8.x86_64

Comment 2 Chris Snyder 2019-07-09 20:29:23 UTC
I'd say this is a candidate for blocker considering it blocks registration, a core piece of functionality necessary to receive updates.

Comment 8 Chris Snyder 2019-08-16 18:11:31 UTC
Moving to python3 based on the investigation done in comment 7 for a fix.

Comment 9 Petr Viktorin (pviktori) 2019-08-19 15:16:18 UTC
We're investigating OpenSSL failures under FIPS, but aren't very familiar with OpenSSL. The "malloc failure" error is not very enlightening. Help would be welcome.

Comment 10 Marcel Plch 2019-08-19 15:20:25 UTC
Hello, Tomáš, TL;DR at the end.

The malloc issue is reproducible also on Fedora 30.
It seems to be solved in rawhide, where there is openssl-1.1.1c-9.fc31.x86_64.

The line mentioned in comment #7:
 839         ret = SSL_do_handshake(self->ssl);
calls OpenSSL mechanics to do the handshake.

Given this fact and given that updated OpenSSL solves the malloc issue, this one is most likely an OpenSSL issue.

However, there is one more issue:

[root@0c43a8c58ae4 cpython]# ./python -m test test_ssl -m test_check_hostname
Run tests sequentially
0:00:00 load avg: 0.70 [1/1] test_ssl
test test_ssl failed -- Traceback (most recent call last):
  File "/root/cpython/Lib/test/test_ssl.py", line 2545, in test_check_hostname
    s.connect((HOST, server.port))
  File "/root/cpython/Lib/ssl.py", line 1109, in connect
    self._real_connect(addr, False)
  File "/root/cpython/Lib/ssl.py", line 1100, in _real_connect
    self.do_handshake()
  File "/root/cpython/Lib/ssl.py", line 1077, in do_handshake
    self._sslobj.do_handshake()
  File "/root/cpython/Lib/ssl.py", line 689, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: TLSV1_ALERT_INTERNAL_ERROR] tlsv1 alert internal error (_ssl.c:852)

test_ssl failed

I am still investigating whether this is also an OpenSSL's fault or Python's one.
Debugging has brought me very deep thtough OpenSSL internals, so chances are that this fault is also on OpenSSL's side.
It uses MD5_SHA1 message digest even though MD5 shouldn't be allowed at all and I fail to find any instruction to use MD5 from Python's side (a well hidden default in either Python or OpenSSL code).

This place seems to be where all the magic happens.
openssl-1.1.1c/ssl/s3_enc.c:

376 int ssl3_digest_cached_records(SSL *s, int keep)
377 {
378     const EVP_MD *md;
379     long hdatalen;
380     void *hdata;  
381 
382     if (s->s3->handshake_dgst == NULL) {
383         hdatalen = BIO_get_mem_data(s->s3->handshake_buffer, &hdata);
384         if (hdatalen <= 0) {
385             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_DIGEST_CACHED_RECORDS,
386                      SSL_R_BAD_HANDSHAKE_LENGTH);
387             return 0;
388         }
389 
390         s->s3->handshake_dgst = EVP_MD_CTX_new();
391         if (s->s3->handshake_dgst == NULL) {
392             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_DIGEST_CACHED_RECORDS,
393                      ERR_R_MALLOC_FAILURE);  // <- the malloc failure from earlier
394             return 0;
395         }
396 
397         md = ssl_handshake_md(s);
398         if (md == NULL || !EVP_DigestInit_ex(s->s3->handshake_dgst, md, NULL)
399             || !EVP_DigestUpdate(s->s3->handshake_dgst, hdata, hdatalen)) {
400             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_DIGEST_CACHED_RECORDS,
401                      ERR_R_INTERNAL_ERROR); // <- the internal error I face now
402             return 0;
403         }
404     }
405     if (keep == 0) {
406         BIO_free(s->s3->handshake_buffer);
407         s->s3->handshake_buffer = NULL;
408     }
409 
410     return 1;
411 }

Note the comments on lines 393 and 401.

I'll continue to investigate and report here afterwards.

TL;DR:

On the line:
397         md = ssl_handshake_md(s);

in openssl-1.1.1c/ssl/s3_enc.c, the message digest gets returned as follows:

(gdb) p md
$3 = (const EVP_MD *) 0x7f0641a64740 <md5_sha1_md>

This gets detected and blocked by FIPS patch code inside openssl-1.1.1c/crypto/evp/digest.c:
132         if (FIPS_mode()) {
133             if (!(type->flags & EVP_MD_FLAG_FIPS)
134                 && !(ctx->flags & EVP_MD_CTX_FLAG_NON_FIPS_ALLOW)) {
135                 EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_DISABLED_FOR_FIPS);
136                 return 0;
137             }
138         }

Tomáš, do you think this could be an OpenSSL's fault?

Comment 11 Tomas Mraz 2019-08-21 12:37:37 UTC
Can you please retest with the latest RHEL-8.1.0 compose which has openssl-1.1.1c-2.el8 build?

Comment 12 Tomas Mraz 2019-08-21 12:46:24 UTC
The test case from comment #10 apparently uses TLS-1.0 which is not FIPS compliant anymore. So that failure is irrelevant in the FIPS mode and should not be related to the original failure.

Comment 13 John Sefler 2019-08-23 14:44:05 UTC
Retesting the original failing scenario from comment 0 and comment 3 against the RHEL-8.1.0-Snapshot-2.1 compose RHEL-8.1.0-20190821.0...

[root@kvm-01-guest07 ~]# rpm -q subscription-manager openssl crypto-policies kernel
subscription-manager-1.25.14-1.el8.x86_64
openssl-1.1.1c-2.el8.x86_64                <============ matches comment 11
crypto-policies-20190807-1.git9b1477b.el8.noarch
kernel-4.18.0-135.el8.x86_64
[root@kvm-01-guest07 ~]# 
[root@kvm-01-guest07 ~]# update-crypto-policies --show
FIPS
[root@kvm-01-guest07 ~]# sysctl crypto.fips_enabled
crypto.fips_enabled = 1
[root@kvm-01-guest07 ~]# getenforce
Enforcing
[root@kvm-01-guest07 ~]# 
[root@kvm-01-guest07 ~]# subscription-manager register --auto-attach
Registering to: subscription.rhsm.redhat.com:443/subscription
Username: rhelentqe
Password: 
The system has been registered with ID: f2363afb-7b8e-4d9e-900d-ffb42f71d1d2
The registered system name is: kvm-01-guest07.lab.eng.rdu2.redhat.com
Installed Product Current Status:
Product Name: Red Hat Enterprise Linux for x86_64 High Touch Beta
Status:       Subscribed



VERIFIED: The registration is successful and is no longer blocked by "Unable to verify server's identity: [SSL] malloc failure (_ssl.c:877)"

Comment 14 Tomas Mraz 2019-08-23 15:25:15 UTC
As this was only a temporary regression in the openssl-1.1.1c-1.el8 build I am going to close it as CURRENTRELEASE.