1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

http: windows - make https requests work in threads

This commit is contained in:
joe-conigliaro 2019-08-29 19:33:20 +10:00 committed by Alexander Medvednikov
parent 4f62e8235f
commit cd625b04eb
3 changed files with 138 additions and 143 deletions

View File

@ -1,5 +1,6 @@
#include <vschannel.h> #include <vschannel.h>
// Proxy // Proxy
CHAR * psz_proxy_server = "proxy"; CHAR * psz_proxy_server = "proxy";
INT i_proxy_port = 80; INT i_proxy_port = 80;
@ -26,14 +27,12 @@ struct TlsContext {
WSADATA wsa_data; WSADATA wsa_data;
CredHandle h_client_creds; CredHandle h_client_creds;
CtxtHandle h_context; CtxtHandle h_context;
PCCERT_CONTEXT p_pemote_cert_context;
BOOL creds_initialized; BOOL creds_initialized;
BOOL context_initialized; BOOL context_initialized;
PCCERT_CONTEXT p_pemote_cert_context;
}; };
struct TlsContext tls_ctx; TlsContext new_tls_context() {
struct TlsContext new_tls_context() {
return (struct TlsContext) { return (struct TlsContext) {
.cert_store = NULL, .cert_store = NULL,
.g_hsecurity = NULL, .g_hsecurity = NULL,
@ -44,26 +43,26 @@ struct TlsContext new_tls_context() {
}; };
}; };
BOOL load_security_library(void) { BOOL load_security_library(TlsContext *tls_ctx) {
INIT_SECURITY_INTERFACE pInitSecurityInterface; INIT_SECURITY_INTERFACE pInitSecurityInterface;
// Load Security DLL // Load Security DLL
tls_ctx.g_hsecurity = LoadLibraryA("schannel.dll"); tls_ctx->g_hsecurity = LoadLibraryA("schannel.dll");
if(tls_ctx.g_hsecurity == NULL) { if(tls_ctx->g_hsecurity == NULL) {
printf("Error 0x%x loading %s.\n", GetLastError(), "schannel.dll"); printf("Error 0x%x loading %s.\n", GetLastError(), "schannel.dll");
return FALSE; return FALSE;
} }
pInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress(tls_ctx.g_hsecurity, "InitSecurityInterfaceA"); pInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress(tls_ctx->g_hsecurity, "InitSecurityInterfaceA");
if(pInitSecurityInterface == NULL) { if(pInitSecurityInterface == NULL) {
printf("Error 0x%x reading InitSecurityInterface entry point.\n", GetLastError()); printf("Error 0x%x reading InitSecurityInterface entry point.\n", GetLastError());
return FALSE; return FALSE;
} }
tls_ctx.sspi = pInitSecurityInterface(); tls_ctx->sspi = pInitSecurityInterface();
if(tls_ctx.sspi == NULL) { if(tls_ctx->sspi == NULL) {
printf("Error 0x%x reading security interface.\n", printf("Error 0x%x reading security interface.\n",
GetLastError()); GetLastError());
return FALSE; return FALSE;
@ -72,69 +71,67 @@ BOOL load_security_library(void) {
return TRUE; return TRUE;
} }
void unload_security_library(void) { void unload_security_library(TlsContext *tls_ctx) {
FreeLibrary(tls_ctx.g_hsecurity); FreeLibrary(tls_ctx->g_hsecurity);
tls_ctx.g_hsecurity = NULL; tls_ctx->g_hsecurity = NULL;
} }
void vschannel_cleanup() { void vschannel_cleanup(TlsContext *tls_ctx) {
// Free the server certificate context. // Free the server certificate context.
if(tls_ctx.p_pemote_cert_context) { if(tls_ctx->p_pemote_cert_context) {
CertFreeCertificateContext(tls_ctx.p_pemote_cert_context); CertFreeCertificateContext(tls_ctx->p_pemote_cert_context);
tls_ctx.p_pemote_cert_context = NULL; tls_ctx->p_pemote_cert_context = NULL;
} }
// Free SSPI context handle. // Free SSPI context handle.
if(tls_ctx.context_initialized) { if(tls_ctx->context_initialized) {
tls_ctx.sspi->DeleteSecurityContext(&tls_ctx.h_context); tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
tls_ctx.context_initialized = FALSE; tls_ctx->context_initialized = FALSE;
} }
// Free SSPI credentials handle. // Free SSPI credentials handle.
if(tls_ctx.creds_initialized) { if(tls_ctx->creds_initialized) {
tls_ctx.sspi->FreeCredentialsHandle(&tls_ctx.h_client_creds); tls_ctx->sspi->FreeCredentialsHandle(&tls_ctx->h_client_creds);
tls_ctx.creds_initialized = FALSE; tls_ctx->creds_initialized = FALSE;
} }
// Close socket. // Close socket.
if(tls_ctx.socket != INVALID_SOCKET) { if(tls_ctx->socket != INVALID_SOCKET) {
closesocket(tls_ctx.socket); closesocket(tls_ctx->socket);
} }
// Shutdown WinSock subsystem. // Shutdown WinSock subsystem.
WSACleanup(); WSACleanup();
// Close "MY" certificate store. // Close "MY" certificate store.
if(tls_ctx.cert_store) { if(tls_ctx->cert_store) {
CertCloseStore(tls_ctx.cert_store, 0); CertCloseStore(tls_ctx->cert_store, 0);
} }
unload_security_library(); unload_security_library(tls_ctx);
} }
void vschannel_init() { void vschannel_init(TlsContext *tls_ctx) {
tls_ctx = new_tls_context(); if(!load_security_library(tls_ctx)) {
if(!load_security_library()) {
printf("Error initializing the security library\n"); printf("Error initializing the security library\n");
vschannel_cleanup(); vschannel_cleanup(tls_ctx);
} }
// Initialize the WinSock subsystem. // Initialize the WinSock subsystem.
if(WSAStartup(0x0101, &tls_ctx.wsa_data) == SOCKET_ERROR) { if(WSAStartup(0x0101, &tls_ctx->wsa_data) == SOCKET_ERROR) {
printf("Error %d returned by WSAStartup\n", GetLastError()); printf("Error %d returned by WSAStartup\n", GetLastError());
vschannel_cleanup(); vschannel_cleanup(tls_ctx);
} }
// Create credentials. // Create credentials.
if(create_credentials()) { if(create_credentials(tls_ctx)) {
printf("Error creating credentials\n"); printf("Error creating credentials\n");
vschannel_cleanup(); vschannel_cleanup(tls_ctx);
} }
tls_ctx.creds_initialized = TRUE; tls_ctx->creds_initialized = TRUE;
} }
INT request(INT iport, CHAR *host, CHAR *req, CHAR **out) INT request(TlsContext *tls_ctx, INT iport, CHAR *host, CHAR *req, CHAR **out)
{ {
SecBuffer ExtraData; SecBuffer ExtraData;
SECURITY_STATUS Status; SECURITY_STATUS Status;
@ -150,34 +147,34 @@ INT request(INT iport, CHAR *host, CHAR *req, CHAR **out)
port_number = iport; port_number = iport;
// Connect to server. // Connect to server.
if(connect_to_server(host, port_number)) { if(connect_to_server(tls_ctx, host, port_number)) {
printf("Error connecting to server\n"); printf("Error connecting to server\n");
vschannel_cleanup(); vschannel_cleanup(tls_ctx);
return resp_length; return resp_length;
} }
// Perform handshake // Perform handshake
if(perform_client_handshake(host, &ExtraData)) { if(perform_client_handshake(tls_ctx, host, &ExtraData)) {
printf("Error performing handshake\n"); printf("Error performing handshake\n");
vschannel_cleanup(); vschannel_cleanup(tls_ctx);
return resp_length; return resp_length;
} }
tls_ctx.context_initialized = TRUE; tls_ctx->context_initialized = TRUE;
// Authenticate server's credentials. // Authenticate server's credentials.
// Get server's certificate. // Get server's certificate.
Status = tls_ctx.sspi->QueryContextAttributes(&tls_ctx.h_context, Status = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context,
SECPKG_ATTR_REMOTE_CERT_CONTEXT, SECPKG_ATTR_REMOTE_CERT_CONTEXT,
(PVOID)&tls_ctx.p_pemote_cert_context); (PVOID)&tls_ctx->p_pemote_cert_context);
if(Status != SEC_E_OK) { if(Status != SEC_E_OK) {
printf("Error 0x%x querying remote certificate\n", Status); printf("Error 0x%x querying remote certificate\n", Status);
vschannel_cleanup(); vschannel_cleanup(tls_ctx);
return resp_length; return resp_length;
} }
// Attempt to validate server certificate. // Attempt to validate server certificate.
Status = verify_server_certificate(tls_ctx.p_pemote_cert_context, host,0); Status = verify_server_certificate(tls_ctx->p_pemote_cert_context, host,0);
if(Status) { if(Status) {
// The server certificate did not validate correctly. At this // The server certificate did not validate correctly. At this
// point, we cannot tell if we are connecting to the correct // point, we cannot tell if we are connecting to the correct
@ -187,35 +184,35 @@ INT request(INT iport, CHAR *host, CHAR *req, CHAR **out)
// It is therefore best if we abort the connection. // It is therefore best if we abort the connection.
printf("Error 0x%x authenticating server credentials!\n", Status); printf("Error 0x%x authenticating server credentials!\n", Status);
vschannel_cleanup(); vschannel_cleanup(tls_ctx);
return resp_length; return resp_length;
} }
// Free the server certificate context. // Free the server certificate context.
CertFreeCertificateContext(tls_ctx.p_pemote_cert_context); CertFreeCertificateContext(tls_ctx->p_pemote_cert_context);
tls_ctx.p_pemote_cert_context = NULL; tls_ctx->p_pemote_cert_context = NULL;
// Request from server // Request from server
if(https_make_request(req, out, &resp_length)) { if(https_make_request(tls_ctx, req, out, &resp_length)) {
vschannel_cleanup(); vschannel_cleanup(tls_ctx);
return resp_length; return resp_length;
} }
// Send a close_notify alert to the server and // Send a close_notify alert to the server and
// close down the connection. // close down the connection.
if(disconnect_from_server()) { if(disconnect_from_server(tls_ctx)) {
printf("Error disconnecting from server\n"); printf("Error disconnecting from server\n");
vschannel_cleanup(); vschannel_cleanup(tls_ctx);
return resp_length; return resp_length;
} }
tls_ctx.context_initialized = FALSE; tls_ctx->context_initialized = FALSE;
tls_ctx.socket = INVALID_SOCKET; tls_ctx->socket = INVALID_SOCKET;
return resp_length; return resp_length;
} }
static SECURITY_STATUS create_credentials() { static SECURITY_STATUS create_credentials(TlsContext *tls_ctx) {
TimeStamp tsExpiry; TimeStamp tsExpiry;
SECURITY_STATUS Status; SECURITY_STATUS Status;
@ -226,10 +223,10 @@ static SECURITY_STATUS create_credentials() {
// Open the "MY" certificate store, which is where Internet Explorer // Open the "MY" certificate store, which is where Internet Explorer
// stores its client certificates. // stores its client certificates.
if(tls_ctx.cert_store == NULL) { if(tls_ctx->cert_store == NULL) {
tls_ctx.cert_store = CertOpenSystemStore(0, "MY"); tls_ctx->cert_store = CertOpenSystemStore(0, "MY");
if(!tls_ctx.cert_store) { if(!tls_ctx->cert_store) {
printf("Error 0x%x returned by CertOpenSystemStore\n", printf("Error 0x%x returned by CertOpenSystemStore\n",
GetLastError()); GetLastError());
return SEC_E_NO_CREDENTIALS; return SEC_E_NO_CREDENTIALS;
@ -241,16 +238,16 @@ static SECURITY_STATUS create_credentials() {
// of course). Real applications may wish to specify other parameters // of course). Real applications may wish to specify other parameters
// as well. // as well.
ZeroMemory(&tls_ctx.schannel_cred, sizeof(tls_ctx.schannel_cred)); ZeroMemory(&tls_ctx->schannel_cred, sizeof(tls_ctx->schannel_cred));
tls_ctx.schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; tls_ctx->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
if(pCertContext) if(pCertContext)
{ {
tls_ctx.schannel_cred.cCreds = 1; tls_ctx->schannel_cred.cCreds = 1;
tls_ctx.schannel_cred.paCred = &pCertContext; tls_ctx->schannel_cred.paCred = &pCertContext;
} }
tls_ctx.schannel_cred.grbitEnabledProtocols = protocol; tls_ctx->schannel_cred.grbitEnabledProtocols = protocol;
if(aid_key_exch) if(aid_key_exch)
{ {
@ -259,11 +256,11 @@ static SECURITY_STATUS create_credentials() {
if(cSupportedAlgs) if(cSupportedAlgs)
{ {
tls_ctx.schannel_cred.cSupportedAlgs = cSupportedAlgs; tls_ctx->schannel_cred.cSupportedAlgs = cSupportedAlgs;
tls_ctx.schannel_cred.palgSupportedAlgs = rgbSupportedAlgs; tls_ctx->schannel_cred.palgSupportedAlgs = rgbSupportedAlgs;
} }
tls_ctx.schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; tls_ctx->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
// The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because // The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because
// this sample verifies the server certificate manually. // this sample verifies the server certificate manually.
@ -272,19 +269,19 @@ static SECURITY_STATUS create_credentials() {
// certificate. Applications running on newer versions of Windows can // certificate. Applications running on newer versions of Windows can
// leave off this flag, in which case the InitializeSecurityContext // leave off this flag, in which case the InitializeSecurityContext
// function will validate the server certificate automatically. // function will validate the server certificate automatically.
// tls_ctx.schannel_cred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION; // tls_ctx->schannel_cred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
// Create an SSPI credential. // Create an SSPI credential.
Status = tls_ctx.sspi->AcquireCredentialsHandle( Status = tls_ctx->sspi->AcquireCredentialsHandle(
NULL, // Name of principal NULL, // Name of principal
UNISP_NAME_A, // Name of package UNISP_NAME_A, // Name of package
SECPKG_CRED_OUTBOUND, // Flags indicating use SECPKG_CRED_OUTBOUND, // Flags indicating use
NULL, // Pointer to logon ID NULL, // Pointer to logon ID
&tls_ctx.schannel_cred, // Package specific data &tls_ctx->schannel_cred, // Package specific data
NULL, // Pointer to GetKey() func NULL, // Pointer to GetKey() func
NULL, // Value to pass to GetKey() NULL, // Value to pass to GetKey()
&tls_ctx.h_client_creds, // (out) Cred Handle &tls_ctx->h_client_creds, // (out) Cred Handle
&tsExpiry); // (out) Lifetime (optional) &tsExpiry); // (out) Lifetime (optional)
if(Status != SEC_E_OK) { if(Status != SEC_E_OK) {
printf("Error 0x%x returned by AcquireCredentialsHandle\n", Status); printf("Error 0x%x returned by AcquireCredentialsHandle\n", Status);
@ -304,7 +301,7 @@ cleanup:
} }
static INT connect_to_server(CHAR *host, INT port_number) { static INT connect_to_server(TlsContext *tls_ctx, CHAR *host, INT port_number) {
SOCKET Socket; SOCKET Socket;
struct sockaddr_in sin; struct sockaddr_in sin;
struct hostent *hp; struct hostent *hp;
@ -381,13 +378,13 @@ static INT connect_to_server(CHAR *host, INT port_number) {
// should continue to receive until CR LF CR LF is received // should continue to receive until CR LF CR LF is received
} }
tls_ctx.socket = Socket; tls_ctx->socket = Socket;
return SEC_E_OK; return SEC_E_OK;
} }
static LONG disconnect_from_server() { static LONG disconnect_from_server(TlsContext *tls_ctx) {
DWORD dwType; DWORD dwType;
PBYTE pbMessage; PBYTE pbMessage;
DWORD cbMessage; DWORD cbMessage;
@ -412,7 +409,7 @@ static LONG disconnect_from_server() {
OutBuffer.pBuffers = OutBuffers; OutBuffer.pBuffers = OutBuffers;
OutBuffer.ulVersion = SECBUFFER_VERSION; OutBuffer.ulVersion = SECBUFFER_VERSION;
Status = tls_ctx.sspi->ApplyControlToken(&tls_ctx.h_context, &OutBuffer); Status = tls_ctx->sspi->ApplyControlToken(&tls_ctx->h_context, &OutBuffer);
if(FAILED(Status)) { if(FAILED(Status)) {
printf("Error 0x%x returned by ApplyControlToken\n", Status); printf("Error 0x%x returned by ApplyControlToken\n", Status);
@ -436,9 +433,9 @@ static LONG disconnect_from_server() {
OutBuffer.pBuffers = OutBuffers; OutBuffer.pBuffers = OutBuffers;
OutBuffer.ulVersion = SECBUFFER_VERSION; OutBuffer.ulVersion = SECBUFFER_VERSION;
Status = tls_ctx.sspi->InitializeSecurityContext( Status = tls_ctx->sspi->InitializeSecurityContext(
&tls_ctx.h_client_creds, &tls_ctx.h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &tls_ctx->h_client_creds, &tls_ctx->h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP,
NULL, 0, &tls_ctx.h_context, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); NULL, 0, &tls_ctx->h_context, &OutBuffer, &dwSSPIOutFlags, &tsExpiry);
if(FAILED(Status)) { if(FAILED(Status)) {
printf("Error 0x%x returned by InitializeSecurityContext\n", Status); printf("Error 0x%x returned by InitializeSecurityContext\n", Status);
@ -451,7 +448,7 @@ static LONG disconnect_from_server() {
// Send the close notify message to the server. // Send the close notify message to the server.
if(pbMessage != NULL && cbMessage != 0) { if(pbMessage != NULL && cbMessage != 0) {
cbData = send(tls_ctx.socket, pbMessage, cbMessage, 0); cbData = send(tls_ctx->socket, pbMessage, cbMessage, 0);
if(cbData == SOCKET_ERROR || cbData == 0) { if(cbData == SOCKET_ERROR || cbData == 0) {
Status = WSAGetLastError(); Status = WSAGetLastError();
printf("Error %d sending close notify\n", Status); printf("Error %d sending close notify\n", Status);
@ -459,23 +456,23 @@ static LONG disconnect_from_server() {
} }
// Free output buffer. // Free output buffer.
tls_ctx.sspi->FreeContextBuffer(pbMessage); tls_ctx->sspi->FreeContextBuffer(pbMessage);
} }
cleanup: cleanup:
// Free the security context. // Free the security context.
tls_ctx.sspi->DeleteSecurityContext(&tls_ctx.h_context); tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
// Close the socket. // Close the socket.
closesocket(tls_ctx.socket); closesocket(tls_ctx->socket);
return Status; return Status;
} }
static SECURITY_STATUS perform_client_handshake(CHAR *host, SecBuffer *pExtraData) { static SECURITY_STATUS perform_client_handshake(TlsContext *tls_ctx, CHAR *host, SecBuffer *pExtraData) {
SecBufferDesc OutBuffer; SecBufferDesc OutBuffer;
SecBuffer OutBuffers[1]; SecBuffer OutBuffers[1];
DWORD dwSSPIFlags; DWORD dwSSPIFlags;
@ -503,8 +500,8 @@ static SECURITY_STATUS perform_client_handshake(CHAR *host, SecBuffer *pExtraDat
OutBuffer.pBuffers = OutBuffers; OutBuffer.pBuffers = OutBuffers;
OutBuffer.ulVersion = SECBUFFER_VERSION; OutBuffer.ulVersion = SECBUFFER_VERSION;
scRet = tls_ctx.sspi->InitializeSecurityContext( scRet = tls_ctx->sspi->InitializeSecurityContext(
&tls_ctx.h_client_creds, &tls_ctx->h_client_creds,
NULL, NULL,
host, host,
dwSSPIFlags, dwSSPIFlags,
@ -512,7 +509,7 @@ static SECURITY_STATUS perform_client_handshake(CHAR *host, SecBuffer *pExtraDat
SECURITY_NATIVE_DREP, SECURITY_NATIVE_DREP,
NULL, NULL,
0, 0,
&tls_ctx.h_context, &tls_ctx->h_context,
&OutBuffer, &OutBuffer,
&dwSSPIOutFlags, &dwSSPIOutFlags,
&tsExpiry); &tsExpiry);
@ -526,24 +523,24 @@ static SECURITY_STATUS perform_client_handshake(CHAR *host, SecBuffer *pExtraDat
// Send response to server if there is one. // Send response to server if there is one.
if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
{ {
cbData = send(tls_ctx.socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0); cbData = send(tls_ctx->socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
if(cbData == SOCKET_ERROR || cbData == 0) { if(cbData == SOCKET_ERROR || cbData == 0) {
printf("Error %d sending data to server (1)\n", WSAGetLastError()); printf("Error %d sending data to server (1)\n", WSAGetLastError());
tls_ctx.sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
tls_ctx.sspi->DeleteSecurityContext(&tls_ctx.h_context); tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
return SEC_E_INTERNAL_ERROR; return SEC_E_INTERNAL_ERROR;
} }
// Free output buffer. // Free output buffer.
tls_ctx.sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
OutBuffers[0].pvBuffer = NULL; OutBuffers[0].pvBuffer = NULL;
} }
return client_handshake_loop(TRUE, pExtraData); return client_handshake_loop(tls_ctx, TRUE, pExtraData);
} }
static SECURITY_STATUS client_handshake_loop(BOOL fDoInitialRead, SecBuffer *pExtraData) { static SECURITY_STATUS client_handshake_loop(TlsContext *tls_ctx, BOOL fDoInitialRead, SecBuffer *pExtraData) {
SecBufferDesc InBuffer; SecBufferDesc InBuffer;
SecBuffer InBuffers[2]; SecBuffer InBuffers[2];
SecBufferDesc OutBuffer; SecBufferDesc OutBuffer;
@ -594,7 +591,7 @@ static SECURITY_STATUS client_handshake_loop(BOOL fDoInitialRead, SecBuffer *pEx
// Read data from server. // Read data from server.
if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) { if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) {
if(fDoRead) { if(fDoRead) {
cbData = recv(tls_ctx.socket, cbData = recv(tls_ctx->socket,
IoBuffer + cbIoBuffer, IoBuffer + cbIoBuffer,
IO_BUFFER_SIZE - cbIoBuffer, IO_BUFFER_SIZE - cbIoBuffer,
0); 0);
@ -647,8 +644,8 @@ static SECURITY_STATUS client_handshake_loop(BOOL fDoInitialRead, SecBuffer *pEx
// Call InitializeSecurityContext. // Call InitializeSecurityContext.
scRet = tls_ctx.sspi->InitializeSecurityContext( scRet = tls_ctx->sspi->InitializeSecurityContext(
&tls_ctx.h_client_creds, &tls_ctx.h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &tls_ctx->h_client_creds, &tls_ctx->h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP,
&InBuffer, 0, NULL, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); &InBuffer, 0, NULL, &OutBuffer, &dwSSPIOutFlags, &tsExpiry);
// If InitializeSecurityContext was successful (or if the error was // If InitializeSecurityContext was successful (or if the error was
@ -659,20 +656,20 @@ static SECURITY_STATUS client_handshake_loop(BOOL fDoInitialRead, SecBuffer *pEx
scRet == SEC_I_CONTINUE_NEEDED || scRet == SEC_I_CONTINUE_NEEDED ||
FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) { FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) {
if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) { if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) {
cbData = send(tls_ctx.socket, cbData = send(tls_ctx->socket,
OutBuffers[0].pvBuffer, OutBuffers[0].pvBuffer,
OutBuffers[0].cbBuffer, OutBuffers[0].cbBuffer,
0); 0);
if(cbData == SOCKET_ERROR || cbData == 0) { if(cbData == SOCKET_ERROR || cbData == 0) {
printf("Error %d sending data to server (2)\n", printf("Error %d sending data to server (2)\n",
WSAGetLastError()); WSAGetLastError());
tls_ctx.sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
tls_ctx.sspi->DeleteSecurityContext(&tls_ctx.h_context); tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
return SEC_E_INTERNAL_ERROR; return SEC_E_INTERNAL_ERROR;
} }
// Free output buffer. // Free output buffer.
tls_ctx.sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
OutBuffers[0].pvBuffer = NULL; OutBuffers[0].pvBuffer = NULL;
} }
} }
@ -739,7 +736,7 @@ static SECURITY_STATUS client_handshake_loop(BOOL fDoInitialRead, SecBuffer *pEx
// we will attempt to connect anonymously (using our current // we will attempt to connect anonymously (using our current
// credentials). // credentials).
get_new_client_credentials(); get_new_client_credentials(tls_ctx);
// Go around again. // Go around again.
fDoRead = FALSE; fDoRead = FALSE;
@ -764,7 +761,7 @@ static SECURITY_STATUS client_handshake_loop(BOOL fDoInitialRead, SecBuffer *pEx
// Delete the security context in the case of a fatal error. // Delete the security context in the case of a fatal error.
if(FAILED(scRet)) { if(FAILED(scRet)) {
tls_ctx.sspi->DeleteSecurityContext(&tls_ctx.h_context); tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
} }
LocalFree(IoBuffer); LocalFree(IoBuffer);
@ -773,7 +770,7 @@ static SECURITY_STATUS client_handshake_loop(BOOL fDoInitialRead, SecBuffer *pEx
} }
static SECURITY_STATUS https_make_request(CHAR *req, CHAR **out, int *length) { static SECURITY_STATUS https_make_request(TlsContext *tls_ctx, CHAR *req, CHAR **out, int *length) {
SecPkgContext_StreamSizes Sizes; SecPkgContext_StreamSizes Sizes;
SECURITY_STATUS scRet; SECURITY_STATUS scRet;
SecBufferDesc Message; SecBufferDesc Message;
@ -793,7 +790,7 @@ static SECURITY_STATUS https_make_request(CHAR *req, CHAR **out, int *length) {
// Read stream encryption properties. // Read stream encryption properties.
scRet = tls_ctx.sspi->QueryContextAttributes(&tls_ctx.h_context, SECPKG_ATTR_STREAM_SIZES, &Sizes); scRet = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, SECPKG_ATTR_STREAM_SIZES, &Sizes);
if(scRet != SEC_E_OK) { if(scRet != SEC_E_OK) {
printf("Error 0x%x reading SECPKG_ATTR_STREAM_SIZES\n", scRet); printf("Error 0x%x reading SECPKG_ATTR_STREAM_SIZES\n", scRet);
return scRet; return scRet;
@ -843,7 +840,7 @@ static SECURITY_STATUS https_make_request(CHAR *req, CHAR **out, int *length) {
Message.cBuffers = 4; Message.cBuffers = 4;
Message.pBuffers = Buffers; Message.pBuffers = Buffers;
scRet = tls_ctx.sspi->EncryptMessage(&tls_ctx.h_context, 0, &Message, 0); scRet = tls_ctx->sspi->EncryptMessage(&tls_ctx->h_context, 0, &Message, 0);
if(FAILED(scRet)) { if(FAILED(scRet)) {
printf("Error 0x%x returned by EncryptMessage\n", scRet); printf("Error 0x%x returned by EncryptMessage\n", scRet);
@ -851,10 +848,10 @@ static SECURITY_STATUS https_make_request(CHAR *req, CHAR **out, int *length) {
} }
// Send the encrypted data to the server. // Send the encrypted data to the server.
cbData = send(tls_ctx.socket, pbIoBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer, 0); cbData = send(tls_ctx->socket, pbIoBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer, 0);
if(cbData == SOCKET_ERROR || cbData == 0) { if(cbData == SOCKET_ERROR || cbData == 0) {
printf("Error %d sending data to server (3)\n", WSAGetLastError()); printf("Error %d sending data to server (3)\n", WSAGetLastError());
tls_ctx.sspi->DeleteSecurityContext(&tls_ctx.h_context); tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
return SEC_E_INTERNAL_ERROR; return SEC_E_INTERNAL_ERROR;
} }
@ -864,7 +861,7 @@ static SECURITY_STATUS https_make_request(CHAR *req, CHAR **out, int *length) {
while(TRUE){ while(TRUE){
// Read some data. // Read some data.
if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) { if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) {
cbData = recv(tls_ctx.socket, pbIoBuffer + cbIoBuffer, cbIoBufferLength - cbIoBuffer, 0); cbData = recv(tls_ctx->socket, pbIoBuffer + cbIoBuffer, cbIoBufferLength - cbIoBuffer, 0);
if(cbData == SOCKET_ERROR) { if(cbData == SOCKET_ERROR) {
printf("Error %d reading data from server\n", WSAGetLastError()); printf("Error %d reading data from server\n", WSAGetLastError());
scRet = SEC_E_INTERNAL_ERROR; scRet = SEC_E_INTERNAL_ERROR;
@ -899,7 +896,7 @@ static SECURITY_STATUS https_make_request(CHAR *req, CHAR **out, int *length) {
Message.cBuffers = 4; Message.cBuffers = 4;
Message.pBuffers = Buffers; Message.pBuffers = Buffers;
scRet = tls_ctx.sspi->DecryptMessage(&tls_ctx.h_context, &Message, 0, NULL); scRet = tls_ctx->sspi->DecryptMessage(&tls_ctx->h_context, &Message, 0, NULL);
if(scRet == SEC_E_INCOMPLETE_MESSAGE) { if(scRet == SEC_E_INCOMPLETE_MESSAGE) {
// The input buffer contains only a fragment of an // The input buffer contains only a fragment of an
@ -963,7 +960,7 @@ static SECURITY_STATUS https_make_request(CHAR *req, CHAR **out, int *length) {
if(scRet == SEC_I_RENEGOTIATE) if(scRet == SEC_I_RENEGOTIATE)
{ {
// The server wants to perform another handshake sequence. // The server wants to perform another handshake sequence.
scRet = client_handshake_loop(FALSE, &ExtraBuffer); scRet = client_handshake_loop(tls_ctx, FALSE, &ExtraBuffer);
if(scRet != SEC_E_OK) { if(scRet != SEC_E_OK) {
return scRet; return scRet;
} }
@ -1079,7 +1076,7 @@ cleanup:
} }
static void get_new_client_credentials() { static void get_new_client_credentials(TlsContext *tls_ctx) {
CredHandle hCreds; CredHandle hCreds;
SecPkgContext_IssuerListInfoEx IssuerListInfo; SecPkgContext_IssuerListInfoEx IssuerListInfo;
PCCERT_CHAIN_CONTEXT pChainContext; PCCERT_CHAIN_CONTEXT pChainContext;
@ -1089,7 +1086,7 @@ static void get_new_client_credentials() {
SECURITY_STATUS Status; SECURITY_STATUS Status;
// Read list of trusted issuers from schannel. // Read list of trusted issuers from schannel.
Status = tls_ctx.sspi->QueryContextAttributes(&tls_ctx.h_context, SECPKG_ATTR_ISSUER_LIST_EX, (PVOID)&IssuerListInfo); Status = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, SECPKG_ATTR_ISSUER_LIST_EX, (PVOID)&IssuerListInfo);
if(Status != SEC_E_OK) { if(Status != SEC_E_OK) {
printf("Error 0x%x querying issuer list info\n", Status); printf("Error 0x%x querying issuer list info\n", Status);
return; return;
@ -1109,7 +1106,7 @@ static void get_new_client_credentials() {
while(TRUE) { while(TRUE) {
// Find a certificate chain. // Find a certificate chain.
pChainContext = CertFindChainInStore(tls_ctx.cert_store, pChainContext = CertFindChainInStore(tls_ctx->cert_store,
X509_ASN_ENCODING, X509_ASN_ENCODING,
0, 0,
CERT_CHAIN_FIND_BY_ISSUER, CERT_CHAIN_FIND_BY_ISSUER,
@ -1124,16 +1121,16 @@ static void get_new_client_credentials() {
pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext; pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;
// Create schannel credential. // Create schannel credential.
tls_ctx.schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; tls_ctx->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
tls_ctx.schannel_cred.cCreds = 1; tls_ctx->schannel_cred.cCreds = 1;
tls_ctx.schannel_cred.paCred = &pCertContext; tls_ctx->schannel_cred.paCred = &pCertContext;
Status = tls_ctx.sspi->AcquireCredentialsHandle( Status = tls_ctx->sspi->AcquireCredentialsHandle(
NULL, // Name of principal NULL, // Name of principal
UNISP_NAME_A, // Name of package UNISP_NAME_A, // Name of package
SECPKG_CRED_OUTBOUND, // Flags indicating use SECPKG_CRED_OUTBOUND, // Flags indicating use
NULL, // Pointer to logon ID NULL, // Pointer to logon ID
&tls_ctx.schannel_cred, // Package specific data &tls_ctx->schannel_cred, // Package specific data
NULL, // Pointer to GetKey() func NULL, // Pointer to GetKey() func
NULL, // Value to pass to GetKey() NULL, // Value to pass to GetKey()
&hCreds, // (out) Cred Handle &hCreds, // (out) Cred Handle
@ -1144,9 +1141,9 @@ static void get_new_client_credentials() {
} }
// Destroy the old credentials. // Destroy the old credentials.
tls_ctx.sspi->FreeCredentialsHandle(&tls_ctx.h_client_creds); tls_ctx->sspi->FreeCredentialsHandle(&tls_ctx->h_client_creds);
tls_ctx.h_client_creds = hCreds; tls_ctx->h_client_creds = hCreds;
// //
// As you can see, this sample code maintains a single credential // As you can see, this sample code maintains a single credential

View File

@ -20,31 +20,28 @@
// Define here to be sure // Define here to be sure
#define SP_PROT_TLS1_2_CLIENT 0x00000800 #define SP_PROT_TLS1_2_CLIENT 0x00000800
typedef struct TlsContext TlsContext;
static struct TlsContext new_tls_context(); TlsContext new_tls_context();
static void vschannel_init(); static void vschannel_init(TlsContext *tls_ctx);
static void vschannel_cleanup(); static void vschannel_cleanup(TlsContext *tls_ctx);
static INT request(INT iport, CHAR *host, CHAR *req, CHAR **out); static INT request(TlsContext *tls_ctx, INT iport, CHAR *host, CHAR *req, CHAR **out);
static SECURITY_STATUS https_make_request( static SECURITY_STATUS https_make_request(TlsContext *tls_ctx, CHAR *req, CHAR **out, int *length);
CHAR *req, CHAR **out, int *length);
static INT connect_to_server(CHAR *host, INT port_number); static INT connect_to_server(TlsContext *tls_ctx, CHAR *host, INT port_number);
static LONG disconnect_from_server(); static LONG disconnect_from_server(TlsContext *tls_ctx);
static SECURITY_STATUS perform_client_handshake( static SECURITY_STATUS perform_client_handshake(TlsContext *tls_ctx, CHAR *host, SecBuffer *pExtraData);
CHAR *host, SecBuffer *pExtraData);
static SECURITY_STATUS client_handshake_loop( static SECURITY_STATUS client_handshake_loop(TlsContext *tls_ctx, BOOL fDoInitialRead, SecBuffer *pExtraData);
BOOL fDoInitialRead, SecBuffer *pExtraData);
static DWORD verify_server_certificate( static DWORD verify_server_certificate(PCCERT_CONTEXT pServerCert, PSTR host, DWORD dwCertFlags);
PCCERT_CONTEXT pServerCert, PSTR host, DWORD dwCertFlags);
static SECURITY_STATUS create_credentials(); static SECURITY_STATUS create_credentials(TlsContext *tls_ctx);
static void get_new_client_credentials(); static void get_new_client_credentials(TlsContext *tls_ctx);

View File

@ -4,9 +4,6 @@
module http module http
import strings
import net.urllib
#flag windows -I @VROOT/thirdparty/vschannel #flag windows -I @VROOT/thirdparty/vschannel
#flag -l ws2_32 #flag -l ws2_32
@ -14,15 +11,19 @@ import net.urllib
#include "vschannel.c" #include "vschannel.c"
fn C.new_tls_context() C.TlsContext
fn init_module() {} fn init_module() {}
fn (req &Request) ssl_do(port int, method, host_name, path string) Response { fn (req &Request) ssl_do(port int, method, host_name, path string) Response {
C.vschannel_init() mut ctx := C.new_tls_context()
C.vschannel_init(&ctx)
mut buff := malloc(C.vsc_init_resp_buff_size) mut buff := malloc(C.vsc_init_resp_buff_size)
addr := host_name addr := host_name
sdata := req.build_request_headers(method, host_name, path) sdata := req.build_request_headers(method, host_name, path)
length := int(C.request(port, addr.str, sdata.str, &buff)) length := int(C.request(&ctx, port, addr.str, sdata.str, &buff))
C.vschannel_cleanup() C.vschannel_cleanup(&ctx)
return parse_response(string(buff, length)) return parse_response(string(buff, length))
} }