Red Hat Bugzilla – Bug 1300655
OpenSSL: X509_verify_cert() fails to report error when check_trust() returns X509_TRUST_REJECTED
Last modified: 2016-08-12 01:42:29 EDT
It was reported that x509_cerify_cert() fails to report an error when check_trust() helper function returns X509_TRUST_REJECTED.
Internally trust settings are stored in ASN1_SEQUENCE(X509_CERT_AUX) on disk and in X509->aux member of X.509 certificates. By default certificates have no auxiliary data. Self-signed certificates without auxiliary trust data are considered as trustworthy. When a certificate has auxiliary data and is loaded with a proper function (e.g. PEM_read_bio_X509_AUX() or SSL_CTX_load_verify_locations()), trust settings are verified with X509_check_trust(). The actual trust depends on the purpose of a connection, e.g. NID_server_auth for a client that wants to verify a connection to a TLS web server. For TLS server and client connections, ssl_verify_cert_chain() calls X509_verify_cert() to verify the chain including trust settings. If X509_TRUST_REJECTED is returned by check_trust(), variable 'ok' is still set to 1 from previous call.
Created attachment 1116900 [details]
This issue was discovered by Christian Heimes of Red Hat.
I have some updates. It looks like OpenSSL 1.0.2 is broken since https://git.openssl.org/?p=openssl.git;a=commit;h=d65b8b2162f33ac0d53dace588a0847ed827626c
OpenSSL 1.0.0 is not affected. check_trust() returns ok flag.
OpenSSL 1.0.1 is not affected. check_trust() also returns ok flag.
OpenSSL 1.0.2 is buggy. check_trust() has been altered and now returns trust flags (X509_TRUST_TRUSTED, X509_TRUST_REJECTED or X509_TRUST_UNTRUSTED). X509_verify_cert() fails to set ok=0 for X509_TRUST_REJECTED. It might also handle X509_TRUST_UNTRUSTED wrong. My patch might be incomplete, I have to look into that case more closely. The X509_TRUST_UNTRUSTED case can occure for certs with e.g. '-addtrust clientAuth'. In that case a certificate is not explicitly distrusted for serverAuth but lacks the trust bit for serverAuth.
OpenSSL master (1.1.0 development) has a different trust and chain building code. It needs further investigation.
Created attachment 1117928 [details]
OpenSSL patch with test cases
Our code does not diverge from upstream 1.0.1 branch in this regard so it is not vulnerable.
According to maintainer, the issue is addressed in master (1.1.0-dev) where the chain construction code has been completely rewritten. The 1.1.0-dev code not only avoids this problem, but also exercises explicit reject auxiliary data in the verify tests.
Created attachment 1127323 [details]
Proposed patch from upstream
Mark, this ticket is about a bug in the trust check code, not about BC / EKU. It's a bug in the trust verification code that was introduced in 2012, https://github.com/openssl/openssl/commit/d65b8b2162f33ac0d53dace588a0847ed827626c#diff-06463ef55f4d9d5411a243b09e66c06fL668
An attack scenario may look like this.
'Evil Country' has a CA called 'Evil CA' that is controlled by an autocratic government. 'Evil CA' has EKU for S/MIME and TLS web server auth. I don't fully trust 'Evil CA' because I know that it signs certs for TLS interception so the government can spy on users. But I still want to trust the CA for S/MIME signing because state officials sign emails.
With OpenSSL's trust feature I can modify the root anchor and make sure that 'Evil CA' is rejected for TLS server auth. After all the feature is documented in the man page of openssl x509. Due to a bug in OpenSSL my policy is silently ignored. 'Evil CA' is able to sign a cert for *.redhat.com and intercept all traffic with Red Hat.
About three years ago we wanted to use the feature in Python and PyOpenSSL. But we weren't able to make trust reject work. We thought it was a problem on our side and gave up. About a month ago I picked up my experiments again. This time I was able to find the bug -- not in my code but in OpenSSL. I don't know if anybody actually uses the feature. But we were almost using it in Python, PyOpenSSL, python-requests and python-cryptography. Just our rigorous unit testing policy prevented us from introducing a serious bug into the Python ecosystem.
I agree that the issue a low severity. As far as I know Red Hat based distros (RHEL, Fedora,...) are the only distros that ship a PEM bundle with TRUSTED CERTIFICATEs, too.
Are you sure that nobody uses /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt or /etc/pki/tls/certs/ca-bundle.trust.crt ?
You can release the embargo. The fix https://github.com/openssl/openssl/commit/a3baa171053547488475709c7197592c66e427cf is in today's OpenSSL 1.0.2g release. The bug wasn't mentioned in the changelog for 1.0.2g and hasn't got a CVE assigned to.