mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-13 09:35:23 -04:00
WIP on better SSL compatibility with pre Windows 2000 operating systems
This commit is contained in:
parent
deafec54b3
commit
dbf908bc64
@ -422,6 +422,7 @@ static cc_result HttpBackend_Do(struct HttpRequest* req, cc_string* url) {
|
|||||||
#include "SSL.h"
|
#include "SSL.h"
|
||||||
|
|
||||||
static void HttpBackend_Init(void) {
|
static void HttpBackend_Init(void) {
|
||||||
|
SSLBackend_Init(httpsVerify);
|
||||||
//httpOnly = true; // TODO: insecure
|
//httpOnly = true; // TODO: insecure
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -855,7 +856,7 @@ static cc_result HttpBackend_Do(struct HttpRequest* req, cc_string* urlStr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static cc_bool HttpBackend_DescribeError(cc_result res, cc_string* dst) {
|
static cc_bool HttpBackend_DescribeError(cc_result res, cc_string* dst) {
|
||||||
return false;
|
return SSLBackend_DescribeError(res, dst);
|
||||||
}
|
}
|
||||||
#elif defined CC_BUILD_WININET
|
#elif defined CC_BUILD_WININET
|
||||||
/*########################################################################################################################*
|
/*########################################################################################################################*
|
||||||
|
@ -506,7 +506,7 @@ cc_result Socket_Connect(cc_socket* s, const cc_string* address, int port, cc_bo
|
|||||||
if (*s == -1) return _WSAGetLastError();
|
if (*s == -1) return _WSAGetLastError();
|
||||||
|
|
||||||
if (nonblocking) {
|
if (nonblocking) {
|
||||||
int blockingMode = -1; /* non-blocking mode */
|
u_long blockingMode = -1; /* non-blocking mode */
|
||||||
_ioctlsocket(*s, FIONBIO, &blockingMode);
|
_ioctlsocket(*s, FIONBIO, &blockingMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
225
src/SSL.c
225
src/SSL.c
@ -3,12 +3,16 @@
|
|||||||
|
|
||||||
#if defined CC_BUILD_SCHANNEL
|
#if defined CC_BUILD_SCHANNEL
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define NOSERVICE
|
||||||
|
#define NOMCX
|
||||||
|
#define NOIME
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#define SECURITY_WIN32
|
#define SECURITY_WIN32
|
||||||
#include <security.h>
|
#include <sspi.h>
|
||||||
#include <schannel.h>
|
#include <schannel.h>
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#pragma comment (lib, "secur32.lib")
|
#include "String.h"
|
||||||
|
#include "Funcs.h"
|
||||||
|
|
||||||
/* https://gist.github.com/mmozeiko/c0dfcc8fec527a90a02145d2cc0bfb6d */
|
/* https://gist.github.com/mmozeiko/c0dfcc8fec527a90a02145d2cc0bfb6d */
|
||||||
/* https://web.archive.org/web/20210116110926/http://www.coastrd.com/c-schannel-smtp */
|
/* https://web.archive.org/web/20210116110926/http://www.coastrd.com/c-schannel-smtp */
|
||||||
@ -16,17 +20,46 @@
|
|||||||
/* https://hpbn.co/transport-layer-security-tls/ */
|
/* https://hpbn.co/transport-layer-security-tls/ */
|
||||||
#define TLS_MAX_PACKET_SIZE (16384 + 512) /* 16kb record size + header/mac/padding */
|
#define TLS_MAX_PACKET_SIZE (16384 + 512) /* 16kb record size + header/mac/padding */
|
||||||
/* TODO: Check against sizes.cbMaximumMessage */
|
/* TODO: Check against sizes.cbMaximumMessage */
|
||||||
|
static void* secur32_lib;
|
||||||
|
static INIT_SECURITY_INTERFACE_A _InitSecurityInterfaceA;
|
||||||
|
static PSecurityFunctionTableA sspiFPs;
|
||||||
|
static cc_bool _verifyCerts;
|
||||||
|
|
||||||
|
void SSLBackend_Init(cc_bool verifyCerts) {
|
||||||
|
/* NOTE: Windows 95 secur32.dll doesn't export EncryptMessage/DecryptMessage */
|
||||||
|
/* so instead retrieve function pointers via SecurityFunctionTable table */
|
||||||
|
static const struct DynamicLibSym funcs[] = {
|
||||||
|
DynamicLib_Sym(InitSecurityInterfaceA)
|
||||||
|
};
|
||||||
|
static const cc_string secur32 = String_FromConst("secur32.dll");
|
||||||
|
static const cc_string security = String_FromConst("security.dll");
|
||||||
|
_verifyCerts = verifyCerts;
|
||||||
|
|
||||||
|
/* TODO: Load later?? prob too hard */
|
||||||
|
DynamicLib_LoadAll(&secur32, funcs, Array_Elems(funcs), &secur32_lib);
|
||||||
|
if (secur32_lib) return;
|
||||||
|
|
||||||
|
/* Windows NT 4.0 only has Security.dll, 9x and later have Secur32.dll */
|
||||||
|
/* (on ??? and later, Security.dll just contains forwards to Secur32.dll */
|
||||||
|
DynamicLib_LoadAll(&security, funcs, Array_Elems(funcs), &secur32_lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_bool SSLBackend_DescribeError(cc_result res, cc_string* dst) {
|
||||||
|
return Platform_DescribeErrorExt(res, dst, secur32_lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct SSLContext {
|
struct SSLContext {
|
||||||
cc_socket socket;
|
cc_socket socket;
|
||||||
CredHandle handle;
|
CredHandle handle;
|
||||||
CtxtHandle context;
|
CtxtHandle context;
|
||||||
SecPkgContext_StreamSizes sizes;
|
SecPkgContext_StreamSizes sizes;
|
||||||
int bufferLen;
|
DWORD flags;
|
||||||
int leftover; /* number of unprocessed bytes leftover from last successful DecryptMessage */
|
int bufferLen;
|
||||||
int decryptedSize;
|
int leftover; /* number of unprocessed bytes leftover from last successful DecryptMessage */
|
||||||
char* decryptedData;
|
int decryptedSize;
|
||||||
char incoming[TLS_MAX_PACKET_SIZE];
|
char* decryptedData;
|
||||||
|
char incoming[TLS_MAX_PACKET_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
static SECURITY_STATUS SSL_CreateHandle(struct SSLContext* ctx) {
|
static SECURITY_STATUS SSL_CreateHandle(struct SSLContext* ctx) {
|
||||||
@ -34,8 +67,8 @@ static SECURITY_STATUS SSL_CreateHandle(struct SSLContext* ctx) {
|
|||||||
cred.dwVersion = SCHANNEL_CRED_VERSION;
|
cred.dwVersion = SCHANNEL_CRED_VERSION;
|
||||||
cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS;
|
cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS;
|
||||||
|
|
||||||
return AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL,
|
return sspiFPs->AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL,
|
||||||
&cred, NULL, NULL, &ctx->handle, NULL);
|
&cred, NULL, NULL, &ctx->handle, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static cc_result SSL_SendRaw(cc_socket socket, const cc_uint8* data, cc_uint32 count) {
|
static cc_result SSL_SendRaw(cc_socket socket, const cc_uint8* data, cc_uint32 count) {
|
||||||
@ -67,36 +100,34 @@ static cc_result SSL_RecvRaw(struct SSLContext* ctx) {
|
|||||||
if (!read) return ERR_END_OF_STREAM;
|
if (!read) return ERR_END_OF_STREAM;
|
||||||
|
|
||||||
ctx->bufferLen += read;
|
ctx->bufferLen += read;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define SSL_FLAGS ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_USE_SUPPLIED_CREDS | ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM
|
|
||||||
|
|
||||||
/* Sends the initial TLS handshake ClientHello message to the server */
|
/* Sends the initial TLS handshake ClientHello message to the server */
|
||||||
static SECURITY_STATUS SSL_Connect(struct SSLContext* ctx, const char* hostname) {
|
static SECURITY_STATUS SSL_Connect(struct SSLContext* ctx, const char* hostname) {
|
||||||
SecBuffer out_buffers[1];
|
SecBuffer out_buffers[1];
|
||||||
SecBufferDesc out_desc;
|
SecBufferDesc out_desc;
|
||||||
SECURITY_STATUS res;
|
SECURITY_STATUS res;
|
||||||
DWORD flags = SSL_FLAGS;
|
DWORD flags = ctx->flags;
|
||||||
|
|
||||||
out_buffers[0].BufferType = SECBUFFER_TOKEN;
|
out_buffers[0].BufferType = SECBUFFER_TOKEN;
|
||||||
out_buffers[0].pvBuffer = NULL;
|
out_buffers[0].pvBuffer = NULL;
|
||||||
out_buffers[0].cbBuffer = 0;
|
out_buffers[0].cbBuffer = 0;
|
||||||
|
|
||||||
out_desc.ulVersion = SECBUFFER_VERSION;
|
out_desc.ulVersion = SECBUFFER_VERSION;
|
||||||
out_desc.cBuffers = ARRAYSIZE(out_buffers);
|
out_desc.cBuffers = Array_Elems(out_buffers);
|
||||||
out_desc.pBuffers = out_buffers;
|
out_desc.pBuffers = out_buffers;
|
||||||
|
|
||||||
res = InitializeSecurityContextA(&ctx->handle, NULL, hostname, flags, 0, 0,
|
res = sspiFPs->InitializeSecurityContextA(&ctx->handle, NULL, hostname, flags, 0, 0,
|
||||||
NULL, 0, &ctx->context, &out_desc, &flags, NULL);
|
NULL, 0, &ctx->context, &out_desc, &flags, NULL);
|
||||||
if (res != SEC_I_CONTINUE_NEEDED) return res;
|
if (res != SEC_I_CONTINUE_NEEDED) return res;
|
||||||
res = 0;
|
res = 0;
|
||||||
|
|
||||||
/* Send initial handshake to the server (if there is one) */
|
/* Send initial handshake to the server (if there is one) */
|
||||||
if (out_buffers[0].pvBuffer) {
|
if (out_buffers[0].pvBuffer) {
|
||||||
res = SSL_SendRaw(ctx->socket, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer);
|
res = SSL_SendRaw(ctx->socket, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer);
|
||||||
FreeContextBuffer(out_buffers[0].pvBuffer);
|
sspiFPs->FreeContextBuffer(out_buffers[0].pvBuffer);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -109,72 +140,71 @@ static SECURITY_STATUS SSL_Negotiate(struct SSLContext* ctx) {
|
|||||||
SecBufferDesc out_desc;
|
SecBufferDesc out_desc;
|
||||||
cc_uint32 leftover_len;
|
cc_uint32 leftover_len;
|
||||||
SECURITY_STATUS sec;
|
SECURITY_STATUS sec;
|
||||||
cc_uint32 read;
|
|
||||||
cc_result res;
|
cc_result res;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
/* buffer 0 = data received from server which SChannel processes */
|
/* buffer 0 = data received from server which SChannel processes */
|
||||||
/* buffer 1 = any leftover data which SChannel didn't process this time */
|
/* buffer 1 = any leftover data which SChannel didn't process this time */
|
||||||
/* (this data must be persisted, as it will be used next time around) */
|
/* (this data must be persisted, as it will be used next time around) */
|
||||||
in_buffers[0].BufferType = SECBUFFER_TOKEN;
|
in_buffers[0].BufferType = SECBUFFER_TOKEN;
|
||||||
in_buffers[0].pvBuffer = ctx->incoming;
|
in_buffers[0].pvBuffer = ctx->incoming;
|
||||||
in_buffers[0].cbBuffer = ctx->bufferLen;
|
in_buffers[0].cbBuffer = ctx->bufferLen;
|
||||||
in_buffers[1].BufferType = SECBUFFER_EMPTY;
|
in_buffers[1].BufferType = SECBUFFER_EMPTY;
|
||||||
in_buffers[1].pvBuffer = NULL;
|
in_buffers[1].pvBuffer = NULL;
|
||||||
in_buffers[1].cbBuffer = 0;
|
in_buffers[1].cbBuffer = 0;
|
||||||
|
|
||||||
out_buffers[0].BufferType = SECBUFFER_TOKEN;
|
out_buffers[0].BufferType = SECBUFFER_TOKEN;
|
||||||
out_buffers[0].pvBuffer = NULL;
|
out_buffers[0].pvBuffer = NULL;
|
||||||
out_buffers[0].cbBuffer = 0;
|
out_buffers[0].cbBuffer = 0;
|
||||||
|
|
||||||
in_desc.ulVersion = SECBUFFER_VERSION;
|
in_desc.ulVersion = SECBUFFER_VERSION;
|
||||||
in_desc.cBuffers = ARRAYSIZE(in_buffers);
|
in_desc.cBuffers = Array_Elems(in_buffers);
|
||||||
in_desc.pBuffers = in_buffers;
|
in_desc.pBuffers = in_buffers;
|
||||||
|
|
||||||
out_desc.ulVersion = SECBUFFER_VERSION;
|
out_desc.ulVersion = SECBUFFER_VERSION;
|
||||||
out_desc.cBuffers = ARRAYSIZE(out_buffers);
|
out_desc.cBuffers = Array_Elems(out_buffers);
|
||||||
out_desc.pBuffers = out_buffers;
|
out_desc.pBuffers = out_buffers;
|
||||||
|
|
||||||
flags = SSL_FLAGS;
|
flags = ctx->flags;
|
||||||
sec = InitializeSecurityContextA(&ctx->handle, &ctx->context, NULL, flags, 0, 0,
|
sec = sspiFPs->InitializeSecurityContextA(&ctx->handle, &ctx->context, NULL, flags, 0, 0,
|
||||||
&in_desc, 0, NULL, &out_desc, &flags, NULL);
|
&in_desc, 0, NULL, &out_desc, &flags, NULL);
|
||||||
|
|
||||||
if (in_buffers[1].BufferType == SECBUFFER_EXTRA)
|
if (in_buffers[1].BufferType == SECBUFFER_EXTRA)
|
||||||
{
|
{
|
||||||
/* SChannel didn't process the entirety of the input buffer */
|
/* SChannel didn't process the entirety of the input buffer */
|
||||||
/* So move the leftover data back to the front of the input buffer */
|
/* So move the leftover data back to the front of the input buffer */
|
||||||
leftover_len = in_buffers[1].cbBuffer;
|
leftover_len = in_buffers[1].cbBuffer;
|
||||||
memmove(ctx->incoming, ctx->incoming + (ctx->bufferLen - leftover_len), leftover_len);
|
memmove(ctx->incoming, ctx->incoming + (ctx->bufferLen - leftover_len), leftover_len);
|
||||||
ctx->bufferLen = leftover_len;
|
ctx->bufferLen = leftover_len;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* SChannel processed entirely of input buffer */
|
/* SChannel processed entirely of input buffer */
|
||||||
ctx->bufferLen = 0;
|
ctx->bufferLen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handshake completed */
|
/* Handshake completed */
|
||||||
if (sec == SEC_E_OK) break;
|
if (sec == SEC_E_OK) break;
|
||||||
|
|
||||||
/* Need to send data to the server */
|
/* Need to send data to the server */
|
||||||
if (sec == SEC_I_CONTINUE_NEEDED)
|
if (sec == SEC_I_CONTINUE_NEEDED)
|
||||||
{
|
{
|
||||||
res = SSL_SendRaw(ctx->socket, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer);
|
res = SSL_SendRaw(ctx->socket, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer);
|
||||||
FreeContextBuffer(out_buffers[0].pvBuffer); /* TODO always free? */
|
sspiFPs->FreeContextBuffer(out_buffers[0].pvBuffer); /* TODO always free? */
|
||||||
|
|
||||||
if (res) return res;
|
if (res) return res;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sec != SEC_E_INCOMPLETE_MESSAGE) return sec;
|
if (sec != SEC_E_INCOMPLETE_MESSAGE) return sec;
|
||||||
/* SEC_E_INCOMPLETE_MESSAGE case - need to read more data from the server first */
|
/* SEC_E_INCOMPLETE_MESSAGE case - need to read more data from the server first */
|
||||||
if ((res = SSL_RecvRaw(ctx))) return res;
|
if ((res = SSL_RecvRaw(ctx))) return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryContextAttributesA(&ctx->context, SECPKG_ATTR_STREAM_SIZES, &ctx->sizes);
|
sspiFPs->QueryContextAttributesA(&ctx->context, SECPKG_ATTR_STREAM_SIZES, &ctx->sizes);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_result SSL_Init(cc_socket socket, const cc_string* host_, void** out_ctx) {
|
cc_result SSL_Init(cc_socket socket, const cc_string* host_, void** out_ctx) {
|
||||||
@ -182,16 +212,23 @@ cc_result SSL_Init(cc_socket socket, const cc_string* host_, void** out_ctx) {
|
|||||||
SECURITY_STATUS res;
|
SECURITY_STATUS res;
|
||||||
cc_winstring host;
|
cc_winstring host;
|
||||||
|
|
||||||
|
if (!_InitSecurityInterfaceA) return HTTP_ERR_NO_SSL;
|
||||||
|
if (!sspiFPs) sspiFPs = _InitSecurityInterfaceA();
|
||||||
|
if (!sspiFPs) return ERR_NOT_SUPPORTED;
|
||||||
|
|
||||||
ctx = Mem_TryAllocCleared(1, sizeof(struct SSLContext));
|
ctx = Mem_TryAllocCleared(1, sizeof(struct SSLContext));
|
||||||
if (!ctx) return ERR_OUT_OF_MEMORY;
|
if (!ctx) return ERR_OUT_OF_MEMORY;
|
||||||
*out_ctx = (void*)ctx;
|
*out_ctx = (void*)ctx;
|
||||||
|
|
||||||
|
ctx->flags = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_USE_SUPPLIED_CREDS | ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
|
||||||
|
if (!_verifyCerts) ctx->flags |= ISC_REQ_MANUAL_CRED_VALIDATION;
|
||||||
|
|
||||||
ctx->socket = socket;
|
ctx->socket = socket;
|
||||||
Platform_EncodeString(&host, host_);
|
Platform_EncodeString(&host, host_);
|
||||||
|
|
||||||
if ((res = SSL_CreateHandle(ctx))) return res;
|
if ((res = SSL_CreateHandle(ctx))) return res;
|
||||||
if ((res = SSL_Connect(ctx, host.ansi))) return res;
|
if ((res = SSL_Connect(ctx, host.ansi))) return res;
|
||||||
if ((res = SSL_Negotiate(ctx))) return res;
|
if ((res = SSL_Negotiate(ctx))) return res;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,8 +260,8 @@ static cc_result SSL_ReadDecrypted(struct SSLContext* ctx, cc_uint8* data, cc_ui
|
|||||||
|
|
||||||
cc_result SSL_Read(void* ctx_, cc_uint8* data, cc_uint32 count, cc_uint32* read) {
|
cc_result SSL_Read(void* ctx_, cc_uint8* data, cc_uint32 count, cc_uint32* read) {
|
||||||
struct SSLContext* ctx = ctx_;
|
struct SSLContext* ctx = ctx_;
|
||||||
SecBuffer out_buffers[4];
|
SecBuffer buffers[4];
|
||||||
SecBufferDesc out_desc;
|
SecBufferDesc desc;
|
||||||
SECURITY_STATUS sec;
|
SECURITY_STATUS sec;
|
||||||
cc_result res;
|
cc_result res;
|
||||||
|
|
||||||
@ -236,36 +273,35 @@ cc_result SSL_Read(void* ctx_, cc_uint8* data, cc_uint32 count, cc_uint32* read)
|
|||||||
/* if any ciphertext data, then try to decrypt it */
|
/* if any ciphertext data, then try to decrypt it */
|
||||||
if (ctx->bufferLen)
|
if (ctx->bufferLen)
|
||||||
{
|
{
|
||||||
out_buffers[0].BufferType = SECBUFFER_DATA;
|
/* https://learn.microsoft.com/en-us/windows/win32/secauthn/stream-contexts */
|
||||||
out_buffers[0].pvBuffer = ctx->incoming;
|
buffers[0].BufferType = SECBUFFER_DATA;
|
||||||
out_buffers[0].cbBuffer = ctx->bufferLen;
|
buffers[0].pvBuffer = ctx->incoming;
|
||||||
out_buffers[1].BufferType = SECBUFFER_EMPTY;
|
buffers[0].cbBuffer = ctx->bufferLen;
|
||||||
out_buffers[2].BufferType = SECBUFFER_EMPTY;
|
buffers[1].BufferType = SECBUFFER_EMPTY;
|
||||||
out_buffers[3].BufferType = SECBUFFER_EMPTY;
|
buffers[2].BufferType = SECBUFFER_EMPTY;
|
||||||
|
buffers[3].BufferType = SECBUFFER_EMPTY;
|
||||||
|
|
||||||
out_desc.ulVersion = SECBUFFER_VERSION;
|
desc.ulVersion = SECBUFFER_VERSION;
|
||||||
out_desc.cBuffers = ARRAYSIZE(out_buffers);
|
desc.cBuffers = Array_Elems(buffers);
|
||||||
out_desc.pBuffers = out_buffers;
|
desc.pBuffers = buffers;
|
||||||
|
|
||||||
sec = DecryptMessage(&ctx->context, &out_desc, 0, NULL);
|
sec = sspiFPs->DecryptMessage(&ctx->context, &desc, 0, NULL);
|
||||||
if (sec == SEC_E_OK)
|
if (sec == SEC_E_OK)
|
||||||
{
|
{
|
||||||
/* After successful decryption the SECBUFFERS will be: */
|
/* After successful decryption the SecBuffers will be: */
|
||||||
/* buffer[0] = headers */
|
/* buffers[0] = headers */
|
||||||
/* buffer[1] = content */
|
/* buffers[1] = content */
|
||||||
/* buffer[2] = trailers */
|
/* buffers[2] = trailers */
|
||||||
/* buffer[3] = extra, if any leftover unprocessed data */
|
/* buffers[3] = extra, if any leftover unprocessed data */
|
||||||
ctx->decryptedData = out_buffers[1].pvBuffer;
|
ctx->decryptedData = buffers[1].pvBuffer;
|
||||||
ctx->decryptedSize = out_buffers[1].cbBuffer;
|
ctx->decryptedSize = buffers[1].cbBuffer;
|
||||||
ctx->leftover = out_buffers[3].BufferType == SECBUFFER_EXTRA ? out_buffers[3].cbBuffer : 0;
|
ctx->leftover = buffers[3].BufferType == SECBUFFER_EXTRA ? buffers[3].cbBuffer : 0;
|
||||||
|
|
||||||
return SSL_ReadDecrypted(ctx, data, count, read);
|
return SSL_ReadDecrypted(ctx, data, count, read);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sec != SEC_E_INCOMPLETE_MESSAGE) return sec;
|
if (sec != SEC_E_INCOMPLETE_MESSAGE) return sec;
|
||||||
/* SEC_E_INCOMPLETE_MESSAGE case - still need to read more data from the server first */
|
/* SEC_E_INCOMPLETE_MESSAGE case - still need to read more data from the server first */
|
||||||
|
|
||||||
/* TODO free output buffer? */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* not enough data received yet to decrypt, so need to read more data from the server */
|
/* not enough data received yet to decrypt, so need to read more data from the server */
|
||||||
@ -276,33 +312,33 @@ cc_result SSL_Read(void* ctx_, cc_uint8* data, cc_uint32 count, cc_uint32* read)
|
|||||||
|
|
||||||
static cc_result SSL_WriteChunk(struct SSLContext* s, const cc_uint8* data, cc_uint32 count) {
|
static cc_result SSL_WriteChunk(struct SSLContext* s, const cc_uint8* data, cc_uint32 count) {
|
||||||
char buffer[TLS_MAX_PACKET_SIZE];
|
char buffer[TLS_MAX_PACKET_SIZE];
|
||||||
SecBuffer in_buffers[3];
|
SecBuffer buffers[3];
|
||||||
SecBufferDesc in_desc;
|
SecBufferDesc desc;
|
||||||
SECURITY_STATUS res;
|
SECURITY_STATUS res;
|
||||||
int total;
|
int total;
|
||||||
|
|
||||||
in_buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
|
buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
|
||||||
in_buffers[0].pvBuffer = buffer;
|
buffers[0].pvBuffer = buffer;
|
||||||
in_buffers[0].cbBuffer = s->sizes.cbHeader;
|
buffers[0].cbBuffer = s->sizes.cbHeader;
|
||||||
in_buffers[1].BufferType = SECBUFFER_DATA;
|
buffers[1].BufferType = SECBUFFER_DATA;
|
||||||
in_buffers[1].pvBuffer = buffer + s->sizes.cbHeader;
|
buffers[1].pvBuffer = buffer + s->sizes.cbHeader;
|
||||||
in_buffers[1].cbBuffer = count;
|
buffers[1].cbBuffer = count;
|
||||||
in_buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
|
buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
|
||||||
in_buffers[2].pvBuffer = buffer + s->sizes.cbHeader + count;
|
buffers[2].pvBuffer = buffer + s->sizes.cbHeader + count;
|
||||||
in_buffers[2].cbBuffer = s->sizes.cbTrailer;
|
buffers[2].cbBuffer = s->sizes.cbTrailer;
|
||||||
|
|
||||||
/* See https://learn.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-encryptmessage */
|
/* See https://learn.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-encryptmessage */
|
||||||
/* ".. The message is encrypted in place, overwriting the original contents of the structure" */
|
/* ".. The message is encrypted in place, overwriting the original contents of the structure" */
|
||||||
Mem_Copy(in_buffers[1].pvBuffer, data, count);
|
Mem_Copy(buffers[1].pvBuffer, data, count);
|
||||||
|
|
||||||
in_desc.ulVersion = SECBUFFER_VERSION;
|
desc.ulVersion = SECBUFFER_VERSION;
|
||||||
in_desc.cBuffers = ARRAYSIZE(in_buffers);
|
desc.cBuffers = Array_Elems(buffers);
|
||||||
in_desc.pBuffers = in_buffers;
|
desc.pBuffers = buffers;
|
||||||
if ((res = EncryptMessage(&s->context, 0, &in_desc, 0))) return res;
|
if ((res = sspiFPs->EncryptMessage(&s->context, 0, &desc, 0))) return res;
|
||||||
|
|
||||||
/* NOTE: Okay to write in one go, since all three buffers will be contiguous */
|
/* NOTE: Okay to write in one go, since all three buffers will be contiguous */
|
||||||
/* (as TLS record header size will always be the same size) */
|
/* (as TLS record header size will always be the same size) */
|
||||||
total = in_buffers[0].cbBuffer + in_buffers[1].cbBuffer + in_buffers[2].cbBuffer;
|
total = buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer;
|
||||||
return SSL_SendRaw(s->socket, buffer, total);
|
return SSL_SendRaw(s->socket, buffer, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,12 +363,15 @@ cc_result SSL_Write(void* ctx, const cc_uint8* data, cc_uint32 count, cc_uint32*
|
|||||||
cc_result SSL_Free(void* ctx_) {
|
cc_result SSL_Free(void* ctx_) {
|
||||||
/* TODO send TLS close */
|
/* TODO send TLS close */
|
||||||
struct SSLContext* ctx = (struct SSLContext*)ctx_;
|
struct SSLContext* ctx = (struct SSLContext*)ctx_;
|
||||||
DeleteSecurityContext(&ctx->context);
|
sspiFPs->DeleteSecurityContext(&ctx->context);
|
||||||
FreeCredentialsHandle(&ctx->handle);
|
sspiFPs->FreeCredentialsHandle(&ctx->handle);
|
||||||
Mem_Free(ctx);
|
Mem_Free(ctx);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
void SSLBackend_Init(cc_bool verifyCerts) { }
|
||||||
|
cc_bool SSLBackend_DescribeError(cc_result res, cc_string* dst) { return false; }
|
||||||
|
|
||||||
cc_result SSL_Init(cc_socket socket, const cc_string* host, void** ctx) {
|
cc_result SSL_Init(cc_socket socket, const cc_string* host, void** ctx) {
|
||||||
return HTTP_ERR_NO_SSL;
|
return HTTP_ERR_NO_SSL;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ Wraps a socket connection in a TLS/SSL connection
|
|||||||
Copyright 2014-2022 ClassiCube | Licensed under BSD-3
|
Copyright 2014-2022 ClassiCube | Licensed under BSD-3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void SSLBackend_Init(cc_bool verifyCerts);
|
||||||
|
cc_bool SSLBackend_DescribeError(cc_result res, cc_string* dst);
|
||||||
|
|
||||||
cc_result SSL_Init(cc_socket socket, const cc_string* host, void** ctx);
|
cc_result SSL_Init(cc_socket socket, const cc_string* host, void** ctx);
|
||||||
cc_result SSL_Read(void* ctx, cc_uint8* data, cc_uint32 count, cc_uint32* read);
|
cc_result SSL_Read(void* ctx, cc_uint8* data, cc_uint32 count, cc_uint32* read);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user