SSL_use_certificate似乎导致双重释放 [英] SSL_use_certificate seems to be causing a double free

查看:628
本文介绍了SSL_use_certificate似乎导致双重释放的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一些上下文

我使用openSSL在C ++中编写了一个透明/拦截,支持HTTPS的代理。我使用WinDivert重定向流量通过我的代理。对于我的SSL初始化,我的HTTPSAcceptor为握手操作的整个服务器上下文生成一个临时EC_KEY。我保留一个内存中的存储(不是实际的X509_STORE对象),其中我使用主机/ SAN DNS条目作为查找键欺骗和存储证书。作为附注,这是我第一次使用openSSL,所以请纠正和赦免任何无知在我的方法。



肉类

无论如何,当我得到一个传入的HTTPS连接,我检索或欺骗,然后检索真正的上游证书。当我生成这些证书,我使用EC键。 Les code:

  EC_KEY * ecdh = NULL; 

if((ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1))== NULL || EC_KEY_generate_key(ecdh)!= 1)
{
std :: cout< In CertStore :: GenerateEcKey() - Failed to generate EC_KEY<< std :: endl;
}
else
{
EC_KEY_set_asn1_flag(ecdh,OPENSSL_EC_NAMED_CURVE);

EVP_PKEY * pkey = NULL;

pkey = EVP_PKEY_new();

if(pkey == nullptr)
{
std :: cout< 在CertStore :: GenerateEcKey() - 无法生成EVP_PKEY< std :: endl;
}
else
{
if(1!= EVP_PKEY_set1_EC_KEY(pkey,ecdh))
{
std :: cout< In CertStore :: GenerateEcKey() - Failed EVP_PKEY_set1_EC_KEY< std :: endl;
EVP_PKEY_free(pkey);
return nullptr
} else {

EC_KEY_up_ref(ecdh);
return pkey;
}
}
}

成功获取欺骗性证书&关联键,然后告诉我的SSL *对象来使用这些来进行握手。

  if(SSL_use_PrivateKey(m_secureDownstreamSocket-> ; native_handle(),upKey)< = 0)
{
std :: cout< set private key failed<< std :: endl;
Kill();
return;
}

if(SSL_use_certificate(m_secureDownstreamSocket-> native_handle(),upCert)< = 0)
{
std :: cout< set use cert failed< std :: endl;
Kill();
return;
}

m_secureDownstreamSocket-> async_handshake(SslSocket :: server,m_strand.wrap(boost :: bind(& HttpsBridge :: OnDownstreamHandshake,shared_from_this(),boost :: asio :: placeholder :: error)));

但是,这个原因似乎是我的应用程序死亡的可怕死亡的起源。我曾经在每个HTTPS连接(客户端和服务器)上生成一个新的CTX,但在文档和一些SO帖子中做一些阅读后,我被引导相信正确的方式是使用全局上下文创建SSL对象。无论如何,提到的是,我得到的错误,我很快会提出,过去很少发生时,当我是愚蠢的和创建一吨CTX的。由于更改为两个全局CTX(客户端,服务器),此错误现在发生得非常快,但仍然在随机点。



