Memory leak with multi threaded c++ application on ssl certificate validation - c++

We are using openssl library in our multi threaded c++ application. The application eats up all the memory within 3 days (7 GB instance) due to a memory leak, only if SSL certificate validation is enabled.
Please find my application flow here:
On app launch, We create 150 threads for syncing 30k users data and reserving one SSL_CTX_new object per thread. The same object is reused until the process is killed. The object SSL_CTX_new is created only once on thread initialization, it will be reused for all subsequent ssl connections.
We do the following on processing user's data:
A thread creates a new socket connection to the third party server via ssl and once the required data is fetched from the 3rd party server, ssl connection is terminated.
Similarly all threads pick up one user at a time from the queue and fetches the data from the server.
We have to do above connect, fetch data and disconnect ssl connections for all 30k users.
Please find pseudocode example of our application:
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;
}
You must free the X509* returned from SSL_get_peer_certificate using X509_free. There may be more leaks, but that one seems consistent with the description of your issue.
The memory leak is also why OpenSSL's TLS Client example immediately free's the X509*. Its reference counted, so its safe to decrement the count and use the X509* until the session is destroyed (the SSL*).
X509* cert = SSL_get_peer_certificate(ssl);
if(cert) { X509_free(cert); } /* Free immediately */
if(NULL == cert) handleFailure();
...
You may also be leaking some of the names returned from the name walk. I don't use IA5 strings, so I'm not certain. I use UTF8 strings, and they must be freed.
Here's some unrelated comments... You should probably include SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3:
SSL_CTX_set_options (ssl_context,SSL_OP_NO_COMPRESSION|SSL_MODE_RELEASE_BUFFERS);
You should also probably specify SSL_set_tlsext_host_name somewhere to use SNI for hosted environments, where the default site certificate may not be the target site's certificate. SNI is a TLS extension, so it speaks to the need for SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3.
You should also test the application well when using SSL_MODE_RELEASE_BUFFERS. I seem to recall it caused memory errors. Also see Issue 2167: OpenSSL fails if used from multiple threads and with SSL_MODE_RELEASE_BUFFERS, CVE-2010-5298, and Adam Langley's Overclocking SSL.
The sample program provided on the wiki TLS Client may also help you with you name matching. The best I can tell, the code is vulnerable to Marlinspike's embedded NULL tricks. See his Blackhat talk at More Tricks For Defeating SSL In Practice for more details.
This is just an observation... Since you are using C++, why are you not using smart pointers to manage your resources? Something like this works very well in practice, and it would have fixed the leak because X509_ptr has the destructor function specified:
X509_ptr cert(SSL_get_peer_certificate(ssl));
// Use cert, its free'd automatically
char* name = ssl_validate_cert(cert.get(), "www.example.com");
And:
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)>;

Related

Semaphore FreeRTOS. xQueueGenericSend assert failed

