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