mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2025-07-07 08:14:58 +03:00
Cedar: Implement support for WireGuard
Please note that the implementation is not 100% conformant to the protocol whitepaper (https://www.wireguard.com/papers/wireguard.pdf). More specifically: all peers are expected to send a handshake initiation once the current keypair is about to expire or is expired. I decided not to do that because our implementation is meant to act as a server only. A true WireGuard peer acts, instead, as both a client and a server. Once the keypair is expired, we immediately delete the session. The cookie mechanism can be implemented in future. As for authentication: unfortunately using the already existing methods is not possible due to the protocol not providing a way to send strings to a peer. That's because WireGuard doesn't have a concept of "users": it identifies a peer through the public key, which is determined using the source address. As a solution, this commit adds a special authentication method: once we receive the handshake initiation message and decrypt the peer's public key, we check whether it's in the allowed key list. If it is, we retrieve the associated Virtual Hub and user; if the hub exists and the user is in it, the authentication is successful. The allowed key list is stored in the configuration file like this: declare WireGuardKeyList { declare 96oA7iMvjn7oXiG3ghBDPaSUytT75uXceLV+Fx3XMlM= { string Hub DEFAULT string User user } }
This commit is contained in:
@ -26,6 +26,21 @@ set(BLAKE2_SRC_PATH $<IF:$<BOOL:HAS_SSE2>,${TOP_DIRECTORY}/3rdparty/BLAKE2/sse,$
|
||||
target_include_directories(cedar PUBLIC ${BLAKE2_SRC_PATH})
|
||||
target_sources(cedar PRIVATE "${BLAKE2_SRC_PATH}/blake2s.c")
|
||||
|
||||
if(VCPKG_TARGET_TRIPLET)
|
||||
find_package(unofficial-sodium CONFIG REQUIRED)
|
||||
target_link_libraries(cedar PUBLIC unofficial-sodium::sodium)
|
||||
else()
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_search_module(SODIUM libsodium REQUIRED)
|
||||
target_include_directories(cedar PUBLIC ${SODIUM_INCLUDE_DIRS})
|
||||
if(NOT ("$ENV{TRAVIS_CPU_ARCH}" STREQUAL ppc64le))
|
||||
target_link_libraries(cedar PUBLIC $<IF:$<BOOL:SODIUM_LINK_LIBRARIES>,${SODIUM_LINK_LIBRARIES},${SODIUM_LIBRARIES}>)
|
||||
else()
|
||||
# TODO: investigate why on ppc64le the use of SODIUM_LINK_LIBRARIES causes undefined references to libsodium functions.
|
||||
target_link_libraries(cedar PUBLIC ${SODIUM_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(cedar
|
||||
PROPERTIES
|
||||
@ -38,10 +53,10 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
find_library(LIB_READLINE readline)
|
||||
find_package(Curses REQUIRED)
|
||||
find_library(LIB_READLINE readline)
|
||||
|
||||
target_link_libraries(cedar PRIVATE ${LIB_READLINE} ${CURSES_LIBRARIES})
|
||||
target_link_libraries(cedar PRIVATE ${CURSES_LIBRARIES} ${LIB_READLINE})
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
target_link_libraries(cedar PRIVATE mayaqua pcap)
|
||||
|
@ -1094,12 +1094,13 @@ void CleanupCedar(CEDAR *c)
|
||||
WuFreeWebUI(c->WebUI);
|
||||
FreeCedarLayer3(c);
|
||||
|
||||
/*
|
||||
for (i = 0;i < LIST_NUM(c->HubList);i++)
|
||||
for (i = 0; i < LIST_NUM(c->WgkList); ++i)
|
||||
{
|
||||
HUB *h = LIST_DATA(c->HubList, i);
|
||||
WGK *wgk = LIST_DATA(c->WgkList, i);
|
||||
Free(wgk);
|
||||
}
|
||||
*/
|
||||
ReleaseList(c->WgkList);
|
||||
|
||||
for (i = 0;i < LIST_NUM(c->CaList);i++)
|
||||
{
|
||||
X *x = LIST_DATA(c->CaList, i);
|
||||
@ -1491,6 +1492,7 @@ CEDAR *NewCedar(X *server_x, K *server_k)
|
||||
c->Traffic = NewTraffic();
|
||||
c->TrafficLock = NewLock();
|
||||
c->CaList = NewList(CompareCert);
|
||||
c->WgkList = NewList(CompareWgk);
|
||||
|
||||
c->TrafficDiffList = NewList(NULL);
|
||||
|
||||
@ -1600,6 +1602,12 @@ void InitCedar()
|
||||
return;
|
||||
}
|
||||
|
||||
if (sodium_init() == -1)
|
||||
{
|
||||
Debug("InitCedar(): sodium_init() failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize protocol module
|
||||
InitProtocol();
|
||||
}
|
||||
|
@ -367,6 +367,7 @@
|
||||
#define AUTHTYPE_ROOTCERT 3 // Root certificate which is issued by trusted Certificate Authority
|
||||
#define AUTHTYPE_RADIUS 4 // Radius authentication
|
||||
#define AUTHTYPE_NT 5 // Windows NT authentication
|
||||
#define AUTHTYPE_WIREGUARD_KEY 97 // WireGuard public key authentication
|
||||
#define AUTHTYPE_OPENVPN_CERT 98 // TLS client certificate authentication
|
||||
#define AUTHTYPE_TICKET 99 // Ticket authentication
|
||||
|
||||
@ -923,6 +924,7 @@ typedef struct CEDAR
|
||||
UINT Type; // Type
|
||||
LIST *ListenerList; // Listener list
|
||||
LIST *HubList; // HUB list
|
||||
LIST *WgkList; // WireGuard key list
|
||||
LIST *ConnectionList; // Negotiating connection list
|
||||
LIST *CaList; // List of CA
|
||||
volatile bool Halt; // Halt flag
|
||||
@ -1032,8 +1034,6 @@ typedef struct CEDAR
|
||||
#include <Cedar/Command.h>
|
||||
// RPC over HTTP
|
||||
#include <Cedar/Wpc.h>
|
||||
// Layer-2/Layer-3 converter
|
||||
#include <Cedar/IPC.h>
|
||||
// Third party protocols
|
||||
#include <Cedar/Proto.h>
|
||||
#include <Cedar/Proto_IPsec.h>
|
||||
@ -1045,6 +1045,9 @@ typedef struct CEDAR
|
||||
#include <Cedar/Proto_PPP.h>
|
||||
#include <Cedar/Proto_SSTP.h>
|
||||
#include <Cedar/Proto_Win7.h>
|
||||
#include <Cedar/Proto_WireGuard.h>
|
||||
// Layer-2/Layer-3 converter
|
||||
#include <Cedar/IPC.h>
|
||||
// UDP Acceleration
|
||||
#include <Cedar/UdpAccel.h>
|
||||
// DDNS Client
|
||||
|
@ -277,6 +277,7 @@ typedef struct HUB_SNAPSHOT HUB_SNAPSHOT;
|
||||
typedef struct SERVER_SNAPSHOT SERVER_SNAPSHOT;
|
||||
typedef struct SERVER_HUB_CREATE_HISTORY SERVER_HUB_CREATE_HISTORY;
|
||||
typedef struct OPENVPN_SSTP_CONFIG OPENVPN_SSTP_CONFIG;
|
||||
typedef struct WGK WGK;
|
||||
|
||||
// ==============================================================
|
||||
// Server Admin Tool
|
||||
|
@ -226,8 +226,8 @@ IPC *NewIPCByParam(CEDAR *cedar, IPC_PARAM *param, UINT *error_code)
|
||||
}
|
||||
|
||||
ipc = NewIPC(cedar, param->ClientName, param->Postfix, param->HubName,
|
||||
param->UserName, param->Password, error_code, ¶m->ClientIp,
|
||||
param->ClientPort, ¶m->ServerIp, param->ServerPort,
|
||||
param->UserName, param->Password, param->WgKey, error_code,
|
||||
¶m->ClientIp, param->ClientPort, ¶m->ServerIp, param->ServerPort,
|
||||
param->ClientHostname, param->CryptName,
|
||||
param->BridgeMode, param->Mss, NULL, param->ClientCertificate, param->Layer);
|
||||
|
||||
@ -235,7 +235,7 @@ IPC *NewIPCByParam(CEDAR *cedar, IPC_PARAM *param, UINT *error_code)
|
||||
}
|
||||
|
||||
// Start a new IPC connection
|
||||
IPC *NewIPC(CEDAR *cedar, char *client_name, char *postfix, char *hubname, char *username, char *password,
|
||||
IPC *NewIPC(CEDAR *cedar, char *client_name, char *postfix, char *hubname, char *username, char *password, char *wg_key,
|
||||
UINT *error_code, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port,
|
||||
char *client_hostname, char *crypt_name,
|
||||
bool bridge_mode, UINT mss, EAP_CLIENT *eap_client, X *client_certificate,
|
||||
@ -337,7 +337,11 @@ IPC *NewIPC(CEDAR *cedar, char *client_name, char *postfix, char *hubname, char
|
||||
FreePack(p);
|
||||
|
||||
// Upload the authentication data
|
||||
if (client_certificate != NULL)
|
||||
if (IsEmptyStr(wg_key) == false)
|
||||
{
|
||||
p = PackLoginWithWireGuardKey(wg_key);
|
||||
}
|
||||
else if (client_certificate != NULL)
|
||||
{
|
||||
p = PackLoginWithOpenVPNCertificate(hubname, username, client_certificate);
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ struct IPC_PARAM
|
||||
char HubName[MAX_HUBNAME_LEN + 1];
|
||||
char UserName[MAX_USERNAME_LEN + 1];
|
||||
char Password[MAX_PASSWORD_LEN + 1];
|
||||
char WgKey[WG_KEY_BASE64_SIZE];
|
||||
IP ClientIp;
|
||||
UINT ClientPort;
|
||||
IP ServerIp;
|
||||
@ -171,7 +172,7 @@ struct IPC_IPV6_ROUTER_ADVERTISEMENT
|
||||
UCHAR RouterLinkLayerAddress[6];
|
||||
};
|
||||
|
||||
IPC *NewIPC(CEDAR *cedar, char *client_name, char *postfix, char *hubname, char *username, char *password,
|
||||
IPC *NewIPC(CEDAR *cedar, char *client_name, char *postfix, char *hubname, char *username, char *password, char *wg_key,
|
||||
UINT *error_code, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port,
|
||||
char *client_hostname, char *crypt_name,
|
||||
bool bridge_mode, UINT mss, EAP_CLIENT *eap_client, X *client_certificate,
|
||||
|
@ -213,6 +213,8 @@ PROTO *ProtoNew(CEDAR *cedar)
|
||||
|
||||
AddRef(cedar->ref);
|
||||
|
||||
// WireGuard
|
||||
Add(proto->Containers, ProtoContainerNew(WgsGetProtoImpl()));
|
||||
// OpenVPN
|
||||
Add(proto->Containers, ProtoContainerNew(OvsGetProtoImpl()));
|
||||
// SSTP
|
||||
|
@ -59,7 +59,7 @@ void EtherIPIpcConnectThread(THREAD *t, void *p)
|
||||
EtherIPLog(s, "LE_START_IPC", id.HubName, id.UserName, mss);
|
||||
ipc = NewIPC(s->Cedar, client_name,
|
||||
(s->L2TPv3 ? ETHERIP_L2TPV3_POSTFIX : ETHERIP_POSTFIX),
|
||||
id.HubName, id.UserName, id.Password,
|
||||
id.HubName, id.UserName, id.Password, NULL,
|
||||
&error_code,
|
||||
&s->ClientIP, s->ClientPort,
|
||||
&s->ServerIP, s->ServerPort,
|
||||
|
@ -1519,7 +1519,7 @@ bool PPPProcessPAPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp)
|
||||
// Attempt to connect with IPC
|
||||
UINT error_code;
|
||||
|
||||
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, hub, id, password,
|
||||
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, hub, id, password, NULL,
|
||||
&error_code, &p->ClientIP, p->ClientPort, &p->ServerIP, p->ServerPort,
|
||||
p->ClientHostname, p->CryptName, false, p->AdjustMss, NULL, NULL,
|
||||
IPC_LAYER_3);
|
||||
@ -2844,7 +2844,7 @@ bool PPPParseMSCHAP2ResponsePacket(PPP_SESSION *p, PPP_PACKET *pp)
|
||||
else if (p->Ipc == NULL)
|
||||
{
|
||||
Debug("MSCHAPv2 creating IPC\n");
|
||||
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, hub, id, password,
|
||||
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, hub, id, password, NULL,
|
||||
&error_code, &p->ClientIP, p->ClientPort, &p->ServerIP, p->ServerPort,
|
||||
p->ClientHostname, p->CryptName, false, p->AdjustMss, p->EapClient, NULL,
|
||||
+ IPC_LAYER_3);
|
||||
@ -3252,7 +3252,7 @@ bool PPPProcessEAPTlsResponse(PPP_SESSION *p, PPP_EAP *eap_packet, UINT eapTlsSi
|
||||
|
||||
PPPParseUsername(p->Cedar, p->Eap_Identity, &d);
|
||||
|
||||
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, d.HubName, d.UserName, "",
|
||||
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, d.HubName, d.UserName, "", NULL,
|
||||
&error_code, &p->ClientIP, p->ClientPort, &p->ServerIP, p->ServerPort,
|
||||
p->ClientHostname, p->CryptName, false, p->AdjustMss, NULL, p->Eap_TlsCtx.ClientCert.X,
|
||||
IPC_LAYER_3);
|
||||
|
1088
src/Cedar/Proto_WireGuard.c
Normal file
1088
src/Cedar/Proto_WireGuard.c
Normal file
File diff suppressed because it is too large
Load Diff
209
src/Cedar/Proto_WireGuard.h
Normal file
209
src/Cedar/Proto_WireGuard.h
Normal file
@ -0,0 +1,209 @@
|
||||
#ifndef PROTO_WIREGUARD_H
|
||||
#define PROTO_WIREGUARD_H
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
#define WG_IPC_POSTFIX "WIREGUARD"
|
||||
|
||||
#define WG_CIPHER "ChaCha20-Poly1305"
|
||||
|
||||
#define WG_CONSTRUCTION "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
|
||||
#define WG_IDENTIFIER "WireGuard v1 zx2c4 Jason@zx2c4.com"
|
||||
#define WG_LABEL_COOKIE "cookie--"
|
||||
#define WG_LABEL_MAC1 "mac1----"
|
||||
|
||||
#define WG_MAX_INITIATIONS_PER_SECOND 50
|
||||
|
||||
#define WG_KEEPALIVE_TIMEOUT 10000 // 10 seconds
|
||||
#define WG_INITIATION_GIVEUP 30000 // 30 seconds
|
||||
|
||||
#define WG_REJECT_AFTER_TIME 180000 // 180 seconds
|
||||
#define WG_REJECT_AFTER_MESSAGES (UINT64_MAX - 16 - 1)
|
||||
|
||||
#define WG_KEY_SIZE crypto_aead_chacha20poly1305_ietf_KEYBYTES
|
||||
#define WG_IV_SIZE crypto_aead_chacha20poly1305_ietf_NPUBBYTES
|
||||
#define WG_TAG_SIZE crypto_aead_chacha20poly1305_ietf_ABYTES
|
||||
|
||||
#define WG_COOKIE_IV_SIZE crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
|
||||
#define WG_COOKIE_TAG_SIZE crypto_aead_xchacha20poly1305_ietf_ABYTES
|
||||
|
||||
#define WG_HASH_SIZE 32
|
||||
#define WG_BLOCK_SIZE 16
|
||||
#define WG_COOKIE_SIZE 16
|
||||
#define WG_TIMESTAMP_SIZE (sizeof(UINT64) + sizeof(UINT))
|
||||
|
||||
#define WG_KEY_BASE64_SIZE (sodium_base64_ENCODED_LEN(WG_KEY_SIZE, sodium_base64_VARIANT_ORIGINAL))
|
||||
|
||||
#define WG_AEAD_SIZE(plain_size) (plain_size + WG_TAG_SIZE)
|
||||
#define WG_PLAIN_SIZE(aead_size) (aead_size - WG_TAG_SIZE)
|
||||
|
||||
// RFC 6479
|
||||
#define WG_REPLAY_WINDOW_SIZE 1024
|
||||
#define WG_REPLAY_BITMAP_SIZE (WG_REPLAY_WINDOW_SIZE / (sizeof(int) * 8))
|
||||
#define WG_REPLAY_BITMAP_INDEX_MASK (WG_REPLAY_BITMAP_SIZE - 1)
|
||||
#define WG_REPLAY_REDUNDANT_BIT_SHIFTS 5
|
||||
#define WG_REPLAY_REDUNDANT_BITS (1 << WG_REPLAY_REDUNDANT_BIT_SHIFTS)
|
||||
#define WG_REPLAY_BITMAP_LOC_MASK (WG_REPLAY_REDUNDANT_BITS - 1)
|
||||
|
||||
typedef enum WG_MSG_TYPE
|
||||
{
|
||||
WG_MSG_INVALID = 0,
|
||||
WG_MSG_HANDSHAKE_INIT,
|
||||
WG_MSG_HANDSHAKE_REPLY,
|
||||
WG_MSG_HANDSHAKE_COOKIE,
|
||||
WG_MSG_TRANSPORT_DATA
|
||||
} WG_MSG_TYPE;
|
||||
|
||||
typedef enum WG_KEYPAIR_STATE
|
||||
{
|
||||
WG_KEYPAIR_INVALID = 0,
|
||||
WG_KEYPAIR_INITIATED,
|
||||
WG_KEYPAIR_CONFIRMED
|
||||
} WG_KEYPAIR_STATE;
|
||||
|
||||
typedef struct WG_HEADER
|
||||
{
|
||||
BYTE Type;
|
||||
BYTE Reserved[3];
|
||||
} WG_HEADER;
|
||||
|
||||
typedef struct WG_COMMON
|
||||
{
|
||||
WG_HEADER Header;
|
||||
UINT Index;
|
||||
} WG_COMMON;
|
||||
|
||||
typedef struct WG_MACS
|
||||
{
|
||||
BYTE Mac1[WG_COOKIE_SIZE];
|
||||
BYTE Mac2[WG_COOKIE_SIZE];
|
||||
} WG_MACS;
|
||||
|
||||
typedef struct WG_HANDSHAKE_INIT
|
||||
{
|
||||
WG_HEADER Header;
|
||||
UINT SenderIndex;
|
||||
BYTE UnencryptedEphemeral[WG_KEY_SIZE];
|
||||
BYTE EncryptedStatic[WG_AEAD_SIZE(WG_KEY_SIZE)];
|
||||
BYTE EncryptedTimestamp[WG_AEAD_SIZE(WG_TIMESTAMP_SIZE)];
|
||||
WG_MACS Macs;
|
||||
} WG_HANDSHAKE_INIT;
|
||||
|
||||
typedef struct WG_HANDSHAKE_REPLY
|
||||
{
|
||||
WG_HEADER Header;
|
||||
UINT SenderIndex;
|
||||
UINT ReceiverIndex;
|
||||
BYTE UnencryptedEphemeral[WG_KEY_SIZE];
|
||||
BYTE EncryptedNothing[WG_AEAD_SIZE(0)];
|
||||
WG_MACS Macs;
|
||||
} WG_HANDSHAKE_REPLY;
|
||||
|
||||
typedef struct WG_COOKIE_REPLY
|
||||
{
|
||||
WG_HEADER Header;
|
||||
UINT ReceiverIndex;
|
||||
BYTE Nonce[WG_COOKIE_IV_SIZE];
|
||||
BYTE EncryptedCookie[WG_COOKIE_SIZE + WG_COOKIE_TAG_SIZE];
|
||||
} WG_COOKIE_REPLY;
|
||||
|
||||
typedef struct WG_TRANSPORT_DATA
|
||||
{
|
||||
WG_HEADER Header;
|
||||
UINT ReceiverIndex;
|
||||
UINT64 Counter;
|
||||
BYTE EncapsulatedPacket[];
|
||||
} WG_TRANSPORT_DATA;
|
||||
|
||||
typedef struct WG_KEYPAIR
|
||||
{
|
||||
WG_KEYPAIR_STATE State;
|
||||
UINT64 CreationTime;
|
||||
UINT IndexLocal;
|
||||
UINT IndexRemote;
|
||||
UINT64 CounterLocal;
|
||||
UINT64 CounterRemote;
|
||||
BYTE KeyLocal[WG_KEY_SIZE];
|
||||
BYTE KeyRemote[WG_KEY_SIZE];
|
||||
UINT64 ReplayWindow[WG_REPLAY_WINDOW_SIZE];
|
||||
} WG_KEYPAIR;
|
||||
|
||||
typedef struct WG_KEYPAIRS
|
||||
{
|
||||
WG_KEYPAIR *Current;
|
||||
WG_KEYPAIR *Next;
|
||||
WG_KEYPAIR *Previous;
|
||||
} WG_KEYPAIRS;
|
||||
|
||||
typedef struct WG_SESSION
|
||||
{
|
||||
WG_KEYPAIRS Keypairs;
|
||||
IPC *IPC;
|
||||
IP IPLocal;
|
||||
IP IPRemote;
|
||||
USHORT PortLocal;
|
||||
USHORT PortRemote;
|
||||
UINT64 LastInitiationReceived;
|
||||
UINT64 LastDataReceived;
|
||||
UINT64 LastDataSent;
|
||||
BYTE StaticRemote[WG_KEY_SIZE];
|
||||
BYTE LastTimestamp[WG_TIMESTAMP_SIZE];
|
||||
BYTE Hash[WG_HASH_SIZE];
|
||||
BYTE ChainingKey[WG_HASH_SIZE];
|
||||
BYTE PrecomputedStaticStatic[WG_KEY_SIZE];
|
||||
} WG_SESSION;
|
||||
|
||||
typedef struct WG_SERVER
|
||||
{
|
||||
UINT64 Now;
|
||||
UINT64 CreationTime;
|
||||
WG_SESSION Session;
|
||||
CEDAR *Cedar;
|
||||
SOCK_EVENT *SockEvent;
|
||||
INTERRUPT_MANAGER *InterruptManager;
|
||||
BYTE PresharedKey[WG_KEY_SIZE];
|
||||
BYTE StaticPublic[WG_KEY_SIZE];
|
||||
BYTE StaticPrivate[WG_KEY_SIZE];
|
||||
BYTE HandshakeInitHash[WG_HASH_SIZE];
|
||||
BYTE HandshakeInitChainingKey[WG_HASH_SIZE];
|
||||
} WG_SERVER;
|
||||
|
||||
const PROTO_IMPL *WgsGetProtoImpl();
|
||||
const char *WgsName();
|
||||
const PROTO_OPTION *WgsOptions();
|
||||
char *WgsOptionStringValue(const char *name);
|
||||
bool WgsInit(void **param, const LIST *options, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname);
|
||||
void WgsFree(void *param);
|
||||
bool WgsIsPacketForMe(const PROTO_MODE mode, const void *data, const UINT size);
|
||||
bool WgsProcessDatagrams(void *param, LIST *in, LIST *out);
|
||||
|
||||
void WgsLog(const WG_SERVER *server, const char *name, ...);
|
||||
|
||||
WG_MSG_TYPE WgsDetectMessageType(const void *data, const UINT size);
|
||||
|
||||
UINT WgsMSS(const WG_SESSION *session);
|
||||
|
||||
IPC *WgsIPCNew(WG_SERVER *server);
|
||||
|
||||
WG_KEYPAIR *WgsProcessHandshakeInit(WG_SERVER *server, const WG_HANDSHAKE_INIT *init, BYTE *ephemeral_remote);
|
||||
WG_HANDSHAKE_REPLY *WgsCreateHandshakeReply(WG_SERVER *server, WG_KEYPAIR *keypair, const BYTE *ephemeral_remote);
|
||||
|
||||
bool WgsProcessTransportData(WG_SERVER *server, WG_TRANSPORT_DATA *data, const UINT size);
|
||||
WG_TRANSPORT_DATA *WgsCreateTransportData(WG_SERVER *server, const void *data, const UINT size, UINT *final_size);
|
||||
|
||||
bool WgsIsInReplayWindow(const WG_KEYPAIR *keypair, const UINT64 counter);
|
||||
void WgsUpdateReplayWindow(WG_KEYPAIR *keypair, const UINT64 counter);
|
||||
|
||||
UINT WgsEncryptData(void *key, const UINT64 counter, void *dst, const void *src, const UINT src_size);
|
||||
UINT WgsDecryptData(void *key, const UINT64 counter, void *dst, const void *src, const UINT src_size);
|
||||
|
||||
bool WgsEncryptWithHash(void *dst, const void *src, const UINT src_size, BYTE *hash, const BYTE *key);
|
||||
bool WgsDecryptWithHash(void *dst, const void *src, const UINT src_size, BYTE *hash, const BYTE *key);
|
||||
|
||||
void WgsEphemeral(BYTE *ephemeral_dst, const BYTE *ephemeral_src, BYTE *chaining_key, BYTE *hash);
|
||||
void WgsHKDF(BYTE *dst_1, BYTE *dst_2, BYTE *dst_3, const BYTE *data, const UINT data_size, const BYTE *chaining_key);
|
||||
|
||||
void WgsMixHash(void *dst, const void *src, const UINT size);
|
||||
bool WgsMixDh(BYTE *chaining_key, BYTE *key, const BYTE *priv, const BYTE *pub);
|
||||
|
||||
#endif
|
@ -1330,12 +1330,45 @@ bool ServerAccept(CONNECTION *c)
|
||||
goto CLEANUP;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Login
|
||||
if (GetHubnameAndUsernameFromPack(p, username, sizeof(username), hubname, sizeof(hubname)) == false)
|
||||
// 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)
|
||||
{
|
||||
// Protocol error
|
||||
FreePack(p);
|
||||
c->Err = ERR_PROTOCOL_ERROR;
|
||||
error_detail = "GetHubnameAndUsernameFromPack";
|
||||
@ -1345,9 +1378,7 @@ bool ServerAccept(CONNECTION *c)
|
||||
if (farm_member)
|
||||
{
|
||||
bool ok = false;
|
||||
UINT authtype;
|
||||
|
||||
authtype = GetAuthTypeFromPack(p);
|
||||
if (StrCmpi(username, ADMINISTRATOR_USERNAME) == 0 &&
|
||||
authtype == AUTHTYPE_PASSWORD)
|
||||
{
|
||||
@ -1600,9 +1631,6 @@ bool ServerAccept(CONNECTION *c)
|
||||
PackGetData(p, "unique_id", unique);
|
||||
}
|
||||
|
||||
// Get the authentication method
|
||||
authtype = GetAuthTypeFromPack(p);
|
||||
|
||||
if (1)
|
||||
{
|
||||
// Log
|
||||
@ -1622,12 +1650,15 @@ bool ServerAccept(CONNECTION *c)
|
||||
case CLIENT_AUTHTYPE_CERT:
|
||||
authtype_str = _UU("LH_AUTH_CERT");
|
||||
break;
|
||||
case AUTHTYPE_TICKET:
|
||||
authtype_str = _UU("LH_AUTH_TICKET");
|
||||
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);
|
||||
@ -1640,7 +1671,6 @@ bool ServerAccept(CONNECTION *c)
|
||||
|
||||
// Attempt an anonymous authentication first
|
||||
auth_ret = SamAuthUserByAnonymous(hub, username);
|
||||
|
||||
if (auth_ret)
|
||||
{
|
||||
if (c->IsInProc)
|
||||
@ -1734,8 +1764,6 @@ bool ServerAccept(CONNECTION *c)
|
||||
|
||||
if (auth_ret)
|
||||
{
|
||||
// User authentication success by anonymous authentication
|
||||
HLog(hub, "LH_AUTH_OK", c->Name, username);
|
||||
is_empty_password = true;
|
||||
}
|
||||
}
|
||||
@ -1961,6 +1989,24 @@ bool ServerAccept(CONNECTION *c)
|
||||
}
|
||||
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.
|
||||
@ -2014,25 +2060,14 @@ bool ServerAccept(CONNECTION *c)
|
||||
error_detail = "ERR_AUTHTYPE_NOT_SUPPORTED";
|
||||
goto CLEANUP;
|
||||
}
|
||||
|
||||
if (auth_ret == false)
|
||||
{
|
||||
// Get client IP to feed tools such as Fail2Ban
|
||||
char ip[64];
|
||||
IPToStr(ip, sizeof(ip), &c->FirstSock->RemoteIP);
|
||||
// Authentication failure
|
||||
HLog(hub, "LH_AUTH_NG", c->Name, username, ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Authentication success
|
||||
HLog(hub, "LH_AUTH_OK", c->Name, username);
|
||||
}
|
||||
}
|
||||
|
||||
if (auth_ret == false)
|
||||
{
|
||||
// Authentication failure
|
||||
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);
|
||||
@ -2046,13 +2081,12 @@ bool ServerAccept(CONNECTION *c)
|
||||
}
|
||||
else
|
||||
{
|
||||
if(is_empty_password)
|
||||
if (is_empty_password)
|
||||
{
|
||||
SOCK *s = c->FirstSock;
|
||||
const SOCK *s = c->FirstSock;
|
||||
if (s != NULL && s->RemoteIP.addr[0] != 127)
|
||||
{
|
||||
if(StrCmpi(username, ADMINISTRATOR_USERNAME) == 0 ||
|
||||
GetHubAdminOption(hub, "deny_empty_password") != 0)
|
||||
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);
|
||||
@ -2066,6 +2100,8 @@ bool ServerAccept(CONNECTION *c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HLog(hub, "LH_AUTH_OK", c->Name, username);
|
||||
}
|
||||
|
||||
policy = NULL;
|
||||
@ -6592,6 +6628,24 @@ PACK *PackLoginWithPlainPassword(char *hubname, char *username, void *plain_pass
|
||||
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)
|
||||
{
|
||||
|
@ -134,6 +134,7 @@ PACK *PackLoginWithAnonymous(char *hubname, char *username);
|
||||
PACK *PackLoginWithPassword(char *hubname, char *username, void *secure_password);
|
||||
PACK *PackLoginWithPlainPassword(char *hubname, char *username, void *plain_password);
|
||||
PACK *PackLoginWithCert(char *hubname, char *username, X *x, void *sign, UINT sign_size);
|
||||
PACK *PackLoginWithWireGuardKey(char *key);
|
||||
PACK *PackLoginWithOpenVPNCertificate(char *hubname, char *username, X *x);
|
||||
bool GetMethodFromPack(PACK *p, char *method, UINT size);
|
||||
bool GetHubnameAndUsernameFromPack(PACK *p, char *username, UINT username_size,
|
||||
|
@ -402,6 +402,11 @@ void SiCheckDeadLockMain(SERVER *s, UINT timeout)
|
||||
CheckDeadLock(cedar->CaList->lock, timeout, "cedar->CaList->lock");
|
||||
}
|
||||
|
||||
if (cedar->WgkList != NULL)
|
||||
{
|
||||
CheckDeadLock(cedar->WgkList->lock, timeout, "cedar->WgkList->lock");
|
||||
}
|
||||
|
||||
if (cedar->TrafficLock != NULL)
|
||||
{
|
||||
CheckDeadLock(cedar->TrafficLock, timeout, "cedar->TrafficLock");
|
||||
@ -2677,16 +2682,13 @@ bool SiIsAzureSupported(SERVER *s)
|
||||
// Read the server settings from the CFG
|
||||
bool SiLoadConfigurationCfg(SERVER *s, FOLDER *root)
|
||||
{
|
||||
FOLDER *f1, *f2, *f3, *f4, *f5, *f6, *f7, *f8, *f;
|
||||
FOLDER *f1, *f2, *f3, *f4, *f5, *f6, *f7, *f8, *f9;
|
||||
// Validate arguments
|
||||
if (s == NULL || root == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
f = NULL;
|
||||
|
||||
|
||||
f1 = CfgGetFolder(root, "ServerConfiguration");
|
||||
f2 = CfgGetFolder(root, "VirtualHUB");
|
||||
f3 = CfgGetFolder(root, "ListenerList");
|
||||
@ -2695,6 +2697,7 @@ bool SiLoadConfigurationCfg(SERVER *s, FOLDER *root)
|
||||
f6 = CfgGetFolder(root, "LicenseManager");
|
||||
f7 = CfgGetFolder(root, "IPsec");
|
||||
f8 = CfgGetFolder(root, "DDnsClient");
|
||||
f9 = CfgGetFolder(root, "WireGuardKeyList");
|
||||
|
||||
if (f1 == NULL)
|
||||
{
|
||||
@ -2736,6 +2739,30 @@ bool SiLoadConfigurationCfg(SERVER *s, FOLDER *root)
|
||||
|
||||
if (s->ServerType != SERVER_TYPE_FARM_MEMBER)
|
||||
{
|
||||
TOKEN_LIST *t = CfgEnumFolderToTokenList(f9);
|
||||
if (t != NULL)
|
||||
{
|
||||
LockList(s->Cedar->WgkList);
|
||||
{
|
||||
UINT i;
|
||||
for (i = 0; i < t->NumTokens; ++i)
|
||||
{
|
||||
const char *name = t->Token[i];
|
||||
FOLDER *f = CfgGetFolder(f9, name);
|
||||
if (f != NULL)
|
||||
{
|
||||
WGK *wgk = Malloc(sizeof(WGK));
|
||||
StrCpy(wgk->Key, sizeof(wgk->Key), name);
|
||||
CfgGetStr(f, "Hub", wgk->Hub, sizeof(wgk->Hub));
|
||||
CfgGetStr(f, "User", wgk->User, sizeof(wgk->User));
|
||||
Add(s->Cedar->WgkList, wgk);
|
||||
}
|
||||
}
|
||||
}
|
||||
UnlockList(s->Cedar->WgkList);
|
||||
FreeToken(t);
|
||||
}
|
||||
|
||||
SiLoadHubs(s, f2);
|
||||
}
|
||||
|
||||
@ -3102,9 +3129,28 @@ FOLDER *SiWriteConfigurationToCfg(SERVER *s)
|
||||
|
||||
SiWriteServerCfg(CfgCreateFolder(root, "ServerConfiguration"), s);
|
||||
|
||||
|
||||
if (s->UpdatedServerType != SERVER_TYPE_FARM_MEMBER)
|
||||
{
|
||||
FOLDER *f = CfgCreateFolder(root, "WireGuardKeyList");
|
||||
if (f != NULL)
|
||||
{
|
||||
LockList(s->Cedar->WgkList);
|
||||
{
|
||||
UINT i;
|
||||
for (i = 0; i < LIST_NUM(s->Cedar->WgkList); ++i)
|
||||
{
|
||||
WGK *wgk = LIST_DATA(s->Cedar->WgkList, i);
|
||||
FOLDER *ff = CfgCreateFolder(f, wgk->Key);
|
||||
if (ff != NULL)
|
||||
{
|
||||
CfgAddStr(ff, "Hub", wgk->Hub);
|
||||
CfgAddStr(ff, "User", wgk->User);
|
||||
}
|
||||
}
|
||||
}
|
||||
UnlockList(s->Cedar->WgkList);
|
||||
}
|
||||
|
||||
SiWriteHubs(CfgCreateFolder(root, "VirtualHUB"), s);
|
||||
}
|
||||
|
||||
@ -10301,6 +10347,27 @@ int CompareHubList(void *p1, void *p2)
|
||||
return StrCmpi(h1->Name, h2->Name);
|
||||
}
|
||||
|
||||
// Search in WireGuard key list
|
||||
int CompareWgk(void *p1, void *p2)
|
||||
{
|
||||
WGK *wgk_1, *wgk_2;
|
||||
|
||||
if (p1 == NULL || p2 == NULL)
|
||||
{
|
||||
return (p1 == NULL && p2 == NULL ? 0 : (p1 == NULL ? -1 : 1));
|
||||
}
|
||||
|
||||
wgk_1 = *(WGK **)p1;
|
||||
wgk_2 = *(WGK **)p2;
|
||||
|
||||
if (wgk_1 == NULL || wgk_2 == NULL)
|
||||
{
|
||||
return (wgk_1 == NULL && wgk_2 == NULL ? 0 : (wgk_1 == NULL ? -1 : 1));
|
||||
}
|
||||
|
||||
return StrCmp(wgk_1->Key, wgk_2->Key);
|
||||
}
|
||||
|
||||
// Connection thread to the controller
|
||||
void SiConnectToControllerThread(THREAD *thread, void *param)
|
||||
{
|
||||
|
@ -150,6 +150,14 @@ struct OPENVPN_SSTP_CONFIG
|
||||
bool EnableSSTP; // SSTP is enabled
|
||||
};
|
||||
|
||||
// WireGuard key structure
|
||||
struct WGK
|
||||
{
|
||||
char Key[WG_KEY_BASE64_SIZE];
|
||||
char Hub[MAX_HUBNAME_LEN + 1];
|
||||
char User[MAX_USERNAME_LEN + 1];
|
||||
};
|
||||
|
||||
// Server object
|
||||
struct SERVER
|
||||
{
|
||||
@ -631,6 +639,8 @@ void SiUpdateCurrentRegion(CEDAR *c, char *region, bool force_update);
|
||||
void SiGetCurrentRegion(CEDAR *c, char *region, UINT region_size);
|
||||
bool SiIsEnterpriseFunctionsRestrictedOnOpenSource(CEDAR *c);
|
||||
|
||||
int CompareWgk(void *p1, void *p2);
|
||||
|
||||
#endif // SERVER_H
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user