mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2025-07-08 00:34:57 +03:00
OpenVPN client certificate authentication (Individual Certificate Authentication) (#327)
* Implement OpenVPN certificate authentication, fixes #55 * fixup! Implement OpenVPN certificate authentication, fixes #55
This commit is contained in:
committed by
Moataz Elmasry
parent
863f87fac1
commit
83295bb736
@ -448,6 +448,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_OPENVPN_CERT 98 // TLS client certificate authentication
|
||||
#define AUTHTYPE_TICKET 99 // Ticket authentication
|
||||
|
||||
// Constant of the client side
|
||||
|
@ -170,7 +170,7 @@ void EtherIPIpcConnectThread(THREAD *t, void *p)
|
||||
&s->ClientIP, s->ClientPort,
|
||||
&s->ServerIP, s->ServerPort,
|
||||
tmp,
|
||||
s->CryptName, true, mss, NULL);
|
||||
s->CryptName, true, mss, NULL, NULL);
|
||||
|
||||
if (ipc != NULL)
|
||||
{
|
||||
|
@ -323,7 +323,7 @@ IPC *NewIPCByParam(CEDAR *cedar, IPC_PARAM *param, UINT *error_code)
|
||||
param->UserName, param->Password, error_code, ¶m->ClientIp,
|
||||
param->ClientPort, ¶m->ServerIp, param->ServerPort,
|
||||
param->ClientHostname, param->CryptName,
|
||||
param->BridgeMode, param->Mss, NULL);
|
||||
param->BridgeMode, param->Mss, NULL, param->ClientCertificate);
|
||||
|
||||
return ipc;
|
||||
}
|
||||
@ -332,7 +332,7 @@ IPC *NewIPCByParam(CEDAR *cedar, IPC_PARAM *param, UINT *error_code)
|
||||
IPC *NewIPC(CEDAR *cedar, char *client_name, char *postfix, char *hubname, char *username, char *password,
|
||||
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)
|
||||
bool bridge_mode, UINT mss, EAP_CLIENT *eap_client, X *client_certificate)
|
||||
{
|
||||
IPC *ipc;
|
||||
UINT dummy_int = 0;
|
||||
@ -425,7 +425,14 @@ IPC *NewIPC(CEDAR *cedar, char *client_name, char *postfix, char *hubname, char
|
||||
FreePack(p);
|
||||
|
||||
// Upload the authentication data
|
||||
p = PackLoginWithPlainPassword(hubname, username, password);
|
||||
if (client_certificate != NULL)
|
||||
{
|
||||
p = PackLoginWithOpenVPNCertificate(hubname, username, client_certificate);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = PackLoginWithPlainPassword(hubname, username, password);
|
||||
}
|
||||
PackAddStr(p, "hello", client_name);
|
||||
PackAddInt(p, "client_ver", cedar->Version);
|
||||
PackAddInt(p, "client_build", cedar->Build);
|
||||
|
@ -165,6 +165,7 @@ struct IPC_PARAM
|
||||
UINT Mss;
|
||||
bool IsL3Mode;
|
||||
bool IsOpenVPN;
|
||||
X *ClientCertificate;
|
||||
};
|
||||
|
||||
// IPC_ASYNC object
|
||||
@ -224,7 +225,7 @@ struct IPC_MSCHAP_V2_AUTHINFO
|
||||
IPC *NewIPC(CEDAR *cedar, char *client_name, char *postfix, char *hubname, char *username, char *password,
|
||||
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);
|
||||
bool bridge_mode, UINT mss, EAP_CLIENT *eap_client, X *client_certificate);
|
||||
IPC *NewIPCByParam(CEDAR *cedar, IPC_PARAM *param, UINT *error_code);
|
||||
IPC *NewIPCBySock(CEDAR *cedar, SOCK *s, void *mac_address);
|
||||
void FreeIPC(IPC *ipc);
|
||||
|
@ -1018,7 +1018,7 @@ PPP_PACKET *PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *req)
|
||||
// Attempt to connect with IPC
|
||||
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, hub, id, password,
|
||||
&error_code, &p->ClientIP, p->ClientPort, &p->ServerIP, p->ServerPort,
|
||||
p->ClientHostname, p->CryptName, false, p->AdjustMss, p->EapClient);
|
||||
p->ClientHostname, p->CryptName, false, p->AdjustMss, p->EapClient, NULL);
|
||||
|
||||
if (ipc != NULL)
|
||||
{
|
||||
@ -1151,7 +1151,7 @@ PPP_PACKET *PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *req)
|
||||
|
||||
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, hub, id, password,
|
||||
&error_code, &p->ClientIP, p->ClientPort, &p->ServerIP, p->ServerPort,
|
||||
p->ClientHostname, p->CryptName, false, p->AdjustMss, NULL);
|
||||
p->ClientHostname, p->CryptName, false, p->AdjustMss, NULL, NULL);
|
||||
|
||||
if (ipc != NULL)
|
||||
{
|
||||
|
@ -442,7 +442,8 @@ void OvsProcessRecvControlPacket(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN
|
||||
// Create an SSL pipe
|
||||
Lock(s->Cedar->lock);
|
||||
{
|
||||
c->SslPipe = NewSslPipe(true, s->Cedar->ServerX, s->Cedar->ServerK, s->Dh);
|
||||
bool cert_verify = true;
|
||||
c->SslPipe = NewSslPipeEx(true, s->Cedar->ServerX, s->Cedar->ServerK, s->Dh, cert_verify, &c->ClientCert);
|
||||
}
|
||||
Unlock(s->Cedar->lock);
|
||||
|
||||
@ -712,6 +713,11 @@ void OvsBeginIPCAsyncConnectionIfEmpty(OPENVPN_SERVER *s, OPENVPN_SESSION *se, O
|
||||
p.BridgeMode = true;
|
||||
}
|
||||
|
||||
if (c->ClientCert.X != NULL)
|
||||
{
|
||||
p.ClientCertificate = c->ClientCert.X;
|
||||
}
|
||||
|
||||
p.IsOpenVPN = true;
|
||||
|
||||
// Calculate the MSS
|
||||
@ -780,6 +786,26 @@ void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_C
|
||||
|
||||
OvsLog(s, se, c, "LO_OPTION_STR_RECV", data->OptionString);
|
||||
|
||||
if (c->ClientCert.X != NULL)
|
||||
{
|
||||
if (c->ClientCert.X->subject_name != NULL)
|
||||
{
|
||||
OvsLog(s, se, c, "LO_CLIENT_CERT", c->ClientCert.X->subject_name->CommonName);
|
||||
}
|
||||
else
|
||||
{
|
||||
OvsLog(s, se, c, "LO_CLIENT_CERT", "(unknown CN)");
|
||||
}
|
||||
}
|
||||
else if (!c->ClientCert.PreverifyErr)
|
||||
{
|
||||
OvsLog(s, se, c, "LO_CLIENT_NO_CERT");
|
||||
}
|
||||
else
|
||||
{
|
||||
OvsLog(s, se, c, "LO_CLIENT_UNVERIFIED_CERT", c->ClientCert.PreverifyErrMessage);
|
||||
}
|
||||
|
||||
Zero(opt_str, sizeof(opt_str));
|
||||
StrCpy(opt_str, sizeof(opt_str), data->OptionString);
|
||||
if (s->Cedar != NULL && (IsEmptyStr(opt_str) || StartWith(opt_str, "V0 UNDEF") || InStr(opt_str, ",") == false))
|
||||
@ -1359,6 +1385,11 @@ void OvsFreeChannel(OPENVPN_CHANNEL *c)
|
||||
FreeMd(c->MdRecv);
|
||||
FreeMd(c->MdSend);
|
||||
|
||||
if (c->ClientCert.X != NULL)
|
||||
{
|
||||
FreeX(c->ClientCert.X);
|
||||
}
|
||||
|
||||
Free(c);
|
||||
}
|
||||
|
||||
|
@ -257,6 +257,7 @@ struct OPENVPN_CHANNEL
|
||||
bool IsInitiatorServer; // Whether the channel was started from the server side
|
||||
bool RekeyInitiated; // Whether re-keying has already started
|
||||
UINT64 NextRekey;
|
||||
struct SslClientCertInfo ClientCert; // Client certificate and verification data
|
||||
};
|
||||
|
||||
// OpenVPN session
|
||||
|
@ -1795,6 +1795,9 @@ bool ServerAccept(CONNECTION *c)
|
||||
case AUTHTYPE_TICKET:
|
||||
authtype_str = _UU("LH_AUTH_TICKET");
|
||||
break;
|
||||
case AUTHTYPE_OPENVPN_CERT:
|
||||
authtype_str = _UU("LH_AUTH_OPENVPN_CERT");
|
||||
break;
|
||||
}
|
||||
IPToStr(ip1, sizeof(ip1), &c->FirstSock->RemoteIP);
|
||||
IPToStr(ip2, sizeof(ip2), &c->FirstSock->LocalIP);
|
||||
@ -2128,6 +2131,50 @@ bool ServerAccept(CONNECTION *c)
|
||||
}
|
||||
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);
|
||||
@ -7247,6 +7294,45 @@ PACK *PackLoginWithPlainPassword(char *hubname, char *username, void *plain_pass
|
||||
return p;
|
||||
}
|
||||
|
||||
// Generate a packet of OpenVPN certificate login
|
||||
PACK *PackLoginWithOpenVPNCertificate(char *hubname, char *username, X *x)
|
||||
{
|
||||
PACK *p;
|
||||
// Validate arguments
|
||||
if (hubname == NULL || username == NULL || x == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = NewPack();
|
||||
PackAddStr(p, "method", "login");
|
||||
PackAddStr(p, "hubname", hubname);
|
||||
|
||||
char cn_username[128];
|
||||
if (IsEmptyStr(username))
|
||||
{
|
||||
if (x->subject_name == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
wcstombs(cn_username, x->subject_name->CommonName, 127);
|
||||
cn_username[127] = '\0';
|
||||
PackAddStr(p, "username", cn_username);
|
||||
}
|
||||
else
|
||||
{
|
||||
PackAddStr(p, "username", username);
|
||||
}
|
||||
|
||||
PackAddInt(p, "authtype", AUTHTYPE_OPENVPN_CERT);
|
||||
|
||||
BUF *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)
|
||||
{
|
||||
|
@ -242,6 +242,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 *PackLoginWithOpenVPNCertificate(char *hubname, char *username, X *x);
|
||||
bool GetMethodFromPack(PACK *p, char *method, UINT size);
|
||||
bool GetHubnameAndUsernameFromPack(PACK *p, char *username, UINT username_size,
|
||||
char *hubname, UINT hubname_size);
|
||||
|
Reference in New Issue
Block a user