mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
[AccessControl] Fix checking for allowed IP access of webinterface
This commit is contained in:
@@ -77,24 +77,16 @@ IPAddress NetworkLocalIP() { return WiFi.localIP(); }
|
||||
|
||||
IPAddress NetworkID()
|
||||
{
|
||||
const IPAddress subnet = NetworkSubnetMask();
|
||||
IPAddress networkID = ESPEasy::net::NetworkLocalIP();
|
||||
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
networkID[i] &= subnet[i];
|
||||
}
|
||||
return networkID;
|
||||
return NWPlugin::getNetworkID(
|
||||
ESPEasy::net::NetworkLocalIP(),
|
||||
NetworkSubnetMask());
|
||||
}
|
||||
|
||||
IPAddress NetworkBroadcast()
|
||||
{
|
||||
const IPAddress subnet = NetworkSubnetMask();
|
||||
IPAddress broadcast = ESPEasy::net::NetworkLocalIP();
|
||||
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
broadcast[i] |= ~subnet[i];
|
||||
}
|
||||
return broadcast;
|
||||
return NWPlugin::getNetworkBroadcast(
|
||||
ESPEasy::net::NetworkLocalIP(),
|
||||
NetworkSubnetMask());
|
||||
}
|
||||
|
||||
IPAddress NetworkSubnetMask() { return WiFi.subnetMask(); }
|
||||
|
||||
@@ -472,21 +472,38 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str)
|
||||
IPAddress client_ip;
|
||||
client_ip.fromString(str);
|
||||
|
||||
success = true;
|
||||
|
||||
if (SecuritySettings.IPblockLevel == LOCAL_SUBNET_ALLOWED) {
|
||||
success = NWPlugin::IP_in_subnet(client_ip, event->networkInterface);
|
||||
|
||||
const NWPlugin::IP_type ip_type = NWPlugin::get_IP_type(client_ip);
|
||||
|
||||
IPAddress networkID;
|
||||
IPAddress broadcast;
|
||||
|
||||
if (NWPlugin::get_subnet(ip_type, event->networkInterface, networkID, broadcast)) {
|
||||
# if FEATURE_USE_IPV6
|
||||
const bool includeZone = ip_type == NWPlugin::IP_type::ipv6_link_local;
|
||||
event->String1 = formatIP(networkID, includeZone);
|
||||
event->String2 = formatIP(broadcast, includeZone);
|
||||
# else // if FEATURE_USE_IPV6
|
||||
event->String1 = formatIP(networkID);
|
||||
event->String2 = formatIP(broadcast);
|
||||
# endif // if FEATURE_USE_IPV6
|
||||
}
|
||||
} else if (SecuritySettings.IPblockLevel == ONLY_IP_RANGE_ALLOWED) {
|
||||
const IPAddress low(SecuritySettings.AllowedIPrangeLow);
|
||||
const IPAddress high(SecuritySettings.AllowedIPrangeHigh);
|
||||
|
||||
if (IPAddressSet(low) && IPAddressSet(high))
|
||||
if (IPAddressSet(low) || IPAddressSet(high))
|
||||
{
|
||||
success =
|
||||
NWPlugin::ipInRange(client_ip, low, high) &&
|
||||
NWPlugin::IP_in_subnet(low, event->networkInterface) &&
|
||||
NWPlugin::IP_in_subnet(high, event->networkInterface);
|
||||
} else { success = true; }
|
||||
} else {
|
||||
success = true;
|
||||
// Use || as for example 0.0.0.0 ... 123.0.0.0 might also be a possible range.
|
||||
// Though I have no idea for what practical use case...
|
||||
success = NWPlugin::ipInRange(client_ip, low, high);
|
||||
event->String1 = formatIP(low);
|
||||
event->String2 = formatIP(high);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -30,43 +30,31 @@ bool ipInAllowedSubnet(const IPAddress& ip)
|
||||
}
|
||||
|
||||
String describeAllowedIPrange() {
|
||||
String reply;
|
||||
|
||||
switch (SecuritySettings.IPblockLevel)
|
||||
{
|
||||
case ALL_ALLOWED:
|
||||
reply += F("All Allowed");
|
||||
break;
|
||||
default:
|
||||
{
|
||||
IPAddress low, high;
|
||||
getIPallowedRange(low, high);
|
||||
reply += formatIP(low);
|
||||
reply += F(" - ");
|
||||
reply += formatIP(high);
|
||||
}
|
||||
}
|
||||
return reply;
|
||||
return describeAllowedIPrange(ESPEasy::net::NetworkLocalIP());
|
||||
}
|
||||
|
||||
bool getIPallowedRange(IPAddress& low, IPAddress& high)
|
||||
{
|
||||
switch (SecuritySettings.IPblockLevel)
|
||||
{
|
||||
case LOCAL_SUBNET_ALLOWED:
|
||||
low = NetworkID();
|
||||
high = NetworkBroadcast();
|
||||
return true;
|
||||
case ONLY_IP_RANGE_ALLOWED:
|
||||
low = IPAddress(SecuritySettings.AllowedIPrangeLow);
|
||||
high = IPAddress(SecuritySettings.AllowedIPrangeHigh);
|
||||
break;
|
||||
default:
|
||||
low = IPAddress(0, 0, 0, 0);
|
||||
high = IPAddress(255, 255, 255, 255);
|
||||
return false;
|
||||
String describeAllowedIPrange(const IPAddress& ip) {
|
||||
if (SecuritySettings.IPblockLevel == ALL_ALLOWED) {
|
||||
return F("All Allowed");
|
||||
}
|
||||
return true;
|
||||
String allowedRange;
|
||||
String ip_str = ip.toString();
|
||||
|
||||
for (networkIndex_t x = 0; x < NETWORK_MAX; x++) {
|
||||
EventStruct tempEvent;
|
||||
tempEvent.NetworkIndex = x;
|
||||
|
||||
if (NWPluginCall(NWPlugin::Function::NWPLUGIN_CLIENT_IP_WEB_ACCESS_ALLOWED, &tempEvent, ip_str)) {
|
||||
if (!tempEvent.String1.isEmpty() && !tempEvent.String2.isEmpty()) {
|
||||
|
||||
if (!allowedRange.isEmpty()) { allowedRange += F(", "); }
|
||||
allowedRange += tempEvent.String1;
|
||||
allowedRange += F(" - ");
|
||||
allowedRange += tempEvent.String2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return allowedRange;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
||||
@@ -15,8 +15,7 @@ namespace net {
|
||||
bool ipInAllowedSubnet(const IPAddress& ip);
|
||||
|
||||
String describeAllowedIPrange();
|
||||
|
||||
bool getIPallowedRange(IPAddress& low, IPAddress& high);
|
||||
String describeAllowedIPrange(const IPAddress& ip);
|
||||
|
||||
|
||||
} // namespace net
|
||||
|
||||
@@ -297,21 +297,25 @@ bool NWPlugin_001(NWPlugin::Function function, EventStruct *event, String& strin
|
||||
|
||||
case NWPlugin::Function::NWPLUGIN_CLIENT_IP_WEB_ACCESS_ALLOWED:
|
||||
{
|
||||
IPAddress client_ip;
|
||||
client_ip.fromString(string);
|
||||
|
||||
if ((SecuritySettings.IPblockLevel == LOCAL_SUBNET_ALLOWED) &&
|
||||
!Settings.getNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex)) {
|
||||
success = NWPlugin::ipInRange(client_ip, NetworkID(), NetworkBroadcast());
|
||||
} else if (SecuritySettings.IPblockLevel == ONLY_IP_RANGE_ALLOWED) {
|
||||
const IPAddress low(SecuritySettings.AllowedIPrangeLow);
|
||||
const IPAddress high(SecuritySettings.AllowedIPrangeHigh);
|
||||
success =
|
||||
!IPAddressSet(low) ||
|
||||
!IPAddressSet(high) ||
|
||||
NWPlugin::ipInRange(client_ip, low, high);
|
||||
} else {
|
||||
if (!Settings.getNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex)) {
|
||||
IPAddress client_ip;
|
||||
client_ip.fromString(string);
|
||||
success = true;
|
||||
if (SecuritySettings.IPblockLevel != ALL_ALLOWED) {
|
||||
IPAddress low, high;
|
||||
if (SecuritySettings.IPblockLevel == LOCAL_SUBNET_ALLOWED) {
|
||||
low = NetworkID();
|
||||
high = NetworkBroadcast();
|
||||
} else if (SecuritySettings.IPblockLevel == ONLY_IP_RANGE_ALLOWED) {
|
||||
low = IPAddress(SecuritySettings.AllowedIPrangeLow);
|
||||
high = IPAddress(SecuritySettings.AllowedIPrangeHigh);
|
||||
}
|
||||
if (IPAddressSet(low) || IPAddressSet(high)) {
|
||||
success = NWPlugin::ipInRange(client_ip, low, high);
|
||||
event->String1 = formatIP(low);
|
||||
event->String2 = formatIP(high);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -230,14 +230,7 @@ bool NWPlugin_002(NWPlugin::Function function, EventStruct *event, String& strin
|
||||
|
||||
// FIXME TD-er: Do we allow to set the subnetmask for AP to anything else?
|
||||
const IPAddress subnet(255, 255, 255, 0);
|
||||
const IPAddress localIP = WiFi.softAPIP();
|
||||
bool success = true;
|
||||
|
||||
for (uint8_t i = 0; success && i < 4; ++i) {
|
||||
if ((localIP[i] & subnet[i]) != (client_ip[i] & subnet[i])) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
success = NWPlugin::IP_in_subnet(WiFi.softAPIP(), client_ip, subnet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -185,21 +185,22 @@ bool NWPlugin::get_subnet(NWPlugin::IP_type ip_type, NetworkInterface*networkInt
|
||||
broadcast = networkInterface->broadcastIP();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ifdef ESP32
|
||||
|
||||
bool NWPlugin::ipLessEqual(const IPAddress& ip, const IPAddress& high)
|
||||
{
|
||||
// FIXME TD-er: Must check whether both are of same type and check full range IPv6
|
||||
int nrOctets = 4;
|
||||
|
||||
# if FEATURE_USE_IPV6
|
||||
#if FEATURE_USE_IPV6
|
||||
|
||||
if (ip.type() != high.type()) { return false; }
|
||||
|
||||
if (ip.type() == IPv6) {
|
||||
nrOctets = 16;
|
||||
}
|
||||
# endif // if FEATURE_USE_IPV6
|
||||
#endif // if FEATURE_USE_IPV6
|
||||
|
||||
for (int i = 0; i < nrOctets; ++i) {
|
||||
if (ip[i] != high[i]) {
|
||||
@@ -211,11 +212,69 @@ bool NWPlugin::ipLessEqual(const IPAddress& ip, const IPAddress& high)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NWPlugin::ipInRange(const IPAddress& ip, const IPAddress& low, const IPAddress& high) {
|
||||
return ipLessEqual(low, ip) && ipLessEqual(ip, high);
|
||||
bool NWPlugin::ipInRange(const IPAddress& ip, const IPAddress& low, const IPAddress& high) {
|
||||
return ipLessEqual(low, ip) && ipLessEqual(ip, high);
|
||||
}
|
||||
|
||||
bool NWPlugin::IP_in_subnet(const IPAddress& localIP,
|
||||
const IPAddress& client_ip,
|
||||
const IPAddress& subnet)
|
||||
{
|
||||
#if FEATURE_USE_IPV6
|
||||
if ((localIP.type() != client_ip.type()) ||
|
||||
(localIP.type() != subnet.type())) { return false; }
|
||||
#endif // if FEATURE_USE_IPV6
|
||||
|
||||
return ipInRange(
|
||||
client_ip,
|
||||
getNetworkID(localIP, subnet),
|
||||
getNetworkBroadcast(localIP, subnet));
|
||||
}
|
||||
|
||||
IPAddress NWPlugin::getNetworkID(const IPAddress& localIP, const IPAddress& subnet)
|
||||
{
|
||||
IPAddress networkID = localIP;
|
||||
|
||||
int nrOctets = 4;
|
||||
|
||||
#if FEATURE_USE_IPV6
|
||||
|
||||
if (localIP.type() != subnet.type()) { return IPAddress(); }
|
||||
|
||||
if (localIP.type() == IPv6) {
|
||||
nrOctets = 16;
|
||||
}
|
||||
#endif // if FEATURE_USE_IPV6
|
||||
|
||||
for (uint8_t i = 0; i < nrOctets; ++i) {
|
||||
networkID[i] &= subnet[i];
|
||||
}
|
||||
return networkID;
|
||||
}
|
||||
|
||||
IPAddress NWPlugin::getNetworkBroadcast(const IPAddress& localIP, const IPAddress& subnet)
|
||||
{
|
||||
IPAddress broadcast = localIP;
|
||||
|
||||
int nrOctets = 4;
|
||||
|
||||
#if FEATURE_USE_IPV6
|
||||
|
||||
if (localIP.type() != subnet.type()) { return IPAddress(); }
|
||||
|
||||
if (localIP.type() == IPv6) {
|
||||
nrOctets = 16;
|
||||
}
|
||||
#endif // if FEATURE_USE_IPV6
|
||||
|
||||
for (uint8_t i = 0; i < nrOctets; ++i) {
|
||||
broadcast[i] |= ~subnet[i];
|
||||
}
|
||||
return broadcast;
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
bool NWPlugin::IP_in_subnet(const IPAddress & ip,
|
||||
NetworkInterface *networkInterface)
|
||||
{
|
||||
@@ -223,17 +282,18 @@ bool NWPlugin::IP_in_subnet(const IPAddress & ip,
|
||||
IPAddress networkID;
|
||||
IPAddress broadcast;
|
||||
|
||||
if (!get_subnet(ip_type, networkInterface, networkID, broadcast)) {
|
||||
return false;
|
||||
if (!get_subnet(ip_type, networkInterface, networkID, broadcast)) {
|
||||
return false;
|
||||
}
|
||||
#if FEATURE_USE_IPV6
|
||||
# if FEATURE_USE_IPV6
|
||||
|
||||
if (ip_type == NWPlugin::IP_type::ipv6_link_local) {
|
||||
// Must match zone, or else it will always match.
|
||||
return (ip.zone() == networkID.zone());
|
||||
if (ip.zone() != networkID.zone()) return false;
|
||||
}
|
||||
#endif
|
||||
# endif // if FEATURE_USE_IPV6
|
||||
|
||||
return ipLessEqual(networkID, ip) && ipLessEqual(ip, broadcast);
|
||||
return ipInRange(ip, networkID, broadcast);
|
||||
}
|
||||
|
||||
NWPlugin::IP_type NWPlugin::get_IP_type(const IPAddress& ip)
|
||||
|
||||
@@ -255,6 +255,14 @@ public:
|
||||
const IPAddress& low,
|
||||
const IPAddress& high);
|
||||
|
||||
static bool IP_in_subnet(const IPAddress& localIP,
|
||||
const IPAddress& client_ip,
|
||||
const IPAddress& subnet);
|
||||
|
||||
static IPAddress getNetworkID(const IPAddress& localIP, const IPAddress& subnet);
|
||||
|
||||
static IPAddress getNetworkBroadcast(const IPAddress& localIP, const IPAddress& subnet);
|
||||
|
||||
#ifdef ESP32
|
||||
static bool IP_in_subnet(const IPAddress & ip,
|
||||
NetworkInterface *networkInterface);
|
||||
|
||||
@@ -212,7 +212,7 @@ int SettingsType::getFileSize(Enum settingsType) {
|
||||
return max_file_pos;
|
||||
}
|
||||
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
#if FEATURE_CHART_STORAGE_LAYOUT && !defined(BUILD_NO_DEBUG)
|
||||
unsigned int SettingsType::getSVGcolor(Enum settingsType) {
|
||||
switch (settingsType) {
|
||||
case Enum::BasicSettings_Type:
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
static int getMaxFilePos(Enum settingsType);
|
||||
static int getFileSize(Enum settingsType);
|
||||
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
#if FEATURE_CHART_STORAGE_LAYOUT && !defined(BUILD_NO_DEBUG)
|
||||
static unsigned int getSVGcolor(Enum settingsType);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -615,7 +615,7 @@ void logMemUsageAfter(const __FlashStringHelper *function, int value) {
|
||||
// The recorded used memory is not an exact value, as background (or interrupt) tasks may also allocate or free heap memory.
|
||||
static int last_freemem = ESP.getFreeHeap();
|
||||
const int freemem_end = ESP.getFreeHeap();
|
||||
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
String log;
|
||||
|
||||
@@ -639,6 +639,7 @@ void logMemUsageAfter(const __FlashStringHelper *function, int value) {
|
||||
addLogMove(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
last_freemem = freemem_end;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ bool clientIPallowed()
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
|
||||
response += F(" Allowed: ");
|
||||
response += ESPEasy::net::describeAllowedIPrange();
|
||||
response += ESPEasy::net::describeAllowedIPrange(remoteIP);
|
||||
addLogMove(LOG_LEVEL_ERROR, response);
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -1091,7 +1091,7 @@ void getWiFi_RSSI_icon(int rssi, int width_pixels)
|
||||
addHtml(F("</svg>\n"));
|
||||
}
|
||||
|
||||
#if FEATURE_CHART_STORAGE_LAYOUT
|
||||
#if FEATURE_CHART_STORAGE_LAYOUT && !defined(BUILD_NO_DEBUG)
|
||||
void getConfig_dat_file_layout() {
|
||||
const int shiftY = 2;
|
||||
float yOffset = shiftY;
|
||||
|
||||
@@ -193,7 +193,7 @@ void write_SVG_image_header(int width,
|
||||
void getWiFi_RSSI_icon(int rssi,
|
||||
int width_pixels);
|
||||
|
||||
#if FEATURE_CHART_STORAGE_LAYOUT
|
||||
#if FEATURE_CHART_STORAGE_LAYOUT && !defined(BUILD_NO_DEBUG)
|
||||
void getConfig_dat_file_layout();
|
||||
|
||||
void getStorageTableSVG(SettingsType::Enum settingsType);
|
||||
|
||||
@@ -668,7 +668,7 @@ void handle_sysinfo_Storage() {
|
||||
}
|
||||
# endif // ifndef LIMIT_BUILD_SIZE
|
||||
|
||||
# if FEATURE_CHART_STORAGE_LAYOUT
|
||||
#if FEATURE_CHART_STORAGE_LAYOUT && !defined(BUILD_NO_DEBUG)
|
||||
|
||||
if (showSettingsFileLayout) {
|
||||
addTableSeparator(F("Settings Files"), 2, 3);
|
||||
|
||||
Reference in New Issue
Block a user