gpt4 book ai didi

c++ - 多线程 C++ 应用程序在 SSL 证书验证中的内存泄漏

转载 作者:行者123 更新时间:2023-11-30 01:43:03 31 4
gpt4 key购买 nike

我们在多线程 C++ 应用程序中使用 openssl 库。由于内存泄漏,应用程序会在 3 天内(7 GB 实例)耗尽所有内存,前提是启用了 SSL 证书验证。

请在这里找到我的申请流程:

在应用启动时,我们创建了 150 个线程来同步 30k 用户数据并为每个线程保留一个 SSL_CTX_new 对象。重复使用相同的对象,直到进程被终止。对象 SSL_CTX_new 仅在线程初始化时创建一次,它将被所有后续 ssl 连接重用。

我们在处理用户数据时执行以下操作:

线程通过 ssl 创建到第三方服务器的新套接字连接,一旦从第三方服务器获取所需数据,ssl 连接就会终止。类似地,所有线程一次从队列中挑选一个用户并从服务器获取数据。

我们必须为所有 30k 用户执行上述连接、获取数据和断开 ssl 连接。

请找到我们应用程序的伪代码示例:

ThreadTask() 
{
ssldata* ssl_data;

1. Creates SSL connection: user_ssl_new_connect(ssl_data)
2. Fetch users data
3. Terminate ssl connection: ssl_abort()
}



