1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-12-26 01:59:53 +03:00

Cedar: handle SSTP through Proto

This commit is contained in:
Davide Beatrici 2020-07-17 02:00:30 +02:00
parent d917b2ac73
commit 869496be3e
4 changed files with 184 additions and 260 deletions

View File

@ -133,6 +133,8 @@ PROTO *ProtoNew(CEDAR *cedar)
// OpenVPN
ProtoImplAdd(proto, OvsGetProtoImpl());
// SSTP
ProtoImplAdd(proto, SstpGetProtoImpl());
proto->UdpListener = NewUdpListener(ProtoHandleDatagrams, proto, &cedar->Server->ListenIP);

View File

@ -16,6 +16,171 @@ bool GetNoSstp()
return g_no_sstp;
}
PROTO_IMPL *SstpGetProtoImpl()
{
static PROTO_IMPL impl =
{
SstpInit,
SstpFree,
SstpName,
NULL,
SstpProcessData,
NULL
};
return &impl;
}
bool SstpInit(void **param, struct CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname)
{
if (param == NULL || cedar == NULL || im == NULL || se == NULL)
{
return false;
}
Debug("SstpInit(): cipher: %s, hostname: %s\n", cipher, hostname);
*param = NewSstpServer(cedar, im, se, cipher, hostname);
return true;
}
void SstpFree(void *param)
{
FreeSstpServer(param);
}
char *SstpName()
{
return "SSTP";
}
bool SstpProcessData(void *param, TCP_RAW_DATA *in, FIFO *out)
{
FIFO *recv_fifo;
bool disconnected = false;
SSTP_SERVER *server = param;
if (server == NULL || in == NULL || out == NULL)
{
return false;
}
if (server->Status == SSTP_SERVER_STATUS_NOT_INITIALIZED)
{
HTTP_HEADER *header;
char *header_str, date_str[MAX_SIZE];
GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());
header = NewHttpHeader("HTTP/1.1", "200", "OK");
AddHttpValue(header, NewHttpValue("Content-Length", "18446744073709551615"));
AddHttpValue(header, NewHttpValue("Server", "Microsoft-HTTPAPI/2.0"));
AddHttpValue(header, NewHttpValue("Date", date_str));
header_str = HttpHeaderToStr(header);
FreeHttpHeader(header);
if (header_str == NULL)
{
return false;
}
WriteFifo(out, header_str, StrLen(header_str));
Free(header_str);
Copy(&server->ClientIp, &in->SrcIP, sizeof(server->ClientIp));
server->ClientPort = in->SrcPort;
Copy(&server->ServerIp, &in->DstIP, sizeof(server->ServerIp));
server->ServerPort = in->DstPort;
server->Status = SSTP_SERVER_STATUS_REQUEST_PENGING;
return true;
}
recv_fifo = in->Data;
while (recv_fifo->size >= 4)
{
UCHAR *first4;
bool ok = false;
UINT read_size = 0;
// Read 4 bytes from the beginning of the received queue.
first4 = ((UCHAR *)recv_fifo->p) + recv_fifo->pos;
if (first4[0] == SSTP_VERSION_1)
{
const USHORT len = READ_USHORT(first4 + 2) & 0xFFF;
if (len >= 4)
{
ok = true;
if (recv_fifo->size >= len)
{
UCHAR *data;
BLOCK *b;
read_size = len;
data = Malloc(read_size);
ReadFifo(recv_fifo, data, read_size);
b = NewBlock(data, read_size, 0);
InsertQueue(server->RecvQueue, b);
}
}
}
if (read_size == 0)
{
break;
}
if (ok == false)
{
// Bad packet received, trigger disconnection.
disconnected = true;
break;
}
}
// Process the timer interrupt
SstpProcessInterrupt(server);
if (server->Disconnected)
{
disconnected = true;
}
while (true)
{
BLOCK *b = GetNext(server->SendQueue);
if (b == NULL)
{
break;
}
// Discard the data block if the transmission queue's size is greater than ~2.5 MB.
if (b->PriorityQoS || (FifoSize(out) <= MAX_BUFFERING_PACKET_SIZE))
{
WriteFifo(out, b->Buf, b->Size);
}
FreeBlock(b);
}
if (disconnected)
{
return false;
}
return true;
}
// Process the SSTP control packet reception
void SstpProcessControlPacket(SSTP_SERVER *s, SSTP_PACKET *p)
{
@ -829,39 +994,28 @@ void SstpFreePacket(SSTP_PACKET *p)
}
// Create a SSTP server
SSTP_SERVER *NewSstpServer(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_ip,
UINT server_port, SOCK_EVENT *se,
char *client_host_name, char *crypt_name)
SSTP_SERVER *NewSstpServer(CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname)
{
SSTP_SERVER *s = ZeroMalloc(sizeof(SSTP_SERVER));
s->LastRecvTick = Tick64();
s->Status = SSTP_SERVER_STATUS_NOT_INITIALIZED;
StrCpy(s->ClientHostName, sizeof(s->ClientHostName), client_host_name);
StrCpy(s->ClientCipherName, sizeof(s->ClientCipherName), crypt_name);
s->Now = Tick64();
s->LastRecvTick = s->Now;
s->Cedar = cedar;
AddRef(s->Cedar->ref);
s->Interrupt = im;
s->SockEvent = se;
StrCpy(s->ClientHostName, sizeof(s->ClientHostName), hostname);
StrCpy(s->ClientCipherName, sizeof(s->ClientCipherName), cipher);
NewTubePair(&s->TubeSend, &s->TubeRecv, 0);
SetTubeSockEvent(s->TubeSend, se);
s->Now = Tick64();
Copy(&s->ClientIp, client_ip, sizeof(IP));
s->ClientPort = client_port;
Copy(&s->ServerIp, server_ip, sizeof(IP));
s->ServerPort = server_port;
s->SockEvent = se;
AddRef(s->SockEvent->ref);
s->RecvQueue = NewQueueFast();
s->SendQueue = NewQueueFast();
s->Interrupt = NewInterruptManager();
return s;
}
@ -907,242 +1061,8 @@ void FreeSstpServer(SSTP_SERVER *s)
ReleaseQueue(s->RecvQueue);
ReleaseQueue(s->SendQueue);
ReleaseSockEvent(s->SockEvent);
FreeInterruptManager(s->Interrupt);
ReleaseCedar(s->Cedar);
ReleaseTube(s->TubeSend);
ReleaseTube(s->TubeRecv);
Free(s);
}
// Handle the communication of SSTP protocol
bool ProcessSstpHttps(CEDAR *cedar, SOCK *s, SOCK_EVENT *se)
{
UINT tmp_size = 65536;
UCHAR *tmp_buf;
FIFO *recv_fifo;
FIFO *send_fifo;
SSTP_SERVER *sstp;
bool ret = false;
// Validate arguments
if (cedar == NULL || s == NULL || se == NULL)
{
return false;
}
tmp_buf = Malloc(tmp_size);
recv_fifo = NewFifo();
send_fifo = NewFifo();
sstp = NewSstpServer(cedar, &s->RemoteIP, s->RemotePort, &s->LocalIP, s->LocalPort, se,
s->RemoteHostname, s->CipherName);
while (true)
{
UINT r;
bool is_disconnected = false;
bool state_changed = false;
// Receive data over SSL
while (true)
{
r = Recv(s, tmp_buf, tmp_size, true);
if (r == 0)
{
// SSL is disconnected
is_disconnected = true;
break;
}
else if (r == SOCK_LATER)
{
// Data is not received any more
break;
}
else
{
// Queue the received data
WriteFifo(recv_fifo, tmp_buf, r);
state_changed = true;
}
}
while (recv_fifo->size >= 4)
{
UCHAR *first4;
UINT read_size = 0;
bool ok = false;
// Read 4 bytes from the beginning of the receive queue
first4 = ((UCHAR *)recv_fifo->p) + recv_fifo->pos;
if (first4[0] == SSTP_VERSION_1)
{
USHORT len = READ_USHORT(first4 + 2) & 0xFFF;
if (len >= 4)
{
ok = true;
if (recv_fifo->size >= len)
{
UCHAR *data;
BLOCK *b;
read_size = len;
data = Malloc(read_size);
ReadFifo(recv_fifo, data, read_size);
b = NewBlock(data, read_size, 0);
InsertQueue(sstp->RecvQueue, b);
}
}
}
if (read_size == 0)
{
break;
}
if (ok == false)
{
// Disconnect the connection since a bad packet received
is_disconnected = true;
break;
}
}
// Process the timer interrupt
SstpProcessInterrupt(sstp);
if (sstp->Disconnected)
{
is_disconnected = true;
}
// Put the transmission data that SSTP module has generated into the transmission queue
while (true)
{
BLOCK *b = GetNext(sstp->SendQueue);
if (b == NULL)
{
break;
}
// When transmit a data packet, If there are packets of more than about
// 2.5 MB in the transmission queue of the TCP, discard without transmission
if (b->PriorityQoS || (send_fifo->size <= MAX_BUFFERING_PACKET_SIZE))
{
WriteFifo(send_fifo, b->Buf, b->Size);
}
FreeBlock(b);
}
// Data is transmitted over SSL
while (send_fifo->size != 0)
{
r = Send(s, ((UCHAR *)send_fifo->p) + send_fifo->pos, send_fifo->size, true);
if (r == 0)
{
// SSL is disconnected
is_disconnected = true;
break;
}
else if (r == SOCK_LATER)
{
// Can not send any more
break;
}
else
{
// Advance the transmission queue by the amount of the transmitted
ReadFifo(send_fifo, NULL, r);
state_changed = true;
}
}
if (is_disconnected)
{
// Disconnected
break;
}
// Wait for the next state change
if (state_changed == false)
{
UINT select_time = SELECT_TIME;
UINT r = GetNextIntervalForInterrupt(sstp->Interrupt);
WaitSockEvent(se, MIN(r, select_time));
}
}
if (sstp != NULL && sstp->EstablishedCount >= 1)
{
ret = true;
}
FreeSstpServer(sstp);
ReleaseFifo(recv_fifo);
ReleaseFifo(send_fifo);
Free(tmp_buf);
YieldCpu();
Disconnect(s);
return ret;
}
// Accept the SSTP connection
bool AcceptSstp(CONNECTION *c)
{
SOCK *s;
HTTP_HEADER *h;
char date_str[MAX_SIZE];
bool ret;
bool ret2 = false;
SOCK_EVENT *se;
// Validate arguments
if (c == NULL)
{
return false;
}
s = c->FirstSock;
GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());
// Return a response
h = NewHttpHeader("HTTP/1.1", "200", "OK");
AddHttpValue(h, NewHttpValue("Content-Length", "18446744073709551615"));
AddHttpValue(h, NewHttpValue("Server", "Microsoft-HTTPAPI/2.0"));
AddHttpValue(h, NewHttpValue("Date", date_str));
ret = PostHttp(s, h, NULL, 0);
FreeHttpHeader(h);
if (ret)
{
SetTimeout(s, INFINITE);
se = NewSockEvent();
JoinSockToSockEvent(s, se);
Debug("ProcessSstpHttps Start.\n");
ret2 = ProcessSstpHttps(c->Cedar, s, se);
Debug("ProcessSstpHttps End.\n");
ReleaseSockEvent(se);
}
Disconnect(s);
return ret2;
}

