OpenSSL keychain certificate verification broken with callbacks
| Originator: | bruno | ||
| Number: | rdar://10509597 | Date Originated: | 01-Dec-2011 10:03 AM |
| Status: | Open | Resolved: | |
| Product: | Mac OS X | Product Version: | 10.7.2 |
| Classification: | Other Bug | Reproducible: | Always |
Summary:
Since Snow Leopard, Apple patched the OpenSSL library that ships with the OS to look for trusted root certificates in the Mac OS X keychain. Essentially, the patch is here: http://opensource.apple.com/source/OpenSSL098/OpenSSL098-27/src/crypto/x509/x509_vfy_apple.c
The patch correctly affects the output of SSL_get_verify_result (see "Test 0" below).
However, the patched OpenSSL library operates incorrectly with respect to the "verify callback" functionality of OpenSSL (functions SSL_CTX_set_verify and SSL_set_verify); during the certificate verification, registered callback functions are called with wrong verification results (see "Test 1").
The problem originates from the fact that the patch is implemented as a wrapper to the original X509_verify_cert function in OpenSSL, so it comes "too late" to influence the callback mechanism.
The situation is even worse when a callback indicates to continue the certificate verification process in case of an error. In that case, the verify result may change into something other than the single verify result that the patch picks up (X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY), causing the patch not to work at all and causing the verification to fail entirely (see "Test 2" below).
Steps to Reproduce:
The included ssl-client.c is a minimal program that makes an SSL connection to the https service of www.apple.com. It does basic certificate verification ("Test 0"), verification with a callback that does not indicate to continue in case of errors ("Test 1"), and verification with a callback that does indicate to continue ("Test 2").
1. Compile ssl-client.c using the following command:
gcc -o ssl-client ssl-client.c -lssl -lcrypto
2. Verify that no trusted root certificates are installed in
/System/Library/OpenSSL/cert.pem
or
/System/Library/OpenSSL/certs/
3. Run ssl-client by tying:
./ssl-client
Expected Results:
Test 0: No callback
Verify OK
Test 1: Callback without continue
Preverify call: ok, depth 2, err 0
Preverify call: ok, depth 1, err 0
Preverify call: ok, depth 0, err 0
Verify OK
Test 2: Callback with continue
Preverify call: ok, depth 2, err 0
Preverify call: ok, depth 1, err 0
Preverify call: ok, depth 0, err 0
Verify OK
Actual Results:
Test 0: No callback
Verify OK
Test 1: Callback without continue
Preverify call: err, depth 1, err 20
Verify OK
Test 2: Callback with continue
Preverify call: err, depth 1, err 20
Preverify call: err, depth 1, err 27
Preverify call: ok, depth 0, err 27
Verify failed
Regression:
This problem can be equally reproduced on both Snow Leopard (10.6.8) and Lion (10.7.2).
Notes:
This problem affects other open source software that ships with Mac OS X. Please see the following reports on the web.
http://redmine.ruby-lang.org/issues/3150
http://permalink.gmane.org/gmane.comp.web.webdav.neon.general/815
It is very important to note that on Mac OS X Lion, the version of Neon that is included (0.29.0), employs OpenSSL callbacks and therefore fails to verify any certificates. This greatly affect the usability of the included Subversion, which employs Neon for https connection. (Back on Mac OS X Snow Leopard, the included version of Neon (0.28.6) did not use callbacks yet.)
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!
ssl-client.c
include
include
include
include
include
include
include
include
include
include
include
int cont = 0;
int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { int depth = X509_STORE_CTX_get_error_depth(x509_ctx); int err = X509_STORE_CTX_get_error(x509_ctx); fprintf(stderr, "Preverify call: %s, depth %d, err %dn", preverify_ok == 0 ? "err" : "ok", depth, err); return cont ? 1 : preverify_ok; }
void ssl_test(struct addrinfo ai, SSL_CTX ctx) { int r;
}
int main() { // Init SSL_load_error_strings(); SSL_library_init();
}