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:
@ -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)
|
||||
{
|
||||
|
Reference in New Issue
Block a user