diff --git a/src/Cedar/CedarType.h b/src/Cedar/CedarType.h index 7ae8fd9f..4e4de076 100644 --- a/src/Cedar/CedarType.h +++ b/src/Cedar/CedarType.h @@ -603,6 +603,9 @@ typedef struct PPP_OPTION PPP_OPTION; typedef struct PPP_LCP PPP_LCP; typedef struct PPP_PACKET PPP_PACKET; typedef struct PPP_IPOPTION PPP_IPOPTION; +typedef struct PPP_IPV6OPTION PPP_IPV6OPTION; +typedef struct PPP_REQUEST_RESEND PPP_REQUEST_RESEND; +typedef struct PPP_DELAYED_PACKET PPP_DELAYED_PACKET; // ============================================================== diff --git a/src/Cedar/IPsec_PPP.c b/src/Cedar/IPsec_PPP.c index d2b7cb18..8f8e1233 100644 --- a/src/Cedar/IPsec_PPP.c +++ b/src/Cedar/IPsec_PPP.c @@ -101,11 +101,6 @@ // Thank you for your cooperation. // // -// NO MEMORY OR RESOURCE LEAKS -// --------------------------- -// -// The memory-leaks and resource-leaks verification under the stress -// test has been passed before release this source code. // IPsec_PPP.c @@ -113,7 +108,7 @@ #include "CedarPch.h" -// PPP thread +// PPP main thread void PPPThread(THREAD *thread, void *param) { PPP_SESSION *p = (PPP_SESSION *)param; @@ -124,7 +119,9 @@ void PPPThread(THREAD *thread, void *param) USHORT next_protocol = 0; bool ret = false; char ipstr1[128], ipstr2[128]; - bool established = false; + bool authReqSent = false; + UINT64 now = Tick64(); + // Validate arguments if (thread == NULL || param == NULL) { @@ -132,315 +129,257 @@ void PPPThread(THREAD *thread, void *param) } // Initialize + + Debug("PPP Initialize"); + + PPPSetStatus(p, PPP_STATUS_CONNECTED); + p->IPv4_State = PPP_PROTO_STATUS_CLOSED; + p->IPv6_State = PPP_PROTO_STATUS_CLOSED; + p->Mru1 = p->Mru2 = PPP_MRU_DEFAULT; p->RecvPacketList = NewList(NULL); + p->SentReqPacketList = NewList(NULL); + p->DelayedPackets = NewList(PPPDelayedPacketsComparator); p->MsChapV2_UseDoubleMsChapV2 = CedarIsThereAnyEapEnabledRadiusConfig(p->Cedar); + Debug("MsChapV2_UseDoubleMsChapV2 = 0x%x\n", p->MsChapV2_UseDoubleMsChapV2); + //// Link establishment phase + + Debug("PPP Link establishment phase\n"); + IPToStr(ipstr1, sizeof(ipstr1), &p->ClientIP); IPToStr(ipstr2, sizeof(ipstr2), &p->ServerIP); PPPLog(p, "LP_CONNECTED", p->Postfix, ipstr1, p->ClientHostname, p->ClientPort, ipstr2, p->ServerPort, p->ClientSoftwareName, p->AdjustMss); - // Request the use of PAP - c = NewPPPLCP(PPP_LCP_CODE_REQ, 0); - us = Endian16(PPP_LCP_AUTH_PAP); - Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &us, sizeof(USHORT))); - ret = PPPSendRequest(p, PPP_PROTOCOL_LCP, c); - FreePPPLCP(c); - if (ret == false) + // We need that so we don't time out on connection immediately + p->LastRecvTime = Tick64(); + + Debug("PPP starting main dataloop\n"); + + // Dataloop active if the receiving tube is still connected + while (true) { - if (IsTubeConnected(p->TubeRecv)) + PPP_LCP *lcp; + bool receivedPacketProcessed = false; + TUBE *tubes[2]; + UINT r; + + PPPGetNextPacket(p); + + if (p->CurrentPacket != NULL) { - // PAP protocol is denied + // First we process any possible unsupported packets + receivedPacketProcessed = PPPRejectUnsupportedPacket(p, p->CurrentPacket); + + // Now do some basic processing + if (!receivedPacketProcessed && p->CurrentPacket->IsControl && p->CurrentPacket->Protocol == PPP_PROTOCOL_LCP) + { + if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_ECHO_REQUEST && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus)) + { + // Immediately return the echo response to the echo request + PPP_PACKET *pp2 = ZeroMalloc(sizeof(PPP_PACKET)); + + pp2->IsControl = true; + pp2->Protocol = PPP_PROTOCOL_LCP; + pp2->Lcp = NewPPPLCP(PPP_LCP_CODE_ECHO_RESPONSE, p->CurrentPacket->Lcp->Id); + pp2->Lcp->Data = Clone(p->CurrentPacket->Lcp->Data, p->CurrentPacket->Lcp->DataSize); + pp2->Lcp->DataSize = p->CurrentPacket->Lcp->DataSize; + + if (PPPSendPacketAndFree(p, pp2) == false) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + } + + receivedPacketProcessed = true; + } + else if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_ECHO_RESPONSE && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus)) + { + receivedPacketProcessed = true; + // Ignore the Echo response packet + } + else if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_DROP && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus)) + { + receivedPacketProcessed = true; + // Ignore the Drop packet + } + else if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_IDENTIFICATION && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus)) + { + receivedPacketProcessed = true; + // Ignore the Identification packet + WHERE; + } + else if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_TERMINATE_REQ) + { + PPP_PACKET *pp2 = ZeroMalloc(sizeof(PPP_PACKET));; + receivedPacketProcessed = true; + // Return the Terminate ACK If a Terminate Request has been received + + pp2->IsControl = true; + pp2->Protocol = PPP_PROTOCOL_LCP; + pp2->Lcp = NewPPPLCP(PPP_LCP_CODE_TERMINATE_ACK, p->CurrentPacket->Lcp->Id); + pp2->Lcp->Data = Clone(p->CurrentPacket->Lcp->Data, p->CurrentPacket->Lcp->DataSize); + pp2->Lcp->DataSize = p->CurrentPacket->Lcp->DataSize; + + p->IsTerminateReceived = true; + + if (PPPSendPacketAndFree(p, pp2) == false) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + } + else + { + SleepThread(100); + PPPSetStatus(p, PPP_STATUS_CLOSED); + } + } + else if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_TERMINATE_ACK) + { + PPPSetStatus(p, PPP_STATUS_CLOSED); + } + } + + // Process responses + if (!receivedPacketProcessed && p->CurrentPacket != NULL && p->CurrentPacket->IsControl && PPP_CODE_IS_RESPONSE(p->CurrentPacket->Protocol, p->CurrentPacket->Lcp->Code) && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus)) + { + PPP_PACKET* request = NULL; + // Removing from resend list + for (i = 0; i < LIST_NUM(p->SentReqPacketList); i++) + { + PPP_REQUEST_RESEND *t = LIST_DATA(p->SentReqPacketList, i); + + if (t->Id == p->CurrentPacket->Lcp->Id) + { + request = t->Packet; + Delete(p->SentReqPacketList, t); + Free(t); + break; + } + } + PPPProcessResponsePacket(p, p->CurrentPacket, request); + FreePPPPacket(request); + receivedPacketProcessed = true; + } + + // Process requests + if (!receivedPacketProcessed && p->CurrentPacket != NULL && p->CurrentPacket->IsControl && PPP_CODE_IS_REQUEST(p->CurrentPacket->Protocol, p->CurrentPacket->Lcp->Code) && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus)) + { + PPPProcessRequestPacket(p, p->CurrentPacket); + receivedPacketProcessed = true; + } + + // Process data packets, discarded before we got any links up + if (!receivedPacketProcessed && p->CurrentPacket != NULL && !p->CurrentPacket->IsControl && p->PPPStatus == PPP_STATUS_NETWORK_LAYER && p->Ipc != NULL) + { + UINT64 timeBeforeLoop = Tick64(); + while (true) + { + UINT64 nowL; + // Here client to server + if (p->CurrentPacket->Protocol == PPP_PROTOCOL_IP && p->IPv4_State == PPP_PROTO_STATUS_OPENED) + { + receivedPacketProcessed = true; + IPCSendIPv4(p->Ipc, p->CurrentPacket->Data, p->CurrentPacket->DataSize); + } + else if (p->CurrentPacket->Protocol == PPP_PROTOCOL_IP) + { + Debug("Got IPv4 packet before IPv4 ready!\n"); + } + else if (p->CurrentPacket->Protocol == PPP_PROTOCOL_IPV6 && p->IPv6_State == PPP_PROTO_STATUS_OPENED) + { + receivedPacketProcessed = true; + Debug("IPv6 to be implemented\n"); + } + else if (p->CurrentPacket->Protocol == PPP_PROTOCOL_IPV6) + { + Debug("Got IPv6 packet before IPv6 ready!\n"); + } + + // Let's break out of the loop once in a while so we don't get stuck here endlessly + nowL = Tick64(); + if (nowL > timeBeforeLoop + PPP_PACKET_RESEND_INTERVAL) + { + break; + } + + PPPGetNextPacket(p); + if (p->CurrentPacket == NULL) + { + break; + } + // Making sure we got a correctly parsed packet by rejecting all invalid ones + if (PPPRejectUnsupportedPacket(p, p->CurrentPacket)) + { + break; + } + if (p->CurrentPacket->IsControl || p->PPPStatus != PPP_STATUS_NETWORK_LAYER || p->Ipc == NULL) + { + PPPAddNextPacket(p, p->CurrentPacket, 0); + p->CurrentPacket = NULL; + break; + } + } + } + + if (!receivedPacketProcessed && p->CurrentPacket != NULL) + { + Debug("Unprocessed and unrejected packet, protocol = 0x%x\n", p->CurrentPacket->Protocol); + } + } + else if (p->PPPStatus == PPP_STATUS_BEFORE_AUTH && p->AuthProtocol == PPP_PROTOCOL_CHAP) + { + // We got to start CHAP when we got no LCP packets from the client on previous iteration + // which means we parsed all the client requests and responses + Debug("Starting PPP Authentication phase MS-CHAP v2\n"); + + lcp = BuildMSCHAP2ChallengePacket(p); + if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_CHAP, lcp)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + } + + PPPSetStatus(p, PPP_STATUS_AUTHENTICATING); + } + + if (p->PPPStatus == PPP_STATUS_CONNECTED && !authReqSent) + { + // MSCHAPv2 code + PPP_LCP *c = NewPPPLCP(PPP_LCP_CODE_REQ, 0); + UCHAR ms_chap_v2_code[3]; + WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP); + ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2; + + Debug("Request MSCHAPv2\n"); + Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, ms_chap_v2_code, sizeof(ms_chap_v2_code))); + if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, c)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + } + authReqSent = true; + } + + if (p->PPPStatus == PPP_STATUS_AUTHENTICATING) + { + Debug("Tick waiting for auth...\n"); + } + + if (p->PPPStatus == PPP_STATUS_AUTH_FAIL) + { + Debug("PPP auth failed, giving up\n"); p->DisconnectCauseCode = 15; p->DisconnectCauseDirection = 1; - Debug("PPP: PAP Rejected.\n"); - - if (p->EnableMSCHAPv2) - { - // Try to request the use of MS-CHAPv2 - UCHAR ms_chap_v2_code[3]; - WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP); - ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2; - - c = NewPPPLCP(PPP_LCP_CODE_REQ, 0); - Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, ms_chap_v2_code, sizeof(ms_chap_v2_code))); - ret = PPPSendRequest(p, PPP_PROTOCOL_LCP, c); - FreePPPLCP(c); - - if (ret == false) - { - if (IsTubeConnected(p->TubeRecv)) - { - // MS-CHAPv2 protocol was also rejected - p->DisconnectCauseCode = 15; - p->DisconnectCauseDirection = 1; - Debug("PPP: MS-CHAPv2 Rejected.\n"); - PPPLog(p, "LP_PAP_MSCHAPV2_REJECTED"); - } - } - else - { - // It is to be used for the MS-CHAPv2 - p->AuthProtocol = PPP_PROTOCOL_CHAP; - } - } - else - { - PPPLog(p, "LP_PAP_REJECTED"); - } + PPPSetStatus(p, PPP_STATUS_CLOSING); } - if (ret == false) + if (p->PPPStatus == PPP_STATUS_NETWORK_LAYER) { - goto LABEL_CLEANUP; - } - } - - //// Authentication phase - - if (p->AuthProtocol == PPP_PROTOCOL_PAP) - { - // PAP - next_protocol = PPPContinueCurrentProtocolRequestListening(p, PPP_PROTOCOL_LCP); - if (next_protocol == 0) - { - goto LABEL_CLEANUP; - } - - Debug("next_protocol = 0x%x\n", next_protocol); - - if (next_protocol != PPP_PROTOCOL_PAP) - { - Debug("next_protocol is not PAP !!\n"); - PPPLog(p, "LP_NEXT_PROTOCOL_IS_NOT_PAP", next_protocol); - goto LABEL_CLEANUP; - } - - next_protocol = PPPContinueCurrentProtocolRequestListening(p, PPP_PROTOCOL_PAP); - if (next_protocol == 0 || p->AuthOk == false) - { - if (IsTubeConnected(p->TubeRecv)) - { - // PAP authentication failed - p->DisconnectCauseCode = 15; - p->DisconnectCauseDirection = 1; - Debug("PPP: PAP Failed.\n"); - PPPLog(p, "LP_PAP_FAILED"); - } - goto LABEL_CLEANUP; - } - } - else - { - // MS-CHAP v2 - PPP_PACKET *pp, *pp_ret; - BUF *b; - char machine_name[MAX_SIZE]; - UINT64 start_tick = Tick64(); - UINT64 timeout_tick = start_tick + (UINT64)PPP_PACKET_RECV_TIMEOUT; - UINT64 next_send_tick = 0; - USHORT pp_ret_protocol; - - PPPContinueUntilFinishAllLCPOptionRequestsDetermined(p); - - if (p->MsChapV2_UseDoubleMsChapV2) - { - // Use the double-MSCHAPv2 technieue - GetMachineHostName(machine_name, sizeof(machine_name)); - MsChapV2Server_GenerateChallenge(p->MsChapV2_ServerChallenge); - - pp = ZeroMalloc(sizeof(PPP_PACKET)); - pp->Protocol = PPP_PROTOCOL_CHAP; - pp->IsControl = true; - pp->Lcp = NewPPPLCP(PPP_CHAP_CODE_CHALLENGE, 99); - - b = NewBuf(); - WriteBufChar(b, 16); - WriteBuf(b, p->MsChapV2_ServerChallenge, sizeof(p->MsChapV2_ServerChallenge)); - WriteBuf(b, machine_name, StrLen(machine_name)); - pp->Lcp->Data = Clone(b->Buf, b->Size); - pp->Lcp->DataSize = b->Size; - FreeBuf(b); - - PPPSendPacket(p, pp); - - pp_ret = PPPRecvResponsePacket(p, pp, 0, &pp_ret_protocol, false, true); - - if (pp_ret != NULL) - { - // Extract the username from the first MS-CHAP v2 packet - if (pp_ret->Lcp != NULL && pp_ret->Lcp->DataSize >= 51) - { - BUF *b; - - b = MemToBuf(pp_ret->Lcp->Data, pp_ret->Lcp->DataSize); - - if (ReadBufChar(b) == 49) - { - UCHAR client_response_buffer[49]; - char username_tmp[MAX_SIZE]; - char id[MAX_SIZE]; - char hub[MAX_SIZE]; - char client_ip_tmp[256]; - EAP_CLIENT *eap; - ETHERIP_ID d; - - ReadBuf(b, client_response_buffer, 49); - - Zero(username_tmp, sizeof(username_tmp)); - ReadBuf(b, username_tmp, sizeof(username_tmp) - 1); - - Debug("First MS-CHAPv2: id=%s\n", username_tmp); - - Zero(id, sizeof(id)); - Zero(hub, sizeof(hub)); - - // The user name is divided into the ID and the virtual HUB name - Zero(&d, sizeof(d)); - PPPParseUsername(p->Cedar, username_tmp, &d); - - StrCpy(id, sizeof(id), d.UserName); - StrCpy(hub, sizeof(hub), d.HubName); - Debug("First MS-CHAPv2: username=%s, hubname=%s\n", id, hub); - - IPToStr(client_ip_tmp, sizeof(client_ip_tmp), &p->ClientIP); - - eap = HubNewEapClient(p->Cedar, hub, client_ip_tmp, id); - - if (eap) - { - p->EapClient = eap; - } - } - - FreeBuf(b); - } - - FreePPPPacket(pp_ret); - } - - FreePPPPacket(pp); - } - - // Generate a Server Challenge packet of MS-CHAP v2 - GetMachineHostName(machine_name, sizeof(machine_name)); - - if (p->EapClient == NULL) - { - MsChapV2Server_GenerateChallenge(p->MsChapV2_ServerChallenge); - } - else - { - Copy(p->MsChapV2_ServerChallenge, p->EapClient->MsChapV2Challenge.Chap_ChallengeValue, 16); - } - - pp = ZeroMalloc(sizeof(PPP_PACKET)); - pp->Protocol = PPP_PROTOCOL_CHAP; - pp->IsControl = true; - pp->Lcp = NewPPPLCP(PPP_CHAP_CODE_CHALLENGE, 0); - - b = NewBuf(); - WriteBufChar(b, 16); - WriteBuf(b, p->MsChapV2_ServerChallenge, sizeof(p->MsChapV2_ServerChallenge)); - WriteBuf(b, machine_name, StrLen(machine_name)); - pp->Lcp->Data = Clone(b->Buf, b->Size); - pp->Lcp->DataSize = b->Size; - FreeBuf(b); - - PPPSendPacket(p, pp); - - pp_ret_protocol = 0; - pp_ret = PPPRecvResponsePacket(p, pp, 0, &pp_ret_protocol, false, false); - - if (pp_ret != NULL) - { - FreePPPPacket(pp_ret); - } - - FreePPPPacket(pp); - - if (pp_ret_protocol == 0 || p->AuthOk == false) - { - if (IsTubeConnected(p->TubeRecv)) - { - // MS-CHAPv2 authentication failed - p->DisconnectCauseCode = 15; - p->DisconnectCauseDirection = 1; - Debug("PPP: MS-CHAPv2 Failed.\n"); - PPPLog(p, "LP_MSCHAPV2_FAILED"); - } - goto LABEL_CLEANUP; - } - - next_protocol = pp_ret_protocol; - } - - Debug("next_protocol = 0x%x\n", next_protocol); - - if (next_protocol != PPP_PROTOCOL_IPCP) - { - // Receive the protocol of non-IPCP - Debug("Not IPCP Protocol.\n"); - PPPLog(p, "LP_NEXT_PROTOCOL_IS_NOT_IPCP", next_protocol); - goto LABEL_CLEANUP; - } - - // Notify the IP address of the PPP server - c = NewPPPLCP(PPP_LCP_CODE_REQ, 0); - ui = Endian32(0x01000001); // 1.0.0.1 - Add(c->OptionList, NewPPPOption(PPP_IPCP_OPTION_IP, &ui, sizeof(UINT))); - ret = PPPSendRequest(p, PPP_PROTOCOL_IPCP, c); - FreePPPLCP(c); - if (ret == false) - { - goto LABEL_CLEANUP; - } - - next_protocol = PPPContinueCurrentProtocolRequestListening(p, PPP_PROTOCOL_IPCP); - Debug("next_protocol = 0x%x\n", next_protocol); - - if (p->Ipc == NULL || IsZeroIP(&p->Ipc->ClientIPAddress)) - { - // IP address is undetermined - PPPLog(p, "LP_IP_ADDRESS_NOT_DETERMIND"); - goto LABEL_CLEANUP; - } - - if (next_protocol == PPP_PROTOCOL_IP) - { - established = true; - - // Do the IP communication - while (true) - { - TUBE *tubes[2]; - UINT64 now = Tick64(); - UINT r; - - // Flush the ARP table of the IPC - IPCFlushArpTable(p->Ipc); - - // Packet of client to server direction - while (true) - { - PPP_PACKET *pp = PPPRecvPacketForCommunication(p); - if (pp == NULL) - { - break; - } - - if (pp->Protocol == PPP_PROTOCOL_IP) - { - // Since I want to send the IP packet, pass it to the IPC - IPCSendIPv4(p->Ipc, pp->Data, pp->DataSize); - } - - FreePPPPacket(pp); - } - + UINT64 timeBeforeLoop; if (p->DhcpAllocated) { if (now >= p->DhcpNextRenewTime) @@ -456,12 +395,13 @@ void PPPThread(THREAD *thread, void *param) } } - // Happy procedure IPCProcessL3Events(p->Ipc); + + timeBeforeLoop = Tick64(); - // Packet of server to client direction while (true) { + UINT64 nowL; BLOCK *b = IPCRecvIPv4(p->Ipc); PPP_PACKET *pp; PPP_PACKET tmp; @@ -482,622 +422,915 @@ void PPPThread(THREAD *thread, void *param) FreePPPPacketEx(pp, true); Free(b); + + // Let's break out of the loop once in a while so we don't get stuck here endlessly + nowL = Tick64(); + if (nowL > timeBeforeLoop + PPP_PACKET_RESEND_INTERVAL) + { + break; + } } FlushTubeFlushList(p->FlushList); - - // PPP Echo Request - if (p->NextEchoSendTime == 0 || now >= p->NextEchoSendTime) - { - p->NextEchoSendTime = now + (UINT64)PPP_ECHO_SEND_INTERVAL; - AddInterrupt(p->Ipc->Interrupt, p->NextEchoSendTime); - - PPPSendEchoRequest(p); - } - - // Terminate if any tube is disconnected - if (IsTubeConnected(p->TubeRecv) == false || IsTubeConnected(p->TubeSend) == false) - { - // Higher-level protocol is disconnected - PPPLog(p, "LP_UPPER_PROTOCOL_DISCONNECTED", p->Postfix); - break; - } - if (IsIPCConnected(p->Ipc) == false) - { - // IPC VPN session is disconnected - PPPLog(p, "LP_VPN_SESSION_TERMINATED"); - break; - } - - // Time-out inspection - if ((p->LastRecvTime + (UINT64)PPP_DATA_TIMEOUT) <= now) - { - // Communication time-out occurs - PPPLog(p, "LP_DATA_TIMEOUT"); - break; - } - - // Terminate if the PPP disconnected - if (p->IsTerminateReceived) - { - PPPLog(p, "LP_NORMAL_TERMINATE"); - break; - } - - // Wait until the next packet arrives - tubes[0] = p->TubeRecv; - tubes[1] = p->Ipc->Sock->RecvTube; - - r = GetNextIntervalForInterrupt(p->Ipc->Interrupt); - WaitForTubes(tubes, 2, MIN(r, 1234)); } - // Disconnected normally - PPPLog(p, "LP_DISCONNECTED"); - } - - if (p->DhcpAllocated) - { - // If any address is assigned from the DHCP, release it - IP ip; - char tmp[MAX_SIZE]; - - UINTToIP(&ip, p->ClientAddressOption.ServerAddress); - - IPToStr(tmp, sizeof(tmp), &ip); - Debug("Releasing IP Address from DHCP Server %s...\n", tmp); - - IPCDhcpFreeIP(p->Ipc, &ip); - IPCProcessL3Events(p->Ipc); - - SleepThread(300); - } - -LABEL_CLEANUP: - - if (established == false) - { - // Disconnected Abnormally - PPPLog(p, "LP_DISCONNECTED_ABNORMAL"); - } - - // Disconnection process - PPPCleanTerminate(p); - - // Release the memory - for (i = 0;i < LIST_NUM(p->RecvPacketList);i++) - { - PPP_PACKET *pp = LIST_DATA(p->RecvPacketList, i); - - FreePPPPacket(pp); - } - ReleaseList(p->RecvPacketList); - - // Release the PPP session - FreePPPSession(p); -} - -// Disconnect the PPP cleanly -void PPPCleanTerminate(PPP_SESSION *p) -{ - PPP_PACKET *pp; - PPP_PACKET *res; - UINT64 giveup_tick = Tick64() + (UINT64)PPP_TERMINATE_TIMEOUT; - // Validate arguments - if (p == NULL) - { - return; - } - - // Send a Terminate Request - pp = ZeroMalloc(sizeof(PPP_PACKET)); - pp->IsControl = true; - pp->Protocol = PPP_PROTOCOL_LCP; - pp->Lcp = NewPPPLCP(PPP_LCP_CODE_TERMINATE_REQ, p->NextId++); - Debug("PPP: Terminate Request is Sent.\n"); - if (PPPSendPacket(p, pp) == false) - { - goto LABEL_CLEANUP; - } - - // Wait for Terminate ACK - while (true) - { - UINT64 now = Tick64(); - UINT interval; - - if (now >= giveup_tick) + if (p->PPPStatus == PPP_STATUS_AUTH_SUCCESS) { + Debug("PPP auth success, ready for network layer on next tick\n"); + p->AuthOk = true; + PPPSetStatus(p, PPP_STATUS_NETWORK_LAYER); + } + + if ((p->PPPStatus == PPP_STATUS_CLOSING || p->PPPStatus == PPP_STATUS_FAIL) && IsTubeConnected(p->TubeRecv) && IsTubeConnected(p->TubeSend)) + { + Debug("Trying to cleanly close the connection, status = 0x%x\n", p->PPPStatus); + PPPSetStatus(p, PPP_STATUS_CLOSING_WAIT); + lcp = NewPPPLCP(PPP_LCP_CODE_TERMINATE_REQ, 0); + if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, lcp)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + } + } + + if (!PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus) || p->PPPStatus == PPP_STATUS_CLOSING_WAIT) + { + PPPProcessRetransmissions(p); + PPPSendEchoRequest(p); + } + + tubes[0] = p->TubeRecv; + + if (p->PPPStatus == PPP_STATUS_NETWORK_LAYER && p->Ipc != NULL && IsIPCConnected(p->Ipc)) + { + r = GetNextIntervalForInterrupt(p->Ipc->Interrupt); + tubes[1] = p->Ipc->Sock->RecvTube; + WaitForTubes(tubes, 2, MIN(r, PPP_PACKET_RESEND_INTERVAL)); + } + else + { + WaitForTubes(tubes, 1, 100); + } + + if (IsTubeConnected(p->TubeRecv) == false || IsTubeConnected(p->TubeSend) == false) + { + // Higher-level protocol is disconnected + PPPLog(p, "LP_UPPER_PROTOCOL_DISCONNECTED", p->Postfix); break; } - while (true) + if (IsIPCConnected(p->Ipc) == false && p->PPPStatus == PPP_STATUS_NETWORK_LAYER) { - if (IsTubeConnected(p->TubeRecv) == false) - { - break; - } - - res = PPPRecvPacket(p, true); - - if (res == NULL) - { - break; - } - - if (res->IsControl && res->Protocol == PPP_PROTOCOL_LCP && res->Lcp->Code == PPP_LCP_CODE_TERMINATE_ACK) - { - Debug("PPP: Terminate ACK is Received.\n"); - FreePPPPacket(res); - goto LABEL_CLEANUP; - } - - FreePPPPacket(res); + // IPC VPN session is disconnected + PPPLog(p, "LP_VPN_SESSION_TERMINATED"); + break; } - interval = (UINT)(giveup_tick - now); - - Wait(p->TubeRecv->Event, interval); - } - -LABEL_CLEANUP: - FreePPPPacket(pp); -} - -// Wait until all pending LCP option are determined -bool PPPContinueUntilFinishAllLCPOptionRequestsDetermined(PPP_SESSION *p) -{ - USHORT received_protocol = 0; - // Validate arguments - if (p == NULL) - { - return false; - } - - PPPRecvResponsePacket(p, NULL, PPP_PROTOCOL_LCP, &received_protocol, true, false); - - return p->ClientLCPOptionDetermined; -} - -// Continue the processing of the request packet protocol on the current PPP -USHORT PPPContinueCurrentProtocolRequestListening(PPP_SESSION *p, USHORT protocol) -{ - USHORT received_protocol = 0; - // Validate arguments - if (p == NULL) - { - return 0; - } - - PPPRecvResponsePacket(p, NULL, protocol, &received_protocol, false, false); - - return received_protocol; -} - -// Send the PPP Echo Request -void PPPSendEchoRequest(PPP_SESSION *p) -{ - PPP_PACKET *pp; - char echo_data[]= "\0\0\0\0Aho Baka Manuke"; - // Validate arguments - if (p == NULL) - { - return; - } - - pp = ZeroMalloc(sizeof(PPP_PACKET)); - pp->Protocol = PPP_PROTOCOL_LCP; - pp->IsControl = true; - pp->Lcp = NewPPPLCP(PPP_LCP_CODE_ECHO_REQUEST, p->NextId++); - - pp->Lcp->Data = Clone(echo_data, sizeof(echo_data)); - pp->Lcp->DataSize = sizeof(echo_data); - - PPPSendPacket(p, pp); - - FreePPPPacket(pp); -} - -// Send a request packet in the PPP -bool PPPSendRequest(PPP_SESSION *p, USHORT protocol, PPP_LCP *c) -{ - PPP_PACKET *pp; - PPP_PACKET *pp2; - bool ret = false; - // Validate arguments - if (p == NULL || c == NULL) - { - return false; - } - - pp = ZeroMalloc(sizeof(PPP_PACKET)); - pp->Protocol = protocol; - pp->IsControl = true; - pp->Lcp = c; - pp->Lcp->Id = p->NextId++; - - // Send the PPP packet - if (PPPSendPacket(p, pp) == false) - { - goto LABEL_ERROR; - } - - // Receive a corresponding PPP packet - pp2 = PPPRecvResponsePacket(p, pp, 0, NULL, false, false); - - if (pp2 != NULL) - { - if (protocol == PPP_PROTOCOL_LCP || protocol == PPP_PROTOCOL_IPCP) + // Time-out inspection + if ((p->LastRecvTime + (UINT64)PPP_DATA_TIMEOUT) <= now) { - if (!PPP_LCP_CODE_IS_NEGATIVE(pp2->Lcp->Code)) - { - // A positive response is received - ret = true; - } + // Communication time-out occurs + PPPLog(p, "LP_DATA_TIMEOUT"); + break; + } + + // Terminate if the PPP disconnected + if (p->IsTerminateReceived) + { + PPPLog(p, "LP_NORMAL_TERMINATE"); + break; + } + + if (p->PPPStatus == PPP_STATUS_FAIL || p->PPPStatus == PPP_STATUS_CLOSED) + { + Debug("Exiting main dataloop, status = 0x%x\n", p->PPPStatus); + break; } } - FreePPPPacket(pp2); - Free(pp); + Debug("Exited main dataloop, status = 0x%x\n", p->PPPStatus); - return ret; - -LABEL_ERROR: - Free(pp); - return false; -} - -// Check whether the Virtual HUB with the specified name exist? -bool IsHubExistsWithLock(CEDAR *cedar, char *hubname) -{ - bool ret = false; - // Validate arguments - if (cedar == NULL || hubname == NULL) + if (p->PPPStatus != PPP_STATUS_FAIL) { - return false; - } + IP ip; + char tmp[MAX_SIZE]; - LockList(cedar->HubList); - { - ret = IsHub(cedar, hubname); - } - UnlockList(cedar->HubList); + // Disconnected normally + PPPLog(p, "LP_DISCONNECTED"); - return ret; -} - -// Separate into the user name and the Virtual HUB name by analyzing the string -bool PPPParseUsername(CEDAR *cedar, char *src_username, ETHERIP_ID *dst) -{ - UINT i, len, last_at, first_en; - char token1[MAX_SIZE]; // username - char token2[MAX_SIZE]; // hub_name - char src[MAX_SIZE]; - // Validate arguments - Zero(dst, sizeof(ETHERIP_ID)); - if (cedar == NULL || src == NULL || dst == NULL) - { - return false; - } - - StrCpy(src, sizeof(src), src_username); - Trim(src); - - // Search for the first "\\" in the string - len = StrLen(src); - - first_en = SearchStrEx(src, "\\", 0, true); - - if (first_en != INFINITE && first_en >= 1 && (first_en < (len - 1))) - { - StrCpy(token1, sizeof(token1), src + first_en + 1); - StrCpy(token2, sizeof(token2), src); - token2[first_en] = 0; - - // Confirm whether the hubname exists if the virtual HUB name is - // specified like as hubname\username - if (IsHubExistsWithLock(cedar, token2) == false) + if (p != NULL && p->DhcpAllocated && IsIPCConnected(p->Ipc) && p->ClientAddressOption.ServerAddress != 0) { - // If the hubname does not exist, restore to the original name - StrCpy(token1, sizeof(token1), src); - ClearStr(token2, sizeof(token2)); + // If any address is assigned from the DHCP, release it + UINTToIP(&ip, p->ClientAddressOption.ServerAddress); + + IPToStr(tmp, sizeof(tmp), &ip); + Debug("Releasing IP Address from DHCP Server %s...\n", tmp); + + IPCDhcpFreeIP(p->Ipc, &ip); + IPCProcessL3Events(p->Ipc); + + SleepThread(300); } } else { - // Search for the last "@" in the string - len = StrLen(src); - last_at = INFINITE; - for (i = 0;i < len;i++) - { - char c = src[i]; - - if (c == '@') - { - last_at = i; - } - } - - Zero(token1, sizeof(token1)); - Zero(token2, sizeof(token2)); - - if (last_at == INFINITE) - { - // "@" is not specified - StrCpy(token1, sizeof(token1), src); - } - else - { - // Split with last "@" - StrCpy(token1, sizeof(token1), src); - token1[last_at] = 0; - - StrCpy(token2, sizeof(token2), src + last_at + 1); - } - - // Check whether such Virtual HUB exists If the virtual HUB name is specified - if (IsEmptyStr(token2) == false) - { - if (IsHubExistsWithLock(cedar, token2) == false) - { - // Because the specified virtual HUB name doesn't exist, it's considered to be a part of the user name - StrCpy(token1, sizeof(token1), src); - - ClearStr(token2, sizeof(token2)); - } - } + PPPLog(p, "LP_DISCONNECTED_ABNORMAL"); } - if (IsEmptyStr(token2)) - { - // Select the default Virtual HUB if the Virtual HUB name is not specified - StrCpy(token2, sizeof(token2), SERVER_DEFAULT_HUB_NAME); - if (cedar->Server != NULL && cedar->Server->IPsecServer != NULL) - { - Lock(cedar->Server->IPsecServer->LockSettings); - { - IPsecNormalizeServiceSetting(cedar->Server->IPsecServer); - - StrCpy(token2, sizeof(token2), cedar->Server->IPsecServer->Services.L2TP_DefaultHub); - } - Unlock(cedar->Server->IPsecServer->LockSettings); - } - - } - - // Return the results - StrCpy(dst->HubName, sizeof(dst->HubName), token2); - StrCpy(dst->UserName, sizeof(dst->UserName), token1); - - return true; + FreePPPSession(p); + Debug("PPP Session ended correctly\n"); } -// Process the PPP request packet -PPP_PACKET *PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *req) + +// Entry point + +// Create a new PPP session +THREAD *NewPPPSession(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, TUBE *send_tube, TUBE *recv_tube, char *postfix, char *client_software_name, char *client_hostname, char *crypt_name, UINT adjust_mss) { - UINT i; - PPP_PACKET *ret = NULL; - UINT num_not_supported = 0; - UINT num_not_accepted = 0; - bool no_return_option_list = false; - UINT return_code = 0; - BUF *lcp_ret_data = NULL; + PPP_SESSION *p; + THREAD *t; // Validate arguments - if (p == NULL || req == NULL || req->Lcp == NULL) + if (cedar == NULL || client_ip == NULL || server_ip == NULL || send_tube == NULL || recv_tube == NULL) { return NULL; } - - // Initialize - for (i = 0;i < LIST_NUM(req->Lcp->OptionList);i++) + if (IsEmptyStr(postfix)) { - PPP_OPTION *t = LIST_DATA(req->Lcp->OptionList, i); - - t->IsAccepted = false; - t->IsSupported = false; - t->AltDataSize = 0; - Zero(t->AltData, sizeof(t->AltData)); + postfix = "PPP"; + } + if (IsEmptyStr(crypt_name)) + { + crypt_name = ""; + } + if (IsEmptyStr(client_software_name)) + { + client_software_name = "PPP VPN Client"; } - // Process by scanning the specified option value - if (req->Protocol == PPP_PROTOCOL_LCP) - { - // LCP - if (req->Lcp == NULL) - { - return NULL; - } - for (i = 0;i < LIST_NUM(req->Lcp->OptionList);i++) - { - PPP_OPTION *t = LIST_DATA(req->Lcp->OptionList, i); + // Data structure initialization + p = ZeroMalloc(sizeof(PPP_SESSION)); - switch (t->Type) + p->EnableMSCHAPv2 = true; + p->AuthProtocol = NULL; + p->MsChapV2_ErrorCode = 691; + p->EapClient = NULL; + + p->Cedar = cedar; + AddRef(cedar->ref); + + p->AdjustMss = adjust_mss; + + StrCpy(p->CryptName, sizeof(p->CryptName), crypt_name); + + Copy(&p->ClientIP, client_ip, sizeof(IP)); + p->ClientPort = client_port; + + Copy(&p->ServerIP, server_ip, sizeof(IP)); + p->ServerPort = server_port; + + p->TubeRecv = recv_tube; + p->TubeSend = send_tube; + + AddRef(p->TubeRecv->Ref); + AddRef(p->TubeSend->Ref); + + StrCpy(p->Postfix, sizeof(p->Postfix), postfix); + StrCpy(p->ClientSoftwareName, sizeof(p->ClientSoftwareName), client_software_name); + + if (IsEmptyStr(client_hostname)) + { + IPToStr(p->ClientHostname, sizeof(p->ClientHostname), client_ip); + } + else + { + StrCpy(p->ClientHostname, sizeof(p->ClientHostname), client_hostname); + } + + p->FlushList = NewTubeFlushList(); + + // Thread creation + t = NewThread(PPPThread, p); + + return t; +} + +// PPP processing functions + +// Finds out if a packet is supported, if not - sends a notification to the peer +// result: false - supported, true - unsupported +bool PPPRejectUnsupportedPacket(PPP_SESSION *p, PPP_PACKET *pp) +{ + return PPPRejectUnsupportedPacketEx(p, pp, false); +} +bool PPPRejectUnsupportedPacketEx(PPP_SESSION *p, PPP_PACKET *pp, bool force) +{ + bool result = false; + if (p == NULL || pp == NULL) + { + return false; + } + + if (PPP_IS_SUPPORTED_PROTOCOL(pp->Protocol) == false || force == true) + { + // Unsupported algorithm + PPP_PACKET *pp2 = ZeroMalloc(sizeof(PPP_PACKET)); + BUF *buf; + UCHAR c; + USHORT us; + + Debug("Rejecting PPP protocol = 0x%x\n", pp->Protocol); + result = true; + + pp2->Protocol = PPP_PROTOCOL_LCP; + pp2->IsControl = false; + + buf = NewBuf(); + + // Code + c = PPP_LCP_CODE_PROTOCOL_REJECT; + WriteBuf(buf, &c, 1); + + // ID + c = p->NextId++; + WriteBuf(buf, &c, 1); + + // Length + us = Endian16(pp->DataSize + 6); + WriteBuf(buf, &us, 2); + + // Rejected Protocol + us = Endian16(pp->Protocol); + WriteBuf(buf, &us, 2); + + // Packet Data + WriteBuf(buf, pp->Data, pp->DataSize); + + pp2->Data = Clone(buf->Buf, buf->Size); + pp2->DataSize = buf->Size; + + FreeBuf(buf); + + if (!PPPSendPacketAndFree(p, pp2)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + } + } + return result; +} + +// Do the retransmissions if needed +bool PPPProcessRetransmissions(PPP_SESSION *p) +{ + INT64 i = 0; + UINT64 now = Tick64(); + UINT64 count; + if (p->SentReqPacketList == NULL) + { + Debug("Somehow SentReqPacketList is NULL!\n"); + return false; + } + // Making it signed but expanding to 64 bits + count = LIST_NUM(p->SentReqPacketList); + if (count == 0) + { + return true; + } + for (i = count - 1; i >= 0; --i) + { + PPP_REQUEST_RESEND *t = LIST_DATA(p->SentReqPacketList, i); + + if (t->TimeoutTime <= now) + { + Debug("Timing out on resending control packet protocol = 0x%x\n", t->Packet->Protocol); + Delete(p->SentReqPacketList, t); + FreePPPPacket(t->Packet); + Free(t); + } + else if (t->ResendTime <= now) + { + Debug("Resending control packet protocol = 0x%x\n", t->Packet->Protocol); + if (!PPPSendPacketEx(p, t->Packet, false)) { - case PPP_LCP_OPTION_MRU: - // MRU - t->IsSupported = true; - if (t->DataSize == sizeof(USHORT)) + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + t->ResendTime = now + PPP_PACKET_RESEND_INTERVAL; + } + } + return true; +} + +// Send the PPP Echo Request +bool PPPSendEchoRequest(PPP_SESSION *p) +{ + UINT64 now = Tick64(); + if (p->NextEchoSendTime == 0 || now >= p->NextEchoSendTime) + { + PPP_PACKET *pp; + char echo_data[] = "\0\0\0\0Aho Baka Manuke"; + + p->NextEchoSendTime = now + (UINT64)PPP_ECHO_SEND_INTERVAL; + if (IsIPCConnected(p->Ipc)) + { + AddInterrupt(p->Ipc->Interrupt, p->NextEchoSendTime); + } + + // Validate arguments + if (p == NULL) + { + return false; + } + + pp = ZeroMalloc(sizeof(PPP_PACKET)); + pp->Protocol = PPP_PROTOCOL_LCP; + pp->IsControl = true; + pp->Lcp = NewPPPLCP(PPP_LCP_CODE_ECHO_REQUEST, 0); + + pp->Lcp->Data = Clone(echo_data, sizeof(echo_data)); + pp->Lcp->DataSize = sizeof(echo_data); + + if (!PPPSendPacketAndFree(p, pp)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + return true; + } + return false; +} + +// Processes response packets +bool PPPProcessResponsePacket(PPP_SESSION *p, PPP_PACKET* pp, PPP_PACKET* req) +{ + if (req == NULL) + { + Debug("We received a response for... What? We never sent this request, protocol = 0x%x, code = 0x%x\n", pp->Protocol, pp->Lcp->Code); + // Let's just discard this, as this was probably already parsed, and we just stumbled upon a resend + return false; + } + + switch (pp->Protocol) + { + case PPP_PROTOCOL_LCP: + return PPPProcessLCPResponsePacket(p, pp, req); + break; + case PPP_PROTOCOL_PAP: + Debug("Got a response PAP, which is invalid, we should get a request instead\n"); + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + break; + case PPP_PROTOCOL_CHAP: + return PPPProcessCHAPResponsePacket(p, pp, req); + break; + case PPP_PROTOCOL_IPCP: + return PPPProcessIPCPResponsePacket(p, pp, req); + break; + case PPP_PROTOCOL_IPV6CP: + Debug("IPv6CP to be implemented\n"); + break; + default: + Debug("We received a response for an unsupported protocol??? Should be filtered out already! protocol = 0x%x, code = 0x%x\n", pp->Protocol, pp->Lcp->Code); + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + + return false; +} + +bool PPPProcessLCPResponsePacket(PPP_SESSION *p, PPP_PACKET* pp, PPP_PACKET* req) +{ + UINT i; + bool isAccepted = !PPP_LCP_CODE_IS_NEGATIVE(pp->Lcp->Code); + bool result = true; + // MSCHAPv2 code + UCHAR ms_chap_v2_code[3]; + WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP); + ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2; + + // We got one of rejects here, not NACKs + if (!isAccepted && pp->Lcp->Code == PPP_LCP_CODE_PROTOCOL_REJECT) + { + // If we receive a protocol reject before we finished authenticating + // probably means the PPP client is not compatible anyway so we fail the connection + if (p->PPPStatus != PPP_STATUS_NETWORK_LAYER) + { + USHORT* protocol = pp->Lcp->Data; + Debug("Protocol 0x%x rejected before auth, probably unsupported client, failing connection\n", *protocol); + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + else + { + USHORT* protocol = pp->Lcp->Data; + if (*protocol == PPP_PROTOCOL_IPCP || *protocol == PPP_PROTOCOL_IP) + { + p->IPv4_State == PPP_PROTO_STATUS_REJECTED; + } + if (*protocol == PPP_PROTOCOL_IPV6CP || *protocol == PPP_PROTOCOL_IPV6) + { + p->IPv6_State == PPP_PROTO_STATUS_REJECTED; + } + } + } + + if (!isAccepted && pp->Lcp->Code == PPP_LCP_CODE_CODE_REJECT) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + + for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++) + { + PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i); + PPP_OPTION *opt = NULL; + + switch (t->Type) + { + case PPP_LCP_OPTION_MRU: + // MRU + if (t->DataSize == sizeof(USHORT)) + { + USHORT value = READ_USHORT(t->Data); + if (!isAccepted) { - UINT value = READ_USHORT(t->Data); + if (pp->Lcp->Code != PPP_LCP_CODE_NAK) + { + Debug("MRU setup failed, rejected"); + p->Mru1 = p->Mru2 = PPP_MRU_DEFAULT; + } if (value < PPP_MRU_MIN || value > PPP_MRU_MAX) { - t->IsAccepted = false; - value = MAKESURE(value, PPP_MRU_MIN, PPP_MRU_MAX); - WRITE_USHORT(t->AltData, value); - t->AltDataSize = sizeof(USHORT); + Debug("Couldn't agree on an MRU! Breaking link... MRU = 0x%x\n", value); + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; } else { - p->Mru1 = value; - Debug("PPP: Client set %u as MRU\n", p->Mru1); - t->IsAccepted = true; + PPP_LCP* lcp = NewPPPLCP(PPP_LCP_CODE_REQ, 0); + Add(lcp->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &value, sizeof(USHORT))); + if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, lcp)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + Debug("PPP: Server got %u as MRU from NACK, re-requesting\n", p->Mru2); } } - break; + else if (value < PPP_MRU_MIN || value > PPP_MRU_MAX) + { + Debug("The client somehow ACKed an invalid MRU, breaking link... MRU = 0x%x\n", value); + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + result = false; + } + else + { + p->Mru2 = value; + Debug("PPP: Server set %u as MRU\n", p->Mru2); + } } + break; + case PPP_LCP_OPTION_AUTH: + opt = PPPGetOptionValue(req->Lcp, PPP_LCP_OPTION_AUTH); + if (opt == NULL) + { + Debug("We got some weird response with option absent in request, wut? Disconnecting\n"); + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + if (opt->DataSize == sizeof(ms_chap_v2_code) && Cmp(opt->Data, ms_chap_v2_code, opt->DataSize) == 0) + { + // Try to request PAP then + if (!isAccepted || !p->EnableMSCHAPv2) + { + UINT64 offer = 0; + PPP_LCP *c = NewPPPLCP(PPP_LCP_CODE_REQ, 0); + USHORT proto = Endian16(PPP_LCP_AUTH_PAP); + Copy(&offer, t->Data, t->DataSize > sizeof(UINT64) ? sizeof(UINT64) : t->DataSize); + Debug("NACK proto with code = 0x%x, cypher = 0x%x, offered cypher = 0x%x\n", pp->Lcp->Code, *((USHORT*)(opt->Data)), offer); + Debug("Request PAP\n"); + Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &proto, sizeof(USHORT))); + if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, c)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + } + else if (p->AuthProtocol == NULL) + { + p->AuthProtocol = PPP_PROTOCOL_CHAP; + Debug("Setting BEFORE_AUTH from ACK on LCP response parse on CHAP accept\n"); + PPPSetStatus(p, PPP_STATUS_BEFORE_AUTH); + } + + } + else if (opt->DataSize == sizeof(USHORT) && *((USHORT*)(opt->Data)) == Endian16(PPP_LCP_AUTH_PAP)) + { + // We couldn't agree on auth proto, failing connection + if (!isAccepted) + { + UINT64 offer = 0; + Copy(&offer, t->Data, t->DataSize > sizeof(UINT64) ? sizeof(UINT64) : t->DataSize); + Debug("NACK proto with code = 0x%x, cypher = 0x%x, offered cypher = 0x%x\n", pp->Lcp->Code, *((USHORT*)(opt->Data)), offer); + Debug("Couldn't agree on auth protocol!\n"); + PPPLog(p, "LP_PAP_MSCHAPV2_REJECTED"); + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + else if (p->AuthProtocol == NULL) + { + p->AuthProtocol = PPP_PROTOCOL_PAP; + Debug("Setting BEFORE_AUTH from ACK on LCP response parse on PAP accept\n"); + PPPSetStatus(p, PPP_STATUS_BEFORE_AUTH); + } + } + break; } } - else if (req->Protocol == PPP_PROTOCOL_CHAP) + + return result; +} + +// Process CHAP responses +bool PPPProcessCHAPResponsePacket(PPP_SESSION *p, PPP_PACKET* pp, PPP_PACKET* req) +{ + PPP_LCP* lcp; + if (pp->Lcp->Code == PPP_CHAP_CODE_RESPONSE) { bool ok = false; - char ret_str[MAX_SIZE]; - - no_return_option_list = true; - - if (p->Ipc == NULL) + if (p->PPPStatus != PPP_STATUS_AUTHENTICATING && !p->AuthOk) { - // MS-CHAPv2 - if (req->Lcp->DataSize >= 51) + Debug("Receiving CHAP response packets outside of auth status, some errors probably!"); + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + if (p->AuthProtocol != PPP_PROTOCOL_CHAP) + { + Debug("Receiving CHAP packet when auth protocol set to 0x%x\n", p->AuthProtocol); + PPPLog(p, "LP_NEXT_PROTOCOL_IS_NOT_PAP", pp->Protocol); + PPPRejectUnsupportedPacketEx(p, pp, true); + return false; + } + + ok = PPPParseMSCHAP2ResponsePacket(p, pp); + + // If we got only first packet of double CHAP then send second challenge + if (ok && p->MsChapV2_UseDoubleMsChapV2 && p->EapClient != NULL && p->Ipc == NULL) + { + lcp = BuildMSCHAP2ChallengePacket(p); + if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_CHAP, lcp)) { - BUF *b; - - b = NewBuf(); - - WriteBuf(b, req->Lcp->Data, req->Lcp->DataSize); - SeekBuf(b, 0, 0); - - if (ReadBufChar(b) == 49) - { - UCHAR client_response_buffer[49]; - UCHAR *client_challenge_16; - UCHAR *client_response_24; - char username_tmp[MAX_SIZE]; - IPC *ipc = NULL; - char id[MAX_SIZE]; - char hub[MAX_SIZE]; - char password[MAX_SIZE]; - char server_challenge_hex[MAX_SIZE]; - char client_challenge_hex[MAX_SIZE]; - char client_response_hex[MAX_SIZE]; - char eap_client_hex[64]; - ETHERIP_ID d; - UINT error_code; - UINT64 eap_client_ptr = (UINT64)p->EapClient; - - ReadBuf(b, client_response_buffer, 49); - - Zero(username_tmp, sizeof(username_tmp)); - ReadBuf(b, username_tmp, sizeof(username_tmp) - 1); - - client_challenge_16 = client_response_buffer + 0; - client_response_24 = client_response_buffer + 16 + 8; - - Copy(p->MsChapV2_ClientChallenge, client_challenge_16, 16); - Copy(p->MsChapV2_ClientResponse, client_response_24, 24); - - Debug("MS-CHAPv2: id=%s\n", username_tmp); - - Zero(id, sizeof(id)); - Zero(hub, sizeof(hub)); - - // The user name is divided into the ID and the virtual HUB name - Zero(&d, sizeof(d)); - PPPParseUsername(p->Cedar, username_tmp, &d); - - StrCpy(id, sizeof(id), d.UserName); - StrCpy(hub, sizeof(hub), d.HubName); - - // Convert the MS-CHAPv2 data to a password string - BinToStr(server_challenge_hex, sizeof(server_challenge_hex), - p->MsChapV2_ServerChallenge, sizeof(p->MsChapV2_ServerChallenge)); - BinToStr(client_challenge_hex, sizeof(client_challenge_hex), - p->MsChapV2_ClientChallenge, sizeof(p->MsChapV2_ClientChallenge)); - BinToStr(client_response_hex, sizeof(client_response_hex), - p->MsChapV2_ClientResponse, sizeof(p->MsChapV2_ClientResponse)); - BinToStr(eap_client_hex, sizeof(eap_client_hex), - &eap_client_ptr, 8); - - Format(password, sizeof(password), "%s%s:%s:%s:%s:%s", - IPC_PASSWORD_MSCHAPV2_TAG, - username_tmp, - server_challenge_hex, - client_challenge_hex, - client_response_hex, - eap_client_hex); - - // Attempt to connect with IPC - ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, hub, id, password, - &error_code, &p->ClientIP, p->ClientPort, &p->ServerIP, p->ServerPort, - p->ClientHostname, p->CryptName, false, p->AdjustMss, p->EapClient); - - if (ipc != NULL) - { - p->Ipc = ipc; - - Copy(p->MsChapV2_ServerResponse, ipc->MsChapV2_ServerResponse, 20); - - ok = true; - } - else - { - switch (error_code) - { - default: - // Normal authentication error - p->MsChapV2_ErrorCode = 691; - break; - - case ERR_MSCHAP2_PASSWORD_NEED_RESET: - // Authentication errors due to compatibility issues of the password - p->MsChapV2_ErrorCode = 942; - break; - } - } - } - - FreeBuf(b); + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; } } - else + // We got a successful MSCHAPv2 response, so let's send a SUCCESS + else if (ok) { - // Return success for a request from the second time when it is successfully authenticated once - ok = true; - } - - // Generate a response options string - if (ok == false) - { - // In the case of failure - char hex[MAX_SIZE]; - BinToStr(hex, sizeof(hex), p->MsChapV2_ServerChallenge, 16); - - Format(ret_str, sizeof(ret_str), - "E=%u R=0 C=%s V=3", p->MsChapV2_ErrorCode, hex); - - return_code = PPP_CHAP_CODE_FAILURE; - } - else - { - // In the case of success char hex[MAX_SIZE]; + char ret_str[MAX_SIZE]; + BUF* lcp_ret_data = NewBuf(); + PPP_PACKET* res = ZeroMalloc(sizeof(PPP_PACKET)); BinToStr(hex, sizeof(hex), p->MsChapV2_ServerResponse, 20); Format(ret_str, sizeof(ret_str), "S=%s", hex); - return_code = PPP_CHAP_CODE_SUCCESS; + WriteBuf(lcp_ret_data, ret_str, StrLen(ret_str)); + + lcp = NewPPPLCP(PPP_CHAP_CODE_SUCCESS, p->MsChapV2_PacketId); + lcp->Data = Clone(lcp_ret_data->Buf, lcp_ret_data->Size); + lcp->DataSize = lcp_ret_data->Size; + + if (lcp_ret_data != NULL) + { + FreeBuf(lcp_ret_data); + } + + res->Lcp = lcp; + res->IsControl = true; + res->Protocol = PPP_PROTOCOL_CHAP; + + if (!PPPSendPacketAndFree(p, res)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } p->AuthOk = true; + PPPSetStatus(p, PPP_STATUS_AUTH_SUCCESS); + } + // We failed MSCHAPv2 auth + else + { + char hex[MAX_SIZE]; + char ret_str[MAX_SIZE]; + BUF* lcp_ret_data = NewBuf(); + PPP_PACKET* res = ZeroMalloc(sizeof(PPP_PACKET)); + + BinToStr(hex, sizeof(hex), p->MsChapV2_ServerChallenge, 16); + + Format(ret_str, sizeof(ret_str), + "E=%u R=0 C=%s V=3", p->MsChapV2_ErrorCode, hex); + + WriteBuf(lcp_ret_data, ret_str, StrLen(ret_str)); + + lcp = NewPPPLCP(PPP_CHAP_CODE_FAILURE, p->MsChapV2_PacketId); + lcp->Data = Clone(lcp_ret_data->Buf, lcp_ret_data->Size); + lcp->DataSize = lcp_ret_data->Size; + + if (lcp_ret_data != NULL) + { + FreeBuf(lcp_ret_data); + } + + res->Lcp = lcp; + res->IsControl = true; + res->Protocol = PPP_PROTOCOL_CHAP; + + if (!PPPSendPacketAndFree(p, res)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + + PPPLog(p, "LP_CHAP_FAILED"); + PPPSetStatus(p, PPP_STATUS_AUTH_FAIL); } - lcp_ret_data = NewBuf(); - WriteBuf(lcp_ret_data, ret_str, StrLen(ret_str)); + return ok; } - else if (req->Protocol == PPP_PROTOCOL_PAP) + return false; +} + +// Process IPCP responses +bool PPPProcessIPCPResponsePacket(PPP_SESSION *p, PPP_PACKET* pp, PPP_PACKET* req) +{ + bool isAccepted = !PPP_LCP_CODE_IS_NEGATIVE(pp->Lcp->Code); + + IP addrStruct; + char addrStr[MAX_SIZE]; + UINT addr; + IP prevAddrStruct; + char prevAddrStr[MAX_SIZE]; + UINT prevAddr; + PPP_LCP* c; + UINT ui; + + if (!PPPGetIPAddressValueFromLCP(pp->Lcp, PPP_IPCP_OPTION_IP, &addrStruct) || pp->Lcp->Code == PPP_LCP_CODE_REJECT || pp->Lcp->Code == PPP_LCP_CODE_CODE_REJECT) + { + Debug("Unsupported IPCP protocol"); + p->IPv4_State = PPP_PROTO_STATUS_REJECTED; + PPPRejectUnsupportedPacketEx(p, pp, true); + return false; + } + + // We're dealing either with ACK or NACK + addr = IPToUINT(&addrStruct); + IPToStr(addrStr, MAX_SIZE, &addrStruct); + + if (isAccepted) + { + Debug("Accepted server IP address of %s\n", addrStr); + + // We already configured client address, now server address is also confirmed, ready for IPv4 data flow + if (p->IPv4_State == PPP_PROTO_STATUS_CONFIG) + { + p->IPv4_State = PPP_PROTO_STATUS_CONFIG_WAIT; + } + return true; + } + + p->IPv4_State == PPP_PROTO_STATUS_CONFIG; + + PPPGetIPAddressValueFromLCP(req->Lcp, PPP_IPCP_OPTION_IP, &prevAddrStruct); + prevAddr = IPToUINT(&prevAddrStruct); + IPToStr(prevAddrStr, MAX_SIZE, &prevAddrStruct); + + Debug("Denied server IP address %s, proposed %s\n", prevAddrStr, addrStr); + + // Fallback mechanism - just request 1.0.0.1 + if (prevAddr == Endian32(0x01000001)) + { + Debug("We already tried the fallback IP of 1.0.0.1, giving up\n"); + p->IPv4_State = PPP_PROTO_STATUS_REJECTED; + PPPRejectUnsupportedPacketEx(p, pp, true); + return false; + } + + c = NewPPPLCP(PPP_LCP_CODE_REQ, 0); + ui = Endian32(0x01000001); // 1.0.0.1 + Add(c->OptionList, NewPPPOption(PPP_IPCP_OPTION_IP, &ui, sizeof(UINT))); + if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_IPCP, c)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + + return false; +} + + +// Processes request packets +bool PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *pp) +{ + switch (pp->Protocol) + { + case PPP_PROTOCOL_LCP: + return PPPProcessLCPRequestPacket(p, pp); + break; + case PPP_PROTOCOL_PAP: + return PPPProcessPAPRequestPacket(p, pp); + break; + case PPP_PROTOCOL_CHAP: + Debug("Got a CHAP request, which is invalid, we should get CHAP response instead\n"); + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + break; + case PPP_PROTOCOL_IPCP: + return PPPProcessIPCPRequestPacket(p, pp); + break; + case PPP_PROTOCOL_IPV6CP: + PPPRejectUnsupportedPacketEx(p, pp, true); + Debug("IPv6CP to be implemented\n"); + break; + default: + Debug("Unsupported protocols should be already filtered out! protocol = 0x%x, code = 0x%x\n", pp->Protocol, pp->Lcp->Code); + return false; + break; + } + return false; +} + +bool PPPProcessLCPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp) +{ + bool result = true; + UINT i = 0; + + USHORT NegotiatedAuthProto = PPP_UNSPECIFIED; + USHORT NegotiatedMRU = PPP_UNSPECIFIED; + // MSCHAPv2 code + UCHAR ms_chap_v2_code[3]; + WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP); + ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2; + + for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++) + { + PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i); + + switch (t->Type) + { + case PPP_LCP_OPTION_AUTH: + t->IsSupported = true; + if (t->DataSize == sizeof(USHORT) && *((USHORT*)t->Data) == Endian16(PPP_LCP_AUTH_PAP) && p->AuthProtocol != PPP_PROTOCOL_CHAP) + { + t->IsAccepted = true; + NegotiatedAuthProto = PPP_PROTOCOL_PAP; + } + else if (t->DataSize == sizeof(ms_chap_v2_code) && Cmp(t->Data, ms_chap_v2_code, t->DataSize) == 0) + { + t->IsAccepted = true; + NegotiatedAuthProto = PPP_PROTOCOL_CHAP; + } + else + { + // We're recommending MSCHAPv2 by default as a more secure algo + t->IsAccepted = false; + t->AltDataSize = sizeof(ms_chap_v2_code); + Copy(t->AltData, ms_chap_v2_code, sizeof(ms_chap_v2_code)); + } + break; + case PPP_LCP_OPTION_MRU: + t->IsSupported = true; + if (t->DataSize == sizeof(USHORT)) + { + USHORT value = READ_USHORT(t->Data); + if (value < PPP_MRU_MIN || value > PPP_MRU_MAX) + { + t->IsAccepted = false; + value = MAKESURE(value, PPP_MRU_MIN, PPP_MRU_MAX); + //Debug("MRU not accepted, sending NACK with value = 0x%x\n", value); + t->AltDataSize = sizeof(USHORT); + WRITE_USHORT(t->AltData, value); + } + else + { + t->IsAccepted = true; + NegotiatedMRU = value; + //Debug("MRU accepted, value = 0x%x\n", value); + } + } + else + { + t->IsAccepted = false; + t->AltDataSize = sizeof(USHORT); + WRITE_USHORT(t->AltData, PPP_MRU_DEFAULT); + } + break; + default: + t->IsSupported = false; + Debug("Unsupported LCP option = 0x%x\n", t->Type); + break; + } + } + + if (PPPRejectLCPOptions(p, pp)) + { + Debug("Rejected LCP options...\n"); + return false; + } + + if (PPPNackLCPOptions(p, pp)) + { + Debug("NACKed LCP options...\n"); + return false; + } + + if (!PPPAckLCPOptions(p, pp)) + { + return false; + } + + if (NegotiatedAuthProto != PPP_UNSPECIFIED) + { + if (p->AuthProtocol == NULL) + { + p->AuthProtocol = NegotiatedAuthProto; + PPPSetStatus(p, PPP_STATUS_BEFORE_AUTH); + Debug("Setting BEFORE_AUTH from REQ on LCP request parse\n"); + } + } + if (NegotiatedMRU != PPP_UNSPECIFIED) + { + p->Mru1 = NegotiatedMRU; + } + + return true; +} + +bool PPPProcessPAPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp) +{ + if (p->PPPStatus != PPP_STATUS_BEFORE_AUTH && !p->AuthOk) + { + PPP_LCP* lcp = NewPPPLCP(PPP_PAP_CODE_NAK, pp->Lcp->Id); + PPP_PACKET* ret = ZeroMalloc(sizeof(PPP_PACKET)); + + Debug("Got a PAP request before we're ready for AUTH procedure!\n"); + + ret->IsControl = true; + ret->Protocol = PPP_PROTOCOL_PAP; + ret->Lcp = lcp; + if (!PPPSendPacketAndFree(p, ret)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + + return false; + } + if (p->AuthProtocol != PPP_PROTOCOL_PAP) + { + Debug("Trying to auth with PAP when should be 0x%x\n", p->AuthProtocol); + PPPLog(p, "LP_NEXT_PROTOCOL_IS_NOT_CHAP", pp->Protocol); + + // Forcing rejection of PAP on configured MSCHAPv2 + PPPRejectUnsupportedPacketEx(p, pp, true); + + return false; + } + if (!p->AuthOk) { UCHAR *data; UINT size; - bool ok = false; - no_return_option_list = true; + PPPSetStatus(p, PPP_STATUS_AUTHENTICATING); if (p->Ipc == NULL) { // PAP // Extract the ID and the password - data = req->Lcp->Data; - size = req->Lcp->DataSize; + data = pp->Lcp->Data; + size = pp->Lcp->DataSize; if (size >= 1) { @@ -1156,7 +1389,13 @@ PPP_PACKET *PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *req) if (ipc != NULL) { p->Ipc = ipc; - ok = true; + p->AuthOk = true; + } + else + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; } } } @@ -1167,871 +1406,611 @@ PPP_PACKET *PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *req) else { // Return success for a request from the second time when it is successfully authenticated once - ok = true; - } - - if (ok == false) - { - // Authentication failure - return_code = PPP_PAP_CODE_NAK; - } - else - { - // Authentication success - return_code = PPP_PAP_CODE_ACK; - p->AuthOk = true; } } - else if (req->Protocol == PPP_PROTOCOL_IPCP) + if (p->AuthOk) { - PPP_IPOPTION o; - // Get the IP options data from the request data - if (PPPGetIPOptionFromLCP(&o, req->Lcp)) + PPP_LCP* lcp = NewPPPLCP(PPP_PAP_CODE_ACK, pp->Lcp->Id); + PPP_PACKET* ret = ZeroMalloc(sizeof(PPP_PACKET)); + ret->IsControl = true; + ret->Protocol = PPP_PROTOCOL_PAP; + ret->Lcp = lcp; + if (!PPPSendPacketAndFree(p, ret)) { - PPP_IPOPTION res; - IP subnet; - IP gw; + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } - if (IsZeroIP(&o.IpAddress) == false) + if (p->PPPStatus == PPP_STATUS_AUTHENTICATING) + { + PPPSetStatus(p, PPP_STATUS_AUTH_SUCCESS); + } + return true; + } + if (!p->AuthOk) + { + PPP_LCP* lcp = NewPPPLCP(PPP_PAP_CODE_NAK, pp->Lcp->Id); + PPP_PACKET* ret = ZeroMalloc(sizeof(PPP_PACKET)); + ret->IsControl = true; + ret->Protocol = PPP_PROTOCOL_PAP; + ret->Lcp = lcp; + if (!PPPSendPacketAndFree(p, ret)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + + if (p->PPPStatus == PPP_STATUS_AUTHENTICATING) + { + PPPSetStatus(p, PPP_STATUS_AUTH_FAIL); + PPPLog(p, "LP_PAP_FAILED"); + } + + return false; + } + +} + + +bool PPPProcessIPCPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp) +{ + PPP_IPOPTION o; + PPP_IPOPTION res; + DHCP_OPTION_LIST cao; + IP client_ip; + IP subnet; + IP zero; + IP gw; + bool ok = true; + bool processed = false; + + if (p->IPv4_State == PPP_PROTO_STATUS_REJECTED) + { + Debug("We got an IPCP packet after we had it rejected"); + return PPPRejectUnsupportedPacketEx(p, pp, true); + } + + if (!PPPGetIPOptionFromLCP(&o, pp->Lcp)) + { + Debug("Unsupported IPCP request!"); + ok = false; + } + + // Process if not configured yet by server + if (IsZero(&p->ClientAddressOption, sizeof(DHCP_OPTION_LIST)) && ok) + { + // Decide if we received a static IP from client and it is allowed + if (IsZeroIP(&o.IpAddress) == false) + { + if (p->Ipc->Policy->DHCPForce == false) { - if (p->Ipc->Policy->DHCPForce == false) + if (p->DhcpAllocated == false) { - if (p->DhcpAllocated == false) + if (p->UseStaticIPAddress == false) { - if (p->UseStaticIPAddress == false) - { - DHCP_OPTION_LIST cao; + DHCP_OPTION_LIST cao; - // The client specify an IP address - Zero(&cao, sizeof(cao)); + // The client specify an IP address + Zero(&cao, sizeof(cao)); - cao.ClientAddress = IPToUINT(&o.IpAddress); + cao.ClientAddress = IPToUINT(&o.IpAddress); - Copy(&p->ClientAddressOption, &cao, sizeof(cao)); - - p->UseStaticIPAddress = true; - } - } - } - } - else - { - p->UseStaticIPAddress = false; - } - - if (p->UseStaticIPAddress) - { - if (p->DhcpIpInformTried == false) - { - // Get additional information such as the subnet mask from the DHCP server - DHCP_OPTION_LIST cao; - IP client_ip; - - IP subnet; - IP zero; - - SetIP(&subnet, 255, 0, 0, 0); - Zero(&zero, sizeof(zero)); - - UINTToIP(&client_ip, p->ClientAddressOption.ClientAddress); - - Zero(&cao, sizeof(cao)); - - IPCSetIPv4Parameters(p->Ipc, &client_ip, &subnet, &zero, NULL); - - p->DhcpIpInformTried = true; - - PPPLog(p, "LP_DHCP_INFORM_TRYING"); - - if (IPCDhcpRequestInformIP(p->Ipc, &cao, p->TubeRecv, &client_ip)) - { - Debug("IPCDhcpRequestInformIP ok.\n"); - Copy(&p->ClientAddressOption, &cao, sizeof(cao)); - p->ClientAddressOption.ClientAddress = IPToUINT(&client_ip); - - if (true) - { - char server_ip_str[64]; - char subnet_str[64], defgw_str[64]; - char dns1_str[64], dns2_str[64]; - char wins1_str[64], wins2_str[64]; - - IPToStr32(server_ip_str, sizeof(server_ip_str), cao.ServerAddress); - IPToStr32(subnet_str, sizeof(subnet_str), cao.SubnetMask); - IPToStr32(defgw_str, sizeof(defgw_str), cao.Gateway); - IPToStr32(dns1_str, sizeof(dns1_str), cao.DnsServer); - IPToStr32(dns2_str, sizeof(dns2_str), cao.DnsServer2); - IPToStr32(wins1_str, sizeof(wins1_str), cao.WinsServer); - IPToStr32(wins2_str, sizeof(wins2_str), cao.WinsServer2); - - PPPLog(p, "LP_DHCP_INFORM_OK", - subnet_str, defgw_str, cao.DomainName, - dns1_str, dns2_str, wins1_str, wins2_str, - server_ip_str); - } - } - else - { - Debug("IPCDhcpRequestInformIP failed.\n"); - - PPPLog(p, "LP_DHCP_INFORM_NG"); - } - - IPCSetIPv4Parameters(p->Ipc, &zero, &zero, &zero, NULL); - } - } - else - { - // Get an IP address from a DHCP server - if (p->DhcpIpAllocTried == false) - { - DHCP_OPTION_LIST cao; - - Zero(&cao, sizeof(cao)); - p->DhcpIpAllocTried = true; - - PPPLog(p, "LP_DHCP_REQUEST_TRYING"); - - if (IPCDhcpAllocateIP(p->Ipc, &cao, p->TubeRecv)) - { - UINT t; - - Debug("IPCDhcpAllocateIP ok.\n"); - - // IP address has been determined Copy(&p->ClientAddressOption, &cao, sizeof(cao)); - p->DhcpAllocated = true; - - // Determine the DHCP update interval - t = cao.LeaseTime; - if (t == 0) - { - t = 600; - } - - t = t / 3; - - if (t == 0) - { - t = 1; - } - - p->DhcpRenewInterval = (UINT64)(t * 1000); - p->DhcpNextRenewTime = Tick64() + p->DhcpRenewInterval; - - if (true) - { - char client_ip_str[64], server_ip_str[64]; - char subnet_str[64], defgw_str[64]; - char dns1_str[64], dns2_str[64]; - char wins1_str[64], wins2_str[64]; - - IPToStr32(client_ip_str, sizeof(client_ip_str), cao.ClientAddress); - IPToStr32(server_ip_str, sizeof(server_ip_str), cao.ServerAddress); - IPToStr32(subnet_str, sizeof(subnet_str), cao.SubnetMask); - IPToStr32(defgw_str, sizeof(defgw_str), cao.Gateway); - IPToStr32(dns1_str, sizeof(dns1_str), cao.DnsServer); - IPToStr32(dns2_str, sizeof(dns2_str), cao.DnsServer2); - IPToStr32(wins1_str, sizeof(wins1_str), cao.WinsServer); - IPToStr32(wins2_str, sizeof(wins2_str), cao.WinsServer2); - - PPPLog(p, "LP_DHCP_REQUEST_OK", - client_ip_str, subnet_str, defgw_str, cao.DomainName, - dns1_str, dns2_str, wins1_str, wins2_str, - server_ip_str, cao.LeaseTime); - } - } - else - { - Debug("IPCDhcpAllocateIP failed.\n"); - - PPPLog(p, "LP_DHCP_REQUEST_NG"); + p->UseStaticIPAddress = true; } } } + } + else + { + p->UseStaticIPAddress = false; + } - if (IsValidUnicastIPAddressUINT4(p->ClientAddressOption.ClientAddress) && - p->ClientAddressOption.SubnetMask != 0) + // Get additional information for static clients + if (p->UseStaticIPAddress) + { + if (p->DhcpIpInformTried == false) { - // Success to determine the address - UINTToIP(&subnet, p->ClientAddressOption.SubnetMask); - UINTToIP(&gw, p->ClientAddressOption.Gateway); + // Get additional information such as the subnet mask from the DHCP server + SetIP(&subnet, 255, 0, 0, 0); + Zero(&zero, sizeof(zero)); - Zero(&res, sizeof(res)); - UINTToIP(&res.IpAddress, p->ClientAddressOption.ClientAddress); - UINTToIP(&res.DnsServer1, p->ClientAddressOption.DnsServer); - UINTToIP(&res.DnsServer2, p->ClientAddressOption.DnsServer2); - UINTToIP(&res.WinsServer1, p->ClientAddressOption.WinsServer); - UINTToIP(&res.WinsServer2, p->ClientAddressOption.WinsServer2); + UINTToIP(&client_ip, p->ClientAddressOption.ClientAddress); - if (IPCSetIPv4Parameters(p->Ipc, &res.IpAddress, &subnet, &gw, &p->ClientAddressOption.ClasslessRoute)) + Zero(&cao, sizeof(cao)); + + IPCSetIPv4Parameters(p->Ipc, &client_ip, &subnet, &zero, NULL); + + p->DhcpIpInformTried = true; + + PPPLog(p, "LP_DHCP_INFORM_TRYING"); + + if (IPCDhcpRequestInformIP(p->Ipc, &cao, p->TubeRecv, &client_ip)) { - char client_ip_str[64]; - char subnet_str[64], defgw_str[64]; - char dns1_str[64], dns2_str[64]; - char wins1_str[64], wins2_str[64]; + Debug("IPCDhcpRequestInformIP ok.\n"); + Copy(&p->ClientAddressOption, &cao, sizeof(cao)); + p->ClientAddressOption.ClientAddress = IPToUINT(&client_ip); - // IPv4 parameters have been set for the first time - Debug("Param First Set.\n"); + if (true) + { + char server_ip_str[64]; + char subnet_str[64], defgw_str[64]; + char dns1_str[64], dns2_str[64]; + char wins1_str[64], wins2_str[64]; - IPToStr(client_ip_str, sizeof(client_ip_str), &res.IpAddress); - IPToStr(subnet_str, sizeof(subnet_str), &subnet); - IPToStr(defgw_str, sizeof(defgw_str), &gw); - IPToStr(dns1_str, sizeof(dns1_str), &res.DnsServer1); - IPToStr(dns2_str, sizeof(dns2_str), &res.DnsServer2); - IPToStr(wins1_str, sizeof(wins1_str), &res.WinsServer1); - IPToStr(wins2_str, sizeof(wins2_str), &res.WinsServer2); + IPToStr32(server_ip_str, sizeof(server_ip_str), cao.ServerAddress); + IPToStr32(subnet_str, sizeof(subnet_str), cao.SubnetMask); + IPToStr32(defgw_str, sizeof(defgw_str), cao.Gateway); + IPToStr32(dns1_str, sizeof(dns1_str), cao.DnsServer); + IPToStr32(dns2_str, sizeof(dns2_str), cao.DnsServer2); + IPToStr32(wins1_str, sizeof(wins1_str), cao.WinsServer); + IPToStr32(wins2_str, sizeof(wins2_str), cao.WinsServer2); - PPPLog(p, "LP_SET_IPV4_PARAM", client_ip_str, subnet_str, - defgw_str, dns1_str, dns2_str, wins1_str, wins2_str); + PPPLog(p, "LP_DHCP_INFORM_OK", + subnet_str, defgw_str, cao.DomainName, + dns1_str, dns2_str, wins1_str, wins2_str, + server_ip_str); + } + } + else + { + Debug("IPCDhcpRequestInformIP failed.\n"); + ok = false; + p->DhcpIpInformTried = false; + PPPLog(p, "LP_DHCP_INFORM_NG"); } - PPPSetIPOptionToLCP(&res, req->Lcp, true); + IPCSetIPv4Parameters(p->Ipc, &zero, &zero, &zero, NULL); } - else + } + // Get IP address and additional information from DHCP + else + { + if (p->DhcpIpAllocTried == false) { - // Failed to determine the address - Debug("IP Address Determination Failed.\n"); + DHCP_OPTION_LIST cao; - Zero(&res, sizeof(res)); + Zero(&cao, sizeof(cao)); + p->DhcpIpAllocTried = true; - PPPSetIPOptionToLCP(&res, req->Lcp, true); + PPPLog(p, "LP_DHCP_REQUEST_TRYING"); + + if (IPCDhcpAllocateIP(p->Ipc, &cao, p->TubeRecv)) + { + UINT t; + + Debug("IPCDhcpAllocateIP ok.\n"); + + // IP address has been determined + Copy(&p->ClientAddressOption, &cao, sizeof(cao)); + + p->DhcpAllocated = true; + + // Determine the DHCP update interval + t = cao.LeaseTime; + if (t == 0) + { + t = 600; + } + + t = t / 3; + + if (t == 0) + { + t = 1; + } + + p->DhcpRenewInterval = (UINT64)(t * 1000); + p->DhcpNextRenewTime = Tick64() + p->DhcpRenewInterval; + + if (true) + { + char client_ip_str[64], server_ip_str[64]; + char subnet_str[64], defgw_str[64]; + char dns1_str[64], dns2_str[64]; + char wins1_str[64], wins2_str[64]; + + IPToStr32(client_ip_str, sizeof(client_ip_str), cao.ClientAddress); + IPToStr32(server_ip_str, sizeof(server_ip_str), cao.ServerAddress); + IPToStr32(subnet_str, sizeof(subnet_str), cao.SubnetMask); + IPToStr32(defgw_str, sizeof(defgw_str), cao.Gateway); + IPToStr32(dns1_str, sizeof(dns1_str), cao.DnsServer); + IPToStr32(dns2_str, sizeof(dns2_str), cao.DnsServer2); + IPToStr32(wins1_str, sizeof(wins1_str), cao.WinsServer); + IPToStr32(wins2_str, sizeof(wins2_str), cao.WinsServer2); + + PPPLog(p, "LP_DHCP_REQUEST_OK", + client_ip_str, subnet_str, defgw_str, cao.DomainName, + dns1_str, dns2_str, wins1_str, wins2_str, + server_ip_str, cao.LeaseTime); + } + } + else + { + Debug("IPCDhcpAllocateIP failed.\n"); + p->DhcpIpAllocTried = false; + ok = false; + PPPLog(p, "LP_DHCP_REQUEST_NG"); + } } } } - // Assemble the LCP response packet based on the results - for (i = 0;i < LIST_NUM(req->Lcp->OptionList);i++) + // If we already have a configured IP data - send it along + if (IsValidUnicastIPAddressUINT4(p->ClientAddressOption.ClientAddress) && + p->ClientAddressOption.SubnetMask != 0 && ok) { - PPP_OPTION *t = LIST_DATA(req->Lcp->OptionList, i); + // Success to determine the address + UINTToIP(&subnet, p->ClientAddressOption.SubnetMask); + UINTToIP(&gw, p->ClientAddressOption.Gateway); + + Zero(&res, sizeof(res)); + UINTToIP(&res.IpAddress, p->ClientAddressOption.ClientAddress); + UINTToIP(&res.DnsServer1, p->ClientAddressOption.DnsServer); + UINTToIP(&res.DnsServer2, p->ClientAddressOption.DnsServer2); + UINTToIP(&res.WinsServer1, p->ClientAddressOption.WinsServer); + UINTToIP(&res.WinsServer2, p->ClientAddressOption.WinsServer2); + + if (IPCSetIPv4Parameters(p->Ipc, &res.IpAddress, &subnet, &gw, &p->ClientAddressOption.ClasslessRoute)) + { + char client_ip_str[64]; + char subnet_str[64], defgw_str[64]; + char dns1_str[64], dns2_str[64]; + char wins1_str[64], wins2_str[64]; + + // IPv4 parameters have been set for the first time + Debug("Param First Set.\n"); + + IPToStr(client_ip_str, sizeof(client_ip_str), &res.IpAddress); + IPToStr(subnet_str, sizeof(subnet_str), &subnet); + IPToStr(defgw_str, sizeof(defgw_str), &gw); + IPToStr(dns1_str, sizeof(dns1_str), &res.DnsServer1); + IPToStr(dns2_str, sizeof(dns2_str), &res.DnsServer2); + IPToStr(wins1_str, sizeof(wins1_str), &res.WinsServer1); + IPToStr(wins2_str, sizeof(wins2_str), &res.WinsServer2); + + PPPLog(p, "LP_SET_IPV4_PARAM", client_ip_str, subnet_str, + defgw_str, dns1_str, dns2_str, wins1_str, wins2_str); + } + + /*// Backporting static configuration received from client - let him use whatever he wants, + // he won't accept anything else anyway (as per testing with Windows clients) + if (!IsZeroIP(&o.DnsServer1)) + { + CopyIP(&res.DnsServer1, &o.DnsServer1); + Debug("Setting DNS1 from client\n"); + } + if (!IsZeroIP(&o.DnsServer2)) + { + CopyIP(&res.DnsServer2, &o.DnsServer2); + Debug("Setting DNS2 from client\n"); + } + if (!IsZeroIP(&o.WinsServer1)) + { + CopyIP(&res.WinsServer1, &o.WinsServer1); + Debug("Setting WINS1 from client\n"); + } + if (!IsZeroIP(&o.WinsServer2)) + { + CopyIP(&res.WinsServer2, &o.WinsServer2); + Debug("Setting WINS2 from client\n"); + }*/ + /*if (!IsZeroIP(&res.DnsServer1) && IsZeroIP(&res.DnsServer2)) + { + CopyIP(&res.DnsServer2, &res.DnsServer1); + } + if (!IsZeroIP(&res.WinsServer1) && IsZeroIP(&res.WinsServer2)) + { + CopyIP(&res.WinsServer2, &res.WinsServer1); + }*/ + PPPSetIPOptionToLCP(&res, pp->Lcp, true); + } + // We couldn't configure address for the client + else + { + // Failed to determine the address + Debug("IP Address Determination Failed.\n"); + + Zero(&res, sizeof(res)); + // We will try to reconfigure if we receive another request by wiping all data + Zero(&p->ClientAddressOption, sizeof(DHCP_OPTION_LIST)); + + PPPSetIPOptionToLCP(&res, pp->Lcp, true); + } + + if (PPPRejectLCPOptionsEx(p, pp, processed)) + { + Debug("Rejected IPCP options ID = 0x%x\n", pp->Lcp->Id); + processed = true; + } + + if (ok && PPPNackLCPOptionsEx(p, pp, processed)) + { + Debug("NACKed IPCP options ID = 0x%x\n", pp->Lcp->Id); + processed = true; + } + + // We will delay this packet ACK and send the server IP first, then wait for a reparse + // it is kind of dirty but fixes issues on some clients (namely VPN Client Pro on Android) + if (p->IPv4_State == PPP_PROTO_STATUS_CLOSED && p->ClientAddressOption.ServerAddress != NULL && ok) + { + PPP_LCP* c = NewPPPLCP(PPP_LCP_CODE_REQ, 0); + UINT ui = p->ClientAddressOption.ServerAddress; + Add(c->OptionList, NewPPPOption(PPP_IPCP_OPTION_IP, &ui, sizeof(UINT))); + if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_IPCP, c)) + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + p->IPv4_State = PPP_PROTO_STATUS_CONFIG; + if (!processed) + { + PPPAddNextPacket(p, pp, 1); + } + return false; + } + + // We still haven't received any answer from client about server IP, keep waiting... + if ((p->IPv4_State == PPP_PROTO_STATUS_CONFIG || p->IPv4_State == PPP_PROTO_STATUS_CLOSED) && !processed) + { + PPPAddNextPacket(p, pp, 1); + return false; + } + + //Debug("PPPAckLCPOptionsEx ok=%x, processed=%x", ok, processed); + if (!ok || !PPPAckLCPOptionsEx(p, pp, processed)) + { + return false; + } + Debug("ACKed IPCP options ID = 0x%x\n", pp->Lcp->Id); + + if (ok && p->IPv4_State == PPP_PROTO_STATUS_CONFIG_WAIT) + { + p->IPv4_State = PPP_PROTO_STATUS_OPENED; + Debug("IPv4 OPENED\n"); + } + return ok; +} + +// LCP option based packets utility +bool PPPRejectLCPOptions(PPP_SESSION *p, PPP_PACKET* pp) +{ + return PPPRejectLCPOptionsEx(p, pp, false); +} +bool PPPRejectLCPOptionsEx(PPP_SESSION *p, PPP_PACKET* pp, bool simulate) +{ + UINT i = 0; + bool toBeRejected = false; + PPP_PACKET* ret; + for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++) + { + PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i); if (t->IsSupported == false) { - num_not_supported++; - } - - if (t->IsAccepted == false) - { - num_not_accepted++; + toBeRejected = true; + break; } } - // Create a PPP response packet + if (toBeRejected == false) + { + return false; + } + ret = ZeroMalloc(sizeof(PPP_PACKET)); ret->IsControl = true; - ret->Protocol = req->Protocol; + ret->Protocol = pp->Protocol; + // Return a Reject if there are unsupported parameters + ret->Lcp = NewPPPLCP(PPP_LCP_CODE_REJECT, pp->Lcp->Id); - if (no_return_option_list == false) + for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++) { - // Response by attaching an optional list - if (num_not_supported >= 1) + PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i); + + if (t->IsSupported == false) { - // Return a Reject if there are unsupported parameters - ret->Lcp = NewPPPLCP(PPP_LCP_CODE_REJECT, req->Lcp->Id); - - for (i = 0;i < LIST_NUM(req->Lcp->OptionList);i++) - { - PPP_OPTION *t = LIST_DATA(req->Lcp->OptionList, i); - - if (t->IsSupported == false) - { - // Attach the original option value as is - Add(ret->Lcp->OptionList, NewPPPOption(t->Type, t->Data, t->DataSize)); - } - } - } - else if (num_not_accepted >= 1) - { - // Return a NAK if there are any unacceptable parameter - // even that all parameters are supported - ret->Lcp = NewPPPLCP(PPP_LCP_CODE_NAK, req->Lcp->Id); - - for (i = 0;i < LIST_NUM(req->Lcp->OptionList);i++) - { - PPP_OPTION *t = LIST_DATA(req->Lcp->OptionList, i); - - if (t->IsAccepted == false) - { - // Replace the original option value with an acceptable value - Add(ret->Lcp->OptionList, NewPPPOption(t->Type, t->AltData, t->AltDataSize)); - } - } - } - else - { - // Return an ACK if all parameters are accepted - ret->Lcp = NewPPPLCP(PPP_LCP_CODE_ACK, req->Lcp->Id); - - for (i = 0;i < LIST_NUM(req->Lcp->OptionList);i++) - { - PPP_OPTION *t = LIST_DATA(req->Lcp->OptionList, i); - - // Attach the original option value as is - Add(ret->Lcp->OptionList, NewPPPOption(t->Type, t->Data, t->DataSize)); - } - - if (req->Protocol == PPP_PROTOCOL_LCP) - { - p->ClientLCPOptionDetermined = true; - } - } - } - else - { - // Response without attaching a list of options - ret->Lcp = NewPPPLCP(return_code, req->Lcp->Id); - - if (lcp_ret_data != NULL && lcp_ret_data->Size >= 1) - { - ret->Lcp->Data = Clone(lcp_ret_data->Buf, lcp_ret_data->Size); - ret->Lcp->DataSize = lcp_ret_data->Size; + // Attach the original option value as is + Add(ret->Lcp->OptionList, NewPPPOption(t->Type, t->Data, t->DataSize)); + Debug("Rejected LCP option = 0x%x, proto = 0x%x\n", t->Type, pp->Protocol); } } - if (lcp_ret_data != NULL) + if (LIST_NUM(ret->Lcp->OptionList) == 0 || simulate) { - FreeBuf(lcp_ret_data); + FreePPPPacket(ret); + return false; } - return ret; + PPPSendPacketAndFree(p, ret); + return true; } - -// Set the IP options of PPP to LCP -bool PPPSetIPOptionToLCP(PPP_IPOPTION *o, PPP_LCP *c, bool only_modify) +bool PPPNackLCPOptions(PPP_SESSION *p, PPP_PACKET* pp) { - bool ret = false; - // Validate arguments - if (c == NULL || o == NULL) - { - return false; - } - - ret = PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_IP, &o->IpAddress, only_modify); - - PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_DNS1, &o->DnsServer1, only_modify); - PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_DNS2, &o->DnsServer2, only_modify); - PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_WINS1, &o->WinsServer1, only_modify); - PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_WINS2, &o->WinsServer2, only_modify); - - return ret; + return PPPNackLCPOptionsEx(p, pp, false); } - -// Get the IP options of PPP from LCP -bool PPPGetIPOptionFromLCP(PPP_IPOPTION *o, PPP_LCP *c) +bool PPPNackLCPOptionsEx(PPP_SESSION *p, PPP_PACKET* pp, bool simulate) { - bool ret; - // Validate arguments - if (c == NULL || o == NULL) + UINT i = 0; + PPP_PACKET* ret; + bool toBeNACKed = false; + for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++) { - return false; - } + PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i); - Zero(o, sizeof(PPP_IPOPTION)); - - ret = PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_IP, &o->IpAddress); - - PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_DNS1, &o->DnsServer1); - PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_DNS2, &o->DnsServer2); - PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_WINS1, &o->WinsServer1); - PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_WINS2, &o->WinsServer2); - - return ret; -} - -// Set the IP address data to the option list of the LCP -bool PPPSetIPAddressValueToLCP(PPP_LCP *c, UINT type, IP *ip, bool only_modify) -{ - IP ip2; - UINT ui; - // Validate arguments - if (c == NULL || ip == NULL) - { - return false; - } - - ui = IPToUINT(ip); - - if (PPPGetIPAddressValueFromLCP(c, type, &ip2)) - { - PPP_OPTION *opt; - opt = GetOptionValue(c, type); - - if (opt != NULL) + if (t->IsAccepted == false && t->IsSupported == true) { - if (IsZeroIP(ip) == false) - { - if (CmpIpAddr(&ip2, ip) == 0) - { - // No change - opt->IsAccepted = true; - opt->IsSupported = true; - } - else - { - // Changed - opt->IsAccepted = false; - opt->IsSupported = true; - opt->AltDataSize = 4; - Copy(opt->AltData, &ui, 4); - } - } - else - { - // The parameter itself is not supported - // (if the IP address is 0.0.0.0) - opt->IsSupported = false; - opt->IsAccepted = false; - } - } - - return true; - } - else - { - if (IsZeroIP(ip) == false) - { - // Add as a new item - if (only_modify != false) - { - return false; - } - else - { - PPP_OPTION *opt2 = NewPPPOption(type, &ui, 4); - opt2->IsAccepted = opt2->IsSupported = true; - - Add(c->OptionList, opt2); - - return true; - } - } - else - { - return false; + toBeNACKed = true; + break; } } + + if (toBeNACKed == false) + { + return false; + } + + ret = ZeroMalloc(sizeof(PPP_PACKET)); + ret->IsControl = true; + ret->Protocol = pp->Protocol; + // Return a NAK if there are any unacceptable parameter + // even that all parameters are supported + ret->Lcp = NewPPPLCP(PPP_LCP_CODE_NAK, pp->Lcp->Id); + + for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++) + { + PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i); + + if (t->IsAccepted == false && t->IsSupported == true) + { + // Replace the original option value with an acceptable value + Add(ret->Lcp->OptionList, NewPPPOption(t->Type, t->AltData, t->AltDataSize)); + Debug("NACKed LCP option = 0x%x, proto = 0x%x\n", t->Type, pp->Protocol); + } + } + + if (LIST_NUM(ret->Lcp->OptionList) == 0 || simulate) + { + FreePPPPacket(ret); + return false; + } + + PPPSendPacketAndFree(p, ret); + return true; } - -// Get the IP address data from the option list of the LCP -bool PPPGetIPAddressValueFromLCP(PPP_LCP *c, UINT type, IP *ip) +bool PPPAckLCPOptions(PPP_SESSION *p, PPP_PACKET* pp) { - PPP_OPTION *opt; - UINT ui; - // Validate arguments - if (c == NULL || ip == NULL) + return PPPAckLCPOptionsEx(p, pp, false); +} +bool PPPAckLCPOptionsEx(PPP_SESSION *p, PPP_PACKET* pp, bool simulate) +{ + UINT i = 0; + PPP_PACKET* ret; + bool toBeACKed = false; + for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++) + { + PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i); + + if (t->IsAccepted == true && t->IsSupported == true) + { + toBeACKed = true; + break; + } + } + + if (toBeACKed == false) { return false; } - opt = GetOptionValue(c, type); - if (opt == NULL) + ret = ZeroMalloc(sizeof(PPP_PACKET)); + ret->IsControl = true; + ret->Protocol = pp->Protocol; + // Return an ACK if all parameters are accepted + ret->Lcp = NewPPPLCP(PPP_LCP_CODE_ACK, pp->Lcp->Id); + + for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++) { + PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i); + + if (t->IsAccepted == true && t->IsSupported == true) + { + // Attach the original option value as is + Add(ret->Lcp->OptionList, NewPPPOption(t->Type, t->Data, t->DataSize)); + Debug("ACKed LCP option = 0x%x, proto = 0x%x\n", t->Type, pp->Protocol); + } + } + + if (LIST_NUM(ret->Lcp->OptionList) == 0 || simulate) + { + FreePPPPacket(ret); return false; } - if (opt->DataSize != 4) - { - return false; - } - - opt->IsSupported = true; - - ui = *((UINT *)opt->Data); - - UINTToIP(ip, ui); - + PPPSendPacketAndFree(p, ret); return true; } -// Process corresponding to the incoming request while receiving a PPP packet corresponding to the transmitted request. -// (If req == NULL, process on that protocol while the protocol specified in expected_protocol have received. -//If other protocols has arrived, without further processing, and then store that packet in the session context once, -// return NULL by setting the received_protocol.) -PPP_PACKET *PPPRecvResponsePacket(PPP_SESSION *p, PPP_PACKET *req, USHORT expected_protocol, USHORT *received_protocol, bool finish_when_all_lcp_acked, - bool return_mschapv2_response_with_no_processing) +// PPP networking functions +// Send a request packet in the PPP +bool PPPSendAndRetransmitRequest(PPP_SESSION *p, USHORT protocol, PPP_LCP *c) { - UINT64 giveup_tick = Tick64() + (UINT64)PPP_PACKET_RECV_TIMEOUT; - UINT64 next_resend = Tick64() + (UINT64)PPP_PACKET_RESEND_INTERVAL; - PPP_PACKET *ret = NULL; - USHORT tmp_us = 0; + PPP_PACKET *pp; + UINT64 now = Tick64(); + PPP_REQUEST_RESEND* resend; + // Validate arguments - if (p == NULL || req != NULL && req->Lcp == NULL) + if (p == NULL || c == NULL) { - return NULL; + return false; } - if (received_protocol == NULL) + pp = ZeroMalloc(sizeof(PPP_PACKET)); + pp->Protocol = protocol; + pp->IsControl = true; + pp->Lcp = c; + if (pp->Lcp->Id == 0) { - received_protocol = &tmp_us; + pp->Lcp->Id = p->NextId++; } - if (req != NULL) + // Send the PPP packet + if (!PPPSendPacketEx(p, pp, false)) { - expected_protocol = req->Protocol; + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; } - *received_protocol = 0; + resend = ZeroMalloc(sizeof(PPP_REQUEST_RESEND)); + resend->Id = pp->Lcp->Id; + resend->Packet = pp; + resend->ResendTime = now + PPP_PACKET_RESEND_INTERVAL; + resend->TimeoutTime = now + PPP_PACKET_RECV_TIMEOUT; - // Receive the next packet (Retransmission repeatedly the last packet until the reception is completed) - while (true) - { - UINT64 now = Tick64(); - UINT interval; + Add(p->SentReqPacketList, resend); - if (IsTubeConnected(p->TubeRecv) == false) - { - return NULL; - } - - while (true) - { - PPP_PACKET *pp; - PPP_PACKET *response; - - if (p->LastStoredPacket != NULL) - { - pp = p->LastStoredPacket; - p->LastStoredPacket = NULL; - } - else - { - pp = PPPRecvPacketWithLowLayerProcessing(p, true); - } - - if (pp == NULL) - { - break; - } - - if (req != NULL) - { - // Determine whether the packet is corresponding to the request that was sent at the last - if (pp->IsControl && pp->Protocol == req->Protocol && pp->Lcp->Id == req->Lcp->Id && - PPP_CODE_IS_RESPONSE(pp->Protocol, pp->Lcp->Code)) - { - return pp; - } - - if (return_mschapv2_response_with_no_processing) - { - // For the double-MSCHAPv2 technique - if (pp->IsControl && pp->Protocol == req->Protocol && pp->Lcp->Id == req->Lcp->Id && - pp->Protocol == PPP_PROTOCOL_CHAP && PPP_PAP_CODE_IS_RESPONSE(pp->Lcp->Code)) - { - return pp; - } - } - } - - // Return a response immediately without processing if a protocol other than the expected received - if ((pp->IsControl && pp->Protocol != expected_protocol) || pp->IsControl == false) - { - if (PPP_IS_SUPPORTED_PROTOCOL(pp->Protocol)) - { - // This is another supported protocol - // Store this packet - PPPStoreLastPacket(p, pp); - - *received_protocol = pp->Protocol; - return NULL; - } - else - { - // Unsupported protocol - Debug("Unsupported Protocol: 0x%x\n", pp->Protocol); - FreePPPPacket(pp); - - return NULL; - } - } - - if (pp->IsControl && PPP_CODE_IS_REQUEST(pp->Protocol, pp->Lcp->Code)) - { - // Record current resend because next steps may take a while - UINT64 currentresend = next_resend - now; - - // Process when the received packet is a request packet - response = PPPProcessRequestPacket(p, pp); - - // Increase next resend because this may have taken a while - next_resend = Tick64() + currentresend; - - FreePPPPacket(pp); - - if (response == NULL) - { - return NULL; - } - else - { - bool is_pap_and_disconnect_now = false; - bool is_chap_and_disconnect_now = false; - - if (PPPSendPacket(p, response) == false) - { - FreePPPPacket(response); - return NULL; - } - - if (response->Protocol == PPP_PROTOCOL_PAP && response->IsControl && - response->Lcp->Code != PPP_PAP_CODE_ACK) - { - is_pap_and_disconnect_now = true; - } - - if (response->Protocol == PPP_PROTOCOL_CHAP && response->IsControl && - response->Lcp->Code == PPP_CHAP_CODE_FAILURE) - { - is_chap_and_disconnect_now = true; - } - - FreePPPPacket(response); - - if (is_pap_and_disconnect_now) - { - // Disconnect immediately if user authentication fails at least once in the PAP authentication protocol - Debug("Disconnecting because PAP failed.\n"); - SleepThread(300); - return NULL; - } - - if (is_chap_and_disconnect_now) - { - // Disconnect immediately if it fails to user authentication at least once in the CHAP authentication protocol - Debug("Disconnecting because CHAP failed.\n"); - SleepThread(300); - return NULL; - } - } - } - else - { - // Ignore in the case of the other packets - FreePPPPacket(pp); - } - } - - // Packet retransmission - if (req != NULL) - { - if (now >= next_resend) - { - next_resend = now + PPP_PACKET_RESEND_INTERVAL; - - if (PPPSendPacket(p, req) == false) - { - return NULL; - } - } - } - - if (req == NULL) - { - giveup_tick = now + (UINT64)PPP_PACKET_RECV_TIMEOUT; - } - - // Time-out decision - if (now >= giveup_tick) - { - PPPLog(p, "LP_CONTROL_TIMEOUT"); - return NULL; - } - - // Wait - if (req != NULL) - { - interval = MIN((UINT)(giveup_tick - now), (UINT)(next_resend - now)); - } - else - { - interval = (UINT)(giveup_tick - now); - } - - if (finish_when_all_lcp_acked && p->ClientLCPOptionDetermined) - { - return NULL; - } - - Wait(p->TubeRecv->Event, interval); - } + return true; } - -// Store the last packet in the session (to be read the next time) -void PPPStoreLastPacket(PPP_SESSION *p, PPP_PACKET *pp) +// Send the PPP packet and frees the sent packet +bool PPPSendPacketAndFree(PPP_SESSION *p, PPP_PACKET *pp) { - // Validate arguments - if (p == NULL) - { - return; - } - - if (p->LastStoredPacket != NULL) - { - FreePPPPacket(p->LastStoredPacket); - } - - p->LastStoredPacket = pp; + bool result = PPPSendPacketEx(p, pp, false); + FreePPPPacket(pp); + return result; } - -// Receive a PPP communication packet -PPP_PACKET *PPPRecvPacketForCommunication(PPP_SESSION *p) +// Send the PPP packet +bool PPPSendPacketEx(PPP_SESSION *p, PPP_PACKET *pp, bool no_flush) { + bool ret = false; + BUF *b; // Validate arguments - if (p == NULL) + if (p == NULL || pp == NULL) { - return NULL; + return false; } - if (p->LastStoredPacket != NULL) + b = BuildPPPPacketData(pp); + if (b == NULL) { - PPP_PACKET *pp = p->LastStoredPacket; - p->LastStoredPacket = NULL; - return pp; + return false; } - return PPPRecvPacketWithLowLayerProcessing(p, true); -} + ret = TubeSendEx(p->TubeSend, b->Buf, b->Size, NULL, no_flush); -// Receive a PPP packet (Also performs low layer processing) -PPP_PACKET *PPPRecvPacketWithLowLayerProcessing(PPP_SESSION *p, bool async) -{ - PPP_PACKET *pp = NULL; - // Validate arguments - if (p == NULL) + if (no_flush) { - return NULL; + AddTubeToFlushList(p->FlushList, p->TubeSend); } -LABEL_LOOP: - pp = PPPRecvPacket(p, async); - if (pp == NULL) - { - return NULL; - } + FreeBuf(b); - if (PPP_IS_SUPPORTED_PROTOCOL(pp->Protocol) == false) - { - // Unsupported algorithm - PPP_PACKET *pp2 = ZeroMalloc(sizeof(PPP_PACKET)); - BUF *buf; - UCHAR c; - USHORT us; - - pp2->Protocol = PPP_PROTOCOL_LCP; - pp2->IsControl = false; - - buf = NewBuf(); - - // Code - c = PPP_LCP_CODE_PROTOCOL_REJECT; - WriteBuf(buf, &c, 1); - - // ID - c = p->NextId++; - WriteBuf(buf, &c, 1); - - // Length - us = Endian16(pp->DataSize + 6); - WriteBuf(buf, &us, 2); - - // Rejected Protocol - us = Endian16(pp->Protocol); - WriteBuf(buf, &us, 2); - - // Packet Data - WriteBuf(buf, pp->Data, pp->DataSize); - - pp2->Data = Clone(buf->Buf, buf->Size); - pp2->DataSize = buf->Size; - - FreePPPPacket(pp); - - FreeBuf(buf); - - if (PPPSendPacket(p, pp2) == false) - { - FreePPPPacket(pp2); - return NULL; - } - - FreePPPPacket(pp2); - goto LABEL_LOOP; - } - - if (pp->IsControl && pp->Protocol == PPP_PROTOCOL_LCP) - { - if (pp->Lcp->Code == PPP_LCP_CODE_ECHO_REQUEST) - { - // Immediately return the echo response to the echo request - PPP_PACKET *pp2 = ZeroMalloc(sizeof(PPP_PACKET)); - - pp2->IsControl = true; - pp2->Protocol = PPP_PROTOCOL_LCP; - pp2->Lcp = NewPPPLCP(PPP_LCP_CODE_ECHO_RESPONSE, pp->Lcp->Id); - pp2->Lcp->Data = Clone(pp->Lcp->Data, pp->Lcp->DataSize); - pp2->Lcp->DataSize = pp->Lcp->DataSize; - - FreePPPPacket(pp); - - if (PPPSendPacket(p, pp2) == false) - { - FreePPPPacket(pp2); - return NULL; - } - - FreePPPPacket(pp2); - goto LABEL_LOOP; - } - else if (pp->Lcp->Code == PPP_LCP_CODE_ECHO_RESPONSE) - { - // Ignore the Echo response packet - FreePPPPacket(pp); - goto LABEL_LOOP; - } - else if (pp->Lcp->Code == PPP_LCP_CODE_DROP) - { - // Ignore the Drop packet - FreePPPPacket(pp); - goto LABEL_LOOP; - } - else if (pp->Lcp->Code == PPP_LCP_CODE_IDENTIFICATION) - { - // Ignore the Identification packet - FreePPPPacket(pp); - WHERE; - goto LABEL_LOOP; - } - else if (pp->Lcp->Code == PPP_LCP_CODE_TERMINATE_REQ) - { - // Return the Terminate ACK If a Terminate Request has been received - PPP_PACKET *pp2 = ZeroMalloc(sizeof(PPP_PACKET)); - - pp2->IsControl = true; - pp2->Protocol = PPP_PROTOCOL_LCP; - pp2->Lcp = NewPPPLCP(PPP_LCP_CODE_TERMINATE_ACK, pp->Lcp->Id); - pp2->Lcp->Data = Clone(pp->Lcp->Data, pp->Lcp->DataSize); - pp2->Lcp->DataSize = pp->Lcp->DataSize; - - p->IsTerminateReceived = true; - - FreePPPPacket(pp); - - if (PPPSendPacket(p, pp2) == false) - { - FreePPPPacket(pp2); - return NULL; - } - - SleepThread(100); - - FreePPPPacket(pp2); - goto LABEL_LOOP; - } - } - - return pp; + return ret; } // Receive a PPP packet @@ -2075,39 +2054,212 @@ LABEL_LOOP: return pp; } -// Send the PPP packet -bool PPPSendPacket(PPP_SESSION *p, PPP_PACKET *pp) +PPP_PACKET *PPPGetNextPacket(PPP_SESSION *p) { - return PPPSendPacketEx(p, pp, false); -} -bool PPPSendPacketEx(PPP_SESSION *p, PPP_PACKET *pp, bool no_flush) -{ - bool ret = false; - BUF *b; - // Validate arguments - if (p == NULL || pp == NULL) + PPP_PACKET* ret = NULL; + UINT i = 0; + if (p->CurrentPacket != NULL) { - return false; + FreePPPPacket(p->CurrentPacket); + p->CurrentPacket = NULL; + } + for (i = 0; i < LIST_NUM(p->DelayedPackets); i++) + { + PPP_DELAYED_PACKET* t = LIST_DATA(p->DelayedPackets, i); + if (t->DelayTicks > 0) + { + t->DelayTicks--; + } + else + { + ret = t->Packet; + Delete(p->DelayedPackets, t); + Free(t); + break; + } } - b = BuildPPPPacketData(pp); - if (b == NULL) + if (ret != NULL) { - return false; + p->CurrentPacket = ret; + return ret; } - ret = TubeSendEx(p->TubeSend, b->Buf, b->Size, NULL, no_flush); - - if (no_flush) + ret = PPPRecvPacket(p, true); + + if (ret != NULL && ret->IsControl && ret->Lcp != NULL) { - AddTubeToFlushList(p->FlushList, p->TubeSend); + PPP_DELAYED_PACKET* firstRelated = NULL; + for (i = 0; i < LIST_NUM(p->DelayedPackets); i++) + { + PPP_DELAYED_PACKET* t = LIST_DATA(p->DelayedPackets, i); + char related = PPPRelatedPacketComparator(ret, t->Packet); + if (related != 0xF && related != 0xE) + { + if (related == 0) + { + // It's the same packet, just remove it and wait for it's delays + FreePPPPacket(ret); + firstRelated = NULL; + ret = NULL; + break; + } + if (related == 1) + { + // We got a packet which should come later than any of delayed ones + PPPAddNextPacket(p, ret, t->DelayTicks); + firstRelated = NULL; + ret = NULL; + break; + } + if (related == -1) + { + char prevFoundRelated = -1; + if (firstRelated != NULL) + { + prevFoundRelated = PPPRelatedPacketComparator(t->Packet, firstRelated->Packet); + } + if (prevFoundRelated == -1) + { + firstRelated = t; + } + } + } + } + + if (firstRelated != NULL) + { + PPPAddNextPacket(p, ret, firstRelated->DelayTicks); + ret = NULL; + } } - FreeBuf(b); - + p->CurrentPacket = ret; return ret; } +void PPPAddNextPacket(PPP_SESSION *p, PPP_PACKET *pp, UINT delay) +{ + PPP_DELAYED_PACKET* t = ZeroMalloc(sizeof(PPP_DELAYED_PACKET)); + UINT i; + if (p->CurrentPacket == pp) + { + p->CurrentPacket = NULL; + } + t->Packet = pp; + t->DelayTicks = delay; + Add(p->DelayedPackets, t); + Sort(p->DelayedPackets); + /*Debug("after sorting delayeds\n"); + for (i = 0; i < LIST_NUM(p->DelayedPackets); i++) + { + t = LIST_DATA(p->DelayedPackets, i); + if (t->Packet->Lcp != NULL) + { + Debug("> Packet proto = 0x%x, id = 0x%x, code = 0x%x, delay = 0x%x\n", t->Packet->Protocol, t->Packet->Lcp->Id, t->Packet->Lcp->Code, t->DelayTicks); + } + } + Debug("after sorting delayeds end\n");*/ +} + +int PPPDelayedPacketsComparator(const void* a, const void* b) +{ + PPP_DELAYED_PACKET* first = a; + PPP_DELAYED_PACKET* second = b; + + char related = PPPRelatedPacketComparator(first->Packet, second->Packet); + + if (related == 0xF || related == 0xE) + { + if (first->DelayTicks < second->DelayTicks) + { + return -1; + } + if (first->DelayTicks > second->DelayTicks) + { + return 1; + } + return 0; + } + + // We make all delay ticks to be accounted with the sorting + if (related <= 1 && related >= -1) + { + if (related == -1 && first->DelayTicks >= second->DelayTicks) + { + second->DelayTicks = first->DelayTicks; + second->DelayTicks++; + } + else if (related == 1 && first->DelayTicks <= second->DelayTicks) + { + first->DelayTicks = second->DelayTicks; + first->DelayTicks++; + } + return related; + } + return 0; +} + +// -1 - packet a comes before packet b +// 0 - this is the same packet +// 1 - packet a comes after packet b +// 0xF - packet is not related +// 0xE - we got an error while comparing, treating as not related would be the most correct +char PPPRelatedPacketComparator(PPP_PACKET* a, PPP_PACKET* b) +{ + if (a->IsControl && b->IsControl && + a->Lcp != NULL && b->Lcp != NULL && + a->Protocol == b->Protocol && + PPP_CODE_IS_REQUEST(a->Protocol, a->Lcp->Code) == PPP_CODE_IS_REQUEST(b->Protocol, b->Lcp->Code) && + PPP_CODE_IS_RESPONSE(a->Protocol, a->Lcp->Code) == PPP_CODE_IS_RESPONSE(b->Protocol, b->Lcp->Code)) + { + // The packet is related! + if (a->Lcp->Id < b->Lcp->Id) + { + return -1; + } + else if (a->Lcp->Id == b->Lcp->Id) + { + if (a->Lcp->Code == b->Lcp->Code) + { + return 0; + } + else + { + return 0xE; + } + } + else if (a->Lcp->Id > b->Lcp->Id) + { + return 1; + } + else + { + return 0xE; + } + } + else + { + // The packet is not related! + return 0xF; + } +} + +// PPP utility functions +// Packet structure creation utilities + +// Create the LCP +PPP_LCP *NewPPPLCP(UCHAR code, UCHAR id) +{ + PPP_LCP *c = ZeroMalloc(sizeof(PPP_LCP)); + + c->Code = code; + c->Id = id; + c->OptionList = NewListFast(NULL); + + return c; +} + // Create a new PPP options PPP_OPTION *NewPPPOption(UCHAR type, void *data, UINT size) { @@ -2127,6 +2279,8 @@ PPP_OPTION *NewPPPOption(UCHAR type, void *data, UINT size) return o; } +// Packet parse utilities + // Analyse the PPP packet PPP_PACKET *ParsePPPPacket(void *data, UINT size) { @@ -2172,10 +2326,11 @@ PPP_PACKET *ParsePPPPacket(void *data, UINT size) goto LABEL_ERROR; } pp->Protocol = READ_USHORT(buf); + size -= 2; buf += 2; - if (pp->Protocol == PPP_PROTOCOL_LCP || pp->Protocol == PPP_PROTOCOL_PAP || pp->Protocol == PPP_PROTOCOL_CHAP || pp->Protocol == PPP_PROTOCOL_IPCP) + if (pp->Protocol == PPP_PROTOCOL_LCP || pp->Protocol == PPP_PROTOCOL_PAP || pp->Protocol == PPP_PROTOCOL_CHAP || pp->Protocol == PPP_PROTOCOL_IPCP || pp->Protocol == PPP_PROTOCOL_IPV6CP) { pp->IsControl = true; } @@ -2185,7 +2340,7 @@ PPP_PACKET *ParsePPPPacket(void *data, UINT size) if (pp->IsControl) { - pp->Lcp = ParseLCP(pp->Protocol, pp->Data, pp->DataSize); + pp->Lcp = PPPParseLCP(pp->Protocol, pp->Data, pp->DataSize); if (pp->Lcp == NULL) { goto LABEL_ERROR; @@ -2199,107 +2354,8 @@ LABEL_ERROR: return NULL; } -// Build a PPP packet data -BUF *BuildPPPPacketData(PPP_PACKET *pp) -{ - BUF *ret; - UCHAR c; - USHORT us; - // Validate arguments - if (pp == NULL) - { - return NULL; - } - - ret = NewBuf(); - - // Address - c = 0xff; - WriteBuf(ret, &c, 1); - - // Control - c = 0x03; - WriteBuf(ret, &c, 1); - - // Protocol - us = Endian16(pp->Protocol); - WriteBuf(ret, &us, 2); - - if (pp->IsControl) - { - // LCP - BUF *b = BuildLCPData(pp->Lcp); - - WriteBufBuf(ret, b); - - FreeBuf(b); - } - else - { - // Data - WriteBuf(ret, pp->Data, pp->DataSize); - } - - SeekBuf(ret, 0, 0); - - return ret; -} - -// Build the LCP packet data -BUF *BuildLCPData(PPP_LCP *c) -{ - BUF *b; - UCHAR zero = 0; - UINT i; - // Validate arguments - if (c == NULL) - { - return NULL; - } - - b = NewBuf(); - - // Code - WriteBuf(b, &c->Code, 1); - - // ID - WriteBuf(b, &c->Id, 1); - - // Length (to be updated later) - zero = 0; - WriteBuf(b, &zero, 1); - WriteBuf(b, &zero, 1); - - if (c->Data == NULL) - { - // Option List - for (i = 0;i < LIST_NUM(c->OptionList);i++) - { - PPP_OPTION *o = LIST_DATA(c->OptionList, i); - UCHAR sz = o->DataSize + 2; - - WriteBuf(b, &o->Type, 1); - WriteBuf(b, &sz, 1); - - WriteBuf(b, o->Data, o->DataSize); - } - } - else - { - // Data - WriteBuf(b, c->Data, c->DataSize); - } - - SeekBuf(b, 0, 0); - - // Update Length - WRITE_USHORT(((UCHAR *)b->Buf) + 2, b->Size); - - return b; -} - // Analyse the LCP data -PPP_LCP *ParseLCP(USHORT protocol, void *data, UINT size) +PPP_LCP *PPPParseLCP(USHORT protocol, void *data, UINT size) { UCHAR *buf; PPP_LCP *c; @@ -2413,38 +2469,535 @@ LABEL_ERROR: return NULL; } -// Release the PPP packet -void FreePPPPacket(PPP_PACKET *pp) +// Analyse MS CHAP v2 Response packet +bool PPPParseMSCHAP2ResponsePacket(PPP_SESSION* p, PPP_PACKET* pp) { - FreePPPPacketEx(pp, false); + bool ok = false; + + char client_ip_tmp[256]; + EAP_CLIENT *eap; + + UCHAR client_response_buffer[49]; + UCHAR *client_challenge_16; + UCHAR *client_response_24; + char username_tmp[MAX_SIZE]; + IPC *ipc = NULL; + char id[MAX_SIZE]; + char hub[MAX_SIZE]; + char password[MAX_SIZE]; + char server_challenge_hex[MAX_SIZE]; + char client_challenge_hex[MAX_SIZE]; + char client_response_hex[MAX_SIZE]; + char eap_client_hex[64]; + ETHERIP_ID d; + UINT error_code; + UINT64 eap_client_ptr = (UINT64)p->EapClient; + + if (pp->Lcp != NULL && pp->Lcp->DataSize >= 51) + { + BUF *b; + if (pp->Lcp->Id != p->MsChapV2_PacketId) + { + Debug("Got incorrect LCP PacketId! Should be 0x%x, got 0x%x\n", p->MsChapV2_PacketId, pp->Lcp->Id); + p->MsChapV2_PacketId = pp->Lcp->Id; + } + + b = NewBuf(); + + WriteBuf(b, pp->Lcp->Data, pp->Lcp->DataSize); + SeekBuf(b, 0, 0); + + if (ReadBufChar(b) == 49) + { + ReadBuf(b, client_response_buffer, 49); + + Zero(username_tmp, sizeof(username_tmp)); + ReadBuf(b, username_tmp, sizeof(username_tmp) - 1); + Debug("MS-CHAPv2: id=%s\n", username_tmp); + + client_challenge_16 = client_response_buffer + 0; + client_response_24 = client_response_buffer + 16 + 8; + + Copy(p->MsChapV2_ClientChallenge, client_challenge_16, 16); + Copy(p->MsChapV2_ClientResponse, client_response_24, 24); + + Zero(id, sizeof(id)); + Zero(hub, sizeof(hub)); + + // The user name is divided into the ID and the virtual HUB name + Zero(&d, sizeof(d)); + PPPParseUsername(p->Cedar, username_tmp, &d); + + StrCpy(id, sizeof(id), d.UserName); + StrCpy(hub, sizeof(hub), d.HubName); + Debug("MS-CHAPv2: username=%s, hubname=%s\n", id, hub); + + IPToStr(client_ip_tmp, sizeof(client_ip_tmp), &p->ClientIP); + + // Convert the MS-CHAPv2 data to a password string + BinToStr(server_challenge_hex, sizeof(server_challenge_hex), + p->MsChapV2_ServerChallenge, sizeof(p->MsChapV2_ServerChallenge)); + BinToStr(client_challenge_hex, sizeof(client_challenge_hex), + p->MsChapV2_ClientChallenge, sizeof(p->MsChapV2_ClientChallenge)); + BinToStr(client_response_hex, sizeof(client_response_hex), + p->MsChapV2_ClientResponse, sizeof(p->MsChapV2_ClientResponse)); + BinToStr(eap_client_hex, sizeof(eap_client_hex), + &eap_client_ptr, 8); + + Format(password, sizeof(password), "%s%s:%s:%s:%s:%s", + IPC_PASSWORD_MSCHAPV2_TAG, + username_tmp, + server_challenge_hex, + client_challenge_hex, + client_response_hex, + eap_client_hex); + + if (p->MsChapV2_UseDoubleMsChapV2 && p->EapClient == NULL) + { + Debug("Double MSCHAPv2 creating EAP client\n"); + eap = HubNewEapClient(p->Cedar, hub, client_ip_tmp, id); + + if (eap) + { + ok = true; + p->EapClient = eap; + } + else + { + PPPSetStatus(p, PPP_STATUS_FAIL); + WHERE; + return false; + } + } + else if (p->Ipc == NULL) + { + Debug("MSCHAPv2 creating IPC\n"); + ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, hub, id, password, + &error_code, &p->ClientIP, p->ClientPort, &p->ServerIP, p->ServerPort, + p->ClientHostname, p->CryptName, false, p->AdjustMss, p->EapClient); + + if (ipc != NULL) + { + p->Ipc = ipc; + + Copy(p->MsChapV2_ServerResponse, ipc->MsChapV2_ServerResponse, 20); + + ok = true; + } + } + else + { + Debug("Got weird packet when we already have an active IPC! Ipc = 0x%x, AuthOk = 0x%x, Status = 0x%x\n", p->Ipc, p->AuthOk, p->PPPStatus); + ok = p->AuthOk; + } + } + + FreeBuf(b); + } + else + { + Debug("Got invalid MSCHAPv2 packet\n"); + } + + return ok; } -void FreePPPPacketEx(PPP_PACKET *pp, bool no_free_struct) + +// Packet building utilities + +// Build a PPP packet data +BUF *BuildPPPPacketData(PPP_PACKET *pp) { + BUF *ret; + UCHAR c; + USHORT us; // Validate arguments if (pp == NULL) { - return; + return NULL; } - FreePPPLCP(pp->Lcp); + ret = NewBuf(); - Free(pp->Data); + // Address + c = 0xff; + WriteBuf(ret, &c, 1); - if (no_free_struct == false) + // Control + c = 0x03; + WriteBuf(ret, &c, 1); + + // Protocol + us = Endian16(pp->Protocol); + WriteBuf(ret, &us, 2); + + if (pp->IsControl) { - Free(pp); + // LCP + BUF *b = BuildLCPData(pp->Lcp); + + WriteBufBuf(ret, b); + + FreeBuf(b); + } + else + { + // Data + WriteBuf(ret, pp->Data, pp->DataSize); + } + + SeekBuf(ret, 0, 0); + + return ret; +} + +// Build the LCP packet data +BUF *BuildLCPData(PPP_LCP *c) +{ + BUF *b; + UCHAR zero = 0; + UINT i; + // Validate arguments + if (c == NULL) + { + return NULL; + } + + b = NewBuf(); + + // Code + WriteBuf(b, &c->Code, 1); + + // ID + WriteBuf(b, &c->Id, 1); + + // Length (to be updated later) + zero = 0; + WriteBuf(b, &zero, 1); + WriteBuf(b, &zero, 1); + + if (c->Data == NULL) + { + // Option List + for (i = 0; i < LIST_NUM(c->OptionList); i++) + { + PPP_OPTION *o = LIST_DATA(c->OptionList, i); + UCHAR sz = o->DataSize + 2; + + WriteBuf(b, &o->Type, 1); + WriteBuf(b, &sz, 1); + + WriteBuf(b, o->Data, o->DataSize); + } + } + else + { + // Data + WriteBuf(b, c->Data, c->DataSize); + } + + SeekBuf(b, 0, 0); + + // Update Length + WRITE_USHORT(((UCHAR *)b->Buf) + 2, b->Size); + + return b; +} + +// Build the MS CHAP v2 challenge packet +PPP_LCP *BuildMSCHAP2ChallengePacket(PPP_SESSION* p) +{ + PPP_LCP *lcp; + BUF *b; + char machine_name[MAX_SIZE]; + UINT64 now = Tick64(); + + // Generate a Server Challenge packet of MS-CHAP v2 + GetMachineHostName(machine_name, sizeof(machine_name)); + + if (p->EapClient == NULL) + { + MsChapV2Server_GenerateChallenge(p->MsChapV2_ServerChallenge); + } + else + { + Copy(p->MsChapV2_ServerChallenge, p->EapClient->MsChapV2Challenge.Chap_ChallengeValue, 16); + } + + p->MsChapV2_PacketId = p->NextId++; + lcp = NewPPPLCP(PPP_CHAP_CODE_CHALLENGE, p->MsChapV2_PacketId); + + b = NewBuf(); + WriteBufChar(b, 16); + WriteBuf(b, p->MsChapV2_ServerChallenge, sizeof(p->MsChapV2_ServerChallenge)); + WriteBuf(b, machine_name, StrLen(machine_name)); + lcp->Data = Clone(b->Buf, b->Size); + lcp->DataSize = b->Size; + FreeBuf(b); + + Debug("Building MS-CHAP v2 Challenge\n"); + + return lcp; +} + +// IPCP packet utilities + +// Set the IP options of PPP to LCP +bool PPPSetIPOptionToLCP(PPP_IPOPTION *o, PPP_LCP *c, bool only_modify) +{ + bool ret = false; + // Validate arguments + if (c == NULL || o == NULL) + { + return false; + } + + ret = PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_IP, &o->IpAddress, only_modify); + + PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_DNS1, &o->DnsServer1, only_modify); + PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_DNS2, &o->DnsServer2, only_modify); + PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_WINS1, &o->WinsServer1, only_modify); + PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_WINS2, &o->WinsServer2, only_modify); + + return ret; +} + +// Get the IP options of PPP from LCP +bool PPPGetIPOptionFromLCP(PPP_IPOPTION *o, PPP_LCP *c) +{ + bool ret; + // Validate arguments + if (c == NULL || o == NULL) + { + return false; + } + + Zero(o, sizeof(PPP_IPOPTION)); + + ret = PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_IP, &o->IpAddress); + + PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_DNS1, &o->DnsServer1); + PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_DNS2, &o->DnsServer2); + PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_WINS1, &o->WinsServer1); + PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_WINS2, &o->WinsServer2); + + return ret; +} + +// Set the IP address data to the option list of the LCP +bool PPPSetIPAddressValueToLCP(PPP_LCP *c, UINT type, IP *ip, bool only_modify) +{ + IP ip2; + UINT ui; + // Validate arguments + if (c == NULL || ip == NULL) + { + return false; + } + + ui = IPToUINT(ip); + + if (PPPGetIPAddressValueFromLCP(c, type, &ip2)) + { + PPP_OPTION *opt; + opt = PPPGetOptionValue(c, type); + + if (opt != NULL) + { + if (IsZeroIP(ip) == false) + { + if (CmpIpAddr(&ip2, ip) == 0) + { + // No change + opt->IsAccepted = true; + opt->IsSupported = true; + } + else + { + // Changed + opt->IsAccepted = false; + opt->IsSupported = true; + opt->AltDataSize = 4; + Copy(opt->AltData, &ui, 4); + } + } + else + { + // The parameter itself is not supported + // (if the IP address is 0.0.0.0) + opt->IsSupported = false; + opt->IsAccepted = false; + } + } + + return true; + } + else + { + if (IsZeroIP(ip) == false) + { + // Add as a new item + if (only_modify != false) + { + return false; + } + else + { + PPP_OPTION *opt2 = NewPPPOption(type, &ui, 4); + UCHAR ipstr[MAX_SIZE]; + + opt2->IsAccepted = true; + opt2->IsSupported = true; + Copy(opt2->AltData, opt2->Data, opt2->DataSize); + opt2->AltDataSize = opt2->DataSize; + + Add(c->OptionList, opt2); + IPToStr(ipstr, MAX_SIZE, ip); + + return true; + } + } + else + { + return false; + } } } +// Get the IP address data from the option list of the LCP +bool PPPGetIPAddressValueFromLCP(PPP_LCP *c, UINT type, IP *ip) +{ + PPP_OPTION *opt; + UINT ui; + // Validate arguments + if (c == NULL || ip == NULL) + { + return false; + } + + opt = PPPGetOptionValue(c, type); + if (opt == NULL) + { + return false; + } + + if (opt->DataSize != 4) + { + return false; + } + + opt->IsSupported = true; + + ui = *((UINT *)opt->Data); + + UINTToIP(ip, ui); + + return true; +} + +// Other packet utilities + +// Get the option value +PPP_OPTION *PPPGetOptionValue(PPP_LCP *c, UCHAR type) +{ + UINT i; + // Validate arguments + if (c == NULL) + { + return NULL; + } + + for (i = 0; i < LIST_NUM(c->OptionList); i++) + { + PPP_OPTION *t = LIST_DATA(c->OptionList, i); + + if (t->Type == type) + { + return t; + } + } + + return NULL; +} + +// Check whether the Virtual HUB with the specified name exist? +bool IsHubExistsWithLock(CEDAR *cedar, char *hubname) +{ + bool ret = false; + // Validate arguments + if (cedar == NULL || hubname == NULL) + { + return false; + } + + LockList(cedar->HubList); + { + ret = IsHub(cedar, hubname); + } + UnlockList(cedar->HubList); + + return ret; +} + +// Sets the PPP status without overwriting the FAIL status +void PPPSetStatus(PPP_SESSION *p, UINT status) +{ + if (status == PPP_STATUS_FAIL) + { + Debug("SETTING PPP_STATUS_FAIL!!!\n"); + } + if (!PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus) || PPP_STATUS_IS_UNAVAILABLE(status)) + { + p->PPPStatus = status; + } +} + + +// Memory freeing functions + // Release the PPP session void FreePPPSession(PPP_SESSION *p) { + UINT i; // Validate arguments if (p == NULL) { return; } + // Release the memory + for (i = 0; i < LIST_NUM(p->RecvPacketList); i++) + { + PPP_PACKET *pp = LIST_DATA(p->RecvPacketList, i); + + FreePPPPacket(pp); + } + ReleaseList(p->RecvPacketList); + + for (i = 0; i < LIST_NUM(p->SentReqPacketList); i++) + { + PPP_REQUEST_RESEND *t = LIST_DATA(p->SentReqPacketList, i); + FreePPPPacket(t->Packet); + + Free(t); + } + + ReleaseList(p->SentReqPacketList); + + for (i = 0; i < LIST_NUM(p->DelayedPackets); i++) + { + PPP_DELAYED_PACKET *t = LIST_DATA(p->DelayedPackets, i); + FreePPPPacket(t->Packet); + + Free(t); + } + + ReleaseList(p->DelayedPackets); + + if (p->CurrentPacket != NULL) + { + FreePPPPacket(p->CurrentPacket); + } + if (p->TubeRecv != NULL) { // Record the PPP disconnect reason code for L2TP @@ -2462,8 +3015,6 @@ void FreePPPSession(PPP_SESSION *p) ReleaseTube(p->TubeRecv); ReleaseTube(p->TubeSend); - PPPStoreLastPacket(p, NULL); - if (p->Ipc != NULL) { FreeIPC(p->Ipc); @@ -2474,56 +3025,6 @@ void FreePPPSession(PPP_SESSION *p) Free(p); } -// Free the associated EAP client -void PPPFreeEapClient(PPP_SESSION *p) -{ - if (p == NULL) - { - return; - } - - if (p->EapClient != NULL) - { - ReleaseEapClient(p->EapClient); - p->EapClient = NULL; - } -} - -// Get the option value -PPP_OPTION *GetOptionValue(PPP_LCP *c, UCHAR type) -{ - UINT i; - // Validate arguments - if (c == NULL) - { - return NULL; - } - - for (i = 0;i < LIST_NUM(c->OptionList);i++) - { - PPP_OPTION *t = LIST_DATA(c->OptionList, i); - - if (t->Type == type) - { - return t; - } - } - - return NULL; -} - -// Create the LCP -PPP_LCP *NewPPPLCP(UCHAR code, UCHAR id) -{ - PPP_LCP *c = ZeroMalloc(sizeof(PPP_LCP)); - - c->Code = code; - c->Id = id; - c->OptionList = NewListFast(NULL); - - return c; -} - // Release the LCP void FreePPPLCP(PPP_LCP *c) { @@ -2550,7 +3051,7 @@ void FreePPPOptionList(LIST *o) return; } - for (i = 0;i < LIST_NUM(o);i++) + for (i = 0; i < LIST_NUM(o); i++) { PPP_OPTION *t = LIST_DATA(o, i); @@ -2560,73 +3061,152 @@ void FreePPPOptionList(LIST *o) ReleaseList(o); } -// Create a new PPP session -THREAD *NewPPPSession(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, TUBE *send_tube, TUBE *recv_tube, char *postfix, char *client_software_name, char *client_hostname, char *crypt_name, UINT adjust_mss) +// Release the PPP packet +void FreePPPPacket(PPP_PACKET *pp) +{ + FreePPPPacketEx(pp, false); +} + +void FreePPPPacketEx(PPP_PACKET *pp, bool no_free_struct) { - PPP_SESSION *p; - THREAD *t; // Validate arguments - if (cedar == NULL || client_ip == NULL || server_ip == NULL || send_tube == NULL || recv_tube == NULL) + if (pp == NULL) { - return NULL; - } - if (IsEmptyStr(postfix)) - { - postfix = "PPP"; - } - if (IsEmptyStr(crypt_name)) - { - crypt_name = ""; - } - if (IsEmptyStr(client_software_name)) - { - client_software_name = "PPP VPN Client"; + return; } - // Data structure initialization - p = ZeroMalloc(sizeof(PPP_SESSION)); + FreePPPLCP(pp->Lcp); - p->EnableMSCHAPv2 = true; - p->AuthProtocol = PPP_PROTOCOL_PAP; - p->MsChapV2_ErrorCode = 691; + Free(pp->Data); - p->Cedar = cedar; - AddRef(cedar->ref); - - p->AdjustMss = adjust_mss; - - StrCpy(p->CryptName, sizeof(p->CryptName), crypt_name); - - Copy(&p->ClientIP, client_ip, sizeof(IP)); - p->ClientPort = client_port; - - Copy(&p->ServerIP, server_ip, sizeof(IP)); - p->ServerPort = server_port; - - p->TubeRecv = recv_tube; - p->TubeSend = send_tube; - - AddRef(p->TubeRecv->Ref); - AddRef(p->TubeSend->Ref); - - StrCpy(p->Postfix, sizeof(p->Postfix), postfix); - StrCpy(p->ClientSoftwareName, sizeof(p->ClientSoftwareName), client_software_name); - - if (IsEmptyStr(client_hostname)) + if (no_free_struct == false) { - IPToStr(p->ClientHostname, sizeof(p->ClientHostname), client_ip); + Free(pp); + } +} + +// Free the associated EAP client +void PPPFreeEapClient(PPP_SESSION *p) +{ + if (p == NULL) + { + return; + } + + if (p->EapClient != NULL) + { + ReleaseEapClient(p->EapClient); + p->EapClient = NULL; + } +} + + +// Utility functions used not only in PPP stack + +// Separate into the user name and the Virtual HUB name by analyzing the string +bool PPPParseUsername(CEDAR *cedar, char *src_username, ETHERIP_ID *dst) +{ + UINT i, len, last_at, first_en; + char token1[MAX_SIZE]; // username + char token2[MAX_SIZE]; // hub_name + char src[MAX_SIZE]; + // Validate arguments + Zero(dst, sizeof(ETHERIP_ID)); + if (cedar == NULL || src == NULL || dst == NULL) + { + return false; + } + + StrCpy(src, sizeof(src), src_username); + Trim(src); + + // Search for the first "\\" in the string + len = StrLen(src); + + first_en = SearchStrEx(src, "\\", 0, true); + + if (first_en != INFINITE && first_en >= 1 && (first_en < (len - 1))) + { + StrCpy(token1, sizeof(token1), src + first_en + 1); + StrCpy(token2, sizeof(token2), src); + token2[first_en] = 0; + + // Confirm whether the hubname exists if the virtual HUB name is + // specified like as hubname\username + if (IsHubExistsWithLock(cedar, token2) == false) + { + // If the hubname does not exist, restore to the original name + StrCpy(token1, sizeof(token1), src); + ClearStr(token2, sizeof(token2)); + } } else { - StrCpy(p->ClientHostname, sizeof(p->ClientHostname), client_hostname); + // Search for the last "@" in the string + len = StrLen(src); + last_at = INFINITE; + for (i = 0; i < len; i++) + { + char c = src[i]; + + if (c == '@') + { + last_at = i; + } + } + + Zero(token1, sizeof(token1)); + Zero(token2, sizeof(token2)); + + if (last_at == INFINITE) + { + // "@" is not specified + StrCpy(token1, sizeof(token1), src); + } + else + { + // Split with last "@" + StrCpy(token1, sizeof(token1), src); + token1[last_at] = 0; + + StrCpy(token2, sizeof(token2), src + last_at + 1); + } + + // Check whether such Virtual HUB exists If the virtual HUB name is specified + if (IsEmptyStr(token2) == false) + { + if (IsHubExistsWithLock(cedar, token2) == false) + { + // Because the specified virtual HUB name doesn't exist, it's considered to be a part of the user name + StrCpy(token1, sizeof(token1), src); + + ClearStr(token2, sizeof(token2)); + } + } } - p->FlushList = NewTubeFlushList(); + if (IsEmptyStr(token2)) + { + // Select the default Virtual HUB if the Virtual HUB name is not specified + StrCpy(token2, sizeof(token2), SERVER_DEFAULT_HUB_NAME); + if (cedar->Server != NULL && cedar->Server->IPsecServer != NULL) + { + Lock(cedar->Server->IPsecServer->LockSettings); + { + IPsecNormalizeServiceSetting(cedar->Server->IPsecServer); - // Thread creation - t = NewThread(PPPThread, p); + StrCpy(token2, sizeof(token2), cedar->Server->IPsecServer->Services.L2TP_DefaultHub); + } + Unlock(cedar->Server->IPsecServer->LockSettings); + } - return t; + } + + // Return the results + StrCpy(dst->HubName, sizeof(dst->HubName), token2); + StrCpy(dst->UserName, sizeof(dst->UserName), token1); + + return true; } // Generate the NT hash of the password diff --git a/src/Cedar/IPsec_PPP.h b/src/Cedar/IPsec_PPP.h index 58f94519..13df01fa 100644 --- a/src/Cedar/IPsec_PPP.h +++ b/src/Cedar/IPsec_PPP.h @@ -119,26 +119,30 @@ #define PPP_LCP_CODE_IS_NEGATIVE(c) ((c) == PPP_LCP_CODE_NAK || (c) == PPP_LCP_CODE_REJECT || (c) == PPP_LCP_CODE_CODE_REJECT || (c) == PPP_LCP_CODE_PROTOCOL_REJECT) #define PPP_LCP_CODE_IS_REQUEST(c) ((c) == PPP_LCP_CODE_REQ) #define PPP_LCP_CODE_IS_RESPONSE(c) ((c) == PPP_LCP_CODE_ACK || (c) == PPP_LCP_CODE_NAK || (c) == PPP_LCP_CODE_REJECT || (c) == PPP_LCP_CODE_PROTOCOL_REJECT) -#define PPP_LCP_CODE_IS_WITH_OPTION_LIST(c) ((c) == PPP_LCP_CODE_REQ || (c) == PPP_LCP_CODE_ACK || (c) == PPP_LCP_CODE_NAK) +#define PPP_LCP_CODE_IS_WITH_OPTION_LIST(c) ((c) == PPP_LCP_CODE_REQ || (c) == PPP_LCP_CODE_ACK || (c) == PPP_LCP_CODE_NAK || (c) == PPP_LCP_CODE_REJECT) #define PPP_PAP_CODE_IS_REQUEST(c) ((c) == PPP_PAP_CODE_REQ) #define PPP_PAP_CODE_IS_RESPONSE(c) ((c) == PPP_PAP_CODE_ACK || (c) == PPP_PAP_CODE_NAK) -#define PPP_CODE_IS_RESPONSE(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP) && PPP_LCP_CODE_IS_RESPONSE(c)) || (((protocol) == PPP_PROTOCOL_PAP) && PPP_PAP_CODE_IS_RESPONSE(c))) -#define PPP_CODE_IS_REQUEST(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP) && PPP_LCP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_PAP) && PPP_PAP_CODE_IS_REQUEST(c)) || ((protocol) == PPP_PROTOCOL_CHAP)) -#define PPP_CODE_IS_WITH_OPTION_LIST(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP) && PPP_LCP_CODE_IS_WITH_OPTION_LIST(c)) || false) +#define PPP_CHAP_CODE_IS_REQUEST(c) ((c) == PPP_CHAP_CODE_CHALLENGE || (c) == PPP_CHAP_CODE_SUCCESS || (c) == PPP_CHAP_CODE_FAILURE) +#define PPP_CHAP_CODE_IS_RESPONSE(c) ((c) == PPP_CHAP_CODE_RESPONSE) -#define PPP_IS_SUPPORTED_PROTOCOL(p) ((p) == PPP_PROTOCOL_LCP || (p) == PPP_PROTOCOL_PAP || (p) == PPP_PROTOCOL_CHAP || (p) == PPP_PROTOCOL_IPCP || (p) == PPP_PROTOCOL_IP) +#define PPP_CODE_IS_RESPONSE(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_RESPONSE(c)) || (((protocol) == PPP_PROTOCOL_PAP) && PPP_PAP_CODE_IS_RESPONSE(c)) || (((protocol) == PPP_PROTOCOL_CHAP) && PPP_CHAP_CODE_IS_RESPONSE(c))) +#define PPP_CODE_IS_REQUEST(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_PAP) && PPP_PAP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_CHAP) && PPP_CHAP_CODE_IS_REQUEST(c))) +#define PPP_CODE_IS_WITH_OPTION_LIST(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_WITH_OPTION_LIST(c)) || false) +#define PPP_IS_SUPPORTED_PROTOCOL(p) ((p) == PPP_PROTOCOL_LCP || (p) == PPP_PROTOCOL_PAP || (p) == PPP_PROTOCOL_CHAP || (p) == PPP_PROTOCOL_IPCP || (p) == PPP_PROTOCOL_IPV6CP || (p) == PPP_PROTOCOL_IP || (p) == PPP_PROTOCOL_IPV6) + +#define PPP_STATUS_IS_UNAVAILABLE(c) ((c) == PPP_STATUS_FAIL || (c) == PPP_STATUS_AUTH_FAIL || (c) == PPP_STATUS_CLOSING || (c) == PPP_STATUS_CLOSING_WAIT || (c) == PPP_STATUS_CLOSED) //// Constants // Time-out value -#define PPP_PACKET_RECV_TIMEOUT 10000 // Timeout until the next packet is received -#define PPP_PACKET_RESEND_INTERVAL 1000 // Retransmission interval of the last packet +#define PPP_PACKET_RECV_TIMEOUT (30 * 1000) // Timeout until the next packet is received +#define PPP_PACKET_RESEND_INTERVAL (5 * 1000) // Retransmission interval of the last packet #define PPP_TERMINATE_TIMEOUT 2000 // Timeout value to complete disconnection after requesting to disconnect in the PPP #define PPP_ECHO_SEND_INTERVAL 4792 // Transmission interval of PPP Echo Request -#define PPP_DATA_TIMEOUT (20 * 1000) // Communication time-out +#define PPP_DATA_TIMEOUT (60 * 1000) // Communication time-out // MRU #define PPP_MRU_DEFAULT 1500 // Default value @@ -150,9 +154,11 @@ #define PPP_PROTOCOL_PAP 0xc023 #define PPP_PROTOCOL_IPCP 0x8021 #define PPP_PROTOCOL_CHAP 0xc223 +#define PPP_PROTOCOL_IPV6CP 0x8057 // PPP protocol (for transfer) #define PPP_PROTOCOL_IP 0x0021 +#define PPP_PROTOCOL_IPV6 0x0057 // LCP code #define PPP_LCP_CODE_REQ 1 @@ -190,6 +196,9 @@ #define PPP_IPCP_OPTION_WINS1 130 #define PPP_IPCP_OPTION_WINS2 132 +// IPV6CP option type +#define PPP_IPV6CP_OPTION_IID 1 + // Authentication protocol #define PPP_LCP_AUTH_PAP PPP_PROTOCOL_PAP #define PPP_LCP_AUTH_CHAP PPP_PROTOCOL_CHAP @@ -197,6 +206,26 @@ // Algorithm of CHAP #define PPP_CHAP_ALG_MS_CHAP_V2 0x81 +// Link status +#define PPP_STATUS_CONNECTED 0x1 +#define PPP_STATUS_BEFORE_AUTH 0x10 +#define PPP_STATUS_AUTHENTICATING 0x11 +#define PPP_STATUS_AUTH_SUCCESS 0x19 +#define PPP_STATUS_NETWORK_LAYER 0x20 +#define PPP_STATUS_CLOSING 0x100 +#define PPP_STATUS_CLOSING_WAIT 0x101 +#define PPP_STATUS_CLOSED 0x110 +#define PPP_STATUS_FAIL 0x1000 +#define PPP_STATUS_AUTH_FAIL 0x1010 + +// Protocol status +#define PPP_PROTO_STATUS_CLOSED 0x0 +#define PPP_PROTO_STATUS_CONFIG 0x1 +#define PPP_PROTO_STATUS_CONFIG_WAIT 0x2 +#define PPP_PROTO_STATUS_OPENED 0x10 +#define PPP_PROTO_STATUS_REJECTED 0x100 + +#define PPP_UNSPECIFIED 0xFFFF //// Type @@ -241,6 +270,22 @@ struct PPP_OPTION UINT AltDataSize; // Alternate data size }; +// PPP request resend +struct PPP_REQUEST_RESEND +{ + PPP_PACKET* Packet; + UCHAR Id; + UINT64 ResendTime; + UINT64 TimeoutTime; +}; + +// PPP next packet struct +struct PPP_DELAYED_PACKET +{ + PPP_PACKET* Packet; + UINT DelayTicks; +}; + // PPP session struct PPP_SESSION { @@ -255,7 +300,6 @@ struct PPP_SESSION UINT Mru1; // MRU (server -> client) UINT Mru2; // MRU (client -> server) LIST *RecvPacketList; // Received packet list - PPP_PACKET *LastStoredPacket; // Packet that is stored at the last bool IsTerminateReceived; // Whether a Terminate has been received UINT DisconnectCauseCode; // L2TP disconnect cause code UINT DisconnectCauseDirection; // L2TP disconnect cause direction code @@ -284,48 +328,103 @@ struct PPP_SESSION UCHAR MsChapV2_ClientResponse[24]; // MS-CHAPv2 Client Response UCHAR MsChapV2_ServerResponse[20]; // MS-CHAPv2 Server Response UINT MsChapV2_ErrorCode; // Authentication failure error code of MS-CHAPv2 + UINT MsChapV2_PacketId; // MS-CHAPv2 Packet ID - bool MsChapV2_UseDoubleMsChapV2; // Use the double-MSCHAPv2 technieue + bool MsChapV2_UseDoubleMsChapV2; // Use the double-MSCHAPv2 technique EAP_CLIENT *EapClient; // EAP client + + UCHAR ServerInterfaceId[8]; // Server IPv6CP Interface Identifier + UCHAR ClientInterfaceId[8]; // Client IPv6CP Interface Identifier + + UINT PPPStatus; + UINT IPv4_State; + UINT IPv6_State; + + LIST *SentReqPacketList; // Sent requests list + + PPP_PACKET* CurrentPacket; + LIST *DelayedPackets; }; + + // Function prototype -THREAD *NewPPPSession(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, TUBE *send_tube, TUBE *recv_tube, char *postfix, char *client_software_name, char *client_hostname, char *crypt_name, UINT adjust_mss); + +// Main dataloop void PPPThread(THREAD *thread, void *param); -void FreePPPSession(PPP_SESSION *p); -void FreePPPOptionList(LIST *o); -void FreePPPLCP(PPP_LCP *c); -PPP_LCP *NewPPPLCP(UCHAR code, UCHAR id); -PPP_LCP *ParseLCP(USHORT protocol, void *data, UINT size); -BUF *BuildLCPData(PPP_LCP *c); -PPP_OPTION *GetOptionValue(PPP_LCP *c, UCHAR type); -PPP_PACKET *ParsePPPPacket(void *data, UINT size); -void FreePPPPacket(PPP_PACKET *pp); -void FreePPPPacketEx(PPP_PACKET *pp, bool no_free_struct); -BUF *BuildPPPPacketData(PPP_PACKET *pp); -PPP_OPTION *NewPPPOption(UCHAR type, void *data, UINT size); -bool PPPSendPacket(PPP_SESSION *p, PPP_PACKET *pp); + +// Entry point +THREAD *NewPPPSession(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, TUBE *send_tube, TUBE *recv_tube, char *postfix, char *client_software_name, char *client_hostname, char *crypt_name, UINT adjust_mss); + +// PPP processing functions +bool PPPRejectUnsupportedPacket(PPP_SESSION *p, PPP_PACKET* pp); +bool PPPRejectUnsupportedPacketEx(PPP_SESSION *p, PPP_PACKET* pp, bool force); +bool PPPProcessRetransmissions(PPP_SESSION *p); +bool PPPSendEchoRequest(PPP_SESSION *p); +// Response packets +bool PPPProcessResponsePacket(PPP_SESSION *p, PPP_PACKET* pp, PPP_PACKET* req); +bool PPPProcessLCPResponsePacket(PPP_SESSION *p, PPP_PACKET* pp, PPP_PACKET* req); +bool PPPProcessCHAPResponsePacket(PPP_SESSION *p, PPP_PACKET* pp, PPP_PACKET* req); +bool PPPProcessIPCPResponsePacket(PPP_SESSION *p, PPP_PACKET* pp, PPP_PACKET* req); +// Request packets +bool PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *pp); +bool PPPProcessLCPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp); +bool PPPProcessPAPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp); +bool PPPProcessIPCPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp); + +// LCP option based packets utility +bool PPPRejectLCPOptions(PPP_SESSION *p, PPP_PACKET* pp); +bool PPPRejectLCPOptionsEx(PPP_SESSION *p, PPP_PACKET* pp, bool simulate); +bool PPPNackLCPOptions(PPP_SESSION *p, PPP_PACKET* pp); +bool PPPNackLCPOptionsEx(PPP_SESSION *p, PPP_PACKET* pp, bool simulate); +bool PPPAckLCPOptions(PPP_SESSION *p, PPP_PACKET* pp); +bool PPPAckLCPOptionsEx(PPP_SESSION *p, PPP_PACKET* pp, bool simulate); + +// PPP networking functions +// Send packets +bool PPPSendAndRetransmitRequest(PPP_SESSION *p, USHORT protocol, PPP_LCP* c); +bool PPPSendPacketAndFree(PPP_SESSION *p, PPP_PACKET *pp); bool PPPSendPacketEx(PPP_SESSION *p, PPP_PACKET *pp, bool no_flush); +// Receive packets PPP_PACKET *PPPRecvPacket(PPP_SESSION *p, bool async); -PPP_PACKET *PPPRecvPacketWithLowLayerProcessing(PPP_SESSION *p, bool async); -PPP_PACKET *PPPRecvPacketForCommunication(PPP_SESSION *p); -void PPPStoreLastPacket(PPP_SESSION *p, PPP_PACKET *pp); -void PPPCleanTerminate(PPP_SESSION *p); +// Helpers for delaying packets +PPP_PACKET *PPPGetNextPacket(PPP_SESSION *p); +void PPPAddNextPacket(PPP_SESSION *p, PPP_PACKET *pp, UINT delay); +int PPPDelayedPacketsComparator(const void* a, const void* b); +char PPPRelatedPacketComparator(PPP_PACKET* a, PPP_PACKET* b); + +// PPP utility functions +// Packet structures creation utilities +PPP_LCP *NewPPPLCP(UCHAR code, UCHAR id); +PPP_OPTION *NewPPPOption(UCHAR type, void *data, UINT size); +// Packet parse utilities +PPP_PACKET *ParsePPPPacket(void *data, UINT size); +PPP_LCP *PPPParseLCP(USHORT protocol, void *data, UINT size); +bool PPPParseMSCHAP2ResponsePacket(PPP_SESSION* p, PPP_PACKET* req); +// Packet building utilities +BUF *BuildPPPPacketData(PPP_PACKET *pp); +BUF *BuildLCPData(PPP_LCP *c); +PPP_LCP *BuildMSCHAP2ChallengePacket(PPP_SESSION* p); +// IPCP packet utilities bool PPPGetIPOptionFromLCP(PPP_IPOPTION *o, PPP_LCP *c); bool PPPSetIPOptionToLCP(PPP_IPOPTION *o, PPP_LCP *c, bool only_modify); bool PPPGetIPAddressValueFromLCP(PPP_LCP *c, UINT type, IP *ip); bool PPPSetIPAddressValueToLCP(PPP_LCP *c, UINT type, IP *ip, bool only_modify); - -bool PPPSendRequest(PPP_SESSION *p, USHORT protocol, PPP_LCP *c); -USHORT PPPContinueCurrentProtocolRequestListening(PPP_SESSION *p, USHORT protocol); -bool PPPContinueUntilFinishAllLCPOptionRequestsDetermined(PPP_SESSION *p); -PPP_PACKET *PPPRecvResponsePacket(PPP_SESSION *p, PPP_PACKET *req, USHORT expected_protocol, USHORT *received_protocol, bool finish_when_all_lcp_acked, - bool return_mschapv2_response_with_no_processing); -PPP_PACKET *PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *req); -void PPPSendEchoRequest(PPP_SESSION *p); -bool PPPParseUsername(CEDAR *cedar, char *src, ETHERIP_ID *dst); +// Other packet utilities +PPP_OPTION *PPPGetOptionValue(PPP_LCP *c, UCHAR type); bool IsHubExistsWithLock(CEDAR *cedar, char *hubname); +void PPPSetStatus(PPP_SESSION *p, UINT status); +// Memory freeing functions +void FreePPPSession(PPP_SESSION *p); +void FreePPPLCP(PPP_LCP *c); +void FreePPPOptionList(LIST *o); +void FreePPPPacket(PPP_PACKET *pp); +void FreePPPPacketEx(PPP_PACKET *pp, bool no_free_struct); +void PPPFreeEapClient(PPP_SESSION *p); + +// Utility functions used not only in PPP stack +bool PPPParseUsername(CEDAR *cedar, char *src, ETHERIP_ID *dst); void GenerateNtPasswordHash(UCHAR *dst, char *password); void GenerateNtPasswordHashHash(UCHAR *dst_hash, UCHAR *src_hash); void MsChapV2Server_GenerateChallenge(UCHAR *dst); @@ -335,7 +434,6 @@ void MsChapV2Client_GenerateResponse(UCHAR *dst, UCHAR *challenge8, UCHAR *nt_pa void MsChapV2Server_GenerateResponse(UCHAR *dst, UCHAR *nt_password_hash_hash, UCHAR *client_response, UCHAR *challenge8); bool MsChapV2VerityPassword(IPC_MSCHAP_V2_AUTHINFO *d, char *password); char *MsChapV2DoBruteForce(IPC_MSCHAP_V2_AUTHINFO *d, LIST *password_list); -void PPPFreeEapClient(PPP_SESSION *p); #endif // IPSEC_PPP_H diff --git a/src/Mayaqua/Mayaqua.c b/src/Mayaqua/Mayaqua.c index 5bc06b86..c3896fd8 100644 --- a/src/Mayaqua/Mayaqua.c +++ b/src/Mayaqua/Mayaqua.c @@ -491,7 +491,11 @@ void InitMayaqua(bool memcheck, bool debug, int argc, char **argv) } g_memcheck = memcheck; +#ifdef DEBUG + g_debug = true; +#else g_debug = debug; +#endif cmdline = NULL; if (dot_net_mode == false) { diff --git a/src/Mayaqua/Network.c b/src/Mayaqua/Network.c index 73166847..d148ab04 100644 --- a/src/Mayaqua/Network.c +++ b/src/Mayaqua/Network.c @@ -7707,9 +7707,9 @@ void SetIP6(IP *ip, UCHAR *value) Zero(ip, sizeof(IP)); - ip->addr[0] = 223; - ip->addr[1] = 255; - ip->addr[2] = 255; + ip->addr[0] = 192; + ip->addr[1] = 0; + ip->addr[2] = 2; ip->addr[3] = 254; if (value != NULL) @@ -7732,7 +7732,7 @@ bool IsIP6(IP *ip) return false; } - if (ip->addr[0] == 223 && ip->addr[1] == 255 && ip->addr[2] == 255 && ip->addr[3] == 254) + if (ip->addr[0] == 192 && ip->addr[1] == 0 && ip->addr[2] == 2 && ip->addr[3] == 254) { return true; } diff --git a/src/Mayaqua/Network.h b/src/Mayaqua/Network.h index 21b67ba7..7b7e2c05 100644 --- a/src/Mayaqua/Network.h +++ b/src/Mayaqua/Network.h @@ -184,7 +184,7 @@ struct DYN_VALUE // IP address struct IP { - UCHAR addr[4]; // IPv4 address, (meaning that 223.255.255.254 = IPv6) + UCHAR addr[4]; // IPv4 address, (meaning that 192.0.2.254 = IPv6) UCHAR ipv6_addr[16]; // IPv6 address UINT ipv6_scope_id; // IPv6 scope ID }; diff --git a/src/vpnweb/stdafx.h b/src/vpnweb/stdafx.h index d5dd2200..51731251 100644 --- a/src/vpnweb/stdafx.h +++ b/src/vpnweb/stdafx.h @@ -30,6 +30,7 @@ #include "resource.h" #include #include +#pragma comment(lib, "atlthunk.lib") using namespace ATL;