Fix SSL failing with SEC_E_SECPKG_NOT_FOUND on Win 9x

This commit is contained in:
UnknownShadow200 2023-06-02 22:27:46 +10:00
parent 4323e6d0f5
commit 2851e02dcf

View File

@ -20,28 +20,39 @@
/* 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 void* schannel_lib;
static INIT_SECURITY_INTERFACE_A _InitSecurityInterfaceA; static INIT_SECURITY_INTERFACE_A _InitSecurityInterfaceA;
static PSecurityFunctionTableA sspiFPs;
static cc_bool _verifyCerts; static cc_bool _verifyCerts;
static ACQUIRE_CREDENTIALS_HANDLE_FN_A FP_AcquireCredentialsHandleA;
static FREE_CREDENTIALS_HANDLE_FN FP_FreeCredentialsHandle;
static INITIALIZE_SECURITY_CONTEXT_FN_A FP_InitializeSecurityContextA;
static ACCEPT_SECURITY_CONTEXT_FN FP_AcceptSecurityContext;
static COMPLETE_AUTH_TOKEN_FN FP_CompleteAuthToken;
static DELETE_SECURITY_CONTEXT_FN FP_DeleteSecurityContext;
static QUERY_CONTEXT_ATTRIBUTES_FN_A FP_QueryContextAttributesA;
static FREE_CONTEXT_BUFFER_FN FP_FreeContextBuffer;
static ENCRYPT_MESSAGE_FN FP_EncryptMessage;
static DECRYPT_MESSAGE_FN FP_DecryptMessage;
void SSLBackend_Init(cc_bool verifyCerts) { void SSLBackend_Init(cc_bool verifyCerts) {
/* NOTE: Windows 95 secur32.dll doesn't export EncryptMessage/DecryptMessage */ /* secur32.dll is available on Win9x and later */
/* so instead retrieve function pointers via SecurityFunctionTable table */ /* Security.dll is available on NT 4 and later */
/* Officially, InitSecurityInterfaceA and then AcquireCredentialsA from */
/* secur32.dll (or security.dll) should be called - however */
/* AcquireCredentialsA fails with SEC_E_SECPKG_NOT_FOUND on Win 9x */
/* But if you instead directly call those functions from schannel.dll,
/* then it DOES work. (and on later Windows versions, those functions */
/* exported from schannel.dll are just DLL forwards to secur32.dll */
static const struct DynamicLibSym funcs[] = { static const struct DynamicLibSym funcs[] = {
DynamicLib_Sym(InitSecurityInterfaceA) DynamicLib_Sym(InitSecurityInterfaceA)
}; };
static const cc_string secur32 = String_FromConst("secur32.dll"); static const cc_string schannel = String_FromConst("schannel.dll");
static const cc_string security = String_FromConst("security.dll");
_verifyCerts = verifyCerts; _verifyCerts = verifyCerts;
/* TODO: Load later?? prob too hard */ /* TODO: Load later?? prob too hard */
DynamicLib_LoadAll(&secur32, funcs, Array_Elems(funcs), &secur32_lib); DynamicLib_LoadAll(&schannel, funcs, Array_Elems(funcs), &schannel_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) { cc_bool SSLBackend_DescribeError(cc_result res, cc_string* dst) {
@ -56,6 +67,7 @@ cc_bool SSLBackend_DescribeError(cc_result res, cc_string* dst) {
String_AppendConst(dst, "The signature of the website's SSL certificate cannot be verified"); String_AppendConst(dst, "The signature of the website's SSL certificate cannot be verified");
return true; return true;
} }
return false;
} }
@ -77,8 +89,8 @@ static SECURITY_STATUS SSL_CreateHandle(struct SSLContext* ctx) {
cred.dwVersion = SCHANNEL_CRED_VERSION; cred.dwVersion = SCHANNEL_CRED_VERSION;
cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | (_verifyCerts ? SCH_CRED_AUTO_CRED_VALIDATION : SCH_CRED_MANUAL_CRED_VALIDATION); cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | (_verifyCerts ? SCH_CRED_AUTO_CRED_VALIDATION : SCH_CRED_MANUAL_CRED_VALIDATION);
/* TODO: SCHANNEL_NAME_A and use SChannel dll? */ /* TODO: SCHANNEL_NAME_A ? */
return sspiFPs->AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, return FP_AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL,
&cred, NULL, NULL, &ctx->handle, NULL); &cred, NULL, NULL, &ctx->handle, NULL);
} }
@ -130,7 +142,7 @@ static SECURITY_STATUS SSL_Connect(struct SSLContext* ctx, const char* hostname)
out_desc.cBuffers = Array_Elems(out_buffers); out_desc.cBuffers = Array_Elems(out_buffers);
out_desc.pBuffers = out_buffers; out_desc.pBuffers = out_buffers;
res = sspiFPs->InitializeSecurityContextA(&ctx->handle, NULL, hostname, flags, 0, 0, res = FP_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;
@ -138,7 +150,7 @@ static SECURITY_STATUS SSL_Connect(struct SSLContext* ctx, const char* hostname)
/* 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);
sspiFPs->FreeContextBuffer(out_buffers[0].pvBuffer); FP_FreeContextBuffer(out_buffers[0].pvBuffer);
} }
return res; return res;
} }
@ -179,7 +191,7 @@ static SECURITY_STATUS SSL_Negotiate(struct SSLContext* ctx) {
out_desc.pBuffers = out_buffers; out_desc.pBuffers = out_buffers;
flags = ctx->flags; flags = ctx->flags;
sec = sspiFPs->InitializeSecurityContextA(&ctx->handle, &ctx->context, NULL, flags, 0, 0, sec = FP_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) {
@ -199,7 +211,7 @@ static SECURITY_STATUS SSL_Negotiate(struct SSLContext* ctx) {
/* 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);
sspiFPs->FreeContextBuffer(out_buffers[0].pvBuffer); /* TODO always free? */ FP_FreeContextBuffer(out_buffers[0].pvBuffer); /* TODO always free? */
if (res) return res; if (res) return res;
continue; continue;
@ -210,18 +222,41 @@ static SECURITY_STATUS SSL_Negotiate(struct SSLContext* ctx) {
if ((res = SSL_RecvRaw(ctx))) return res; if ((res = SSL_RecvRaw(ctx))) return res;
} }
sspiFPs->QueryContextAttributesA(&ctx->context, SECPKG_ATTR_STREAM_SIZES, &ctx->sizes); FP_QueryContextAttributesA(&ctx->context, SECPKG_ATTR_STREAM_SIZES, &ctx->sizes);
return 0; return 0;
} }
static void SSL_LoadSecurityFunctions(PSecurityFunctionTableA sspiFPs) {
FP_AcquireCredentialsHandleA = sspiFPs->AcquireCredentialsHandleA;
FP_FreeCredentialsHandle = sspiFPs->FreeCredentialsHandle;
FP_InitializeSecurityContextA = sspiFPs->InitializeSecurityContextA;
FP_AcceptSecurityContext = sspiFPs->AcceptSecurityContext;
FP_CompleteAuthToken = sspiFPs->CompleteAuthToken;
FP_DeleteSecurityContext = sspiFPs->DeleteSecurityContext;
FP_QueryContextAttributesA = sspiFPs->QueryContextAttributesA;
FP_FreeContextBuffer = sspiFPs->FreeContextBuffer;
FP_EncryptMessage = sspiFPs->EncryptMessage;
FP_DecryptMessage = sspiFPs->DecryptMessage;
/* Old Windows versions don't have EncryptMessage/DecryptMessage, */
/* but have the older SealMessage/UnsealMessage functions instead */
if (!FP_EncryptMessage) FP_EncryptMessage = (ENCRYPT_MESSAGE_FN)sspiFPs->Reserved3;
if (!FP_DecryptMessage) FP_DecryptMessage = (DECRYPT_MESSAGE_FN)sspiFPs->Reserved4;
}
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) {
PSecurityFunctionTableA sspiFPs;
struct SSLContext* ctx; struct SSLContext* ctx;
SECURITY_STATUS res; SECURITY_STATUS res;
cc_winstring host; cc_winstring host;
if (!_InitSecurityInterfaceA) return HTTP_ERR_NO_SSL; if (!_InitSecurityInterfaceA) return HTTP_ERR_NO_SSL;
if (!sspiFPs) sspiFPs = _InitSecurityInterfaceA();
if (!FP_InitializeSecurityContextA) {
sspiFPs = _InitSecurityInterfaceA();
if (!sspiFPs) return ERR_NOT_SUPPORTED; if (!sspiFPs) return ERR_NOT_SUPPORTED;
SSL_LoadSecurityFunctions(sspiFPs);
}
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;
@ -239,6 +274,7 @@ cc_result SSL_Init(cc_socket socket, const cc_string* host_, void** out_ctx) {
return 0; return 0;
} }
static cc_result SSL_ReadDecrypted(struct SSLContext* ctx, cc_uint8* data, cc_uint32 count, cc_uint32* read) { static cc_result SSL_ReadDecrypted(struct SSLContext* ctx, cc_uint8* data, cc_uint32 count, cc_uint32* read) {
int len = min(count, ctx->decryptedSize); int len = min(count, ctx->decryptedSize);
Mem_Copy(data, ctx->decryptedData, len); Mem_Copy(data, ctx->decryptedData, len);
@ -288,7 +324,7 @@ cc_result SSL_Read(void* ctx_, cc_uint8* data, cc_uint32 count, cc_uint32* read)
desc.cBuffers = Array_Elems(buffers); desc.cBuffers = Array_Elems(buffers);
desc.pBuffers = buffers; desc.pBuffers = buffers;
sec = sspiFPs->DecryptMessage(&ctx->context, &desc, 0, NULL); sec = FP_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: */
/* buffers[0] = headers */ /* buffers[0] = headers */
@ -337,7 +373,7 @@ static cc_result SSL_WriteChunk(struct SSLContext* s, const cc_uint8* data, cc_u
desc.ulVersion = SECBUFFER_VERSION; desc.ulVersion = SECBUFFER_VERSION;
desc.cBuffers = Array_Elems(buffers); desc.cBuffers = Array_Elems(buffers);
desc.pBuffers = buffers; desc.pBuffers = buffers;
if ((res = sspiFPs->EncryptMessage(&s->context, 0, &desc, 0))) return res; if ((res = FP_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) */
@ -366,8 +402,8 @@ 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_;
sspiFPs->DeleteSecurityContext(&ctx->context); FP_DeleteSecurityContext(&ctx->context);
sspiFPs->FreeCredentialsHandle(&ctx->handle); FP_FreeCredentialsHandle(&ctx->handle);
Mem_Free(ctx); Mem_Free(ctx);
return 0; return 0;
} }