1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2026-05-22 13:30:14 +03:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Alexey Kryuchkov 54448f7f13 Merge a366bdbf02 into 4fe5352931 2024-06-11 02:22:08 -04:00
Alexey Kryuchkov a366bdbf02 Add server option 'JsonRpcWebApiAllowedSubnet' to restrict access to JSON-RPC API based on client IP address 2023-06-02 00:00:43 +03:00
12 changed files with 112 additions and 79 deletions
@@ -30,6 +30,7 @@
<ul>
<li>Older versions of SoftEther VPN before June 2019 don't support JSON-RPC APIs.</li>
<li>If you want to completely disable the JSON-RPC on your VPN Server, set the <code>DisableJsonRpcWebApi</code> variable to <code>true</code> on the <code>vpn_server.config</code>.</li>
<li>You may also restrict access to JSON-RPC API to a specific subnet, e.g. your internal network, by setting the <code>JsonRpcWebApiAllowedSubnet</code> variable to, for example, <code>192.168.0.0/16</code>.</li>
</ul>
<h3 id="json-rpc-specification">JSON-RPC specification</h3>
<p>You must use HTTPS 1.1 <code>POST</code> method to call each of JSON-RPC APIs.<br />
@@ -25,6 +25,7 @@ https://<vpn_server_hostname>:<port>/api/
- Older versions of SoftEther VPN before June 2019 don't support JSON-RPC APIs.
- If you want to completely disable the JSON-RPC on your VPN Server, set the `DisableJsonRpcWebApi` variable to `true` on the `vpn_server.config`.
- You may also restrict access to JSON-RPC API to a specific subnet, e.g. your internal network, by setting the `JsonRpcWebApiAllowedSubnet` variable to, for example, `192.168.0.0/16`.
### JSON-RPC specification
@@ -65,23 +65,12 @@
}
},
"braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"requires": {
"fill-range": "^7.1.1"
},
"dependencies": {
"fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
}
"fill-range": "^7.0.1"
}
},
"builtin-modules": {
@@ -162,6 +151,15 @@
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
"dev": true
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -25,6 +25,7 @@ https://<vpn_server_hostname>:<port>/api/
- Older versions of SoftEther VPN before June 2019 don't support JSON-RPC APIs.
- If you want to completely disable the JSON-RPC on your VPN Server, set the `DisableJsonRpcWebApi` variable to `true` on the `vpn_server.config`.
- You may also restrict access to JSON-RPC API to a specific subnet, e.g. your internal network, by setting the `JsonRpcWebApiAllowedSubnet` variable to, for example, `192.168.0.0/16`.
### JSON-RPC specification
+30 -4
View File
@@ -463,13 +463,39 @@ void ProcIPsecEspPacketRecv(IKE_SERVER *ike, UDPPACKET *p)
seq = READ_UINT(src + sizeof(UINT));
// Search and retrieve the IPsec SA from SPI
// thank to @phillibert report, responding to bad SA might lead to amplification
// according to RFC4303 we should drop such packets
ipsec_sa = SearchClientToServerIPsecSaBySpi(ike, spi);
if (ipsec_sa == NULL)
{
// Invalid SPI
UINT64 init_cookie = Rand64();
UINT64 resp_cookie = 0;
IKE_CLIENT *c = NULL;
IKE_CLIENT t;
Copy(&t.ClientIP, &p->SrcIP, sizeof(IP));
t.ClientPort = p->SrcPort;
Copy(&t.ServerIP, &p->DstIP, sizeof(IP));
t.ServerPort = p->DestPort;
t.CurrentIkeSa = NULL;
if (p->DestPort == IPSEC_PORT_IPSEC_ESP_RAW)
{
t.ClientPort = t.ServerPort = IPSEC_PORT_IPSEC_ISAKMP;
}
c = Search(ike->ClientList, &t);
if (c != NULL && c->CurrentIkeSa != NULL)
{
init_cookie = c->CurrentIkeSa->InitiatorCookie;
resp_cookie = c->CurrentIkeSa->ResponderCookie;
}
SendInformationalExchangePacketEx(ike, (c == NULL ? &t : c), IkeNewNoticeErrorInvalidSpiPayload(spi), false,
init_cookie, resp_cookie);
SendDeleteIPsecSaPacket(ike, (c == NULL ? &t : c), spi);
return;
}
+14 -4
View File
@@ -5740,6 +5740,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
UINT num = 0, max = 19;
SERVER *server;
char *vpn_http_target = HTTP_VPN_TARGET2;
bool disableJsonRpcWebApi;
// Validate arguments
if (c == NULL)
{
@@ -5750,6 +5751,15 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
s = c->FirstSock;
disableJsonRpcWebApi = server->DisableJsonRpcWebApi;
if (!disableJsonRpcWebApi && !IsZeroIP(&server->JsonRpcWebApiAllowedSubnetAddr)
&& !IsZeroIP(&server->JsonRpcWebApiAllowedSubnetMask)) {
// restrict JSON-RPC Web API to specified subnet only
if (!IsInSameNetwork(&s->RemoteIP, &server->JsonRpcWebApiAllowedSubnetAddr, &server->JsonRpcWebApiAllowedSubnetMask)) {
disableJsonRpcWebApi = true;
}
}
while (true)
{
bool not_found_error = false;
@@ -5782,7 +5792,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
// Receive the data since it's POST
data_size = GetContentLength(h);
if (server->DisableJsonRpcWebApi == false)
if (disableJsonRpcWebApi == false)
{
if (StrCmpi(h->Target, "/api") == 0 || StrCmpi(h->Target, "/api/") == 0)
{
@@ -5868,7 +5878,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
}
else if (StrCmpi(h->Method, "OPTIONS") == 0)
{
if (server->DisableJsonRpcWebApi == false)
if (disableJsonRpcWebApi == false)
{
if (StrCmpi(h->Target, "/api") == 0 || StrCmpi(h->Target, "/api/") == 0 || StartWith(h->Target, "/admin"))
{
@@ -5939,7 +5949,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
BUF *b = NULL;
*error_detail_str = "HTTP_ROOT";
if (server->DisableJsonRpcWebApi == false)
if (disableJsonRpcWebApi == false)
{
b = ReadDump("|wwwroot/index.html");
}
@@ -6019,7 +6029,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
if (b == false)
{
if (server->DisableJsonRpcWebApi == false)
if (disableJsonRpcWebApi == false)
{
if (StartWith(h->Target, "/api?") || StartWith(h->Target, "/api/") || StrCmpi(h->Target, "/api") == 0)
{
+15
View File
@@ -30,6 +30,7 @@
#include "Mayaqua/Internat.h"
#include "Mayaqua/Memory.h"
#include "Mayaqua/Microsoft.h"
#include "Mayaqua/Network.h"
#include "Mayaqua/Object.h"
#include "Mayaqua/OS.h"
#include "Mayaqua/Pack.h"
@@ -6032,6 +6033,15 @@ void SiLoadServerCfg(SERVER *s, FOLDER *f)
// Disable JSON-RPC Web API
s->DisableJsonRpcWebApi = CfgGetBool(f, "DisableJsonRpcWebApi");
char tmpaddr[MAX_PATH];
if (CfgGetStr(f, "JsonRpcWebApiAllowedSubnet", tmpaddr, sizeof(tmpaddr))) {
IP _subnet, _mask;
if (ParseIpAndMask46(tmpaddr, &_subnet, &_mask)) {
s->JsonRpcWebApiAllowedSubnetAddr = _subnet;
s->JsonRpcWebApiAllowedSubnetMask = _mask;
}
}
// Bits of Diffie-Hellman parameters
c->DhParamBits = CfgGetInt(f, "DhParamBits");
if (c->DhParamBits == 0)
@@ -6365,6 +6375,11 @@ void SiWriteServerCfg(FOLDER *f, SERVER *s)
// Disable JSON-RPC Web API
CfgAddBool(f, "DisableJsonRpcWebApi", s->DisableJsonRpcWebApi);
char tmpaddr[MAX_PATH];
IPAndMaskToStr(tmpaddr, sizeof(tmpaddr),
&s->JsonRpcWebApiAllowedSubnetAddr, &s->JsonRpcWebApiAllowedSubnetMask);
CfgAddStr(f, "JsonRpcWebApiAllowedSubnet", tmpaddr);
}
Unlock(c->lock);
}
+3
View File
@@ -276,6 +276,9 @@ struct SERVER
IP ListenIP; // Listen IP
bool StrictSyslogDatetimeFormat; // Make syslog datetime format strict RFC3164
bool DisableJsonRpcWebApi; // Disable JSON-RPC Web API
IP JsonRpcWebApiAllowedSubnetAddr; // If set, allow access to JSON-RPC Web API from
IP JsonRpcWebApiAllowedSubnetMask; // this subnet only
};
-8
View File
@@ -88,7 +88,6 @@ int ssl_clientcert_index = 0;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static OSSL_PROVIDER *ossl_provider_legacy = NULL;
static OSSL_PROVIDER *ossl_provider_default = NULL;
static OSSL_PROVIDER *ossl_provider_oqsprovider = NULL;
#endif
LOCK **ssl_lock_obj = NULL;
@@ -3975,12 +3974,6 @@ void FreeCryptLibrary()
OSSL_PROVIDER_unload(ossl_provider_legacy);
ossl_provider_legacy = NULL;
}
if (ossl_provider_oqsprovider != NULL)
{
OSSL_PROVIDER_unload(ossl_provider_oqsprovider);
ossl_provider_oqsprovider = NULL;
}
#endif
}
@@ -4003,7 +3996,6 @@ void InitCryptLibrary()
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
ossl_provider_default = OSSL_PROVIDER_load(NULL, "legacy");
ossl_provider_legacy = OSSL_PROVIDER_load(NULL, "default");
ossl_provider_oqsprovider = OSSL_PROVIDER_load(NULL, "oqsprovider");
#endif
ssl_clientcert_index = SSL_get_ex_new_index(0, "struct SslClientCertInfo *", NULL, NULL, NULL);
+20 -29
View File
@@ -6993,6 +6993,18 @@ void IPToStr6Inner(char *str, IP *ip)
}
}
// Format IP and subnet mask as "<ip>/<masksize>"
void IPAndMaskToStr(char *str, UINT size, IP *ip, IP *subnet)
{
int iplen;
UINT masksize;
IPToStr(str, size, ip);
iplen = StrLen(str);
masksize = SubnetMaskToInt(subnet);
Format(str + iplen, size - iplen, "/%d", masksize);
}
// Convert the string to an IP address
bool StrToIP6(IP *ip, char *str)
{
@@ -11905,10 +11917,6 @@ bool StartSSLEx3(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char
Unlock(openssl_lock);
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
SSL_set1_groups_list(sock->ssl, PQ_GROUP_LIST);
#endif
if (sock->ServerMode)
{
// Lock(ssl_connect_lock);
@@ -11988,7 +11996,7 @@ bool StartSSLEx3(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char
// Unlock(ssl_connect_lock);
}
else
{
{
prev_timeout = GetTimeout(sock);
SetTimeout(sock, ssl_timeout);
// Client mode
@@ -12289,7 +12297,6 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__);
return 0;
}
ERR_clear_error();
ret = SSL_peek(ssl, &c, sizeof(c));
}
Unlock(sock->ssl_lock);
@@ -12321,11 +12328,9 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
#endif
)
{
UINT ssl_err_no;
while (ssl_err_no = ERR_get_error()){
Debug("%s %u SSL_ERROR_SSL on ASYNC socket !!! ssl_err_no = %u: '%s'\n", __FILE__, __LINE__, ssl_err_no, ERR_error_string(ssl_err_no, NULL));
};
UINT ssl_err_no = ERR_get_error();
Debug("%s %u SSL_ERROR_SSL on ASYNC socket !!! ssl_err_no = %u: '%s'\n", __FILE__, __LINE__, ssl_err_no, ERR_error_string(ssl_err_no, NULL));
Disconnect(sock);
return 0;
}
@@ -12357,7 +12362,6 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
ttparam = NewSocketTimeout(sock);
#endif // UNIX_SOLARIS
ERR_clear_error();
ret = SSL_read(ssl, data, size);
// Stop the timeout thread
@@ -12428,11 +12432,9 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
#endif
)
{
UINT ssl_err_no;
while (ssl_err_no = ERR_get_error()) {
Debug("%s %u SSL_ERROR_SSL on ASYNC socket !!! ssl_err_no = %u: '%s'\n", __FILE__, __LINE__, ssl_err_no, ERR_error_string(ssl_err_no, NULL));
};
UINT ssl_err_no = ERR_get_error();
Debug("%s %u SSL_ERROR_SSL on ASYNC socket !!! ssl_err_no = %u: '%s'\n", __FILE__, __LINE__, ssl_err_no, ERR_error_string(ssl_err_no, NULL));
Disconnect(sock);
return 0;
}
@@ -12441,8 +12443,8 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
return SOCK_LATER;
}
}
Debug("%s %u e=%u SecureRecv() Disconnect\n", __FILE__, __LINE__, e);
Disconnect(sock);
Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__);
return 0;
}
@@ -12469,7 +12471,6 @@ UINT SecureSend(SOCK *sock, void *data, UINT size)
return 0;
}
ERR_clear_error();
ret = SSL_write(ssl, data, size);
#if OPENSSL_VERSION_NUMBER < 0x30000000L
if (ret < 0) // OpenSSL version < 3.0.0
@@ -12513,22 +12514,12 @@ UINT SecureSend(SOCK *sock, void *data, UINT size)
// Confirmation of the error value
if (e == SSL_ERROR_WANT_READ || e == SSL_ERROR_WANT_WRITE || e == SSL_ERROR_SSL)
{
if (e == SSL_ERROR_SSL)
{
UINT ssl_err_no;
while (ssl_err_no = ERR_get_error()) {
Debug("%s %u SSL_ERROR_SSL on ASYNC socket !!! ssl_err_no = %u: '%s'\n", __FILE__, __LINE__, ssl_err_no, ERR_error_string(ssl_err_no, NULL));
};
Disconnect(sock);
return 0;
}
sock->WriteBlocked = true;
return SOCK_LATER;
}
Debug("%s %u e=%u\n", __FILE__, __LINE__, e);
}
Debug("%s %u e=%u SecureSend() Disconnect\n", __FILE__, __LINE__, e);
//Debug("%s %u SecureSend() Disconnect\n", __FILE__, __LINE__);
Disconnect(sock);
return 0;
}
+1 -4
View File
@@ -59,10 +59,6 @@ struct DYN_VALUE
#define DEFAULT_CIPHER_LIST "ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:ECDHE+AES256:DHE+AES256:RSA+AES"
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#define PQ_GROUP_LIST "p521_kyber1024:x25519_kyber768:P-521:X25519:P-256"
#endif
// SSL logging function
//#define ENABLE_SSL_LOGGING
#define SSL_LOGGING_DIRNAME "@ssl_log"
@@ -1293,6 +1289,7 @@ void IPToStr6(char *str, UINT size, IP *ip);
void IP6AddrToStr(char *str, UINT size, IPV6_ADDR *addr);
void IPToStr6Array(char *str, UINT size, UCHAR *bytes);
void IPToStr6Inner(char *str, IP *ip);
void IPAndMaskToStr(char *str, UINT size, IP *ip, IP *subnet);
void IntToSubnetMask6(IP *ip, UINT i);
void IPAnd6(IP *dst, IP *a, IP *b);
void GetAllRouterMulticastAddress6(IP *ip);
+13 -15
View File
@@ -373,23 +373,12 @@
}
},
"braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"requires": {
"fill-range": "^7.1.1"
},
"dependencies": {
"fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
}
"fill-range": "^7.0.1"
}
},
"browserslist": {
@@ -614,6 +603,15 @@
"integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
"dev": true
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",