c++ - SSL_use_certificate 似乎导致双重释放

标签 c++ ssl openssl boost-asio

一些背景
我正在使用 openSSL 在 C++ 中编写一个透明/拦截的、支持 HTTPS 的代理。我正在使用 WinDivert 通过我的代理重定向流量。对于我的 SSL 初始化,我的 HTTPSAcceptor 为握手操作的整个服务器上下文生成一个临时的 EC_KEY。我在内存中保留了一个“存储”(不是实际的 X509_STORE 对象),我在其中使用主机/SAN DNS 条目作为查找键来欺骗和存储证书。作为旁注,这是我第一次使用 openSSL,所以请更正并原谅我的方法中的任何无知。 :) 同时请原谅过度滥用 cout 来调试/错误,这些稍后将被包装到一个记录器中。


无论如何,当我获得传入的 HTTPS 连接时,我要么检索或欺骗,然后检索真正的上游证书。当我生成这些证书时,我使用的是 EC key 。代码:

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;
        }
    }
}

在成功获取欺骗性证书和关联 key 后,显然我会告诉我的 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::placeholders::error)));

但是,这个原因似乎是我的应用程序死于可怕死亡的起源。我曾经在每个 HTTPS 连接(客户端和服务器)上生成一个新的 CTX,但是在阅读了一些文档和一些 SO 帖子之后,我相信正确的方法是使用全局上下文来创建 SSL对象。不管怎样,我要提到的一点是,当我愚蠢地创建大量 CTX 时,我得到的错误很少发生,我将在稍后介绍。由于更改为两个全局 CTX(客户端、服务器),此错误现在发生得非常快,但仍然在随机点。

错误是,不知何故,来自键的 EC_GROUP 被双重释放。问题是我什至不知道为什么他们一开始就被释放了。我在文档中找不到任何我使用释放任何东西的 SSL_* 或 SSL_CTX* 方法的提及。下面是来自 App Verifier 的事件跟踪,因为 Eclipse 在调试这个时毫无用处,而 visual studio 调试器在我拦截和处理本地网络流量时只是以某种方式拒绝工作。请各位网友帮忙。 :(

<?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>

附言- 让我感到奇怪的一件事是堆栈跟踪显示对 SSL_use_PrivateKey_ASN1 的调用。不知道为什么,因为我只是每次调用 SSL_use_cert 和 SSL_use_prvkey。难道 use_cert 试图从证书中提取私钥?只是有那个想法,我要调查 更新 - 不。出于良好的安全原因,不可能将私钥添加到 X509 结构。这是一个绝望的、未经深思熟虑的想法。

最佳答案

我被这个问题困扰了,感谢你的跟进回答。

我的解决方案不是在创建它的析构函数中删除 ssl::context 实例,而是将删除发布到主 io_service:

类似于:

MyThing::~MyThing() {
    boost::asio::ssl::context *c = ssl_context_;
    socket_.get_io_service().post([c](){ delete c; });
}

这对我来说似乎很好地治愈了它。 (我使用 new/delete 是因为我只在需要时创建它)。

我认为必须有一种更确定的方法来执行此操作,也许是 shared_ptr,但我还没有解决。

关于c++ - SSL_use_certificate 似乎导致双重释放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29465501/

相关文章:

c++ - 模板函数特化默认

c# - 使用 XmlTextReader https 请求忽略无效的 SSL(版本 3)证书

security - 从命令行确定 TLS/SSL 证书是否为 'trusted'?

c++ - 样式检查:错误:打开括号 '('前需要空格

c++ - 智能指针与自动引用计数

.htaccess - 删除 SSL 后的浏览器警告

Python-Requests 验证参数如何工作? SSL 证书

c++ - 如何初始化 OpenSSL BIO 对象?

c++ - 测试返回三角形类型的函数

ssl - 带有 SSL 加密的私有(private) Docker 注册表前端