1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-23 01:49:53 +03:00
SoftEtherVPN/src/Cedar/Protocol.c
Tetsuo Sugiyama c9508b7fb7
Password change from client increments config file revision
Fixed an issue where changing the password from the client did not increment the revision of the server config file and the changes were not saved
2021-09-21 18:28:17 +09:00

6766 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);
IncrementServerConfigRevision(cedar->Server);
}
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 (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("NATT_MSG"), 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 (s->ClientOption->ProxyType == PROXY_DIRECT)
{
if (IsIP6(&c->FirstSock->RemoteIP) == false)
{
info->ServerIpAddress = IPToUINT(&c->FirstSock->RemoteIP);
}
else
{
Copy(info->ServerIpAddress6, c->FirstSock->RemoteIP.address, sizeof(info->ServerIpAddress6));
}
}
else 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 (additional_connect && 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;
}