首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于ssl证书验证的多线程c++应用程序内存泄漏

基于ssl证书验证的多线程c++应用程序内存泄漏
EN

Stack Overflow用户
提问于 2016-07-19 06:13:02
回答 1查看 1.7K关注 0票数 1

我们在多线程c++应用程序中使用openssl库。只有在启用SSL证书验证的情况下,应用程序才会在3天内(7 GB实例)内因为内存泄漏而耗尽所有内存。

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

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

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

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

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

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

代码语言:javascript
复制
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;
}
EN

回答 1

Stack Overflow用户

发布于 2016-07-19 07:15:36

代码语言:javascript
复制
if ((err = ssl_validate_cert (cert = SSL_get_peer_certificate (sslconnection),host))) {
    return NULL;
}

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

内存泄漏也是OpenSSL的TLS客户端示例立即释放X509*的原因。它的引用计数,因此可以安全地减少计数并使用X509*,直到会话被销毁( SSL*)。

代码语言:javascript
复制
X509* cert = SSL_get_peer_certificate(ssl);
if(cert) { X509_free(cert); } /* Free immediately */
if(NULL == cert) handleFailure();
...

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

以下是一些无关的评论..。您可能应该包括SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3

代码语言:javascript
复制
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时,您还应该很好地测试应用程序。我好像记得它导致了记忆错误。也见缓冲器CVE-2010-5298,和Adam的超频SSL

wiki TLS客户端上提供的示例程序也可以帮助您进行名称匹配。据我所知,代码很容易受到Marlinspike嵌入的空技巧的攻击。有关更多细节,请参见他在更多在实践中击败SSL的技巧的黑帽演讲。

这只是一个观察..。既然您使用的是C++,为什么不使用智能指针来管理资源?就像这样在实践中效果很好,它会修复漏洞,因为X509_ptr指定了析构函数:

代码语言:javascript
复制
X509_ptr cert(SSL_get_peer_certificate(ssl));
// Use cert, its free'd automatically
char* name = ssl_validate_cert(cert.get(), "www.example.com");

和:

代码语言:javascript
复制
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)>;
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38450767

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档