diff --git a/src/Cedar/Connection.c b/src/Cedar/Connection.c index d105ef12..be0262d6 100644 --- a/src/Cedar/Connection.c +++ b/src/Cedar/Connection.c @@ -2936,7 +2936,7 @@ void ConnectionAccept(CONNECTION *c) if (c->Cedar != NULL && c->Cedar->Server != NULL) { PROTO *proto = c->Cedar->Server->Proto; - if (proto && ProtoHandleConnection(proto, s) == true) + if (proto && ProtoHandleConnection(proto, s, NULL) == true) { c->Type = CONNECTION_TYPE_OTHER; goto FINAL; diff --git a/src/Cedar/Proto.c b/src/Cedar/Proto.c index 2c59f371..23ad8c0b 100644 --- a/src/Cedar/Proto.c +++ b/src/Cedar/Proto.c @@ -133,6 +133,8 @@ PROTO *ProtoNew(CEDAR *cedar) // OpenVPN ProtoImplAdd(proto, OvsGetProtoImpl()); + // SSTP + ProtoImplAdd(proto, SstpGetProtoImpl()); proto->UdpListener = NewUdpListener(ProtoHandleDatagrams, proto, &cedar->Server->ListenIP); @@ -187,7 +189,7 @@ PROTO_IMPL *ProtoImplDetect(PROTO *proto, const PROTO_MODE mode, const UCHAR *da for (i = 0; i < LIST_NUM(proto->Impls); ++i) { PROTO_IMPL *impl = LIST_DATA(proto->Impls, i); - if (impl->IsPacketForMe(mode, data, size) == false) + if (impl->IsPacketForMe == NULL || impl->IsPacketForMe(mode, data, size) == false) { continue; } @@ -220,7 +222,7 @@ PROTO_SESSION *ProtoNewSession(PROTO *proto, PROTO_IMPL *impl, const IP *src_ip, session->SockEvent = NewSockEvent(); session->InterruptManager = NewInterruptManager(); - if (impl->Init != NULL && impl->Init(&session->Param, proto->Cedar, session->InterruptManager, session->SockEvent) == false) + if (impl->Init != NULL && impl->Init(&session->Param, proto->Cedar, session->InterruptManager, session->SockEvent, NULL, NULL) == false) { Debug("ProtoNewSession(): failed to initialize %s\n", impl->Name()); @@ -309,10 +311,10 @@ bool ProtoSetUdpPorts(PROTO *proto, const LIST *ports) return true; } -bool ProtoHandleConnection(PROTO *proto, SOCK *sock) +bool ProtoHandleConnection(PROTO *proto, SOCK *sock, const char *protocol) { + const PROTO_IMPL *impl = NULL; void *impl_data = NULL; - const PROTO_IMPL *impl; UCHAR *buf; TCP_RAW_DATA *recv_raw_data; @@ -325,6 +327,20 @@ bool ProtoHandleConnection(PROTO *proto, SOCK *sock) return false; } + if (protocol != NULL) + { + UINT i; + for (i = 0; i < LIST_NUM(proto->Impls); ++i) + { + const PROTO_IMPL *tmp = LIST_DATA(proto->Impls, i); + if (StrCmp(tmp->Name(), protocol) == 0) + { + impl = tmp; + break; + } + } + } + else { UCHAR tmp[PROTO_CHECK_BUFFER_SIZE]; if (Peek(sock, tmp, sizeof(tmp)) == 0) @@ -333,16 +349,17 @@ bool ProtoHandleConnection(PROTO *proto, SOCK *sock) } impl = ProtoImplDetect(proto, PROTO_MODE_TCP, tmp, sizeof(tmp)); - if (impl == NULL) - { - return false; - } + } + + if (impl == NULL) + { + return false; } im = NewInterruptManager(); se = NewSockEvent(); - if (impl->Init != NULL && impl->Init(&impl_data, proto->Cedar, im, se) == false) + if (impl->Init != NULL && impl->Init(&impl_data, proto->Cedar, im, se, sock->CipherName, sock->RemoteHostname) == false) { Debug("ProtoHandleConnection(): failed to initialize %s\n", impl->Name()); FreeInterruptManager(im); @@ -368,7 +385,7 @@ bool ProtoHandleConnection(PROTO *proto, SOCK *sock) while (true) { - const UINT ret = Recv(sock, buf, PROTO_TCP_BUFFER_SIZE, false); + const UINT ret = Recv(sock, buf, PROTO_TCP_BUFFER_SIZE, sock->SecureMode); if (ret == SOCK_LATER) { @@ -396,7 +413,7 @@ bool ProtoHandleConnection(PROTO *proto, SOCK *sock) // Send data to the TCP socket while (FifoSize(send_fifo) >= 1) { - const UINT ret = Send(sock, FifoPtr(send_fifo), FifoSize(send_fifo), false); + const UINT ret = Send(sock, FifoPtr(send_fifo), FifoSize(send_fifo), sock->SecureMode); if (ret == SOCK_LATER) { @@ -416,8 +433,6 @@ bool ProtoHandleConnection(PROTO *proto, SOCK *sock) } } - impl->BufferLimit(impl_data, FifoSize(send_fifo) > MAX_BUFFERING_PACKET_SIZE); - if (stop) { // Error or disconnection occurs diff --git a/src/Cedar/Proto.h b/src/Cedar/Proto.h index b9b946f2..d932f5ea 100644 --- a/src/Cedar/Proto.h +++ b/src/Cedar/Proto.h @@ -24,13 +24,12 @@ typedef struct PROTO typedef struct PROTO_IMPL { - bool (*Init)(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se); + bool (*Init)(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname); void (*Free)(void *param); char *(*Name)(); bool (*IsPacketForMe)(const PROTO_MODE mode, const UCHAR *data, const UINT size); bool (*ProcessData)(void *param, TCP_RAW_DATA *in, FIFO *out); bool (*ProcessDatagrams)(void *param, LIST *in, LIST *out); - void (*BufferLimit)(void *param, const bool reached); } PROTO_IMPL; typedef struct PROTO_SESSION @@ -68,7 +67,7 @@ void ProtoDeleteSession(PROTO_SESSION *session); bool ProtoSetListenIP(PROTO *proto, const IP *ip); bool ProtoSetUdpPorts(PROTO *proto, const LIST *ports); -bool ProtoHandleConnection(PROTO *proto, SOCK *sock); +bool ProtoHandleConnection(PROTO *proto, SOCK *sock, const char *protocol); void ProtoHandleDatagrams(UDPLISTENER *listener, LIST *datagrams); void ProtoSessionThread(THREAD *thread, void *param); diff --git a/src/Cedar/Proto_OpenVPN.c b/src/Cedar/Proto_OpenVPN.c index fbce1940..cfcba6a5 100644 --- a/src/Cedar/Proto_OpenVPN.c +++ b/src/Cedar/Proto_OpenVPN.c @@ -23,20 +23,21 @@ PROTO_IMPL *OvsGetProtoImpl() OvsName, OvsIsPacketForMe, OvsProcessData, - OvsProcessDatagrams, - OvsBufferLimit, + OvsProcessDatagrams }; return &impl; } -bool OvsInit(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se) +bool OvsInit(void **param, 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("OvsInit(): cipher: %s, hostname: %s\n", cipher, hostname); + *param = NewOpenVpnServer(cedar, im, se); return true; @@ -185,6 +186,8 @@ bool OvsProcessData(void *param, TCP_RAW_DATA *in, FIFO *out) return false; } + server->SupressSendPacket = FifoSize(out) > MAX_BUFFERING_PACKET_SIZE; + return ret; } @@ -229,16 +232,6 @@ bool OvsProcessDatagrams(void *param, LIST *in, LIST *out) return true; } -void OvsBufferLimit(void *param, const bool reached) -{ - if (param == NULL) - { - return; - } - - ((OPENVPN_SERVER *)param)->SupressSendPacket = reached; -} - // Write the OpenVPN log void OvsLog(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, char *name, ...) { diff --git a/src/Cedar/Proto_OpenVPN.h b/src/Cedar/Proto_OpenVPN.h index 39324b84..20fa3e23 100644 --- a/src/Cedar/Proto_OpenVPN.h +++ b/src/Cedar/Proto_OpenVPN.h @@ -209,13 +209,12 @@ struct OPENVPN_SERVER //// Function prototype PROTO_IMPL *OvsGetProtoImpl(); -bool OvsInit(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se); +bool OvsInit(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname); void OvsFree(void *param); char *OvsName(); bool OvsIsPacketForMe(const PROTO_MODE mode, const UCHAR *data, const UINT size); bool OvsProcessData(void *param, TCP_RAW_DATA *in, FIFO *out); bool OvsProcessDatagrams(void *param, LIST *in, LIST *out); -void OvsBufferLimit(void *param, const bool reached); bool OvsIsOk(void *param); UINT OvsEstablishedSessions(void *param); diff --git a/src/Cedar/Proto_SSTP.c b/src/Cedar/Proto_SSTP.c index 63c2666c..001bab7d 100644 --- a/src/Cedar/Proto_SSTP.c +++ b/src/Cedar/Proto_SSTP.c @@ -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; -} - diff --git a/src/Cedar/Proto_SSTP.h b/src/Cedar/Proto_SSTP.h index 8ef8e424..ccec28c7 100644 --- a/src/Cedar/Proto_SSTP.h +++ b/src/Cedar/Proto_SSTP.h @@ -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); diff --git a/src/Cedar/Protocol.c b/src/Cedar/Protocol.c index 4dd2bd62..a34515cb 100644 --- a/src/Cedar/Protocol.c +++ b/src/Cedar/Protocol.c @@ -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);