So I'm trying to adapt a CPP library for ESP-IDF/Arduino BLE control into ESP-IDF v5.0 but I'm getting some errors regarding Semaphore from FreeRTOS when waiting. This is most likerly due to some APIs changing between ESP-IDF 3.0 to 5.0 but since Im just learning FreeRTOS I got a bit lost with the error.
Some logs from the program before the crash:
I (2376) BLEClient: >> connect(a6:c0:82:01:17:72)
I (2386) : 119660
I (2386) BLEDevice: add conn_id: 0, GATT role: client
I (2386) FreeRTOS: Semaphore taking: name: RegEvt (0x3ffdb0d4), owner: <N/A> for connect
I (2396) FreeRTOS: Semaphore taken: name: RegEvt (0x3ffdb0d4), owner: connect
I (2406) : 119628
assert failed: xQueueGenericSend queue.c:837 (pxQueue->pcHead != ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle())
The traceback:
Backtrace: 0x40081d1e:0x3ffdacb0 0x40090699:0x3ffdacd0 0x4009687d:0x3ffdacf0 0x40090e62:0x3ffdae10 0x400dc597:0x3ffdae50 0x400d94d9:0x3ffdae90 0x400da145:0x3ffdaf10 0x400e0226:0x3ffdaf60 0x400e0922:0x3ffdaf80 0x400f73fd:0x3ffdafc0 0x400f95ab:0x3ffdafe0
0x40081d1e: panic_abort at C:/Espressif/frameworks/esp-idf-v5.0/components/esp_system/panic.c:412
0x40090699: esp_system_abort at C:/Espressif/frameworks/esp-idf-v5.0/components/esp_system/esp_system.c:135
0x4009687d: __assert_func at C:/Espressif/frameworks/esp-idf-v5.0/components/newlib/assert.c:78
0x40090e62: xQueueGenericSend at C:/Espressif/frameworks/esp-idf-v5.0/components/freertos/FreeRTOS-Kernel/queue.c:828 (discriminator 2)
0x400dc597: FreeRTOS::Semaphore::give() at C:/Users/amng8/Sync/PROJECTS/PillCPP/components/BLE/FreeRTOS.cpp:107
0x400d94d9: BLEClient::gattClientEventHandler(esp_gattc_cb_event_t, unsigned char, esp_ble_gattc_cb_param_t*) at C:/Users/amng8/Sync/PROJECTS/PillCPP/components/BLE/BLEClient.cpp:233
0x400da145: BLEDevice::gattClientEventHandler(esp_gattc_cb_event_t, unsigned char, esp_ble_gattc_cb_param_t*) at C:/Users/amng8/Sync/PROJECTS/PillCPP/components/BLE/BLEDevice.cpp:163
0x400e0226: btc_gattc_cb_to_app at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c:24
0x400e0922: btc_gattc_cb_handler at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c:1002
0x400f73fd: btc_thread_handler at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/common/btc/core/btc_task.c:207
ELF file SHA256: b8542ed11e07aba6
0x400f95ab: osi_thread_run at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/common/osi/thread.c:165
The code in question is from the file BLEClient.cpp function bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type):
/**
* #brief Connect to the partner (BLE Server).
* #param [in] address The address of the partner.
* #return True on success.
*/
bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) {
ESP_LOGI(LOG_TAG, ">> connect(%s)", address.toString().c_str());
// We need the connection handle that we get from registering the application. We register the app
// and then block on its completion. When the event has arrived, we will have the handle.
m_appId = BLEDevice::m_appId++;
BLEDevice::addPeerDevice(this, true, m_appId);
m_semaphoreRegEvt.take("connect");
// clearServices(); // we dont need to delete services since every client is unique?
esp_err_t errRc = ::esp_ble_gattc_app_register(m_appId);
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
}
m_semaphoreRegEvt.wait("connect");
m_peerAddress = address;
// Perform the open connection request against the target BLE Server.
m_semaphoreOpenEvt.take("connect");
errRc = ::esp_ble_gattc_open(
m_gattc_if,
*getPeerAddress().getNative(), // address
type, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature.
1 // direct connection <-- maybe needs to be changed in case of direct indirect connection???
);
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
}
uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK);
return rc == ESP_GATT_OK;
} // connect
and what seems to be triggering the crash is the .wait("connect") command.
Once again I'm new to FreeRTOS and Semaphore and not sure what's useful or not to get help so if you need any more information please let me know.

C++ OpenSSL: libssl fails to verify certificates on Windows

