1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2025-04-03 18:00:08 +03:00
SoftEtherVPN/src/Cedar/Radius.c
Daiyuu Nobori 7de986dcca 7 missing memory boundaries checks and similar memory problems. There are no risk of arbitrary code execution or intrusion on these bugs in my analysis. However, these problems may lead to crash the running server process. So these bugs must be fixed.
Buffer overread in ParseL2TPPacket()
Memory corruption in IcmpParseResult
Missing bounds check in ParseUDP() can lead to invalid memory access
Out-of-bounds read in IPsec_PPP.c (unterminated string buffer)
Overlapping parameters to memcpy() via StrToIp6()
PACK ReadValue() crash vulnerability
Potential use of uninitialized memory via IPToInAddr6()

4 memory leaks. While the amount of leakage is very small per time, these bugs can finally cause process crash by out of memory. So these bugs must be fixed.

Memory leak in NnReadDnsRecord
Memory leak in RadiusLogin()
Memory leak via ParsePacketIPv4WithDummyMacHeader
Remote memory leak in OpenVPN server code

1 coding improvement. This is not a bug, however, I fixed the code to avoid furture misunderstanding.

RecvAll can return success on failure (leading to use of uninitialized memory)

Contributors for this bugfix:

- Max Planck Institute for Molecular Genetics
- Guido Vranken
2018-01-15 10:25:10 +09:00

2422 lines
57 KiB
C

