1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-25 02:49:52 +03:00

Compare commits

...

6 Commits

Author SHA1 Message Date
Alexey Kryuchkov
64023c87c9
Merge a366bdbf02 into bfaff4fdb0 2024-05-27 20:41:19 -04:00
Ilya Shipitsin
bfaff4fdb0
Merge pull request #1994 from hiura2023/master
Fix Virtual DHCP Server: Correct IP reassignment
2024-05-27 13:13:40 +02:00
hiura
08213b7f0e CHANGE ERROR HANDLER FOR SSL ERROR: Change of indent 2024-05-26 23:50:05 +09:00
hiura
98852b77d9 CHANGE ERROR HANDLER FOR SSL ERROR: 2024-05-26 23:36:21 +09:00
hiura
5a88b34ddb Fix Virtual DHCP Server: Correct IP reassignment 2024-05-08 10:55:00 +09: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
9 changed files with 116 additions and 57 deletions

View File

@ -30,6 +30,7 @@
<ul> <ul>
<li>Older versions of SoftEther VPN before June 2019 don't support JSON-RPC APIs.</li> <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>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> </ul>
<h3 id="json-rpc-specification">JSON-RPC specification</h3> <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 /> <p>You must use HTTPS 1.1 <code>POST</code> method to call each of JSON-RPC APIs.<br />

View File

@ -25,6 +25,7 @@ https://<vpn_server_hostname>:<port>/api/
- Older versions of SoftEther VPN before June 2019 don't support JSON-RPC APIs. - 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`. - 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 ### JSON-RPC specification

View File

@ -25,6 +25,7 @@ https://<vpn_server_hostname>:<port>/api/
- Older versions of SoftEther VPN before June 2019 don't support JSON-RPC APIs. - 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`. - 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 ### JSON-RPC specification

View File