错误,是不知何故,EC_GROUP的键被双重释放。问题是,我甚至不知道他们为什么被释放在第一位。我可以找到没有提到任何SSL_ *或SSL_CTX *方法的文档,我使用释放任何东西。下面是来自App Verifier的事件跟踪,因为Eclipse在调试时是无用的,并且Visual Studio调试器在我拦截和处理本地网络流量时只是以某种方式拒绝工作。请网络,帮助。 :(

 <?xml version =1.0encoding =UTF-8standalone =no?& 
< avrf:logfile xmlns:avrf =Application Verifier>
< avrf:logSession TimeStarted =2015-04-05:23:51:30PID =812Version = 2>
< avrf:logEntry Time =2015-04-05:23:51:57LayerName =HeapsStopCode =0x7Severity =Error>
< / avrf:message>
< avrf:parameter1> 8411000 - 拥有该块的堆的堆栈句柄< / avrf:parameter1>
< / avrf:parameter2>
< avrf:parameter3> 20 - 堆块的大小< / avrf:parameter3>
< avrf:parameter4> 0 - 未使用< / avrf:parameter4>
< avrf:stackTrace>
< avrf:trace> vrfcore!VerifierDisableVerifier + 948(@ 0)< / avrf: trace>
< avrf:trace> verifier!VerifierStopMessage + a0(@ 0)< / avrf:trace>
< avrf:trace> verifier!VerifierDisableFaultInjectionExclusionRange + 318b(@ 0)< / avrf:trace>
< avrf:trace> verifier!VerifierDisableFaultInjectionExclusionRange + 8a6(@ 0)< / avrf:trace>
< avrf:trace> verifier!VerifierDisableFaultInjectionExclusionRange + 94b(@ 0)< / avrf:trace>
< avrf:trace> verifier!VerifierCheckPageHeapAllocation + 40(@ 0)< / avrf:trace>
< avrf:trace> vfbasics!+ 7ff99e7f3773(@ 0)< / avrf:trace>
< avrf:trace> msvcrt!setjmp + 123(@ 0)< / avrf:trace>
< avrf:trace> vfbasics!+ 7ff99e7f4606(@ 0)< / avrf:trace>
< avrf:trace> LIBEAY32!CRYPTO_free + 2b(@ 0)< / avrf:trace>
< avrf:trace> LIBEAY32!BN_free + 29(@ 0)< / avrf:trace>
< avrf:trace> LIBEAY32!EC_GROUP_cmp + 307(@ 0)< / avrf:trace>
< avrf:trace> LIBEAY32!EC_GROUP_free + 2c(@ 0)< / avrf:trace>
< avrf:trace> LIBEAY32!EC_KEY_set_group + 2b(@ 0)< / avrf:trace>
< avrf:trace> LIBEAY32!EC_GF2m_simple_method + 180f(@ 0)< / avrf:trace>
< avrf:trace> SSLEAY32!SSL_use_PrivateKey_ASN1 + 1a5(@ 0)< / avrf:trace>
< avrf:trace> SSLEAY32!SSL_use_certificate + 9a(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 407ef6(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 4a2dbf(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 4a2e0a(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 45b6b1(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 45c50e(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+488870(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+461241(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+451908(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 47d3a0(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+451938(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+472739(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 45e9c4(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+474001(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 4a4098(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 465a7d(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 488af1(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 47774c(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+461001(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+451488(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 47ce40(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 4514b8(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 478de7(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 470f8b(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 45e2c7(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 47d3f4(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 451e18(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 464f44(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 451e48(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 4819b1(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 47cc68(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 47f2d2(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 47ecb8(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 45db6c(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 4a2c75(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 45b32c(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 45ce36(@ 0)< / avrf:trace>
< avrf:trace> SSITM!+ 48ce4e(@ 0)< / avrf:trace>
< avrf:trace> libboost_thread!ZN5boost6detail23get_current_thread_dataEv + 729(@ 0)< / avrf:trace>
< avrf:trace> msvcrt!strupr + c3(@ 0)< / avrf:trace>
< avrf:trace> msvcrt!endthreadex + 9d(@ 0)< / avrf:trace>
< avrf:trace> vfbasics!+ 7ff99e7fc729(@ 0)< / avrf:trace>
< avrf:trace> KERNEL32!BaseThreadInitThunk + 22(@ 0)< / avrf:trace>
< avrf:trace> ntdll!RtlUserThreadStart + 34(@ 0)< / avrf:trace>
< / avrf:stackTrace>
< / avrf:logEntry>
< / avrf:logSession>
< / avrf:logfile>

- 对我来说奇怪的是,stacktrace显示了对SSL_use_PrivateKey_ASN1的调用。不知道为什么,因为我只是每次调用SSL_use_cert和SSL_use_prvkey。可能是因为use_cert正在尝试从cert提取私钥?只是有了这个想法,我要调查更新 - 不是。不可能为X509结构添加私钥,并且出于安全考虑。

解决方案

因此,Michael Foukarakis帮助我在评论中对我的一些假设提出质疑这最终导致我解决这个问题,所以完全信贷给他。



如问题所述,我最初创建了一个新的 SSL_CTX boost :: asio :: ssl :: context )对象:一个用于下游,一个向上。上游,作为代理充当客户端,有 boost :: asio :: ssl :: context :: load_verify_file ca-bundle.crt)在初始化期间的上下文,这导致应用程序的ram消耗的大量膨胀。



原始问题的第二部分是我为每个下游连接创建了一个新的 SSL_CTX 到我们的客户端,我们正在提供一个欺骗性证书和握手假装是原始服务器。每个新的连接桥都被引用到构造上的CertStore对象,该对象被设计为欺骗,存储和检索由主机名索引的证书及其密钥对。



因此,我们所做的是一个中心位置保存证书和密钥,但每个 SSL_CTX 这些证书和密钥,然后在连接关闭时被销毁,这最终随机将导致一个双重自由的地方,因为openSSL使用内部引用计数(最多?)对象。这些引用将通过创建 SSL_CTX 而增加和减少,具有 SSL_CTX_use_certificate SSL_CTX_use_PrivateKey 被调用,然后销毁。最终,两个 SSL_CTX 的死亡将持有对引用计数为1的相同的cert或密钥对的引用,导致当两个上下文都终止时双重释放内存。



解决方案是使用一个共享上下文,保持安全和活着,直到上游(客户端)连接的应用程序关闭。调用 boost :: asio :: ssl :: context :: load_verify_file ca-bundle.crt),然后让所有的客户端SSL对象从它生成。对于服务器上下文,为每个主机创建一个服务器上下文,设置该上下文的证书和私钥,然后在代表该主机的所有下游SSL *套接字之间共享该上下文。 p>

Some Context
I'm writing a transparent/intercepting, HTTPS capable proxy in C++ using openSSL. I'm redirecting traffic through my proxy using WinDivert. For my SSL initialization, my HTTPSAcceptor generates a temporary EC_KEY for the entire server context for the handshake operation. I keep an in-memory "store" (Not an actual X509_STORE object) where I spoof and store certificates using host/SAN DNS entries as the lookup keys. As a side note, this is the first time I've ever worked with openSSL so please correct and pardon any ignorance in my approach. :) Also pardon the excessive abuse of cout for puking debugging/errors, these will later be wrapped into a logger.

The Meat
Anyway so with that, when I get an incoming HTTPS connection, I either retrieve or spoof then retrieve the real upstream certificate. When I generate these certs, I'm using EC Keys. Les code:

EC_KEY *ecdh = NULL;

if ((ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || EC_KEY_generate_key(ecdh) != 1)
{
    std::cout << "In CertStore::GenerateEcKey() - Failed to generate EC_KEY" << std::endl;
}
else
{
    EC_KEY_set_asn1_flag(ecdh, OPENSSL_EC_NAMED_CURVE);

    EVP_PKEY* pkey = NULL;

    pkey = EVP_PKEY_new();

    if (pkey == nullptr)
    {
        std::cout << "In CertStore::GenerateEcKey() - Failed to generate EVP_PKEY" << std::endl;
    }
    else
    {
        if (1 != EVP_PKEY_set1_EC_KEY(pkey, ecdh))
        {
            std::cout << "In CertStore::GenerateEcKey() - Failed EVP_PKEY_set1_EC_KEY" << std::endl;
            EVP_PKEY_free(pkey);
            return nullptr;
        }else{

            EC_KEY_up_ref(ecdh);
            return pkey;
        }
    }
}

Upon successfully fetching a spoofed cert & associated key, I then tell my SSL* object to use these for the handshake, obviously.

if (SSL_use_PrivateKey(m_secureDownstreamSocket->native_handle(), upKey) <= 0)
{
    std::cout << "set private key failed" << std::endl;
    Kill();
    return;
}

if (SSL_use_certificate(m_secureDownstreamSocket->native_handle(), upCert) <= 0)
{
    std::cout << "set use cert failed" << std::endl;
    Kill();
    return;
}

m_secureDownstreamSocket->async_handshake(SslSocket::server, m_strand.wrap(boost::bind(&HttpsBridge::OnDownstreamHandshake, shared_from_this(), boost::asio::placeholders::error)));

However, this causes seems to be the origin of my application dying a terrible death. I used to generate a new CTX on each HTTPS connection (both client and server), but after doing some reading in the docs and a few SO posts, I was led to believe that the correct way is to use a global context for creating SSL objects. Anyway the point of mentioning that was that the error I'm getting, which I'll present shortly, used to occur very rarely when I was being dumb and creating a ton of CTX's. Since changing to two global CTX's (client, server), this error now occurs very quickly, but still at random points.

The error, is that somehow, EC_GROUP's from the keys are being double freed. The issue is that I don't even know why they are being freed in the first place. I can find no mention in the docs of any of the SSL_* or SSL_CTX* methods that I use freeing anything. Below is the trace of events from App Verifier, since Eclipse is being useless at debugging this and visual studio debugger simply somehow refuses to work while I'm intercepting and processing local network traffic. Please interwebs, help. :(

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<avrf:logfile xmlns:avrf="Application Verifier">
    <avrf:logSession TimeStarted="2015-04-05 : 23:51:30" PID="812" Version="2">
        <avrf:logEntry Time="2015-04-05 : 23:51:57" LayerName="Heaps" StopCode="0x7" Severity="Error">
            <avrf:message>Heap block already freed.</avrf:message>
            <avrf:parameter1>8411000 - Heap handle for the heap owning the block.</avrf:parameter1>
            <avrf:parameter2>aac49270 - Heap block being freed again.</avrf:parameter2>
            <avrf:parameter3>20 - Size of the heap block.</avrf:parameter3>
            <avrf:parameter4>0 - Not used</avrf:parameter4>
            <avrf:stackTrace>
                <avrf:trace>vrfcore!VerifierDisableVerifier+948 ( @ 0)</avrf:trace>
                <avrf:trace>verifier!VerifierStopMessage+a0 ( @ 0)</avrf:trace>
                <avrf:trace>verifier!VerifierDisableFaultInjectionExclusionRange+318b ( @ 0)</avrf:trace>
                <avrf:trace>verifier!VerifierDisableFaultInjectionExclusionRange+8a6 ( @ 0)</avrf:trace>
                <avrf:trace>verifier!VerifierDisableFaultInjectionExclusionRange+94b ( @ 0)</avrf:trace>
                <avrf:trace>verifier!VerifierCheckPageHeapAllocation+40 ( @ 0)</avrf:trace>
                <avrf:trace>vfbasics!+7ff99e7f3773 ( @ 0)</avrf:trace>
                <avrf:trace>msvcrt!setjmp+123 ( @ 0)</avrf:trace>
                <avrf:trace>vfbasics!+7ff99e7f4606 ( @ 0)</avrf:trace>
                <avrf:trace>LIBEAY32!CRYPTO_free+2b ( @ 0)</avrf:trace>
                <avrf:trace>LIBEAY32!BN_free+29 ( @ 0)</avrf:trace>
                <avrf:trace>LIBEAY32!EC_GROUP_cmp+307 ( @ 0)</avrf:trace>
                <avrf:trace>LIBEAY32!EC_GROUP_free+2c ( @ 0)</avrf:trace>
                <avrf:trace>LIBEAY32!EC_KEY_set_group+2b ( @ 0)</avrf:trace>
                <avrf:trace>LIBEAY32!EC_GF2m_simple_method+180f ( @ 0)</avrf:trace>
                <avrf:trace>SSLEAY32!SSL_use_PrivateKey_ASN1+1a5 ( @ 0)</avrf:trace>
                <avrf:trace>SSLEAY32!SSL_use_certificate+9a ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+407ef6 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+4a2dbf ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+4a2e0a ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+45b6b1 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+45c50e ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+488870 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+461241 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+451908 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+47d3a0 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+451938 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+472739 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+45e9c4 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+474001 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+4a4098 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+465a7d ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+488af1 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+47774c ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+461001 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+451488 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+47ce40 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+4514b8 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+478de7 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+470f8b ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+45e2c7 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+47d3f4 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+451e18 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+464f44 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+451e48 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+4819b1 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+47cc68 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+47f2d2 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+47ecb8 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+45db6c ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+4a2c75 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+45b32c ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+45ce36 ( @ 0)</avrf:trace>
                <avrf:trace>SSITM!+48ce4e ( @ 0)</avrf:trace>
                <avrf:trace>libboost_thread!ZN5boost6detail23get_current_thread_dataEv+729 ( @ 0)</avrf:trace>
                <avrf:trace>msvcrt!strupr+c3 ( @ 0)</avrf:trace>
                <avrf:trace>msvcrt!endthreadex+9d ( @ 0)</avrf:trace>
                <avrf:trace>vfbasics!+7ff99e7fc729 ( @ 0)</avrf:trace>
                <avrf:trace>KERNEL32!BaseThreadInitThunk+22 ( @ 0)</avrf:trace>
                <avrf:trace>ntdll!RtlUserThreadStart+34 ( @ 0)</avrf:trace>
            </avrf:stackTrace>
        </avrf:logEntry>
    </avrf:logSession>
</avrf:logfile>

P.S. - One thing that's strange to me is that the stacktrace shows a call to SSL_use_PrivateKey_ASN1. Not sure why, because I'm only every calling SSL_use_cert and SSL_use_prvkey.Could it be that use_cert is trying to extract the private key from the cert? Just had that thought, I'm going to investigate Update - Nope. It's not possible to add private keys to X509 structures and for good security reasons. Was a desperate, not-thought-out idea.

解决方案

So, Michael Foukarakis helped me question some of my assumptions in the comments which ultimately led to me solving this issue, so full credits to him. Here is where I went wrong in my approach and how I resolved it.

As mentioned in the question, I was originally creating a new SSL_CTX (boost::asio::ssl::context) object for every single proxy connection: one for the downstream, one up. The upstream, being the proxy acting as a client, was having boost::asio::ssl::context::load_verify_file("ca-bundle.crt") on it's context during initialization, which was leading to massive bloat in the application's ram consumption.

The second part of the original problem was that I was creating a new SSL_CTX for each downstream connection, the connection to our client where we're serving a spoofed certificate and handshaking pretending to be the origin server. Each new connection "bridge" is handed a reference to a "CertStore" object on construction that is designed to spoof, store and retrieve certificates and their keypairs, indexed by the host name.

So what we had going on was a central place holding certs and keys, but each individual SSL_CTX object being assigned these certs and keys and then destroyed when the connection closed, which eventually, "randomly" would lead to a double-free somewhere since openSSL uses internal reference counting on (most?) objects. These references would be increased and decreased by SSL_CTX's being created, having SSL_CTX_use_certificate and SSL_CTX_use_PrivateKey being called on them, then destroyed. Eventually two SSL_CTX's dying would be holding a reference to the same cert or keypair with a reference count of 1, leading to a double free of the memory when both contexts finally died.

The solution is to use a single shared context that you keep safe and alive somewhere until application shutdown for your upstream (client) connections. Call boost::asio::ssl::context::load_verify_file("ca-bundle.crt") on that single context once, then have all client SSL-Object spawn from it. As for server contexts, create one server context per-host that you're acting as, set the cert and private key of that context, and then share that context between all downstream SSL* sockets that act on behalf of that host.

这篇关于SSL_use_certificate似乎导致双重释放的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