1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-09-20 10:40:39 +03:00
SoftEtherVPN/src/Cedar/Protocol.c
2018-10-30 15:11:16 +01:00

7494 lines
177 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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.
// Protocol.c
// SoftEther protocol related routines
#include "CedarPch.h"
static UCHAR ssl_packet_start[3] = {0x17, 0x03, 0x00};
// Download and save intermediate certificates if necessary
bool DownloadAndSaveIntermediateCertificatesIfNecessary(X *x)
{
LIST *o;
bool ret = false;
// Validate arguments
if (x == NULL)
{
return false;
}
if (x->root_cert)
{
return true;
}
o = NewCertList(true);
ret = TryGetRootCertChain(o, x, true, NULL);
FreeCertList(o);
return ret;
}
// Attempt to fetch the full chain of the specified cert
bool TryGetRootCertChain(LIST *o, X *x, bool auto_save, X **found_root_x)
{
bool ret = false;
LIST *chain = NULL;
LIST *current_chain_dir = NULL;
// Validate arguments
if (o == NULL || x == NULL)
{
return false;
}
chain = NewCertList(false);
ret = TryGetParentCertFromCertList(o, x, chain);
if (ret)
{
UINT i;
DIRLIST *dir;
wchar_t dirname[MAX_SIZE];
wchar_t exedir[MAX_SIZE];
GetExeDirW(exedir, sizeof(exedir));
CombinePathW(dirname, sizeof(dirname), exedir, L"chain_certs");
MakeDirExW(dirname);
if (auto_save)
{
// delete the current auto_save files
dir = EnumDirW(dirname);
if (dir != NULL)
{
for (i = 0;i < dir->NumFiles;i++)
{
DIRENT *e = dir->File[i];
if (e->Folder == false)
{
if (UniStartWith(e->FileNameW, AUTO_DOWNLOAD_CERTS_PREFIX))
{
wchar_t tmp[MAX_SIZE];
CombinePathW(tmp, sizeof(tmp), dirname, e->FileNameW);
FileDeleteW(tmp);
}
}
}
FreeDir(dir);
}
}
current_chain_dir = NewCertList(false);
AddAllChainCertsToCertList(current_chain_dir);
for (i = 0;i < LIST_NUM(chain);i++)
{
wchar_t tmp[MAX_SIZE];
X *xx = LIST_DATA(chain, i);
GetAllNameFromName(tmp, sizeof(tmp), xx->subject_name);
Debug("depth = %u, subject = %S\n", i, tmp);
if (auto_save && CompareX(x, xx) == false && IsXInCertList(current_chain_dir, xx) == false)
{
wchar_t fn[MAX_PATH];
char hex_a[128];
wchar_t hex[128];
UCHAR hash[SHA1_SIZE];
wchar_t tmp[MAX_SIZE];
BUF *b;
GetXDigest(xx, hash, true);
BinToStr(hex_a, sizeof(hex_a), hash, SHA1_SIZE);
StrToUni(hex, sizeof(hex), hex_a);
UniStrCpy(fn, sizeof(fn), AUTO_DOWNLOAD_CERTS_PREFIX);
UniStrCat(fn, sizeof(fn), hex);
UniStrCat(fn, sizeof(fn), L".cer");
CombinePathW(tmp, sizeof(tmp), dirname, fn);
b = XToBuf(xx, true);
DumpBufW(b, tmp);
FreeBuf(b);
}
if (xx->root_cert)
{
if (found_root_x != NULL)
{
*found_root_x = CloneX(xx);
}
}
}
}
FreeCertList(chain);
FreeCertList(current_chain_dir);
return ret;
}
// Try get the parent cert
bool TryGetParentCertFromCertList(LIST *o, X *x, LIST *found_chain)
{
bool ret = false;
X *r;
bool do_free = false;
// Validate arguments
if (o == NULL || x == NULL || found_chain == NULL)
{
return false;
}
if (LIST_NUM(found_chain) >= FIND_CERT_CHAIN_MAX_DEPTH)
{
return false;
}
Add(found_chain, CloneX(x));
if (x->root_cert)
{
return true;
}
r = FindCertIssuerFromCertList(o, x);
if (r == NULL)
{
if (IsEmptyStr(x->issuer_url) == false)
{
r = DownloadCert(x->issuer_url);
if (CheckXEx(x, r, true, true) && CompareX(x, r) == false)
{
// found
do_free = true;
}
else
{
// invalid
FreeX(r);
r = NULL;
}
}
}
if (r != NULL)
{
ret = TryGetParentCertFromCertList(o, r, found_chain);
}
if (do_free)
{
FreeX(r);
}
return ret;
}
// Find the issuer of the cert from the cert list
X *FindCertIssuerFromCertList(LIST *o, X *x)
{
UINT i;
// Validate arguments
if (o == NULL || x == NULL)
{
return NULL;
}
if (x->root_cert)
{
return NULL;
}
for (i = 0;i < LIST_NUM(o);i++)
{
X *xx = LIST_DATA(o, i);
if (CheckXEx(x, xx, true, true))
{
if (CompareX(x, xx) == false)
{
return xx;
}
}
}
return NULL;
}
// Download a cert by using HTTP
X *DownloadCert(char *url)
{
BUF *b;
URL_DATA url_data;
X *ret = NULL;
// Validate arguments
if (IsEmptyStr(url))
{
return NULL;
}
Debug("Trying to download a cert from %s ...\n", url);
if (ParseUrl(&url_data, url, false, NULL) == false)
{
Debug("Download failed.\n");
return NULL;
}
b = HttpRequestEx(&url_data, NULL, CERT_HTTP_DOWNLOAD_TIMEOUT, CERT_HTTP_DOWNLOAD_TIMEOUT,
NULL, false, NULL, NULL, NULL, NULL, NULL, CERT_HTTP_DOWNLOAD_MAXSIZE);
if (b == NULL)
{
Debug("Download failed.\n");
return NULL;
}
ret = BufToX(b, IsBase64(b));
FreeBuf(b);
Debug("Download ok.\n");
return ret;
}
// New cert list
LIST *NewCertList(bool load_root_and_chain)
{
LIST *o;
o = NewList(NULL);
if (load_root_and_chain)
{
AddAllRootCertsToCertList(o);
AddAllChainCertsToCertList(o);
}
return o;
}
// Free cert list
void FreeCertList(LIST *o)
{
UINT i;
// Validate arguments
if (o == NULL)
{
return;
}
for (i = 0;i < LIST_NUM(o);i++)
{
X *x = LIST_DATA(o, i);
FreeX(x);
}
ReleaseList(o);
}
// Check whether the cert is in the cert list
bool IsXInCertList(LIST *o, X *x)
{
UINT i;
// Validate arguments
if (o == NULL || x == NULL)
{
return false;
}
for (i = 0;i < LIST_NUM(o);i++)
{
X *xx = LIST_DATA(o, i);
if (CompareX(x, xx))
{
return true;
}
}
return false;
}
// Add a cert to the cert list
void AddXToCertList(LIST *o, X *x)
{
// Validate arguments
if (o == NULL || x == NULL)
{
return;
}
if (IsXInCertList(o, x))
{
return;
}
if (CheckXDateNow(x) == false)
{
return;
}
Add(o, CloneX(x));
}
// Add all chain certs to the cert list
void AddAllChainCertsToCertList(LIST *o)
{
wchar_t dirname[MAX_SIZE];
wchar_t exedir[MAX_SIZE];
DIRLIST *dir;
// Validate arguments
if (o == NULL)
{
return;
}
GetExeDirW(exedir, sizeof(exedir));
CombinePathW(dirname, sizeof(dirname), exedir, L"chain_certs");
MakeDirExW(dirname);
dir = EnumDirW(dirname);
if (dir != NULL)
{
UINT i;
for (i = 0;i < dir->NumFiles;i++)
{
DIRENT *e = dir->File[i];
if (e->Folder == false)
{
wchar_t tmp[MAX_SIZE];
X *x;
CombinePathW(tmp, sizeof(tmp), dirname, e->FileNameW);
x = FileToXW(tmp);
if (x != NULL)
{
AddXToCertList(o, x);
FreeX(x);
}
}
}
FreeDir(dir);
}
}
// Add all root certs to the cert list
void AddAllRootCertsToCertList(LIST *o)
{
BUF *buf;
PACK *p;
UINT num_ok = 0, num_error = 0;
// Validate arguments
if (o == NULL)
{
return;
}
buf = ReadDump(ROOT_CERTS_FILENAME);
if (buf == NULL)
{
return;
}
p = BufToPack(buf);
if (p != NULL)
{
UINT num = PackGetIndexCount(p, "cert");
UINT i;
for (i = 0;i < num;i++)
{
bool ok = false;
BUF *b = PackGetBufEx(p, "cert", i);
if (b != NULL)
{
X *x = BufToX(b, false);
if (x != NULL)
{
AddXToCertList(o, x);
ok = true;
FreeX(x);
}
FreeBuf(b);
}
if (ok)
{
num_ok++;
}
else
{
num_error++;
}
}
FreePack(p);
}
FreeBuf(buf);
Debug("AddAllRootCertsToCertList: ok=%u error=%u total_list_len=%u\n", num_ok, num_error, LIST_NUM(o));
}
// Convert the date of YYYYMMDD format to a number
UINT64 ShortStrToDate64(char *str)
{
UINT v;
SYSTEMTIME st;
// Validate arguments
if (str == NULL)
{
return 0;
}
v = ToInt(str);
Zero(&st, sizeof(st));
st.wYear = (v % 100000000) / 10000;
st.wMonth = (v % 10000) / 100;
st.wDay = v % 100;
return SystemToUINT64(&st);
}
// Handle the response that is returned from the server in the update client
void UpdateClientThreadProcessResults(UPDATE_CLIENT *c, BUF *b)
{
bool exit = false;
// Validate arguments
if (c == NULL || b == NULL)
{
return;
}
SeekBufToBegin(b);
while (true)
{
char *line = CfgReadNextLine(b);
if (line == NULL)
{
break;
}
Trim(line);
if (StartWith(line, "#") == false && IsEmptyStr(line) == false)
{
TOKEN_LIST *t = ParseTokenWithNullStr(line, " \t");
if (t != NULL)
{
if (t->NumTokens >= 5)
{
if (StrCmpi(t->Token[0], c->FamilyName) == 0)
{
// Match
UINT64 date = ShortStrToDate64(t->Token[1]);
if (date != 0)
{
UINT build = ToInt(t->Token[2]);
if (build != 0)
{
if (build > c->MyBuild && build > c->LatestBuild && build > c->Setting.LatestIgnoreBuild)
{
c->Callback(c, build, date, t->Token[3], t->Token[4], &c->HaltFlag, c->Param);
c->LatestBuild = build;
exit = true;
}
}
}
}
}
FreeToken(t);
}
}
Free(line);
if (exit)
{
break;
}
}
}
// Update client main process
void UpdateClientThreadMain(UPDATE_CLIENT *c)
{
char url[MAX_SIZE];
char id[MAX_SIZE];
URL_DATA data;
BUF *cert_hash;
UINT ret = 0;
BUF *recv;
// Validate arguments
if (c == NULL)
{
return;
}
// Generate the URL
Format(url, sizeof(url), IsUseAlternativeHostname() ? UPDATE_SERVER_URL_CHINA : UPDATE_SERVER_URL_GLOBAL, c->FamilyName, c->SoftwareName, c->MyBuild, c->MyLanguage);
if (IsEmptyStr(c->ClientId) == false)
{
Format(id, sizeof(id), "&id=%s", c->ClientId);
StrCat(url, sizeof(url), id);
}
// Get a text file at this URL
if (ParseUrl(&data, url, false, NULL) == false)
{
return;
}
cert_hash = StrToBin(UPDATE_SERVER_CERT_HASH);
StrCpy(data.SniString, sizeof(data.SniString), DDNS_SNI_VER_STRING);
recv = HttpRequestEx3(&data, NULL, UPDATE_CONNECT_TIMEOUT, UPDATE_COMM_TIMEOUT, &ret, false, NULL, NULL,
NULL, ((cert_hash != NULL && (cert_hash->Size % SHA1_SIZE) == 0) ? cert_hash->Buf : NULL),
(cert_hash != NULL ? (cert_hash->Size / SHA1_SIZE) : 0),
(bool *)&c->HaltFlag, 0, NULL, NULL);
FreeBuf(cert_hash);
if (recv != NULL)
{
UpdateClientThreadProcessResults(c, recv);
FreeBuf(recv);
}
}
// Update client main thread
void UpdateClientThreadProc(THREAD *thread, void *param)
{
UPDATE_CLIENT *c = (UPDATE_CLIENT *)param;
bool first_loop = true;
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
while (true)
{
// Termination check
if (c->HaltFlag)
{
break;
}
if (first_loop == false)
{
// Wait for the foreground
if (c->IsForegroundCb != NULL)
{
while (true)
{
if (c->HaltFlag)
{
break;
}
if (c->IsForegroundCb(c, c->Param))
{
break;
}
Wait(c->HaltEvent, 1000);
}
}
}
first_loop = false;
if (c->HaltFlag)
{
break;
}
if (c->Setting.DisableCheck == false)
{
UpdateClientThreadMain(c);
}
// Wait until the next attempt
Wait(c->HaltEvent, GenRandInterval(UPDATE_CHECK_INTERVAL_MIN, UPDATE_CHECK_INTERVAL_MAX));
}
}
// Update the configuration of the update client
void SetUpdateClientSetting(UPDATE_CLIENT *c, UPDATE_CLIENT_SETTING *s)
{
bool old_disable;
// Validate arguments
if (c == NULL || s == NULL)
{
return;
}
old_disable = c->Setting.DisableCheck;
Copy(&c->Setting, s, sizeof(UPDATE_CLIENT_SETTING));
Set(c->HaltEvent);
}
// Start the update client
UPDATE_CLIENT *NewUpdateClient(UPDATE_NOTIFY_PROC *cb, UPDATE_ISFOREGROUND_PROC *isforeground_cb, void *param, char *family_name, char *software_name, wchar_t *software_title, UINT my_build, UINT64 my_date, char *my_lang, UPDATE_CLIENT_SETTING *current_setting, char *client_id)
{
UPDATE_CLIENT *c;
// Validate arguments
if (family_name == NULL || software_title == NULL || software_name == NULL || my_build == 0 ||
my_lang == NULL || current_setting == NULL || cb == NULL)
{
return NULL;
}
c = ZeroMalloc(sizeof(UPDATE_CLIENT));
c->Callback = cb;
c->IsForegroundCb = isforeground_cb;
StrCpy(c->ClientId, sizeof(c->ClientId), client_id);
StrCpy(c->FamilyName, sizeof(c->FamilyName), family_name);
StrCpy(c->SoftwareName, sizeof(c->SoftwareName), software_name);
UniStrCpy(c->SoftwareTitle, sizeof(c->SoftwareTitle), software_title);
c->MyBuild = my_build;
c->MyDate = my_date;
StrCpy(c->MyLanguage, sizeof(c->MyLanguage), my_lang);
Copy(&c->Setting, current_setting, sizeof(c->Setting));
c->Param = param;
c->HaltEvent = NewEvent();
// Create a thread
c->Thread = NewThread(UpdateClientThreadProc, c);
return c;
}
// Terminate the update client
void FreeUpdateClient(UPDATE_CLIENT *c)
{
// Validate arguments
if (c == NULL)
{
return;
}
// Thread stop
c->HaltFlag = true;
Set(c->HaltEvent);
// Wait for thread termination
WaitThread(c->Thread, INFINITE);
ReleaseThread(c->Thread);
ReleaseEvent(c->HaltEvent);
Free(c);
}
// Generate unique IDs for each machine
void GenerateMachineUniqueHash(void *data)
{
BUF *b;
char name[64];
OS_INFO *osinfo;
UINT64 iphash = 0;
// Validate arguments
if (data == NULL)
{
return;
}
iphash = GetHostIPAddressListHash();
b = NewBuf();
GetMachineName(name, sizeof(name));
osinfo = GetOsInfo();
WriteBuf(b, name, StrLen(name));
WriteBufInt64(b, iphash);
WriteBuf(b, &osinfo->OsType, sizeof(osinfo->OsType));
WriteBuf(b, osinfo->KernelName, StrLen(osinfo->KernelName));
WriteBuf(b, osinfo->KernelVersion, StrLen(osinfo->KernelVersion));
WriteBuf(b, osinfo->OsProductName, StrLen(osinfo->OsProductName));
WriteBuf(b, &osinfo->OsServicePack, sizeof(osinfo->OsServicePack));
WriteBuf(b, osinfo->OsSystemName, StrLen(osinfo->OsSystemName));
WriteBuf(b, osinfo->OsVendorName, StrLen(osinfo->OsVendorName));
WriteBuf(b, osinfo->OsVersion, StrLen(osinfo->OsVersion));
Sha0(data, b->Buf, b->Size);
FreeBuf(b);
}
// Convert a node information to a string
void NodeInfoToStr(wchar_t *str, UINT size, NODE_INFO *info)
{
char client_ip[128], server_ip[128], proxy_ip[128], unique_id[128];
// Validate arguments
if (str == NULL || info == NULL)
{
return;
}
IPToStr4or6(client_ip, sizeof(client_ip), info->ClientIpAddress, info->ClientIpAddress6);
IPToStr4or6(server_ip, sizeof(server_ip), info->ServerIpAddress, info->ServerIpAddress6);
IPToStr4or6(proxy_ip, sizeof(proxy_ip), info->ProxyIpAddress, info->ProxyIpAddress6);
BinToStr(unique_id, sizeof(unique_id), info->UniqueId, sizeof(info->UniqueId));
UniFormat(str, size, _UU("LS_NODE_INFO_TAG"), info->ClientProductName,
Endian32(info->ClientProductVer), Endian32(info->ClientProductBuild),
info->ServerProductName, Endian32(info->ServerProductVer), Endian32(info->ServerProductBuild),
info->ClientOsName, info->ClientOsVer, info->ClientOsProductId,
info->ClientHostname, client_ip, Endian32(info->ClientPort),
info->ServerHostname, server_ip, Endian32(info->ServerPort),
info->ProxyHostname, proxy_ip, Endian32(info->ProxyPort),
info->HubName, unique_id);
}
// Comparison of node information
bool CompareNodeInfo(NODE_INFO *a, NODE_INFO *b)
{
// Validate arguments
if (a == NULL || b == NULL)
{
return false;
}
if (StrCmp(a->ClientProductName, b->ClientProductName) != 0)
{
return false;
}
if (a->ClientProductVer != b->ClientProductVer)
{
return false;
}
if (a->ClientProductBuild != b->ClientProductBuild)
{
return false;
}
if (StrCmp(a->ServerProductName, b->ServerProductName) != 0)
{
return false;
}
if (a->ServerProductVer != b->ServerProductVer)
{
return false;
}
if (a->ServerProductBuild != b->ServerProductBuild)
{
return false;
}
if (StrCmp(a->ClientOsName, b->ClientOsName) != 0)
{
return false;
}
if (StrCmp(a->ClientOsVer, b->ClientOsVer) != 0)
{
return false;
}
if (StrCmp(a->ClientOsProductId, b->ClientOsProductId) != 0)
{
return false;
}
if (StrCmp(a->ClientHostname, b->ClientHostname) != 0)
{
return false;
}
if (a->ClientIpAddress != b->ClientIpAddress)
{
return false;
}
if (StrCmp(a->ServerHostname, b->ServerHostname) != 0)
{
return false;
}
if (a->ServerIpAddress != b->ServerIpAddress)
{
return false;
}
if (a->ServerPort != b->ServerPort)
{
return false;
}
if (StrCmp(a->ProxyHostname, b->ProxyHostname) != 0)
{
return false;
}
if (a->ProxyIpAddress != b->ProxyIpAddress)
{
return false;
}
if (a->ProxyPort != b->ProxyPort)
{
return false;
}
if (StrCmp(a->HubName, b->HubName) != 0)
{
return false;
}
if (Cmp(a->UniqueId, b->UniqueId, 16) != 0)
{
return false;
}
return true;
}
// Accept the password change
UINT ChangePasswordAccept(CONNECTION *c, PACK *p)
{
CEDAR *cedar;
UCHAR random[SHA1_SIZE];
char hubname[MAX_HUBNAME_LEN + 1];
char username[MAX_USERNAME_LEN + 1];
UCHAR secure_old_password[SHA1_SIZE];
UCHAR new_password[SHA1_SIZE];
UCHAR new_password_ntlm[SHA1_SIZE];
UCHAR check_secure_old_password[SHA1_SIZE];
UINT ret = ERR_NO_ERROR;
HUB *hub;
bool save = false;
// Validate arguments
if (c == NULL || p == NULL)
{
return ERR_INTERNAL_ERROR;
}
Copy(random, c->Random, SHA1_SIZE);
if (PackGetStr(p, "hubname", hubname, sizeof(hubname)) == false ||
PackGetStr(p, "username", username, sizeof(username)) == false ||
PackGetData2(p, "secure_old_password", secure_old_password, sizeof(secure_old_password)) == false ||
PackGetData2(p, "new_password", new_password, sizeof(new_password)) == false)
{
return ERR_PROTOCOL_ERROR;
}
if (PackGetData2(p, "new_password_ntlm", new_password_ntlm, MD5_SIZE) == false)
{
Zero(new_password_ntlm, sizeof(new_password_ntlm));
}
cedar = c->Cedar;
LockHubList(cedar);
{
hub = GetHub(cedar, hubname);
}
UnlockHubList(cedar);
if (hub == NULL)
{
ret = ERR_HUB_NOT_FOUND;
}
else
{
char tmp[MAX_SIZE];
if (GetHubAdminOption(hub, "deny_change_user_password") != 0)
{
ReleaseHub(hub);
return ERR_NOT_ENOUGH_RIGHT;
}
IPToStr(tmp, sizeof(tmp), &c->FirstSock->RemoteIP);
HLog(hub, "LH_CHANGE_PASSWORD_1", c->Name, tmp);
AcLock(hub);
{
USER *u = AcGetUser(hub, username);
if (u == NULL)
{
HLog(hub, "LH_CHANGE_PASSWORD_2", c->Name, username);
ret = ERR_OLD_PASSWORD_WRONG;
}
else
{
Lock(u->lock);
{
if (u->AuthType != AUTHTYPE_PASSWORD)
{
// Not a password authentication
HLog(hub, "LH_CHANGE_PASSWORD_3", c->Name, username);
ret = ERR_USER_AUTHTYPE_NOT_PASSWORD;
}
else
{
bool fix_password = false;
if (u->Policy != NULL)
{
fix_password = u->Policy->FixPassword;
}
else
{
if (u->Group != NULL)
{
if (u->Group->Policy != NULL)
{
fix_password = u->Group->Policy->FixPassword;
}
}
}
if (fix_password == false)
{
// Confirmation of the old password
AUTHPASSWORD *pw = (AUTHPASSWORD *)u->AuthData;
SecurePassword(check_secure_old_password, pw->HashedKey, random);
if (Cmp(check_secure_old_password, secure_old_password, SHA1_SIZE) != 0)
{
// Old password is incorrect
ret = ERR_OLD_PASSWORD_WRONG;
HLog(hub, "LH_CHANGE_PASSWORD_4", c->Name, username);
}
else
{
// Write a new password
if (Cmp(pw->HashedKey, new_password, SHA1_SIZE) != 0 || IsZero(pw->NtLmSecureHash, MD5_SIZE))
{
Copy(pw->HashedKey, new_password, SHA1_SIZE);
Copy(pw->NtLmSecureHash, new_password_ntlm, MD5_SIZE);
}
HLog(hub, "LH_CHANGE_PASSWORD_5", c->Name, username);
save = true;
}
}
else
{
// Password change is prohibited
ret = ERR_NOT_ENOUGH_RIGHT;
}
}
}
Unlock(u->lock);
ReleaseUser(u);
}
}
AcUnlock(hub);
ReleaseHub(hub);
}
return ret;
}
// Change the password
UINT ChangePassword(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, char *username, char *old_pass, char *new_pass)
{
UINT ret = ERR_NO_ERROR;
UCHAR old_password[SHA1_SIZE];
UCHAR secure_old_password[SHA1_SIZE];
UCHAR new_password[SHA1_SIZE];
UCHAR new_password_ntlm[MD5_SIZE];
SOCK *sock;
SESSION *s;
// Validate arguments
if (cedar == NULL || o == NULL || hubname == NULL || username == NULL || old_pass == NULL || new_pass == NULL)
{
return ERR_INTERNAL_ERROR;
}
// Create a session
s = NewRpcSessionEx(cedar, o, &ret, NULL);
if (s != NULL)
{
PACK *p = NewPack();
sock = s->Connection->FirstSock;
HashPassword(old_password, username, old_pass);
SecurePassword(secure_old_password, old_password, s->Connection->Random);
HashPassword(new_password, username, new_pass);
GenerateNtPasswordHash(new_password_ntlm, new_pass);
PackAddClientVersion(p, s->Connection);
PackAddStr(p, "method", "password");
PackAddStr(p, "hubname", hubname);
PackAddStr(p, "username", username);
PackAddData(p, "secure_old_password", secure_old_password, SHA1_SIZE);
PackAddData(p, "new_password", new_password, SHA1_SIZE);
PackAddData(p, "new_password_ntlm", new_password_ntlm, MD5_SIZE);
if (HttpClientSend(sock, p))
{
PACK *p = HttpClientRecv(sock);
if (p == NULL)
{
ret = ERR_DISCONNECTED;
}
else
{
ret = GetErrorFromPack(p);
}
FreePack(p);
}
else
{
ret = ERR_DISCONNECTED;
}
FreePack(p);
ReleaseSession(s);
}
return ret;
}
// Enumerate HUBs
TOKEN_LIST *EnumHub(SESSION *s)
{
SOCK *sock;
TOKEN_LIST *ret;
PACK *p;
UINT num;
UINT i;
// Validate arguments
if (s == NULL || s->Connection == NULL)
{
return NULL;
}
sock = s->Connection->FirstSock;
if (sock == NULL)
{
return NULL;
}
// Set the Timeout
SetTimeout(sock, 10000);
p = NewPack();
PackAddStr(p, "method", "enum_hub");
PackAddClientVersion(p, s->Connection);
if (HttpClientSend(sock, p) == false)
{
FreePack(p);
return NULL;
}
FreePack(p);
p = HttpClientRecv(sock);
if (p == NULL)
{
return NULL;
}
num = PackGetInt(p, "NumHub");
ret = ZeroMalloc(sizeof(TOKEN_LIST));
ret->NumTokens = num;
ret->Token = ZeroMalloc(sizeof(char *) * num);
for (i = 0;i < num;i++)
{
char tmp[MAX_SIZE];
if (PackGetStrEx(p, "HubName", tmp, sizeof(tmp), i))
{
ret->Token[i] = CopyStr(tmp);
}
}
FreePack(p);
return ret;
}
// Server accepts a connection from client
bool ServerAccept(CONNECTION *c)
{
bool ret = false;
UINT err;
PACK *p;
char username_real[MAX_SIZE];
char method[MAX_SIZE];
char hubname[MAX_SIZE];
char username[MAX_SIZE];
char groupname[MAX_SIZE];
UCHAR session_key[SHA1_SIZE];
UCHAR ticket[SHA1_SIZE];
UINT authtype;
POLICY *policy;
UINT assigned_vlan_id = 0;
HUB *hub;
SESSION *s = NULL;
UINT64 user_expires = 0;
bool use_encrypt;
bool use_compress;
bool half_connection;
UINT adjust_mss;
bool use_udp_acceleration_client;
bool support_hmac_on_udp_acceleration_client = false;
bool support_udp_accel_fast_disconnect_detect;
bool use_hmac_on_udp_acceleration = false;
bool supress_return_pack_error = false;
IP udp_acceleration_client_ip;
UCHAR udp_acceleration_client_key[UDP_ACCELERATION_COMMON_KEY_SIZE];
UINT udp_acceleration_client_port;
bool admin_mode = false;
UINT direction;
UINT max_connection;
UINT timeout;
bool no_reconnect_to_session = false;
bool farm_controller = false;
bool farm_member = false;
bool farm_mode = false;
bool require_bridge_routing_mode;
bool require_monitor_mode;
bool support_bulk_on_rudp = false;
bool support_hmac_on_bulk_of_rudp = false;
bool support_udp_recovery = false;
bool enable_bulk_on_rudp = false;
bool enable_udp_recovery = false;
bool enable_hmac_on_bulk_of_rudp = false;
bool use_client_license = false, use_bridge_license = false;
bool local_host_session = false;
char sessionname[MAX_SESSION_NAME_LEN + 1];
bool is_server_or_bridge = false;
bool qos = false;
bool cluster_dynamic_secure_nat = false;
bool no_save_password = false;
NODE_INFO node;
wchar_t *msg = NULL;
bool suppress_client_update_notification = false;
USER *loggedin_user_object = NULL;
FARM_MEMBER *f = NULL;
SERVER *server = NULL;
POLICY ticketed_policy;
UCHAR unique[SHA1_SIZE], unique2[SHA1_SIZE];
CEDAR *cedar;
RPC_WINVER winver;
UINT client_id;
bool no_more_users_in_server = false;
UCHAR mschap_v2_server_response_20[20];
UINT ms_chap_error = 0;
bool is_empty_password = false;
char *error_detail = NULL;
char *error_detail_2 = NULL;
char ctoken_hash_str[64];
EAP_CLIENT *release_me_eap_client = NULL;
// Validate arguments
if (c == NULL)
{
return false;
}
GenerateMachineUniqueHash(unique2);
Zero(ctoken_hash_str, sizeof(ctoken_hash_str));
Zero(mschap_v2_server_response_20, sizeof(mschap_v2_server_response_20));
Zero(&udp_acceleration_client_ip, sizeof(udp_acceleration_client_ip));
udp_acceleration_client_port = 0;
Zero(udp_acceleration_client_key, sizeof(udp_acceleration_client_key));
Zero(&winver, sizeof(winver));
StrCpy(groupname, sizeof(groupname), "");
StrCpy(sessionname, sizeof(sessionname), "");
if (IsZero(c->CToken_Hash, SHA1_SIZE) == false)
{
BinToStr(ctoken_hash_str, sizeof(ctoken_hash_str), c->CToken_Hash, SHA1_SIZE);
}
cedar = c->Cedar;
// Get the license status
no_more_users_in_server = SiTooManyUserObjectsInServer(cedar->Server, true);
c->Status = CONNECTION_STATUS_NEGOTIATION;
if (c->Cedar->Server != NULL)
{
SERVER *s = c->Cedar->Server;
server = s;
if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
{
farm_member = true;
farm_mode = true;
}
if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
{
farm_controller = true;
farm_mode = true;
}
}
// Receive the signature
Debug("Downloading Signature...\n");
error_detail_2 = NULL;
if (ServerDownloadSignature(c, &error_detail_2) == false)
{
if (error_detail_2 == NULL)
{
error_detail = "ServerDownloadSignature";
}
else
{
error_detail = error_detail_2;
}
supress_return_pack_error = true;
goto CLEANUP;
}
// Send a Hello packet
Debug("Uploading Hello...\n");
if (ServerUploadHello(c) == false)
{
error_detail = "ServerUploadHello";
goto CLEANUP;
}
// Receive the authentication data
Debug("Auth...\n");
p = HttpServerRecv(c->FirstSock);
if (p == NULL)
{
// The connection disconnected
c->Err = ERR_DISCONNECTED;
error_detail = "RecvAuth1";
goto CLEANUP;
}
if (err = GetErrorFromPack(p))
{
// An error has occured
FreePack(p);
c->Err = err;
error_detail = "RecvAuth2";
goto CLEANUP;
}
// Get the method
if (GetMethodFromPack(p, method, sizeof(method)) == false)
{
// Protocol error
FreePack(p);
c->Err = ERR_PROTOCOL_ERROR;
error_detail = "GetMethodFromPack";
goto CLEANUP;
}
// Brand string for the connection limit
{
char tmp[20];
char *branded_ctos = _SS("BRANDED_C_TO_S");
PackGetStr(p, "branded_ctos", tmp, sizeof(tmp));
if(StrCmpi(method, "login") == 0 && StrLen(branded_ctos) > 0 && StrCmpi(branded_ctos, tmp) != 0)
{
FreePack(p);
c->Err = ERR_BRANDED_C_TO_S;
goto CLEANUP;
}
}
// Get the client version
PackGetStr(p, "client_str", c->ClientStr, sizeof(c->ClientStr));
c->ClientVer = PackGetInt(p, "client_ver");
c->ClientBuild = PackGetInt(p, "client_build");
if (SearchStrEx(c->ClientStr, "server", 0, false) != INFINITE ||
SearchStrEx(c->ClientStr, "bridge", 0, false) != INFINITE)
{
is_server_or_bridge = true;
}
// Get the client Windows version
InRpcWinVer(&winver, p);
DecrementNoSsl(c->Cedar, &c->FirstSock->RemoteIP, 2);
if (StrCmpi(method, "login") == 0)
{
bool auth_ret = false;
Debug("Login...\n");
c->Status = CONNECTION_STATUS_USERAUTH;
c->Type = CONNECTION_TYPE_LOGIN;
if (no_more_users_in_server)
{
// There are many users than are allowed in the VPN Server
FreePack(p);
c->Err = ERR_TOO_MANY_USER;
error_detail = "ERR_TOO_MANY_USER";
goto CLEANUP;
}
// Such as the client name
if (PackGetStr(p, "hello", c->ClientStr, sizeof(c->ClientStr)) == false)
{
StrCpy(c->ClientStr, sizeof(c->ClientStr), "Unknown");
}
c->ServerVer = GetCedarVersionNumber();
c->ServerBuild = CEDAR_VERSION_BUILD;
// Get the NODE_INFO
Zero(&node, sizeof(node));
InRpcNodeInfo(&node, p);
// Protocol
c->Protocol = GetProtocolFromPack(p);
if (c->Protocol == CONNECTION_UDP)
{
// Release the structure of the TCP connection
if (c->Tcp)
{
ReleaseList(c->Tcp->TcpSockList);
Free(c->Tcp);
}
}
if (GetServerCapsBool(c->Cedar->Server, "b_vpn_client_connect") == false)
{
// VPN client is unable to connect
FreePack(p);
c->Err = ERR_NOT_SUPPORTED;
goto CLEANUP;
}
// Login
if (GetHubnameAndUsernameFromPack(p, username, sizeof(username), hubname, sizeof(hubname)) == false)
{
// Protocol error
FreePack(p);
c->Err = ERR_PROTOCOL_ERROR;
error_detail = "GetHubnameAndUsernameFromPack";
goto CLEANUP;
}
if (farm_member)
{
bool ok = false;
UINT authtype;
authtype = GetAuthTypeFromPack(p);
if (StrCmpi(username, ADMINISTRATOR_USERNAME) == 0 &&
authtype == AUTHTYPE_PASSWORD)
{
ok = true;
}
if (authtype == AUTHTYPE_TICKET)
{
ok = true;
}
if (ok == false)
{
// Logging on directly to server farm members by
// non-Administrators are prohibited
FreePack(p);
SLog(c->Cedar, "LS_FARMMEMBER_NOT_ADMIN", c->Name, hubname, ADMINISTRATOR_USERNAME, username);
c->Err = ERR_ACCESS_DENIED;
goto CLEANUP;
}
}
Debug("Username = %s, HubName = %s\n", username, hubname);
LockHubList(c->Cedar);
{
hub = GetHub(c->Cedar, hubname);
}
UnlockHubList(c->Cedar);
if (hub == NULL)
{
// The HUB does not exist
FreePack(p);
c->Err = ERR_HUB_NOT_FOUND;
SLog(c->Cedar, "LS_HUB_NOT_FOUND", c->Name, hubname);
error_detail = "ERR_HUB_NOT_FOUND";
goto CLEANUP;
}
if (hub->ForceDisableComm)
{
// Communication function is disabled
FreePack(p);
c->Err = ERR_SERVER_CANT_ACCEPT;
error_detail = "ERR_COMM_DISABLED";
ReleaseHub(hub);
goto CLEANUP;
}
if (GetGlobalServerFlag(GSF_DISABLE_AC) == 0)
{
if (hub->HubDb != NULL && c->FirstSock != NULL)
{
IP ip;
Copy(&ip, &c->FirstSock->RemoteIP, sizeof(IP));
if (IsIpDeniedByAcList(&ip, hub->HubDb->AcList))
{
char ip_str[64];
// Access denied
ReleaseHub(hub);
hub = NULL;
FreePack(p);
c->Err = ERR_IP_ADDRESS_DENIED;
IPToStr(ip_str, sizeof(ip_str), &ip);
SLog(c->Cedar, "LS_IP_DENIED", c->Name, ip_str);
goto CLEANUP;
}
}
}
Lock(hub->lock);
{
UINT cert_size = 0;
void *cert_buf = NULL;
USER *user;
USERGROUP *group;
char plain_password[MAX_PASSWORD_LEN + 1];
RADIUS_LOGIN_OPTION radius_login_opt;
if (hub->Halt || hub->Offline)
{
// HUB is off-line
FreePack(p);
Unlock(hub->lock);
ReleaseHub(hub);
c->Err = ERR_HUB_STOPPING;
goto CLEANUP;
}
Zero(&radius_login_opt, sizeof(radius_login_opt));
if (hub->Option != NULL)
{
radius_login_opt.In_CheckVLanId = hub->Option->AssignVLanIdByRadiusAttribute;
radius_login_opt.In_DenyNoVlanId = hub->Option->DenyAllRadiusLoginWithNoVlanAssign;
if (hub->Option->UseHubNameAsRadiusNasId)
{
StrCpy(radius_login_opt.NasId, sizeof(radius_login_opt.NasId), hubname);
}
}
// Get the various flags
use_encrypt = PackGetInt(p, "use_encrypt") == 0 ? false : true;
use_compress = PackGetInt(p, "use_compress") == 0 ? false : true;
max_connection = PackGetInt(p, "max_connection");
half_connection = PackGetInt(p, "half_connection") == 0 ? false : true;
qos = PackGetInt(p, "qos") ? true : false;
client_id = PackGetInt(p, "client_id");
adjust_mss = PackGetInt(p, "adjust_mss");
use_udp_acceleration_client = PackGetBool(p, "use_udp_acceleration");
support_hmac_on_udp_acceleration_client = PackGetBool(p, "support_hmac_on_udp_acceleration");
support_udp_accel_fast_disconnect_detect = PackGetBool(p, "support_udp_accel_fast_disconnect_detect");
support_bulk_on_rudp = PackGetBool(p, "support_bulk_on_rudp");
support_hmac_on_bulk_of_rudp = PackGetBool(p, "support_hmac_on_bulk_of_rudp");
support_udp_recovery = PackGetBool(p, "support_udp_recovery");
if (c->IsInProc)
{
char tmp[MAX_SIZE];
UINT64 ptr;
ptr = PackGetInt64(p, "release_me_eap_client");
if (ptr != 0)
{
release_me_eap_client = (EAP_CLIENT *)ptr;
}
PackGetStr(p, "inproc_postfix", c->InProcPrefix, sizeof(c->InProcPrefix));
Zero(tmp, sizeof(tmp));
PackGetStr(p, "inproc_cryptname", tmp, sizeof(tmp));
if (c->FirstSock != NULL)
{
if (IsEmptyStr(c->InProcPrefix) == false)
{
Format(c->FirstSock->UnderlayProtocol, sizeof(c->FirstSock->UnderlayProtocol),
SOCK_UNDERLAY_INPROC_EX, c->InProcPrefix);
}
}
if (c->CipherName != NULL)
{
Free(c->CipherName);
}
c->CipherName = NULL;
if (IsEmptyStr(tmp) == false)
{
c->CipherName = CopyStr(tmp);
use_encrypt = true;
}
use_udp_acceleration_client = false;
}
else
{
if (c->CipherName != NULL)
{
Free(c->CipherName);
}
c->CipherName = NULL;
if (c->FirstSock != NULL && IsEmptyStr(c->FirstSock->CipherName) == false)
{
c->CipherName = CopyStr(c->FirstSock->CipherName);
}
}
if (support_bulk_on_rudp && c->FirstSock != NULL && c->FirstSock->IsRUDPSocket &&
c->FirstSock->BulkRecvKey != NULL && c->FirstSock->BulkSendKey != NULL)
{
// RAllow UDP bulk transfer if the client side supports
// in the case of using R-UDP Socket
enable_bulk_on_rudp = true;
enable_hmac_on_bulk_of_rudp = support_hmac_on_bulk_of_rudp;
}
if (support_udp_recovery && c->FirstSock != NULL && c->FirstSock->IsRUDPSocket)
{
// Allow UDP recovery
enable_udp_recovery = true;
}
if (use_udp_acceleration_client)
{
// Get the parameters for the UDP acceleration function
if (PackGetIp(p, "udp_acceleration_client_ip", &udp_acceleration_client_ip) == false ||
PackGetData2(p, "udp_acceleration_client_key", udp_acceleration_client_key, UDP_ACCELERATION_COMMON_KEY_SIZE) == false)
{
use_udp_acceleration_client = false;
}
else
{
if (IsZeroIp(&udp_acceleration_client_ip))
{
Copy(&udp_acceleration_client_ip, &c->FirstSock->RemoteIP, sizeof(IP));
}
udp_acceleration_client_port = PackGetInt(p, "udp_acceleration_client_port");
if (udp_acceleration_client_port == 0)
{
use_udp_acceleration_client = false;
}
}
use_hmac_on_udp_acceleration = support_hmac_on_udp_acceleration_client;
}
Debug("use_udp_acceleration_client = %u\n", use_udp_acceleration_client);
Debug("use_hmac_on_udp_acceleration = %u\n", use_hmac_on_udp_acceleration);
// Request mode
require_bridge_routing_mode = PackGetBool(p, "require_bridge_routing_mode");
require_monitor_mode = PackGetBool(p, "require_monitor_mode");
if (require_monitor_mode)
{
qos = false;
}
if (is_server_or_bridge)
{
require_bridge_routing_mode = true;
}
// Client unique ID
Zero(unique, sizeof(unique));
if (PackGetDataSize(p, "unique_id") == SHA1_SIZE)
{
PackGetData(p, "unique_id", unique);
}
// Get the authentication method
authtype = GetAuthTypeFromPack(p);
if (1)
{
// Log
char ip1[64], ip2[64], verstr[64];
wchar_t *authtype_str = _UU("LH_AUTH_UNKNOWN");
switch (authtype)
{
case CLIENT_AUTHTYPE_ANONYMOUS:
authtype_str = _UU("LH_AUTH_ANONYMOUS");
break;
case CLIENT_AUTHTYPE_PASSWORD:
authtype_str = _UU("LH_AUTH_PASSWORD");
break;
case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
authtype_str = _UU("LH_AUTH_PLAIN_PASSWORD");
break;
case CLIENT_AUTHTYPE_CERT:
authtype_str = _UU("LH_AUTH_CERT");
break;
case AUTHTYPE_TICKET:
authtype_str = _UU("LH_AUTH_TICKET");
break;
case AUTHTYPE_OPENVPN_CERT:
authtype_str = _UU("LH_AUTH_OPENVPN_CERT");
break;
}
IPToStr(ip1, sizeof(ip1), &c->FirstSock->RemoteIP);
IPToStr(ip2, sizeof(ip2), &c->FirstSock->LocalIP);
Format(verstr, sizeof(verstr), "%u.%02u", c->ClientVer / 100, c->ClientVer % 100);
HLog(hub, "LH_CONNECT_CLIENT", c->Name, ip1, c->FirstSock->RemoteHostname, c->FirstSock->RemotePort,
c->ClientStr, verstr, c->ClientBuild, authtype_str, username);
}
// Attempt an anonymous authentication first
auth_ret = SamAuthUserByAnonymous(hub, username);
if (auth_ret)
{
if (c->IsInProc)
{
IPC_MSCHAP_V2_AUTHINFO mschap;
char password_tmp[MAX_SIZE];
Zero(&mschap, sizeof(mschap));
Zero(password_tmp, sizeof(password_tmp));
PackGetStr(p, "plain_password", password_tmp, sizeof(password_tmp));
if (ParseAndExtractMsChapV2InfoFromPassword(&mschap, password_tmp))
{
// Because the server don't know the NTLM hashed password, the bet to the possibility of
// the same character to the user name and empty, search a password of different
// versions of the upper and lower case characters in the case of anonymous authentication.
// Returns the MS-CHAPv2 response by using the password if there is a match.
// Fail the authentication if no match is found.
// (Because, if return a false MS-CHAPv2 Response, PPP client cause an error)
LIST *o = NewListFast(NULL);
char tmp1[MAX_SIZE];
char tmp2[MAX_SIZE];
char tmp3[MAX_SIZE];
char tmp4[MAX_SIZE];
char *response_pw;
char psk[MAX_SIZE];
ParseNtUsername(mschap.MsChapV2_PPPUsername, tmp1, sizeof(tmp1), tmp2, sizeof(tmp2), false);
ParseNtUsername(mschap.MsChapV2_PPPUsername, tmp3, sizeof(tmp3), tmp4, sizeof(tmp4), true);
Add(o, "");
Add(o, "-");
Add(o, ".");
Add(o, "*");
Add(o, "?");
Add(o, " ");
Add(o, "p");
Add(o, "guest");
Add(o, "anony");
Add(o, "anonymous");
Add(o, "password");
Add(o, "passwd");
Add(o, "pass");
Add(o, "pw");
Add(o, mschap.MsChapV2_PPPUsername);
Add(o, tmp1);
Add(o, tmp2);
Add(o, tmp3);
Add(o, tmp4);
Zero(psk, sizeof(psk));
if (c->Cedar->Server != NULL)
{
SERVER *s = c->Cedar->Server;
if (s->IPsecServer != NULL)
{
StrCpy(psk, sizeof(psk), s->IPsecServer->Services.IPsec_Secret);
Add(o, psk);
}
}
response_pw = MsChapV2DoBruteForce(&mschap, o);
ReleaseList(o);
if (response_pw != NULL)
{
UCHAR challenge8[8];
UCHAR nt_hash[16];
UCHAR nt_hash_hash[16];
GenerateNtPasswordHash(nt_hash, response_pw);
GenerateNtPasswordHashHash(nt_hash_hash, nt_hash);
MsChapV2_GenerateChallenge8(challenge8, mschap.MsChapV2_ClientChallenge, mschap.MsChapV2_ServerChallenge,
mschap.MsChapV2_PPPUsername);
MsChapV2Server_GenerateResponse(mschap_v2_server_response_20, nt_hash_hash,
mschap.MsChapV2_ClientResponse, challenge8);
Free(response_pw);
}
else
{
auth_ret = false;
}
}
}
if (auth_ret)
{
// User authentication success by anonymous authentication
HLog(hub, "LH_AUTH_OK", c->Name, username);
is_empty_password = true;
}
}
if (auth_ret == false)
{
// Attempt other authentication methods if anonymous authentication fails
switch (authtype)
{
case CLIENT_AUTHTYPE_ANONYMOUS:
// Anonymous authentication (this have been already attempted)
break;
case AUTHTYPE_TICKET:
// Ticket authentication
if (PackGetDataSize(p, "ticket") == SHA1_SIZE)
{
PackGetData(p, "ticket", ticket);
auth_ret = SiCheckTicket(hub, ticket, username, sizeof(username), username_real, sizeof(username_real),
&ticketed_policy, sessionname, sizeof(sessionname), groupname, sizeof(groupname));
}
break;
case CLIENT_AUTHTYPE_PASSWORD:
// Password authentication
if (PackGetDataSize(p, "secure_password") == SHA1_SIZE)
{
POLICY *pol = NULL;
UCHAR secure_password[SHA1_SIZE];
Zero(secure_password, sizeof(secure_password));
if (PackGetDataSize(p, "secure_password") == SHA1_SIZE)
{
PackGetData(p, "secure_password", secure_password);
}
auth_ret = SamAuthUserByPassword(hub, username, c->Random, secure_password, NULL, NULL, NULL);
pol = SamGetUserPolicy(hub, username);
if (pol != NULL)
{
no_save_password = pol->NoSavePassword;
Free(pol);
}
if(auth_ret){
// Check whether the password was empty
UCHAR hashed_empty_password[SHA1_SIZE];
UCHAR secure_empty_password[SHA1_SIZE];
HashPassword(hashed_empty_password, username, "");
SecurePassword(secure_empty_password, hashed_empty_password, c->Random);
if(Cmp(secure_password, secure_empty_password, SHA1_SIZE)==0){
is_empty_password = true;
}
}
}
break;
case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
{
POLICY *pol = NULL;
// Plaintext password authentication
Zero(plain_password, sizeof(plain_password));
PackGetStr(p, "plain_password", plain_password, sizeof(plain_password));
if (c->IsInProc == false && StartWith(plain_password, IPC_PASSWORD_MSCHAPV2_TAG))
{
// Do not allow the MS-CHAPv2 authentication other than IPC sessions
Zero(plain_password, sizeof(plain_password));
}
if (auth_ret == false)
{
// Attempt a password authentication of normal user
UCHAR secure_password[SHA1_SIZE];
UCHAR hash_password[SHA1_SIZE];
bool is_mschap = StartWith(plain_password, IPC_PASSWORD_MSCHAPV2_TAG);
HashPassword(hash_password, username, plain_password);
SecurePassword(secure_password, hash_password, c->Random);
if (is_mschap == false)
{
auth_ret = SamAuthUserByPassword(hub, username, c->Random, secure_password, NULL, NULL, NULL);
}
else
{
auth_ret = SamAuthUserByPassword(hub, username, c->Random, secure_password,
plain_password, mschap_v2_server_response_20, &ms_chap_error);
}
if (auth_ret && pol == NULL)
{
pol = SamGetUserPolicy(hub, username);
}
}
if (auth_ret == false)
{
// Attempt external authentication registered users
bool fail_ext_user_auth = false;
if (GetGlobalServerFlag(GSF_DISABLE_RADIUS_AUTH) != 0)
{
fail_ext_user_auth = true;
}
if (fail_ext_user_auth == false)
{
auth_ret = SamAuthUserByPlainPassword(c, hub, username, plain_password, false, mschap_v2_server_response_20, &radius_login_opt);
}
if (auth_ret && pol == NULL)
{
pol = SamGetUserPolicy(hub, username);
}
}
if (auth_ret == false)
{
// Attempt external authentication asterisk user
bool b = false;
bool fail_ext_user_auth = false;
if (GetGlobalServerFlag(GSF_DISABLE_RADIUS_AUTH) != 0)
{
fail_ext_user_auth = true;
}
if (fail_ext_user_auth == false)
{
AcLock(hub);
{
b = AcIsUser(hub, "*");
}
AcUnlock(hub);
// If there is asterisk user, log on as the user
if (b)
{
auth_ret = SamAuthUserByPlainPassword(c, hub, username, plain_password, true, mschap_v2_server_response_20, &radius_login_opt);
if (auth_ret && pol == NULL)
{
pol = SamGetUserPolicy(hub, "*");
}
}
}
}
if (pol != NULL)
{
no_save_password = pol->NoSavePassword;
Free(pol);
}
if(auth_ret){
// Check whether the password was empty
if(IsEmptyStr(plain_password)){
is_empty_password = true;
}
}
}
break;
case CLIENT_AUTHTYPE_CERT:
if (GetGlobalServerFlag(GSF_DISABLE_CERT_AUTH) == 0)
{
// Certificate authentication
cert_size = PackGetDataSize(p, "cert");
if (cert_size >= 1 && cert_size <= 100000)
{
cert_buf = ZeroMalloc(cert_size);
if (PackGetData(p, "cert", cert_buf))
{
UCHAR sign[4096 / 8];
UINT sign_size = PackGetDataSize(p, "sign");
if (sign_size <= sizeof(sign) && sign_size >= 1)
{
if (PackGetData(p, "sign", sign))
{
BUF *b = NewBuf();
X *x;
WriteBuf(b, cert_buf, cert_size);
x = BufToX(b, false);
if (x != NULL && x->is_compatible_bit &&
sign_size == (x->bits / 8))
{
K *k = GetKFromX(x);
// Verify the signature received from the client
if (RsaVerifyEx(c->Random, SHA1_SIZE, sign, k, x->bits))
{
// Confirmed that the client has had this certificate
// certainly because the signature matched.
// Check whether the certificate is valid.
auth_ret = SamAuthUserByCert(hub, username, x);
if (auth_ret)
{
// Copy the certificate
c->ClientX = CloneX(x);
}
}
else
{
// Authentication failure
}
FreeK(k);
}
FreeX(x);
FreeBuf(b);
}
}
}
Free(cert_buf);
}
}
else
{
// Certificate authentication is not supported in the open source version
HLog(hub, "LH_AUTH_CERT_NOT_SUPPORT_ON_OPEN_SOURCE", c->Name, username);
Unlock(hub->lock);
ReleaseHub(hub);
FreePack(p);
c->Err = ERR_AUTHTYPE_NOT_SUPPORTED;
goto CLEANUP;
}
break;
case AUTHTYPE_OPENVPN_CERT:
// For OpenVPN; mostly same as CLIENT_AUTHTYPE_CERT, but without
// signature verification, because it was already performed during TLS handshake.
if (c->IsInProc)
{
// Certificate authentication
cert_size = PackGetDataSize(p, "cert");
if (cert_size >= 1 && cert_size <= 100000)
{
cert_buf = ZeroMalloc(cert_size);
if (PackGetData(p, "cert", cert_buf))
{
BUF *b = NewBuf();
X *x;
WriteBuf(b, cert_buf, cert_size);
x = BufToX(b, false);
if (x != NULL && x->is_compatible_bit)
{
Debug("Got to SamAuthUserByCert %s\n", username); // XXX
// Check whether the certificate is valid.
auth_ret = SamAuthUserByCert(hub, username, x);
if (auth_ret)
{
// Copy the certificate
c->ClientX = CloneX(x);
}
}
FreeX(x);
FreeBuf(b);
}
Free(cert_buf);
}
}
else
{
// OpenVPN certificate authentication cannot be used directly by external clients
Unlock(hub->lock);
ReleaseHub(hub);
FreePack(p);
c->Err = ERR_AUTHTYPE_NOT_SUPPORTED;
goto CLEANUP;
}
break;
default:
// Unknown authentication method
Unlock(hub->lock);
ReleaseHub(hub);
FreePack(p);
c->Err = ERR_AUTHTYPE_NOT_SUPPORTED;
error_detail = "ERR_AUTHTYPE_NOT_SUPPORTED";
goto CLEANUP;
}
if (auth_ret == false)
{
// Authentication failure
HLog(hub, "LH_AUTH_NG", c->Name, username);
}
else
{
// Authentication success
HLog(hub, "LH_AUTH_OK", c->Name, username);
}
}
if (auth_ret == false)
{
// Authentication failure
Unlock(hub->lock);
ReleaseHub(hub);
FreePack(p);
c->Err = ERR_AUTH_FAILED;
if (ms_chap_error != 0)
{
c->Err = ms_chap_error;
}
error_detail = "ERR_AUTH_FAILED";
goto CLEANUP;
}
else
{
if(is_empty_password)
{
SOCK *s = c->FirstSock;
if (s != NULL && s->RemoteIP.addr[0] != 127)
{
if(StrCmpi(username, ADMINISTRATOR_USERNAME) == 0 ||
GetHubAdminOption(hub, "deny_empty_password") != 0)
{
// When the password is empty, remote connection is not acceptable
HLog(hub, "LH_LOCAL_ONLY", c->Name, username);
Unlock(hub->lock);
ReleaseHub(hub);
FreePack(p);
c->Err = ERR_NULL_PASSWORD_LOCAL_ONLY;
error_detail = "ERR_NULL_PASSWORD_LOCAL_ONLY";
goto CLEANUP;
}
}
}
}
policy = NULL;
// Authentication success
FreePack(p);
// Check the assigned VLAN ID
if (radius_login_opt.Out_IsRadiusLogin)
{
if (radius_login_opt.In_CheckVLanId)
{
if (radius_login_opt.Out_VLanId != 0)
{
assigned_vlan_id = radius_login_opt.Out_VLanId;
}
if (radius_login_opt.In_DenyNoVlanId && assigned_vlan_id == 0 || assigned_vlan_id >= 4096)
{
// Deny this session
Unlock(hub->lock);
ReleaseHub(hub);
c->Err = ERR_ACCESS_DENIED;
error_detail = "In_DenyNoVlanId";
goto CLEANUP;
}
}
}
if (StrCmpi(username, ADMINISTRATOR_USERNAME) != 0)
{
// Get the policy
if (farm_member == false)
{
// In the case of not a farm member
user = AcGetUser(hub, username);
if (user == NULL)
{
user = AcGetUser(hub, "*");
if (user == NULL)
{
// User acquisition failure
Unlock(hub->lock);
ReleaseHub(hub);
c->Err = ERR_ACCESS_DENIED;
error_detail = "AcGetUser";
goto CLEANUP;
}
}
policy = NULL;
Lock(user->lock);
{
// Get the expiration date
user_expires = user->ExpireTime;
StrCpy(username_real, sizeof(username_real), user->Name);
group = user->Group;
if (group != NULL)
{
AddRef(group->ref);
Lock(group->lock);
{
// Get the group name
StrCpy(groupname, sizeof(groupname), group->Name);
}
Unlock(group->lock);
}
if (user->Policy != NULL)
{
policy = ClonePolicy(user->Policy);
}
else
{
if (group)
{
Lock(group->lock);
{
if (group->Policy != NULL)
{
policy = ClonePolicy(group->Policy);
}
}
Unlock(group->lock);
}
}
if (group != NULL)
{
ReleaseGroup(group);
}
}
Unlock(user->lock);
loggedin_user_object = user;
}
else
{
// In the case of farm member
policy = ClonePolicy(&ticketed_policy);
}
}
else
{
// Administrator mode
admin_mode = true;
StrCpy(username_real, sizeof(username_real), ADMINISTRATOR_USERNAME);
policy = ClonePolicy(GetDefaultPolicy());
policy->NoBroadcastLimiter = true;
policy->MonitorPort = true;
}
if (policy == NULL)
{
// Use the default policy
policy = ClonePolicy(GetDefaultPolicy());
}
if (policy->MaxConnection == 0)
{
policy->MaxConnection = MAX_TCP_CONNECTION;
}
if (policy->TimeOut == 0)
{
policy->TimeOut = 20;
}
if (qos)
{
// VoIP / QoS
if (policy->NoQoS)
{
// Policy does not allow QoS
qos = false;
}
if (GetServerCapsBool(c->Cedar->Server, "b_support_qos") == false)
{
// Server does not support QoS
qos = false;
policy->NoQoS = true;
}
if (GetHubAdminOption(hub, "deny_qos") != 0)
{
// It is prohibited in the management options
qos = false;
policy->NoQoS = true;
}
}
if (GetHubAdminOption(hub, "max_bitrates_download") != 0)
{
if (policy->MaxDownload == 0)
{
policy->MaxDownload = GetHubAdminOption(hub, "max_bitrates_download");
}
else
{
UINT r = GetHubAdminOption(hub, "max_bitrates_download");
policy->MaxDownload = MIN(policy->MaxDownload, r);
}
}
if (GetHubAdminOption(hub, "max_bitrates_upload") != 0)
{
if (policy->MaxUpload == 0)
{
policy->MaxUpload = GetHubAdminOption(hub, "max_bitrates_upload");
}
else
{
UINT r = GetHubAdminOption(hub, "max_bitrates_upload");
policy->MaxUpload = MIN(policy->MaxUpload, r);
}
}
if (GetHubAdminOption(hub, "deny_bridge") != 0)
{
policy->NoBridge = true;
}
if (GetHubAdminOption(hub, "deny_routing") != 0)
{
policy->NoRouting = true;
}
if (c->IsInProc)
{
policy->NoBridge = false;
policy->NoRouting = false;
}
if (hub->Option->ClientMinimumRequiredBuild > c->ClientBuild &&
InStrEx(c->ClientStr, "client", false))
{
// Build number of the client is too small
HLog(hub, "LH_CLIENT_VERSION_OLD", c->Name, c->ClientBuild, hub->Option->ClientMinimumRequiredBuild);
Unlock(hub->lock);
ReleaseHub(hub);
c->Err = ERR_VERSION_INVALID;
Free(policy);
error_detail = "ERR_VERSION_INVALID";
goto CLEANUP;
}
if (hub->Option->RequiredClientId != 0 &&
hub->Option->RequiredClientId != client_id &&
InStrEx(c->ClientStr, "client", false))
{
// Build number of the client is too small
HLog(hub, "LH_CLIENT_ID_REQUIRED", c->Name, client_id, hub->Option->RequiredClientId);
Unlock(hub->lock);
ReleaseHub(hub);
c->Err = ERR_CLIENT_ID_REQUIRED;
error_detail = "ERR_CLIENT_ID_REQUIRED";
Free(policy);
goto CLEANUP;
}
if ((policy->NoSavePassword) || (policy->AutoDisconnect != 0))
{
if (c->ClientBuild < 6560 && InStrEx(c->ClientStr, "client", false))
{
// If NoSavePassword policy is specified,
// only supported client can connect
HLog(hub, "LH_CLIENT_VERSION_OLD", c->Name, c->ClientBuild, 6560);
Unlock(hub->lock);
ReleaseHub(hub);
c->Err = ERR_VERSION_INVALID;
error_detail = "ERR_VERSION_INVALID";
Free(policy);
goto CLEANUP;
}
}
if (user_expires != 0 && user_expires <= SystemTime64())
{
// User expired
HLog(hub, "LH_USER_EXPIRES", c->Name, username);
Unlock(hub->lock);
ReleaseHub(hub);
c->Err = ERR_ACCESS_DENIED;
error_detail = "LH_USER_EXPIRES";
Free(policy);
goto CLEANUP;
}
if (policy->Access == false)
{
// Access is denied
HLog(hub, "LH_POLICY_ACCESS_NG", c->Name, username);
Unlock(hub->lock);
ReleaseHub(hub);
error_detail = "LH_POLICY_ACCESS_NG";
c->Err = ERR_ACCESS_DENIED;
Free(policy);
goto CLEANUP;
}
// Determine the contents of the policy by comparing to
// option presented by client or deny the connection.
// Confirm the connectivity in the monitor-mode first
if (require_monitor_mode && policy->MonitorPort == false)
{
// Can not connect in the monitor port mode
HLog(hub, "LH_POLICY_MONITOR_MODE", c->Name);
Unlock(hub->lock);
ReleaseHub(hub);
c->Err = ERR_MONITOR_MODE_DENIED;
Free(policy);
error_detail = "ERR_MONITOR_MODE_DENIED";
goto CLEANUP;
}
if (policy->MonitorPort)
{
if (require_monitor_mode == false)
{
policy->MonitorPort = false;
}
}
if (policy->MonitorPort)
{
qos = false;
}
// Determine whether it can be connected by a bridge / routing mode next
if (require_bridge_routing_mode &&
(policy->NoBridge && policy->NoRouting))
{
// Can not be connected by a bridge / routing mode
HLog(hub, "LH_POLICY_BRIDGE_MODE", c->Name);
Unlock(hub->lock);
ReleaseHub(hub);
c->Err = ERR_BRIDGE_MODE_DENIED;
error_detail = "ERR_BRIDGE_MODE_DENIED";
Free(policy);
goto CLEANUP;
}
if (require_bridge_routing_mode == false)
{
policy->NoBridge = true;
policy->NoRouting = true;
}
if (Cmp(unique, unique2, SHA1_SIZE) == 0)
{
// It's a localhost session
local_host_session = true;
}
if (local_host_session == false)
{
// Make further judgment whether localhost session
SOCK *s = c->FirstSock;
if (s != NULL)
{
if (IsIPMyHost(&s->RemoteIP))
{
// It's a localhost session
local_host_session = true;
}
}
}
if (local_host_session)
{
// Permit routing or bridging in the case of localhost session
policy->NoBridge = false;
policy->NoRouting = false;
}
if (local_host_session == false)
{
if (policy->NoBridge == false || policy->NoRouting == false)
{
use_bridge_license = true;
}
else
{
use_client_license = true;
}
}
if (server != NULL && server->ServerType != SERVER_TYPE_FARM_MEMBER &&
policy != NULL)
{
if (GetServerCapsBool(hub->Cedar->Server, "b_support_limit_multilogin"))
{
// Check if the number of concurrent multiple logins limit is specified in the policy
RPC_ENUM_SESSION t;
UINT i, num;
UINT max_logins = policy->MultiLogins;
UINT ao = GetHubAdminOption(hub, "max_multilogins_per_user");
if (ao != 0)
{
if (max_logins != 0)
{
max_logins = MIN(max_logins, ao);
}
else
{
max_logins = ao;
}
}
if (max_logins != 0)
{
Zero(&t, sizeof(t));
StrCpy(t.HubName, sizeof(t.HubName), hub->Name);
Unlock(hub->lock);
SiEnumSessionMain(server, &t);
Lock(hub->lock);
num = 0;
for (i = 0;i < t.NumSession;i++)
{
RPC_ENUM_SESSION_ITEM *e = &t.Sessions[i];
if (e->BridgeMode == false && e->Layer3Mode == false && e->LinkMode == false && e->CurrentNumTcp != 0)
{
if (StrCmpi(e->Username, username) == 0 &&
(IsZero(e->UniqueId, 16) || Cmp(e->UniqueId, node.UniqueId, 16) != 0))
{
num++;
}
}
}
FreeRpcEnumSession(&t);
if (num >= max_logins)
{
// Can not connect any more
Unlock(hub->lock);
// Dump a detailed error log
HLog(hub, "LH_TOO_MANY_MULTILOGINS",
c->Name,
username, max_logins, num);
ReleaseHub(hub);
c->Err = ERR_TOO_MANY_USER_SESSION;
Free(policy);
goto CLEANUP;
}
}
}
}
if (loggedin_user_object != NULL)
{
// Update the user information
Lock(loggedin_user_object->lock);
{
loggedin_user_object->LastLoginTime = SystemTime64();
}
Unlock(loggedin_user_object->lock);
}
// Update the number of log-ins
hub->LastCommTime = hub->LastLoginTime = SystemTime64();
if (farm_controller)
{
wchar_t *msg = GetHubMsg(hub);
Unlock(hub->lock);
Lock(cedar->CedarSuperLock);
// In the case of farm controller, choose a farm members to host this HUB
LockList(server->FarmMemberList);
{
HLog(hub, "LH_FARM_SELECT_1", c->Name);
f = SiGetHubHostingMember(server, hub, admin_mode, c);
if (f == NULL)
{
// Failed in the selection
HLog(hub, "LH_FARM_SELECT_2", c->Name);
UnlockList(server->FarmMemberList);
Unlock(cedar->CedarSuperLock);
ReleaseHub(hub);
c->Err = ERR_COULD_NOT_HOST_HUB_ON_FARM;
Free(policy);
Free(msg);
goto CLEANUP;
}
else
{
if (f->Me == false)
{
UCHAR ticket[SHA1_SIZE];
PACK *p;
BUF *b;
UINT i;
SLog(c->Cedar, "LH_FARM_SELECT_4", c->Name, f->hostname);
// Create a session on the selected server farm member
Rand(ticket, sizeof(ticket));
SiCallCreateTicket(server, f, hub->Name,
username, username_real, policy, ticket, Inc(hub->SessionCounter), groupname);
p = NewPack();
PackAddInt(p, "Redirect", 1);
PackAddIp32(p, "Ip", f->Ip);
for (i = 0;i < f->NumPort;i++)
{
PackAddIntEx(p, "Port", f->Ports[i], i, f->NumPort);
}
PackAddData(p, "Ticket", ticket, sizeof(ticket));
if (true)
{
char *utf = CopyUniToUtf(msg);
PackAddData(p, "Msg", utf, StrLen(utf));
Free(utf);
}
b = XToBuf(f->ServerCert, false);
PackAddBuf(p, "Cert", b);
FreeBuf(b);
UnlockList(server->FarmMemberList);
Unlock(cedar->CedarSuperLock);
ReleaseHub(hub);
HttpServerSend(c->FirstSock, p);
FreePack(p);
c->Err = 0;
Free(policy);
FreePack(HttpServerRecv(c->FirstSock));
Free(msg);
goto CLEANUP;
}
else
{
HLog(hub, "LH_FARM_SELECT_3", c->Name);
// Continue the process because myself was selected
UnlockList(server->FarmMemberList);
Unlock(cedar->CedarSuperLock);
f->Point = SiGetPoint(server);
Lock(hub->lock);
Free(msg);
}
}
}
}
if (admin_mode == false)
{
// Check the maximum number of connections of the HUB
if (hub->Option->MaxSession != 0 &&
hub->Option->MaxSession <= Count(hub->NumSessions))
{
// Can not connect any more
Unlock(hub->lock);
HLog(hub, "LH_MAX_SESSION", c->Name, hub->Option->MaxSession);
ReleaseHub(hub);
c->Err = ERR_HUB_IS_BUSY;
Free(policy);
error_detail = "ERR_HUB_IS_BUSY";
goto CLEANUP;
}
}
if (use_encrypt == false && c->FirstSock->IsReverseAcceptedSocket)
{
// On VPN Azure, SSL encryption is mandated.
use_encrypt = true;
}
if (use_client_license || use_bridge_license)
{
// Examine whether not to conflict with the limit of simultaneous connections
// number of sessions defined by the Virtual HUB management options
if (
(GetHubAdminOption(hub, "max_sessions") != 0 &&
(Count(hub->NumSessionsClient) + Count(hub->NumSessionsBridge)) >= GetHubAdminOption(hub, "max_sessions"))
||
(hub->Option->MaxSession != 0 &&
(Count(hub->NumSessionsClient) + Count(hub->NumSessionsBridge)) >= hub->Option->MaxSession))
{
// Can not connect any more
Unlock(hub->lock);
HLog(hub, "LH_MAX_SESSION", c->Name, GetHubAdminOption(hub, "max_sessions"));
ReleaseHub(hub);
c->Err = ERR_HUB_IS_BUSY;
Free(policy);
goto CLEANUP;
}
}
if (use_client_license)
{
// Examine whether not to conflict with the limit of simultaneous connections
// number of sessions(client) defined by the Virtual HUB management options
if (((GetHubAdminOption(hub, "max_sessions_client_bridge_apply") != 0
) &&
Count(hub->NumSessionsClient) >= GetHubAdminOption(hub, "max_sessions_client") && hub->Cedar->Server != NULL && hub->Cedar->Server->ServerType != SERVER_TYPE_FARM_MEMBER)
||
(hub->FarmMember_MaxSessionClientBridgeApply &&
Count(hub->NumSessionsClient) >= hub->FarmMember_MaxSessionClient))
{
// Can not connect any more
Unlock(hub->lock);
HLog(hub, "LH_MAX_SESSION_CLIENT", c->Name, GetHubAdminOption(hub, "max_sessions_client"));
ReleaseHub(hub);
c->Err = ERR_HUB_IS_BUSY;
Free(policy);
goto CLEANUP;
}
}
if (use_bridge_license)
{
// Examine whether not to conflict with the limit of simultaneous connections
// number of sessions(bridge) defined by the Virtual HUB management options
if (((GetHubAdminOption(hub, "max_sessions_client_bridge_apply") != 0
) &&
Count(hub->NumSessionsBridge) >= GetHubAdminOption(hub, "max_sessions_bridge") && hub->Cedar->Server != NULL && hub->Cedar->Server->ServerType != SERVER_TYPE_FARM_MEMBER)
||
(hub->FarmMember_MaxSessionClientBridgeApply &&
Count(hub->NumSessionsBridge) >= hub->FarmMember_MaxSessionBridge))
{
// Can not connect any more
Unlock(hub->lock);
HLog(hub, "LH_MAX_SESSION_BRIDGE", c->Name, GetHubAdminOption(hub, "max_sessions_bridge"));
ReleaseHub(hub);
c->Err = ERR_HUB_IS_BUSY;
Free(policy);
goto CLEANUP;
}
}
if (Count(hub->Cedar->CurrentSessions) >= GetServerCapsInt(hub->Cedar->Server, "i_max_sessions"))
{
// Can not connect any more
Unlock(hub->lock);
HLog(hub, "LH_MAX_SESSION_2", c->Name, GetServerCapsInt(hub->Cedar->Server, "i_max_sessions"));
ReleaseHub(hub);
c->Err = ERR_HUB_IS_BUSY;
Free(policy);
goto CLEANUP;
}
// Increment the current number of connections
Inc(hub->NumSessions);
if (use_bridge_license)
{
Inc(hub->NumSessionsBridge);
}
if (use_client_license)
{
Inc(hub->NumSessionsClient);
}
Inc(hub->Cedar->CurrentSessions);
// Calculate the time-out period
timeout = policy->TimeOut * 1000; // Convert milliseconds to seconds
if (timeout == 0)
{
timeout = TIMEOUT_DEFAULT;
}
timeout = MIN(timeout, TIMEOUT_MAX);
timeout = MAX(timeout, TIMEOUT_MIN);
// Update the max_connection according to the policy
max_connection = MIN(max_connection, policy->MaxConnection);
max_connection = MIN(max_connection, MAX_TCP_CONNECTION);
max_connection = MAX(max_connection, 1);
if (c->FirstSock->IsRUDPSocket)
{
// In the case of TCP-over-UDP
half_connection = false;
// Disable the QoS
qos = false;
if (enable_udp_recovery == false)
{
// Disable the session reconnection feature
no_reconnect_to_session = true;
max_connection = 1;
}
else
{
// If the UDP recovery is enabled, permit the session re-connection feature (for 2)
no_reconnect_to_session = false;
max_connection = NUM_TCP_CONNECTION_FOR_UDP_RECOVERY;
}
}
if (half_connection)
{
// Number of connections should be more than 2 in the case of Half Connection
max_connection = MAX(max_connection, 2);
}
if (qos)
{
// Number of connections is set to 2 or more when using the VoIP / QoS
max_connection = MAX(max_connection, 2);
if (half_connection)
{
max_connection = MAX(max_connection, 4);
}
}
c->Status = CONNECTION_STATUS_ESTABLISHED;
// Remove the connection from Cedar
DelConnection(c->Cedar, c);
// VLAN ID
if (assigned_vlan_id != 0)
{
if (policy->VLanId == 0)
{
policy->VLanId = assigned_vlan_id;
}
}
// Create a Session
StrLower(username);
s = NewServerSessionEx(c->Cedar, c, hub, username, policy, c->IsInProc);
s->EnableUdpRecovery = enable_udp_recovery;
s->LocalHostSession = local_host_session;
s->NormalClient = true;
IPToStr(s->ClientIP, sizeof(s->ClientIP), &c->ClientIp);
if (c->FirstSock->IsRUDPSocket)
{
// R-UDP session
s->IsRUDPSession = true;
s->RUdpMss = c->FirstSock->RUDP_OptimizedMss;
Debug("Optimized MSS Value for R-UDP: %u\n", s->RUdpMss);
}
if (enable_bulk_on_rudp)
{
// Allow bulk transfer on R-UDP
s->EnableBulkOnRUDP = true;
s->EnableHMacOnBulkOfRUDP = enable_hmac_on_bulk_of_rudp;
}
s->IsAzureSession = c->FirstSock->IsReverseAcceptedSocket;
StrCpy(s->UnderlayProtocol, sizeof(s->UnderlayProtocol), c->FirstSock->UnderlayProtocol);
if (server != NULL)
{
s->NoSendSignature = server->NoSendSignature;
}
if (c->IsInProc)
{
s->NoSendSignature = true;
}
if (c->IsInProc && StrCmpi(c->InProcPrefix, OPENVPN_IPC_POSTFIX_L3) == 0)
{
// OpenVPN L3 session
s->IsOpenVPNL3Session = true;
}
if (c->IsInProc && StrCmpi(c->InProcPrefix, OPENVPN_IPC_POSTFIX_L2) == 0)
{
// OpenVPN L2 session
s->IsOpenVPNL2Session = true;
}
// Determine whether the use of UDP acceleration mode
if (use_udp_acceleration_client)
{
s->UseUdpAcceleration = true;
s->UdpAccelFastDisconnectDetect = support_udp_accel_fast_disconnect_detect;
}
if (hub->Option != NULL && hub->Option->DisableUdpAcceleration)
{
s->UseUdpAcceleration = false;
}
if (IsZeroIP(&c->FirstSock->Reverse_MyServerGlobalIp) == false &&
CmpIpAddr(&c->FirstSock->Reverse_MyServerGlobalIp, &c->FirstSock->RemoteIP) == 0)
{
// Disable forcibly the UDP acceleration mode if VPN Server and VPN Client
// are in same LAN in the case of using VPN Azure.
// (Or this may cause infinite loop of packet)
s->UseUdpAcceleration = false;
}
if (s->UseUdpAcceleration)
{
s->UseHMacOnUdpAcceleration = use_hmac_on_udp_acceleration;
}
Debug("UseUdpAcceleration = %u\n", s->UseUdpAcceleration);
Debug("UseHMacOnUdpAcceleration = %u\n", s->UseHMacOnUdpAcceleration);
if (s->UseUdpAcceleration)
{
bool no_nat_t = false;
// Initialize the UDP acceleration function
s->UdpAccel = NewUdpAccel(c->Cedar, (c->FirstSock->IsRUDPSocket ? NULL : &c->FirstSock->LocalIP), false, c->FirstSock->IsRUDPSocket, no_nat_t);
if (s->UdpAccel == NULL)
{
s->UseUdpAcceleration = false;
Debug("NewUdpAccel Failed.\n");
}
else
{
if (UdpAccelInitServer(s->UdpAccel, udp_acceleration_client_key, &udp_acceleration_client_ip, udp_acceleration_client_port,
&c->FirstSock->RemoteIP) == false)
{
Debug("UdpAccelInitServer Failed.\n");
s->UseUdpAcceleration = false;
}
s->UdpAccel->FastDetect = s->UdpAccelFastDisconnectDetect;
if (use_encrypt == false)
{
s->UdpAccel->PlainTextMode = true;
}
s->UdpAccel->UseHMac = s->UseHMacOnUdpAcceleration;
}
}
s->UseClientLicense = use_client_license;
s->UseBridgeLicense = use_bridge_license;
s->AdjustMss = adjust_mss;
if (s->AdjustMss != 0)
{
Debug("AdjustMSS: %u\n", s->AdjustMss);
}
s->IsBridgeMode = (policy->NoBridge == false) || (policy->NoRouting == false);
s->IsMonitorMode = policy->MonitorPort;
// Decide whether IPv6 session
s->IPv6Session = false;
if (node.ClientIpAddress == 0)
{
s->IPv6Session = true;
}
if (use_bridge_license)
{
Inc(s->Cedar->AssignedBridgeLicense);
}
if (use_client_license)
{
Inc(s->Cedar->AssignedClientLicense);
}
if (server != NULL)
{
// Update the total allocation of the number of licenses for Server structure
if (server->ServerType == SERVER_TYPE_STANDALONE)
{
// Update only stand-alone mode
// (Periodically poll in the cluster controller mode)
server->CurrentAssignedClientLicense = Count(s->Cedar->AssignedClientLicense);
server->CurrentAssignedBridgeLicense = Count(s->Cedar->AssignedBridgeLicense);
}
}
if (StrLen(sessionname) != 0)
{
// Specify the session name
Free(s->Name);
s->Name = CopyStr(sessionname);
}
{
char ip[128];
IPToStr(ip, sizeof(ip), &c->FirstSock->RemoteIP);
HLog(hub, "LH_NEW_SESSION", c->Name, s->Name, ip, c->FirstSock->RemotePort,
c->FirstSock->UnderlayProtocol);
}
c->Session = s;
s->AdministratorMode = admin_mode;
StrCpy(s->UserNameReal, sizeof(s->UserNameReal), username_real);
StrCpy(s->GroupName, sizeof(s->GroupName), groupname);
// Get the session key
Copy(session_key, s->SessionKey, SHA1_SIZE);
// Set the parameters
s->MaxConnection = max_connection;
s->UseEncrypt = use_encrypt;
s->UseCompress = use_compress;
s->HalfConnection = half_connection;
s->Timeout = timeout;
s->QoS = qos;
s->NoReconnectToSession = no_reconnect_to_session;
s->VLanId = policy->VLanId;
// User name
s->Username = CopyStr(username);
HLog(hub, "LH_SET_SESSION", s->Name, s->MaxConnection,
s->UseEncrypt ? _UU("L_YES") : _UU("L_NO"),
s->UseCompress ? _UU("L_YES") : _UU("L_NO"),
s->HalfConnection ? _UU("L_YES") : _UU("L_NO"),
s->Timeout / 1000);
msg = GetHubMsg(hub);
// Suppress client update notification flag
if (hub->Option != NULL)
{
suppress_client_update_notification = hub->Option->SuppressClientUpdateNotification;
}
}
Unlock(hub->lock);
// Send a Welcome packet to the client
p = PackWelcome(s);
PackAddBool(p, "suppress_client_update_notification", suppress_client_update_notification);
if (s->InProcMode)
{
if (IsZero(mschap_v2_server_response_20, sizeof(mschap_v2_server_response_20)) == false)
{
// MS-CHAPv2 Response
PackAddData(p, "IpcMsChapV2ServerResponse", mschap_v2_server_response_20, sizeof(mschap_v2_server_response_20));
}
}
if (true)
{
// A message to be displayed in the VPN Client (Will not be displayed if the VPN Gate Virtual HUB)
char *utf;
wchar_t winver_msg_client[3800];
wchar_t winver_msg_server[3800];
UINT tmpsize;
wchar_t *tmp;
RPC_WINVER server_winver;
GetWinVer(&server_winver);
Zero(winver_msg_client, sizeof(winver_msg_client));
Zero(winver_msg_server, sizeof(winver_msg_server));
if (IsSupportedWinVer(&winver) == false)
{
SYSTEMTIME st;
LocalTime(&st);
UniFormat(winver_msg_client, sizeof(winver_msg_client), _UU("WINVER_ERROR_FORMAT"),
_UU("WINVER_ERROR_PC_LOCAL"),
winver.Title,
_UU("WINVER_ERROR_VPNSERVER"),
SUPPORTED_WINDOWS_LIST,
_UU("WINVER_ERROR_PC_LOCAL"),
_UU("WINVER_ERROR_VPNSERVER"),
_UU("WINVER_ERROR_VPNSERVER"),
_UU("WINVER_ERROR_VPNSERVER"),
st.wYear, st.wMonth);
}
if (IsSupportedWinVer(&server_winver) == false)
{
SYSTEMTIME st;
LocalTime(&st);
UniFormat(winver_msg_server, sizeof(winver_msg_server), _UU("WINVER_ERROR_FORMAT"),
_UU("WINVER_ERROR_PC_REMOTE"),
server_winver.Title,
_UU("WINVER_ERROR_VPNSERVER"),
SUPPORTED_WINDOWS_LIST,
_UU("WINVER_ERROR_PC_REMOTE"),
_UU("WINVER_ERROR_VPNSERVER"),
_UU("WINVER_ERROR_VPNSERVER"),
_UU("WINVER_ERROR_VPNSERVER"),
st.wYear, st.wMonth);
}
tmpsize = UniStrSize(winver_msg_client) + UniStrSize(winver_msg_server) + UniStrSize(msg) + (16000 + 3000) * sizeof(wchar_t);
tmp = ZeroMalloc(tmpsize);
if (IsURLMsg(msg, NULL, 0) == false)
{
if (s != NULL && s->IsRUDPSession && c != NULL && StrCmpi(hub->Name, VG_HUBNAME) != 0)
{
// Show the warning message if the connection is made by NAT-T
wchar_t *tmp2;
UINT tmp2_size = 2400 * sizeof(wchar_t);
char local_name[128];
wchar_t local_name_2[128];
char local_name_3[128];
Zero(local_name, sizeof(local_name));
Zero(local_name_2, sizeof(local_name_2));
Zero(local_name_3, sizeof(local_name_3));
GetMachineName(local_name, sizeof(local_name));
#ifdef OS_WIN32
MsGetComputerNameFullEx(local_name_2, sizeof(local_name_2), true);
UniToStr(local_name_3, sizeof(local_name_3), local_name_2);
if (IsEmptyStr(local_name_3) == false)
{
StrCpy(local_name, sizeof(local_name), local_name_3);
}
#endif // OS_WIN32
tmp2 = ZeroMalloc(tmp2_size);
UniFormat(tmp2, tmp2_size, _UU(c->ClientBuild >= 9428 ? "NATT_MSG" : "NATT_MSG2"), local_name);
UniStrCat(tmp, tmpsize, tmp2);
Free(tmp2);
}
{
if (GetGlobalServerFlag(GSF_SHOW_OSS_MSG) != 0)
{
UniStrCat(tmp, tmpsize, _UU("OSS_MSG"));
}
}
{
UniStrCat(tmp, tmpsize, winver_msg_client);
UniStrCat(tmp, tmpsize, winver_msg_server);
}
}
UniStrCat(tmp, tmpsize, msg);
utf = CopyUniToUtf(tmp);
PackAddData(p, "Msg", utf, StrLen(utf));
Free(tmp);
Free(utf);
}
Free(msg);
// Brand string for the connection limit
{
char *branded_cfroms = _SS("BRANDED_C_FROM_S");
if(StrLen(branded_cfroms) > 0)
{
PackAddStr(p, "branded_cfroms", branded_cfroms);
}
}
HttpServerSend(c->FirstSock, p);
FreePack(p);
// Receive a signature
Copy(&c->Session->NodeInfo, &node, sizeof(NODE_INFO));
{
wchar_t tmp[MAX_SIZE * 2];
NodeInfoToStr(tmp, sizeof(tmp), &s->NodeInfo);
HLog(hub, "LH_NODE_INFO", s->Name, tmp);
if (s->VLanId != 0)
{
HLog(hub, "LH_VLAN_ID", s->Name, s->VLanId);
}
}
// Shift the connection to the tunneling mode
StartTunnelingMode(c);
// Processing of half-connection mode
if (s->HalfConnection)
{
// The direction of the first socket is client to server
TCPSOCK *ts = (TCPSOCK *)LIST_DATA(c->Tcp->TcpSockList, 0);
ts->Direction = TCP_CLIENT_TO_SERVER;
}
if (s->Hub->Type == HUB_TYPE_FARM_DYNAMIC && s->Cedar->Server != NULL && s->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)
{
if (s->Hub->BeingOffline == false)
{
// Start the SecureNAT on the dynamic Virtual HUB
EnableSecureNATEx(s->Hub, false, true);
cluster_dynamic_secure_nat = true;
}
}
if (s->LocalHostSession)
{
// Update the local MAC address list
RefreshLocalMacAddressList();
}
// Discard the user list cache
DeleteAllUserListCache(hub->UserList);
// Main routine of the session
Debug("SessionMain()\n");
s->NumLoginIncrementUserObject = loggedin_user_object;
s->NumLoginIncrementHubObject = s->Hub;
s->NumLoginIncrementTick = Tick64() + (UINT64)NUM_LOGIN_INCREMENT_INTERVAL;
SessionMain(s);
// Discard the user list cache
DeleteAllUserListCache(hub->UserList);
// Decrement the current number of connections
Lock(s->Hub->lock);
{
if (use_bridge_license)
{
Dec(hub->NumSessionsBridge);
}
if (use_client_license)
{
Dec(hub->NumSessionsClient);
}
Dec(s->Hub->NumSessions);
Dec(s->Hub->Cedar->CurrentSessions);
// Decrement the number of licenses
if (use_bridge_license)
{
Dec(s->Cedar->AssignedBridgeLicense);
}
if (use_client_license)
{
Dec(s->Cedar->AssignedClientLicense);
}
if (server != NULL)
{
// Update the total allocation of the number of licenses for Server structure
if (server->ServerType == SERVER_TYPE_STANDALONE)
{
// Update only stand-alone mode
// (Periodically polled in the cluster controller mode)
server->CurrentAssignedClientLicense = Count(s->Cedar->AssignedClientLicense);
server->CurrentAssignedBridgeLicense = Count(s->Cedar->AssignedBridgeLicense);
}
}
}
Unlock(s->Hub->lock);
PrintSessionTotalDataSize(s);
HLog(s->Hub, "LH_END_SESSION", s->Name, s->TotalSendSizeReal, s->TotalRecvSizeReal);
if (cluster_dynamic_secure_nat && s->Hub->BeingOffline == false)
{
// Stop the SecureNAT on the dynamic Virtual HUB
EnableSecureNATEx(s->Hub, false, true);
}
if (s->UdpAccel != NULL)
{
// Release the UDP acceleration
FreeUdpAccel(s->UdpAccel);
s->UdpAccel = NULL;
}
ReleaseSession(s);
ret = true;
c->Err = ERR_SESSION_REMOVED;
ReleaseHub(hub);
goto CLEANUP;
}
else if (StrCmpi(method, "additional_connect") == 0)
{
SOCK *sock;
TCPSOCK *ts;
UINT dummy;
c->Type = CONNECTION_TYPE_ADDITIONAL;
// Additional connection
// Read the session key
if (GetSessionKeyFromPack(p, session_key, &dummy) == false)
{
FreePack(p);
c->Err = ERR_PROTOCOL_ERROR;
goto CLEANUP;
}
FreePack(p);
// Get the session from the session key
s = GetSessionFromKey(c->Cedar, session_key);
if (s == NULL || s->Halt || s->NoReconnectToSession)
{
// Session can not be found, or re-connection is prohibited
Debug("Session Not Found.\n");
c->Err = ERR_SESSION_TIMEOUT;
goto CLEANUP;
}
// Session is found
Debug("Session Found: %s\n", s->Name);
// Check the protocol of session
c->Err = 0;
Lock(s->lock);
{
if (s->Connection->Protocol != CONNECTION_TCP)
{
c->Err = ERR_INVALID_PROTOCOL;
}
}
Unlock(s->lock);
// Check the current number of connections of the session
Lock(s->Connection->lock);
if (c->Err == 0)
{
if (Count(s->Connection->CurrentNumConnection) > s->MaxConnection)
{
c->Err = ERR_TOO_MANY_CONNECTION;
}
}
if (c->Err != 0)
{
Unlock(s->Connection->lock);
if (c->Err == ERR_TOO_MANY_CONNECTION)
{
Debug("Session TOO MANY CONNECTIONS !!: %u\n",
Count(s->Connection->CurrentNumConnection));
}
else
{
Debug("Session Invalid Protocol.\n");
}
ReleaseSession(s);
goto CLEANUP;
}
// Add the socket of this connection to the connection list of the session (TCP)
sock = c->FirstSock;
ts = NewTcpSock(sock);
SetTimeout(sock, CONNECTING_TIMEOUT);
direction = TCP_BOTH;
LockList(s->Connection->Tcp->TcpSockList);
{
if (s->HalfConnection)
{
// In half-connection, directions of the TCP connections are automatically
// adjusted by examining all current direction of the TCP connections
UINT i, c2s, s2c;
c2s = s2c = 0;
for (i = 0;i < LIST_NUM(s->Connection->Tcp->TcpSockList);i++)
{
TCPSOCK *ts = (TCPSOCK *)LIST_DATA(s->Connection->Tcp->TcpSockList, i);
if (ts->Direction == TCP_SERVER_TO_CLIENT)
{
s2c++;
}
else
{
c2s++;
}
}
if (s2c > c2s)
{
direction = TCP_CLIENT_TO_SERVER;
}
else
{
direction = TCP_SERVER_TO_CLIENT;
}
Debug("%u/%u\n", s2c, c2s);
ts->Direction = direction;
}
}
UnlockList(s->Connection->Tcp->TcpSockList);
// Return a success result
p = PackError(ERR_NO_ERROR);
PackAddInt(p, "direction", direction);
HttpServerSend(c->FirstSock, p);
FreePack(p);
SetTimeout(sock, INFINITE);
LockList(s->Connection->Tcp->TcpSockList);
{
Add(s->Connection->Tcp->TcpSockList, ts);
}
UnlockList(s->Connection->Tcp->TcpSockList);
// Increment the number of connections
Inc(s->Connection->CurrentNumConnection);
Debug("TCP Connection Incremented: %u\n", Count(s->Connection->CurrentNumConnection));
// Issue the Cancel of session
Cancel(s->Cancel1);
Unlock(s->Connection->lock);
c->flag1 = true;
ReleaseSession(s);
return true;
}
else if (StrCmpi(method, "enum_hub") == 0)
{
// Enumerate the Virtual HUB
UINT i, num;
LIST *o;
o = NewListFast(NULL);
c->Type = CONNECTION_TYPE_ENUM_HUB;
FreePack(p);
p = NewPack();
LockList(c->Cedar->HubList);
{
num = LIST_NUM(c->Cedar->HubList);
for (i = 0;i < num;i++)
{
HUB *h = LIST_DATA(c->Cedar->HubList, i);
if (h->Option != NULL && h->Option->NoEnum == false)
{
Insert(o, CopyStr(h->Name));
}
}
}
UnlockList(c->Cedar->HubList);
num = LIST_NUM(o);
for (i = 0;i < num;i++)
{
char *name = LIST_DATA(o, i);
PackAddStrEx(p, "HubName", name, i, num);
Free(name);
}
ReleaseList(o);
PackAddInt(p, "NumHub", num);
HttpServerSend(c->FirstSock, p);
FreePack(p);
FreePack(HttpServerRecv(c->FirstSock));
c->Err = 0;
SLog(c->Cedar, "LS_ENUM_HUB", c->Name, num);
error_detail = "enum_hub";
goto CLEANUP;
}
else if (StrCmpi(method, "farm_connect") == 0)
{
// Server farm connection request
CEDAR *cedar = c->Cedar;
c->Type = CONNECTION_TYPE_FARM_RPC;
c->Err = 0;
if (c->Cedar->Server == NULL)
{
// Unsupported
c->Err = ERR_NOT_FARM_CONTROLLER;
}
else
{
SERVER *s = c->Cedar->Server;
if (s->ServerType != SERVER_TYPE_FARM_CONTROLLER || s->FarmControllerInited == false)
{
// Not a farm controller
SLog(c->Cedar, "LS_FARM_ACCEPT_1", c->Name);
c->Err = ERR_NOT_FARM_CONTROLLER;
}
else
{
UCHAR check_secure_password[SHA1_SIZE];
UCHAR secure_password[SHA1_SIZE];
// User authentication
SecurePassword(check_secure_password, s->HashedPassword, c->Random);
if (PackGetDataSize(p, "SecurePassword") == sizeof(secure_password))
{
PackGetData(p, "SecurePassword", secure_password);
}
else
{
Zero(secure_password, sizeof(secure_password));
}
if (Cmp(secure_password, check_secure_password, SHA1_SIZE) != 0)
{
// Password is different
SLog(c->Cedar, "LS_FARM_ACCEPT_2", c->Name);
c->Err = ERR_ACCESS_DENIED;
}
else
{
// Get the certificate
BUF *b;
X *server_x;
SLog(c->Cedar, "LS_FARM_ACCEPT_3", c->Name);
b = PackGetBuf(p, "ServerCert");
if (b == NULL)
{
c->Err = ERR_PROTOCOL_ERROR;
}
else
{
server_x = BufToX(b, false);
FreeBuf(b);
if (server_x == NULL)
{
c->Err = ERR_PROTOCOL_ERROR;
}
else
{
UINT ip;
UINT point;
char hostname[MAX_SIZE];
#ifdef OS_WIN32
MsSetThreadPriorityRealtime();
#endif // OS_WIN32
SetTimeout(c->FirstSock, SERVER_CONTROL_TCP_TIMEOUT);
ip = PackGetIp32(p, "PublicIp");
point = PackGetInt(p, "Point");
if (PackGetStr(p, "HostName", hostname, sizeof(hostname)))
{
UINT num_port = PackGetIndexCount(p, "PublicPort");
if (num_port >= 1 && num_port <= MAX_PUBLIC_PORT_NUM)
{
UINT *ports = ZeroMalloc(sizeof(UINT) * num_port);
UINT i;
for (i = 0;i < num_port;i++)
{
ports[i] = PackGetIntEx(p, "PublicPort", i);
}
SiFarmServ(s, c->FirstSock, server_x, ip, num_port, ports, hostname, point,
PackGetInt(p, "Weight"), PackGetInt(p, "MaxSessions"));
Free(ports);
}
}
FreeX(server_x);
}
}
}
}
}
FreePack(p);
goto CLEANUP;
}
else if (StrCmpi(method, "admin") == 0 && c->Cedar->Server != NULL)
{
UINT err;
// Administrative RPC connection request
c->Type = CONNECTION_TYPE_ADMIN_RPC;
err = AdminAccept(c, p);
FreePack(p);
if (err != ERR_NO_ERROR)
{
PACK *p = PackError(err);
HttpServerSend(c->FirstSock, p);
FreePack(p);
}
error_detail = "admin_rpc";
goto CLEANUP;
}
else if (StrCmpi(method, "password") == 0)
{
UINT err;
// Password change request
c->Type = CONNECTION_TYPE_PASSWORD;
err = ChangePasswordAccept(c, p);
FreePack(p);
p = PackError(err);
HttpServerSend(c->FirstSock, p);
FreePack(p);
error_detail = "change_password";
goto CLEANUP;
}
else
{
// Unknown method
FreePack(p);
c->Err = ERR_PROTOCOL_ERROR;
error_detail = "unknown_method";
goto CLEANUP;
}
CLEANUP:
// Release the user object
if (loggedin_user_object != NULL)
{
ReleaseUser(loggedin_user_object);
}
// Error packet transmission
if (supress_return_pack_error == false)
{
p = PackError(c->Err);
PackAddBool(p, "no_save_password", no_save_password);
HttpServerSend(c->FirstSock, p);
FreePack(p);
}
FreePack(HttpServerRecv(c->FirstSock));
SleepThread(25);
SLog(c->Cedar, "LS_CONNECTION_ERROR", c->Name, GetUniErrorStr(c->Err), c->Err);
if (release_me_eap_client != NULL)
{
ReleaseEapClient(release_me_eap_client);
}
return ret;
}
// Create a Node information
void CreateNodeInfo(NODE_INFO *info, CONNECTION *c)
{
SESSION *s;
OS_INFO *os;
char *product_id;
IP ip;
bool is_vgc = false;
// Validate arguments
if (c == NULL)
{
return;
}
s = c->Session;
os = GetOsInfo();
Zero(info, sizeof(NODE_INFO));
// Client product name
StrCpy(info->ClientProductName, sizeof(info->ClientProductName), c->ClientStr);
// Client version
info->ClientProductVer = Endian32(c->ClientVer);
// Client build number
info->ClientProductBuild = Endian32(c->ClientBuild);
// Server product name
StrCpy(info->ServerProductName, sizeof(info->ServerProductName), c->ServerStr);
// Server version
info->ServerProductVer = Endian32(c->ServerVer);
// Server build number
info->ServerProductBuild = Endian32(c->ServerBuild);
// Client OS name
StrCpy(info->ClientOsName, sizeof(info->ClientOsName), os->OsProductName);
// Client OS version
StrCpy(info->ClientOsVer, sizeof(info->ClientOsVer), os->OsVersion);
// Client OS Product ID
product_id = OSGetProductId();
StrCpy(info->ClientOsProductId, sizeof(info->ClientOsProductId), product_id);
Free(product_id);
// Client host name
#ifndef OS_WIN32
GetMachineName(info->ClientHostname, sizeof(info->ClientHostname));
#else // OS_WIN32
if (true)
{
wchar_t namew[256];
char namea[256];
Zero(namew, sizeof(namew));
MsGetComputerNameFullEx(namew, sizeof(namew), true);
Zero(namea, sizeof(namea));
UniToStr(namea, sizeof(namea), namew);
if (IsEmptyStr(namea))
{
GetMachineName(namea, sizeof(namea));
}
StrCpy(info->ClientHostname, sizeof(info->ClientHostname), namea);
}
#endif // OS_WIN32
// Client IP address
if (IsIP6(&c->FirstSock->LocalIP) == false)
{
info->ClientIpAddress = IPToUINT(&c->FirstSock->LocalIP);
}
else
{
Copy(info->ClientIpAddress6, c->FirstSock->LocalIP.ipv6_addr, sizeof(info->ClientIpAddress6));
}
// Client port number
info->ClientPort = Endian32(c->FirstSock->LocalPort);
// Server host name
StrCpy(info->ServerHostname, sizeof(info->ServerHostname), c->ServerName);
// Server IP address
if (GetIP(&ip, info->ServerHostname))
{
if (IsIP6(&ip) == false)
{
info->ServerIpAddress = IPToUINT(&ip);
}
else
{
Copy(info->ServerIpAddress6, ip.ipv6_addr, sizeof(info->ServerIpAddress6));
}
}
// Server port number
info->ServerPort = Endian32(c->ServerPort);
if (s->ClientOption->ProxyType == PROXY_SOCKS || s->ClientOption->ProxyType == PROXY_HTTP)
{
// Proxy host name
StrCpy(info->ProxyHostname, sizeof(info->ProxyHostname), s->ClientOption->ProxyName);
// Proxy Server IP Address
if (IsIP6(&c->FirstSock->RemoteIP) == false)
{
info->ProxyIpAddress = IPToUINT(&c->FirstSock->RemoteIP);
}
else
{
Copy(&info->ProxyIpAddress6, c->FirstSock->RemoteIP.ipv6_addr, sizeof(info->ProxyIpAddress6));
}
info->ProxyPort = Endian32(c->FirstSock->RemotePort);
}
// HUB name
StrCpy(info->HubName, sizeof(info->HubName), s->ClientOption->HubName);
// Unique ID
Copy(info->UniqueId, c->Cedar->UniqueId, sizeof(info->UniqueId));
}
// Connect a socket additionally
SOCK *ClientAdditionalConnectToServer(CONNECTION *c)
{
SOCK *s;
// Validate arguments
if (c == NULL)
{
return NULL;
}
// Socket connection
s = ClientConnectGetSocket(c, true);
if (s == NULL)
{
// Connection failure
return NULL;
}
// Add the socket to the list
LockList(c->ConnectingSocks);
{
Add(c->ConnectingSocks, s);
AddRef(s->ref);
}
UnlockList(c->ConnectingSocks);
if (c->Session->Halt)
{
// Stop
Disconnect(s);
LockList(c->ConnectingSocks);
{
if (Delete(c->ConnectingSocks, s))
{
ReleaseSock(s);
}
}
UnlockList(c->ConnectingSocks);
ReleaseSock(s);
return NULL;
}
// Time-out
SetTimeout(s, CONNECTING_TIMEOUT);
// Start the SSL communication
if (StartSSLEx(s, NULL, NULL, 0, c->ServerName) == false)
{
// SSL communication failure
Disconnect(s);
LockList(c->ConnectingSocks);
{
if (Delete(c->ConnectingSocks, s))
{
ReleaseSock(s);
}
}
UnlockList(c->ConnectingSocks);
ReleaseSock(s);
return NULL;
}
// Check the certificate
if (CompareX(s->RemoteX, c->ServerX) == false)
{
// The certificate is invalid
Disconnect(s);
c->Session->SessionTimeOuted = true;
}
return s;
}
// Remove the key and certificate in the secure device
UINT SecureDelete(UINT device_id, char *pin, char *cert_name, char *key_name)
{
SECURE *sec;
// Validate arguments
if (pin == NULL || device_id == 0)
{
return ERR_INTERNAL_ERROR;
}
// Open the device
sec = OpenSec(device_id);
if (sec == NULL)
{
return ERR_SECURE_DEVICE_OPEN_FAILED;
}
// Open the session
if (OpenSecSession(sec, 0) == false)
{
CloseSec(sec);
return ERR_SECURE_DEVICE_OPEN_FAILED;
}
// Login
if (LoginSec(sec, pin) == false)
{
CloseSecSession(sec);
CloseSec(sec);
return ERR_SECURE_PIN_LOGIN_FAILED;
}
// Delete the certificate
if (cert_name != NULL)
{
DeleteSecCert(sec, cert_name);
}
// Delete the Private key
if (key_name != NULL)
{
DeleteSecKey(sec, key_name);
}
// Log out
LogoutSec(sec);
// Close the session
CloseSecSession(sec);
// Close the device
CloseSec(sec);
return ERR_NO_ERROR;
}
// Enumerate certificates and keys in the secure device
UINT SecureEnum(UINT device_id, char *pin, TOKEN_LIST **cert_list, TOKEN_LIST **key_list)
{
SECURE *sec;
LIST *o;
LIST *cert_name_list, *key_name_list;
// Validate arguments
if (pin == NULL || device_id == 0 || cert_list == NULL || key_list == NULL)
{
return ERR_INTERNAL_ERROR;
}
// Open the device
sec = OpenSec(device_id);
if (sec == NULL)
{
return ERR_SECURE_DEVICE_OPEN_FAILED;
}
// Open the session
if (OpenSecSession(sec, 0) == false)
{
CloseSec(sec);
return ERR_SECURE_DEVICE_OPEN_FAILED;
}
// Login
if (LoginSec(sec, pin) == false)
{
CloseSecSession(sec);
CloseSec(sec);
return ERR_SECURE_PIN_LOGIN_FAILED;
}
// Enumerate objects
if ((o = EnumSecObject(sec)) != NULL)
{
UINT i;
cert_name_list = NewList(CompareStr);
key_name_list = NewList(CompareStr);
for (i = 0;i < LIST_NUM(o);i++)
{
SEC_OBJ *obj = LIST_DATA(o, i);
if (obj->Type == SEC_X)
{
Add(cert_name_list, CopyStr(obj->Name));
}
else if (obj->Type == SEC_K)
{
Add(key_name_list, CopyStr(obj->Name));
}
}
Sort(cert_name_list);
Sort(key_name_list);
*cert_list = ListToTokenList(cert_name_list);
*key_list = ListToTokenList(key_name_list);
// Release the memory
FreeStrList(cert_name_list);
FreeStrList(key_name_list);
FreeEnumSecObject(o);
}
else
{
*cert_list = NullToken();
*key_list = NullToken();
}
// Log out
LogoutSec(sec);
// Close the session
CloseSecSession(sec);
// Close the device
CloseSec(sec);
return ERR_NO_ERROR;
}
// Record the certificate and key to secure device
UINT SecureWrite(UINT device_id, char *cert_name, X *x, char *key_name, K *k, char *pin)
{
SECURE *sec;
bool failed;
// Validate arguments
if (pin == NULL || device_id == 0 || cert_name == NULL || x == NULL || key_name == NULL || k == NULL)
{
return ERR_INTERNAL_ERROR;
}
// Open the device
sec = OpenSec(device_id);
if (sec == NULL)
{
return ERR_SECURE_DEVICE_OPEN_FAILED;
}
// Open the session
if (OpenSecSession(sec, 0) == false)
{
CloseSec(sec);
return ERR_SECURE_DEVICE_OPEN_FAILED;
}
// Login
if (LoginSec(sec, pin) == false)
{
CloseSecSession(sec);
CloseSec(sec);
return ERR_SECURE_PIN_LOGIN_FAILED;
}
// Registration
failed = false;
// Register the certificate
if (WriteSecCert(sec, true, cert_name, x) == false)
{
failed = true;
}
// Register the private key
if (WriteSecKey(sec, true, key_name, k) == false)
{
failed = true;
}
// Log out
LogoutSec(sec);
// Close the session
CloseSecSession(sec);
// Close the device
CloseSec(sec);
if (failed == false)
{
// Success
return ERR_NO_ERROR;
}
else
{
// Failure
return ERR_SECURE_CANT_WRITE;
}
}
// Attempt to sign by the secure device
UINT SecureSign(SECURE_SIGN *sign, UINT device_id, char *pin)
{
SECURE *sec;
X *x;
// Validate arguments
if (sign == false || pin == NULL || device_id == 0)
{
return ERR_INTERNAL_ERROR;
}
// Open the device
sec = OpenSec(device_id);
if (sec == NULL)
{
return ERR_SECURE_DEVICE_OPEN_FAILED;
}
// Open the session
if (OpenSecSession(sec, 0) == false)
{
CloseSec(sec);
return ERR_SECURE_DEVICE_OPEN_FAILED;
}
// Login
if (LoginSec(sec, pin) == false)
{
CloseSecSession(sec);
CloseSec(sec);
return ERR_SECURE_PIN_LOGIN_FAILED;
}
// Read the certificate
x = ReadSecCert(sec, sign->SecurePublicCertName);
if (x == NULL)
{
LogoutSec(sec);
CloseSecSession(sec);
CloseSec(sec);
return ERR_SECURE_NO_CERT;
}
// Sign by the private key
if (SignSec(sec, sign->SecurePrivateKeyName, sign->Signature, sign->Random, SHA1_SIZE) == false)
{
// Signing failure
FreeX(x);
LogoutSec(sec);
CloseSecSession(sec);
CloseSec(sec);
return ERR_SECURE_NO_PRIVATE_KEY;
}
// Convert the certificate to buffer
sign->ClientCert = x;
// Log out
LogoutSec(sec);
// Close the session
CloseSecSession(sec);
// Close the device
CloseSec(sec);
// Success
return ERR_NO_ERROR;
}
// Client connects to the server additionally
bool ClientAdditionalConnect(CONNECTION *c, THREAD *t)
{
SOCK *s;
PACK *p;
TCPSOCK *ts;
UINT err;
UINT direction;
// Validate arguments
if (c == NULL)
{
return false;
}
// Socket connection to the server
s = ClientAdditionalConnectToServer(c);
if (s == NULL)
{
// Failed to connect socket
return false;
}
if (c->Halt)
{
goto CLEANUP;
}
// Send a signature
Debug("Uploading Signature...\n");
if (ClientUploadSignature(s) == false)
{
goto CLEANUP;
}
if (c->Halt)
{
// Stop
goto CLEANUP;
}
// Receive a Hello packet
Debug("Downloading Hello...\n");
if (ClientDownloadHello(c, s) == false)
{
goto CLEANUP;
}
if (c->Halt)
{
// Stop
goto CLEANUP;
}
// Send a authentication data for the additional connection
if (ClientUploadAuth2(c, s) == false)
{
// Disconnected
goto CLEANUP;
}
// Receive a response
p = HttpClientRecv(s);
if (p == NULL)
{
// Disconnected
goto CLEANUP;
}
err = GetErrorFromPack(p);
direction = PackGetInt(p, "direction");
FreePack(p);
p = NULL;
if (err != 0)
{
// Error has occurred
Debug("Additional Connect Error: %u\n", err);
if (err == ERR_SESSION_TIMEOUT || err == ERR_INVALID_PROTOCOL)
{
// We shall re-connection because it is a fatal error
c->Session->SessionTimeOuted = true;
}
goto CLEANUP;
}
Debug("Additional Connect Succeed!\n");
// Success the additional connection
// Add to the TcpSockList of the connection
ts = NewTcpSock(s);
if (c->ServerMode == false)
{
if (c->Session->ClientOption->ConnectionDisconnectSpan != 0)
{
ts->DisconnectTick = Tick64() + c->Session->ClientOption->ConnectionDisconnectSpan * (UINT64)1000;
}
}
LockList(c->Tcp->TcpSockList);
{
ts->Direction = direction;
Add(c->Tcp->TcpSockList, ts);
}
UnlockList(c->Tcp->TcpSockList);
Debug("TCP Connection Incremented: %u\n", Count(c->CurrentNumConnection));
if (c->Session->HalfConnection)
{
Debug("New Half Connection: %s\n",
direction == TCP_SERVER_TO_CLIENT ? "TCP_SERVER_TO_CLIENT" : "TCP_CLIENT_TO_SERVER"
);
}
// Issue the Cancel to the session
Cancel(c->Session->Cancel1);
// Remove the socket from the socket list of connected
LockList(c->ConnectingSocks);
{
if (Delete(c->ConnectingSocks, s))
{
ReleaseSock(s);
}
}
UnlockList(c->ConnectingSocks);
ReleaseSock(s);
return true;
CLEANUP:
// Disconnection process
Disconnect(s);
LockList(c->ConnectingSocks);
{
if (Delete(c->ConnectingSocks, s))
{
ReleaseSock(s);
}
}
UnlockList(c->ConnectingSocks);
ReleaseSock(s);
return false;
}
// Secure device signing thread
void ClientSecureSignThread(THREAD *thread, void *param)
{
SECURE_SIGN_THREAD_PROC *p = (SECURE_SIGN_THREAD_PROC *)param;
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
NoticeThreadInit(thread);
p->Ok = p->SecureSignProc(p->Connection->Session, p->Connection, p->SecureSign);
p->UserFinished = true;
}
// Signing with the secure device
bool ClientSecureSign(CONNECTION *c, UCHAR *sign, UCHAR *random, X **x)
{
SECURE_SIGN_THREAD_PROC *p;
SECURE_SIGN *ss;
SESSION *s;
CLIENT_OPTION *o;
CLIENT_AUTH *a;
THREAD *thread;
UINT64 start;
bool ret;
// Validate arguments
if (c == NULL || sign == NULL || random == NULL || x == NULL)
{
return false;
}
s = c->Session;
o = s->ClientOption;
a = s->ClientAuth;
p = ZeroMalloc(sizeof(SECURE_SIGN_THREAD_PROC));
p->Connection = c;
ss = p->SecureSign = ZeroMallocEx(sizeof(SECURE_SIGN), true);
StrCpy(ss->SecurePrivateKeyName, sizeof(ss->SecurePrivateKeyName),
a->SecurePrivateKeyName);
StrCpy(ss->SecurePublicCertName, sizeof(ss->SecurePublicCertName),
a->SecurePublicCertName);
ss->UseSecureDeviceId = c->Cedar->Client->UseSecureDeviceId;
Copy(ss->Random, random, SHA1_SIZE);
#ifdef OS_WIN32
ss->BitmapId = CmGetSecureBitmapId(c->ServerName);
#endif // OS_WIN32
p->SecureSignProc = a->SecureSignProc;
// Create a thread
thread = NewThread(ClientSecureSignThread, p);
WaitThreadInit(thread);
// Poll every 0.5 seconds until signing is completed or canceled
start = Tick64();
while (true)
{
if ((Tick64() - start) > CONNECTING_POOLING_SPAN)
{
// Send a NOOP periodically for disconnection prevention
start = Tick64();
ClientUploadNoop(c);
}
if (p->UserFinished)
{
// User selected
break;
}
WaitThread(thread, 500);
}
ReleaseThread(thread);
ret = p->Ok;
if (ret)
{
Copy(sign, ss->Signature, sizeof(ss->Signature));
*x = ss->ClientCert;
}
Free(p->SecureSign);
Free(p);
return ret;
}
// Server certificate confirmation thread
void ClientCheckServerCertThread(THREAD *thread, void *param)
{
CHECK_CERT_THREAD_PROC *p = (CHECK_CERT_THREAD_PROC *)param;
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
// Notify the completion of initialization
NoticeThreadInit(thread);
// Query for the selection to the user
p->Ok = p->CheckCertProc(p->Connection->Session, p->Connection, p->ServerX, &p->Expired);
p->UserSelected = true;
}
// Client verify the certificate of the server
bool ClientCheckServerCert(CONNECTION *c, bool *expired)
{
CLIENT_AUTH *auth;
X *x;
CHECK_CERT_THREAD_PROC *p;
THREAD *thread;
CEDAR *cedar;
bool ret;
UINT64 start;
// Validate arguments
if (c == NULL)
{
return false;
}
if (expired != NULL)
{
*expired = false;
}
auth = c->Session->ClientAuth;
cedar = c->Cedar;
if (auth->CheckCertProc == NULL && c->Session->LinkModeClient == false)
{
// No checking function
return true;
}
if (c->Session->LinkModeClient && c->Session->Link->CheckServerCert == false)
{
// It's in cascade connection mode, but do not check the server certificate
return true;
}
if (c->UseTicket)
{
// Check the certificate of the redirected VPN server
if (CompareX(c->FirstSock->RemoteX, c->ServerX) == false)
{
return false;
}
else
{
return true;
}
}
x = CloneX(c->FirstSock->RemoteX);
if (x == NULL)
{
// Strange error occurs
return false;
}
if (CheckXDateNow(x))
{
// Check whether it is signed by the root certificate to trust
if (c->Session->LinkModeClient == false)
{
// Normal VPN Client mode
if (CheckSignatureByCa(cedar, x))
{
// This certificate can be trusted because it is signed
FreeX(x);
return true;
}
}
else
{
// Cascade connection mode
if (CheckSignatureByCaLinkMode(c->Session, x))
{
// This certificate can be trusted because it is signed
FreeX(x);
return true;
}
}
}
if (c->Session->LinkModeClient)
{
if (CheckXDateNow(x))
{
Lock(c->Session->Link->lock);
{
if (c->Session->Link->ServerCert != NULL)
{
if (CompareX(c->Session->Link->ServerCert, x))
{
Unlock(c->Session->Link->lock);
// Exactly match the certificate that is registered in the cascade configuration
FreeX(x);
return true;
}
}
}
Unlock(c->Session->Link->lock);
}
else
{
if (expired != NULL)
{
*expired = true;
}
}
// Verification failure at this point in the case of cascade connection mode
FreeX(x);
return false;
}
p = ZeroMalloc(sizeof(CHECK_CERT_THREAD_PROC));
p->ServerX = x;
p->CheckCertProc = auth->CheckCertProc;
p->Connection = c;
// Create a thread
thread = NewThread(ClientCheckServerCertThread, p);
WaitThreadInit(thread);
// Poll at 0.5-second intervals until the user selects whether the connection
start = Tick64();
while (true)
{
if ((Tick64() - start) > CONNECTING_POOLING_SPAN)
{
// Send a NOOP periodically for disconnection prevention
start = Tick64();
ClientUploadNoop(c);
}
if (p->UserSelected)
{
// User-selected
break;
}
WaitThread(thread, 500);
}
if (expired != NULL)
{
*expired = p->Expired;
}
ret = p->Ok;
FreeX(p->ServerX);
Free(p);
ReleaseThread(thread);
return ret;
}
// Client connects to the server
bool ClientConnect(CONNECTION *c)
{
bool ret = false;
bool ok = false;
UINT err;
SOCK *s;
PACK *p = NULL;
UINT session_key_32;
SESSION *sess;
char session_name[MAX_SESSION_NAME_LEN + 1];
char connection_name[MAX_CONNECTION_NAME_LEN + 1];
UCHAR session_key[SHA1_SIZE];
POLICY *policy;
bool expired = false;
IP server_ip;
// Validate arguments
if (c == NULL)
{
return false;
}
sess = c->Session;
PrintStatus(sess, L"init");
PrintStatus(sess, _UU("STATUS_1"));
REDIRECTED:
// [Connecting]
c->Status = CONNECTION_STATUS_CONNECTING;
c->Session->ClientStatus = CLIENT_STATUS_CONNECTING;
s = ClientConnectToServer(c);
if (s == NULL)
{
PrintStatus(sess, L"free");
return false;
}
Copy(&server_ip, &s->RemoteIP, sizeof(IP));
if (c->Halt)
{
// Stop
c->Err = ERR_USER_CANCEL;
goto CLEANUP;
}
// [Negotiating]
c->Session->ClientStatus = CLIENT_STATUS_NEGOTIATION;
// Initialize the UDP acceleration function
if (sess->ClientOption != NULL && sess->ClientOption->NoUdpAcceleration == false)
{
if (sess->ClientOption->ProxyType == PROXY_DIRECT)
{
if (s->Type == SOCK_TCP)
{
if (sess->UdpAccel == NULL)
{
bool no_nat_t = false;
if (sess->ClientOption->PortUDP != 0)
{
// There is no need for NAT-T treatment on my part if the UDP port on the other end is known beforehand
no_nat_t = true;
}
sess->UdpAccel = NewUdpAccel(c->Cedar, &s->LocalIP, true, true, no_nat_t);
}
}
}
}
// Send a signature
Debug("Uploading Signature...\n");
if (ClientUploadSignature(s) == false)
{
c->Err = ERR_DISCONNECTED;
goto CLEANUP;
}
if (c->Halt)
{
// Stop
c->Err = ERR_USER_CANCEL;
goto CLEANUP;
}
PrintStatus(sess, _UU("STATUS_5"));
// Receive a Hello packet
Debug("Downloading Hello...\n");
if (ClientDownloadHello(c, s) == false)
{
goto CLEANUP;
}
if (c->Session->ClientOption != NULL && c->Session->ClientOption->FromAdminPack)
{
if (IsAdminPackSupportedServerProduct(c->ServerStr) == false)
{
c->Err = ERR_NOT_ADMINPACK_SERVER;
goto CLEANUP;
}
}
if (c->Halt)
{
// Stop
c->Err = ERR_USER_CANCEL;
goto CLEANUP;
}
Debug("Server Version : %u\n"
"Server String : %s\n"
"Server Build : %u\n"
"Client Version : %u\n"
"Client String : %s\n"
"Client Build : %u\n",
c->ServerVer, c->ServerStr, c->ServerBuild,
c->ClientVer, c->ClientStr, c->ClientBuild);
// During user authentication
c->Session->ClientStatus = CLIENT_STATUS_AUTH;
// Verify the server certificate by the client
if (ClientCheckServerCert(c, &expired) == false)
{
if (expired == false)
{
c->Err = ERR_CERT_NOT_TRUSTED;
}
else
{
c->Err = ERR_SERVER_CERT_EXPIRES;
}
if (c->Session->LinkModeClient == false && c->Err == ERR_CERT_NOT_TRUSTED
&& (c->Session->Account == NULL || ! c->Session->Account->RetryOnServerCert))
{
c->Session->ForceStopFlag = true;
}
goto CLEANUP;
}
PrintStatus(sess, _UU("STATUS_6"));
// Send the authentication data
if (ClientUploadAuth(c) == false)
{
goto CLEANUP;
}
if (c->Halt)
{
// Stop
c->Err = ERR_USER_CANCEL;
goto CLEANUP;
}
// Receive a Welcome packet
p = HttpClientRecv(s);
if (p == NULL)
{
c->Err = ERR_DISCONNECTED;
goto CLEANUP;
}
// Error checking
err = GetErrorFromPack(p);
if (err != 0)
{
// An error has occured
c->Err = err;
c->ClientConnectError_NoSavePassword = PackGetBool(p, "no_save_password");
goto CLEANUP;
}
// Branding string check for the connection limit
{
char tmp[20];
char *branded_cfroms = _SS("BRANDED_C_FROM_S");
PackGetStr(p, "branded_cfroms", tmp, sizeof(tmp));
if(StrLen(branded_cfroms) > 0 && StrCmpi(branded_cfroms, tmp) != 0)
{
c->Err = ERR_BRANDED_C_FROM_S;
goto CLEANUP;
}
}
if (c->Cedar->Server == NULL)
{
// Suppress client notification flag
if (PackIsValueExists(p, "suppress_client_update_notification"))
{
bool suppress_client_update_notification = PackGetBool(p, "suppress_client_update_notification");
#ifdef OS_WIN32
MsRegWriteIntEx2(REG_LOCAL_MACHINE, PROTO_SUPPRESS_CLIENT_UPDATE_NOTIFICATION_REGKEY, PROTO_SUPPRESS_CLIENT_UPDATE_NOTIFICATION_REGVALUE,
(suppress_client_update_notification ? 1 : 0), false, true);
#endif // OS_WIN32
}
}
if (true)
{
// Message retrieval
UINT utf_size;
char *utf;
wchar_t *msg;
utf_size = PackGetDataSize(p, "Msg");
utf = ZeroMalloc(utf_size + 8);
PackGetData(p, "Msg", utf);
msg = CopyUtfToUni(utf);
if (IsEmptyUniStr(msg) == false)
{
if (c->Session->Client_Message != NULL)
{
Free(c->Session->Client_Message);
}
c->Session->Client_Message = msg;
}
else
{
Free(msg);
}
Free(utf);
}
if (PackGetInt(p, "Redirect") != 0)
{
UINT i;
UINT ip;
UINT num_port;
UINT *ports;
UINT use_port = 0;
UINT current_port = c->ServerPort;
UCHAR ticket[SHA1_SIZE];
X *server_cert;
BUF *b;
// Redirect mode
PrintStatus(sess, _UU("STATUS_8"));
ip = PackGetIp32(p, "Ip");
num_port = MAX(MIN(PackGetIndexCount(p, "Port"), MAX_PUBLIC_PORT_NUM), 1);
ports = ZeroMalloc(sizeof(UINT) * num_port);
for (i = 0;i < num_port;i++)
{
ports[i] = PackGetIntEx(p, "Port", i);
}
// Select a port number
for (i = 0;i < num_port;i++)
{
if (ports[i] == current_port)
{
use_port = current_port;
}
}
if (use_port == 0)
{
use_port = ports[0];
}
Free(ports);
if (PackGetDataSize(p, "Ticket") == SHA1_SIZE)
{
PackGetData(p, "Ticket", ticket);
}
b = PackGetBuf(p, "Cert");
if (b != NULL)
{
server_cert = BufToX(b, false);
FreeBuf(b);
}
if (c->ServerX != NULL)
{
FreeX(c->ServerX);
}
c->ServerX = server_cert;
IPToStr32(c->ServerName, sizeof(c->ServerName), ip);
c->ServerPort = use_port;
c->UseTicket = true;
Copy(c->Ticket, ticket, SHA1_SIZE);
FreePack(p);
p = NewPack();
HttpClientSend(s, p);
FreePack(p);
p = NULL;
c->FirstSock = NULL;
Disconnect(s);
ReleaseSock(s);
s = NULL;
goto REDIRECTED;
}
PrintStatus(sess, _UU("STATUS_7"));
// Parse the Welcome packet
if (ParseWelcomeFromPack(p, session_name, sizeof(session_name),
connection_name, sizeof(connection_name), &policy) == false)
{
// Parsing failure
c->Err = ERR_PROTOCOL_ERROR;
goto CLEANUP;
}
// Get the session key
if (GetSessionKeyFromPack(p, session_key, &session_key_32) == false)
{
// Acquisition failure
Free(policy);
policy = NULL;
c->Err = ERR_PROTOCOL_ERROR;
goto CLEANUP;
}
Copy(c->Session->SessionKey, session_key, SHA1_SIZE);
c->Session->SessionKey32 = session_key_32;
// Save the contents of the Welcome packet
Debug("session_name: %s, connection_name: %s\n",
session_name, connection_name);
Lock(c->Session->lock);
{
// Deploy and update connection parameters
sess->EnableUdpRecovery = PackGetBool(p, "enable_udp_recovery");
c->Session->MaxConnection = PackGetInt(p, "max_connection");
if (sess->EnableUdpRecovery == false)
{
c->Session->MaxConnection = MIN(c->Session->MaxConnection, c->Session->ClientOption->MaxConnection);
}
c->Session->MaxConnection = MIN(c->Session->MaxConnection, MAX_TCP_CONNECTION);
c->Session->MaxConnection = MAX(c->Session->MaxConnection, 1);
c->Session->UseCompress = PackGetInt(p, "use_compress") == 0 ? false : true;
c->Session->UseEncrypt = PackGetInt(p, "use_encrypt") == 0 ? false : true;
c->Session->NoSendSignature = PackGetBool(p, "no_send_signature");
c->Session->HalfConnection = PackGetInt(p, "half_connection") == 0 ? false : true;
c->Session->IsAzureSession = PackGetInt(p, "is_azure_session") == 0 ? false : true;
c->Session->Timeout = PackGetInt(p, "timeout");
c->Session->QoS = PackGetInt(p, "qos") == 0 ? false : true;
if (c->Session->QoS)
{
c->Session->MaxConnection = MAX(c->Session->MaxConnection, (UINT)(c->Session->HalfConnection ? 4 : 2));
}
c->Session->VLanId = PackGetInt(p, "vlan_id");
// R-UDP Session ?
c->Session->IsRUDPSession = s->IsRUDPSocket;
ZeroIP4(&c->Session->AzureRealServerGlobalIp);
if (c->Session->IsAzureSession)
{
// Disable the life parameter of the connection in the case of VPN Azure relayed session
c->Session->ClientOption->ConnectionDisconnectSpan = 0;
// Get the AzureRealServerGlobalIp the case of VPN Azure relayed
PackGetIp(p, "azure_real_server_global_ip", &c->Session->AzureRealServerGlobalIp);
}
if (c->Session->IsRUDPSession)
{
// Disable the life parameter of the connection in the case of R-UDP session
c->Session->ClientOption->ConnectionDisconnectSpan = 0;
// Disable QoS, etc. in the case of R-UDP session
c->Session->QoS = false;
c->Session->HalfConnection = false;
if (c->Session->EnableUdpRecovery == false)
{
// Set the number of connection to 1 if UDP recovery is not supported
c->Session->MaxConnection = 1;
}
}
// Physical communication protocol
StrCpy(c->Session->UnderlayProtocol, sizeof(c->Session->UnderlayProtocol), s->UnderlayProtocol);
if (c->Session->IsAzureSession)
{
StrCpy(c->Session->UnderlayProtocol, sizeof(c->Session->UnderlayProtocol), SOCK_UNDERLAY_AZURE);
}
if (c->Protocol == CONNECTION_UDP)
{
// In the case of UDP protocol, receive the key from the server
if (PackGetDataSize(p, "udp_send_key") == sizeof(c->Session->UdpSendKey))
{
PackGetData(p, "udp_send_key", c->Session->UdpSendKey);
}
if (PackGetDataSize(p, "udp_recv_key") == sizeof(c->Session->UdpRecvKey))
{
PackGetData(p, "udp_recv_key", c->Session->UdpRecvKey);
}
}
sess->EnableBulkOnRUDP = false;
sess->EnableHMacOnBulkOfRUDP = false;
if (s->IsRUDPSocket && s->BulkRecvKey != NULL && s->BulkSendKey != NULL)
{
// Bulk transfer on R-UDP
if (PackGetBool(p, "enable_bulk_on_rudp"))
{
// Receive the key
UCHAR key_send[SHA1_SIZE];
UCHAR key_recv[SHA1_SIZE];
if (PackGetData2(p, "bulk_on_rudp_send_key", key_send, SHA1_SIZE) &&
PackGetData2(p, "bulk_on_rudp_recv_key", key_recv, SHA1_SIZE))
{
sess->EnableBulkOnRUDP = true;
Copy(s->BulkSendKey->Data, key_send, SHA1_SIZE);
Copy(s->BulkRecvKey->Data, key_recv, SHA1_SIZE);
}
}
sess->EnableHMacOnBulkOfRUDP = PackGetBool(p, "enable_hmac_on_bulk_of_rudp");
}
Debug("EnableBulkOnRUDP = %u\n", sess->EnableBulkOnRUDP);
Debug("EnableHMacOnBulkOfRUDP = %u\n", sess->EnableHMacOnBulkOfRUDP);
Debug("EnableUdpRecovery = %u\n", sess->EnableUdpRecovery);
sess->UseUdpAcceleration = false;
sess->IsUsingUdpAcceleration = false;
sess->UseHMacOnUdpAcceleration = false;
if (sess->UdpAccel != NULL)
{
sess->UdpAccel->UseHMac = false;
sess->UdpAccelFastDisconnectDetect = false;
if (PackGetBool(p, "use_udp_acceleration"))
{
IP udp_acceleration_server_ip;
sess->UdpAccelFastDisconnectDetect = PackGetBool(p, "udp_accel_fast_disconnect_detect");
if (PackGetIp(p, "udp_acceleration_server_ip", &udp_acceleration_server_ip))
{
UINT udp_acceleration_server_port = PackGetInt(p, "udp_acceleration_server_port");
if (IsZeroIp(&udp_acceleration_server_ip))
{
Copy(&udp_acceleration_server_ip, &s->RemoteIP, sizeof(IP));
}
if (udp_acceleration_server_port != 0)
{
UCHAR udp_acceleration_server_key[UDP_ACCELERATION_COMMON_KEY_SIZE];
if (PackGetData2(p, "udp_acceleration_server_key", udp_acceleration_server_key, UDP_ACCELERATION_COMMON_KEY_SIZE))
{
UINT server_cookie = PackGetInt(p, "udp_acceleration_server_cookie");
UINT client_cookie = PackGetInt(p, "udp_acceleration_client_cookie");
bool encryption = PackGetBool(p, "udp_acceleration_use_encryption");
if (server_cookie != 0 && client_cookie != 0)
{
IP remote_ip;
Copy(&remote_ip, &s->RemoteIP, sizeof(IP));
if (IsZeroIp(&c->Session->AzureRealServerGlobalIp) == false)
{
Copy(&remote_ip, &c->Session->AzureRealServerGlobalIp, sizeof(IP));
}
if (UdpAccelInitClient(sess->UdpAccel, udp_acceleration_server_key,
&udp_acceleration_server_ip, udp_acceleration_server_port,
server_cookie, client_cookie, &remote_ip) == false)
{
Debug("UdpAccelInitClient failed.\n");
}
else
{
sess->UseUdpAcceleration = true;
sess->UdpAccel->FastDetect = sess->UdpAccelFastDisconnectDetect;
sess->UdpAccel->PlainTextMode = !encryption;
sess->UseHMacOnUdpAcceleration = PackGetBool(p, "use_hmac_on_udp_acceleration");
if (sess->UseHMacOnUdpAcceleration)
{
sess->UdpAccel->UseHMac = true;
}
}
}
}
}
}
}
}
}
Unlock(c->Session->lock);
Debug("UseUdpAcceleration = %u\n", sess->UseUdpAcceleration);
if (sess->UseUdpAcceleration == false)
{
if (sess->UdpAccel != NULL)
{
FreeUdpAccel(sess->UdpAccel);
sess->UdpAccel = NULL;
}
}
Lock(c->lock);
{
if (c->Name != NULL)
{
Free(c->Name);
}
c->Name = CopyStr(connection_name);
// Save the name of a cryptographic algorithm
if (c->CipherName != NULL)
{
Free(c->CipherName);
}
c->CipherName = CopyStr(c->FirstSock->CipherName);
}
Unlock(c->lock);
Lock(c->Session->lock);
{
if (c->Session->Name != NULL)
{
Free(c->Session->Name);
}
c->Session->Name = CopyStr(session_name);
c->Session->Policy = policy;
}
Unlock(c->Session->lock);
// Discard the Welcome packet
FreePack(p);
p = NULL;
// Connection establishment
c->Session->ClientStatus = CLIENT_STATUS_ESTABLISHED;
// Save the server certificate
if (c->ServerX == NULL)
{
c->ServerX = CloneX(c->FirstSock->RemoteX);
}
PrintStatus(sess, _UU("STATUS_9"));
#ifdef OS_UNIX
// Set TUN up if session has NicDownOnDisconnect set
if (c->Session->NicDownOnDisconnect != NULL)
{
UnixVLanSetState(c->Session->ClientOption->DeviceName, true);
}
#endif
// Shift the connection to the tunneling mode
StartTunnelingMode(c);
s = NULL;
if (c->Session->HalfConnection)
{
// Processing in the case of half-connection
TCPSOCK *ts = (TCPSOCK *)LIST_DATA(c->Tcp->TcpSockList, 0);
ts->Direction = TCP_CLIENT_TO_SERVER;
}
PrintStatus(sess, L"free");
CLog(c->Cedar->Client, "LC_CONNECT_2", c->Session->ClientOption->AccountName,
session_name);
if (c->Session->LinkModeClient && c->Session->Link != NULL)
{
HLog(c->Session->Link->Hub, "LH_CONNECT_2", c->Session->ClientOption->AccountName, session_name);
}
// Main routine of the session
SessionMain(c->Session);
ok = true;
if (c->Err == ERR_USER_CANCEL)
{
ret = true;
}
CLEANUP:
c->FirstSock = NULL;
if (sess->UdpAccel != NULL)
{
FreeUdpAccel(sess->UdpAccel);
sess->UdpAccel = NULL;
}
if (p != NULL)
{
FreePack(p);
}
Disconnect(s);
ReleaseSock(s);
Debug("Error: %u\n", c->Err);
if (ok == false)
{
PrintStatus(sess, L"free");
}
return ret;
}
// Parse the Welcome packet
bool ParseWelcomeFromPack(PACK *p, char *session_name, UINT session_name_size,
char *connection_name, UINT connection_name_size,
POLICY **policy)
{
// Validate arguments
if (p == NULL || session_name == NULL || connection_name == NULL || policy == NULL)
{
return false;
}
// Session name
if (PackGetStr(p, "session_name", session_name, session_name_size) == false)
{
return false;
}
// Connection name
if (PackGetStr(p, "connection_name", connection_name, connection_name_size) == false)
{
return false;
}
// Policy
*policy = PackGetPolicy(p);
if (*policy == NULL)
{
return false;
}
return true;
}
// Generate the Welcome packet
PACK *PackWelcome(SESSION *s)
{
PACK *p;
// Validate arguments
if (s == NULL)
{
return NULL;
}
p = NewPack();
// Session name
PackAddStr(p, "session_name", s->Name);
// Connection name
PackAddStr(p, "connection_name", s->Connection->Name);
// Parameters
PackAddInt(p, "max_connection", s->MaxConnection);
PackAddInt(p, "use_encrypt", s->UseEncrypt == false ? 0 : 1);
PackAddInt(p, "use_compress", s->UseCompress == false ? 0 : 1);
PackAddInt(p, "half_connection", s->HalfConnection == false ? 0 : 1);
PackAddInt(p, "timeout", s->Timeout);
PackAddInt(p, "qos", s->QoS ? 1 : 0);
PackAddInt(p, "is_azure_session", s->IsAzureSession);
// Session key
PackAddData(p, "session_key", s->SessionKey, SHA1_SIZE);
PackAddInt(p, "session_key_32", s->SessionKey32);
// Policy
PackAddPolicy(p, s->Policy);
// VLAN ID
PackAddInt(p, "vlan_id", s->VLanId);
if (s->Connection->Protocol == CONNECTION_UDP)
{
// In the case of UDP protocol, generate 2 pairs of key
Rand(s->UdpSendKey, sizeof(s->UdpSendKey));
Rand(s->UdpRecvKey, sizeof(s->UdpRecvKey));
// Send to client by exchanging 2 keys
PackAddData(p, "udp_send_key", s->UdpRecvKey, sizeof(s->UdpRecvKey));
PackAddData(p, "udp_recv_key", s->UdpSendKey, sizeof(s->UdpSendKey));
}
// no_send_signature
if (s->NoSendSignature)
{
PackAddBool(p, "no_send_signature", true);
}
if (s->InProcMode)
{
// MAC address for IPC
PackAddData(p, "IpcMacAddress", s->IpcMacAddress, 6);
// Virtual HUB name
PackAddStr(p, "IpcHubName", s->Hub->Name);
}
if (s->UdpAccel != NULL)
{
// UDP acceleration function
PackAddBool(p, "use_udp_acceleration", true);
PackAddIp(p, "udp_acceleration_server_ip", &s->UdpAccel->MyIp);
PackAddInt(p, "udp_acceleration_server_port", s->UdpAccel->MyPort);
PackAddData(p, "udp_acceleration_server_key", s->UdpAccel->MyKey, UDP_ACCELERATION_COMMON_KEY_SIZE);
PackAddInt(p, "udp_acceleration_server_cookie", s->UdpAccel->MyCookie);
PackAddInt(p, "udp_acceleration_client_cookie", s->UdpAccel->YourCookie);
PackAddBool(p, "udp_acceleration_use_encryption", !s->UdpAccel->PlainTextMode);
PackAddBool(p, "use_hmac_on_udp_acceleration", s->UdpAccel->UseHMac);
PackAddBool(p, "udp_accel_fast_disconnect_detect", s->UdpAccelFastDisconnectDetect);
}
if (s->EnableBulkOnRUDP)
{
// Allow bulk transfer on R-UDP
PackAddBool(p, "enable_bulk_on_rudp", true);
PackAddBool(p, "enable_hmac_on_bulk_of_rudp", s->EnableHMacOnBulkOfRUDP);
PackAddData(p, "bulk_on_rudp_send_key", s->Connection->FirstSock->BulkRecvKey->Data, SHA1_SIZE);
PackAddData(p, "bulk_on_rudp_recv_key", s->Connection->FirstSock->BulkSendKey->Data, SHA1_SIZE);
}
if (s->IsAzureSession)
{
if (s->Connection != NULL && s->Connection->FirstSock != NULL)
{
SOCK *sock = s->Connection->FirstSock;
PackAddIp(p, "azure_real_server_global_ip", &sock->Reverse_MyServerGlobalIp);
}
}
PackAddBool(p, "enable_udp_recovery", s->EnableUdpRecovery);
return p;
}
#define PACK_ADD_POLICY_BOOL(name, value) \
PackAddInt(p, "policy:" name, y->value == false ? 0 : 1)
#define PACK_ADD_POLICY_UINT(name, value) \
PackAddInt(p, "policy:" name, y->value)
#define PACK_GET_POLICY_BOOL(name, value) \
y->value = (PackGetInt(p, "policy:" name) == 0 ? false : true)
#define PACK_GET_POLICY_UINT(name, value) \
y->value = PackGetInt(p, "policy:" name)
// Get a PACK from the session key
bool GetSessionKeyFromPack(PACK *p, UCHAR *session_key, UINT *session_key_32)
{
// Validate arguments
if (p == NULL || session_key == NULL || session_key_32 == NULL)
{
return false;
}
if (PackGetDataSize(p, "session_key") != SHA1_SIZE)
{
return false;
}
if (PackGetData(p, "session_key", session_key) == false)
{
return false;
}
*session_key_32 = PackGetInt(p, "session_key_32");
return true;
}
// Get the policy from the PACK
POLICY *PackGetPolicy(PACK *p)
{
POLICY *y;
// Validate arguments
if (p == NULL)
{
return NULL;
}
y = ZeroMalloc(sizeof(POLICY));
// Bool value
// Ver 2
PACK_GET_POLICY_BOOL("Access", Access);
PACK_GET_POLICY_BOOL("DHCPFilter", DHCPFilter);
PACK_GET_POLICY_BOOL("DHCPNoServer", DHCPNoServer);
PACK_GET_POLICY_BOOL("DHCPForce", DHCPForce);
PACK_GET_POLICY_BOOL("NoBridge", NoBridge);
PACK_GET_POLICY_BOOL("NoRouting", NoRouting);
PACK_GET_POLICY_BOOL("PrivacyFilter", PrivacyFilter);
PACK_GET_POLICY_BOOL("NoServer", NoServer);
PACK_GET_POLICY_BOOL("CheckMac", CheckMac);
PACK_GET_POLICY_BOOL("CheckIP", CheckIP);
PACK_GET_POLICY_BOOL("ArpDhcpOnly", ArpDhcpOnly);
PACK_GET_POLICY_BOOL("MonitorPort", MonitorPort);
PACK_GET_POLICY_BOOL("NoBroadcastLimiter", NoBroadcastLimiter);
PACK_GET_POLICY_BOOL("FixPassword", FixPassword);
PACK_GET_POLICY_BOOL("NoQoS", NoQoS);
// Ver 3
PACK_GET_POLICY_BOOL("RSandRAFilter", RSandRAFilter);
PACK_GET_POLICY_BOOL("RAFilter", RAFilter);
PACK_GET_POLICY_BOOL("DHCPv6Filter", DHCPv6Filter);
PACK_GET_POLICY_BOOL("DHCPv6NoServer", DHCPv6NoServer);
PACK_GET_POLICY_BOOL("NoRoutingV6", NoRoutingV6);
PACK_GET_POLICY_BOOL("CheckIPv6", CheckIPv6);
PACK_GET_POLICY_BOOL("NoServerV6", NoServerV6);
PACK_GET_POLICY_BOOL("NoSavePassword", NoSavePassword);
PACK_GET_POLICY_BOOL("FilterIPv4", FilterIPv4);
PACK_GET_POLICY_BOOL("FilterIPv6", FilterIPv6);
PACK_GET_POLICY_BOOL("FilterNonIP", FilterNonIP);
PACK_GET_POLICY_BOOL("NoIPv6DefaultRouterInRA", NoIPv6DefaultRouterInRA);
PACK_GET_POLICY_BOOL("NoIPv6DefaultRouterInRAWhenIPv6", NoIPv6DefaultRouterInRAWhenIPv6);
// UINT value
// Ver 2
PACK_GET_POLICY_UINT("MaxConnection", MaxConnection);
PACK_GET_POLICY_UINT("TimeOut", TimeOut);
PACK_GET_POLICY_UINT("MaxMac", MaxMac);
PACK_GET_POLICY_UINT("MaxIP", MaxIP);
PACK_GET_POLICY_UINT("MaxUpload", MaxUpload);
PACK_GET_POLICY_UINT("MaxDownload", MaxDownload);
PACK_GET_POLICY_UINT("MultiLogins", MultiLogins);
// Ver 3
PACK_GET_POLICY_UINT("MaxIPv6", MaxIPv6);
PACK_GET_POLICY_UINT("AutoDisconnect", AutoDisconnect);
PACK_GET_POLICY_UINT("VLanId", VLanId);
// Ver 3 flag
PACK_GET_POLICY_BOOL("Ver3", Ver3);
return y;
}
// Insert the policy into the PACK
void PackAddPolicy(PACK *p, POLICY *y)
{
// Validate arguments
if (p == NULL || y == NULL)
{
return;
}
// Bool value
// Ver 2
PACK_ADD_POLICY_BOOL("Access", Access);
PACK_ADD_POLICY_BOOL("DHCPFilter", DHCPFilter);
PACK_ADD_POLICY_BOOL("DHCPNoServer", DHCPNoServer);
PACK_ADD_POLICY_BOOL("DHCPForce", DHCPForce);
PACK_ADD_POLICY_BOOL("NoBridge", NoBridge);
PACK_ADD_POLICY_BOOL("NoRouting", NoRouting);
PACK_ADD_POLICY_BOOL("PrivacyFilter", PrivacyFilter);
PACK_ADD_POLICY_BOOL("NoServer", NoServer);
PACK_ADD_POLICY_BOOL("CheckMac", CheckMac);
PACK_ADD_POLICY_BOOL("CheckIP", CheckIP);
PACK_ADD_POLICY_BOOL("ArpDhcpOnly", ArpDhcpOnly);
PACK_ADD_POLICY_BOOL("MonitorPort", MonitorPort);
PACK_ADD_POLICY_BOOL("NoBroadcastLimiter", NoBroadcastLimiter);
PACK_ADD_POLICY_BOOL("FixPassword", FixPassword);
PACK_ADD_POLICY_BOOL("NoQoS", NoQoS);
// Ver 3
PACK_ADD_POLICY_BOOL("RSandRAFilter", RSandRAFilter);
PACK_ADD_POLICY_BOOL("RAFilter", RAFilter);
PACK_ADD_POLICY_BOOL("DHCPv6Filter", DHCPv6Filter);
PACK_ADD_POLICY_BOOL("DHCPv6NoServer", DHCPv6NoServer);
PACK_ADD_POLICY_BOOL("NoRoutingV6", NoRoutingV6);
PACK_ADD_POLICY_BOOL("CheckIPv6", CheckIPv6);
PACK_ADD_POLICY_BOOL("NoServerV6", NoServerV6);
PACK_ADD_POLICY_BOOL("NoSavePassword", NoSavePassword);
PACK_ADD_POLICY_BOOL("FilterIPv4", FilterIPv4);
PACK_ADD_POLICY_BOOL("FilterIPv6", FilterIPv6);
PACK_ADD_POLICY_BOOL("FilterNonIP", FilterNonIP);
PACK_ADD_POLICY_BOOL("NoIPv6DefaultRouterInRA", NoIPv6DefaultRouterInRA);
PACK_ADD_POLICY_BOOL("NoIPv6DefaultRouterInRAWhenIPv6", NoIPv6DefaultRouterInRAWhenIPv6);
// UINT value
// Ver 2
PACK_ADD_POLICY_UINT("MaxConnection", MaxConnection);
PACK_ADD_POLICY_UINT("TimeOut", TimeOut);
PACK_ADD_POLICY_UINT("MaxMac", MaxMac);
PACK_ADD_POLICY_UINT("MaxIP", MaxIP);
PACK_ADD_POLICY_UINT("MaxUpload", MaxUpload);
PACK_ADD_POLICY_UINT("MaxDownload", MaxDownload);
PACK_ADD_POLICY_UINT("MultiLogins", MultiLogins);
// Ver 3
PACK_ADD_POLICY_UINT("MaxIPv6", MaxIPv6);
PACK_ADD_POLICY_UINT("AutoDisconnect", AutoDisconnect);
PACK_ADD_POLICY_UINT("VLanId", VLanId);
// Ver 3 flag
PackAddBool(p, "policy:Ver3", true);
}
// Upload the authentication data for the additional connection
bool ClientUploadAuth2(CONNECTION *c, SOCK *s)
{
PACK *p = NULL;
// Validate arguments
if (c == NULL)
{
return false;
}
p = PackAdditionalConnect(c->Session->SessionKey);
PackAddClientVersion(p, c);
if (HttpClientSend(s, p) == false)
{
FreePack(p);
return false;
}
FreePack(p);
return true;
}
// Send a NOOP
void ClientUploadNoop(CONNECTION *c)
{
PACK *p;
// Validate arguments
if (c == NULL)
{
return;
}
p = PackError(0);
PackAddInt(p, "noop", 1);
(void)HttpClientSend(c->FirstSock, p);
FreePack(p);
p = HttpClientRecv(c->FirstSock);
if (p != NULL)
{
FreePack(p);
}
}
// Add client version information to the PACK
void PackAddClientVersion(PACK *p, CONNECTION *c)
{
// Validate arguments
if (p == NULL || c == NULL)
{
return;
}
PackAddStr(p, "client_str", c->ClientStr);
PackAddInt(p, "client_ver", c->ClientVer);
PackAddInt(p, "client_build", c->ClientBuild);
}
// Upload the certificate data for the new connection
bool ClientUploadAuth(CONNECTION *c)
{
PACK *p = NULL;
CLIENT_AUTH *a;
CLIENT_OPTION *o;
X *x;
bool ret;
NODE_INFO info;
UCHAR secure_password[SHA1_SIZE];
UCHAR sign[4096 / 8];
UCHAR unique[SHA1_SIZE];
RPC_WINVER v;
// Validate arguments
if (c == NULL)
{
return false;
}
Zero(sign, sizeof(sign));
a = c->Session->ClientAuth;
o = c->Session->ClientOption;
if (c->UseTicket == false)
{
switch (a->AuthType)
{
case CLIENT_AUTHTYPE_ANONYMOUS:
// Anonymous authentication
p = PackLoginWithAnonymous(o->HubName, a->Username);
break;
case CLIENT_AUTHTYPE_PASSWORD:
// Password authentication
SecurePassword(secure_password, a->HashedPassword, c->Random);
p = PackLoginWithPassword(o->HubName, a->Username, secure_password);
break;
case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
// Plaintext password authentication
p = PackLoginWithPlainPassword(o->HubName, a->Username, a->PlainPassword);
break;
case CLIENT_AUTHTYPE_CERT:
// Certificate authentication
if (a->ClientX != NULL && a->ClientX->is_compatible_bit &&
a->ClientX->bits != 0 && (a->ClientX->bits / 8) <= sizeof(sign))
{
if (RsaSignEx(sign, c->Random, SHA1_SIZE, a->ClientK, a->ClientX->bits))
{
p = PackLoginWithCert(o->HubName, a->Username, a->ClientX, sign, a->ClientX->bits / 8);
c->ClientX = CloneX(a->ClientX);
}
}
break;
case CLIENT_AUTHTYPE_SECURE:
// Authentication by secure device
if (ClientSecureSign(c, sign, c->Random, &x))
{
p = PackLoginWithCert(o->HubName, a->Username, x, sign, x->bits / 8);
c->ClientX = CloneX(x);
FreeX(x);
}
else
{
c->Err = ERR_SECURE_DEVICE_OPEN_FAILED;
c->Session->ForceStopFlag = true;
}
break;
}
}
else
{
// Ticket
p = NewPack();
PackAddStr(p, "method", "login");
PackAddStr(p, "hubname", o->HubName);
PackAddStr(p, "username", a->Username);
PackAddInt(p, "authtype", AUTHTYPE_TICKET);
PackAddData(p, "ticket", c->Ticket, SHA1_SIZE);
}
if (p == NULL)
{
// Error
if (c->Err != ERR_SECURE_DEVICE_OPEN_FAILED)
{
c->Err = ERR_PROTOCOL_ERROR;
}
return false;
}
PackAddClientVersion(p, c);
// Protocol
PackAddInt(p, "protocol", c->Protocol);
// Version, etc.
PackAddStr(p, "hello", c->ClientStr);
PackAddInt(p, "version", c->ClientVer);
PackAddInt(p, "build", c->ClientBuild);
PackAddInt(p, "client_id", c->Cedar->ClientId);
// The maximum number of connections
PackAddInt(p, "max_connection", o->MaxConnection);
// Flag to use of cryptography
PackAddInt(p, "use_encrypt", o->UseEncrypt == false ? 0 : 1);
// Data compression flag
PackAddInt(p, "use_compress", o->UseCompress == false ? 0 : 1);
// Half connection flag
PackAddInt(p, "half_connection", o->HalfConnection == false ? 0 : 1);
// Bridge / routing mode flag
PackAddBool(p, "require_bridge_routing_mode", o->RequireBridgeRoutingMode);
// Monitor mode flag
PackAddBool(p, "require_monitor_mode", o->RequireMonitorMode);
// VoIP / QoS flag
PackAddBool(p, "qos", o->DisableQoS ? false : true);
// Bulk transfer support
PackAddBool(p, "support_bulk_on_rudp", true);
PackAddBool(p, "support_hmac_on_bulk_of_rudp", true);
// UDP recovery support
PackAddBool(p, "support_udp_recovery", true);
// Unique ID
GenerateMachineUniqueHash(unique);
PackAddData(p, "unique_id", unique, SHA1_SIZE);
// UDP acceleration function using flag
if (o->NoUdpAcceleration == false && c->Session->UdpAccel != NULL)
{
IP my_ip;
Zero(&my_ip, sizeof(my_ip));
PackAddBool(p, "use_udp_acceleration", true);
Copy(&my_ip, &c->Session->UdpAccel->MyIp, sizeof(IP));
if (IsLocalHostIP(&my_ip))
{
if (IsIP4(&my_ip))
{
ZeroIP4(&my_ip);
}
else
{
ZeroIP6(&my_ip);
}
}
PackAddIp(p, "udp_acceleration_client_ip", &my_ip);
PackAddInt(p, "udp_acceleration_client_port", c->Session->UdpAccel->MyPort);
PackAddData(p, "udp_acceleration_client_key", c->Session->UdpAccel->MyKey, UDP_ACCELERATION_COMMON_KEY_SIZE);
PackAddBool(p, "support_hmac_on_udp_acceleration", true);
PackAddBool(p, "support_udp_accel_fast_disconnect_detect", true);
}
// Brand string for the connection limit
{
char *branded_ctos = _SS("BRANDED_C_TO_S");
if(StrLen(branded_ctos) > 0)
{
PackAddStr(p, "branded_ctos", branded_ctos);
}
}
// Node information
CreateNodeInfo(&info, c);
OutRpcNodeInfo(p, &info);
// OS information
GetWinVer(&v);
OutRpcWinVer(p, &v);
ret = HttpClientSend(c->FirstSock, p);
if (ret == false)
{
c->Err = ERR_DISCONNECTED;
}
FreePack(p);
return ret;
}
// Upload the Hello packet
bool ServerUploadHello(CONNECTION *c)
{
PACK *p;
// Validate arguments
if (c == NULL)
{
return false;
}
// Random number generation
Rand(c->Random, SHA1_SIZE);
p = PackHello(c->Random, c->ServerVer, c->ServerBuild, c->ServerStr);
if (HttpServerSend(c->FirstSock, p) == false)
{
FreePack(p);
c->Err = ERR_DISCONNECTED;
return false;
}
FreePack(p);
return true;
}
// Download the Hello packet
bool ClientDownloadHello(CONNECTION *c, SOCK *s)
{
PACK *p;
UINT err;
UCHAR random[SHA1_SIZE];
// Validate arguments
if (c == NULL)
{
return false;
}
// Data reception
p = HttpClientRecv(s);
if (p == NULL)
{
c->Err = ERR_SERVER_IS_NOT_VPN;
return false;
}
if (err = GetErrorFromPack(p))
{
// An error has occured
c->Err = err;
FreePack(p);
return false;
}
// Packet interpretation
if (GetHello(p, random, &c->ServerVer, &c->ServerBuild, c->ServerStr, sizeof(c->ServerStr)) == false)
{
c->Err = ERR_SERVER_IS_NOT_VPN;
FreePack(p);
return false;
}
if (c->FirstSock == s)
{
Copy(c->Random, random, SHA1_SIZE);
}
FreePack(p);
return true;
}
// Download the signature
bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
{
HTTP_HEADER *h;
UCHAR *data;
UINT data_size;
SOCK *s;
UINT num = 0, max = 19;
SERVER *server;
char *vpn_http_target = HTTP_VPN_TARGET2;
// Validate arguments
if (c == NULL)
{
return false;
}
server = c->Cedar->Server;
s = c->FirstSock;
while (true)
{
bool not_found_error = false;
num++;
if (num > max)
{
// Disconnect
Disconnect(s);
c->Err = ERR_CLIENT_IS_NOT_VPN;
*error_detail_str = "HTTP_TOO_MANY_REQUEST";
return false;
}
// Receive a header
h = RecvHttpHeader(s);
if (h == NULL)
{
c->Err = ERR_CLIENT_IS_NOT_VPN;
return false;
}
// Interpret
if (StrCmpi(h->Method, "POST") == 0)
{
// Receive the data since it's POST
data_size = GetContentLength(h);
if ((data_size > MAX_WATERMARK_SIZE || data_size < SizeOfWaterMark()) && (data_size != StrLen(HTTP_VPN_TARGET_POSTDATA)))
{
// Data is too large
HttpSendForbidden(s, h->Target, NULL);
FreeHttpHeader(h);
c->Err = ERR_CLIENT_IS_NOT_VPN;
*error_detail_str = "POST_Recv_TooLong";
return false;
}
data = Malloc(data_size);
if (RecvAll(s, data, data_size, s->SecureMode) == false)
{
// Data reception failure
Free(data);
FreeHttpHeader(h);
c->Err = ERR_DISCONNECTED;
*error_detail_str = "POST_Recv_Failed";
return false;
}
// Check the Target
if ((StrCmpi(h->Target, vpn_http_target) != 0) || not_found_error)
{
// Target is invalid
HttpSendNotFound(s, h->Target);
Free(data);
FreeHttpHeader(h);
*error_detail_str = "POST_Target_Wrong";
}
else
{
// Compare posted data with the WaterMark
if ((data_size == StrLen(HTTP_VPN_TARGET_POSTDATA) && (Cmp(data, HTTP_VPN_TARGET_POSTDATA, data_size) == 0))
|| ((data_size >= SizeOfWaterMark()) && Cmp(data, WaterMark, SizeOfWaterMark()) == 0))
{
// Check the WaterMark
Free(data);
FreeHttpHeader(h);
return true;
}
else
{
// WaterMark is incorrect
HttpSendForbidden(s, h->Target, NULL);
FreeHttpHeader(h);
*error_detail_str = "POST_WaterMark_Error";
}
}
}
else if (StrCmpi(h->Method, "SSTP_DUPLEX_POST") == 0 && (server->DisableSSTPServer == false || s->IsReverseAcceptedSocket
) &&
GetServerCapsBool(server, "b_support_sstp") && GetNoSstp() == false)
{
// SSTP client is connected
c->WasSstp = true;
if (StrCmpi(h->Target, SSTP_URI) == 0)
{
bool sstp_ret;
// Accept the SSTP connection
c->Type = CONNECTION_TYPE_SSTP;
sstp_ret = AcceptSstp(c);
c->Err = ERR_DISCONNECTED;
FreeHttpHeader(h);
if (sstp_ret)
{
*error_detail_str = "";
}
else
{
*error_detail_str = "SSTP_ABORT";
}
return false;
}
else
{
// URI is invalid
HttpSendNotFound(s, h->Target);
*error_detail_str = "SSTP_URL_WRONG";
}
FreeHttpHeader(h);
}
else
{
// This should not be a VPN client, but interpret a bit more
if (StrCmpi(h->Method, "GET") != 0 && StrCmpi(h->Method, "HEAD") != 0
&& StrCmpi(h->Method, "POST") != 0)
{
// Unsupported method calls
HttpSendNotImplemented(s, h->Method, h->Target, h->Version);
*error_detail_str = "HTTP_BAD_METHOD";
}
else
{
if (StrCmpi(h->Target, "/") == 0)
{
// Root directory
SERVER *s = c->Cedar->Server;
*error_detail_str = "HTTP_ROOT";
{
// Other than free version
HttpSendForbidden(c->FirstSock, h->Target, "");
}
}
else
{
bool b = false;
// Show the WebUI if the configuration allow to use the WebUI
if (c->Cedar->Server != NULL && c->Cedar->Server->UseWebUI)
{
WU_WEBPAGE *page;
// Show the WebUI
page = WuGetPage(h->Target, c->Cedar->WebUI);
if (page != NULL)
{
PostHttp(s, page->header, page->data, page->size);
b = true;
WuFreeWebPage(page);
}
}
if (c->FirstSock->RemoteIP.addr[0] == 127)
{
if (StrCmpi(h->Target, HTTP_SAITAMA) == 0)
{
// Saitama (joke)
FreeHttpHeader(h);
h = NewHttpHeader("HTTP/1.1", "202", "OK");
AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE3));
AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
PostHttp(s, h, Saitama, SizeOfSaitama());
b = true;
}
else if (StartWith(h->Target, HTTP_PICTURES))
{
BUF *buf;
// Lots of photos
buf = ReadDump("|Pictures.mht");
if (buf != NULL)
{
FreeHttpHeader(h);
h = NewHttpHeader("HTTP/1.1", "202", "OK");
AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE5));
AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
PostHttp(s, h, buf->Buf, buf->Size);
b = true;
FreeBuf(buf);
}
}
}
if (b == false)
{
// Not Found
HttpSendNotFound(s, h->Target);
*error_detail_str = "HTTP_NOT_FOUND";
}
}
}
FreeHttpHeader(h);
}
}
}
// Upload a signature
bool ClientUploadSignature(SOCK *s)
{
HTTP_HEADER *h;
UINT water_size, rand_size;
UCHAR *water;
char ip_str[128];
// Validate arguments
if (s == NULL)
{
return false;
}
IPToStr(ip_str, sizeof(ip_str), &s->RemoteIP);
h = NewHttpHeader("POST", HTTP_VPN_TARGET2, "HTTP/1.1");
AddHttpValue(h, NewHttpValue("Host", ip_str));
AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE3));
AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
// Generate a watermark
rand_size = Rand32() % (HTTP_PACK_RAND_SIZE_MAX * 2);
water_size = SizeOfWaterMark() + rand_size;
water = Malloc(water_size);
Copy(water, WaterMark, SizeOfWaterMark());
Rand(&water[SizeOfWaterMark()], rand_size);
// Upload the watermark data
if (PostHttp(s, h, water, water_size) == false)
{
Free(water);
FreeHttpHeader(h);
return false;
}
Free(water);
FreeHttpHeader(h);
return true;
}
// Establish a connection to the server
SOCK *ClientConnectToServer(CONNECTION *c)
{
SOCK *s = NULL;
X *x = NULL;
K *k = NULL;
// Validate arguments
if (c == NULL)
{
return NULL;
}
if (c->Halt)
{
c->Err = ERR_USER_CANCEL;
return NULL;
}
// Get the socket by connecting
s = ClientConnectGetSocket(c, false);
if (s == NULL)
{
// Connection failure
return NULL;
}
c->FirstSock = s;
if (c->Halt)
{
c->Err = ERR_USER_CANCEL;
ReleaseSock(s);
c->FirstSock = NULL;
return NULL;
}
// Time-out
SetTimeout(s, CONNECTING_TIMEOUT);
// Start the SSL communication
if (StartSSLEx(s, x, k, 0, c->ServerName) == false)
{
// SSL communication start failure
Disconnect(s);
ReleaseSock(s);
c->FirstSock = NULL;
c->Err = ERR_SERVER_IS_NOT_VPN;
return NULL;
}
if (s->RemoteX == NULL)
{
// SSL communication start failure
Disconnect(s);
ReleaseSock(s);
c->FirstSock = NULL;
c->Err = ERR_SERVER_IS_NOT_VPN;
return NULL;
}
return s;
}
// Return a socket by connecting to the server
SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
{
SOCK *s = NULL;
CLIENT_OPTION *o;
WPC_CONNECT w;
wchar_t tmp[MAX_SIZE];
SESSION *sess;
volatile bool *cancel_flag = NULL;
void *hWnd;
UINT nat_t_err = 0;
bool is_additional_rudp_session = false;
UCHAR uc = 0;
IP ret_ip;
// Validate arguments
if (c == NULL)
{
return NULL;
}
Zero(&ret_ip, sizeof(IP));
Zero(&w, sizeof(w));
sess = c->Session;
if (sess != NULL)
{
cancel_flag = &sess->CancelConnect;
is_additional_rudp_session = sess->IsRUDPSession;
}
hWnd = c->hWndForUI;
o = c->Session->ClientOption;
if (additional_connect)
{
if (sess != NULL)
{
Copy(&ret_ip, &sess->ServerIP_CacheForNextConnect, sizeof(IP));
}
}
if (c->RestoreServerNameAndPort && additional_connect)
{
// Restore to the original server name and port number
c->RestoreServerNameAndPort = false;
if (StrCmpi(c->ServerName, o->Hostname) != 0)
{
StrCpy(c->ServerName, sizeof(c->ServerName), o->Hostname);
Zero(&ret_ip, sizeof(IP));
}
c->ServerPort = o->Port;
}
StrCpy(w.HostName, sizeof(w.HostName), c->ServerName);
w.Port = c->ServerPort;
StrCpy(w.ProxyHostName, sizeof(w.ProxyHostName), o->ProxyName);
w.ProxyPort = o->ProxyPort;
StrCpy(w.ProxyUsername, sizeof(w.ProxyUsername), o->ProxyUsername);
StrCpy(w.ProxyPassword, sizeof(w.ProxyPassword), o->ProxyPassword);
switch (o->ProxyType)
{
case PROXY_DIRECT: // TCP/IP
UniFormat(tmp, sizeof(tmp), _UU("STATUS_4"), w.HostName);
PrintStatus(sess, tmp);
// Production job
if (o->PortUDP == 0)
{
{
// If additional_connect == false, enable trying to NAT-T connection
// If additional_connect == true, follow the IsRUDPSession setting in this session
s = TcpIpConnectEx(w.HostName, w.Port,
(bool *)cancel_flag, hWnd, &nat_t_err, (additional_connect ? (!is_additional_rudp_session) : false),
true, &ret_ip);
}
}
else
{
// Mode to connect with R-UDP directly without using NAT-T server when using UDP
IP ip;
Zero(&ip, sizeof(ip));
StrToIP(&ip, o->Hostname);
s = NewRUDPClientDirect(VPN_RUDP_SVC_NAME, &ip, o->PortUDP, &nat_t_err,
TIMEOUT_TCP_PORT_CHECK, (bool *)cancel_flag, NULL, NULL, 0, false);
if (s != NULL)
{
StrCpy(s->UnderlayProtocol, sizeof(s->UnderlayProtocol), SOCK_UNDERLAY_NAT_T);
}
}
if (s == NULL)
{
// Connection failure
if (nat_t_err != RUDP_ERROR_NAT_T_TWO_OR_MORE)
{
c->Err = ERR_CONNECT_FAILED;
}
else
{
c->Err = ERR_NAT_T_TWO_OR_MORE;
}
return NULL;
}
break;
case PROXY_HTTP: // HTTP Proxy
UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), w.HostName, w.ProxyHostName);
PrintStatus(sess, tmp);
// Proxy connection
s = ProxyConnectEx(c, w.ProxyHostName, w.ProxyPort,
w.HostName, w.Port, w.ProxyUsername, w.ProxyPassword,
additional_connect, (bool *)cancel_flag, hWnd);
if (s == NULL)
{
// Connection failure
return NULL;
}
break;
case PROXY_SOCKS: // SOCKS4 Proxy
UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), w.HostName, w.ProxyHostName);
PrintStatus(sess, tmp);
// SOCKS4 connection
s = SocksConnectEx2(c, w.ProxyHostName, w.ProxyPort,
w.HostName, w.Port, w.ProxyUsername, additional_connect, (bool *)cancel_flag,
hWnd, 0, &ret_ip);
if (s == NULL)
{
// Connection failure
return NULL;
}
break;
case PROXY_SOCKS5: // SOCKS5 Proxy
UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), w.HostName, w.ProxyHostName);
PrintStatus(sess, tmp);
// SOCKS5 connection
s = Socks5Connect(c, &w, additional_connect, (bool *)cancel_flag, hWnd, 0, &ret_ip);
if (s == NULL)
{
// Connection failure
return NULL;
}
break;
}
if (s == NULL)
{
// Connection failure
c->Err = ERR_CONNECT_FAILED;
}
else
{
// Success to connect
// Keep a note of the IP address
if (additional_connect == false || IsZeroIP(&s->RemoteIP))
{
char *hostname = o->ProxyType == PROXY_DIRECT ? w.HostName : w.ProxyHostName;
if (((s->IsRUDPSocket || s->IPv6) && IsZeroIP(&s->RemoteIP) == false && o->ProxyType == PROXY_DIRECT) || GetIP(&c->Session->ServerIP, hostname) == false)
{
Copy(&c->Session->ServerIP, &s->RemoteIP, sizeof(IP));
}
}
if (IsZeroIP(&ret_ip) == false)
{
if (c->Session != NULL)
{
if (additional_connect == false)
{
Copy(&c->Session->ServerIP_CacheForNextConnect, &ret_ip, sizeof(IP));
Debug("Saved ServerIP_CacheForNextConnect: %s = %r\n", c->ServerName, &ret_ip);
}
}
}
}
return s;
}
// Connect via SOCKS4
SOCK *SocksConnect(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
char *server_host_name, UINT server_port,
char *username, bool additional_connect)
{
return SocksConnectEx(c, proxy_host_name, proxy_port,
server_host_name, server_port, username, additional_connect, NULL, NULL);
}
SOCK *SocksConnectEx(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
char *server_host_name, UINT server_port,
char *username, bool additional_connect,
bool *cancel_flag, void *hWnd)
{
return SocksConnectEx2(c, proxy_host_name, proxy_port,
server_host_name, server_port, username, additional_connect, cancel_flag,
hWnd, 0, NULL);
}
SOCK *SocksConnectEx2(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
char *server_host_name, UINT server_port,
char *username, bool additional_connect,
bool *cancel_flag, void *hWnd, UINT timeout, IP *ret_ip)
{
SOCK *s = NULL;
IP ip;
// Validate arguments
if (c == NULL || proxy_host_name == NULL || proxy_port == 0 || server_host_name == NULL
|| server_port == 0)
{
if (c != NULL)
{
c->Err = ERR_PROXY_CONNECT_FAILED;
}
return NULL;
}
// Get the IP address of the destination server
if (GetIP(&ip, server_host_name) == false)
{
// Failure
c->Err = ERR_CONNECT_FAILED;
return NULL;
}
if (c->Halt)
{
// Stop
c->Err = ERR_USER_CANCEL;
return NULL;
}
// Connection
s = TcpConnectEx3(proxy_host_name, proxy_port, timeout, cancel_flag, hWnd, true, NULL, false, ret_ip);
if (s == NULL)
{
// Failure
c->Err = ERR_PROXY_CONNECT_FAILED;
return NULL;
}
// Timeout setting
SetTimeout(s, MIN(CONNECTING_TIMEOUT_PROXY, (timeout == 0 ? INFINITE : timeout)));
if (additional_connect == false)
{
c->FirstSock = s;
}
// Request packet transmission
if (SocksSendRequestPacket(c, s, server_port, &ip, username) == false)
{
// Failure
if (additional_connect == false)
{
c->FirstSock = NULL;
}
Disconnect(s);
ReleaseSock(s);
return NULL;
}
// Receive a response packet
if (SocksRecvResponsePacket(c, s) == false)
{
// Failure
if (additional_connect == false)
{
c->FirstSock = NULL;
}
Disconnect(s);
ReleaseSock(s);
return NULL;
}
SetTimeout(s, INFINITE);
return s;
}
// Receive a SOCKS4 response packet
bool SocksRecvResponsePacket(CONNECTION *c, SOCK *s)
{
BUF *b;
UINT size = 8;
UCHAR tmp[8];
UCHAR vn, cd;
// Validate arguments
if (c == NULL || s == NULL)
{
return false;
}
if (RecvAll(s, tmp, sizeof(tmp), false) == false)
{
c->Err = ERR_DISCONNECTED;
return false;
}
b = NewBuf();
WriteBuf(b, tmp, sizeof(tmp));
SeekBuf(b, 0, 0);
ReadBuf(b, &vn, 1);
ReadBuf(b, &cd, 1);
FreeBuf(b);
if (vn != 0)
{
c->Err = ERR_PROXY_ERROR;
return false;
}
switch (cd)
{
case 90:
// Success
return true;
case 93:
// Authentication failure
c->Err = ERR_PROXY_AUTH_FAILED;
return false;
default:
// Connection to the server failure
c->Err = ERR_CONNECT_FAILED;
return false;
}
}
// Send a SOCKS4 request packet
bool SocksSendRequestPacket(CONNECTION *c, SOCK *s, UINT dest_port, IP *dest_ip, char *userid)
{
BUF *b;
UCHAR vn, cd;
USHORT port;
UINT ip;
bool ret;
// Validate arguments
if (s == NULL || dest_port == 0 || dest_ip == NULL || c == NULL)
{
return false;
}
if (userid == NULL)
{
userid = "";
}
b = NewBuf();
vn = 4;
cd = 1;
WriteBuf(b, &vn, 1);
WriteBuf(b, &cd, 1);
port = Endian16((USHORT)dest_port);
ip = IPToUINT(dest_ip);
WriteBuf(b, &port, 2);
WriteBuf(b, &ip, 4);
WriteBuf(b, userid, StrLen(userid) + 1);
ret = SendAll(s, b->Buf, b->Size, false);
if (ret == false)
{
c->Err = ERR_DISCONNECTED;
}
FreeBuf(b);
return ret;
}
// Connect via SOCKS5 (RFC1928)
SOCK *Socks5Connect(CONNECTION *c, WPC_CONNECT *w, bool additional_connect, bool *cancel_flag, void *hWnd, UINT timeout, IP *ret_ip)
{
UCHAR tmp, recv_buf[2], *recv_buf_final;
USHORT port;
bool ret;
SOCK *s;
BUF *b;
IP ip;
// Validate arguments
if (c == NULL || w == NULL || w->Port == 0 || w->ProxyPort == 0 || IsEmptyStr(w->HostName) || IsEmptyStr(w->ProxyHostName))
{
if (c != NULL)
{
c->Err = ERR_PROXY_CONNECT_FAILED;
}
return NULL;
}
if (c->Halt)
{
// Stop
c->Err = ERR_USER_CANCEL;
return NULL;
}
// Open TCP connection to the proxy server
s = TcpConnectEx3(w->ProxyHostName, w->ProxyPort, timeout, cancel_flag, hWnd, true, NULL, false, ret_ip);
if (s == NULL)
{
// Failure
c->Err = ERR_PROXY_CONNECT_FAILED;
return NULL;
}
// Set the timeout setting
SetTimeout(s, MIN(CONNECTING_TIMEOUT_PROXY, (timeout == 0 ? INFINITE : timeout)));
if (additional_connect == false)
{
c->FirstSock = s;
}
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
//
// X'00' NO AUTHENTICATION REQUIRED
// X'01' GSSAPI
// X'02' USERNAME/PASSWORD
// X'03' to X'7F' IANA ASSIGNED
// X'80' to X'FE' RESERVED FOR PRIVATE METHODS
// X'FF' NO ACCEPTABLE METHOD
b = NewBuf();
tmp = 5;
WriteBuf(b, &tmp, sizeof(tmp)); // SOCKS version
tmp = 2;
WriteBuf(b, &tmp, sizeof(tmp)); // Number of supported methods
tmp = 0;
WriteBuf(b, &tmp, sizeof(tmp)); // No authentication
tmp = 2;
WriteBuf(b, &tmp, sizeof(tmp)); // Username/password
ret = SendAll(s, b->Buf, b->Size, false);
FreeBuf(b);
if (ret == false)
{
Debug("Socks5Connect(): [Phase 1] Failed to send initial data to the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false)
{
Debug("Socks5Connect(): [Phase 1] Failed to receive initial data response from the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
if (recv_buf[0] != 5)
{
Debug("Socks5Connect(): [Phase 1] Unmatching version: %u.\n", recv_buf[0]);
c->Err = ERR_PROXY_ERROR;
goto failure;
}
// Username/password authentication (RFC1929)
if (recv_buf[1] == 2)
{
// +----+------+----------+------+----------+
// |VER | ULEN | UNAME | PLEN | PASSWD |
// +----+------+----------+------+----------+
// | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
// +----+------+----------+------+----------+
b = NewBuf();
tmp = 1;
WriteBuf(b, &tmp, sizeof(tmp)); // Authentication protocol version
tmp = StrLen(w->ProxyUsername);
WriteBuf(b, &tmp, sizeof(tmp)); // Username length
WriteBuf(b, w->ProxyUsername, tmp); // Username
tmp = StrLen(w->ProxyPassword);
WriteBuf(b, &tmp, sizeof(tmp)); // Password length
WriteBuf(b, w->ProxyPassword, tmp); // Password
ret = SendAll(s, b->Buf, b->Size, false);
FreeBuf(b);
if (ret == false)
{
Debug("Socks5Connect(): [Phase 1] Failed to send authentication data to the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
// +----+--------+
// |VER | STATUS |
// +----+--------+
// | 1 | 1 |
// +----+--------+
if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false)
{
Debug("Socks5Connect(): [Phase 1] Failed to receive authentication data response from the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
if (recv_buf[1] != 0)
{
Debug("Socks5Connect(): [Phase 1] Authentication failure error code sent by the server: %u.\n", recv_buf[1]);
c->Err = ERR_PROXY_AUTH_FAILED;
goto failure;
}
}
// +----+-----+-------+------+----------+----------+
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
//
// VER protocol version: X'05'
// CMD
// CONNECT X'01'
// BIND X'02'
// UDP ASSOCIATE X'03'
// RSV RESERVED
// ATYP address type of following address
// IP V4 address X'01'
// DOMAINNAME X'03'
// IP V6 address X'04'
// DST.ADDR desired destination address
// DST.PORT desired destination port in network octet order
// Prepare data to send
b = NewBuf();
tmp = 5;
WriteBuf(b, &tmp, sizeof(tmp)); // SOCKS version
tmp = 1;
WriteBuf(b, &tmp, sizeof(tmp)); // Command
tmp = 0;
WriteBuf(b, &tmp, sizeof(tmp)); // Reserved byte
// Convert the hostname to an IP structure (if it's an IP address)
StrToIP(&ip, w->HostName);
// If the IP structure doesn't contain an IP address, it means that the string is an hostname
if (IsZeroIp(&ip))
{
UCHAR dest_length = StrLen(w->HostName);
tmp = 3;
WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (hostname)
WriteBuf(b, &dest_length, sizeof(dest_length)); // Destination hostname length
WriteBuf(b, w->HostName, dest_length); // Destination hostname
}
else
{
if (IsIP6(&ip))
{
tmp = 4;
WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (IPv6)
WriteBuf(b, ip.ipv6_addr, sizeof(ip.ipv6_addr)); // Destination IPv6 address
}
else
{
tmp = 1;
WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (IPv4)
WriteBuf(b, ip.addr, sizeof(ip.addr)); // Destination IPv4 address
}
}
// Convert the port in network octet order
port = Endian16((USHORT)w->Port);
WriteBuf(b, &port, sizeof(port)); // Destination port
// Send data
ret = SendAll(s, b->Buf, b->Size, false);
FreeBuf(b);
if (ret == false)
{
Debug("Socks5Connect(): [Phase 2] Failed to send data to the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
// +----+-----+-------+------+----------+----------+
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X00 | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
//
// VER protocol version: X05
// REP Reply field:
// X00 succeeded
// X01 general SOCKS server failure
// X02 connection not allowed by ruleset
// X03 Network unreachable
// X04 Host unreachable
// X05 Connection refused
// X06 TTL expired
// X07 Command not supported
// X08 Address type not supported
// X09 to XFF unassigned
// The packet sent by the server should always have the same size as the one we sent to it.
// However, there are some implementations which send fixed values (aside from the first 2 bytes).
// In order to support such implementations, we read the first 4 bytes in order to know the address type before trying to read the rest of the packet.
recv_buf_final = Malloc(4);
if (RecvAll(s, recv_buf_final, 4, false) == false)
{
Free(recv_buf_final);
Debug("Socks5Connect(): [Phase 2] Failed to receive response from the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
// We only need the first two bytes (version and response code), but we have to read the entire packet from the socket
recv_buf[0] = recv_buf_final[0];
recv_buf[1] = recv_buf_final[1];
// We receive the rest of the packet by knowing the size according to the address type
switch (recv_buf_final[3])
{
case 1:
// IPv4
recv_buf_final = ReAlloc(recv_buf_final, 6); // 4 bytes (IPv4) + 2 bytes (port)
ret = RecvAll(s, recv_buf_final, 6, false);
break;
case 4:
// IPv6
recv_buf_final = ReAlloc(recv_buf_final, 18); // 4 bytes (IPv4) + 2 bytes (port)
ret = RecvAll(s, recv_buf_final, 18, false);
break;
case 3:
// Hostname
ret = RecvAll(s, &tmp, 1, false);
if (ret == true)
{
recv_buf_final = ReAlloc(recv_buf_final, tmp + 2); // Hostname length + 2 bytes (port)
ret = RecvAll(s, recv_buf_final, tmp + 2, false);
}
}
Free(recv_buf_final);
if (ret == false)
{
Debug("Socks5Connect(): [Phase 2] Malformed response received from the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
if (recv_buf[0] != 5)
{
Debug("Socks5Connect(): [Phase 2] Unmatching version: %u.\n", recv_buf_final[0]);
c->Err = ERR_PROXY_ERROR;
goto failure;
}
if (recv_buf[1] == 0)
{
// Success
SetTimeout(s, INFINITE);
return s;
}
else
{
Debug("Socks5Connect(): [Phase 2] Connection failed with error: %u\n", recv_buf[1]);
c->Err = ERR_PROXY_ERROR;
}
failure:
if (additional_connect == false)
{
c->FirstSock = NULL;
}
Disconnect(s);
ReleaseSock(s);
return NULL;
}
// Connect through a proxy
SOCK *ProxyConnect(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
char *server_host_name, UINT server_port,
char *username, char *password, bool additional_connect)
{
return ProxyConnectEx(c, proxy_host_name, proxy_port,
server_host_name, server_port, username, password, additional_connect, NULL, NULL);
}
SOCK *ProxyConnectEx(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
char *server_host_name, UINT server_port,
char *username, char *password, bool additional_connect,
bool *cancel_flag, void *hWnd)
{
return ProxyConnectEx2(c, proxy_host_name, proxy_port,
server_host_name, server_port, username, password, additional_connect,
cancel_flag, hWnd, 0);
}
SOCK *ProxyConnectEx2(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
char *server_host_name, UINT server_port,
char *username, char *password, bool additional_connect,
bool *cancel_flag, void *hWnd, UINT timeout)
{
SOCK *s = NULL;
bool use_auth = false;
char tmp[MAX_SIZE];
char auth_tmp_str[MAX_SIZE], auth_b64_str[MAX_SIZE * 2];
char basic_str[MAX_SIZE * 2];
UINT http_error_code;
HTTP_HEADER *h;
char server_host_name_tmp[256];
UINT i, len;
// Validate arguments
if (c == NULL || proxy_host_name == NULL || proxy_port == 0 || server_host_name == NULL ||
server_port == 0)
{
if( c != NULL)
{
c->Err = ERR_PROXY_CONNECT_FAILED;
}
return NULL;
}
if (username != NULL && password != NULL &&
(StrLen(username) != 0 || StrLen(password) != 0))
{
use_auth = true;
}
if (c->Halt)
{
// Stop
c->Err = ERR_USER_CANCEL;
return NULL;
}
Zero(server_host_name_tmp, sizeof(server_host_name_tmp));
StrCpy(server_host_name_tmp, sizeof(server_host_name_tmp), server_host_name);
len = StrLen(server_host_name_tmp);
for (i = 0;i < len;i++)
{
if (server_host_name_tmp[i] == '/')
{
server_host_name_tmp[i] = 0;
}
}
// Connection
s = TcpConnectEx3(proxy_host_name, proxy_port, timeout, cancel_flag, hWnd, true, NULL, false, NULL);
if (s == NULL)
{
// Failure
c->Err = ERR_PROXY_CONNECT_FAILED;
return NULL;
}
// Timeout setting
SetTimeout(s, MIN(CONNECTING_TIMEOUT_PROXY, (timeout == 0 ? INFINITE : timeout)));
if (additional_connect == false)
{
c->FirstSock = s;
}
// HTTP header generation
if (IsStrIPv6Address(server_host_name_tmp))
{
IP ip;
char iptmp[MAX_PATH];
StrToIP(&ip, server_host_name_tmp);
IPToStr(iptmp, sizeof(iptmp), &ip);
Format(tmp, sizeof(tmp), "[%s]:%u", iptmp, server_port);
}
else
{
Format(tmp, sizeof(tmp), "%s:%u", server_host_name_tmp, server_port);
}
h = NewHttpHeader("CONNECT", tmp, "HTTP/1.0");
AddHttpValue(h, NewHttpValue("User-Agent", (c->Cedar == NULL ? DEFAULT_USER_AGENT : c->Cedar->HttpUserAgent)));
AddHttpValue(h, NewHttpValue("Host", server_host_name_tmp));
AddHttpValue(h, NewHttpValue("Content-Length", "0"));
AddHttpValue(h, NewHttpValue("Proxy-Connection", "Keep-Alive"));
AddHttpValue(h, NewHttpValue("Pragma", "no-cache"));
if (use_auth)
{
wchar_t tmp[MAX_SIZE];
UniFormat(tmp, sizeof(tmp), _UU("STATUS_3"), server_host_name_tmp);
// Generate the authentication string
Format(auth_tmp_str, sizeof(auth_tmp_str), "%s:%s",
username, password);
// Base64 encode
Zero(auth_b64_str, sizeof(auth_b64_str));
Encode64(auth_b64_str, auth_tmp_str);
Format(basic_str, sizeof(basic_str), "Basic %s", auth_b64_str);
AddHttpValue(h, NewHttpValue("Proxy-Authorization", basic_str));
}
// Transmission
if (SendHttpHeader(s, h) == false)
{
// Failure
if (additional_connect == false)
{
c->FirstSock = NULL;
}
FreeHttpHeader(h);
Disconnect(s);
ReleaseSock(s);
c->Err = ERR_PROXY_ERROR;
return NULL;
}
FreeHttpHeader(h);
if (c->Halt)
{
// Stop
if (additional_connect == false)
{
c->FirstSock = NULL;
}
Disconnect(s);
ReleaseSock(s);
c->Err = ERR_USER_CANCEL;
return NULL;
}
// Receive the results
h = RecvHttpHeader(s);
if (h == NULL)
{
// Failure
if (additional_connect == false)
{
c->FirstSock = NULL;
}
FreeHttpHeader(h);
Disconnect(s);
ReleaseSock(s);
c->Err = ERR_PROXY_ERROR;
return NULL;
}
http_error_code = 0;
if (StrLen(h->Method) == 8)
{
if (Cmp(h->Method, "HTTP/1.", 7) == 0)
{
http_error_code = ToInt(h->Target);
}
}
FreeHttpHeader(h);
// Check the code
switch (http_error_code)
{
case 401:
case 403:
case 407:
// Authentication failure
if (additional_connect == false)
{
c->FirstSock = NULL;
}
Disconnect(s);
ReleaseSock(s);
c->Err = ERR_PROXY_AUTH_FAILED;
return NULL;
default:
if ((http_error_code / 100) == 2)
{
// Success
SetTimeout(s, INFINITE);
return s;
}
else
{
// Receive an unknown result
if (additional_connect == false)
{
c->FirstSock = NULL;
}
Disconnect(s);
ReleaseSock(s);
c->Err = ERR_PROXY_ERROR;
return NULL;
}
}
}
// TCP connection function
SOCK *TcpConnectEx2(char *hostname, UINT port, UINT timeout, bool *cancel_flag, void *hWnd, bool try_start_ssl)
{
return TcpConnectEx3(hostname, port, timeout, cancel_flag, hWnd, false, NULL, try_start_ssl, NULL);
}
SOCK *TcpConnectEx3(char *hostname, UINT port, UINT timeout, bool *cancel_flag, void *hWnd, bool no_nat_t, UINT *nat_t_error_code, bool try_start_ssl, IP *ret_ip)
{
#ifdef OS_WIN32
if (hWnd == NULL)
{
#endif // OS_WIN32
return ConnectEx4(hostname, port, timeout, cancel_flag, (no_nat_t ? NULL : VPN_RUDP_SVC_NAME), nat_t_error_code, try_start_ssl, true, ret_ip);
#ifdef OS_WIN32
}
else
{
return WinConnectEx3((HWND)hWnd, hostname, port, timeout, 0, NULL, NULL, nat_t_error_code, (no_nat_t ? NULL : VPN_RUDP_SVC_NAME), try_start_ssl);
}
#endif // OS_WIN32
}
// Connect with TCP/IP
SOCK *TcpIpConnect(char *hostname, UINT port, bool try_start_ssl)
{
return TcpIpConnectEx(hostname, port, NULL, NULL, NULL, false, try_start_ssl, NULL);
}
SOCK *TcpIpConnectEx(char *hostname, UINT port, bool *cancel_flag, void *hWnd, UINT *nat_t_error_code, bool no_nat_t, bool try_start_ssl, IP *ret_ip)
{
SOCK *s = NULL;
UINT dummy_int = 0;
// Validate arguments
if (nat_t_error_code == NULL)
{
nat_t_error_code = &dummy_int;
}
*nat_t_error_code = 0;
if (hostname == NULL || port == 0)
{
return NULL;
}
s = TcpConnectEx3(hostname, port, 0, cancel_flag, hWnd, no_nat_t, nat_t_error_code, try_start_ssl, ret_ip);
if (s == NULL)
{
return NULL;
}
return s;
}
// Protocol routine initialization
void InitProtocol()
{
}
// Release the protocol routine
void FreeProtocol()
{
}
// Create a Hello packet
PACK *PackHello(void *random, UINT ver, UINT build, char *server_str)
{
PACK *p;
// Validate arguments
if (random == NULL || server_str == NULL)
{
return NULL;
}
p = NewPack();
PackAddStr(p, "hello", server_str);
PackAddInt(p, "version", ver);
PackAddInt(p, "build", build);
PackAddData(p, "random", random, SHA1_SIZE);
return p;
}
// Interpret the Hello packet
bool GetHello(PACK *p, void *random, UINT *ver, UINT *build, char *server_str, UINT server_str_size)
{
// Validate arguments
if (p == NULL || random == NULL || ver == NULL || server_str == NULL)
{
return false;
}
if (PackGetStr(p, "hello", server_str, server_str_size) == false)
{
return false;
}
*ver = PackGetInt(p, "version");
*build = PackGetInt(p, "build");
if (PackGetDataSize(p, "random") != SHA1_SIZE)
{
return false;
}
if (PackGetData(p, "random", random) == false)
{
return false;
}
return true;
}
// Get the authentication method from PACK
UINT GetAuthTypeFromPack(PACK *p)
{
// Validate arguments
if (p == NULL)
{
return 0;
}
return PackGetInt(p, "authtype");
}
// Get the HUB name and the user name from the PACK
bool GetHubnameAndUsernameFromPack(PACK *p, char *username, UINT username_size,
char *hubname, UINT hubname_size)
{
// Validate arguments
if (p == NULL || username == NULL || hubname == NULL)
{
return false;
}
if (PackGetStr(p, "username", username, username_size) == false)
{
return false;
}
if (PackGetStr(p, "hubname", hubname, hubname_size) == false)
{
return false;
}
return true;
}
// Get the protocol from PACK
UINT GetProtocolFromPack(PACK *p)
{
// Validate arguments
if (p == NULL)
{
return 0;
}
#if 0
return PackGetInt(p, "protocol");
#else
// Limit to the TCP protocol in the current version
return CONNECTION_TCP;
#endif
}
// Get the method from the PACK
bool GetMethodFromPack(PACK *p, char *method, UINT size)
{
// Validate arguments
if (p == NULL || method == NULL || size == 0)
{
return false;
}
return PackGetStr(p, "method", method, size);
}
// Generate a packet of certificate authentication login
PACK *PackLoginWithCert(char *hubname, char *username, X *x, void *sign, UINT sign_size)
{
PACK *p;
BUF *b;
// Validate arguments
if (hubname == NULL || username == NULL)
{
return NULL;
}
p = NewPack();
PackAddStr(p, "method", "login");
PackAddStr(p, "hubname", hubname);
PackAddStr(p, "username", username);
PackAddInt(p, "authtype", CLIENT_AUTHTYPE_CERT);
// Certificate
b = XToBuf(x, false);
PackAddData(p, "cert", b->Buf, b->Size);
FreeBuf(b);
// Signature data
PackAddData(p, "sign", sign, sign_size);
return p;
}
// Generate a packet of plain text password authentication login
PACK *PackLoginWithPlainPassword(char *hubname, char *username, void *plain_password)
{
PACK *p;
// Validate arguments
if (hubname == NULL || username == NULL)
{
return NULL;
}
p = NewPack();
PackAddStr(p, "method", "login");
PackAddStr(p, "hubname", hubname);
PackAddStr(p, "username", username);
PackAddInt(p, "authtype", CLIENT_AUTHTYPE_PLAIN_PASSWORD);
PackAddStr(p, "plain_password", plain_password);
return p;
}
// Generate a packet of OpenVPN certificate login
PACK *PackLoginWithOpenVPNCertificate(char *hubname, char *username, X *x)
{
PACK *p;
char cn_username[128];
BUF *cert_buf = NULL;
// Validate arguments
if (hubname == NULL || username == NULL || x == NULL)
{
return NULL;
}
p = NewPack();
PackAddStr(p, "method", "login");
PackAddStr(p, "hubname", hubname);
if (IsEmptyStr(username))
{
if (x->subject_name == NULL)
{
return NULL;
}
UniToStr(cn_username, sizeof(cn_username), x->subject_name->CommonName);
PackAddStr(p, "username", cn_username);
}
else
{
PackAddStr(p, "username", username);
}
PackAddInt(p, "authtype", AUTHTYPE_OPENVPN_CERT);
cert_buf = XToBuf(x, false);
PackAddBuf(p, "cert", cert_buf);
FreeBuf(cert_buf);
return p;
}
// Create a packet of password authentication login
PACK *PackLoginWithPassword(char *hubname, char *username, void *secure_password)
{
PACK *p;
// Validate arguments
if (hubname == NULL || username == NULL)
{
return NULL;
}
p = NewPack();
PackAddStr(p, "method", "login");
PackAddStr(p, "hubname", hubname);
PackAddStr(p, "username", username);
PackAddInt(p, "authtype", CLIENT_AUTHTYPE_PASSWORD);
PackAddData(p, "secure_password", secure_password, SHA1_SIZE);
return p;
}
// Create a packet for anonymous login
PACK *PackLoginWithAnonymous(char *hubname, char *username)
{
PACK *p;
// Validate arguments
if (hubname == NULL || username == NULL)
{
return NULL;
}
p = NewPack();
PackAddStr(p, "method", "login");
PackAddStr(p, "hubname", hubname);
PackAddStr(p, "username", username);
PackAddInt(p, "authtype", CLIENT_AUTHTYPE_ANONYMOUS);
return p;
}
// Create a packet for the additional connection
PACK *PackAdditionalConnect(UCHAR *session_key)
{
PACK *p;
// Validate arguments
if (session_key == NULL)
{
return NULL;
}
p = NewPack();
PackAddStr(p, "method", "additional_connect");
PackAddData(p, "session_key", session_key, SHA1_SIZE);
return p;
}