char* user_ssl_new_connect(ssldata* ssl_data) {

SSL_CTX *ssl_context = InitsslonePerThread();
if (!ssl_context) {
if (!(ssl_context = SSL_CTX_new (SSLv23_client_method ()) {
retur NULL;
}
}

SSL_CTX_set_options (ssl_context,SSL_OP_NO_COMPRESSION|SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_verify (ssl_context,SSL_VERIFY_PEER,ssl_open_verify);
SSL_CTX_set_default_verify_paths (ssl_context);
char * s = "sslpath"
SSL_CTX_load_verify_locations (ssl_context,s,NIL);
SetsslconnectionPerThread(ssl_context);

if (!(ssl_data->sslconnection = (SSL *) SSL_new (ssl_context)))
return NULL

bio = BIO_new_socket (ssl_data->sockettcpsi,BIO_NOCLOSE);
SSL_set_bio (ssl_data->sslconnection,bio,bio);
SSL_set_connect_state(ssl_data->sslconnection);
if (SSL_in_init(ssl_data->sslconnection)) SSL_total_renegotiations (ssl_data->sslconnection);
/* now negotiate SSL */
if ((retval = SSL_write (ssl_data->sslconnection,"",0)) < 0) {
return NULL
}
/* validating host names? */
if ((err = ssl_validate_cert (cert = SSL_get_peer_certificate (sslconnection),host))) {
return NULL;
}
}


// one ssl_context per thread in global variable
ssl_context* InitsslonePerThread() {

yULong threadid;
threadid = (unsigned long) pthread_self();

if ssl_context is not created for this threadid
returns new ssl_context.
else
returns previous ssl_context.
}

void SetsslconnectionPerThread(ssl_context*) {

yULong threadid;
threadid = (unsigned long) pthread_self();

#setting ssl_context in global variable
}


long ssl_abort (ssldata* ssl_data)
{
if (ssl_data->sslconnection) { /* close SSL connection */
SSL_shutdown (ssl_data->sslconnection);
SSL_free (ssl_data->sslconnection);
}
return NIL;
}



static char *ssl_validate_cert (X509 *cert,char *host)
{
int i,n;
char *s,*t,*ret;
void *ext;
GENERAL_NAME *name;

char tmp[MAILTMPLEN];

/* make sure have a certificate */
if (!cert) ret = "No certificate from server";
/* and that it has a name */
else if (!cert->name) ret = "No name in certificate";
/* locate CN */
else if (s = strstr (cert->name,"/CN=")) {
if (t = strchr (s += 4,'/')) *t = '\0';
/* host name matches pattern? */
ret = ssl_compare_hostnames (host,s) ? NIL :
"Server name does not match certificate";
if (t) *t = '/'; /* restore smashed delimiter */
/* if mismatch, see if in extensions */
if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
(n = sk_GENERAL_NAME_num (ext)))
/* older versions of OpenSSL use "ia5" instead of dNSName */
for (i = 0; ret && (i < n); i++)
if ((name = sk_GENERAL_NAME_value (ext,i)) &&
(name->type = GEN_DNS) && (s = name->d.ia5->data) &&
ssl_compare_hostnames (host,s)) ret = NIL;
} else ret = "Unable to locate common name in certificate";
return ret;
}

最佳答案

if ((err = ssl_validate_cert (cert = SSL_get_peer_certificate (sslconnection),host))) {
return NULL;
}

您必须释放从 SSL_get_peer_certificate 返回的 X509*使用 X509_free。可能还有更多泄漏,但这似乎与您的问题描述一致。

内存泄漏也是 OpenSSL 的 TLS Client 的原因例如立即免费的 X509*。它的引用计数,因此减少计数并使用 X509* 直到 session 被销毁(SSL*)是安全的。

X509* cert = SSL_get_peer_certificate(ssl);
if(cert) { X509_free(cert); } /* Free immediately */
if(NULL == cert) handleFailure();
...

您可能还会泄露一些从 name walk 返回的名称。我不使用 IA5 字符串,所以我不确定。我使用 UTF8 字符串,它们必须被释放。


这里有一些不相关的评论...您可能应该包括 SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3:

SSL_CTX_set_options (ssl_context,SSL_OP_NO_COMPRESSION|SSL_MODE_RELEASE_BUFFERS);

您可能还应该在某处指定 SSL_set_tlsext_host_name 以使用 SNI对于托管环境,默认站点证书可能不是目标站点的证书。 SNI 是一个 TLS 扩展,因此它说明需要 SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3

您还应该在使用 SSL_MODE_RELEASE_BUFFERS 时很好地测试应用程序。我似乎记得它导致了内存错误。另见 Issue 2167: OpenSSL fails if used from multiple threads and with SSL_MODE_RELEASE_BUFFERS , CVE-2010-5298 ,以及亚当兰利的 Overclocking SSL .

wiki 上提供的示例程序TLS Client也可以帮助您进行名称匹配。据我所知,代码很容易受到 Marlinspike 的嵌入式 NULL 技巧的攻击。在 More Tricks For Defeating SSL In Practice 查看他的 Blackhat 演讲了解更多详情。


这只是一个观察...既然您使用的是 C++,为什么不使用智能指针来管理您的资源?东西like this works very well in practice ,它会修复泄漏,因为 X509_ptr 具有指定的析构函数:

X509_ptr cert(SSL_get_peer_certificate(ssl));
// Use cert, its free'd automatically
char* name = ssl_validate_cert(cert.get(), "www.example.com");

和:

using EC_KEY_ptr = std::unique_ptr<EC_KEY, decltype(&::EC_KEY_free)>;
using EC_GROUP_ptr = std::unique_ptr<EC_GROUP, decltype(&::EC_GROUP_free)>;
using EC_POINT_ptr = std::unique_ptr<EC_POINT, decltype(&::EC_POINT_free)>;

using DH_ptr = std::unique_ptr<DH, decltype(&::DH_free)>;

using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>;

using DSA_ptr = std::unique_ptr<DSA, decltype(&::DSA_free)>;

using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;

using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;

using FILE_ptr = std::unique_ptr<FILE, decltype(&::fclose)>;

using BIO_MEM_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
using BIO_FILE_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;

using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_destroy)>;

using X509_ptr = std::unique_ptr<X509, decltype(&::X509_free)>;
using ASN1_INTEGER_ptr = std::unique_ptr<ASN1_INTEGER, decltype(&::ASN1_INTEGER_free)>;
using ASN1_TIME_ptr = std::unique_ptr<ASN1_TIME, decltype(&::ASN1_TIME_free)>;
using X509_EXTENSION_ptr = std::unique_ptr<X509_EXTENSION, decltype(&::X509_EXTENSION_free)>;

using X509_NAME_ptr = std::unique_ptr<X509_NAME, decltype(&::X509_NAME_free)>;
using X509_NAME_ENTRY_ptr = std::unique_ptr<X509_NAME_ENTRY, decltype(&::X509_NAME_ENTRY_free)>;

using X509_STORE_ptr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
using X509_LOOKUP_ptr = std::unique_ptr<X509_LOOKUP, decltype(&::X509_LOOKUP_free)>;
using X509_STORE_CTX_ptr = std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;

关于c++ - 多线程 C++ 应用程序在 SSL 证书验证中的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38450767/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com