Can not programmatically determine which TLS version my app uses

1.4k views Asked by At

INTRODUCTION:

I want to programatically determine which version of TLS my application used when communicating with a server.

Application is written in C++, using WinInet.

MY EFFORTS TO SOLVE THIS:

I have found InternetQueryOption which seemed like the solution due to INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT flag.

PROBLEM:

When incorporated in my app, InternetQueryOption does fill in INTERNET_CERTIFICATE_INFO structure.

The problem is that structure's lpszProtocolName is empty, so I can not see which protocol is used.

Below is MVCE that illustrates the problem:

#include <Windows.h>
#include <iostream>
#include <WinInet.h>
#include <string>

#pragma comment(lib, "Wininet.lib")

int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        std::cout << "Enter HTTPS address for test" << std::endl;
        return -1;
    }

    URL_COMPONENTS urlComp;
    ::ZeroMemory(&urlComp, sizeof(URL_COMPONENTS));
    urlComp.dwStructSize = sizeof(URL_COMPONENTS);
    urlComp.dwHostNameLength = -1;
    urlComp.dwSchemeLength = -1;
    urlComp.dwUrlPathLength = -1;

    if (!::InternetCrackUrl(argv[1], strlen(argv[1]), 0, &urlComp))
    {
        std::cout << "#0 " << ::GetLastError() << std::endl;
        return -1;
    }

    if (INTERNET_SCHEME_HTTPS != urlComp.nScheme)
    {
        std::cout << "#1 " << ::GetLastError() << std::endl;
        return -1;
    }

    HINTERNET hIntSession = ::InternetOpen("WinInet", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);

    if (NULL == hIntSession)
    {
        std::cout << "#2 " << ::GetLastError() << std::endl;
        return -1;
    }

    std::string s(strlen(argv[1]), 0);
    ::memcpy(&s[0], urlComp.lpszHostName, urlComp.dwHostNameLength);

    HINTERNET hHttpSession = ::InternetConnect(hIntSession, s.c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);

    if (NULL == hHttpSession)
    {
        std::cout << "#3 " << ::GetLastError() << std::endl;
        ::InternetCloseHandle(hIntSession);
        return -1;
    }

    HINTERNET hHttpRequest = ::HttpOpenRequest(hHttpSession, "HEAD", NULL, 0, 0, 0, INTERNET_FLAG_SECURE, 0);

    if (NULL == hHttpRequest)
    {
        std::cout << "#4 " << ::GetLastError() << std::endl;
        ::InternetCloseHandle(hHttpSession);
        ::InternetCloseHandle(hIntSession);
        return -1;
    }

    if (!::HttpSendRequest(hHttpRequest, NULL, 0, NULL, 0)) 
    {
        std::cout << "#5 " << ::GetLastError() << std::endl;
        ::InternetCloseHandle(hHttpRequest);
        ::InternetCloseHandle(hHttpSession);
        ::InternetCloseHandle(hIntSession);
        return -1;
    }

    INTERNET_CERTIFICATE_INFO certificateInfo;
    DWORD certInfoLength = sizeof(INTERNET_CERTIFICATE_INFO);

    if (!::InternetQueryOption(hHttpRequest, INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, &certificateInfo, &certInfoLength))
    {
        std::cout << "#6 " << ::GetLastError() << std::endl;
        ::InternetCloseHandle(hHttpRequest);
        ::InternetCloseHandle(hHttpSession);
        ::InternetCloseHandle(hIntSession);
        return -1;
    }

    if (certificateInfo.lpszProtocolName)
    {
        std::cout << certificateInfo.lpszProtocolName << std::endl;
        ::LocalFree(certificateInfo.lpszProtocolName);
    }

    ::InternetCloseHandle(hHttpRequest);
    ::InternetCloseHandle(hHttpSession);
    ::InternetCloseHandle(hIntSession);

    return 0;
}

QUESTION:

Since this is my first time using this API with the mentioned flag, I believe I am doing something wrong.

Can you help me correct the MVCE so that I can see which version of TLS has been used?

1

There are 1 answers

2
Simon Mourier On BEST ANSWER

Wininet has a bunch of extended functions, options and structures that are not documented as such in MSDN but are only available with a winineti.h (note the ending 'i') header file that should be present aside the standard wininet.h file. You don't need any external lib, they are implemented by wininet.dll (and available for linking in wininet.lib).

You will be interested specifically by the INTERNET_OPTION_SECURITY_CONNECTION_INFO option that should get you exactly what you need.

You have to pass a pointer to an INTERNET_SECURITY_CONNECTION_INFO struct. This struct contains a connectionInfo field of type SecPkgContext_ConnectionInfo which is fully documented. This struct is in fact defined by SSPI/Schannel which is what Wininet uses internally to handle all low level protocol work.

Here is a sample code that you can add to your existing code:

INTERNET_SECURITY_CONNECTION_INFO connInfo = { 0 };
DWORD certInfoLength = sizeof(INTERNET_SECURITY_CONNECTION_INFO);
InternetQueryOption(hHttpRequest, INTERNET_OPTION_SECURITY_CONNECTION_INFO, &connInfo, &certInfoLength);

// now, connInfo.connectionInfo should contain all you need