There is a double free bug in curl when using p12 client certificate to connect to an https server.
Connect to any https server using a client certificate in pkcs12 format.
curl -vvv --cert yourcertificate.p12:yourpassword --cert-type P12 https://yourserver.com
eg:
Bug is 100% reproducible.
Affected version are curl-7.24 to 7.30.
Bug was introduced in following commit to fix a memory leak:
https://github.com/bagder/curl/commit/6ea7acf5a96786f7514be4fbce174cdc8bedfdd1
on line 512 of ssluse.c: sk_X509_pop_free(ca, X509_free);
curl is freeing data related to the handling of the certificate which may have already being freed by SSL_CTX_free call made on line 865 of ssluse.c when we are closing the ssl connexion.
Here is a valgrind stacktrace:
==4136== Invalid free() / delete / delete[] / realloc()
==4136== at 0x4C2BA6C: free (vg_replace_malloc.c:446)
==4136== by 0x5B964EC: CRYPTO_free (mem.c:397)
==4136== by 0x5C4029F: asn1_item_combine_free (tasn_fre.c:176)
==4136== by 0x5C40484: ASN1_item_free (tasn_fre.c:71)
==4136== by 0x5C1E96F: sk_pop_free (stack.c:283)
==4136== by 0x590F806: SSL_CTX_free (ssl_lib.c:1947)
==4136== by 0x4E67BD4: Curl_ossl_close (ssluse.c:865)
==4136== by 0x4E89822: Curl_ssl_close (sslgen.c:421)
==4136== by 0x4E60906: Curl_disconnect (url.c:2565)
==4136== by 0x4E81B40: multi_runsingle (multi.c:1637)
==4136== by 0x4E81D54: curl_multi_perform (multi.c:1708)
==4136== by 0x4E76CDC: curl_easy_perform (easy.c:448)
==4136== Address 0x6636d00 is 0 bytes inside a block of size 184 free'd
==4136== at 0x4C2BA6C: free (vg_replace_malloc.c:446)
==4136== by 0x5B964EC: CRYPTO_free (mem.c:397)
==4136== by 0x5C4029F: asn1_item_combine_free (tasn_fre.c:176)
==4136== by 0x5C40484: ASN1_item_free (tasn_fre.c:71)
==4136== by 0x5C1E96F: sk_pop_free (stack.c:283)
==4136== by 0x4E67378: cert_stuff (ssluse.c:510)
==4136== by 0x4E68F54: ossl_connect_step1 (ssluse.c:1524)
==4136== by 0x4E6BBC8: ossl_connect_common (ssluse.c:2433)
==4136== by 0x4E6BE46: Curl_ossl_connect_nonblocking (ssluse.c:2522)
==4136== by 0x4E890A2: Curl_ssl_connect_nonblocking (sslgen.c:218)
==4136== by 0x4E49D5C: https_connecting (http.c:1345)
==4136== by 0x4E49CBA: Curl_http_connect (http.c:1315)
I'm not sure the bug is exploitable nor i see any practical case.
nb: The joined pkcs12 certificate doesn't need password.
Are you really talking about line numbers in the 7.30.0 version of ssluse.c?
The line number i'm referring to are the line number of the commit, thus version 7.24. However, the valgrind stacktrace lines should be referring to the last version.
(Actually the function Curl_ossl_close() is on line 855 in ssluse.c of the version 7.24 and i guess at line 865 in latest)
I've tried to repeat your issue with the p12 file you provided and an edited version of the command line you said you used, but I don't have any site that requires client cert so I don't know exactly how to repeat it as my command line will only end in a "curl: (58) unable to use client certificate (no key found or wrong pass phrase?)" ...
Why is a client cert using the substring 'ca' in it? It sounds suspiciously wrong to me.
Any chance you have a suggested patch for the problem?
You forgot to put the "--cert-type P12" in the command line argument.
It succesfully crashed on 7.30 as well.
However, I surprisingly couldn't reproduce either with your 7.31 developement version:
I don't have a suggested patch I don't know curl code enough, i only notice we could avoid the problem by commenting out the last "sk_X509_pop_free(ca, X509_free);" but It could and would probably introduce a previously patched memory leak.
Last edit: Nikaiw 2013-06-10
Even in 7.31 valgrind is reporting.
So the crash is maybe avoid just because of how the heap is used. My 7.31 compiled version is linked with way less library.
Thanks, I can repeat it at will now. But I haven't quite worked out what the suitable way forward is.
I don't quite understand how we are supposed to deal with the list we get back in the 'ca' argument to the PKCS12_parse() call.
If we call sk_X509_pop_free() like today (I do have a cleaned up code patch to propose but that isn't strictly relevant so I'll save that for now) we get the error you report here, but if we remove the call completely we instead get a memory leak! Have you figured out what the correct logic is?
It's a tricky bug I spend some time on it. I haven't figured out yet what is the correct logic. I think curl should be able to know if the certificate structure has already be cleaned by a closing of the ssl connexion.
The usual way to avoid such problem is to set the pointer to NULL after it is freed. But in this case, I think the certificate parsed structure is part of another big structure which is free (as the client certificate is part of the ssl connection), and the cleaning is made into the libssl library.
It is the call to SSL_CTX_add_extra_chain_cert() that creates the problem. If we remove that call, things work just fine with the sk_X509_pop_free() call still present.
I found it. In commit 7b97f03f09 I no longer get any crash and no leak. Please try it out and see that it works for you too!
Fix is fine for me.