I've done a lot of looking around but I can't seem to find a decent solution to this problem. Many of the StackOverflow posts are regarding Ruby, but I'm using OpenSSL more or less directly (via the https://gitlab.com/eidheim/Simple-Web-Server library) for a C++ application/set of libraries, and need to work out how to fix this completely transparently for users (they should not need to hook up any custom certificate verification file in order to use the application).
On Windows, when I attempt to use the SimpleWeb HTTPS client, connections fail if I have certificate verification switched on, because the certificate for the connection fails to validate. This is not the case on Linux, where verification works fine.
I was advised to follow this solution to import the Windows root certificates into OpenSSL so that they could be used by the verification routines. However, this doesn't seem to make any difference as far as I can see. I have dug into the guts of the libssl verification functions to try and understand exactly what's going on, and although the above answer recommends adding the Windows root certificates to a new X509_STORE, it appears that the SSL connection context has its own store which is set up when the connection is initialised. This makes me think that simply creating a new X509_STORE and adding certificates there is not helping because the connection doesn't actually use that store.
It may well be that I've spent so much time debugging the minutiae of libssl that I'm missing what the actual approach to solving this problem should be. Does OpenSSL provide a canonical way of looking up system certificates that I'm not setting? Alternatively, could the issue be the way that the SimpleWeb library/ASIO is initialising OpenSSL? I know that the library allows you to provide a path for a "verify file" for certificates, but I feel like this wouldn't be an appropriate solution since I as a developer should be using the certificates found on the end user's system, rather than hard-coding my own.
EDIT: For context, this is the code I'm using in a tiny example application:
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
static void LoadSystemCertificates()
{
HCERTSTORE hStore;
PCCERT_CONTEXT pContext = nullptr;
X509 *x509 = nullptr;
X509_STORE *store = X509_STORE_new();
hStore = CertOpenSystemStore(NULL, "ROOT");
if (!hStore)
{
return;
}
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != nullptr)
{
const unsigned char* encodedCert = reinterpret_cast<const unsigned char*>(pContext->pbCertEncoded);
x509 = d2i_X509(nullptr, &encodedCert, pContext->cbCertEncoded);
if (x509)
{
X509_STORE_add_cert(store, x509);
X509_free(x509);
}
}
CertCloseStore(hStore, 0);
}
static void MakeRequest(const std::string& address)
{
using Client = SimpleWeb::Client<SimpleWeb::HTTPS>;
Client httpsClient(address);
httpsClient.io_service = std::make_shared<asio::io_service>();
std::cout << "Making request to: " << address << std::endl;
bool hasResponse = false;
httpsClient.request("GET", [address, &hasResponse](std::shared_ptr<Client::Response> response, const SimpleWeb::error_code& error)
{
hasResponse = true;
if ( error )
{
std::cerr << "Got error from " << address << ": " << error.message() << std::endl;
}
else
{
std::cout << "Got response from " << address << ":\n" << response->content.string() << std::endl;
}
});
while ( !hasResponse )
{
httpsClient.io_service->poll();
httpsClient.io_service->reset();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
}
int main(int, char**)
{
LoadSystemCertificates();
MakeRequest("google.co.uk");
return 0;
}
The call returns me: Got error from google.co.uk: certificate verify failed
OK, to anyone who this might help in future, this is how I solved this issue. This answer to a related question helped.
It turns out that the issue was indeed that the SSL context was not making use of the certificate store that I'd set up. Everything else was OK, bu the missing piece of the puzzle was a call to SSL_CTX_set_cert_store(), which takes the certificate store and provides it to the SSL context.
In the context of the SimpleWeb library, the easiest way to do this appeared to be to subclass the SimpleWeb::Client<SimpleWeb::HTTPS> class and add the following to the constructor:
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <wincrypt.h>
class MyClient : public SimpleWeb::Client<SimpleWeb::HTTPS>
{
public:
MyClient( /* ... */ ) :
SimpleWeb::Client<SimpleWeb::HTTPS>( /* ... */ )
{
AddWindowsRootCertificates();
}
private:
using OpenSSLContext = asio::ssl::context::native_handle_type;
void AddWindowsRootCertificates()
{
// Get the SSL context from the SimpleWeb class.
OpenSSLContext sslContext = context.native_handle();
// Get a certificate store populated with the Windows root certificates.
// If this fails for some reason, the function returns null.
X509_STORE* certStore = GetWindowsCertificateStore();
if ( sslContext && certStore )
{
// Set this store to be used for the SSL context.
SSL_CTX_set_cert_store(sslContext, certStore);
}
}
static X509_STORE* GetWindowsCertificateStore()
{
// To avoid populating the store every time, we keep a static
// pointer to the store and just initialise it the first time
// this function is called.
static X509_STORE* certificateStore = nullptr;
if ( !certificateStore )
{
// Not initialised yet, so do so now.
// Try to open the root certificate store.
HCERTSTORE rootStore = CertOpenSystemStore(0, "ROOT");
if ( rootStore )
{
// The new store is reference counted, so we can create it
// and keep the pointer around for later use.
certificateStore = X509_STORE_new();
PCCERT_CONTEXT pContext = nullptr;
while ( (pContext = CertEnumCertificatesInStore(rootStore, pContext)) != nullptr )
{
// d2i_X509() may modify the pointer, so make a local copy.
const unsigned char* content = pContext->pbCertEncoded;
// Convert the certificate to X509 format.
X509 *x509 = d2i_X509(NULL, &content, pContext->cbCertEncoded);
if ( x509 )
{
// Successful conversion, so add to the store.
X509_STORE_add_cert(certificateStore, x509);
// Release our reference.
X509_free(x509);
}
}
// Make sure to close the store.
CertCloseStore(rootStore, 0);
}
}
return certificateStore;
}
};
Obviously GetWindowsCertificateStore() would need to be abstracted out to somewhere platform-specific if your class needs to compile on multiple platforms.

Finding a way to load multiple ssl certificates(2 RSA) in memory(SSL Context)? OpenSSL

Trying to load multiple ssl certificates in memory. I tried to code from documentation but failed as documentation seen little hard for me to understand. Seeking help from stackoverflow.
I am finding a way, that both certificates can work simultaneously at the time.
Certificates that I have to load in memory are Symantec and Zuul(.pem format).
Both symantec.pem and zuul.pem file are password protected and it contains (one certificate and one private key).
Below code loading symanctic certificate only.
Openssl version : OpenSSL 1.1.1g FIPS 21 Apr 2020
if(server_ctx==NULL)
{
/* Global system initialization*/
SSL_library_init();
SSL_load_error_strings();
}
meth=TLS_server_method();
if(status == true)
{
server_ctx=SSL_CTX_new(meth);
if(server_ctx != NULL)
{
if((strcmp(protocol_current_version,"Y") == 0) || (strcmp(protocol_current_version,"y") == 0))
{
SSL_CTX_set_min_proto_version(server_ctx, TLS1_VERSION);
}
else if((strcmp(protocol_current_version,"N") == 0) || (strcmp(protocol_current_version,"n") == 0))
{
SSL_CTX_set_min_proto_version(server_ctx, TLS1_2_VERSION);
}
}
}
/* Load our keys and certificates*/
if(!(SSL_CTX_use_certificate_file(server_ctx,keyfile,SSL_FILETYPE_PEM)))
{
status = false;
LOG(LEVEL1,"Error!!! Couldn't read certificate file: %s",ERR_error_string(ERR_get_error(),NULL));
}
if(status==true)
{SSL_CTX_set_default_passwd_cb(server_ctx,password_cb);}
if(status==true)
{
if(!(SSL_CTX_use_PrivateKey_file(server_ctx,keyfile,SSL_FILETYPE_PEM)))
{
LOG(LEVEL1,"Error!!! Couldn't read key file: %s",ERR_error_string(ERR_get_error(),NULL));
status = false;
}
}
/* Load the CAs we trust*/
if(status==true)
{
if(!(SSL_CTX_load_verify_locations(server_ctx,ca_list,0)))
{
LOG(LEVEL1,"Error!!! Couldn't read CA_LIST: %s",ERR_error_string(ERR_get_error(),NULL));
status=false;
}
}
//Force Mutual authentication if verify_ssl_peer is set
if(verify_ssl_peer)
{
SSL_CTX_set_verify(server_ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL);
}
SSL_CTX_set_verify_depth(server_ctx,1);
//Set AUTO MODE RETRY Flag, Will not bother application till data is completely read
SSL_CTX_set_mode(server_ctx,SSL_MODE_AUTO_RETRY);
//Set the list of supported cipher suites. Typically used to force only AES128 or AES256 bit encryption:
if(status==true)
{
//Force the cipher is verify_ssl_cipher is set
if(verify_ssl_cipher)
{
if(cipher_list==NULL)
{
LOG(LEVEL1,"[%s]: Error!!! Cipher list is set to NULL, will not initialize",__FUNCTION__);
status = false;
}
else
{
if(!SSL_CTX_set_cipher_list(server_ctx,cipher_list))
{LOG(LEVEL1,"Error!!! Failed to set cipher_list for ctx: %s",ERR_error_string(ERR_get_error(),NULL));}
}
}
}
if(status==true)
{
/* Load randomness */
if(!(RAND_load_file(random_file,1024*1024)))
{
LOG(LEVEL1,"Error!!! Load randomness failed:%s",ERR_error_string(ERR_get_error(),NULL));
status=false;
}
}
if(status==true)
{
long opts = SSL_CTX_get_options(server_ctx);
opts = SSL_CTX_set_options(server_ctx,SSL_OP_ALL|opts);
LOG(LEVEL1,"SSl Server CTX Options set:%04x",opts);
}
A possible solution could be to use the Server Name Indication in the TLS Client Hello: Zuul and Symantec Clients could set the TLS SNI field differently so that your Server could elaborate it to select the proper certificate chain.
In details, your server shall first setup one SSL Context for Zuul and another one for Symantec with the right private key and certificate chain, then the servername callback shall be set in each context.
For example, to set the server name callback for your symantec context:
SSL_CTX_set_tlsext_servername_callback(symantec_context, serverNameCallbackHandler);
You can set an argument that will be handled by serverNameCallbackHandler function the for this SSL_CTX:
SSL_CTX_set_tlsext_servername_arg(symantec_context, &your_data);
Then you shall implement the callback that will be invoked durint the TLS handshake. For example:
static int serverNameCallbackHandler(SSL *ssl, int *ad, void *arg)
{
MyData *data = (MyData *) arg;
// ...
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (servername)
{
//Select the proper context
//...
if (match_found)
{
SSL_CTX* ctx = symantec_context;
SSL_CTX* v = SSL_set_SSL_CTX(ssl, ctx);
if (v != ctx)
{
// Internal Error on ssl context switch!
return SSL_TLSEXT_ERR_NOACK;
}
}
}
// NOTE: On Server name mismatch use the default context
return SSL_TLSEXT_ERR_OK;
}
For information about how to handle the SNI on client side, I think that the opensource code of openssl command is very helpful.
Note that you can test your server with the openssl command:
openssl s_client -servername 'your sni name' -CAfile ./RootCA.pem -connect ip:port

Find service`s uuid of a bluetooth device

I'm trying to get information about services provided by bluetooth devices. Using WSALookupServiceNext I can get list of all services. However, regardless of what bluetooth device I try to discover, the resulting WSAQUERYSET structure (pqs) never contains lpServiceClassId field (while the lpszServiceInstanceName is always present). I want to know UUID of a service to determine to which class of the Table 2: Service Class Profile Identifiers it belongs.
#include<winsock2.h>
int main()
{
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
WSAQUERYSET qs{};
qs.dwSize = sizeof(WSAQUERYSET);
qs.dwNameSpace = NS_BTH;
qs.dwNumberOfCsAddrs = 0;
qs.lpszContext = (LPWSTR)L"12:34:56:78:99:11";
qs.lpServiceClassId = const_cast<LPGUID>(&PublicBrowseGroupServiceClass_UUID);
const DWORD flags = LUP_FLUSHCACHE | LUP_RETURN_ALL;
HANDLE hlookup = nullptr;
WSALookupServiceBegin(&qs, flags, &hlookup);
while (true) {
char buff[4096];
WSAQUERYSET* pqs = (WSAQUERYSET*)buff;
DWORD size = sizeof(buff);
memset(buff, 0, size);
const INT res = WSALookupServiceNext(hlookup, flags, &size, pqs);
if (res != 0 && GetLastError() == WSA_E_NO_MORE) {
break;
}
// it prints "service name=Advanced Audio, service uuid=0x0"
wprintf(L"service name=%s, service uuid=0x%X\n", pqs->lpszServiceInstanceName, pqs->lpServiceClassId->Data1);
}
}
Answering my own question:
In order to get service ID we have to parse SPD structures which are returned from WSALookupServiceNext function call and accessible via WSAQUERYSET.lpBlob member.
For more details see Mike Petrichenko's comment.
Also, there is a very good example how to implement it from Qt framework source code.
As for myself, I decided not to follow this path, because my goal was to write a console utility for pairing Bluetooth devices. I have found that there is a better way to do that using Windows.Devices.Enumeration API. Finally, using this API, I managed to create the BluetoothDevicePairing utility I was working on.

Paho MQTT (C++) client fails to connect to Mosquitto

I've got C++ code using the Paho MQTTPacket Embedded C++ library to connect to an MQTT broker. When that broker is io.adafruit.com, it works perfectly fine. But when it's my own Mosquitto instance running on my Raspberry Pi, the connection fails. I've narrowed it down to this line in MQTTClient.h, in the MQTT::Client::connect method:
// this will be a blocking call, wait for the connack
if (waitfor(CONNACK, connect_timer) == CONNACK)
The app hangs here for about 30 seconds, and then gets a result other than CONNACK (specifically 0 rather than 2).
I have tried both protocol version 3 (i.e. 3.1) and 4 (i.e. 3.1.1); same result.
My Mosquitto instance has no authentication or passwords set up. I've tried turning on debug messages in the Mosquitto log, but they're not showing anything useful. I'm at a loss. Why might I be unable to connect to Mosquitto from my C++ Paho code?
EDIT: Here's the client code... again, this works fine with Adafruit, but when I point it to my Mosquitto at localhost, it hangs as described. (I've elided the username and password -- I am sending them, but I really don't think those are the issue, since with mosquitto_pub or mosquitto_sub on the command line, I can connect regardless of this, since mosquitto is configured to allow anonymous connections.)
const char* host = "127.0.0.1";
int port = 1883;
const char* clientId = "ZoomBridge";
const char* username = "...";
const char* password = "...";
MQTT::QoS subsqos = MQTT::QOS2;
ipstack = new IPStack();
client = new MQTT::Client<IPStack, Countdown, 30000>(*ipstack);
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
data.willFlag = 1;
data.MQTTVersion = 3;
data.clientID.cstring = (char*)clientId;
data.username.cstring = (char*)username;
data.password.cstring = (char*)password;
data.keepAliveInterval = 20;
data.cleansession = 1;
int rc = ipstack->connect(host, port);
if (rc != MQTT::SUCCESS) {
cout << "Failed [1] (result " << rc << ")" << endl;
return rc;
}
rc = client->connect(data);
if (rc != MQTT::SUCCESS) {
cout << "Failed [2] (result " << rc << ")" << endl;
ipstack->disconnect();
return rc;
}
As hashed out in the comments.
It looks like you are setting the flag to indicate you want to set a Last Will and Testament for the client (data.willFlag = 1;) but then not passing any topic or payload for the LWT.
If you don't need the LWT then set the flag to 0 (or remove the line settings flag) as it should default to disabled.
Also worth pointing out to clarity, this is all with the Paho Embedded C++ MQTTPacket client not the full blown Paho C++ client.