From 9f19efb7af660859fbb6a134bc4c6a7e55b0071c Mon Sep 17 00:00:00 2001 From: Davide Beatrici Date: Fri, 26 Jul 2019 07:58:22 +0200 Subject: [PATCH] OpenVPN: use new protocol interface --- src/Cedar/Proto_OpenVPN.c | 532 ++++++++++++++++---------------------- src/Cedar/Proto_OpenVPN.h | 26 +- src/Mayaqua/Network.c | 8 + src/Mayaqua/Network.h | 2 + 4 files changed, 246 insertions(+), 322 deletions(-) diff --git a/src/Cedar/Proto_OpenVPN.c b/src/Cedar/Proto_OpenVPN.c index d23de964..cd4e0aa3 100644 --- a/src/Cedar/Proto_OpenVPN.c +++ b/src/Cedar/Proto_OpenVPN.c @@ -7,10 +7,6 @@ #include "CedarPch.h" - -static bool g_no_openvpn_tcp = false; -static bool g_no_openvpn_udp = false; - // Ping signature of the OpenVPN protocol static UCHAR ping_signature[] = { @@ -18,18 +14,213 @@ static UCHAR ping_signature[] = 0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48 }; -// Get the OpenVPN over TCP disabling flag -bool OvsGetNoOpenVpnTcp() +PROTO_IMPL *OvsGetProtoImpl() { - return g_no_openvpn_tcp; + static PROTO_IMPL impl = + { + OvsInit, + OvsFree, + OvsName, + OvsSupportedModes, + OvsIsPacketForMe, + OvsProcessData, + OvsBufferLimit, + OvsIsOk, + OvsEstablishedSessions + }; + + return &impl; } -// Get the OpenVPN over UDP disabling flag -bool OvsGetNoOpenVpnUdp() +bool OvsInit(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se) { - return g_no_openvpn_udp; + if (param == NULL || cedar == NULL || im == NULL || se == NULL) + { + return false; + } + + *param = NewOpenVpnServer(cedar, im, se); + + return true; } +void OvsFree(void *param) +{ + FreeOpenVpnServer(param); +} + +// Return the protocol name +char *OvsName() +{ + return "OpenVPN"; +} + +// Return the supported modes (TCP & UDP) +UINT OvsSupportedModes() +{ + return PROTO_MODE_TCP | PROTO_MODE_UDP; +} + +// Check whether it's an OpenVPN packet +bool OvsIsPacketForMe(const UCHAR *buf, const UINT size) +{ + if (buf == NULL || size != 2) + { + return false; + } + + if (buf[0] == 0x00 && buf[1] == 0x0E) + { + return true; + } + + return false; +} + +bool OvsProcessData(void *param, TCP_RAW_DATA *received_data, FIFO *data_to_send) +{ + bool ret = true; + UINT i; + OPENVPN_SERVER *server; + UCHAR buf[OPENVPN_TCP_MAX_PACKET_SIZE]; + + if (param == NULL || received_data == NULL || data_to_send == NULL) + { + return false; + } + + server = param; + + // Separate to a list of datagrams by interpreting the data received from the TCP socket + while (true) + { + UDPPACKET *packet; + UCHAR *packet_ptr; + UINT packet_size, total_packet_size; + FIFO *recv_fifo = received_data->Data; + const UINT data_size = FifoSize(recv_fifo); + + if (data_size < sizeof(USHORT)) + { + // Corrupt data + break; + } + + packet_size = READ_USHORT(FifoPtr(recv_fifo)); + + if (packet_size == 0 || packet_size > sizeof(buf)) + { + // Invalid packet size + ret = false; + break; + } + + total_packet_size = packet_size + sizeof(USHORT); + + if (data_size < total_packet_size) + { + // Corrupt data + break; + } + + if (ReadFifo(recv_fifo, buf, total_packet_size) != total_packet_size) + { + // Mismatch + ret = false; + break; + } + + // Read one packet and put it in the list + packet_ptr = buf + sizeof(USHORT); + + packet = NewUdpPacket(&received_data->SrcIP, received_data->SrcPort, &received_data->DstIP, received_data->DstPort, Clone(packet_ptr, packet_size), packet_size); + packet->Type = OPENVPN_PROTOCOL_TCP; + Add(server->RecvPacketList, packet); + } + + // Process the list of received datagrams + OvsRecvPacket(server, server->RecvPacketList); + + // Release the received packet list + for (i = 0; i < LIST_NUM(server->RecvPacketList); ++i) + { + UDPPACKET *p = LIST_DATA(server->RecvPacketList, i); + FreeUdpPacket(p); + } + + DeleteAll(server->RecvPacketList); + + // Store in the queue by getting a list of the datagrams to be transmitted from the OpenVPN server + for (i = 0; i < LIST_NUM(server->SendPacketList); ++i) + { + UDPPACKET *p = LIST_DATA(server->SendPacketList, i); + + // Store the size in the TCP send queue first + USHORT us = Endian16((USHORT)p->Size); + + WriteFifo(data_to_send, &us, sizeof(USHORT)); + + // Write the data body + WriteFifo(data_to_send, p->Data, p->Size); + + // Packet release + FreeUdpPacket(p); + } + + DeleteAll(server->SendPacketList); + + return ret; +} + +void OvsBufferLimit(void *param, const bool reached) +{ + if (param == NULL) + { + return; + } + + ((OPENVPN_SERVER *)param)->SupressSendPacket = reached; +} + +bool OvsIsOk(void *param) +{ + OPENVPN_SERVER *s; + + if (param == NULL) + { + return false; + } + + s = param; + + return (s->DisconnectCount < 1) && (s->SessionEstablishedCount > 0); +} + +UINT OvsEstablishedSessions(void *param) +{ + LIST *sessions; + UINT i; + UINT established_sessions = 0; + + if (param == NULL) + { + return 0; + } + + sessions = ((OPENVPN_SERVER *)param)->SessionList; + + for (i = 0;i < LIST_NUM(sessions);i++) + { + OPENVPN_SESSION *se = LIST_DATA(sessions, i); + + if (se->Established) + { + ++established_sessions; + } + } + + return established_sessions; +} // Write the OpenVPN log void OvsLog(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, char *name, ...) @@ -341,7 +532,7 @@ final: } // Process the received packet -void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol) +void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p) { OPENVPN_CHANNEL *c; OPENVPN_SESSION *se; @@ -353,7 +544,7 @@ void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol) } // Search for the session - se = OvsFindOrCreateSession(s, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, protocol); + se = OvsFindOrCreateSession(s, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, p->Type); if (se == NULL) { return; @@ -1989,7 +2180,7 @@ OPENVPN_SESSION *OvsSearchSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_ } // Receive packets in the OpenVPN server -void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list, UINT protocol) +void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list) { UINT i, j; LIST *delete_session_list = NULL; @@ -2021,7 +2212,7 @@ void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list, UINT protocol) { UDPPACKET *p = LIST_DATA(recv_packet_list, i); - OvsProceccRecvPacket(s, p, protocol); + OvsProceccRecvPacket(s, p); } // Treat for all sessions and all channels @@ -2702,24 +2893,17 @@ OPENVPN_SERVER *NewOpenVpnServer(CEDAR *cedar, INTERRUPT_MANAGER *interrupt, SOC s = ZeroMalloc(sizeof(OPENVPN_SERVER)); s->Cedar = cedar; - - AddRef(s->Cedar->ref); - s->Interrupt = interrupt; + s->SockEvent = sock_event; s->SessionList = NewList(OvsCompareSessionList); + s->RecvPacketList = NewListFast(NULL); s->SendPacketList = NewListFast(NULL); s->Now = Tick64(); s->NextSessionId = 1; - if (sock_event != NULL) - { - s->SockEvent = sock_event; - AddRef(s->SockEvent->ref); - } - OvsLog(s, NULL, NULL, "LO_START"); s->Dh = DhNewFromBits(DH_PARAM_BITS_DEFAULT); @@ -2739,33 +2923,33 @@ void FreeOpenVpnServer(OPENVPN_SERVER *s) OvsLog(s, NULL, NULL, "LO_STOP"); - // Release the session list - for (i = 0;i < LIST_NUM(s->SessionList);i++) + // Release the sessions list + for (i = 0; i < LIST_NUM(s->SessionList); ++i) { OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i); - OvsFreeSession(se); } ReleaseList(s->SessionList); - // Release the packet which is attempting to send - for (i = 0;i < LIST_NUM(s->SendPacketList);i++) + // Release the incoming packets list + for (i = 0; i < LIST_NUM(s->RecvPacketList); ++i) + { + UDPPACKET *p = LIST_DATA(s->RecvPacketList, i); + FreeUdpPacket(p); + } + + ReleaseList(s->RecvPacketList); + + // Release the outgoing packets list + for (i = 0; i < LIST_NUM(s->SendPacketList); ++i) { UDPPACKET *p = LIST_DATA(s->SendPacketList, i); - FreeUdpPacket(p); } ReleaseList(s->SendPacketList); - ReleaseCedar(s->Cedar); - - if (s->SockEvent != NULL) - { - ReleaseSockEvent(s->SockEvent); - } - DhFree(s->Dh); Free(s); @@ -2783,12 +2967,6 @@ void OpenVpnServerUdpListenerProc(UDPLISTENER *u, LIST *packet_list) us = (OPENVPN_SERVER_UDP *)u->Param; - if (OvsGetNoOpenVpnUdp()) - { - // OpenVPN over UDP is disabled - return; - } - if (us->OpenVpnServer != NULL) { { @@ -2797,7 +2975,7 @@ void OpenVpnServerUdpListenerProc(UDPLISTENER *u, LIST *packet_list) ClearStr(us->Cedar->OpenVPNPublicPorts, sizeof(us->Cedar->OpenVPNPublicPorts)); } - OvsRecvPacket(us->OpenVpnServer, packet_list, OPENVPN_PROTOCOL_UDP); + OvsRecvPacket(us->OpenVpnServer, packet_list); UdpListenerSendPackets(u, us->OpenVpnServer->SendPacketList); DeleteAll(us->OpenVpnServer->SendPacketList); @@ -2821,7 +2999,7 @@ OPENVPN_SERVER_UDP *NewOpenVpnServerUdp(CEDAR *cedar) AddRef(u->Cedar->ref); // Create a UDP listener - u->UdpListener = NewUdpListener(OpenVpnServerUdpListenerProc, u, &cedar->Server->ListenIP); + u->UdpListener = NewUdpListenerEx(OpenVpnServerUdpListenerProc, u, &cedar->Server->ListenIP, OPENVPN_PROTOCOL_UDP); // Create an OpenVPN server u->OpenVpnServer = NewOpenVpnServer(cedar, u->UdpListener->Interrupts, u->UdpListener->Event); @@ -2896,269 +3074,3 @@ void FreeOpenVpnServerUdp(OPENVPN_SERVER_UDP *u) Free(u); } - -// Check whether it's OpenSSL protocol by looking the first receive buffer of the TCP -bool OvsCheckTcpRecvBufIfOpenVPNProtocol(UCHAR *buf, UINT size) -{ - if (buf == NULL || size != 2) - { - return false; - } - - if (buf[0] == 0x00 && buf[1] == 0x0E) - { - return true; - } - - return false; -} - -// Run the OpenVPN server in TCP mode -bool OvsPerformTcpServer(CEDAR *cedar, SOCK *sock) -{ - OPENVPN_SERVER *s; - INTERRUPT_MANAGER *im; - SOCK_EVENT *se; - FIFO *tcp_recv_fifo; - FIFO *tcp_send_fifo; - UINT buf_size = (128 * 1024); - UCHAR *buf; - UINT64 giveup_time = Tick64() + (UINT64)OPENVPN_NEW_SESSION_DEADLINE_TIMEOUT; - LIST *ovs_recv_packet; - UINT i; - bool ret = false; - // Validate arguments - if (cedar == NULL || sock == NULL) - { - return false; - } - - // Initialize - buf = Malloc(buf_size); - im = NewInterruptManager(); - se = NewSockEvent(); - SetTimeout(sock, TIMEOUT_INFINITE); - JoinSockToSockEvent(sock, se); - - tcp_recv_fifo = NewFifoFast(); - tcp_send_fifo = NewFifoFast(); - - ovs_recv_packet = NewListFast(NULL); - - // Create an OpenVPN server - s = NewOpenVpnServer(cedar, im, se); - - // Main loop - Debug("Entering OpenVPN TCP Server Main Loop.\n"); - while (true) - { - UINT next_interval; - bool disconnected = false; - UINT64 now = Tick64(); - - // Receive data from a TCP socket - while (true) - { - UINT r = Recv(sock, buf, buf_size, false); - if (r == SOCK_LATER) - { - // Can not read any more - break; - } - else if (r == 0) - { - // Disconnected - disconnected = true; - break; - } - else - { - // Read - WriteFifo(tcp_recv_fifo, buf, r); - } - } - - // Separate to a list of datagrams by interpreting the data received from the TCP socket - while (true) - { - UINT r = FifoSize(tcp_recv_fifo); - if (r >= sizeof(USHORT)) - { - void *ptr = FifoPtr(tcp_recv_fifo); - USHORT packet_size = READ_USHORT(ptr); - if (packet_size != 0 && packet_size <= OPENVPN_TCP_MAX_PACKET_SIZE) - { - UINT total_len = (UINT)packet_size + sizeof(USHORT); - if (r >= total_len) - { - if (ReadFifo(tcp_recv_fifo, buf, total_len) != total_len) - { - // Mismatch - disconnected = true; - break; - } - else - { - // Read one packet - UINT payload_len = packet_size; - UCHAR *payload_ptr = buf + sizeof(USHORT); - - // Pass the packet to the OpenVPN server - Add(ovs_recv_packet, NewUdpPacket(&sock->RemoteIP, sock->RemotePort, - &sock->LocalIP, sock->LocalPort, - Clone(payload_ptr, payload_len), payload_len)); - } - } - else - { - // Non-arrival - break; - } - } - else - { - // Invalid packet size - disconnected = true; - break; - } - } - else - { - // Non-arrival - break; - } - } - - // Pass a list of received datagrams to the OpenVPN server - OvsRecvPacket(s, ovs_recv_packet, OPENVPN_PROTOCOL_TCP); - - // Release the received packet list - for (i = 0;i < LIST_NUM(ovs_recv_packet);i++) - { - UDPPACKET *p = LIST_DATA(ovs_recv_packet, i); - - FreeUdpPacket(p); - } - - DeleteAll(ovs_recv_packet); - - // Store in the queue by getting a list of the datagrams to be transmitted from the OpenVPN server - for (i = 0;i < LIST_NUM(s->SendPacketList);i++) - { - UDPPACKET *p = LIST_DATA(s->SendPacketList, i); - // Store the size to the TCP send queue first - USHORT us = (USHORT)p->Size; - //Debug(" *** TCP SEND %u\n", us); - us = Endian16(us); - WriteFifo(tcp_send_fifo, &us, sizeof(USHORT)); - - // Write the data body - WriteFifo(tcp_send_fifo, p->Data, p->Size); - - // Packet release - FreeUdpPacket(p); - } - DeleteAll(s->SendPacketList); - - // Send data to the TCP socket - while (FifoSize(tcp_send_fifo) >= 1) - { - UINT r = Send(sock, FifoPtr(tcp_send_fifo), FifoSize(tcp_send_fifo), false); - - if (r == SOCK_LATER) - { - // Can not write any more - break; - } - else if (r == 0) - { - // Disconnected - disconnected = true; - break; - } - else - { - // Wrote out - ReadFifo(tcp_send_fifo, NULL, r); - } - } - - if (FifoSize(tcp_send_fifo) > MAX_BUFFERING_PACKET_SIZE) - { - s->SupressSendPacket = true; - } - else - { - s->SupressSendPacket = false; - } - - if (s->DisconnectCount >= 1) - { - // Session disconnection has occurred on OpenVPN server-side - disconnected = true; - } - - if (giveup_time <= now) - { - UINT i; - UINT num_established_sessions = 0; - for (i = 0;i < LIST_NUM(s->SessionList);i++) - { - OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i); - - if (se->Established) - { - num_established_sessions++; - } - } - - if (num_established_sessions == 0) - { - // If the number of sessions is 0 even if wait a certain period of time after the start of server, abort - disconnected = true; - } - } - - if (disconnected) - { - // Error or disconnect occurs - Debug("Breaking OpenVPN TCP Server Main Loop.\n"); - break; - } - - // Wait until the next event occurs - next_interval = GetNextIntervalForInterrupt(im); - next_interval = MIN(next_interval, UDPLISTENER_WAIT_INTERVAL); - WaitSockEvent(se, next_interval); - } - - if (s != NULL && s->SessionEstablishedCount != 0) - { - ret = true; - } - - // Release the OpenVPN server - FreeOpenVpnServer(s); - - // Release object - FreeInterruptManager(im); - ReleaseSockEvent(se); - ReleaseFifo(tcp_recv_fifo); - ReleaseFifo(tcp_send_fifo); - Free(buf); - - // Release the received packet list - for (i = 0;i < LIST_NUM(ovs_recv_packet);i++) - { - UDPPACKET *p = LIST_DATA(ovs_recv_packet, i); - - FreeUdpPacket(p); - } - - ReleaseList(ovs_recv_packet); - - return ret; -} - - - diff --git a/src/Cedar/Proto_OpenVPN.h b/src/Cedar/Proto_OpenVPN.h index 210137ed..02ba2247 100644 --- a/src/Cedar/Proto_OpenVPN.h +++ b/src/Cedar/Proto_OpenVPN.h @@ -188,6 +188,7 @@ struct OPENVPN_SERVER { CEDAR *Cedar; INTERRUPT_MANAGER *Interrupt; // Interrupt manager + LIST *RecvPacketList; // Received packets list LIST *SendPacketList; // Transmission packet list LIST *SessionList; // Session list UINT64 Now; // Current time @@ -212,8 +213,18 @@ struct OPENVPN_SERVER_UDP // OpenVPN Default Client Option String #define OVPN_DEF_CLIENT_OPTION_STRING "dev-type tun,link-mtu 1500,tun-mtu 1500,cipher AES-128-CBC,auth SHA1,keysize 128,key-method 2,tls-client" - //// Function prototype +PROTO_IMPL *OvsGetProtoImpl(); +bool OvsInit(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se); +void OvsFree(void *param); +char *OvsName(); +UINT OvsSupportedModes(); +bool OvsIsPacketForMe(const UCHAR *buf, const UINT size); +bool OvsProcessData(void *param, TCP_RAW_DATA *received_data, FIFO *data_to_send); +void OvsBufferLimit(void *param, const bool reached); +bool OvsIsOk(void *param); +UINT OvsEstablishedSessions(void *param); + OPENVPN_SERVER_UDP *NewOpenVpnServerUdp(CEDAR *cedar); void FreeOpenVpnServerUdp(OPENVPN_SERVER_UDP *u); void OpenVpnServerUdpListenerProc(UDPLISTENER *u, LIST *packet_list); @@ -221,8 +232,8 @@ void OvsApplyUdpPortList(OPENVPN_SERVER_UDP *u, char *port_list, IP *listen_ip); OPENVPN_SERVER *NewOpenVpnServer(CEDAR *cedar, INTERRUPT_MANAGER *interrupt, SOCK_EVENT *sock_event); void FreeOpenVpnServer(OPENVPN_SERVER *s); -void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list, UINT protocol); -void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol); +void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list); +void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p); int OvsCompareSessionList(void *p1, void *p2); OPENVPN_SESSION *OvsSearchSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, UINT protocol); OPENVPN_SESSION *OvsNewSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, UINT protocol); @@ -264,16 +275,7 @@ UINT OvsCalcTcpMss(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c); CIPHER *OvsGetCipher(char *name); MD *OvsGetMd(char *name); -bool OvsCheckTcpRecvBufIfOpenVPNProtocol(UCHAR *buf, UINT size); - -bool OvsPerformTcpServer(CEDAR *cedar, SOCK *sock); - -void OvsSetReplyForVgsPollEnable(bool b); - -bool OvsGetNoOpenVpnTcp(); void OpenVpnServerUdpSetDhParam(OPENVPN_SERVER_UDP *u, DH_CTX *dh); - - #endif // PROTO_OPENVPN_H diff --git a/src/Mayaqua/Network.c b/src/Mayaqua/Network.c index eee6d40c..d9567c8f 100644 --- a/src/Mayaqua/Network.c +++ b/src/Mayaqua/Network.c @@ -18792,6 +18792,8 @@ LABEL_FATAL_ERROR: p->SrcPort = p->DestPort = MAKE_SPECIAL_PORT(50); } + p->Type = u->PacketType; + Add(recv_list, p); } @@ -19084,6 +19086,11 @@ void UdpListenerSendPackets(UDPLISTENER *u, LIST *packet_list) // Creating a UDP listener UDPLISTENER *NewUdpListener(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *listen_ip) +{ + return NewUdpListenerEx(recv_proc, param, listen_ip, INFINITE); +} + +UDPLISTENER *NewUdpListenerEx(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *listen_ip, UINT packet_type) { UDPLISTENER *u; // Validate arguments @@ -19095,6 +19102,7 @@ UDPLISTENER *NewUdpListener(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *l u = ZeroMalloc(sizeof(UDPLISTENER)); u->Param = param; + u->PacketType = packet_type; u->PortList = NewList(NULL); u->Event = NewSockEvent(); diff --git a/src/Mayaqua/Network.h b/src/Mayaqua/Network.h index b44c8f69..54fe2c76 100644 --- a/src/Mayaqua/Network.h +++ b/src/Mayaqua/Network.h @@ -506,6 +506,7 @@ struct UDPLISTENER UINT64 LastCheckTick; // Time which the socket list was checked last UDPLISTENER_RECV_PROC *RecvProc; // Receive procedure LIST *SendPacketList; // Transmission packet list + UINT PacketType; // The type to set when creating an UDPPACKET void *Param; // Parameters INTERRUPT_MANAGER *Interrupts; // Interrupt manager bool HostIPAddressListChanged; // IP address list of the host has changed @@ -1415,6 +1416,7 @@ int CmpIpAddressList(void *p1, void *p2); UINT64 GetHostIPAddressListHash(); UDPLISTENER *NewUdpListener(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *listen_ip); +UDPLISTENER *NewUdpListenerEx(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *listen_ip, UINT packet_type); void UdpListenerThread(THREAD *thread, void *param); void FreeUdpListener(UDPLISTENER *u); void AddPortToUdpListener(UDPLISTENER *u, UINT port);