View File

@ -61,6 +61,7 @@
#define SSTP_SERVER_STATUS_REQUEST_PENGING 0 // Connection incomplete
#define SSTP_SERVER_STATUS_CONNECTED_PENDING 1 // Connection completed. Authentication incomplete
#define SSTP_SERVER_STATUS_ESTABLISHED 2 // Connection completed. Communication available
#define SSTP_SERVER_STATUS_NOT_INITIALIZED INFINITE // Connection not accepted yet.
// Length of Nonce
#define SSTP_NONCE_SIZE 32 // 256 bits
@ -121,12 +122,13 @@ struct SSTP_SERVER
//// Function prototype
bool AcceptSstp(CONNECTION *c);
bool ProcessSstpHttps(CEDAR *cedar, SOCK *s, SOCK_EVENT *se);
PROTO_IMPL *SstpGetProtoImpl();
bool SstpInit(void **param, struct CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname);
void SstpFree(void *param);
char *SstpName();
bool SstpProcessData(void *param, TCP_RAW_DATA *in, FIFO *out);
SSTP_SERVER *NewSstpServer(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_ip,
UINT server_port, SOCK_EVENT *se,
char *client_host_name, char *crypt_name);
SSTP_SERVER *NewSstpServer(CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname);
void FreeSstpServer(SSTP_SERVER *s);
void SstpProcessInterrupt(SSTP_SERVER *s);
SSTP_PACKET *SstpParsePacket(UCHAR *data, UINT size);

View File

@ -5891,7 +5891,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
// Accept the SSTP connection
c->Type = CONNECTION_TYPE_OTHER;
sstp_ret = AcceptSstp(c);
sstp_ret = ProtoHandleConnection(server->Proto, s, "SSTP");
c->Err = ERR_DISCONNECTED;
FreeHttpHeader(h);