// SoftEther VPN Source Code - Developer Edition Master Branch
// Cedar Communication Module
//
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
//
// Copyright (c) Daiyuu Nobori.
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
// Copyright (c) SoftEther Corporation.
//
// All Rights Reserved.
//
// http://www.softether.org/
//
// Author: Daiyuu Nobori, Ph.D.
// Comments: Tetsuo Sugiyama, Ph.D.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License version 2
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
//
//
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
//
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS
// YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY
// CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS
// SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE
// SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO
// COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING
// PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR
// CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE
// NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+
// COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE
// WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY
// COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE
// COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE
// SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR
// COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO
// RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT
// JUST A STATEMENT FOR WARNING AND DISCLAIMER.
//
//
// SOURCE CODE CONTRIBUTION
// ------------------------
//
// Your contribution to SoftEther VPN Project is much appreciated.
// Please send patches to us through GitHub.
// Read the SoftEther VPN Patch Acceptance Policy in advance:
// http://www.softether.org/5-download/src/9.patch
//
//
// DEAR SECURITY EXPERTS
// ---------------------
//
// If you find a bug or a security vulnerability please kindly inform us
// about the problem immediately so that we can fix the security problem
// to protect a lot of users around the world as soon as possible.
//
// Our e-mail address for security reports is:
// softether-vpn-security [at] softether.org
//
// Please note that the above e-mail address is not a technical support
// inquiry address. If you need technical assistance, please visit
// http://www.softether.org/ and ask your question on the users forum.
//
// 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.
// Radius.c
// Radius authentication module
#include "CedarPch.h"
////////// Modern implementation
// send PEAP-MSCHAPv2 auth client response
bool PeapClientSendMsChapv2AuthClientResponse(EAP_CLIENT *e, UCHAR *client_response, UCHAR *client_challenge)
{
bool ret = false;
EAP_MSCHAPV2_RESPONSE msg1;
EAP_MESSAGE msg2;
EAP_MESSAGE msg4;
if (e == NULL || client_response == NULL || client_challenge == NULL)
{
return false;
}
Zero(&msg1, sizeof(msg1));
Zero(&msg2, sizeof(msg2));
Zero(&msg4, sizeof(msg4));
msg1.Type = EAP_TYPE_MS_AUTH;
msg1.Chap_Opcode = EAP_MSCHAPV2_OP_RESPONSE;
msg1.Chap_Id = e->MsChapV2Challenge.Chap_Id;
msg1.Chap_Len = Endian16(54 + StrLen(e->Username));
msg1.Chap_ValueSize = 49;
Copy(msg1.Chap_PeerChallange, client_challenge, 16);
Copy(msg1.Chap_NtResponse, client_response, 24);
Copy(msg1.Chap_Name, e->Username, MIN(StrLen(e->Username), 255));
if (SendPeapPacket(e, &msg1, 59 + StrLen(e->Username)) &&
GetRecvPeapMessage(e, &msg2))
{
if (msg2.Type == EAP_TYPE_MS_AUTH &&
((EAP_MSCHAPV2_GENERAL *)&msg2)->Chap_Opcode == EAP_MSCHAPV2_OP_SUCCESS)
{
EAP_MSCHAPV2_SUCCESS_SERVER *eaps = (EAP_MSCHAPV2_SUCCESS_SERVER *)&msg2;
if (StartWith(eaps->Message, "S="))
{
BUF *buf = StrToBin(eaps->Message + 2);
if (buf && buf->Size == 20)
{
Copy(&e->MsChapV2Success, eaps, sizeof(EAP_MSCHAPV2_SUCCESS_SERVER));
Copy(e->ServerResponse, buf->Buf, 20);
if (true)
{
EAP_MSCHAPV2_SUCCESS_CLIENT msg3;
Zero(&msg3, sizeof(msg3));
msg3.Type = EAP_TYPE_MS_AUTH;
msg3.Chap_Opcode = EAP_MSCHAPV2_OP_SUCCESS;
if (SendPeapPacket(e, &msg3, 6) && GetRecvPeapMessage(e, &msg4))
{
UCHAR *rd = ((UCHAR *)&msg4);
if (rd[4] == 0x01 && rd[8] == 0x21 && rd[9] == 0x80 &&
rd[10] == 0x03 && rd[11] == 0x00 && rd[12] == 0x02 &&
rd[13] == 0x00 && rd[14] == 0x01)
{
UCHAR reply[15];
Zero(reply, sizeof(reply));
reply[4] = 0x02; reply[5] = rd[5]; reply[6] = 0x00; reply[7] = 0x0b;
reply[8] = 0x21; reply[9] = 0x80; reply[10] = 0x03; reply[11] = 0x00;
reply[12] = 0x02; reply[13] = 0x00; reply[14] = 0x01;
if (SendPeapPacket(e, reply, sizeof(reply)))
{
if (e->RecvLastCode == RADIUS_CODE_ACCESS_ACCEPT)
{
ret = true;
}
}
}
}
}
}
FreeBuf(buf);
}
}
}
return ret;
}
// send PEAP-MSCHAPv2 auth request
bool PeapClientSendMsChapv2AuthRequest(EAP_CLIENT *eap)
{
bool ret = false;
UINT num_retry = 0;
if (eap == NULL)
{
return false;
}
if (StartPeapClient(eap))
{
if (StartPeapSslClient(eap))
{
EAP_MESSAGE recv_msg;
EAP_MESSAGE send_msg;
if (GetRecvPeapMessage(eap, &recv_msg) && recv_msg.Type == EAP_TYPE_IDENTITY)
{
LABEL_RETRY:
num_retry++;
if (num_retry >= 10)
{
return false;
}
Zero(&send_msg, sizeof(send_msg));
send_msg.Type = EAP_TYPE_IDENTITY;
send_msg.Len = Endian16(5 + StrLen(eap->Username));
Copy(send_msg.Data, eap->Username, StrLen(eap->Username));
if (SendPeapPacket(eap, &send_msg, 5 + StrLen(eap->Username)) &&
GetRecvPeapMessage(eap, &recv_msg))
{
LABEL_RETRY2:
num_retry++;
if (num_retry >= 10)
{
return false;
}
if (recv_msg.Type == EAP_TYPE_MS_AUTH &&
((EAP_MSCHAPV2_GENERAL *)&recv_msg)->Chap_Opcode == EAP_MSCHAPV2_OP_CHALLENGE)
{
EAP_MSCHAPV2_CHALLENGE *svr_challenge = (EAP_MSCHAPV2_CHALLENGE *)&recv_msg;
Copy(&eap->MsChapV2Challenge, svr_challenge, sizeof(EAP_MSCHAPV2_CHALLENGE));
ret = true;
eap->PeapMode = true;
}
else if (recv_msg.Type == EAP_TYPE_IDENTITY)
{
UCHAR *rd = ((UCHAR *)&recv_msg);
if (rd[4] == 0x01 && rd[8] == 0x21 && rd[9] == 0x80 &&
rd[10] == 0x03 && rd[11] == 0x00 && rd[12] == 0x02 &&
rd[13] == 0x00)
{
if (rd[14] == 0x02)
{
// Fail
return false;
}
}
goto LABEL_RETRY;
}
else
{
EAP_MESSAGE nak;
Zero(&nak, sizeof(nak));
nak.Type = EAP_TYPE_LEGACY_NAK;
nak.Data[0] = EAP_TYPE_MS_AUTH;
if (SendPeapPacket(eap, &nak, 6) &&
GetRecvPeapMessage(eap, &recv_msg))
{
goto LABEL_RETRY2;
}
}
}
}
}
}
return ret;
}
// Send a PEAP packet (encrypted)
bool SendPeapRawPacket(EAP_CLIENT *e, UCHAR *peap_data, UINT peap_size)
{
LIST *fragments = NULL;
bool ret = false;
BUF *buf = NULL;
UINT i;
UINT num;
bool send_empty = false;
bool include_len = false;
if (e == NULL)
{
return false;
}
// divide into 1024 bytes
buf = NewBuf();
// size
if ((peap_size + 6 + 2) >= 256)
{
WriteBufInt(buf, peap_size);
include_len = true;
}
// data
WriteBuf(buf, peap_data, peap_size);
if (peap_data == NULL)
{
send_empty = true;
}
SeekBufToBegin(buf);
fragments = NewListFast(NULL);
for (num = 0;;num++)
{
UCHAR tmp[1024];
EAP_PEAP *send_peap_message;
UINT sz;
sz = ReadBuf(buf, tmp, 1024);
if (sz == 0)
{
break;
}
// add header
send_peap_message = ZeroMalloc(sizeof(EAP_PEAP) + sz);
send_peap_message->Code = EAP_CODE_RESPONSE;
send_peap_message->Id = e->LastRecvEapId + num;
send_peap_message->Len = Endian16((UINT)(((UINT)sizeof(EAP_PEAP) + (UINT)sz)));
send_peap_message->Type = EAP_TYPE_PEAP;
send_peap_message->TlsFlags = 0;
if (num == 0 && include_len)
{
send_peap_message->TlsFlags |= EAP_TLS_FLAGS_LEN;
}
if (ReadBufRemainSize(buf) != 0)
{
send_peap_message->TlsFlags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
}
Copy(((UCHAR *)send_peap_message) + sizeof(EAP_PEAP), tmp, sz);
Add(fragments, MemToBuf(send_peap_message, sizeof(EAP_PEAP) + sz));
Free(send_peap_message);
}
if (num == 0 && send_empty)
{
Add(fragments, MemToBuf("\0", 1));
}
// send each of packets
for (i = 0;i < LIST_NUM(fragments);i++)
{
BUF *b = LIST_DATA(fragments, i);
RADIUS_AVP *eap_avp;
RADIUS_PACKET *response_packet;
RADIUS_PACKET *send_packet = NewRadiusPacket(RADIUS_CODE_ACCESS_REQUEST, e->NextRadiusPacketId++);
EapSetRadiusGeneralAttributes(send_packet, e);
if (e->LastStateSize != 0)
{
Add(send_packet->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_STATE, 0, 0,
e->LastState, e->LastStateSize));
}
if (send_empty == false)
{
eap_avp = NewRadiusAvp(RADIUS_ATTRIBUTE_EAP_MESSAGE, 0, 0, b->Buf, b->Size);
}
else
{
EAP_PEAP empty_peap;
Zero(&empty_peap, sizeof(empty_peap));
empty_peap.Code = EAP_CODE_RESPONSE;
empty_peap.Id = e->LastRecvEapId;
empty_peap.Len = Endian16(sizeof(EAP_PEAP));
empty_peap.Type = EAP_TYPE_PEAP;
eap_avp = NewRadiusAvp(RADIUS_ATTRIBUTE_EAP_MESSAGE, 0, 0, &empty_peap, sizeof(EAP_PEAP));
}
Add(send_packet->AvpList, eap_avp);
response_packet = EapSendPacketAndRecvResponse(e, send_packet);
if (response_packet != NULL)
{
e->RecvLastCode = response_packet->Code;
if (response_packet->Parse_EapMessage != NULL && response_packet->Parse_EapMessage_DataSize >= sizeof(EAP_PEAP))
{
// Received SSL stream
EAP_PEAP *peap_msg = (EAP_PEAP *)response_packet->Parse_EapMessage;
if (peap_msg->Type == EAP_TYPE_PEAP)
{
if (peap_msg->TlsFlags & EAP_TLS_FLAGS_LEN)
{
UINT total_size = READ_UINT(((UCHAR *)peap_msg) + sizeof(EAP_PEAP));
if (total_size <= (response_packet->Parse_EapMessage_DataSize - sizeof(EAP_PEAP) - sizeof(UINT)))
{
WriteFifo(e->SslPipe->RawIn->SendFifo, ((UCHAR *)peap_msg) + sizeof(EAP_PEAP) + sizeof(UINT), total_size);
}
}
else
{
WriteFifo(e->SslPipe->RawIn->SendFifo, ((UCHAR *)peap_msg) + sizeof(EAP_PEAP),
response_packet->Parse_EapMessage_DataSize - sizeof(EAP_PEAP));
}
}
}
}
FreeRadiusPacket(send_packet);
FreeRadiusPacket(response_packet);
}
FreeBuf(buf);
if (fragments != NULL)
{
for (i = 0;i < LIST_NUM(fragments);i++)
{
BUF *b = LIST_DATA(fragments, i);
FreeBuf(b);
}
ReleaseList(fragments);
}
SyncSslPipe(e->SslPipe);
return ret;
}
// Send an encrypted message of PEAP
bool SendPeapPacket(EAP_CLIENT *e, void *msg, UINT msg_size)
{
bool ret = false;
FIFO *send_fifo;
FIFO *recv_fifo;
BUF *buf;
EAP_MESSAGE tmpmsg;
if (e == NULL || msg == NULL || msg_size == 0)
{
return false;
}
if (e->SslPipe == NULL)
{
return false;
}
send_fifo = e->SslPipe->RawOut->RecvFifo;
recv_fifo = e->SslPipe->RawIn->SendFifo;
Zero(&tmpmsg, sizeof(tmpmsg));
Copy(&tmpmsg, msg, MIN(msg_size, sizeof(EAP_MESSAGE)));
WriteFifo(e->SslPipe->SslInOut->SendFifo, &tmpmsg.Type, msg_size - 4);
SyncSslPipe(e->SslPipe);
buf = ReadFifoAll(send_fifo);
while (true)
{
ret = SendPeapRawPacket(e, buf->Buf, buf->Size);
FreeBuf(buf);
if (send_fifo->size == 0)
{
break;
}
buf = ReadFifoAll(send_fifo);
}
return !e->SslPipe->IsDisconnected;
}
// Start a PEAP SSL client
bool StartPeapSslClient(EAP_CLIENT *e)
{
bool ret = false;
FIFO *send_fifo;
FIFO *recv_fifo;
BUF *buf;
if (e == NULL)
{
return false;
}
if (e->SslPipe != NULL)
{
return false;
}
e->SslPipe = NewSslPipe(false, NULL, NULL, NULL);
send_fifo = e->SslPipe->RawOut->RecvFifo;
recv_fifo = e->SslPipe->RawIn->SendFifo;
SyncSslPipe(e->SslPipe);
buf = ReadFifoAll(send_fifo);
while (true)
{
ret = SendPeapRawPacket(e, buf->Buf, buf->Size);
FreeBuf(buf);
if (send_fifo->size == 0)
{
break;
}
buf = ReadFifoAll(send_fifo);
}
SendPeapRawPacket(e, NULL, 0);
return !e->SslPipe->IsDisconnected;
}
// Get a received PEAP message (unencrypted)
bool GetRecvPeapMessage(EAP_CLIENT *e, EAP_MESSAGE *msg)
{
BUF *b;
bool ret = false;
if (e == NULL)
{
return false;
}
if (e->SslPipe == NULL)
{
return false;
}
b = ReadFifoAll(e->SslPipe->SslInOut->RecvFifo);
if (b->Size >= 1)
{
Zero(msg, sizeof(EAP_MESSAGE));
msg->Len = Endian16(b->Size + 4);
Copy(&msg->Type, b->Buf, MIN(b->Size, 1501));
ret = true;
}
FreeBuf(b);
return ret;
}
// Start a PEAP client
bool StartPeapClient(EAP_CLIENT *e)
{
bool ret = false;
RADIUS_PACKET *request1 = NULL;
RADIUS_PACKET *response1 = NULL;
RADIUS_PACKET *request2 = NULL;
RADIUS_PACKET *response2 = NULL;
EAP_MESSAGE *eap1 = NULL;
EAP_MESSAGE *eap2 = NULL;
if (e == NULL)
{
return false;
}
if (e->SslPipe != NULL)
{
return false;
}
request1 = NewRadiusPacket(RADIUS_CODE_ACCESS_REQUEST, e->NextRadiusPacketId++);
EapSetRadiusGeneralAttributes(request1, e);
eap1 = ZeroMalloc(sizeof(EAP_MESSAGE));
eap1->Code = EAP_CODE_RESPONSE;
eap1->Id = e->LastRecvEapId;
eap1->Len = Endian16(StrLen(e->Username) + 5);
eap1->Type = EAP_TYPE_IDENTITY;
Copy(eap1->Data, e->Username, StrLen(e->Username));
Add(request1->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_EAP_MESSAGE, 0, 0, eap1, StrLen(e->Username) + 5));
response1 = EapSendPacketAndRecvResponse(e, request1);
if (response1 != NULL)
{
if (response1->Parse_EapMessage_DataSize != 0 && response1->Parse_EapMessage != NULL)
{
EAP_MESSAGE *eap = response1->Parse_EapMessage;
if (eap->Code == EAP_CODE_REQUEST)
{
if (eap->Type != EAP_TYPE_PEAP)
{
// Unsupported auth type. Request PEAP.
request2 = NewRadiusPacket(RADIUS_CODE_ACCESS_REQUEST, e->NextRadiusPacketId++);
EapSetRadiusGeneralAttributes(request2, e);
if (response1->Parse_StateSize != 0)
{
Add(request2->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_STATE, 0, 0,
response1->Parse_State, response1->Parse_StateSize));
}
eap2 = ZeroMalloc(sizeof(EAP_MESSAGE));
eap2->Code = EAP_CODE_RESPONSE;
eap2->Id = e->LastRecvEapId;
eap2->Len = Endian16(6);
eap2->Type = EAP_TYPE_LEGACY_NAK;
eap2->Data[0] = EAP_TYPE_PEAP;
Add(request2->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_EAP_MESSAGE, 0, 0, eap2, 6));
response2 = EapSendPacketAndRecvResponse(e, request2);
if (response2 != NULL && response2->Parse_EapMessage_DataSize != 0 && response2->Parse_EapMessage != NULL)
{
eap = response2->Parse_EapMessage;
if (eap->Code == EAP_CODE_REQUEST && eap->Type == EAP_TYPE_PEAP)
{
goto LABEL_PARSE_PEAP;
}
}
}
else
{
EAP_PEAP *peap;
LABEL_PARSE_PEAP:
peap = (EAP_PEAP *)eap;
if (peap->TlsFlags == 0x20)
{
ret = true;
}
}
}
}
}
FreeRadiusPacket(request1);
FreeRadiusPacket(request2);
FreeRadiusPacket(response1);
FreeRadiusPacket(response2);
Free(eap1);
Free(eap2);
return ret;
}
// Set RADIUS general attributes
void EapSetRadiusGeneralAttributes(RADIUS_PACKET *r, EAP_CLIENT *e)
{
UINT ui;
char *str;
if (r == NULL || e == NULL)
{
return;
}
ui = Endian32(2);
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_SERVICE_TYPE, 0, 0, &ui, sizeof(UINT)));
ui = Endian32(1);
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_FRAMED_PROTOCOL, 0, 0, &ui, sizeof(UINT)));
ui = Endian32(5);
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_NAS_PORT_TYPE, 0, 0, &ui, sizeof(UINT)));
if (IsEmptyStr(e->CalledStationStr) == false)
{
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_CALLED_STATION_ID, 0, 0, e->CalledStationStr, StrLen(e->CalledStationStr)));
}
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_CALLING_STATION_ID, 0, 0, e->ClientIpStr, StrLen(e->ClientIpStr)));
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_TUNNEL_CLIENT_ENDPOINT, 0, 0, e->ClientIpStr, StrLen(e->ClientIpStr)));
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_USER_NAME, 0, 0, e->Username, StrLen(e->Username)));
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_NAS_ID, 0, 0, CEDAR_SERVER_STR, StrLen(CEDAR_SERVER_STR)));
ui = Endian32(2);
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_VENDOR_SPECIFIC, RADIUS_VENDOR_MICROSOFT,
RADIUS_MS_NETWORK_ACCESS_SERVER_TYPE, &ui, sizeof(UINT)));
ui = Endian32(RADIUS_VENDOR_MICROSOFT);
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_VENDOR_SPECIFIC, RADIUS_VENDOR_MICROSOFT,
RADIUS_MS_RAS_VENDOR, &ui, sizeof(UINT)));
str = "MSRASV5.20";
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_VENDOR_SPECIFIC, RADIUS_VENDOR_MICROSOFT,
RADIUS_MS_VERSION, str, StrLen(str)));
str = "{5DC53D72-9815-4E97-AC91-339BAFEA6C48}";
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_VENDOR_SPECIFIC, RADIUS_VENDOR_MICROSOFT,
RADIUS_MS_RAS_CORRELATION, str, StrLen(str)));
str = "MSRASV5.20";
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_VENDOR_SPECIFIC, RADIUS_VENDOR_MICROSOFT,
RADIUS_MS_RAS_CLIENT_VERSION, str, StrLen(str)));
str = "MSRASV5.20";
Add(r->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_VENDOR_SPECIFIC, RADIUS_VENDOR_MICROSOFT,
RADIUS_MS_RAS_CLIENT_NAME, str, StrLen(str)));
}
// Send a MSCHAPv2 client auth response1
bool EapClientSendMsChapv2AuthClientResponse(EAP_CLIENT *e, UCHAR *client_response, UCHAR *client_challenge)
{
bool ret = false;
RADIUS_PACKET *request1 = NULL;
RADIUS_PACKET *response1 = NULL;
RADIUS_PACKET *request2 = NULL;
RADIUS_PACKET *response2 = NULL;
EAP_MSCHAPV2_RESPONSE *eap1 = NULL;
EAP_MSCHAPV2_SUCCESS_CLIENT *eap2 = NULL;
if (e == NULL || client_response == NULL || client_challenge == NULL)
{
return false;
}
request1 = NewRadiusPacket(RADIUS_CODE_ACCESS_REQUEST, e->NextRadiusPacketId++);
EapSetRadiusGeneralAttributes(request1, e);
if (e->LastStateSize != 0)
{
Add(request1->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_STATE, 0, 0,
e->LastState, e->LastStateSize));
}
eap1 = ZeroMalloc(sizeof(EAP_MSCHAPV2_RESPONSE));
eap1->Code = EAP_CODE_RESPONSE;
eap1->Id = e->NextEapId++;
eap1->Len = Endian16(59 + StrLen(e->Username));
eap1->Type = EAP_TYPE_MS_AUTH;
eap1->Chap_Opcode = EAP_MSCHAPV2_OP_RESPONSE;
eap1->Chap_Id = e->MsChapV2Challenge.Chap_Id;
eap1->Chap_Len = Endian16(54 + StrLen(e->Username));
eap1->Chap_ValueSize = 49;
Copy(eap1->Chap_PeerChallange, client_challenge, 16);
Copy(eap1->Chap_NtResponse, client_response, 24);
Copy(eap1->Chap_Name, e->Username, MIN(StrLen(e->Username), 255));
Add(request1->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_EAP_MESSAGE, 0, 0, eap1, StrLen(e->Username) + 59));
response1 = EapSendPacketAndRecvResponse(e, request1);
if (response1 != NULL)
{
if (response1->Parse_EapMessage_DataSize != 0 && response1->Parse_EapMessage != NULL)
{
EAP_MESSAGE *eap = response1->Parse_EapMessage;
if (eap->Code == EAP_CODE_REQUEST)
{
if (eap->Type == EAP_TYPE_MS_AUTH)
{
if (((EAP_MSCHAPV2_GENERAL *)eap)->Chap_Opcode != EAP_MSCHAPV2_OP_SUCCESS)
{
// Auth fail
}
else
{
// Auth ok
EAP_MSCHAPV2_SUCCESS_SERVER *eaps = (EAP_MSCHAPV2_SUCCESS_SERVER *)eap;
if (StartWith(eaps->Message, "S="))
{
BUF *buf = StrToBin(eaps->Message + 2);
if (buf && buf->Size == 20)
{
Copy(&e->MsChapV2Success, eaps, sizeof(EAP_MSCHAPV2_SUCCESS_SERVER));
Copy(e->ServerResponse, buf->Buf, 20);
if (true)
{
// Send the final packet
request2 = NewRadiusPacket(RADIUS_CODE_ACCESS_REQUEST, e->NextRadiusPacketId++);
EapSetRadiusGeneralAttributes(request2, e);
if (e->LastStateSize != 0)
{
Add(request2->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_STATE, 0, 0,
e->LastState, e->LastStateSize));
}
eap2 = ZeroMalloc(sizeof(EAP_MSCHAPV2_SUCCESS_CLIENT));
eap2->Code = EAP_CODE_RESPONSE;
eap2->Id = e->NextEapId++;
eap2->Len = Endian16(6);
eap2->Type = EAP_TYPE_MS_AUTH;
eap2->Chap_Opcode = EAP_MSCHAPV2_OP_SUCCESS;
Add(request2->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_EAP_MESSAGE, 0, 0, eap2, 6));
response2 = EapSendPacketAndRecvResponse(e, request2);
if (response2 != NULL)
{
if (response2->Code == RADIUS_CODE_ACCESS_ACCEPT)
{
ret = true;
}
}
}
}
FreeBuf(buf);
}
}
}
}
}
}
FreeRadiusPacket(request1);
FreeRadiusPacket(request2);
FreeRadiusPacket(response1);
FreeRadiusPacket(response2);
Free(eap1);
Free(eap2);
return ret;
}
// Send a MSCHAPv2 client auth request
bool EapClientSendMsChapv2AuthRequest(EAP_CLIENT *e)
{
bool ret = false;
RADIUS_PACKET *request1 = NULL;
RADIUS_PACKET *response1 = NULL;
RADIUS_PACKET *request2 = NULL;
RADIUS_PACKET *response2 = NULL;
EAP_MESSAGE *eap1 = NULL;
EAP_MESSAGE *eap2 = NULL;
if (e == NULL)
{
return false;
}
request1 = NewRadiusPacket(RADIUS_CODE_ACCESS_REQUEST, e->NextRadiusPacketId++);
EapSetRadiusGeneralAttributes(request1, e);
eap1 = ZeroMalloc(sizeof(EAP_MESSAGE));
eap1->Code = EAP_CODE_RESPONSE;
eap1->Id = e->NextEapId++;
eap1->Len = Endian16(StrLen(e->Username) + 5);
eap1->Type = EAP_TYPE_IDENTITY;
Copy(eap1->Data, e->Username, StrLen(e->Username));
Add(request1->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_EAP_MESSAGE, 0, 0, eap1, StrLen(e->Username) + 5));
response1 = EapSendPacketAndRecvResponse(e, request1);
if (response1 != NULL)
{
if (response1->Parse_EapMessage_DataSize != 0 && response1->Parse_EapMessage != NULL)
{
EAP_MESSAGE *eap = response1->Parse_EapMessage;
if (eap->Code == EAP_CODE_REQUEST)
{
if (eap->Type != EAP_TYPE_MS_AUTH)
{
// Unsupported auth type. Request MS-CHAP-v2.
request2 = NewRadiusPacket(RADIUS_CODE_ACCESS_REQUEST, e->NextRadiusPacketId++);
EapSetRadiusGeneralAttributes(request2, e);
if (response1->Parse_StateSize != 0)
{
Add(request2->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_STATE, 0, 0,
response1->Parse_State, response1->Parse_StateSize));
}
eap2 = ZeroMalloc(sizeof(EAP_MESSAGE));
eap2->Code = EAP_CODE_RESPONSE;
eap2->Id = e->NextEapId++;
eap2->Len = Endian16(6);
eap2->Type = EAP_TYPE_LEGACY_NAK;
eap2->Data[0] = EAP_TYPE_MS_AUTH;
Add(request2->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_EAP_MESSAGE, 0, 0, eap2, 6));
response2 = EapSendPacketAndRecvResponse(e, request2);
if (response2 != NULL && response2->Parse_EapMessage_DataSize != 0 && response2->Parse_EapMessage != NULL)
{
eap = response2->Parse_EapMessage;
if (eap->Code == EAP_CODE_REQUEST && eap->Type == EAP_TYPE_MS_AUTH)
{
goto LABEL_PARSE_EAP;
}
}
}
else
{
EAP_MSCHAPV2_GENERAL *ms_g;
LABEL_PARSE_EAP:
ms_g = (EAP_MSCHAPV2_GENERAL *)eap;
if (ms_g->Chap_Opcode == EAP_MSCHAPV2_OP_CHALLENGE)
{
EAP_MSCHAPV2_CHALLENGE *ms_c = (EAP_MSCHAPV2_CHALLENGE *)eap;
if (ms_c->Chap_ValueSize == 16)
{
Copy(&e->MsChapV2Challenge, ms_c, sizeof(EAP_MSCHAPV2_CHALLENGE));
ret = true;
}
}
}
}
}
}
FreeRadiusPacket(request1);
FreeRadiusPacket(request2);
FreeRadiusPacket(response1);
FreeRadiusPacket(response2);
Free(eap1);
Free(eap2);
return ret;
}
// Send a packet and recv a response
RADIUS_PACKET *EapSendPacketAndRecvResponse(EAP_CLIENT *e, RADIUS_PACKET *r)
{
SOCKSET set;
UINT64 giveup_tick = 0;
UINT64 next_send_tick = 0;
bool select_inited = false;
bool free_r = false;
RADIUS_PACKET *ret = NULL;
if (e == NULL || r == NULL)
{
return NULL;
}
ClearBuf(e->PEAP_CurrentReceivingMsg);
e->PEAP_CurrentReceivingTotalSize = 0;
InitSockSet(&set);
AddSockSet(&set, e->UdpSock);
while (true)
{
UINT64 now = Tick64();
UINT wait_time = INFINITE;
bool is_finish = false;
if (giveup_tick == 0)
{
giveup_tick = now + (UINT64)e->GiveupTimeout;
}
if (giveup_tick <= now)
{
break;
}
if (select_inited)
{
UINT num_error = 0;
while (true)
{
IP from_ip;
UINT from_port;
UINT size;
UCHAR *tmp = e->TmpBuffer;
size = RecvFrom(e->UdpSock, &from_ip, &from_port, tmp, sizeof(e->TmpBuffer));
if (size == 0 && e->UdpSock->IgnoreRecvErr == false)
{
// UDP socket error
is_finish = true;
break;
}
else if (size == SOCK_LATER)
{
break;
}
if (size == 0 && e->UdpSock->IgnoreRecvErr)
{
num_error++;
if (num_error >= 100)
{
is_finish = true;
break;
}
}
// Receive a response packet
if (size != SOCK_LATER && size >= 1)
{
if (CmpIpAddr(&from_ip, &e->ServerIp) == 0 && from_port == e->ServerPort)
{
RADIUS_PACKET *rp = ParseRadiusPacket(tmp, size);
if (rp != NULL)
{
RADIUS_AVP *eap_msg = GetRadiusAvp(rp, RADIUS_ATTRIBUTE_EAP_MESSAGE);
RADIUS_AVP *vlan_avp = GetRadiusAvp(rp, RADIUS_ATTRIBUTE_VLAN_ID);
if (eap_msg != NULL)
{
e->LastRecvEapId = ((EAP_MESSAGE *)(eap_msg->Data))->Id;
}
if (vlan_avp != NULL)
{
// VLAN ID
UINT vlan_id = 0;
char tmp[32];
Zero(tmp, sizeof(tmp));
Copy(tmp, vlan_avp->Data, MIN(vlan_avp->DataSize, sizeof(tmp) - 1));
vlan_id = ToInt(tmp);
e->LastRecvVLanId = vlan_id;
}
// Validate the received packet
if (rp->Parse_EapAuthMessagePos != 0 && rp->Parse_AuthenticatorPos != 0)
{
UCHAR *tmp_buffer = Clone(tmp, size);
UCHAR auth1[16];
UCHAR auth2[16];
Copy(auth1, &tmp_buffer[rp->Parse_EapAuthMessagePos], 16);
Zero(&tmp_buffer[rp->Parse_EapAuthMessagePos], 16);
Copy(&tmp_buffer[rp->Parse_AuthenticatorPos], r->Authenticator, 16);
HMacMd5(auth2, e->SharedSecret, StrLen(e->SharedSecret),
tmp_buffer, size);
if (Cmp(auth1, auth2, 16) == 0)
{
bool send_ack_packet = false;
// ok
Copy(e->LastState, rp->Parse_State, rp->Parse_StateSize);
e->LastStateSize = rp->Parse_StateSize;
if (rp->Parse_EapMessage_DataSize != 0 && rp->Parse_EapMessage != NULL)
{
EAP_MESSAGE *eap_msg = (EAP_MESSAGE *)rp->Parse_EapMessage;
if (eap_msg->Type == EAP_TYPE_PEAP)
{
EAP_PEAP *peap_message = (EAP_PEAP *)eap_msg;
if (peap_message->TlsFlags & EAP_TLS_FLAGS_MORE_FRAGMENTS || e->PEAP_CurrentReceivingTotalSize != 0)
{
// more fragments: reply ack
RADIUS_PACKET *ack_packet = NewRadiusPacket(RADIUS_CODE_ACCESS_REQUEST, e->NextRadiusPacketId++);
EAP_PEAP *ack_msg = ZeroMalloc(sizeof(EAP_PEAP));
EapSetRadiusGeneralAttributes(ack_packet, e);
if (e->LastStateSize != 0)
{
Add(ack_packet->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_STATE, 0, 0,
e->LastState, e->LastStateSize));
}
ack_msg->Code = EAP_CODE_RESPONSE;
ack_msg->Id = e->LastRecvEapId;
ack_msg->Len = Endian16(6);
ack_msg->Type = EAP_TYPE_PEAP;
ack_msg->TlsFlags = 0;
Add(ack_packet->AvpList, NewRadiusAvp(RADIUS_ATTRIBUTE_EAP_MESSAGE, 0, 0,
ack_msg, sizeof(EAP_PEAP)));
next_send_tick = 0;
if (free_r)
{
FreeRadiusPacket(r);
}
r = ack_packet;
free_r = true;
Free(ack_msg);
send_ack_packet = true;
if (e->PEAP_CurrentReceivingTotalSize == 0)
{
if (peap_message->TlsFlags & EAP_TLS_FLAGS_LEN)
{
if (Endian16(peap_message->Len) >= 9)
{
UINT total_size = READ_UINT(((UCHAR *)peap_message) + sizeof(EAP_PEAP));
if (total_size < 65536)
{
if (rp->Parse_EapMessage_DataSize >= 1)
{
e->PEAP_CurrentReceivingTotalSize = total_size;
WriteBuf(e->PEAP_CurrentReceivingMsg,
((UCHAR *)peap_message),
rp->Parse_EapMessage_DataSize);
}
}
}
}
}
else
{
if ((!(peap_message->TlsFlags & EAP_TLS_FLAGS_LEN)) &&
rp->Parse_EapMessage_DataSize >= sizeof(EAP_PEAP))
{
WriteBuf(e->PEAP_CurrentReceivingMsg,
((UCHAR *)peap_message) + sizeof(EAP_PEAP),
rp->Parse_EapMessage_DataSize - sizeof(EAP_PEAP));
if (e->PEAP_CurrentReceivingTotalSize <= e->PEAP_CurrentReceivingMsg->Size)
{
// all fragmented segments are arrived
send_ack_packet = false;
is_finish = true;
Free(rp->Parse_EapMessage);
rp->Parse_EapMessage = Clone(e->PEAP_CurrentReceivingMsg->Buf, e->PEAP_CurrentReceivingMsg->Size);
rp->Parse_EapMessage_DataSize = e->PEAP_CurrentReceivingMsg->Size;
}
}
}
}
}
}
if (send_ack_packet == false)
{
ret = rp;
}
}
Free(tmp_buffer);
}
if (ret != NULL)
{
is_finish = true;
break;
}
else
{
FreeRadiusPacket(rp);
}
}
}
}
}
}
if (is_finish)
{
break;
}
if (next_send_tick == 0 || next_send_tick <= now)
{
next_send_tick = now + (UINT64)e->ResendTimeout;
if (EapSendPacket(e, r) == false)
{
is_finish = true;
}
}
wait_time = MIN(wait_time, (UINT)(next_send_tick - now));
wait_time = MIN(wait_time, (UINT)(giveup_tick - now));
wait_time = MAX(wait_time, 1);
if (is_finish)
{
break;
}
Select(&set, wait_time, NULL, NULL);
select_inited = true;
}
if (free_r)
{
FreeRadiusPacket(r);
}
return ret;
}
// Send a RADIUS packet
bool EapSendPacket(EAP_CLIENT *e, RADIUS_PACKET *r)
{
BUF *b;
bool ret = false;
if (e == NULL || r == NULL)
{
return false;
}
b = GenerateRadiusPacket(r, e->SharedSecret);
if (b != NULL)
{
UINT r = SendTo(e->UdpSock, &e->ServerIp, e->ServerPort, b->Buf, b->Size);
if (!(r == 0 && e->UdpSock->IgnoreSendErr == false))
{
ret = true;
}
FreeBuf(b);
}
return ret;
}
// New EAP client
EAP_CLIENT *NewEapClient(IP *server_ip, UINT server_port, char *shared_secret, UINT resend_timeout, UINT giveup_timeout, char *client_ip_str, char *username, char *hubname)
{
EAP_CLIENT *e;
if (server_ip == NULL)
{
return NULL;
}
if (resend_timeout == 0)
{
resend_timeout = RADIUS_RETRY_INTERVAL;
}
if (giveup_timeout == 0)
{
giveup_timeout = RADIUS_RETRY_TIMEOUT;
}
e = ZeroMalloc(sizeof(EAP_CLIENT));
e->Ref = NewRef();
e->NextRadiusPacketId = 1;
e->UdpSock = NewUDPEx(0, IsIP6(server_ip));
Copy(&e->ServerIp, server_ip, sizeof(IP));
e->ServerPort = server_port;
e->ResendTimeout = resend_timeout;
e->GiveupTimeout = giveup_timeout;
StrCpy(e->SharedSecret, sizeof(e->SharedSecret), shared_secret);
StrCpy(e->CalledStationStr, sizeof(e->CalledStationStr), hubname);
StrCpy(e->ClientIpStr, sizeof(e->ClientIpStr), client_ip_str);
StrCpy(e->Username, sizeof(e->Username), username);
e->LastRecvEapId = 0;
e->PEAP_CurrentReceivingMsg = NewBuf();
return e;
}
// Free a EAP client
void ReleaseEapClient(EAP_CLIENT *e)
{
if (e == NULL)
{
return;
}
if (Release(e->Ref) == 0)
{
CleanupEapClient(e);
}
}
void CleanupEapClient(EAP_CLIENT *e)
{
if (e == NULL)
{
return;
}
Disconnect(e->UdpSock);
ReleaseSock(e->UdpSock);
FreeSslPipe(e->SslPipe);
FreeBuf(e->PEAP_CurrentReceivingMsg);
Free(e);
}
// New RADIUS AVP value
RADIUS_AVP *NewRadiusAvp(UCHAR type, UINT vendor_id, UCHAR vendor_code, void *data, UINT size)
{
RADIUS_AVP *p = ZeroMalloc(sizeof(RADIUS_AVP));
p->Type = type;
p->VendorId = vendor_id;
p->VendorCode = vendor_code;
p->DataSize = (UCHAR)size;
Copy(p->Data, data, (UCHAR)size);
if (size >= 256)
{
Debug("!! size = %u\n", size);
}
return p;
}
// New RADIUS packet
RADIUS_PACKET *NewRadiusPacket(UCHAR code, UCHAR packet_id)
{
RADIUS_PACKET *r = ZeroMalloc(sizeof(RADIUS_PACKET));
r->Code = code;
r->PacketId = packet_id;
r->AvpList = NewListFast(NULL);
return r;
}
// Get RADIUS AVP
RADIUS_AVP *GetRadiusAvp(RADIUS_PACKET *p, UCHAR type)
{
UINT i;
if (p == NULL)
{
return NULL;
}
for (i = 0;i < LIST_NUM(p->AvpList);i++)
{
RADIUS_AVP *avp = LIST_DATA(p->AvpList, i);
if (avp->Type == type)
{
return avp;
}
}
return NULL;
}
// Free a RADIUS packet
void FreeRadiusPacket(RADIUS_PACKET *p)
{
UINT i;
if (p == NULL)
{
return;
}
if (p->AvpList != NULL)
{
for (i = 0;i < LIST_NUM(p->AvpList);i++)
{
RADIUS_AVP *a = LIST_DATA(p->AvpList, i);
Free(a);
}
ReleaseList(p->AvpList);
}
Free(p->Parse_EapMessage);
Free(p);
}
// Generate a RADIUS packet
BUF *GenerateRadiusPacket(RADIUS_PACKET *p, char *shared_secret)
{
BUF *b;
UINT i;
UCHAR zero16[16];
UINT len_pos = 0;
UINT eap_auth_pos = 0;
bool exist_eap_msg = false;
bool exist_eap_auth = false;
if (p == NULL)
{
return NULL;
}
Zero(zero16, sizeof(zero16));
// Add EAP message auth packet
for (i = 0;i < LIST_NUM(p->AvpList);i++)
{
RADIUS_AVP *a = (RADIUS_AVP *)LIST_DATA(p->AvpList, i);
if (a->Type == RADIUS_ATTRIBUTE_EAP_MESSAGE)
{
exist_eap_msg = true;
}
if (a->Type == RADIUS_ATTRIBUTE_EAP_AUTHENTICATOR)
{
exist_eap_auth = true;
}
}
if (exist_eap_msg && exist_eap_auth == false)
{
RADIUS_AVP *a = NewRadiusAvp(RADIUS_ATTRIBUTE_EAP_AUTHENTICATOR, 0, 0, zero16, sizeof(zero16));
Add(p->AvpList, a);
}
if (IsZero(p->Authenticator, 16))
{
UCHAR rand16[16];
Rand(rand16, sizeof(rand16));
Copy(p->Authenticator, rand16, 16);
}
b = NewBuf();
WriteBufChar(b, p->Code);
WriteBufChar(b, p->PacketId);
len_pos = b->Current;
WriteBufShort(b, 0);
WriteBuf(b, p->Authenticator, 16);
for (i = 0;i < LIST_NUM(p->AvpList);i++)
{
RADIUS_AVP *a = (RADIUS_AVP *)LIST_DATA(p->AvpList, i);
WriteBufChar(b, a->Type);
if (a->Type != RADIUS_ATTRIBUTE_VENDOR_SPECIFIC)
{
WriteBufChar(b, (UCHAR)((UINT)a->DataSize + 2));
if (a->Type == RADIUS_ATTRIBUTE_EAP_AUTHENTICATOR)
{
eap_auth_pos = b->Current;
if (a->DataSize == 16)
{
Zero(a->Data, sizeof(a->Data));
}
}
WriteBuf(b, a->Data, a->DataSize);
}
else
{
WriteBufChar(b, (UCHAR)((UINT)a->DataSize + 8));
WriteBufInt(b, a->VendorId);
WriteBufChar(b, a->VendorCode);
WriteBufChar(b, (UCHAR)((UINT)a->DataSize + 2));
WriteBuf(b, a->Data, a->DataSize);
}
}
WRITE_USHORT(((UCHAR *)b->Buf) + len_pos, b->Size);
if (eap_auth_pos != 0)
{
UCHAR eap_auth[16];
HMacMd5(eap_auth, shared_secret, StrLen(shared_secret), b->Buf, b->Size);
Copy(((UCHAR *)b->Buf) + eap_auth_pos, eap_auth, 16);
}
SeekBufToBegin(b);
return b;
}
// Parse a RADIUS packet
RADIUS_PACKET *ParseRadiusPacket(void *data, UINT size)
{
RADIUS_PACKET *p = NULL;
BUF *buf = NULL;
USHORT len;
UCHAR auth[16];
if (data == NULL || size == 0)
{
return NULL;
}
p = ZeroMalloc(sizeof(RADIUS_PACKET));
p->AvpList = NewListFast(NULL);
buf = MemToBuf(data, size);
// Code
p->Code = ReadBufChar(buf);
p->PacketId = ReadBufChar(buf);
len = ReadBufShort(buf);
p->Parse_AuthenticatorPos = buf->Current;
if (ReadBuf(buf, auth, 16) != 16)
{
goto LABEL_ERROR;
}
if ((UINT)len < 20)
{
goto LABEL_ERROR;
}
if ((UINT)len > buf->Size)
{
goto LABEL_ERROR;
}
else if ((UINT)len < buf->Size)
{
buf->Size = len;
}
while (true)
{
RADIUS_AVP a;
UCHAR uc;
UINT data_size;
Zero(&a, sizeof(a));
if (ReadBuf(buf, &a.Type, 1) == 0)
{
break;
}
if (a.Type != RADIUS_ATTRIBUTE_VENDOR_SPECIFIC)
{
if (ReadBuf(buf, &uc, 1) == 0)
{
break;
}
data_size = (UINT)uc;
if (data_size < 2)
{
goto LABEL_ERROR;
}
data_size -= 2;
a.DataSize = (UCHAR)data_size;
if (a.Type == RADIUS_ATTRIBUTE_EAP_AUTHENTICATOR && a.DataSize == 16)
{
p->Parse_EapAuthMessagePos = buf->Current;
}
if (ReadBuf(buf, a.Data, a.DataSize) != a.DataSize)
{
goto LABEL_ERROR;
}
if (a.Type == RADIUS_ATTRIBUTE_EAP_MESSAGE && a.DataSize >= 5 && a.DataSize <= 1500)
{
UINT sz_tmp = Endian16(((EAP_MESSAGE *)a.Data)->Len);
if (sz_tmp >= 5 && sz_tmp <= a.DataSize)
{
if (p->Parse_EapMessage == NULL)
{
EAP_MESSAGE *eap = Clone(a.Data, a.DataSize);
p->Parse_EapMessage_DataSize = sz_tmp;
p->Parse_EapMessage = eap;
}
}
}
}
else
{
if (ReadBuf(buf, &uc, 1) == 0)
{
break;
}
data_size = (UINT)uc;
if (data_size < 8)
{
goto LABEL_ERROR;
}
data_size -= 8;
a.VendorId = ReadBufInt(buf);
a.VendorCode = ReadBufChar(buf);
if (ReadBuf(buf, &uc, 1) == 0)
{
break;
}
if (((UINT)uc - 2) != data_size)
{
goto LABEL_ERROR;
}
a.DataSize = (UINT)data_size;
if (ReadBuf(buf, a.Data, a.DataSize) != a.DataSize)
{
goto LABEL_ERROR;
}
}
Add(p->AvpList, Clone(&a, sizeof(RADIUS_AVP)));
}
FreeBuf(buf);
if (true)
{
UINT num, i;
RADIUS_AVP *avp = GetRadiusAvp(p, RADIUS_ATTRIBUTE_STATE);
if (avp != NULL)
{
Copy(p->Parse_State, avp->Data, avp->DataSize);
p->Parse_StateSize = avp->DataSize;
}
num = 0;
for (i = 0;i < LIST_NUM(p->AvpList);i++)
{
RADIUS_AVP *avp = LIST_DATA(p->AvpList, i);
if (avp->Type == RADIUS_ATTRIBUTE_EAP_MESSAGE)
{
num++;
}
}
if (num >= 2)
{
// Reassemble multiple EAP messages
BUF *b = NewBuf();
for (i = 0;i < LIST_NUM(p->AvpList);i++)
{
RADIUS_AVP *avp = LIST_DATA(p->AvpList, i);
if (avp->Type == RADIUS_ATTRIBUTE_EAP_MESSAGE)
{
WriteBuf(b, avp->Data, avp->DataSize);
}
}
if (Endian16(((EAP_MESSAGE *)b->Buf)->Len) <= b->Size)
{
if (p->Parse_EapMessage != NULL)
{
Free(p->Parse_EapMessage);
}
p->Parse_EapMessage_DataSize = b->Size;
p->Parse_EapMessage_DataSize = MIN(p->Parse_EapMessage_DataSize, 1500);
p->Parse_EapMessage = Clone(b->Buf, p->Parse_EapMessage_DataSize);
}
FreeBuf(b);
}
}
return p;
LABEL_ERROR:
if (p != NULL)
{
FreeRadiusPacket(p);
}
if (buf != NULL)
{
FreeBuf(buf);
}
return NULL;
}
////////// Classical implementation
// Attempts Radius authentication (with specifying retry interval and multiple server)
bool RadiusLogin(CONNECTION *c, char *server, UINT port, UCHAR *secret, UINT secret_size, wchar_t *username, char *password, UINT interval, UCHAR *mschap_v2_server_response_20,
RADIUS_LOGIN_OPTION *opt, char *hubname)
{
UCHAR random[MD5_SIZE];
UCHAR id;
BUF *encrypted_password = NULL;
BUF *user_name = NULL;
//IP ip;
bool ret = false;
TOKEN_LIST *token;
UINT i;
LIST *ip_list;
IPC_MSCHAP_V2_AUTHINFO mschap;
bool is_mschap;
char client_ip_str[MAX_SIZE];
RADIUS_LOGIN_OPTION opt_dummy;
static UINT packet_id = 0;
// Validate arguments
if (server == NULL || port == 0 || (secret_size != 0 && secret == NULL) || username == NULL || password == NULL)
{
return false;
}
if (opt == NULL)
{
Zero(&opt_dummy, sizeof(opt_dummy));
opt = &opt_dummy;
}
opt->Out_VLanId = 0;
Zero(client_ip_str, sizeof(client_ip_str));
if (c != NULL && c->FirstSock != NULL)
{
IPToStr(client_ip_str, sizeof(client_ip_str), &c->FirstSock->RemoteIP);
}
// Parse the MS-CHAP v2 authentication data
Zero(&mschap, sizeof(mschap));
is_mschap = ParseAndExtractMsChapV2InfoFromPassword(&mschap, password);
if (is_mschap && mschap.MsChapV2_EapClient != NULL)
{
// Try the EAP authentication for RADIUS first
EAP_CLIENT *eap = mschap.MsChapV2_EapClient;
if (eap->PeapMode == false)
{
ret = EapClientSendMsChapv2AuthClientResponse(eap, mschap.MsChapV2_ClientResponse,
mschap.MsChapV2_ClientChallenge);
}
else
{
ret = PeapClientSendMsChapv2AuthClientResponse(eap, mschap.MsChapV2_ClientResponse,
mschap.MsChapV2_ClientChallenge);
}
if (ret)
{
Copy(mschap_v2_server_response_20, eap->ServerResponse, 20);
if (opt->In_CheckVLanId)
{
opt->Out_VLanId = eap->LastRecvVLanId;
}
return true;
}
else
{
return false;
}
}
// Split the server into tokens
token = ParseToken(server, " ,;\t");
// Get the IP address of the server
ip_list = NewListFast(NULL);
for(i = 0; i < token->NumTokens; i++)
{
IP *tmp_ip = Malloc(sizeof(IP));
if (GetIP(tmp_ip, token->Token[i]))
{
Add(ip_list, tmp_ip);
}
else if (GetIPEx(tmp_ip, token->Token[i], true))
{
Add(ip_list, tmp_ip);
}
else
{
Free(tmp_ip);
}
}
FreeToken(token);
if(LIST_NUM(ip_list) == 0)
{
ReleaseList(ip_list);
return false;
}
// Random number generation
Rand(random, sizeof(random));
// ID generation
id = (UCHAR)(packet_id % 254 + 1);
packet_id++;
if (is_mschap == false)
{
// Encrypt the password
encrypted_password = RadiusEncryptPassword(password, random, secret, secret_size);
if (encrypted_password == NULL)
{
// Encryption failure
// Release the ip_list
for(i = 0; i < LIST_NUM(ip_list); i++)
{
IP *tmp_ip = LIST_DATA(ip_list, i);
Free(tmp_ip);
}
ReleaseList(ip_list);
return false;
}
}
// Generate the user name packet
user_name = RadiusCreateUserName(username);
if (user_name != NULL)
{
// Generate a password packet
BUF *user_password = (is_mschap ? NULL : RadiusCreateUserPassword(encrypted_password->Buf, encrypted_password->Size));
BUF *nas_id;
if (IsEmptyStr(opt->NasId))
{
nas_id = RadiusCreateNasId(CEDAR_SERVER_STR);
}
else
{
nas_id = RadiusCreateNasId(opt->NasId);
}
if (is_mschap || user_password != NULL)
{
UINT64 start;
UINT64 next_send_time;
UCHAR tmp[MAX_SIZE];
UINT recv_buf_size = 32768;
UCHAR *recv_buf = MallocEx(recv_buf_size, true);
// Generate an UDP packet
BUF *p = NewBuf();
UCHAR type = 1;
SOCK *sock;
USHORT sz = 0;
UINT pos = 0;
BOOL *finish = ZeroMallocEx(sizeof(BOOL) * LIST_NUM(ip_list), true);
Zero(tmp, sizeof(tmp));
WriteBuf(p, &type, 1);
WriteBuf(p, &id, 1);
WriteBuf(p, &sz, 2);
WriteBuf(p, random, 16);
WriteBuf(p, user_name->Buf, user_name->Size);
if (is_mschap == false)
{
UINT ui;
// PAP
WriteBuf(p, user_password->Buf, user_password->Size);
WriteBuf(p, nas_id->Buf, nas_id->Size);
// Service-Type
ui = Endian32(2);
RadiusAddValue(p, 6, 0, 0, &ui, sizeof(ui));
// NAS-Port-Type
ui = Endian32(5);
RadiusAddValue(p, 61, 0, 0, &ui, sizeof(ui));
// Tunnel-Type
ui = Endian32(1);
RadiusAddValue(p, 64, 0, 0, &ui, sizeof(ui));
// Tunnel-Medium-Type
ui = Endian32(1);
RadiusAddValue(p, 65, 0, 0, &ui, sizeof(ui));
// Called-Station-ID - VPN Hub Name
if (IsEmptyStr(hubname) == false)
{
RadiusAddValue(p, 30, 0, 0, hubname, StrLen(hubname));
}
// Calling-Station-Id
RadiusAddValue(p, 31, 0, 0, client_ip_str, StrLen(client_ip_str));
// Tunnel-Client-Endpoint
RadiusAddValue(p, 66, 0, 0, client_ip_str, StrLen(client_ip_str));
}
else
{
// MS-CHAP v2
static UINT session_id = 0;
USHORT us;
UINT ui;
char *ms_ras_version = "MSRASV5.20";
UCHAR ms_chapv2_response[50];
// Acct-Session-Id
us = Endian16(session_id % 254 + 1);
session_id++;
RadiusAddValue(p, 44, 0, 0, &us, sizeof(us));
// NAS-IP-Address
if (c != NULL && c->FirstSock != NULL && c->FirstSock->IPv6 == false)
{
ui = IPToUINT(&c->FirstSock->LocalIP);
RadiusAddValue(p, 4, 0, 0, &ui, sizeof(ui));
}
// Service-Type
ui = Endian32(2);
RadiusAddValue(p, 6, 0, 0, &ui, sizeof(ui));
// MS-RAS-Vendor
ui = Endian32(311);
RadiusAddValue(p, 26, 311, 9, &ui, sizeof(ui));
// MS-RAS-Version
RadiusAddValue(p, 26, 311, 18, ms_ras_version, StrLen(ms_ras_version));
// NAS-Port-Type
ui = Endian32(5);
RadiusAddValue(p, 61, 0, 0, &ui, sizeof(ui));
// Tunnel-Type
ui = Endian32(1);
RadiusAddValue(p, 64, 0, 0, &ui, sizeof(ui));
// Tunnel-Medium-Type
ui = Endian32(1);
RadiusAddValue(p, 65, 0, 0, &ui, sizeof(ui));
// Called-Station-ID - VPN Hub Name
if (IsEmptyStr(hubname) == false)
{
RadiusAddValue(p, 30, 0, 0, hubname, StrLen(hubname));
}
// Calling-Station-Id
RadiusAddValue(p, 31, 0, 0, client_ip_str, StrLen(client_ip_str));
// Tunnel-Client-Endpoint
RadiusAddValue(p, 66, 0, 0, client_ip_str, StrLen(client_ip_str));
// MS-RAS-Client-Version
RadiusAddValue(p, 26, 311, 35, ms_ras_version, StrLen(ms_ras_version));
// MS-RAS-Client-Name
RadiusAddValue(p, 26, 311, 34, client_ip_str, StrLen(client_ip_str));
// MS-CHAP-Challenge
RadiusAddValue(p, 26, 311, 11, mschap.MsChapV2_ServerChallenge, sizeof(mschap.MsChapV2_ServerChallenge));
// MS-CHAP2-Response
Zero(ms_chapv2_response, sizeof(ms_chapv2_response));
Copy(ms_chapv2_response + 2, mschap.MsChapV2_ClientChallenge, 16);
Copy(ms_chapv2_response + 2 + 16 + 8, mschap.MsChapV2_ClientResponse, 24);
RadiusAddValue(p, 26, 311, 25, ms_chapv2_response, sizeof(ms_chapv2_response));
// NAS-ID
WriteBuf(p, nas_id->Buf, nas_id->Size);
}
SeekBuf(p, 0, 0);
WRITE_USHORT(((UCHAR *)p->Buf) + 2, (USHORT)p->Size);
// Create a socket
sock = NewUDPEx(0, IsIP6(LIST_DATA(ip_list, pos)));
// Transmission process start
start = Tick64();
if(interval < RADIUS_RETRY_INTERVAL)
{
interval = RADIUS_RETRY_INTERVAL;
}
else if(interval > RADIUS_RETRY_TIMEOUT)
{
interval = RADIUS_RETRY_TIMEOUT;
}
next_send_time = start + (UINT64)interval;
while (true)
{
UINT server_port;
UINT recv_size;
//IP server_ip;
SOCKSET set;
UINT64 now;
SEND_RETRY:
//SendTo(sock, &ip, port, p->Buf, p->Size);
SendTo(sock, LIST_DATA(ip_list, pos), port, p->Buf, p->Size);
Debug("send to host:%u\n", pos);
next_send_time = Tick64() + (UINT64)interval;
RECV_RETRY:
now = Tick64();
if (next_send_time <= now)
{
// Switch the host to refer
pos++;
pos = pos % LIST_NUM(ip_list);
goto SEND_RETRY;
}
if ((start + RADIUS_RETRY_TIMEOUT) < now)
{
// Time-out
break;
}
InitSockSet(&set);
AddSockSet(&set, sock);
Select(&set, (UINT)(next_send_time - now), NULL, NULL);
recv_size = RecvFrom(sock, LIST_DATA(ip_list, pos), &server_port, recv_buf, recv_buf_size);
if (recv_size == 0)
{
Debug("Radius recv_size 0\n");
finish[pos] = TRUE;
for(i = 0; i < LIST_NUM(ip_list); i++)
{
if(finish[i] == FALSE)
{
// Switch the host to refer
pos++;
pos = pos % LIST_NUM(ip_list);
goto SEND_RETRY;
}
}
// Failure
break;
}
else if (recv_size == SOCK_LATER)
{
// Waiting
goto RECV_RETRY;
}
else
{
// Check such as the IP address
if (/*Cmp(&server_ip, &ip, sizeof(IP)) != 0 || */server_port != port)
{
goto RECV_RETRY;
}
// Success
if (recv_buf[0] == 2)
{
ret = true;
if (is_mschap && mschap_v2_server_response_20 != NULL)
{
// Cutting corners Zurukko
UCHAR signature[] = {0x1A, 0x33, 0x00, 0x00, 0x01, 0x37, 0x1A, 0x2D, 0x00, 0x53, 0x3D, };
UINT i = SearchBin(recv_buf, 0, recv_buf_size, signature, sizeof(signature));
if (i == INFINITE || ((i + sizeof(signature) + 40) > recv_buf_size))
{
ret = false;
}
else
{
char tmp[MAX_SIZE];
BUF *b;
Zero(tmp, sizeof(tmp));
Copy(tmp, recv_buf + i + sizeof(signature), 40);
b = StrToBin(tmp);
if (b != NULL && b->Size == 20)
{
WHERE;
Copy(mschap_v2_server_response_20, b->Buf, 20);
}
else
{
WHERE;
ret = false;
}
FreeBuf(b);
}
}
if (opt->In_CheckVLanId)
{
BUF *buf = NewBufFromMemory(recv_buf, recv_size);
LIST *o = RadiusParseOptions(buf);
if (o != NULL)
{
DHCP_OPTION *vlan_option = GetDhcpOption(o, RADIUS_ATTRIBUTE_VLAN_ID);
if (vlan_option != NULL)
{
UINT vlan_id = 0;
char tmp[32];
Zero(tmp, sizeof(tmp));
Copy(tmp, vlan_option->Data, MIN(vlan_option->Size, sizeof(tmp) - 1));
vlan_id = ToInt(tmp);
opt->Out_VLanId = vlan_id;
}
}
FreeBuf(buf);
FreeDhcpOptions(o);
}
}
break;
}
}
Free(finish);
// Release the socket
ReleaseSock(sock);
FreeBuf(p);
FreeBuf(user_password);
Free(recv_buf);
}
FreeBuf(nas_id);
FreeBuf(user_name);
}
// Release the ip_list
for(i = 0; i < LIST_NUM(ip_list); i++)
{
IP *tmp_ip = LIST_DATA(ip_list, i);
Free(tmp_ip);
}
ReleaseList(ip_list);
// Release the memory
FreeBuf(encrypted_password);
return ret;
}
// Parse RADIUS attributes
LIST *RadiusParseOptions(BUF *b)
{
LIST *o;
UCHAR code;
UCHAR id;
USHORT len;
UCHAR auth[16];
// Validate arguments
if (b == NULL)
{
return NULL;
}
o = NewList(NULL);
ReadBuf(b, &code, 1);
ReadBuf(b, &id, 1);
len = 0;
ReadBuf(b, &len, 2);
len = Endian16(len);
ReadBuf(b, auth, 16);
while (true)
{
UCHAR attribute_id;
UCHAR size;
UCHAR data[256];
DHCP_OPTION *d;
if (ReadBuf(b, &attribute_id, 1) != 1)
{
break;
}
if (ReadBuf(b, &size, 1) != 1)
{
break;
}
if (size <= 2)
{
break;
}
size -= 2;
if (ReadBuf(b, data, size) != size)
{
break;
}
d = ZeroMalloc(sizeof(DHCP_OPTION));
d->Id = attribute_id;
d->Size = size;
d->Data = Clone(data, d->Size);
Add(o, d);
}
return o;
}
// Adding Attributes
void RadiusAddValue(BUF *b, UCHAR t, UINT v, UCHAR vt, void *data, UINT size)
{
UINT len;
// Validate arguments
if (b == NULL || (data == NULL && size != 0))
{
return;
}
// type
WriteBufChar(b, t);
// length
len = 2 + size;
if (t == 26)
{
len += 6;
}
WriteBufChar(b, (UCHAR)len);
if (t != 26)
{
// value
WriteBuf(b, data, size);
}
else
{
// vendor
WriteBufInt(b, v);
// vendor type
WriteBufChar(b, vt);
// length2
len = size + 2;
WriteBufChar(b, (UCHAR)len);
// value
WriteBuf(b, data, size);
}
}
// Create a password attribute for Radius
BUF *RadiusCreateUserPassword(void *data, UINT size)
{
BUF *b;
UCHAR code, sz;
// Validate arguments
if (size != 0 && data == NULL || size >= 253)
{
return NULL;
}
b = NewBuf();
code = 2;
sz = 2 + (UCHAR)size;
WriteBuf(b, &code, 1);
WriteBuf(b, &sz, 1);
WriteBuf(b, data, size);
return b;
}
// Generate an ID attribute of Nas
BUF *RadiusCreateNasId(char *name)
{
BUF *b;
UCHAR code, size;
// Validate arguments
if (name == NULL)
{
return NULL;
}
if (StrLen(name) == 0 || StrLen(name) >= 128)
{
return NULL;
}
b = NewBuf();
code = 32;
size = 2 + (UCHAR)StrLen(name);
WriteBuf(b, &code, 1);
WriteBuf(b, &size, 1);
WriteBuf(b, name, StrLen(name));
return b;
}
// Create a user name attribute for Radius
BUF *RadiusCreateUserName(wchar_t *username)
{
BUF *b;
UCHAR code, size;
UCHAR utf8[254];
// Validate arguments
if (username == NULL)
{
return NULL;
}
// Convert the user name to a Unicode string
UniToStr(utf8, sizeof(utf8), username);
utf8[253] = 0;
b = NewBuf();
code = 1;
size = 2 + (UCHAR)StrLen(utf8);
WriteBuf(b, &code, 1);
WriteBuf(b, &size, 1);
WriteBuf(b, utf8, StrLen(utf8));
return b;
}
// Encrypt the password for the Radius
BUF *RadiusEncryptPassword(char *password, UCHAR *random, UCHAR *secret, UINT secret_size)
{
UINT n, i;
BUF *buf;
UCHAR c[16][16]; // Result
UCHAR b[16][16]; // Result
UCHAR p[16][16]; // Password
// Validate arguments
if (password == NULL || random == NULL || (secret_size != 0 && secret == NULL))
{
return NULL;
}
if (StrLen(password) > 256)
{
// Password is too long
return NULL;
}
// Initialize
Zero(c, sizeof(c));
Zero(p, sizeof(p));
Zero(b, sizeof(b));
// Divide the password per 16 characters
Copy(p, password, StrLen(password));
// Calculate the number of blocks
n = StrLen(password) / 16;
if ((StrLen(password) % 16) != 0)
{
n++;
}
// Encryption processing
for (i = 0;i < n;i++)
{
// Calculation of b[i]
UINT j;
BUF *tmp = NewBuf();
WriteBuf(tmp, secret, secret_size);
if (i == 0)
{
WriteBuf(tmp, random, 16);
}
else
{
WriteBuf(tmp, c[i - 1], 16);
}
Hash(b[i], tmp->Buf, tmp->Size, false);
FreeBuf(tmp);
// Calculation of c[i]
for (j = 0;j < 16;j++)
{
c[i][j] = p[i][j] ^ b[i][j];
}
}
// Return the results
buf = NewBuf();
WriteBuf(buf, c, n * 16);
return buf;
}