1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-23 01:49:53 +03:00
SoftEtherVPN/src/Cedar/Protocol.c
Davide Beatrici 0472f9c286 Rewrite DNS API from scratch into dedicated file(s)
From a functional point of view, the main improvement is that GetIP() now always prioritizes IPv6 over IPv4.
The previous implementation always returned an IPv4 address, unless not available: in such case it failed.
This means that now connections to hostnames should be established via IPv6 if available.

From a programmer point of view, getting rid of the insane wrappers is enough to justify a complete rewrite.

As an extra, several unrelated unused global variables are removed.
2021-04-18 01:46:59 +02:00

6771 lines
163 KiB
C

// SoftEther VPN Source Code - Developer Edition Master Branch
// Cedar Communication Module
// © 2020 Nokia
// Protocol.c
// SoftEther protocol related routines
#include "Protocol.h"
#include "Admin.h"
#include "Client.h"
#include "CM.h"
#include "DDNS.h"
#include "Hub.h"
#include "IPC.h"
#include "Link.h"
#include "Logging.h"
#include "Proto_IPsec.h"
#include "Proto_OpenVPN.h"
#include "Proto_PPP.h"
#include "Proto_SSTP.h"
#include "Radius.h"
#include "Sam.h"
#include "Server.h"
#include "UdpAccel.h"
#include "VLanUnix.h"
#include "WaterMark.h"
#include "WebUI.h"
#include "WinUi.h"
#include "Wpc.h"
#include "Mayaqua/Cfg.h"
#include "Mayaqua/DNS.h"
#include "Mayaqua/FileIO.h"
#include "Mayaqua/Internat.h"
#include "Mayaqua/Memory.h"
#include "Mayaqua/Microsoft.h"
#include "Mayaqua/Object.h"
#include "Mayaqua/OS.h"
#include "Mayaqua/Pack.h"
#include "Mayaqua/Secure.h"
#include "Mayaqua/Str.h"
#include "Mayaqua/Table.h"
#include "Mayaqua/Tick64.h"
// 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];
GetDbDirW(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;
}
GetDbDirW(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)
{
// Validate arguments
if (c == NULL || s == NULL)
{
return;
}
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);
}
// 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;
// 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);
}
}
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;
UCHAR assigned_ipc_mac_address[6];
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;
UINT client_udp_acceleration_max_version = 1;
UINT udp_acceleration_version = 1;
UINT client_rudp_bulk_max_version = 1;
UINT rudp_bulk_version = 1;
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_V1];
UCHAR udp_acceleration_client_key_v2[UDP_ACCELERATION_COMMON_KEY_SIZE_V2];
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(assigned_ipc_mac_address, sizeof(assigned_ipc_mac_address));
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(udp_acceleration_client_key_v2, sizeof(udp_acceleration_client_key_v2));
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 (c->Type == CONNECTION_TYPE_ADMIN_RPC)
{
c->Err = ERR_NO_ERROR;
}
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;
}
// Get authentication method and initiate login process
authtype = GetAuthTypeFromPack(p);
if (authtype == AUTHTYPE_WIREGUARD_KEY)
{
WGK *wgk, tmp;
bool ok = false;
if (PackGetStr(p, "key", tmp.Key, sizeof(tmp.Key)) == false)
{
FreePack(p);
c->Err = ERR_PROTOCOL_ERROR;
error_detail = "GetWireGuardKeyFromPack";
goto CLEANUP;
}
LockList(c->Cedar->WgkList);
{
wgk = Search(c->Cedar->WgkList, &tmp);
if (wgk != NULL)
{
ok = true;
StrCpy(hubname, sizeof(hubname), wgk->Hub);
StrCpy(username, sizeof(username), wgk->User);
StrCpy(node.HubName, sizeof(node.HubName), hubname);
}
}
UnlockList(c->Cedar->WgkList);
if (ok == false)
{
FreePack(p);
c->Err = ERR_AUTH_FAILED;
SLog(c->Cedar, "LS_WG_KEY_NOT_FOUND", c->Name, hubname);
error_detail = "ERR_AUTH_FAILED";
goto CLEANUP;
}
}
else if (GetHubnameAndUsernameFromPack(p, username, sizeof(username), hubname, sizeof(hubname)) == false)
{
FreePack(p);
c->Err = ERR_PROTOCOL_ERROR;
error_detail = "GetHubnameAndUsernameFromPack";
goto CLEANUP;
}
if (farm_member)
{
bool ok = false;
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");
client_udp_acceleration_max_version = PackGetInt(p, "udp_acceleration_max_version");
if (client_udp_acceleration_max_version == 0)
{
client_udp_acceleration_max_version = 1;
}
client_rudp_bulk_max_version = PackGetInt(p, "rudp_bulk_max_version");
if (client_rudp_bulk_max_version == 0)
{
client_rudp_bulk_max_version = 1;
}
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));
c->InProcLayer = PackGetInt(p, "inproc_layer");
if (c->FirstSock != NULL)
{
if (IsEmptyStr(c->InProcPrefix) == false)
{
Format(c->FirstSock->UnderlayProtocol, sizeof(c->FirstSock->UnderlayProtocol), SOCK_UNDERLAY_INPROC_EX, c->InProcPrefix);
AddProtocolDetailsStr(c->FirstSock->UnderlayProtocol, sizeof(c->FirstSock->UnderlayProtocol), 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;
Format(radius_login_opt.In_VpnProtocolState, sizeof(radius_login_opt.In_VpnProtocolState),
"L%u:%s", c->InProcLayer, c->InProcPrefix);
}
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);
}
Format(radius_login_opt.In_VpnProtocolState, sizeof(radius_login_opt.In_VpnProtocolState),
"L%u:%s", IPC_LAYER_2, "SEVPN");
}
if (support_bulk_on_rudp && c->FirstSock != NULL && c->FirstSock->IsRUDPSocket &&
c->FirstSock->BulkRecvKey != NULL && c->FirstSock->BulkSendKey != NULL)
{
// Allow 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)
{
PackGetData2(p, "udp_acceleration_client_key", udp_acceleration_client_key, UDP_ACCELERATION_COMMON_KEY_SIZE_V1);
PackGetData2(p, "udp_acceleration_client_key_v2", udp_acceleration_client_key_v2, UDP_ACCELERATION_COMMON_KEY_SIZE_V2);
// Get the parameters for the UDP acceleration function
if (PackGetIp(p, "udp_acceleration_client_ip", &udp_acceleration_client_ip) == 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);
}
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_WIREGUARD_KEY:
authtype_str = _UU("LH_AUTH_WIREGUARD_KEY");
break;
case AUTHTYPE_OPENVPN_CERT:
authtype_str = _UU("LH_AUTH_OPENVPN_CERT");
break;
case AUTHTYPE_TICKET:
authtype_str = _UU("LH_AUTH_TICKET");
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)
{
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_WIREGUARD_KEY:
// We already retrieved the hubname and username associated with the key.
// Now we only have to verify that the user effectively exists.
if (c->IsInProc)
{
auth_ret = SamIsUser(hub, username);
}
else
{
// WireGuard public key authentication cannot be used directly by external clients.
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)
{
char ip[64];
IPToStr(ip, sizeof(ip), &c->FirstSock->RemoteIP);
HLog(hub, "LH_AUTH_NG", c->Name, username, ip);
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)
{
const SOCK *s = c->FirstSock;
if (s != NULL && IsLocalHostIP(&s->RemoteIP) == false)
{
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;
}
}
}
HLog(hub, "LH_AUTH_OK", c->Name, username);
}
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;
}
}
}
// Check the assigned MAC Address
if (radius_login_opt.Out_IsRadiusLogin)
{
Copy(assigned_ipc_mac_address, radius_login_opt.Out_VirtualMacAddress, 6);
}
if (StrCmpi(username, ADMINISTRATOR_USERNAME) != 0)
{
// Get the policy
if (farm_member == false)
{
bool is_asterisk_user = 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;
}
is_asterisk_user = true;
}
policy = NULL;
Lock(user->lock);
{
if (is_asterisk_user == false)
{
UCHAR associated_mac_address[6];
// Get the associated virtual MAC address
if (GetUserMacAddressFromUserNote(associated_mac_address, user->Note))
{
if (IsZero(assigned_ipc_mac_address, 6))
{
WHERE;
Copy(assigned_ipc_mac_address, associated_mac_address, 6);
}
}
}
// 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,
(c->IsInProc && IsZero(assigned_ipc_mac_address, 6) == false) ? assigned_ipc_mac_address : NULL);
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("ServerAccept(): Optimized MSS Value for R-UDP: %u\n", s->RUdpMss);
AddProtocolDetailsKeyValueInt(s->ProtocolDetails, sizeof(s->ProtocolDetails), "RUDP_MSS", 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);
AddProtocolDetailsStr(s->ProtocolDetails, sizeof(s->ProtocolDetails), c->FirstSock->ProtocolDetails);
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;
udp_acceleration_version = 1;
if (client_udp_acceleration_max_version >= 2)
{
udp_acceleration_version = 2;
}
}
if (client_rudp_bulk_max_version >= 2)
{
rudp_bulk_version = 2;
}
if (s->EnableBulkOnRUDP)
{
AddProtocolDetailsKeyValueInt(s->ProtocolDetails, sizeof(s->ProtocolDetails), "RUDP_Bulk_Ver", s->BulkOnRUDPVersion);
}
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);
Debug("UdpAccelerationVersion = %u\n", s->UdpAccelerationVersion);
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
{
s->UdpAccel->Version = udp_acceleration_version;
if (UdpAccelInitServer(s->UdpAccel,
s->UdpAccel->Version == 2 ? udp_acceleration_client_key_v2 : udp_acceleration_client_key,
&c->FirstSock->RemoteIP, &udp_acceleration_client_ip, udp_acceleration_client_port) == 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;
AddProtocolDetailsKeyValueInt(s->ProtocolDetails, sizeof(s->ProtocolDetails), "UDPAccel_Ver", s->UdpAccel->Version);
AddProtocolDetailsStr(s->ProtocolDetails, sizeof(s->ProtocolDetails), s->UdpAccel->Version > 1 ? "ChaCha20-Poly1305" : "RC4");
AddProtocolDetailsKeyValueInt(s->ProtocolDetails, sizeof(s->ProtocolDetails), "UDPAccel_MSS", UdpAccelCalcMss(s->UdpAccel));
}
}
s->UseClientLicense = use_client_license;
s->UseBridgeLicense = use_bridge_license;
s->AdjustMss = adjust_mss;
if (s->AdjustMss != 0)
{
Debug("AdjustMSS: %u\n", s->AdjustMss);
AddProtocolDetailsKeyValueInt(s->ProtocolDetails, sizeof(s->ProtocolDetails), "AdjustMSS", 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->FirstSock->ProtocolDetails);
}
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 != NULL && 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)
{
// 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;
if (sock->IsRUDPSocket && sock->BulkRecvKey != NULL && sock->BulkSendKey != NULL)
{
if (s->BulkRecvKeySize != 0 && s->BulkSendKeySize != 0)
{
// Restore R-UDP bulk send/recv keys for additional connections
Copy(sock->BulkRecvKey->Data, s->BulkRecvKey, s->BulkRecvKeySize);
sock->BulkRecvKey->Size = s->BulkRecvKeySize;
Copy(sock->BulkSendKey->Data, s->BulkSendKey, s->BulkSendKeySize);
sock->BulkSendKey->Size = s->BulkSendKeySize;
}
}
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;
// 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.address, 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.address, 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.address, 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;
}
// 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");
if (s->IsRUDPSocket && s->BulkRecvKey != NULL && s->BulkSendKey != NULL)
{
// Restore R-UDP bulk send/recv keys for additional connections
if (c->Session->BulkRecvKeySize != 0 && c->Session->BulkSendKeySize != 0)
{
Copy(s->BulkRecvKey->Data, c->Session->BulkRecvKey, c->Session->BulkRecvKeySize);
s->BulkRecvKey->Size = c->Session->BulkRecvKeySize;
Copy(s->BulkSendKey->Data, c->Session->BulkSendKey, c->Session->BulkSendKeySize);
s->BulkSendKey->Size = c->Session->BulkSendKeySize;
}
}
// 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 = NULL;
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);
AddProtocolDetailsStr(c->Session->ProtocolDetails, sizeof(c->Session->ProtocolDetails), s->ProtocolDetails);
if (c->Session->IsAzureSession)
{
StrCpy(c->Session->UnderlayProtocol, sizeof(c->Session->UnderlayProtocol), SOCK_UNDERLAY_AZURE);
AddProtocolDetailsStr(c->Session->ProtocolDetails, sizeof(c->Session->ProtocolDetails), "VPN 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 != NULL && s->IsRUDPSocket && s->BulkRecvKey != NULL && s->BulkSendKey != NULL)
{
// Bulk transfer on R-UDP
sess->EnableHMacOnBulkOfRUDP = PackGetBool(p, "enable_hmac_on_bulk_of_rudp");
sess->BulkOnRUDPVersion = PackGetInt(p, "rudp_bulk_version");
if (PackGetBool(p, "enable_bulk_on_rudp"))
{
// Receive the key
UCHAR key_send[RUDP_BULK_KEY_SIZE_MAX];
UCHAR key_recv[RUDP_BULK_KEY_SIZE_MAX];
UINT key_size = SHA1_SIZE;
if (sess->BulkOnRUDPVersion == 2)
{
key_size = RUDP_BULK_KEY_SIZE_V2;
}
if (PackGetData2(p, "bulk_on_rudp_send_key", key_send, key_size) &&
PackGetData2(p, "bulk_on_rudp_recv_key", key_recv, key_size))
{
sess->EnableBulkOnRUDP = true;
Copy(s->BulkSendKey->Data, key_send, key_size);
Copy(s->BulkRecvKey->Data, key_recv, key_size);
s->BulkSendKey->Size = key_size;
s->BulkRecvKey->Size = key_size;
// Backup R-UDP bulk send/recv keys for additional connections
Copy(sess->BulkSendKey, s->BulkSendKey->Data, s->BulkSendKey->Size);
sess->BulkSendKeySize = s->BulkSendKey->Size;
Copy(sess->BulkRecvKey, s->BulkRecvKey->Data, s->BulkRecvKey->Size);
sess->BulkRecvKeySize = s->BulkRecvKey->Size;
AddProtocolDetailsKeyValueInt(sess->ProtocolDetails, sizeof(sess->ProtocolDetails), "RUDP_Bulk_Ver", sess->BulkOnRUDPVersion);
}
}
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);
Debug("BulkOnRUDPVersion = %u\n", sess->BulkOnRUDPVersion);
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"))
{
UINT udp_acceleration_version = PackGetInt(p, "udp_acceleration_version");
IP udp_acceleration_server_ip;
if (udp_acceleration_version == 0)
{
udp_acceleration_version = 1;
}
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_V1];
UCHAR udp_acceleration_server_key_v2[UDP_ACCELERATION_COMMON_KEY_SIZE_V2];
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");
Zero(udp_acceleration_server_key, sizeof(udp_acceleration_server_key));
Zero(udp_acceleration_server_key_v2, sizeof(udp_acceleration_server_key_v2));
PackGetData2(p, "udp_acceleration_server_key", udp_acceleration_server_key, UDP_ACCELERATION_COMMON_KEY_SIZE_V1);
PackGetData2(p, "udp_acceleration_server_key_v2", udp_acceleration_server_key_v2, UDP_ACCELERATION_COMMON_KEY_SIZE_V2);
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));
}
sess->UdpAccel->Version = 1;
if (udp_acceleration_version == 2)
{
sess->UdpAccel->Version = 2;
}
if (UdpAccelInitClient(sess->UdpAccel,
sess->UdpAccel->Version == 2 ? udp_acceleration_server_key_v2 : udp_acceleration_server_key,
&remote_ip, &udp_acceleration_server_ip, udp_acceleration_server_port,
server_cookie, client_cookie) == 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;
}
AddProtocolDetailsKeyValueInt(sess->ProtocolDetails, sizeof(sess->ProtocolDetails), "UDPAccel_Ver", sess->UdpAccel->Version);
AddProtocolDetailsStr(sess->ProtocolDetails, sizeof(sess->ProtocolDetails), sess->UdpAccel->Version > 1 ? "ChaCha20-Poly1305" : "RC4");
AddProtocolDetailsKeyValueInt(sess->ProtocolDetails, sizeof(sess->ProtocolDetails), "UDPAccel_MSS", UdpAccelCalcMss(sess->UdpAccel));
}
}
}
}
}
}
}
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
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);
// Shared Buffer
s->IpcSessionSharedBuffer = NewSharedBuffer(NULL, sizeof(IPC_SESSION_SHARED_BUFFER_DATA));
AddRef(s->IpcSessionSharedBuffer->Ref);
s->IpcSessionShared = s->IpcSessionSharedBuffer->Data;
PackAddInt64(p, "IpcSessionSharedBuffer", (UINT64)s->IpcSessionSharedBuffer);
}
if (s->UdpAccel != NULL)
{
// UDP acceleration function
PackAddBool(p, "use_udp_acceleration", true);
PackAddInt(p, "udp_acceleration_version", s->UdpAccel->Version);
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, sizeof(s->UdpAccel->MyKey));
PackAddData(p, "udp_acceleration_server_key_v2", s->UdpAccel->MyKey_V2, sizeof(s->UdpAccel->MyKey_V2));
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);
PackAddInt(p, "rudp_bulk_version", s->BulkOnRUDPVersion);
if (s->BulkOnRUDPVersion == 2)
{
PackAddData(p, "bulk_on_rudp_send_key", s->Connection->FirstSock->BulkRecvKey->Data, RUDP_BULK_KEY_SIZE_V2);
s->Connection->FirstSock->BulkRecvKey->Size = RUDP_BULK_KEY_SIZE_V2;
PackAddData(p, "bulk_on_rudp_recv_key", s->Connection->FirstSock->BulkSendKey->Data, RUDP_BULK_KEY_SIZE_V2);
s->Connection->FirstSock->BulkSendKey->Size = RUDP_BULK_KEY_SIZE_V2;
}
else
{
PackAddData(p, "bulk_on_rudp_send_key", s->Connection->FirstSock->BulkRecvKey->Data, SHA1_SIZE);
s->Connection->FirstSock->BulkRecvKey->Size = SHA1_SIZE;
PackAddData(p, "bulk_on_rudp_recv_key", s->Connection->FirstSock->BulkSendKey->Data, SHA1_SIZE);
s->Connection->FirstSock->BulkSendKey->Size = SHA1_SIZE;
}
// Backup R-UDP bulk send/recv keys for additional connections
Copy(s->BulkSendKey, s->Connection->FirstSock->BulkSendKey->Data,
s->Connection->FirstSock->BulkSendKey->Size);
s->BulkSendKeySize = s->Connection->FirstSock->BulkSendKey->Size;
Copy(s->BulkRecvKey, s->Connection->FirstSock->BulkRecvKey->Data,
s->Connection->FirstSock->BulkRecvKey->Size);
s->BulkRecvKeySize = s->Connection->FirstSock->BulkRecvKey->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) \
PackAddBool(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 = (PackGetBool(p, "policy:" name))
#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_OPENSSLENGINE:
// 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)
{
PackAddBool(p, "use_udp_acceleration", true);
PackAddInt(p, "udp_acceleration_version", c->Session->UdpAccel->Version);
IP my_ip;
if (IsLocalHostIP(&c->Session->UdpAccel->MyIp) == false)
{
Copy(&my_ip, &c->Session->UdpAccel->MyIp, sizeof(my_ip));
}
else
{
Zero(&my_ip, sizeof(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_V1);
PackAddData(p, "udp_acceleration_client_key_v2", c->Session->UdpAccel->MyKey_V2, UDP_ACCELERATION_COMMON_KEY_SIZE_V2);
PackAddBool(p, "support_hmac_on_udp_acceleration", true);
PackAddBool(p, "support_udp_accel_fast_disconnect_detect", true);
PackAddInt(p, "udp_acceleration_max_version", 2);
}
PackAddInt(p, "rudp_bulk_max_version", 2);
// 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;
if (c->IsJsonRpc)
{
c->Err = ERR_DISCONNECTED;
}
return false;
}
// Interpret
if (StrCmpi(h->Method, "POST") == 0)
{
// Receive the data since it's POST
data_size = GetContentLength(h);
if (server->DisableJsonRpcWebApi == false)
{
if (StrCmpi(h->Target, "/api") == 0 || StrCmpi(h->Target, "/api/") == 0)
{
c->IsJsonRpc = true;
c->Type = CONNECTION_TYPE_ADMIN_RPC;
JsonRpcProcPost(c, s, h, data_size);
FreeHttpHeader(h);
if (c->JsonRpcAuthed)
{
num = 0;
}
continue;
}
else if (StartWith(h->Target, "/admin"))
{
c->IsJsonRpc = true;
c->Type = CONNECTION_TYPE_ADMIN_RPC;
AdminWebProcPost(c, s, h, data_size, h->Target);
FreeHttpHeader(h);
if (c->JsonRpcAuthed)
{
num = 0;
}
continue;
}
}
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, "OPTIONS") == 0)
{
if (server->DisableJsonRpcWebApi == false)
{
if (StrCmpi(h->Target, "/api") == 0 || StrCmpi(h->Target, "/api/") == 0 || StartWith(h->Target, "/admin"))
{
c->IsJsonRpc = true;
c->Type = CONNECTION_TYPE_ADMIN_RPC;
JsonRpcProcOptions(c, s, h, h->Target);
FreeHttpHeader(h);
num = 0;
continue;
}
}
}
else if (StrCmpi(h->Method, "SSTP_DUPLEX_POST") == 0 && (ProtoEnabled(server->Proto, "SSTP") || s->IsReverseAcceptedSocket) && GetServerCapsBool(server, "b_support_sstp"))
{
// 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_OTHER;
sstp_ret = ProtoHandleConnection(server->Proto, s, "SSTP");
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
BUF *b = NULL;
*error_detail_str = "HTTP_ROOT";
if (server->DisableJsonRpcWebApi == false)
{
b = ReadDump("|wwwroot\\index.html");
}
if (b != NULL)
{
FreeHttpHeader(h);
h = NewHttpHeader("HTTP/1.1", "202", "OK");
AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE4));
AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
PostHttp(c->FirstSock, h, b->Buf, b->Size);
FreeBuf(b);
}
else
{
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 (IsLocalHostIP(&c->FirstSock->RemoteIP))
{
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)
{
if (server->DisableJsonRpcWebApi == false)
{
if (StartWith(h->Target, "/api?") || StartWith(h->Target, "/api/") || StrCmpi(h->Target, "/api") == 0)
{
c->IsJsonRpc = true;
c->Type = CONNECTION_TYPE_ADMIN_RPC;
JsonRpcProcGet(c, s, h, h->Target);
if (c->JsonRpcAuthed)
{
num = 0;
}
FreeHttpHeader(h);
continue;
}
else if (StartWith(h->Target, "/admin"))
{
c->IsJsonRpc = true;
c->Type = CONNECTION_TYPE_ADMIN_RPC;
AdminWebProcGet(c, s, h, h->Target);
if (c->JsonRpcAuthed)
{
num = 0;
}
FreeHttpHeader(h);
continue;
}
}
}
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)
{
volatile bool *cancel_flag = NULL;
char hostname[MAX_HOST_NAME_LEN];
bool save_resolved_ip = false;
CLIENT_OPTION *o;
SESSION *sess;
SOCK *sock = NULL;
IP resolved_ip;
// Validate arguments
if (c == NULL || c->Session == NULL || c->Session->ClientOption == NULL)
{
return NULL;
}
cancel_flag = &c->Halt;
sess = c->Session;
o = c->Session->ClientOption;
Zero(&resolved_ip, sizeof(resolved_ip));
if (additional_connect == false && c->RestoreServerNameAndPort)
{
// Update server name and port number.
// At the time of writing this comment RestoreServerNameAndPort is never true.
c->RestoreServerNameAndPort = false;
if (StrCmpi(c->ServerName, o->Hostname) != 0)
{
StrCpy(c->ServerName, sizeof(c->ServerName), o->Hostname);
}
c->ServerPort = o->Port;
}
if (IsZeroIP(&sess->ServerIP_CacheForNextConnect) == false)
{
IPToStr(hostname, sizeof(hostname), &sess->ServerIP_CacheForNextConnect);
Debug("ClientConnectGetSocket(): Using cached IP address %s\n", hostname);
}
else
{
IP tmp;
StrCpy(hostname, sizeof(hostname), o->ProxyType == PROXY_DIRECT ? c->ServerName : o->ProxyName);
if (StrToIP(&tmp, hostname) == false)
{
// The hostname is not an IP address
save_resolved_ip = true;
}
}
if (o->ProxyType == PROXY_DIRECT)
{
UINT nat_t_err = 0;
wchar_t tmp[MAX_SIZE];
UniFormat(tmp, sizeof(tmp), _UU("STATUS_4"), hostname);
PrintStatus(sess, tmp);
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
sock = TcpIpConnectEx(hostname, c->ServerPort,
(bool *)cancel_flag, c->hWndForUI, &nat_t_err, (additional_connect ? (!sess->IsRUDPSession) : false),
true, &resolved_ip);
}
else
{
// Mode to connect with R-UDP directly without using NAT-T server when using UDP
IP ip;
if (StrToIP(&ip, hostname))
{
sock = NewRUDPClientDirect(VPN_RUDP_SVC_NAME, &ip, o->PortUDP, &nat_t_err,
TIMEOUT_TCP_PORT_CHECK, (bool *)cancel_flag, NULL, NULL, 0, false);
if (sock != NULL)
{
StrCpy(sock->UnderlayProtocol, sizeof(sock->UnderlayProtocol), SOCK_UNDERLAY_NAT_T);
}
}
}
if (sock == 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;
}
}
else
{
wchar_t tmp[MAX_SIZE];
PROXY_PARAM_OUT out;
PROXY_PARAM_IN in;
UINT ret;
Zero(&in, sizeof(in));
in.Timeout = 0;
StrCpy(in.TargetHostname, sizeof(in.TargetHostname), c->ServerName);
in.TargetPort = c->ServerPort;
StrCpy(in.Hostname, sizeof(in.Hostname), IsEmptyStr(hostname) ? o->ProxyName : hostname);
in.Port = o->ProxyPort;
StrCpy(in.Username, sizeof(in.Username), o->ProxyUsername);
StrCpy(in.Password, sizeof(in.Password), o->ProxyPassword);
StrCpy(in.HttpCustomHeader, sizeof(in.HttpCustomHeader), o->CustomHttpHeader);
StrCpy(in.HttpUserAgent, sizeof(in.HttpUserAgent), c->Cedar->HttpUserAgent);
#ifdef OS_WIN32
in.Hwnd = c->hWndForUI;
#endif
UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), in.TargetHostname, in.Hostname);
PrintStatus(sess, tmp);
switch (o->ProxyType)
{
case PROXY_HTTP:
ret = ProxyHttpConnect(&out, &in, cancel_flag);
break;
case PROXY_SOCKS:
ret = ProxySocks4Connect(&out, &in, cancel_flag);
break;
case PROXY_SOCKS5:
ret = ProxySocks5Connect(&out, &in, cancel_flag);
break;
default:
c->Err = ERR_INTERNAL_ERROR;
Debug("ClientConnectGetSocket(): Unknown proxy type: %u!\n", o->ProxyType);
return NULL;
}
c->Err = ProxyCodeToCedar(ret);
if (c->Err != ERR_NO_ERROR)
{
Debug("ClientConnectGetSocket(): Connection via proxy server failed with error %u\n", ret);
return NULL;
}
sock = out.Sock;
CopyIP(&resolved_ip, &out.ResolvedIp);
}
if (additional_connect == false || IsZeroIP(&sock->RemoteIP))
{
if (((sock->IsRUDPSocket || sock->IPv6) && IsZeroIP(&sock->RemoteIP) == false && o->ProxyType == PROXY_DIRECT) || GetIP(&c->Session->ServerIP, hostname) == false)
{
Copy(&c->Session->ServerIP, &sock->RemoteIP, sizeof(c->Session->ServerIP));
}
}
if (save_resolved_ip && IsZeroIP(&resolved_ip) == false)
{
Copy(&c->Session->ServerIP_CacheForNextConnect, &resolved_ip, sizeof(c->Session->ServerIP_CacheForNextConnect));
Debug("ClientConnectGetSocket(): Saved %s IP address %r for future connections.\n", hostname, &resolved_ip);
}
return sock;
}
UINT ProxyCodeToCedar(UINT code)
{
switch (code)
{
case PROXY_ERROR_SUCCESS:
return ERR_NO_ERROR;
case PROXY_ERROR_GENERIC:
case PROXY_ERROR_VERSION:
return ERR_PROXY_ERROR;
case PROXY_ERROR_CANCELED:
return ERR_USER_CANCEL;
case PROXY_ERROR_CONNECTION:
return ERR_PROXY_CONNECT_FAILED;
case PROXY_ERROR_TARGET:
return ERR_CONNECT_FAILED;
case PROXY_ERROR_DISCONNECTED:
return ERR_DISCONNECTED;
case PROXY_ERROR_AUTHENTICATION:
return ERR_PROXY_AUTH_FAILED;
default:
return ERR_INTERNAL_ERROR;
}
}
// TCP connection function
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 *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 WireGuard key login
PACK *PackLoginWithWireGuardKey(char *key)
{
PACK *p;
// Validate arguments
if (key == NULL)
{
return NULL;
}
p = NewPack();
PackAddStr(p, "method", "login");
PackAddInt(p, "authtype", AUTHTYPE_WIREGUARD_KEY);
PackAddStr(p, "key", key);
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)
{
FreePack(p);
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;
}