@ -5740,6 +5740,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
UINT num = 0, max = 19; UINT num = 0, max = 19;
SERVER *server; SERVER *server;
char *vpn_http_target = HTTP_VPN_TARGET2; char *vpn_http_target = HTTP_VPN_TARGET2;
bool disableJsonRpcWebApi;
// Validate arguments // Validate arguments
if (c == NULL) if (c == NULL)
{ {
@ -5750,6 +5751,15 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
s = c->FirstSock; 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) while (true)
{ {
bool not_found_error = false; bool not_found_error = false;
@ -5782,7 +5792,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
// Receive the data since it's POST // Receive the data since it's POST
data_size = GetContentLength(h); data_size = GetContentLength(h);
if (server->DisableJsonRpcWebApi == false) if (disableJsonRpcWebApi == false)
{ {
if (StrCmpi(h->Target, "/api") == 0 || StrCmpi(h->Target, "/api/") == 0) 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) 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")) 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; BUF *b = NULL;
*error_detail_str = "HTTP_ROOT"; *error_detail_str = "HTTP_ROOT";
if (server->DisableJsonRpcWebApi == false) if (disableJsonRpcWebApi == false)
{ {
b = ReadDump("|wwwroot/index.html"); b = ReadDump("|wwwroot/index.html");
} }
@ -6019,7 +6029,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
if (b == false) if (b == false)
{ {
if (server->DisableJsonRpcWebApi == false) if (disableJsonRpcWebApi == false)
{ {
if (StartWith(h->Target, "/api?") || StartWith(h->Target, "/api/") || StrCmpi(h->Target, "/api") == 0) if (StartWith(h->Target, "/api?") || StartWith(h->Target, "/api/") || StrCmpi(h->Target, "/api") == 0)
{ {

View File

@ -30,6 +30,7 @@
#include "Mayaqua/Internat.h" #include "Mayaqua/Internat.h"
#include "Mayaqua/Memory.h" #include "Mayaqua/Memory.h"
#include "Mayaqua/Microsoft.h" #include "Mayaqua/Microsoft.h"
#include "Mayaqua/Network.h"
#include "Mayaqua/Object.h" #include "Mayaqua/Object.h"
#include "Mayaqua/OS.h" #include "Mayaqua/OS.h"
#include "Mayaqua/Pack.h" #include "Mayaqua/Pack.h"
@ -6032,6 +6033,15 @@ void SiLoadServerCfg(SERVER *s, FOLDER *f)
// Disable JSON-RPC Web API // Disable JSON-RPC Web API
s->DisableJsonRpcWebApi = CfgGetBool(f, "DisableJsonRpcWebApi"); 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 // Bits of Diffie-Hellman parameters
c->DhParamBits = CfgGetInt(f, "DhParamBits"); c->DhParamBits = CfgGetInt(f, "DhParamBits");
if (c->DhParamBits == 0) if (c->DhParamBits == 0)
@ -6365,6 +6375,11 @@ void SiWriteServerCfg(FOLDER *f, SERVER *s)
// Disable JSON-RPC Web API // Disable JSON-RPC Web API
CfgAddBool(f, "DisableJsonRpcWebApi", s->DisableJsonRpcWebApi); CfgAddBool(f, "DisableJsonRpcWebApi", s->DisableJsonRpcWebApi);
char tmpaddr[MAX_PATH];
IPAndMaskToStr(tmpaddr, sizeof(tmpaddr),
&s->JsonRpcWebApiAllowedSubnetAddr, &s->JsonRpcWebApiAllowedSubnetMask);
CfgAddStr(f, "JsonRpcWebApiAllowedSubnet", tmpaddr);
} }
Unlock(c->lock); Unlock(c->lock);
} }

View File

@ -276,6 +276,9 @@ struct SERVER
IP ListenIP; // Listen IP IP ListenIP; // Listen IP
bool StrictSyslogDatetimeFormat; // Make syslog datetime format strict RFC3164 bool StrictSyslogDatetimeFormat; // Make syslog datetime format strict RFC3164
bool DisableJsonRpcWebApi; // Disable JSON-RPC Web API bool DisableJsonRpcWebApi; // Disable JSON-RPC Web API
IP JsonRpcWebApiAllowedSubnetAddr; // If set, allow access to JSON-RPC Web API from
IP JsonRpcWebApiAllowedSubnetMask; // this subnet only
}; };

View File

@ -9349,62 +9349,35 @@ UINT ServeDhcpDiscoverEx(VH *v, UCHAR *mac, UINT request_ip, bool is_static_ip)
// check whether it is a request from the same MAC address // check whether it is a request from the same MAC address
if (Cmp(mac, d->MacAddress, 6) == 0) if (Cmp(mac, d->MacAddress, 6) == 0)
{ {
// Examine whether the specified IP address is within the range of assignment // Examine whether the specified IP address is within the range of static assignment
if (Endian32(v->DhcpIpStart) > Endian32(request_ip) || if (Endian32(v->DhcpIpStart) > Endian32(request_ip) ||
Endian32(request_ip) > Endian32(v->DhcpIpEnd)) Endian32(request_ip) > Endian32(v->DhcpIpEnd))
{ {
// Accept if within the range // Accept if within the range of static assignment
ret = request_ip; ret = request_ip;
} }
} }
else { else {
// Duplicated IPV4 address found. The DHCP server replies to DHCPREQUEST with DHCP NAK. // Duplicated IPV4 address found. The specified IP address is not available for use
char ipstr[MAX_HOST_NAME_LEN + 1] = { 0 }; char ipstr[MAX_HOST_NAME_LEN + 1] = { 0 };
char macstr[128] = { 0 }; char macstr[128] = { 0 };
IPToStr32(ipstr, sizeof(ipstr), request_ip); IPToStr32(ipstr, sizeof(ipstr), request_ip);
BinToStr(macstr, sizeof(macstr), d->MacAddress, 6); MacToStr(macstr, sizeof(macstr), d->MacAddress);
Debug("Virtual DHC Server: Duplicated IP address detected. Static IP: %s, Used by MAC:%s\n", ipstr, macstr); Debug("Virtual DHC Server: Duplicated IP address detected. Static IP: %s, with the MAC: %s\n", ipstr, macstr);
return ret;
} }
} }
else else
{ {
// Examine whether the specified IP address is within the range of assignment // Examine whether the specified IP address is within the range of static assignment
if (Endian32(v->DhcpIpStart) > Endian32(request_ip) || if (Endian32(v->DhcpIpStart) > Endian32(request_ip) ||
Endian32(request_ip) > Endian32(v->DhcpIpEnd)) Endian32(request_ip) > Endian32(v->DhcpIpEnd))
{ {
// Accept if within the range // Accept if within the range of static assignment
ret = request_ip; ret = request_ip;
} }
else else
{ {
// Propose an IP in the range since it's a Discover although It is out of range // The specified IP address is not available for use
}
}
if (ret == 0)
{
// If there is any entry with the same MAC address
// that are already registered, use it with priority
DHCP_LEASE *d = SearchDhcpLeaseByMac(v, mac);
if (d != NULL)
{
// Examine whether the found IP address is in the allocation region
if (Endian32(v->DhcpIpStart) > Endian32(d->IpAddress) ||
Endian32(d->IpAddress) > Endian32(v->DhcpIpEnd))
{
// Use the IP address if it's found within the range
ret = d->IpAddress;
}
}
}
if (ret == 0)
{
// For static IP, the requested IP address must NOT be within the range of the DHCP pool
if (Endian32(v->DhcpIpStart) > Endian32(request_ip) ||
Endian32(request_ip) > Endian32(v->DhcpIpEnd))
{
ret = request_ip;
} }
} }
@ -9595,6 +9568,11 @@ void VirtualDhcpServer(VH *v, PKT *p)
{ {
ip = ServeDhcpRequestEx(v, p->MacAddressSrc, opt->RequestedIp, ip_static); ip = ServeDhcpRequestEx(v, p->MacAddressSrc, opt->RequestedIp, ip_static);
} }
// If the IP address in user's note is changed, then reply to DHCP_REQUEST with DHCP_NAK
if (p->L3.IPv4Header->SrcIP && ip != p->L3.IPv4Header->SrcIP)
{
ip = 0;
}
} }
if (ip != 0 || opt->Opcode == DHCP_INFORM) if (ip != 0 || opt->Opcode == DHCP_INFORM)
@ -9607,6 +9585,14 @@ void VirtualDhcpServer(VH *v, PKT *p)
char client_mac[MAX_SIZE]; char client_mac[MAX_SIZE];
char client_ip[MAX_SIZE]; char client_ip[MAX_SIZE];
// If there is any entry with the same MAC address, then remove it
d = SearchDhcpLeaseByMac(v, p->MacAddressSrc);
if (d != NULL)
{
FreeDhcpLease(d);
Delete(v->DhcpLeaseList, d);
}
// Remove old records with the same IP address // Remove old records with the same IP address
d = SearchDhcpLeaseByIp(v, ip); d = SearchDhcpLeaseByIp(v, ip);
if (d != NULL) if (d != NULL)
@ -9765,7 +9751,7 @@ void VirtualDhcpServer(VH *v, PKT *p)
} }
else else
{ {
// Reply of DHCP_REQUEST must be either DHCP_ACK or DHCP_NAK. // Reply of DHCP_REQUEST must be either DHCP_ACK or DHCP_NAK
if (opt->Opcode == DHCP_REQUEST) if (opt->Opcode == DHCP_REQUEST)
{ {
// There is no IP address that can be provided // There is no IP address that can be provided

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 // Convert the string to an IP address
bool StrToIP6(IP *ip, char *str) bool StrToIP6(IP *ip, char *str)
{ {
@ -12288,6 +12300,11 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
ret = SSL_peek(ssl, &c, sizeof(c)); ret = SSL_peek(ssl, &c, sizeof(c));
} }
Unlock(sock->ssl_lock); Unlock(sock->ssl_lock);
#if OPENSSL_VERSION_NUMBER < 0x30000000L
// 2021/09/10: After OpenSSL 3.x.x, both 0 and negative values might mean retryable.
// See: https://github.com/openssl/openssl/blob/435981cbadad2c58c35bacd30ca5d8b4c9bea72f/doc/man3/SSL_read.pod
// > Old documentation indicated a difference between 0 and -1, and that -1 was retryable.
// > You should instead call SSL_get_error() to find out if it's retryable.
if (ret == 0) if (ret == 0)
{ {
// The communication have been disconnected // The communication have been disconnected
@ -12295,7 +12312,8 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__); Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__);
return 0; return 0;
} }
if (ret < 0) #endif
if (ret <= 0)
{ {
// An error has occurred // An error has occurred
e = SSL_get_error(ssl, ret); e = SSL_get_error(ssl, ret);
@ -12310,7 +12328,9 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
#endif #endif
) )
{ {
Debug("%s %u SSL Fatal Error on ASYNC socket !!!\n", __FILE__, __LINE__); 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); Disconnect(sock);
return 0; return 0;
} }
@ -12357,7 +12377,11 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
} }
#endif // OS_UNIX #endif // OS_UNIX
if (ret < 0) #if OPENSSL_VERSION_NUMBER < 0x30000000L
if (ret < 0) // OpenSSL version < 3.0.0
#else
if (ret <= 0) // OpenSSL version >= 3.0.0
#endif
{ {
e = SSL_get_error(ssl, ret); e = SSL_get_error(ssl, ret);
} }
@ -12380,6 +12404,12 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
return (UINT)ret; return (UINT)ret;
} }
#if OPENSSL_VERSION_NUMBER < 0x30000000L
// 2021/09/10: After OpenSSL 3.x.x, both 0 and negative values might mean retryable.
// See: https://github.com/openssl/openssl/blob/435981cbadad2c58c35bacd30ca5d8b4c9bea72f/doc/man3/SSL_read.pod
// > Old documentation indicated a difference between 0 and -1, and that -1 was retryable.
// > You should instead call SSL_get_error() to find out if it's retryable.
if (ret == 0) if (ret == 0)
{ {
// Disconnect the communication // Disconnect the communication
@ -12387,6 +12417,8 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
//Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__); //Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__);
return 0; return 0;
} }
#endif
if (sock->AsyncMode) if (sock->AsyncMode)
{ {
if (e == SSL_ERROR_WANT_READ || e == SSL_ERROR_WANT_WRITE || e == SSL_ERROR_SSL) if (e == SSL_ERROR_WANT_READ || e == SSL_ERROR_WANT_WRITE || e == SSL_ERROR_SSL)
@ -12400,7 +12432,9 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size)
#endif #endif
) )
{ {
Debug("%s %u SSL Fatal Error on ASYNC socket !!!\n", __FILE__, __LINE__); 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); Disconnect(sock);
return 0; return 0;
} }
@ -12438,7 +12472,11 @@ UINT SecureSend(SOCK *sock, void *data, UINT size)
} }
ret = SSL_write(ssl, data, size); ret = SSL_write(ssl, data, size);
if (ret < 0) #if OPENSSL_VERSION_NUMBER < 0x30000000L
if (ret < 0) // OpenSSL version < 3.0.0
#else
if (ret <= 0) // OpenSSL version >= 3.0.0
#endif
{ {
e = SSL_get_error(ssl, ret); e = SSL_get_error(ssl, ret);
} }
@ -12460,6 +12498,8 @@ UINT SecureSend(SOCK *sock, void *data, UINT size)
sock->WriteBlocked = false; sock->WriteBlocked = false;
return (UINT)ret; return (UINT)ret;
} }
#if OPENSSL_VERSION_NUMBER < 0x30000000L
if (ret == 0) if (ret == 0)
{ {
// Disconnect // Disconnect
@ -12467,6 +12507,7 @@ UINT SecureSend(SOCK *sock, void *data, UINT size)
Disconnect(sock); Disconnect(sock);
return 0; return 0;
} }
#endif
if (sock->AsyncMode) if (sock->AsyncMode)
{ {

View File

@ -1289,6 +1289,7 @@ void IPToStr6(char *str, UINT size, IP *ip);
void IP6AddrToStr(char *str, UINT size, IPV6_ADDR *addr); void IP6AddrToStr(char *str, UINT size, IPV6_ADDR *addr);
void IPToStr6Array(char *str, UINT size, UCHAR *bytes); void IPToStr6Array(char *str, UINT size, UCHAR *bytes);
void IPToStr6Inner(char *str, IP *ip); void IPToStr6Inner(char *str, IP *ip);
void IPAndMaskToStr(char *str, UINT size, IP *ip, IP *subnet);
void IntToSubnetMask6(IP *ip, UINT i); void IntToSubnetMask6(IP *ip, UINT i);
void IPAnd6(IP *dst, IP *a, IP *b); void IPAnd6(IP *dst, IP *a, IP *b);
void GetAllRouterMulticastAddress6(IP *ip); void GetAllRouterMulticastAddress6(IP *ip);