From b4e7781ac065b227a66fdfc22f0eb749c7a1c7d0 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:27:30 +0530 Subject: [PATCH 01/53] AuthZoneInfo: minor refactoring changes --- DnsServerCore/Dns/Zones/AuthZoneInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DnsServerCore/Dns/Zones/AuthZoneInfo.cs b/DnsServerCore/Dns/Zones/AuthZoneInfo.cs index 40cf5864..009400ba 100644 --- a/DnsServerCore/Dns/Zones/AuthZoneInfo.cs +++ b/DnsServerCore/Dns/Zones/AuthZoneInfo.cs @@ -110,7 +110,7 @@ namespace DnsServerCore.Dns.Zones IPAddress[] nameServers = new IPAddress[count]; for (int i = 0; i < count; i++) - nameServers[i] = IPAddressExtension.Parse(bR); + nameServers[i] = IPAddressExtension.ReadFrom(bR); _zoneTransferNameServers = nameServers; } @@ -125,7 +125,7 @@ namespace DnsServerCore.Dns.Zones IPAddress[] nameServers = new IPAddress[count]; for (int i = 0; i < count; i++) - nameServers[i] = IPAddressExtension.Parse(bR); + nameServers[i] = IPAddressExtension.ReadFrom(bR); _notifyNameServers = nameServers; } From e43068f78a80789223c445eba51b355b1fc34824 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:28:19 +0530 Subject: [PATCH 02/53] ForwarderZone: updated code to allow adding/updating APP records. --- DnsServerCore/Dns/Zones/ForwarderZone.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/DnsServerCore/Dns/Zones/ForwarderZone.cs b/DnsServerCore/Dns/Zones/ForwarderZone.cs index 756d3719..97bbfb02 100644 --- a/DnsServerCore/Dns/Zones/ForwarderZone.cs +++ b/DnsServerCore/Dns/Zones/ForwarderZone.cs @@ -61,7 +61,6 @@ namespace DnsServerCore.Dns.Zones case DnsResourceRecordType.SOA: case DnsResourceRecordType.DS: - case DnsResourceRecordType.APP: throw new DnsServerException("The record type is not supported by forwarder zones."); default: @@ -75,7 +74,6 @@ namespace DnsServerCore.Dns.Zones switch (record.Type) { case DnsResourceRecordType.DS: - case DnsResourceRecordType.APP: throw new DnsServerException("The record type is not supported by forwarder zones."); default: From 32b57db960c6178085f2da6954322997cf331297 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:28:44 +0530 Subject: [PATCH 03/53] ForwarderSubDomainZone: updated code to allow adding/updating APP records. --- DnsServerCore/Dns/Zones/ForwarderSubDomainZone.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/DnsServerCore/Dns/Zones/ForwarderSubDomainZone.cs b/DnsServerCore/Dns/Zones/ForwarderSubDomainZone.cs index 64a64d14..badb179b 100644 --- a/DnsServerCore/Dns/Zones/ForwarderSubDomainZone.cs +++ b/DnsServerCore/Dns/Zones/ForwarderSubDomainZone.cs @@ -40,7 +40,6 @@ namespace DnsServerCore.Dns.Zones { case DnsResourceRecordType.SOA: case DnsResourceRecordType.DS: - case DnsResourceRecordType.APP: throw new DnsServerException("The record type is not supported by forwarder zones."); default: @@ -54,7 +53,6 @@ namespace DnsServerCore.Dns.Zones switch (record.Type) { case DnsResourceRecordType.DS: - case DnsResourceRecordType.APP: throw new DnsServerException("The record type is not supported by forwarder zones."); default: From e800afd3a019fc79b21d254808dc1240b379ddd8 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:29:25 +0530 Subject: [PATCH 04/53] BlockListZoneManager: updated UpdateBlockListsAsync() to not use default system proxy. --- DnsServerCore/Dns/ZoneManagers/BlockListZoneManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/DnsServerCore/Dns/ZoneManagers/BlockListZoneManager.cs b/DnsServerCore/Dns/ZoneManagers/BlockListZoneManager.cs index c582345a..a33a1421 100644 --- a/DnsServerCore/Dns/ZoneManagers/BlockListZoneManager.cs +++ b/DnsServerCore/Dns/ZoneManagers/BlockListZoneManager.cs @@ -321,6 +321,7 @@ namespace DnsServerCore.Dns.ZoneManagers SocketsHttpHandler handler = new SocketsHttpHandler(); handler.Proxy = _dnsServer.Proxy; + handler.UseProxy = _dnsServer.Proxy is not null; handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; using (HttpClient http = new HttpClient(handler)) From e1bdfd151aae819d50c587e9efd7ae2218c99607 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:33:23 +0530 Subject: [PATCH 05/53] AuthZoneManager: updated Query() to allow APP records processing for forwarder zones, minor update to return referral response when zone found is a delegation. --- .../Dns/ZoneManagers/AuthZoneManager.cs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs b/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs index 23cae5d2..699a457a 100644 --- a/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs +++ b/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs @@ -1697,9 +1697,6 @@ namespace DnsServerCore.Dns.ZoneManagers if (apexZone is StubZone) return GetReferralResponse(request, false, apexZone, apexZone, isRecursionAllowed); - if (apexZone is ForwarderZone) - return GetForwarderResponse(request, null, closest, apexZone, isRecursionAllowed); - DnsResponseCode rCode = DnsResponseCode.NoError; IReadOnlyList answer = null; IReadOnlyList authority = null; @@ -1733,6 +1730,9 @@ namespace DnsServerCore.Dns.ZoneManagers authority = apexZone.QueryRecords(DnsResourceRecordType.APP, false); if (authority.Count == 0) { + if (apexZone is ForwarderZone) + return GetForwarderResponse(request, null, closest, apexZone, isRecursionAllowed); //no DNAME or APP record available so process FWD response + if (!hasSubDomains) rCode = DnsResponseCode.NxDomain; @@ -1767,12 +1767,20 @@ namespace DnsServerCore.Dns.ZoneManagers else { //zone found - if ((question.Type == DnsResourceRecordType.DS) && (zone is ApexZone)) + if (question.Type == DnsResourceRecordType.DS) { - if (delegation is null || !delegation.IsActive || (delegation.Name.Length > apexZone.Name.Length)) - return null; //no authoritative parent side delegation zone available to answer for DS + if (zone is ApexZone) + { + if (delegation is null || !delegation.IsActive || (delegation.Name.Length > apexZone.Name.Length)) + return null; //no authoritative parent side delegation zone available to answer for DS - zone = delegation; //switch zone to parent side sub domain delegation zone for DS record + zone = delegation; //switch zone to parent side sub domain delegation zone for DS record + } + } + else if (zone.Equals(delegation)) + { + //zone is delegation + return GetReferralResponse(request, dnssecOk, delegation, apexZone, isRecursionAllowed); } IReadOnlyList authority = null; @@ -1806,9 +1814,6 @@ namespace DnsServerCore.Dns.ZoneManagers if (apexZone is StubZone) return GetReferralResponse(request, false, apexZone, apexZone, isRecursionAllowed); - - if (apexZone is ForwarderZone) - return GetForwarderResponse(request, zone, closest, apexZone, isRecursionAllowed); } authority = zone.QueryRecords(DnsResourceRecordType.APP, false); @@ -1822,6 +1827,9 @@ namespace DnsServerCore.Dns.ZoneManagers authority = apexZone.QueryRecords(DnsResourceRecordType.APP, false); if (authority.Count == 0) { + if (apexZone is ForwarderZone) + return GetForwarderResponse(request, zone, closest, apexZone, isRecursionAllowed); //no APP record available so process FWD response + authority = apexZone.QueryRecords(DnsResourceRecordType.SOA, dnssecOk); if (dnssecOk) From 016f7551d60fc723d9e22643965dd1247b0a6152 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:34:02 +0530 Subject: [PATCH 06/53] StatsManager: minor refactoring changes. --- DnsServerCore/Dns/StatsManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DnsServerCore/Dns/StatsManager.cs b/DnsServerCore/Dns/StatsManager.cs index 349ca199..ce8bf789 100644 --- a/DnsServerCore/Dns/StatsManager.cs +++ b/DnsServerCore/Dns/StatsManager.cs @@ -1368,7 +1368,7 @@ namespace DnsServerCore.Dns _clientIpAddresses = new ConcurrentDictionary(1, count); for (int i = 0; i < count; i++) - _clientIpAddresses.TryAdd(IPAddressExtension.Parse(bR), new Counter(bR.ReadInt32())); + _clientIpAddresses.TryAdd(IPAddressExtension.ReadFrom(bR), new Counter(bR.ReadInt32())); if (version < 6) _totalClients = count; @@ -1393,7 +1393,7 @@ namespace DnsServerCore.Dns _errorIpAddresses = new ConcurrentDictionary(1, count); for (int i = 0; i < count; i++) - _errorIpAddresses.TryAdd(IPAddressExtension.Parse(bR), new Counter(bR.ReadInt32())); + _errorIpAddresses.TryAdd(IPAddressExtension.ReadFrom(bR), new Counter(bR.ReadInt32())); } else { From f2ffc891a043d358d380fdef89795d9ed9b04481 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:35:44 +0530 Subject: [PATCH 07/53] DnsServer: updated ProcessAPPAsync() to support new APP interface changes. Updated ProcessCNAMEAsync() to use the correct DO flag in new request. --- DnsServerCore/Dns/DnsServer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DnsServerCore/Dns/DnsServer.cs b/DnsServerCore/Dns/DnsServer.cs index dcd44ca9..d19fa208 100644 --- a/DnsServerCore/Dns/DnsServer.cs +++ b/DnsServerCore/Dns/DnsServer.cs @@ -1464,7 +1464,7 @@ namespace DnsServerCore.Dns { AuthZoneInfo zoneInfo = _authZoneManager.FindAuthZoneInfo(appResourceRecord.Name); - DnsDatagram appResponse = await appRecordRequestHandler.ProcessRequestAsync(request, remoteEP, protocol, isRecursionAllowed, zoneInfo.Name, appResourceRecord.TtlValue, appRecord.Data); + DnsDatagram appResponse = await appRecordRequestHandler.ProcessRequestAsync(request, remoteEP, protocol, isRecursionAllowed, zoneInfo.Name, appResourceRecord.Name, appResourceRecord.TtlValue, appRecord.Data); if (appResponse is null) { //return no error response with SOA @@ -1540,7 +1540,7 @@ namespace DnsServerCore.Dns int queryCount = 0; do { - DnsDatagram newRequest = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { new DnsQuestionRecord((lastRR.RDATA as DnsCNAMERecordData).Domain, request.Question[0].Type, request.Question[0].Class) }, null, null, null, _udpPayloadSize, _dnssecValidation ? EDnsHeaderFlags.DNSSEC_OK : EDnsHeaderFlags.None); + DnsDatagram newRequest = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { new DnsQuestionRecord((lastRR.RDATA as DnsCNAMERecordData).Domain, request.Question[0].Type, request.Question[0].Class) }, null, null, null, _udpPayloadSize, request.DnssecOk ? EDnsHeaderFlags.DNSSEC_OK : EDnsHeaderFlags.None); //query authoritative zone first newResponse = _authZoneManager.Query(newRequest, isRecursionAllowed); From 9df8beb2d4da2a9de92398b04d2bc172fa2b3b17 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:36:29 +0530 Subject: [PATCH 08/53] Lease: minor refactoring change --- DnsServerCore/Dhcp/Lease.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DnsServerCore/Dhcp/Lease.cs b/DnsServerCore/Dhcp/Lease.cs index b20b1ad6..0635844e 100644 --- a/DnsServerCore/Dhcp/Lease.cs +++ b/DnsServerCore/Dhcp/Lease.cs @@ -88,7 +88,7 @@ namespace DnsServerCore.Dhcp _hostName = null; _hardwareAddress = bR.ReadBuffer(); - _address = IPAddressExtension.Parse(bR); + _address = IPAddressExtension.ReadFrom(bR); if (version >= 2) { From 50cbbc9fe1cbf39ecf276d7f70ff203cc2f96564 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:37:41 +0530 Subject: [PATCH 09/53] Scope: minor refactoring changes done. Added AddReservedLease() and RemoveReservedLease() methods. --- DnsServerCore/Dhcp/Scope.cs | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/DnsServerCore/Dhcp/Scope.cs b/DnsServerCore/Dhcp/Scope.cs index bdadca51..2fcfe4dd 100644 --- a/DnsServerCore/Dhcp/Scope.cs +++ b/DnsServerCore/Dhcp/Scope.cs @@ -120,7 +120,7 @@ namespace DnsServerCore.Dhcp _name = bR.ReadShortString(); _enabled = bR.ReadBoolean(); - ChangeNetwork(IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR)); + ChangeNetwork(IPAddressExtension.ReadFrom(bR), IPAddressExtension.ReadFrom(bR), IPAddressExtension.ReadFrom(bR)); _leaseTimeDays = bR.ReadUInt16(); _leaseTimeHours = bR.ReadByte(); @@ -143,7 +143,7 @@ namespace DnsServerCore.Dhcp if (version >= 2) { - _serverAddress = IPAddressExtension.Parse(bR); + _serverAddress = IPAddressExtension.ReadFrom(bR); if (_serverAddress.Equals(IPAddress.Any)) _serverAddress = null; } @@ -159,7 +159,7 @@ namespace DnsServerCore.Dhcp _bootFileName = null; } - _routerAddress = IPAddressExtension.Parse(bR); + _routerAddress = IPAddressExtension.ReadFrom(bR); if (_routerAddress.Equals(IPAddress.Any)) _routerAddress = null; @@ -177,7 +177,7 @@ namespace DnsServerCore.Dhcp IPAddress[] dnsServers = new IPAddress[count]; for (int i = 0; i < count; i++) - dnsServers[i] = IPAddressExtension.Parse(bR); + dnsServers[i] = IPAddressExtension.ReadFrom(bR); _dnsServers = dnsServers; } @@ -191,7 +191,7 @@ namespace DnsServerCore.Dhcp IPAddress[] winsServers = new IPAddress[count]; for (int i = 0; i < count; i++) - winsServers[i] = IPAddressExtension.Parse(bR); + winsServers[i] = IPAddressExtension.ReadFrom(bR); _winsServers = winsServers; } @@ -204,7 +204,7 @@ namespace DnsServerCore.Dhcp IPAddress[] ntpServers = new IPAddress[count]; for (int i = 0; i < count; i++) - ntpServers[i] = IPAddressExtension.Parse(bR); + ntpServers[i] = IPAddressExtension.ReadFrom(bR); _ntpServers = ntpServers; } @@ -249,7 +249,7 @@ namespace DnsServerCore.Dhcp Exclusion[] exclusions = new Exclusion[count]; for (int i = 0; i < count; i++) - exclusions[i] = new Exclusion(IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR)); + exclusions[i] = new Exclusion(IPAddressExtension.ReadFrom(bR), IPAddressExtension.ReadFrom(bR)); _exclusions = exclusions; } @@ -455,7 +455,7 @@ namespace DnsServerCore.Dhcp clientDomainName = request.HostName.HostName + "." + _domainName; } - else if (request.ClientFullyQualifiedDomainName.DomainName.Contains(".")) + else if (request.ClientFullyQualifiedDomainName.DomainName.Contains('.')) { //client domain is fqdn if (request.ClientFullyQualifiedDomainName.DomainName.EndsWith("." + _domainName, StringComparison.OrdinalIgnoreCase)) @@ -1161,6 +1161,19 @@ namespace DnsServerCore.Dhcp } } + public bool AddReservedLease(Lease reservedLease) + { + return _reservedLeases.TryAdd(reservedLease.ClientIdentifier, reservedLease); + } + + public bool RemoveReservedLease(string hardwareAddress) + { + byte[] hardwareAddressBytes = Lease.ParseHardwareAddress(hardwareAddress); + ClientIdentifierOption reservedLeaseClientIdentifier = new ClientIdentifierOption((byte)DhcpMessageHardwareAddressType.Ethernet, hardwareAddressBytes); + + return _reservedLeases.TryRemove(reservedLeaseClientIdentifier, out _); + } + public Lease RemoveLease(string hardwareAddress) { byte[] hardwareAddressBytes = Lease.ParseHardwareAddress(hardwareAddress); @@ -1182,12 +1195,12 @@ namespace DnsServerCore.Dhcp if (removedLease.Type == LeaseType.Reserved) { //remove reserved lease - ClientIdentifierOption reservedLeasesClientIdentifier = new ClientIdentifierOption((byte)DhcpMessageHardwareAddressType.Ethernet, removedLease.HardwareAddress); - if (_reservedLeases.TryGetValue(reservedLeasesClientIdentifier, out Lease existingReservedLease)) + ClientIdentifierOption reservedLeaseClientIdentifier = new ClientIdentifierOption((byte)DhcpMessageHardwareAddressType.Ethernet, removedLease.HardwareAddress); + if (_reservedLeases.TryGetValue(reservedLeaseClientIdentifier, out Lease existingReservedLease)) { //remove reserved lease only if the IP addresses match if (existingReservedLease.Address.Equals(removedLease.Address)) - _reservedLeases.TryRemove(reservedLeasesClientIdentifier, out _); + _reservedLeases.TryRemove(reservedLeaseClientIdentifier, out _); } } From 18915a196f5019579639133d1882c70592a9fe99 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:41:41 +0530 Subject: [PATCH 10/53] DhcpServer: updated UpdateDnsAuthZone() to set permissions for the created primary zones and to allow updating records into a forwarder zone. --- DnsServerCore/Dhcp/DhcpServer.cs | 80 ++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/DnsServerCore/Dhcp/DhcpServer.cs b/DnsServerCore/Dhcp/DhcpServer.cs index f0bca697..6a704ed4 100644 --- a/DnsServerCore/Dhcp/DhcpServer.cs +++ b/DnsServerCore/Dhcp/DhcpServer.cs @@ -17,6 +17,7 @@ along with this program. If not, see . */ +using DnsServerCore.Auth; using DnsServerCore.Dhcp.Options; using DnsServerCore.Dns.ZoneManagers; using DnsServerCore.Dns.Zones; @@ -69,6 +70,7 @@ namespace DnsServerCore.Dhcp readonly ConcurrentDictionary _scopes = new ConcurrentDictionary(); AuthZoneManager _authZoneManager; + AuthManager _authManager; ConcurrentDictionary _modifiedDnsAuthZones = new ConcurrentDictionary(); readonly Timer _saveModifiedDnsAuthZonesTimer; @@ -760,16 +762,42 @@ namespace DnsServerCore.Dhcp { //zone does not exists; create new primary zone zoneInfo = _authZoneManager.CreatePrimaryZone(scope.DomainName, _authZoneManager.ServerDomain, false); - log?.Write("DHCP Server create DNS primary zone '" + scope.DomainName + "'."); + if (zoneInfo is null) + { + log?.Write("DHCP Server failed to create DNS primary zone '" + scope.DomainName + "'."); + return; + } + + //set permissions + _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.DHCP_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SaveConfigFile(); + + log?.Write("DHCP Server create DNS primary zone '" + zoneInfo.Name + "'."); + _authZoneManager.SaveZoneFile(zoneInfo.Name); } - else if (zoneInfo.Type != AuthZoneType.Primary) + else if ((zoneInfo.Type != AuthZoneType.Primary) && (zoneInfo.Type != AuthZoneType.Forwarder)) { if (zoneInfo.Name.Equals(scope.DomainName, StringComparison.OrdinalIgnoreCase)) - throw new DhcpServerException("Cannot update DNS zone '" + zoneInfo.Name + "': not a primary zone."); + throw new DhcpServerException("Cannot update DNS zone '" + zoneInfo.Name + "': not a primary or a forwarder zone."); //create new primary zone zoneInfo = _authZoneManager.CreatePrimaryZone(scope.DomainName, _authZoneManager.ServerDomain, false); - log?.Write("DHCP Server create DNS primary zone '" + scope.DomainName + "'."); + if (zoneInfo is null) + { + log?.Write("DHCP Server failed to create DNS primary zone '" + scope.DomainName + "'."); + return; + } + + //set permissions + _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.DHCP_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SaveConfigFile(); + + log?.Write("DHCP Server create DNS primary zone '" + zoneInfo.Name + "'."); + _authZoneManager.SaveZoneFile(zoneInfo.Name); } zoneName = zoneInfo.Name; @@ -803,18 +831,44 @@ namespace DnsServerCore.Dhcp //reverse zone does not exists; create new reverse primary zone reverseZoneInfo = _authZoneManager.CreatePrimaryZone(reverseZone, _authZoneManager.ServerDomain, false); - log?.Write("DHCP Server create DNS primary zone '" + reverseZone + "'."); + if (reverseZoneInfo is null) + { + log?.Write("DHCP Server failed to create DNS primary zone '" + reverseZone + "'."); + return; + } + + //set permissions + _authManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _authManager.GetGroup(Group.DHCP_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SaveConfigFile(); + + log?.Write("DHCP Server create DNS primary zone '" + reverseZoneInfo.Name + "'."); + _authZoneManager.SaveZoneFile(reverseZoneInfo.Name); } - else if (reverseZoneInfo.Type != AuthZoneType.Primary) + else if ((reverseZoneInfo.Type != AuthZoneType.Primary) && (reverseZoneInfo.Type != AuthZoneType.Forwarder)) { string reverseZone = Zone.GetReverseZone(address, scope.SubnetMask); if (reverseZoneInfo.Name.Equals(reverseZone, StringComparison.OrdinalIgnoreCase)) - throw new DhcpServerException("Cannot update reverse DNS zone '" + reverseZoneInfo.Name + "': not a primary zone."); + throw new DhcpServerException("Cannot update reverse DNS zone '" + reverseZoneInfo.Name + "': not a primary or a forwarder zone."); //create new reverse primary zone reverseZoneInfo = _authZoneManager.CreatePrimaryZone(reverseZone, _authZoneManager.ServerDomain, false); - log?.Write("DHCP Server create DNS primary zone '" + reverseZone + "'."); + if (reverseZoneInfo is null) + { + log?.Write("DHCP Server failed to create DNS primary zone '" + reverseZone + "'."); + return; + } + + //set permissions + _authManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _authManager.GetGroup(Group.DHCP_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SaveConfigFile(); + + log?.Write("DHCP Server create DNS primary zone '" + reverseZoneInfo.Name + "'."); + _authZoneManager.SaveZoneFile(reverseZoneInfo.Name); } reverseZoneName = reverseZoneInfo.Name; @@ -825,7 +879,7 @@ namespace DnsServerCore.Dhcp { //remove from forward zone AuthZoneInfo zoneInfo = _authZoneManager.FindAuthZoneInfo(domain); - if ((zoneInfo is not null) && (zoneInfo.Type == AuthZoneType.Primary)) + if ((zoneInfo is not null) && ((zoneInfo.Type == AuthZoneType.Primary) || (zoneInfo.Type == AuthZoneType.Forwarder))) { //primary zone exists zoneName = zoneInfo.Name; @@ -835,7 +889,7 @@ namespace DnsServerCore.Dhcp //remove from reverse zone AuthZoneInfo reverseZoneInfo = _authZoneManager.FindAuthZoneInfo(reverseDomain); - if ((reverseZoneInfo != null) && (reverseZoneInfo.Type == AuthZoneType.Primary)) + if ((reverseZoneInfo != null) && ((reverseZoneInfo.Type == AuthZoneType.Primary) || (reverseZoneInfo.Type == AuthZoneType.Forwarder))) { //primary reverse zone exists reverseZoneName = reverseZoneInfo.Name; @@ -1397,6 +1451,12 @@ namespace DnsServerCore.Dhcp set { _authZoneManager = value; } } + internal AuthManager AuthManager + { + get { return _authManager; } + set { _authManager = value; } + } + public LogManager LogManager { get { return _log; } From 822f7898749d0b6ce6ae65d05a42bb91a51cd2f2 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:42:43 +0530 Subject: [PATCH 11/53] UserSession: updated implementation for user session. --- DnsServerCore/Auth/UserSession.cs | 179 ++++++++++++++++++++++++++++++ DnsServerCore/UserSession.cs | 66 ----------- 2 files changed, 179 insertions(+), 66 deletions(-) create mode 100644 DnsServerCore/Auth/UserSession.cs delete mode 100644 DnsServerCore/UserSession.cs diff --git a/DnsServerCore/Auth/UserSession.cs b/DnsServerCore/Auth/UserSession.cs new file mode 100644 index 00000000..14d5d1aa --- /dev/null +++ b/DnsServerCore/Auth/UserSession.cs @@ -0,0 +1,179 @@ +/* +Technitium DNS Server +Copyright (C) 2022 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System; +using System.IO; +using System.Net; +using System.Security.Cryptography; +using TechnitiumLibrary.IO; +using TechnitiumLibrary.Net; + +namespace DnsServerCore.Auth +{ + enum UserSessionType : byte + { + Unknown = 0, + Standard = 1, + ApiToken = 2 + } + + class UserSession : IComparable + { + #region variables + + static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create(); + + readonly string _token; + readonly UserSessionType _type; + readonly string _tokenName; + readonly User _user; + DateTime _lastSeen; + IPAddress _lastSeenRemoteAddress; + string _lastSeenUserAgent; + + #endregion + + #region constructor + + public UserSession(UserSessionType type, string tokenName, User user, IPAddress remoteAddress, string lastSeenUserAgent) + { + if ((tokenName is not null) && (tokenName.Length > 255)) + throw new ArgumentOutOfRangeException(nameof(tokenName), "Token name length cannot exceed 255 characters."); + + byte[] tokenBytes = new byte[32]; + _rng.GetBytes(tokenBytes); + _token = Convert.ToHexString(tokenBytes).ToLower(); + + _type = type; + _tokenName = tokenName; + _user = user; + _lastSeen = DateTime.UtcNow; + _lastSeenRemoteAddress = remoteAddress; + _lastSeenUserAgent = lastSeenUserAgent; + + if ((_lastSeenUserAgent is not null) && (_lastSeenUserAgent.Length > 255)) + _lastSeenUserAgent = _lastSeenUserAgent.Substring(0, 255); + } + + public UserSession(BinaryReader bR, AuthManager authManager) + { + switch (bR.ReadByte()) + { + case 1: + _token = bR.ReadShortString(); + _type = (UserSessionType)bR.ReadByte(); + + _tokenName = bR.ReadShortString(); + if (_tokenName.Length == 0) + _tokenName = null; + + _user = authManager.GetUser(bR.ReadShortString()); + _lastSeen = bR.ReadDateTime(); + _lastSeenRemoteAddress = IPAddressExtension.ReadFrom(bR); + + _lastSeenUserAgent = bR.ReadShortString(); + if (_lastSeenUserAgent.Length == 0) + _lastSeenUserAgent = null; + + break; + + default: + throw new InvalidDataException("Invalid data or version not supported."); + } + } + + #endregion + + #region public + + public void UpdateLastSeen(IPAddress remoteAddress, string lastSeenUserAgent) + { + _lastSeen = DateTime.UtcNow; + _lastSeenRemoteAddress = remoteAddress; + _lastSeenUserAgent = lastSeenUserAgent; + + if ((_lastSeenUserAgent is not null) && (_lastSeenUserAgent.Length > 255)) + _lastSeenUserAgent = _lastSeenUserAgent.Substring(0, 255); + } + + public bool HasExpired() + { + if (_type == UserSessionType.ApiToken) + return false; + + if (_user.SessionTimeoutSeconds == 0) + return false; + + return _lastSeen.AddSeconds(_user.SessionTimeoutSeconds) < DateTime.UtcNow; + } + + public void WriteTo(BinaryWriter bW) + { + bW.Write((byte)1); + bW.WriteShortString(_token); + bW.Write((byte)_type); + + if (_tokenName is null) + bW.Write((byte)0); + else + bW.WriteShortString(_tokenName); + + bW.WriteShortString(_user.Username); + bW.Write(_lastSeen); + _lastSeenRemoteAddress.WriteTo(bW); + + if (_lastSeenUserAgent is null) + bW.Write((byte)0); + else + bW.WriteShortString(_lastSeenUserAgent); + } + + public int CompareTo(UserSession other) + { + return other._lastSeen.CompareTo(_lastSeen); + } + + #endregion + + #region properties + + public string Token + { get { return _token; } } + + public UserSessionType Type + { get { return _type; } } + + public string TokenName + { get { return _tokenName; } } + + public User User + { get { return _user; } } + + public DateTime LastSeen + { get { return _lastSeen; } } + + public IPAddress LastSeenRemoteAddress + { get { return _lastSeenRemoteAddress; } } + + public string LastSeenUserAgent + { get { return _lastSeenUserAgent; } } + + #endregion + } +} diff --git a/DnsServerCore/UserSession.cs b/DnsServerCore/UserSession.cs deleted file mode 100644 index 961baa01..00000000 --- a/DnsServerCore/UserSession.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* -Technitium DNS Server -Copyright (C) 2019 Shreyas Zare (shreyas@technitium.com) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -*/ - -using System; - -namespace DnsServerCore -{ - class UserSession - { - #region variables - - const int SESSION_TIMEOUT = 30 * 60 * 1000; //30 mins - - readonly string _username; - DateTime _lastSeen; - - #endregion - - #region constructor - - public UserSession(string username) - { - _username = username; - _lastSeen = DateTime.UtcNow; - } - - #endregion - - #region public - - public void UpdateLastSeen() - { - _lastSeen = DateTime.UtcNow; - } - - public bool HasExpired() - { - return _lastSeen.AddMilliseconds(SESSION_TIMEOUT) < DateTime.UtcNow; - } - - #endregion - - #region properties - - public string Username - { get { return _username; } } - - #endregion - } -} From ffd1c258451e4d990b4f2cd6706f2ea3f6d34bd0 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:43:31 +0530 Subject: [PATCH 12/53] added User implementation --- DnsServerCore/Auth/User.cs | 357 +++++++++++++++++++++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 DnsServerCore/Auth/User.cs diff --git a/DnsServerCore/Auth/User.cs b/DnsServerCore/Auth/User.cs new file mode 100644 index 00000000..60717ad5 --- /dev/null +++ b/DnsServerCore/Auth/User.cs @@ -0,0 +1,357 @@ +/* +Technitium DNS Server +Copyright (C) 2022 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using TechnitiumLibrary.IO; +using TechnitiumLibrary.Net; + +namespace DnsServerCore.Auth +{ + enum UserPasswordHashType : byte + { + Unknown = 0, + OldScheme = 1, + PBKDF2_SHA256 = 2 + } + + class User : IComparable + { + #region variables + + static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create(); + + public const int DEFAULT_ITERATIONS = 1000000; + + string _displayName; + string _username; + UserPasswordHashType _passwordHashType; + int _iterations; + byte[] _salt; + string _passwordHash; + bool _disabled; + int _sessionTimeoutSeconds = 30 * 60; //default 30 mins + + DateTime _previousSessionLoggedOn; + IPAddress _previousSessionRemoteAddress; + DateTime _recentSessionLoggedOn; + IPAddress _recentSessionRemoteAddress; + + readonly ConcurrentDictionary _memberOfGroups; + + #endregion + + #region constructor + + public User(string displayName, string username, string password, int iterations = DEFAULT_ITERATIONS) + { + DisplayName = displayName; + Username = username; + + ChangePassword(password, iterations); + + _previousSessionRemoteAddress = IPAddress.Any; + _recentSessionRemoteAddress = IPAddress.Any; + + _memberOfGroups = new ConcurrentDictionary(1, 2); + } + + public User(BinaryReader bR, AuthManager authManager) + { + switch (bR.ReadByte()) + { + case 1: + _displayName = bR.ReadShortString(); + _username = bR.ReadShortString(); + _passwordHashType = (UserPasswordHashType)bR.ReadByte(); + _iterations = bR.ReadInt32(); + _salt = bR.ReadBuffer(); + _passwordHash = bR.ReadShortString(); + _disabled = bR.ReadBoolean(); + _sessionTimeoutSeconds = bR.ReadInt32(); + + _previousSessionLoggedOn = bR.ReadDateTime(); + _previousSessionRemoteAddress = IPAddressExtension.ReadFrom(bR); + _recentSessionLoggedOn = bR.ReadDateTime(); + _recentSessionRemoteAddress = IPAddressExtension.ReadFrom(bR); + + { + int count = bR.ReadByte(); + _memberOfGroups = new ConcurrentDictionary(1, count); + + for (int i = 0; i < count; i++) + { + Group group = authManager.GetGroup(bR.ReadShortString()); + if (group is not null) + _memberOfGroups.TryAdd(group.Name.ToLower(), group); + } + } + break; + + default: + throw new InvalidDataException("Invalid data or version not supported."); + } + } + + #endregion + + #region internal + + internal void RenameGroup(string oldName) + { + if (_memberOfGroups.TryRemove(oldName.ToLower(), out Group renamedGroup)) + _memberOfGroups.TryAdd(renamedGroup.Name.ToLower(), renamedGroup); + } + + #endregion + + #region public + + public string GetPasswordHashFor(string password) + { + switch (_passwordHashType) + { + case UserPasswordHashType.OldScheme: + using (HMAC hmac = new HMACSHA256(Encoding.UTF8.GetBytes(password))) + { + return Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(_username))).ToLower(); + } + + case UserPasswordHashType.PBKDF2_SHA256: + return Convert.ToHexString(Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(password), _salt, _iterations, HashAlgorithmName.SHA256, 32)).ToLower(); + + default: + throw new NotSupportedException(); + } + } + + public void ChangePassword(string newPassword, int iterations = DEFAULT_ITERATIONS) + { + _passwordHashType = UserPasswordHashType.PBKDF2_SHA256; + _iterations = iterations; + + _salt = new byte[32]; + _rng.GetBytes(_salt); + + _passwordHash = GetPasswordHashFor(newPassword); + } + + public void LoadOldSchemeCredentials(string passwordHash) + { + _passwordHashType = UserPasswordHashType.OldScheme; + _passwordHash = passwordHash; + } + + public void LoggedInFrom(IPAddress remoteAddress) + { + _previousSessionLoggedOn = _recentSessionLoggedOn; + _previousSessionRemoteAddress = _recentSessionRemoteAddress; + + _recentSessionLoggedOn = DateTime.UtcNow; + _recentSessionRemoteAddress = remoteAddress; + } + + public void AddToGroup(Group group) + { + if (_memberOfGroups.Count == 255) + throw new InvalidOperationException("Cannot add user to group: user can be member of max 255 groups."); + + _memberOfGroups.TryAdd(group.Name.ToLower(), group); + } + + public bool RemoveFromGroup(Group group) + { + if (group.Name.Equals("everyone", StringComparison.OrdinalIgnoreCase)) + throw new InvalidOperationException("Access was denied."); + + return _memberOfGroups.TryRemove(group.Name.ToLower(), out _); + } + + public void SyncGroups(IReadOnlyDictionary groups) + { + //remove non-existent groups + foreach (KeyValuePair group in _memberOfGroups) + { + if (!groups.ContainsKey(group.Key)) + _memberOfGroups.TryRemove(group.Key, out _); + } + + //set new groups + foreach (KeyValuePair group in groups) + _memberOfGroups[group.Key] = group.Value; + } + + public bool IsMemberOfGroup(Group group) + { + return _memberOfGroups.ContainsKey(group.Name.ToLower()); + } + + public void WriteTo(BinaryWriter bW) + { + bW.Write((byte)1); + bW.WriteShortString(_displayName); + bW.WriteShortString(_username); + bW.Write((byte)_passwordHashType); + bW.Write(_iterations); + bW.WriteBuffer(_salt); + bW.WriteShortString(_passwordHash); + bW.Write(_disabled); + bW.Write(_sessionTimeoutSeconds); + + bW.Write(_previousSessionLoggedOn); + IPAddressExtension.WriteTo(_previousSessionRemoteAddress, bW); + bW.Write(_recentSessionLoggedOn); + IPAddressExtension.WriteTo(_recentSessionRemoteAddress, bW); + + bW.Write(Convert.ToByte(_memberOfGroups.Count)); + + foreach (KeyValuePair group in _memberOfGroups) + bW.WriteShortString(group.Value.Name.ToLower()); + } + + public override bool Equals(object obj) + { + if (obj is not User other) + return false; + + return _username.Equals(other._username, StringComparison.OrdinalIgnoreCase); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override string ToString() + { + return _username; + } + + public int CompareTo(User other) + { + return _username.CompareTo(other._username); + } + + #endregion + + #region properties + + public string DisplayName + { + get { return _displayName; } + set + { + if (value.Length > 255) + throw new ArgumentException("Display name length cannot exceed 255 characters.", nameof(DisplayName)); + + _displayName = value; + + if (string.IsNullOrWhiteSpace(_displayName)) + _displayName = _username; + } + } + + public string Username + { + get { return _username; } + set + { + if (_passwordHashType == UserPasswordHashType.OldScheme) + throw new InvalidOperationException("Cannot change username when using old password hash scheme. Change password once and try again."); + + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("Username cannot be null or empty.", nameof(Username)); + + if (value.Length > 255) + throw new ArgumentException("Username length cannot exceed 255 characters.", nameof(Username)); + + foreach (char c in value) + { + if ((c >= 97) && (c <= 122)) //[a-z] + continue; + + if ((c >= 65) && (c <= 90)) //[A-Z] + continue; + + if ((c >= 48) && (c <= 57)) //[0-9] + continue; + + if (c == '-') + continue; + + if (c == '_') + continue; + + if (c == '.') + continue; + + throw new ArgumentException("Username can contain only alpha numeric, '-', '_', or '.' characters.", nameof(Username)); + } + + _username = value.ToLower(); + } + } + + public UserPasswordHashType PasswordHashType + { get { return _passwordHashType; } } + + public string PasswordHash + { get { return _passwordHash; } } + + public bool Disabled + { + get { return _disabled; } + set { _disabled = value; } + } + + public int SessionTimeoutSeconds + { + get { return _sessionTimeoutSeconds; } + set + { + if ((value < 0) || (value > 604800)) + throw new ArgumentOutOfRangeException(nameof(SessionTimeoutSeconds), "Session timeout value must be between 0-604800 seconds."); + + _sessionTimeoutSeconds = value; + } + } + + public DateTime PreviousSessionLoggedOn + { get { return _previousSessionLoggedOn; } } + + public IPAddress PreviousSessionRemoteAddress + { get { return _previousSessionRemoteAddress; } } + + public DateTime RecentSessionLoggedOn + { get { return _recentSessionLoggedOn; } } + + public IPAddress RecentSessionRemoteAddress + { get { return _recentSessionRemoteAddress; } } + + public ICollection MemberOfGroups + { get { return _memberOfGroups.Values; } } + + #endregion + } +} From 5e1a68454972f66275215541820fbee4546bdf52 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:44:05 +0530 Subject: [PATCH 13/53] added Group implementation --- DnsServerCore/Auth/Group.cs | 140 ++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 DnsServerCore/Auth/Group.cs diff --git a/DnsServerCore/Auth/Group.cs b/DnsServerCore/Auth/Group.cs new file mode 100644 index 00000000..6a550290 --- /dev/null +++ b/DnsServerCore/Auth/Group.cs @@ -0,0 +1,140 @@ +/* +Technitium DNS Server +Copyright (C) 2022 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System; +using System.IO; +using TechnitiumLibrary.IO; + +namespace DnsServerCore.Auth +{ + class Group : IComparable + { + #region variables + + public const string ADMINISTRATORS = "Administrators"; + public const string EVERYONE = "Everyone"; + public const string DNS_ADMINISTRATORS = "DNS Administrators"; + public const string DHCP_ADMINISTRATORS = "DHCP Administrators"; + + string _name; + string _description; + + #endregion + + #region constructor + + public Group(string name, string description) + { + Name = name; + Description = description; + } + + public Group(BinaryReader bR) + { + switch (bR.ReadByte()) + { + case 1: + _name = bR.ReadShortString(); + _description = bR.ReadShortString(); + break; + + default: + throw new InvalidDataException("Invalid data or version not supported."); + } + } + + #endregion + + #region public + + public void WriteTo(BinaryWriter bW) + { + bW.Write((byte)1); + bW.WriteShortString(_name); + bW.WriteShortString(_description); + } + + public override bool Equals(object obj) + { + if (obj is not Group other) + return false; + + return _name.Equals(other._name, StringComparison.OrdinalIgnoreCase); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override string ToString() + { + return _name; + } + + public int CompareTo(Group other) + { + return _name.CompareTo(other._name); + } + + #endregion + + #region properties + + public string Name + { + get { return _name; } + set + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("Group name cannot be null or empty.", nameof(Name)); + + if (value.Length > 255) + throw new ArgumentException("Group name length cannot exceed 255 characters.", nameof(Name)); + + switch (_name?.ToLower()) + { + case "everyone": + case "administrators": + case "dns administrators": + case "dhcp administrators": + throw new InvalidOperationException("Access was denied."); + + default: + _name = value; + break; + } + } + } + + public string Description + { + get { return _description; } + set + { + if (value.Length > 255) + throw new ArgumentException("Group description length cannot exceed 255 characters.", nameof(Description)); + + _description = value; + } + } + + #endregion + } +} From cddf99c771b3e9b18211e10c9c4c3ff76cdfea7c Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:44:47 +0530 Subject: [PATCH 14/53] Added Permission implementation --- DnsServerCore/Auth/Permission.cs | 339 +++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 DnsServerCore/Auth/Permission.cs diff --git a/DnsServerCore/Auth/Permission.cs b/DnsServerCore/Auth/Permission.cs new file mode 100644 index 00000000..05ac906b --- /dev/null +++ b/DnsServerCore/Auth/Permission.cs @@ -0,0 +1,339 @@ +/* +Technitium DNS Server +Copyright (C) 2022 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using TechnitiumLibrary.IO; + +namespace DnsServerCore.Auth +{ + enum PermissionSection : byte + { + Unknown = 0, + Dashboard = 1, + Zones = 2, + Cache = 3, + Allowed = 4, + Blocked = 5, + Apps = 6, + DnsClient = 7, + Settings = 8, + DhcpServer = 9, + Administration = 10, + Logs = 11 + } + + [Flags] + enum PermissionFlag : byte + { + None = 0, + View = 1, + Modify = 2, + Delete = 4, + ViewModify = 3, + ViewModifyDelete = 7 + } + + class Permission : IComparable + { + #region variables + + readonly PermissionSection _section; + readonly string _subItemName; + + readonly ConcurrentDictionary _userPermissions; + readonly ConcurrentDictionary _groupPermissions; + + readonly ConcurrentDictionary _subItemPermissions; + + #endregion + + #region constructor + + public Permission(PermissionSection section, string subItemName = null) + { + _section = section; + _subItemName = subItemName; + + _userPermissions = new ConcurrentDictionary(1, 1); + _groupPermissions = new ConcurrentDictionary(1, 1); + + _subItemPermissions = new ConcurrentDictionary(1, 1); + } + + public Permission(BinaryReader bR, AuthManager authManager) + { + switch (bR.ReadByte()) + { + case 1: + _section = (PermissionSection)bR.ReadByte(); + + { + int count = bR.ReadByte(); + _userPermissions = new ConcurrentDictionary(1, count); + + for (int i = 0; i < count; i++) + { + User user = authManager.GetUser(bR.ReadShortString()); + PermissionFlag flag = (PermissionFlag)bR.ReadByte(); + + if (user is not null) + _userPermissions.TryAdd(user, flag); + } + } + + { + int count = bR.ReadByte(); + _groupPermissions = new ConcurrentDictionary(1, count); + + for (int i = 0; i < count; i++) + { + Group group = authManager.GetGroup(bR.ReadShortString()); + PermissionFlag flag = (PermissionFlag)bR.ReadByte(); + + if (group is not null) + _groupPermissions.TryAdd(group, flag); + } + } + + { + int count = bR.ReadByte(); + _subItemPermissions = new ConcurrentDictionary(1, count); + + for (int i = 0; i < count; i++) + { + string subItemName = bR.ReadShortString(); + Permission subItemPermission = new Permission(bR, authManager); + + _subItemPermissions.TryAdd(subItemName.ToLower(), subItemPermission); + } + } + + break; + + default: + throw new InvalidDataException("Invalid data or version not supported."); + } + } + + #endregion + + #region public + + public void SetPermission(User user, PermissionFlag flags) + { + _userPermissions[user] = flags; + } + + public void SyncPermissions(IReadOnlyDictionary userPermissions) + { + //remove non-existent permissions + foreach (KeyValuePair userPermission in _userPermissions) + { + if (!userPermissions.ContainsKey(userPermission.Key)) + _userPermissions.TryRemove(userPermission.Key, out _); + } + + //set new permissions + foreach (KeyValuePair userPermission in userPermissions) + _userPermissions[userPermission.Key] = userPermission.Value; + } + + public void SetSubItemPermission(string subItemName, User user, PermissionFlag flags) + { + Permission subItemPermission = _subItemPermissions.GetOrAdd(subItemName.ToLower(), delegate (string key) + { + return new Permission(_section, key); + }); + + subItemPermission.SetPermission(user, flags); + } + + public void SetPermission(Group group, PermissionFlag flags) + { + _groupPermissions[group] = flags; + } + + public void SyncPermissions(IReadOnlyDictionary groupPermissions) + { + //remove non-existent permissions + foreach (KeyValuePair groupPermission in _groupPermissions) + { + if (!groupPermissions.ContainsKey(groupPermission.Key)) + _groupPermissions.TryRemove(groupPermission.Key, out _); + } + + //set new permissions + foreach (KeyValuePair groupPermission in groupPermissions) + _groupPermissions[groupPermission.Key] = groupPermission.Value; + } + + public void SetSubItemPermission(string subItemName, Group group, PermissionFlag flags) + { + Permission subItemPermission = _subItemPermissions.GetOrAdd(subItemName.ToLower(), delegate (string key) + { + return new Permission(_section, key); + }); + + subItemPermission.SetPermission(group, flags); + } + + public bool RemovePermission(User user) + { + return _userPermissions.TryRemove(user, out _); + } + + public bool RemoveSubItemPermission(string subItemName, User user) + { + return _subItemPermissions.TryGetValue(subItemName.ToLower(), out Permission subItemPermission) && subItemPermission.RemovePermission(user); + } + + public bool RemovePermission(Group group) + { + return _groupPermissions.TryRemove(group, out _); + } + + public bool RemoveSubItemPermission(string subItemName, Group group) + { + return _subItemPermissions.TryGetValue(subItemName.ToLower(), out Permission subItemPermission) && subItemPermission.RemovePermission(group); + } + + public bool RemoveAllSubItemPermissions(User user) + { + bool removed = false; + + foreach (KeyValuePair subItemPermission in _subItemPermissions) + { + if (subItemPermission.Value.RemovePermission(user)) + removed = true; + } + + return removed; + } + + public bool RemoveAllSubItemPermissions(Group group) + { + bool removed = false; + + foreach (KeyValuePair subItemPermission in _subItemPermissions) + { + if (subItemPermission.Value.RemovePermission(group)) + removed = true; + } + + return removed; + } + + public bool RemoveAllSubItemPermissions(string subItemName) + { + return _subItemPermissions.TryRemove(subItemName, out _); + } + + public Permission GetSubItemPermission(string subItemName) + { + if (_subItemPermissions.TryGetValue(subItemName.ToLower(), out Permission subItemPermission)) + return subItemPermission; + + return null; + } + + public bool IsPermitted(User user, PermissionFlag flag) + { + if (_userPermissions.TryGetValue(user, out PermissionFlag userPermissions) && userPermissions.HasFlag(flag)) + return true; + + foreach (Group group in user.MemberOfGroups) + { + if (_groupPermissions.TryGetValue(group, out PermissionFlag groupPermissions) && groupPermissions.HasFlag(flag)) + return true; + } + + return false; + } + + public bool IsSubItemPermitted(string subItemName, User user, PermissionFlag flag) + { + return _subItemPermissions.TryGetValue(subItemName.ToLower(), out Permission subItemPermission) && subItemPermission.IsPermitted(user, flag); + } + + public void WriteTo(BinaryWriter bW) + { + bW.Write((byte)1); + bW.Write((byte)_section); + + { + bW.Write(Convert.ToByte(_userPermissions.Count)); + + foreach (KeyValuePair userPermission in _userPermissions) + { + bW.WriteShortString(userPermission.Key.Username); + bW.Write((byte)userPermission.Value); + } + } + + { + bW.Write(Convert.ToByte(_groupPermissions.Count)); + + foreach (KeyValuePair groupPermission in _groupPermissions) + { + bW.WriteShortString(groupPermission.Key.Name); + bW.Write((byte)groupPermission.Value); + } + } + + { + bW.Write(Convert.ToByte(_subItemPermissions.Count)); + + foreach (KeyValuePair subItemPermission in _subItemPermissions) + { + bW.WriteShortString(subItemPermission.Key); + subItemPermission.Value.WriteTo(bW); + } + } + } + + public int CompareTo(Permission other) + { + return _section.CompareTo(other._section); + } + + #endregion + + #region properties + + public PermissionSection Section + { get { return _section; } } + + public string SubItemName + { get { return _subItemName; } } + + public IReadOnlyDictionary UserPermissions + { get { return _userPermissions; } } + + public IReadOnlyDictionary GroupPermissions + { get { return _groupPermissions; } } + + public IReadOnlyDictionary SubItemPermissions + { get { return _subItemPermissions; } } + + #endregion + } +} From a99287e86555f6700cac2ae3478db62819449abb Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:45:38 +0530 Subject: [PATCH 15/53] Added AuthManager implementation --- DnsServerCore/Auth/AuthManager.cs | 837 ++++++++++++++++++++++++++++++ 1 file changed, 837 insertions(+) create mode 100644 DnsServerCore/Auth/AuthManager.cs diff --git a/DnsServerCore/Auth/AuthManager.cs b/DnsServerCore/Auth/AuthManager.cs new file mode 100644 index 00000000..9caa9120 --- /dev/null +++ b/DnsServerCore/Auth/AuthManager.cs @@ -0,0 +1,837 @@ +/* +Technitium DNS Server +Copyright (C) 2022 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace DnsServerCore.Auth +{ + sealed class AuthManager : IDisposable + { + #region variables + + readonly ConcurrentDictionary _groups = new ConcurrentDictionary(1, 4); + readonly ConcurrentDictionary _users = new ConcurrentDictionary(1, 4); + + readonly ConcurrentDictionary _permissions = new ConcurrentDictionary(1, 11); + + readonly ConcurrentDictionary _sessions = new ConcurrentDictionary(1, 10); + + readonly ConcurrentDictionary _failedLoginAttempts = new ConcurrentDictionary(1, 10); + const int MAX_LOGIN_ATTEMPTS = 5; + + readonly ConcurrentDictionary _blockedAddresses = new ConcurrentDictionary(1, 10); + const int BLOCK_ADDRESS_INTERVAL = 5 * 60 * 1000; + + readonly string _configFolder; + readonly LogManager _log; + + readonly object _lockObj = new object(); + bool _pendingSave; + readonly Timer _saveTimer; + const int SAVE_TIMER_INITIAL_INTERVAL = 10000; + + #endregion + + #region constructor + + public AuthManager(string configFolder, LogManager log) + { + _configFolder = configFolder; + _log = log; + + _saveTimer = new Timer(SaveTimerCallback, null, Timeout.Infinite, Timeout.Infinite); + } + + #endregion + + #region IDisposable + + bool _disposed; + + public void Dispose() + { + if (_disposed) + return; + + if (_saveTimer is not null) + _saveTimer.Dispose(); + + lock (_lockObj) + { + SaveConfigFileInternal(); + } + + _disposed = true; + } + + #endregion + + #region private + + private void SaveTimerCallback(object state) + { + try + { + lock (_lockObj) + { + _pendingSave = false; + SaveConfigFileInternal(); + } + } + catch (Exception ex) + { + _log.Write(ex); + } + } + + private void CreateDefaultConfig() + { + Group adminGroup = CreateGroup(Group.ADMINISTRATORS, "Super administrators"); + Group dnsAdminGroup = CreateGroup(Group.DNS_ADMINISTRATORS, "DNS service administrators"); + Group dhcpAdminGroup = CreateGroup(Group.DHCP_ADMINISTRATORS, "DHCP service administrators"); + Group everyoneGroup = CreateGroup(Group.EVERYONE, "All users"); + + SetPermission(PermissionSection.Dashboard, adminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Zones, adminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Cache, adminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Allowed, adminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Blocked, adminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Apps, adminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.DnsClient, adminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Settings, adminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.DhcpServer, adminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Administration, adminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Logs, adminGroup, PermissionFlag.ViewModifyDelete); + + SetPermission(PermissionSection.Zones, dnsAdminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Cache, dnsAdminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Allowed, dnsAdminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Blocked, dnsAdminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Apps, dnsAdminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.DnsClient, dnsAdminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Settings, dnsAdminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Logs, dnsAdminGroup, PermissionFlag.View); + + SetPermission(PermissionSection.Zones, dhcpAdminGroup, PermissionFlag.View); + SetPermission(PermissionSection.DnsClient, dhcpAdminGroup, PermissionFlag.View); + SetPermission(PermissionSection.DhcpServer, dhcpAdminGroup, PermissionFlag.ViewModifyDelete); + SetPermission(PermissionSection.Logs, dhcpAdminGroup, PermissionFlag.View); + + SetPermission(PermissionSection.Dashboard, everyoneGroup, PermissionFlag.View); + SetPermission(PermissionSection.Zones, everyoneGroup, PermissionFlag.View); + SetPermission(PermissionSection.Cache, everyoneGroup, PermissionFlag.View); + SetPermission(PermissionSection.Allowed, everyoneGroup, PermissionFlag.View); + SetPermission(PermissionSection.Blocked, everyoneGroup, PermissionFlag.View); + SetPermission(PermissionSection.Apps, everyoneGroup, PermissionFlag.View); + SetPermission(PermissionSection.DnsClient, everyoneGroup, PermissionFlag.View); + SetPermission(PermissionSection.DhcpServer, everyoneGroup, PermissionFlag.View); + SetPermission(PermissionSection.Logs, everyoneGroup, PermissionFlag.View); + + string adminPassword = Environment.GetEnvironmentVariable("DNS_SERVER_ADMIN_PASSWORD"); + string adminPasswordFile = Environment.GetEnvironmentVariable("DNS_SERVER_ADMIN_PASSWORD_FILE"); + + User adminUser; + + if (!string.IsNullOrEmpty(adminPassword)) + { + adminUser = CreateUser("Administrator", "admin", adminPassword); + } + else if (!string.IsNullOrEmpty(adminPasswordFile)) + { + try + { + using (StreamReader sR = new StreamReader(adminPasswordFile, true)) + { + string password = sR.ReadLine(); + adminUser = CreateUser("Administrator", "admin", password); + } + } + catch (Exception ex) + { + _log.Write(ex); + + adminUser = CreateUser("Administrator", "admin", "admin"); + } + } + else + { + adminUser = CreateUser("Administrator", "admin", "admin"); + } + + adminUser.AddToGroup(adminGroup); + } + + private void LoadConfigFileInternal(UserSession implantSession) + { + string configFile = Path.Combine(_configFolder, "auth.config"); + + try + { + bool passwordResetOption = false; + + if (!File.Exists(configFile)) + { + string passwordResetConfigFile = Path.Combine(_configFolder, "resetadmin.config"); + + if (File.Exists(passwordResetConfigFile)) + { + passwordResetOption = true; + configFile = passwordResetConfigFile; + } + } + + using (FileStream fS = new FileStream(configFile, FileMode.Open, FileAccess.Read)) + { + ReadConfigFrom(new BinaryReader(fS)); + } + + if (implantSession is not null) + { + UserSession newSession; + + using (MemoryStream mS = new MemoryStream()) + { + implantSession.WriteTo(new BinaryWriter(mS)); + + mS.Position = 0; + newSession = new UserSession(new BinaryReader(mS), this); + } + + _sessions.TryAdd(newSession.Token, newSession); + } + + _log.Write("DNS Server auth config file was loaded: " + configFile); + + if (passwordResetOption) + { + User adminUser = GetUser("admin"); + if (adminUser is null) + { + adminUser = CreateUser("Administrator", "admin", "admin"); + } + else + { + adminUser.ChangePassword("admin"); + adminUser.Disabled = false; + } + + adminUser.AddToGroup(GetGroup(Group.ADMINISTRATORS)); + + _log.Write("DNS Server reset password for user: admin"); + SaveConfigFileInternal(); + + try + { + File.Delete(configFile); + } + catch + { } + } + } + catch (FileNotFoundException) + { + _log.Write("DNS Server auth config file was not found: " + configFile); + _log.Write("DNS Server is restoring default auth config file."); + + CreateDefaultConfig(); + + SaveConfigFileInternal(); + } + catch (Exception ex) + { + _log.Write("DNS Server encountered an error while loading auth config file: " + configFile + "\r\n" + ex.ToString()); + _log.Write("Note: You may try deleting the auth config file to fix this issue. However, you will lose auth settings but, rest of the DNS settings and zone data wont be affected."); + throw; + } + } + + private void SaveConfigFileInternal() + { + string configFile = Path.Combine(_configFolder, "auth.config"); + + using (MemoryStream mS = new MemoryStream()) + { + //serialize config + WriteConfigTo(new BinaryWriter(mS)); + + //write config + mS.Position = 0; + + using (FileStream fS = new FileStream(configFile, FileMode.Create, FileAccess.Write)) + { + mS.CopyTo(fS); + } + } + + _log.Write("DNS Server auth config file was saved: " + configFile); + } + + private void ReadConfigFrom(BinaryReader bR) + { + if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "AS") //format + throw new InvalidDataException("DNS Server auth config file format is invalid."); + + int version = bR.ReadByte(); + switch (version) + { + case 1: + { + int count = bR.ReadByte(); + for (int i = 0; i < count; i++) + { + Group group = new Group(bR); + _groups.TryAdd(group.Name.ToLower(), group); + } + } + + { + int count = bR.ReadByte(); + for (int i = 0; i < count; i++) + { + User user = new User(bR, this); + _users.TryAdd(user.Username, user); + } + } + + { + int count = bR.ReadInt32(); + for (int i = 0; i < count; i++) + { + Permission permission = new Permission(bR, this); + _permissions.TryAdd(permission.Section, permission); + } + } + + { + int count = bR.ReadInt32(); + for (int i = 0; i < count; i++) + { + UserSession session = new UserSession(bR, this); + if (!session.HasExpired()) + _sessions.TryAdd(session.Token, session); + } + } + break; + + default: + throw new InvalidDataException("DNS Server auth config version not supported."); + } + } + + private void WriteConfigTo(BinaryWriter bW) + { + bW.Write(Encoding.ASCII.GetBytes("AS")); //format + bW.Write((byte)1); //version + + bW.Write(Convert.ToByte(_groups.Count)); + + foreach (KeyValuePair group in _groups) + group.Value.WriteTo(bW); + + bW.Write(Convert.ToByte(_users.Count)); + + foreach (KeyValuePair user in _users) + user.Value.WriteTo(bW); + + bW.Write(_permissions.Count); + + foreach (KeyValuePair permission in _permissions) + permission.Value.WriteTo(bW); + + List activeSessions = new List(_sessions.Count); + + foreach (KeyValuePair session in _sessions) + { + if (session.Value.HasExpired()) + _sessions.TryRemove(session.Key, out _); + else + activeSessions.Add(session.Value); + } + + bW.Write(activeSessions.Count); + + foreach (UserSession session in activeSessions) + session.WriteTo(bW); + } + + private void FailedLoginAttempt(IPAddress address) + { + _failedLoginAttempts.AddOrUpdate(address, 1, delegate (IPAddress key, int attempts) + { + return attempts + 1; + }); + } + + private bool LoginAttemptsExceedLimit(IPAddress address, int limit) + { + if (!_failedLoginAttempts.TryGetValue(address, out int attempts)) + return false; + + return attempts >= limit; + } + + private void ResetFailedLoginAttempt(IPAddress address) + { + _failedLoginAttempts.TryRemove(address, out _); + } + + private void BlockAddress(IPAddress address, int interval) + { + _blockedAddresses.TryAdd(address, DateTime.UtcNow.AddMilliseconds(interval)); + } + + private bool IsAddressBlocked(IPAddress address) + { + if (!_blockedAddresses.TryGetValue(address, out DateTime expiry)) + return false; + + if (expiry > DateTime.UtcNow) + { + return true; + } + else + { + UnblockAddress(address); + ResetFailedLoginAttempt(address); + + return false; + } + } + + private void UnblockAddress(IPAddress address) + { + _blockedAddresses.TryRemove(address, out _); + } + + #endregion + + #region public + + public User GetUser(string username) + { + if (_users.TryGetValue(username.ToLower(), out User user)) + return user; + + return null; + } + + public User CreateUser(string displayName, string username, string password, int iterations = User.DEFAULT_ITERATIONS) + { + username = username.ToLower(); + + User user = new User(displayName, username, password, iterations); + + if (_users.TryAdd(username, user)) + { + user.AddToGroup(GetGroup(Group.EVERYONE)); + return user; + } + + throw new DnsWebServiceException("User already exists: " + username); + } + + public void ChangeUsername(User user, string newUsername) + { + if (user.Username.Equals(newUsername, StringComparison.OrdinalIgnoreCase)) + return; + + string oldUsername = user.Username; + user.Username = newUsername; + + if (!_users.TryAdd(user.Username, user)) + { + user.Username = oldUsername; //revert + throw new DnsWebServiceException("User already exists: " + newUsername); + } + + _users.TryRemove(oldUsername, out _); + } + + public bool DeleteUser(string username) + { + if (_users.TryRemove(username.ToLower(), out User deletedUser)) + { + //delete all sessions + foreach (UserSession session in GetSessions(deletedUser)) + DeleteSession(session.Token); + + //delete all permissions + foreach (KeyValuePair permission in _permissions) + { + permission.Value.RemovePermission(deletedUser); + permission.Value.RemoveAllSubItemPermissions(deletedUser); + } + + return true; + } + + return false; + } + + public Group GetGroup(string name) + { + if (_groups.TryGetValue(name.ToLower(), out Group group)) + return group; + + return null; + } + + public List GetGroupMembers(Group group) + { + List members = new List(); + + foreach (KeyValuePair user in _users) + { + if (user.Value.IsMemberOfGroup(group)) + members.Add(user.Value); + } + + return members; + } + + public void SyncGroupMembers(Group group, IReadOnlyDictionary users) + { + //remove + foreach (KeyValuePair user in _users) + { + if (!users.ContainsKey(user.Key)) + user.Value.RemoveFromGroup(group); + } + + //set + foreach (KeyValuePair user in users) + user.Value.AddToGroup(group); + } + + public Group CreateGroup(string name, string description) + { + Group group = new Group(name, description); + + if (_groups.TryAdd(name.ToLower(), group)) + return group; + + throw new DnsWebServiceException("Group already exists: " + name); + } + + public void RenameGroup(Group group, string newGroupName) + { + if (group.Name.Equals(newGroupName, StringComparison.OrdinalIgnoreCase)) + { + group.Name = newGroupName; + return; + } + + string oldGroupName = group.Name; + group.Name = newGroupName; + + if (!_groups.TryAdd(group.Name.ToLower(), group)) + { + group.Name = oldGroupName; //revert + throw new DnsWebServiceException("Group already exists: " + newGroupName); + } + + _groups.TryRemove(oldGroupName.ToLower(), out _); + + //update users + foreach (KeyValuePair user in _users) + user.Value.RenameGroup(oldGroupName); + } + + public bool DeleteGroup(string name) + { + name = name.ToLower(); + + switch (name) + { + case "everyone": + case "administrators": + case "dns administrators": + case "dhcp administrators": + throw new InvalidOperationException("Access was denied."); + + default: + if (_groups.TryRemove(name, out Group deletedGroup)) + { + //remove all users from deleted group + foreach (KeyValuePair user in _users) + user.Value.RemoveFromGroup(deletedGroup); + + //delete all permissions + foreach (KeyValuePair permission in _permissions) + { + permission.Value.RemovePermission(deletedGroup); + permission.Value.RemoveAllSubItemPermissions(deletedGroup); + } + + return true; + } + + return false; + } + } + + public UserSession GetSession(string token) + { + if (_sessions.TryGetValue(token, out UserSession session)) + return session; + + return null; + } + + public List GetSessions(User user) + { + List userSessions = new List(); + + foreach (KeyValuePair session in _sessions) + { + if (session.Value.User.Equals(user) && !session.Value.HasExpired()) + userSessions.Add(session.Value); + } + + return userSessions; + } + + public async Task CreateSessionAsync(UserSessionType type, string tokenName, string username, string password, IPAddress remoteAddress, string userAgent) + { + if (IsAddressBlocked(remoteAddress)) + throw new DnsWebServiceException("Max limit of " + MAX_LOGIN_ATTEMPTS + " attempts exceeded. Access blocked for " + (BLOCK_ADDRESS_INTERVAL / 1000) + " seconds."); + + User user = GetUser(username); + + if ((user is null) || !user.PasswordHash.Equals(user.GetPasswordHashFor(password), StringComparison.Ordinal)) + { + if (password != "admin") + { + FailedLoginAttempt(remoteAddress); + + if (LoginAttemptsExceedLimit(remoteAddress, MAX_LOGIN_ATTEMPTS)) + BlockAddress(remoteAddress, BLOCK_ADDRESS_INTERVAL); + + await Task.Delay(1000); + } + + throw new DnsWebServiceException("Invalid username or password for user: " + username); + } + + ResetFailedLoginAttempt(remoteAddress); + + if (user.Disabled) + throw new DnsWebServiceException("User account is disabled. Please contact your administrator."); + + UserSession session = new UserSession(type, tokenName, user, remoteAddress, userAgent); + + if (!_sessions.TryAdd(session.Token, session)) + throw new DnsWebServiceException("Error while creating session. Please try again."); + + user.LoggedInFrom(remoteAddress); + + return session; + } + + public UserSession CreateApiToken(string tokenName, string username, IPAddress remoteAddress, string userAgent) + { + if (IsAddressBlocked(remoteAddress)) + throw new DnsWebServiceException("Max limit of " + MAX_LOGIN_ATTEMPTS + " attempts exceeded. Access blocked for " + (BLOCK_ADDRESS_INTERVAL / 1000) + " seconds."); + + User user = GetUser(username); + if (user is null) + throw new DnsWebServiceException("No such user exists: " + username); + + if (user.Disabled) + throw new DnsWebServiceException("Account is suspended."); + + UserSession session = new UserSession(UserSessionType.ApiToken, tokenName, user, remoteAddress, userAgent); + + if (!_sessions.TryAdd(session.Token, session)) + throw new DnsWebServiceException("Error while creating session. Please try again."); + + user.LoggedInFrom(remoteAddress); + + return session; + } + + public UserSession DeleteSession(string token) + { + if (_sessions.TryRemove(token, out UserSession session)) + return session; + + return null; + } + + public Permission GetPermission(PermissionSection section) + { + if (_permissions.TryGetValue(section, out Permission permission)) + return permission; + + return null; + } + + public Permission GetPermission(PermissionSection section, string subItemName) + { + if (_permissions.TryGetValue(section, out Permission permission)) + return permission.GetSubItemPermission(subItemName); + + return null; + } + + public void SetPermission(PermissionSection section, User user, PermissionFlag flags) + { + Permission permission = _permissions.GetOrAdd(section, delegate (PermissionSection key) + { + return new Permission(key); + }); + + permission.SetPermission(user, flags); + } + + public void SetPermission(PermissionSection section, string subItemName, User user, PermissionFlag flags) + { + Permission permission = _permissions.GetOrAdd(section, delegate (PermissionSection key) + { + return new Permission(key); + }); + + permission.SetSubItemPermission(subItemName, user, flags); + } + + public void SetPermission(PermissionSection section, Group group, PermissionFlag flags) + { + Permission permission = _permissions.GetOrAdd(section, delegate (PermissionSection key) + { + return new Permission(key); + }); + + permission.SetPermission(group, flags); + } + + public void SetPermission(PermissionSection section, string subItemName, Group group, PermissionFlag flags) + { + Permission permission = _permissions.GetOrAdd(section, delegate (PermissionSection key) + { + return new Permission(key); + }); + + permission.SetSubItemPermission(subItemName, group, flags); + } + + public bool RemovePermission(PermissionSection section, User user) + { + return _permissions.TryGetValue(section, out Permission permission) && permission.RemovePermission(user); + } + + public bool RemovePermission(PermissionSection section, string subItemName, User user) + { + return _permissions.TryGetValue(section, out Permission permission) && permission.RemoveSubItemPermission(subItemName, user); + } + + public bool RemovePermission(PermissionSection section, Group group) + { + return _permissions.TryGetValue(section, out Permission permission) && permission.RemovePermission(group); + } + + public bool RemovePermission(PermissionSection section, string subItemName, Group group) + { + return _permissions.TryGetValue(section, out Permission permission) && permission.RemoveSubItemPermission(subItemName, group); + } + + public bool RemoveAllPermissions(PermissionSection section, string subItemName) + { + return _permissions.TryGetValue(section, out Permission permission) && permission.RemoveAllSubItemPermissions(subItemName); + } + + public bool IsPermitted(PermissionSection section, User user, PermissionFlag flag) + { + return _permissions.TryGetValue(section, out Permission permission) && permission.IsPermitted(user, flag); + } + + public bool IsPermitted(PermissionSection section, string subItemName, User user, PermissionFlag flag) + { + return _permissions.TryGetValue(section, out Permission permission) && permission.IsSubItemPermitted(subItemName, user, flag); + } + + public void LoadOldConfig(string password, bool isPasswordHash) + { + User user = GetUser("admin"); + if (user is null) + user = CreateUser("Administrator", "admin", "admin"); + + user.AddToGroup(GetGroup(Group.ADMINISTRATORS)); + + if (isPasswordHash) + user.LoadOldSchemeCredentials(password); + else + user.ChangePassword(password); + + lock (_lockObj) + { + SaveConfigFileInternal(); + } + } + + public void LoadConfigFile(UserSession implantSession = null) + { + lock (_lockObj) + { + _groups.Clear(); + _users.Clear(); + _permissions.Clear(); + _sessions.Clear(); + + LoadConfigFileInternal(implantSession); + } + } + + public void SaveConfigFile() + { + lock (_lockObj) + { + if (_pendingSave) + return; + + _pendingSave = true; + _saveTimer.Change(SAVE_TIMER_INITIAL_INTERVAL, Timeout.Infinite); + } + } + + #endregion + + #region properties + + public ICollection Groups + { get { return _groups.Values; } } + + public ICollection Users + { get { return _users.Values; } } + + public ICollection Permissions + { get { return _permissions.Values; } } + + public ICollection Sessions + { get { return _sessions.Values; } } + + #endregion + } +} From 2da7eca963c2df08d4a07abcab284ecc73bfe710 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:45:56 +0530 Subject: [PATCH 16/53] updated named.root --- DnsServerCore/named.root | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DnsServerCore/named.root b/DnsServerCore/named.root index 151630ac..6a8a4fd9 100644 --- a/DnsServerCore/named.root +++ b/DnsServerCore/named.root @@ -9,8 +9,8 @@ ; on server FTP.INTERNIC.NET ; -OR- RS.INTERNIC.NET ; -; last update: March 16, 2022 -; related version of root zone: 2022031601 +; last update: August 31, 2022 +; related version of root zone: 2022083101 ; ; FORMERLY NS.INTERNIC.NET ; From 4da5842bacad1c594fbb1f106bae73b19ba631bd Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:46:50 +0530 Subject: [PATCH 17/53] added WebServiceAuthApi implementation --- DnsServerCore/WebServiceAuthApi.cs | 1086 ++++++++++++++++++++++++++++ 1 file changed, 1086 insertions(+) create mode 100644 DnsServerCore/WebServiceAuthApi.cs diff --git a/DnsServerCore/WebServiceAuthApi.cs b/DnsServerCore/WebServiceAuthApi.cs new file mode 100644 index 00000000..869e4548 --- /dev/null +++ b/DnsServerCore/WebServiceAuthApi.cs @@ -0,0 +1,1086 @@ +/* +Technitium DNS Server +Copyright (C) 2022 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using DnsServerCore.Auth; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace DnsServerCore +{ + sealed class WebServiceAuthApi + { + #region variables + + readonly DnsWebService _dnsWebService; + + #endregion + + #region constructor + + public WebServiceAuthApi(DnsWebService dnsWebService) + { + _dnsWebService = dnsWebService; + } + + #endregion + + #region private + + private void WriteCurrentSessionDetails(JsonTextWriter jsonWriter, UserSession currentSession, bool includeInfo) + { + if (currentSession.Type == UserSessionType.ApiToken) + { + jsonWriter.WritePropertyName("username"); + jsonWriter.WriteValue(currentSession.User.Username); + + jsonWriter.WritePropertyName("tokenName"); + jsonWriter.WriteValue(currentSession.TokenName); + + jsonWriter.WritePropertyName("token"); + jsonWriter.WriteValue(currentSession.Token); + } + else + { + jsonWriter.WritePropertyName("displayName"); + jsonWriter.WriteValue(currentSession.User.DisplayName); + + jsonWriter.WritePropertyName("username"); + jsonWriter.WriteValue(currentSession.User.Username); + + jsonWriter.WritePropertyName("token"); + jsonWriter.WriteValue(currentSession.Token); + } + + if (includeInfo) + { + jsonWriter.WritePropertyName("info"); + jsonWriter.WriteStartObject(); + + jsonWriter.WritePropertyName("version"); + jsonWriter.WriteValue(_dnsWebService.GetServerVersion()); + + jsonWriter.WritePropertyName("dnsServerDomain"); + jsonWriter.WriteValue(_dnsWebService.DnsServer.ServerDomain); + + jsonWriter.WritePropertyName("defaultRecordTtl"); + jsonWriter.WriteValue(_dnsWebService.ZonesApi.DefaultRecordTtl); + + jsonWriter.WritePropertyName("permissions"); + jsonWriter.WriteStartObject(); + + for (int i = 1; i <= 11; i++) + { + PermissionSection section = (PermissionSection)i; + + jsonWriter.WritePropertyName(section.ToString()); + jsonWriter.WriteStartObject(); + + jsonWriter.WritePropertyName("canView"); + jsonWriter.WriteValue(_dnsWebService.AuthManager.IsPermitted(section, currentSession.User, PermissionFlag.View)); + + jsonWriter.WritePropertyName("canModify"); + jsonWriter.WriteValue(_dnsWebService.AuthManager.IsPermitted(section, currentSession.User, PermissionFlag.Modify)); + + jsonWriter.WritePropertyName("canDelete"); + jsonWriter.WriteValue(_dnsWebService.AuthManager.IsPermitted(section, currentSession.User, PermissionFlag.Delete)); + + jsonWriter.WriteEndObject(); + } + + jsonWriter.WriteEndObject(); + + jsonWriter.WriteEndObject(); + } + } + + private void WriteUserDetails(JsonTextWriter jsonWriter, User user, UserSession currentSession, bool includeMoreDetails, bool includeGroups) + { + jsonWriter.WritePropertyName("displayName"); + jsonWriter.WriteValue(user.DisplayName); + + jsonWriter.WritePropertyName("username"); + jsonWriter.WriteValue(user.Username); + + jsonWriter.WritePropertyName("disabled"); + jsonWriter.WriteValue(user.Disabled); + + jsonWriter.WritePropertyName("previousSessionLoggedOn"); + jsonWriter.WriteValue(user.PreviousSessionLoggedOn); + + jsonWriter.WritePropertyName("previousSessionRemoteAddress"); + jsonWriter.WriteValue(user.PreviousSessionRemoteAddress.ToString()); + + jsonWriter.WritePropertyName("recentSessionLoggedOn"); + jsonWriter.WriteValue(user.RecentSessionLoggedOn); + + jsonWriter.WritePropertyName("recentSessionRemoteAddress"); + jsonWriter.WriteValue(user.RecentSessionRemoteAddress.ToString()); + + if (includeMoreDetails) + { + jsonWriter.WritePropertyName("sessionTimeoutSeconds"); + jsonWriter.WriteValue(user.SessionTimeoutSeconds); + + jsonWriter.WritePropertyName("memberOfGroups"); + jsonWriter.WriteStartArray(); + + List memberOfGroups = new List(user.MemberOfGroups); + memberOfGroups.Sort(); + + foreach (Group group in memberOfGroups) + { + if (group.Name.Equals("Everyone", StringComparison.OrdinalIgnoreCase)) + continue; + + jsonWriter.WriteValue(group.Name); + } + + jsonWriter.WriteEndArray(); + + jsonWriter.WritePropertyName("sessions"); + jsonWriter.WriteStartArray(); + + List sessions = _dnsWebService.AuthManager.GetSessions(user); + sessions.Sort(); + + foreach (UserSession session in sessions) + WriteUserSessionDetails(jsonWriter, session, currentSession); + + jsonWriter.WriteEndArray(); + } + + if (includeGroups) + { + List groups = new List(_dnsWebService.AuthManager.Groups); + groups.Sort(); + + jsonWriter.WritePropertyName("groups"); + jsonWriter.WriteStartArray(); + + foreach (Group group in groups) + { + if (group.Name.Equals("Everyone", StringComparison.OrdinalIgnoreCase)) + continue; + + jsonWriter.WriteValue(group.Name); + } + + jsonWriter.WriteEndArray(); + } + } + + private static void WriteUserSessionDetails(JsonTextWriter jsonWriter, UserSession session, UserSession currentSession) + { + jsonWriter.WriteStartObject(); + + jsonWriter.WritePropertyName("username"); + jsonWriter.WriteValue(session.User.Username); + + jsonWriter.WritePropertyName("isCurrentSession"); + jsonWriter.WriteValue(session.Equals(currentSession)); + + jsonWriter.WritePropertyName("partialToken"); + jsonWriter.WriteValue(session.Token.Substring(0, 16)); + + jsonWriter.WritePropertyName("type"); + jsonWriter.WriteValue(session.Type.ToString()); + + jsonWriter.WritePropertyName("tokenName"); + jsonWriter.WriteValue(session.TokenName); + + jsonWriter.WritePropertyName("lastSeen"); + jsonWriter.WriteValue(session.LastSeen); + + jsonWriter.WritePropertyName("lastSeenRemoteAddress"); + jsonWriter.WriteValue(session.LastSeenRemoteAddress.ToString()); + + jsonWriter.WritePropertyName("lastSeenUserAgent"); + jsonWriter.WriteValue(session.LastSeenUserAgent); + + jsonWriter.WriteEndObject(); + } + + private void WriteGroupDetails(JsonTextWriter jsonWriter, Group group, bool includeMembers, bool includeUsers) + { + jsonWriter.WritePropertyName("name"); + jsonWriter.WriteValue(group.Name); + + jsonWriter.WritePropertyName("description"); + jsonWriter.WriteValue(group.Description); + + if (includeMembers) + { + jsonWriter.WritePropertyName("members"); + jsonWriter.WriteStartArray(); + + List members = _dnsWebService.AuthManager.GetGroupMembers(group); + members.Sort(); + + foreach (User user in members) + jsonWriter.WriteValue(user.Username); + + jsonWriter.WriteEndArray(); + } + + if (includeUsers) + { + List users = new List(_dnsWebService.AuthManager.Users); + users.Sort(); + + jsonWriter.WritePropertyName("users"); + jsonWriter.WriteStartArray(); + + foreach (User user in users) + jsonWriter.WriteValue(user.Username); + + jsonWriter.WriteEndArray(); + } + } + + private void WritePermissionDetails(JsonTextWriter jsonWriter, Permission permission, string subItem, bool includeUsersAndGroups) + { + jsonWriter.WritePropertyName("section"); + jsonWriter.WriteValue(permission.Section.ToString()); + + if (subItem is not null) + { + jsonWriter.WritePropertyName("subItem"); + jsonWriter.WriteValue(subItem.Length == 0 ? "." : subItem); + } + + jsonWriter.WritePropertyName("userPermissions"); + jsonWriter.WriteStartArray(); + + List> userPermissions = new List>(permission.UserPermissions); + + userPermissions.Sort(delegate (KeyValuePair x, KeyValuePair y) + { + return x.Key.Username.CompareTo(y.Key.Username); + }); + + foreach (KeyValuePair userPermission in userPermissions) + { + jsonWriter.WriteStartObject(); + + jsonWriter.WritePropertyName("username"); + jsonWriter.WriteValue(userPermission.Key.Username); + + jsonWriter.WritePropertyName("canView"); + jsonWriter.WriteValue(userPermission.Value.HasFlag(PermissionFlag.View)); + + jsonWriter.WritePropertyName("canModify"); + jsonWriter.WriteValue(userPermission.Value.HasFlag(PermissionFlag.Modify)); + + jsonWriter.WritePropertyName("canDelete"); + jsonWriter.WriteValue(userPermission.Value.HasFlag(PermissionFlag.Delete)); + + jsonWriter.WriteEndObject(); + } + + jsonWriter.WriteEndArray(); + + jsonWriter.WritePropertyName("groupPermissions"); + jsonWriter.WriteStartArray(); + + List> groupPermissions = new List>(permission.GroupPermissions); + + groupPermissions.Sort(delegate (KeyValuePair x, KeyValuePair y) + { + return x.Key.Name.CompareTo(y.Key.Name); + }); + + foreach (KeyValuePair groupPermission in groupPermissions) + { + jsonWriter.WriteStartObject(); + + jsonWriter.WritePropertyName("name"); + jsonWriter.WriteValue(groupPermission.Key.Name); + + jsonWriter.WritePropertyName("canView"); + jsonWriter.WriteValue(groupPermission.Value.HasFlag(PermissionFlag.View)); + + jsonWriter.WritePropertyName("canModify"); + jsonWriter.WriteValue(groupPermission.Value.HasFlag(PermissionFlag.Modify)); + + jsonWriter.WritePropertyName("canDelete"); + jsonWriter.WriteValue(groupPermission.Value.HasFlag(PermissionFlag.Delete)); + + jsonWriter.WriteEndObject(); + } + + jsonWriter.WriteEndArray(); + + if (includeUsersAndGroups) + { + List users = new List(_dnsWebService.AuthManager.Users); + users.Sort(); + + List groups = new List(_dnsWebService.AuthManager.Groups); + groups.Sort(); + + jsonWriter.WritePropertyName("users"); + jsonWriter.WriteStartArray(); + + foreach (User user in users) + jsonWriter.WriteValue(user.Username); + + jsonWriter.WriteEndArray(); + + jsonWriter.WritePropertyName("groups"); + jsonWriter.WriteStartArray(); + + foreach (Group group in groups) + jsonWriter.WriteValue(group.Name); + + jsonWriter.WriteEndArray(); + } + } + + #endregion + + #region public + + public async Task LoginAsync(HttpListenerRequest request, JsonTextWriter jsonWriter, UserSessionType sessionType) + { + string strUsername = request.QueryString["user"]; + if (string.IsNullOrEmpty(strUsername)) + throw new DnsWebServiceException("Parameter 'user' missing."); + + string strPassword = request.QueryString["pass"]; + if (string.IsNullOrEmpty(strPassword)) + throw new DnsWebServiceException("Parameter 'pass' missing."); + + string strTokenName = null; + + if (sessionType == UserSessionType.ApiToken) + { + strTokenName = request.QueryString["tokenName"]; + if (string.IsNullOrEmpty(strTokenName)) + throw new DnsWebServiceException("Parameter 'tokenName' missing."); + } + + bool includeInfo; + string strIncludeInfo = request.QueryString["includeInfo"]; + if (string.IsNullOrEmpty(strIncludeInfo)) + includeInfo = false; + else + includeInfo = bool.Parse(strIncludeInfo); + + IPEndPoint remoteEP = DnsWebService.GetRequestRemoteEndPoint(request); + + UserSession session = await _dnsWebService.AuthManager.CreateSessionAsync(sessionType, strTokenName, strUsername, strPassword, remoteEP.Address, request.UserAgent); + + _dnsWebService.Log.Write(remoteEP, "[" + session.User.Username + "] User logged in."); + + _dnsWebService.AuthManager.SaveConfigFile(); + + WriteCurrentSessionDetails(jsonWriter, session, includeInfo); + } + + public void Logout(HttpListenerRequest request) + { + string strToken = request.QueryString["token"]; + if (string.IsNullOrEmpty(strToken)) + throw new DnsWebServiceException("Parameter 'token' missing."); + + UserSession session = _dnsWebService.AuthManager.DeleteSession(strToken); + if (session is not null) + { + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] User logged out."); + + _dnsWebService.AuthManager.SaveConfigFile(); + } + } + + public void GetCurrentSessionDetails(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + if (!_dnsWebService.TryGetSession(request, out UserSession session)) + throw new InvalidTokenWebServiceException("Invalid token or session expired."); + + WriteCurrentSessionDetails(jsonWriter, session, true); + } + + public void ChangePassword(HttpListenerRequest request) + { + UserSession session = _dnsWebService.GetSession(request); + + if (session.Type != UserSessionType.Standard) + throw new DnsWebServiceException("Access was denied."); + + string strPassword = request.QueryString["pass"]; + if (string.IsNullOrEmpty(strPassword)) + throw new DnsWebServiceException("Parameter 'pass' missing."); + + session.User.ChangePassword(strPassword); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Password was changed successfully."); + + _dnsWebService.AuthManager.SaveConfigFile(); + } + + public void GetProfile(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + UserSession session = _dnsWebService.GetSession(request); + + WriteUserDetails(jsonWriter, session.User, session, true, false); + } + + public void SetProfile(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + UserSession session = _dnsWebService.GetSession(request); + + if (session.Type != UserSessionType.Standard) + throw new DnsWebServiceException("Access was denied."); + + string strDisplayName = request.QueryString["displayName"]; + if (!string.IsNullOrEmpty(strDisplayName)) + session.User.DisplayName = strDisplayName; + + string strSessionTimeoutSeconds = request.QueryString["sessionTimeoutSeconds"]; + if (!string.IsNullOrEmpty(strSessionTimeoutSeconds)) + session.User.SessionTimeoutSeconds = int.Parse(strSessionTimeoutSeconds); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] User profile was updated successfully."); + + _dnsWebService.AuthManager.SaveConfigFile(); + + WriteUserDetails(jsonWriter, session.User, session, true, false); + } + + public void ListSessions(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + UserSession session = _dnsWebService.GetSession(request); + + jsonWriter.WritePropertyName("sessions"); + jsonWriter.WriteStartArray(); + + List sessions = new List(_dnsWebService.AuthManager.Sessions); + sessions.Sort(); + + foreach (UserSession activeSession in sessions) + { + if (!activeSession.HasExpired()) + WriteUserSessionDetails(jsonWriter, activeSession, session); + } + + jsonWriter.WriteEndArray(); + } + + public void CreateApiToken(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + string strUsername = request.QueryString["user"]; + if (string.IsNullOrEmpty(strUsername)) + throw new DnsWebServiceException("Parameter 'user' missing."); + + string strTokenName = request.QueryString["tokenName"]; + if (string.IsNullOrEmpty(strTokenName)) + throw new DnsWebServiceException("Parameter 'tokenName' missing."); + + IPEndPoint remoteEP = DnsWebService.GetRequestRemoteEndPoint(request); + + UserSession session = _dnsWebService.AuthManager.CreateApiToken(strTokenName, strUsername, remoteEP.Address, request.UserAgent); + + _dnsWebService.Log.Write(remoteEP, "[" + session.User.Username + "] API token [" + strTokenName + "] was created successfully for user: " + strUsername); + + _dnsWebService.AuthManager.SaveConfigFile(); + + jsonWriter.WritePropertyName("username"); + jsonWriter.WriteValue(session.User.Username); + + jsonWriter.WritePropertyName("tokenName"); + jsonWriter.WriteValue(session.TokenName); + + jsonWriter.WritePropertyName("token"); + jsonWriter.WriteValue(session.Token); + } + + public void DeleteSession(HttpListenerRequest request, bool isAdminContext) + { + string strPartialToken = request.QueryString["partialToken"]; + if (string.IsNullOrEmpty(strPartialToken)) + throw new DnsWebServiceException("Parameter 'partialToken' missing."); + + UserSession session = _dnsWebService.GetSession(request); + + if (session.Token.StartsWith(strPartialToken)) + throw new InvalidOperationException("Invalid operation: cannot delete current session."); + + string token = null; + + foreach (UserSession activeSession in _dnsWebService.AuthManager.Sessions) + { + if (activeSession.Token.StartsWith(strPartialToken)) + { + token = activeSession.Token; + break; + } + } + + if (token is null) + throw new DnsWebServiceException("No such active session was found for partial token: " + strPartialToken); + + if (!isAdminContext) + { + UserSession sessionToDelete = _dnsWebService.AuthManager.GetSession(token); + if (sessionToDelete.User != session.User) + throw new DnsWebServiceException("Access was denied."); + } + + UserSession deletedSession = _dnsWebService.AuthManager.DeleteSession(token); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] User session [" + strPartialToken + "] was deleted successfully for user: " + deletedSession.User.Username); + + _dnsWebService.AuthManager.SaveConfigFile(); + } + + public void ListUsers(JsonTextWriter jsonWriter) + { + List users = new List(_dnsWebService.AuthManager.Users); + users.Sort(); + + jsonWriter.WritePropertyName("users"); + jsonWriter.WriteStartArray(); + + foreach (User user in users) + { + jsonWriter.WriteStartObject(); + + WriteUserDetails(jsonWriter, user, null, false, false); + + jsonWriter.WriteEndObject(); + } + + jsonWriter.WriteEndArray(); + } + + public void CreateUser(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + string strDisplayName = request.QueryString["displayName"]; + + string strUsername = request.QueryString["user"]; + if (string.IsNullOrEmpty(strUsername)) + throw new DnsWebServiceException("Parameter 'user' missing."); + + string strPassword = request.QueryString["pass"]; + if (string.IsNullOrEmpty(strPassword)) + throw new DnsWebServiceException("Parameter 'pass' missing."); + + User user = _dnsWebService.AuthManager.CreateUser(strDisplayName, strUsername, strPassword); + + UserSession session = _dnsWebService.GetSession(request); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] User account was created successfully with username: " + user.Username); + + _dnsWebService.AuthManager.SaveConfigFile(); + + WriteUserDetails(jsonWriter, user, null, false, false); + } + + public void GetUserDetails(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + string strUsername = request.QueryString["user"]; + if (string.IsNullOrEmpty(strUsername)) + throw new DnsWebServiceException("Parameter 'user' missing."); + + bool includeGroups; + string strIncludeGroups = request.QueryString["includeGroups"]; + if (string.IsNullOrEmpty(strIncludeGroups)) + includeGroups = false; + else + includeGroups = bool.Parse(strIncludeGroups); + + User user = _dnsWebService.AuthManager.GetUser(strUsername); + if (user is null) + throw new DnsWebServiceException("No such user exists: " + strUsername); + + WriteUserDetails(jsonWriter, user, null, true, includeGroups); + } + + public void SetUserDetails(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + string strUsername = request.QueryString["user"]; + if (string.IsNullOrEmpty(strUsername)) + throw new DnsWebServiceException("Parameter 'user' missing."); + + User user = _dnsWebService.AuthManager.GetUser(strUsername); + if (user is null) + throw new DnsWebServiceException("No such user exists: " + strUsername); + + string strDisplayName = request.QueryString["displayName"]; + if (!string.IsNullOrEmpty(strDisplayName)) + user.DisplayName = strDisplayName; + + string strNewUsername = request.QueryString["newUser"]; + if (!string.IsNullOrEmpty(strNewUsername)) + _dnsWebService.AuthManager.ChangeUsername(user, strNewUsername); + + UserSession session = _dnsWebService.GetSession(request); + + string strDisabled = request.QueryString["disabled"]; + if (!string.IsNullOrEmpty(strDisabled) && (session.User != user)) //to avoid self lockout + { + user.Disabled = bool.Parse(strDisabled); + + if (user.Disabled) + { + foreach (UserSession userSession in _dnsWebService.AuthManager.Sessions) + { + if (userSession.Type == UserSessionType.ApiToken) + continue; + + if (userSession.User == user) + _dnsWebService.AuthManager.DeleteSession(userSession.Token); + } + } + } + + string strSessionTimeoutSeconds = request.QueryString["sessionTimeoutSeconds"]; + if (!string.IsNullOrEmpty(strSessionTimeoutSeconds)) + user.SessionTimeoutSeconds = int.Parse(strSessionTimeoutSeconds); + + string strNewPassword = request.QueryString["newPass"]; + if (!string.IsNullOrWhiteSpace(strNewPassword)) + { + int iterations; + string strIterations = request.QueryString["iterations"]; + if (string.IsNullOrEmpty(strIterations)) + iterations = User.DEFAULT_ITERATIONS; + else + iterations = int.Parse(strIterations); + + user.ChangePassword(strNewPassword, iterations); + } + + string strMemberOfGroups = request.QueryString["memberOfGroups"]; + if (strMemberOfGroups is not null) + { + string[] parts = strMemberOfGroups.Split(','); + Dictionary groups = new Dictionary(parts.Length); + + foreach (string part in parts) + { + if (part.Length == 0) + continue; + + Group group = _dnsWebService.AuthManager.GetGroup(part); + if (group is null) + throw new DnsWebServiceException("No such group exists: " + part); + + groups.Add(group.Name.ToLower(), group); + } + + //ensure user is member of everyone group + Group everyone = _dnsWebService.AuthManager.GetGroup(Group.EVERYONE); + groups[everyone.Name.ToLower()] = everyone; + + if (session.User == user) + { + //ensure current admin user is member of administrators group to avoid self lockout + Group admins = _dnsWebService.AuthManager.GetGroup(Group.ADMINISTRATORS); + groups[admins.Name.ToLower()] = admins; + } + + user.SyncGroups(groups); + } + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] User account details were updated successfully for user: " + strUsername); + + _dnsWebService.AuthManager.SaveConfigFile(); + + WriteUserDetails(jsonWriter, user, null, true, false); + } + + public void DeleteUser(HttpListenerRequest request) + { + string strUsername = request.QueryString["user"]; + if (string.IsNullOrEmpty(strUsername)) + throw new DnsWebServiceException("Parameter 'user' missing."); + + UserSession session = _dnsWebService.GetSession(request); + + if (session.User.Username.Equals(strUsername, StringComparison.OrdinalIgnoreCase)) + throw new InvalidOperationException("Invalid operation: cannot delete current user."); + + if (!_dnsWebService.AuthManager.DeleteUser(strUsername)) + throw new DnsWebServiceException("Failed to delete user: " + strUsername); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] User account was deleted successfully with username: " + strUsername); + + _dnsWebService.AuthManager.SaveConfigFile(); + } + + public void ListGroups(JsonTextWriter jsonWriter) + { + List groups = new List(_dnsWebService.AuthManager.Groups); + groups.Sort(); + + jsonWriter.WritePropertyName("groups"); + jsonWriter.WriteStartArray(); + + foreach (Group group in groups) + { + if (group.Name.Equals("Everyone", StringComparison.OrdinalIgnoreCase)) + continue; + + jsonWriter.WriteStartObject(); + + WriteGroupDetails(jsonWriter, group, false, false); + + jsonWriter.WriteEndObject(); + } + + jsonWriter.WriteEndArray(); + } + + public void CreateGroup(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + string strGroup = request.QueryString["group"]; + if (string.IsNullOrEmpty(strGroup)) + throw new DnsWebServiceException("Parameter 'group' missing."); + + string strDescription = request.QueryString["description"]; + if (string.IsNullOrEmpty(strDescription)) + strDescription = ""; + + Group group = _dnsWebService.AuthManager.CreateGroup(strGroup, strDescription); + + UserSession session = _dnsWebService.GetSession(request); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Group was created successfully with name: " + group.Name); + + _dnsWebService.AuthManager.SaveConfigFile(); + + WriteGroupDetails(jsonWriter, group, false, false); + } + + public void GetGroupDetails(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + string strGroup = request.QueryString["group"]; + if (string.IsNullOrEmpty(strGroup)) + throw new DnsWebServiceException("Parameter 'group' missing."); + + bool includeUsers; + string strIncludeGroups = request.QueryString["includeUsers"]; + if (string.IsNullOrEmpty(strIncludeGroups)) + includeUsers = false; + else + includeUsers = bool.Parse(strIncludeGroups); + + Group group = _dnsWebService.AuthManager.GetGroup(strGroup); + if (group is null) + throw new DnsWebServiceException("No such group exists: " + strGroup); + + WriteGroupDetails(jsonWriter, group, true, includeUsers); + } + + public void SetGroupDetails(HttpListenerRequest request, JsonTextWriter jsonWriter) + { + string strGroup = request.QueryString["group"]; + if (string.IsNullOrEmpty(strGroup)) + throw new DnsWebServiceException("Parameter 'group' missing."); + + Group group = _dnsWebService.AuthManager.GetGroup(strGroup); + if (group is null) + throw new DnsWebServiceException("No such group exists: " + strGroup); + + string strNewGroup = request.QueryString["newGroup"]; + if (!string.IsNullOrEmpty(strNewGroup)) + _dnsWebService.AuthManager.RenameGroup(group, strNewGroup); + + string strDescription = request.QueryString["description"]; + if (!string.IsNullOrEmpty(strDescription)) + group.Description = strDescription; + + UserSession session = _dnsWebService.GetSession(request); + + string strMembers = request.QueryString["members"]; + if (strMembers is not null) + { + string[] parts = strMembers.Split(','); + Dictionary users = new Dictionary(); + + foreach (string part in parts) + { + if (part.Length == 0) + continue; + + User user = _dnsWebService.AuthManager.GetUser(part); + if (user is null) + throw new DnsWebServiceException("No such user exists: " + part); + + users.Add(user.Username, user); + } + + if (group.Name.Equals("administrators", StringComparison.OrdinalIgnoreCase)) + users[session.User.Username] = session.User; //ensure current admin user is member of administrators group to avoid self lockout + + _dnsWebService.AuthManager.SyncGroupMembers(group, users); + } + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Group details were updated successfully for group: " + strGroup); + + _dnsWebService.AuthManager.SaveConfigFile(); + + WriteGroupDetails(jsonWriter, group, true, false); + } + + public void DeleteGroup(HttpListenerRequest request) + { + string strGroup = request.QueryString["group"]; + if (string.IsNullOrEmpty(strGroup)) + throw new DnsWebServiceException("Parameter 'group' missing."); + + if (!_dnsWebService.AuthManager.DeleteGroup(strGroup)) + throw new DnsWebServiceException("Failed to delete group: " + strGroup); + + UserSession session = _dnsWebService.GetSession(request); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Group was deleted successfully with name: " + strGroup); + + _dnsWebService.AuthManager.SaveConfigFile(); + } + + public void ListPermissions(JsonTextWriter jsonWriter) + { + List permissions = new List(_dnsWebService.AuthManager.Permissions); + permissions.Sort(); + + jsonWriter.WritePropertyName("permissions"); + jsonWriter.WriteStartArray(); + + foreach (Permission permission in permissions) + { + jsonWriter.WriteStartObject(); + + WritePermissionDetails(jsonWriter, permission, null, false); + + jsonWriter.WriteEndObject(); + } + + jsonWriter.WriteEndArray(); + } + + public void GetPermissionDetails(HttpListenerRequest request, JsonTextWriter jsonWriter, PermissionSection section) + { + if (section == PermissionSection.Unknown) + { + string strSection = request.QueryString["section"]; + if (string.IsNullOrEmpty(strSection)) + throw new DnsWebServiceException("Parameter 'section' missing."); + + if (!Enum.TryParse(strSection, true, out section)) + throw new DnsWebServiceException("No such permission section exists: " + strSection); + } + + string strSubItem; + + switch (section) + { + case PermissionSection.Zones: + strSubItem = request.QueryString["zone"]; + + if (strSubItem is not null) + strSubItem = strSubItem.TrimEnd('.'); + + break; + + default: + strSubItem = null; + break; + } + + bool includeUsersAndGroups; + string strIncludeUsersAndGroups = request.QueryString["includeUsersAndGroups"]; + if (string.IsNullOrEmpty(strIncludeUsersAndGroups)) + includeUsersAndGroups = false; + else + includeUsersAndGroups = bool.Parse(strIncludeUsersAndGroups); + + if (strSubItem is not null) + { + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(section, strSubItem, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + } + + Permission permission; + + if (strSubItem is null) + permission = _dnsWebService.AuthManager.GetPermission(section); + else + permission = _dnsWebService.AuthManager.GetPermission(section, strSubItem); + + if (permission is null) + throw new DnsWebServiceException("No permissions exists for section: " + section.ToString() + (strSubItem is null ? "" : "/" + strSubItem)); + + WritePermissionDetails(jsonWriter, permission, strSubItem, includeUsersAndGroups); + } + + public void SetPermissionsDetails(HttpListenerRequest request, JsonTextWriter jsonWriter, PermissionSection section) + { + if (section == PermissionSection.Unknown) + { + string strSection = request.QueryString["section"]; + if (string.IsNullOrEmpty(strSection)) + throw new DnsWebServiceException("Parameter 'section' missing."); + + if (!Enum.TryParse(strSection, true, out section)) + throw new DnsWebServiceException("No such permission section exists: " + strSection); + } + + string strSubItem; + + switch (section) + { + case PermissionSection.Zones: + strSubItem = request.QueryString["zone"]; + + if (strSubItem is not null) + strSubItem = strSubItem.TrimEnd('.'); + + break; + + default: + strSubItem = null; + break; + } + + UserSession session = _dnsWebService.GetSession(request); + + if (strSubItem is not null) + { + if (!_dnsWebService.AuthManager.IsPermitted(section, strSubItem, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + } + + Permission permission; + + if (strSubItem is null) + permission = _dnsWebService.AuthManager.GetPermission(section); + else + permission = _dnsWebService.AuthManager.GetPermission(section, strSubItem); + + if (permission is null) + throw new DnsWebServiceException("No permissions exists for section: " + section.ToString() + (strSubItem is null ? "" : "/" + strSubItem)); + + string strUserPermissions = request.QueryString["userPermissions"]; + if (strUserPermissions is not null) + { + string[] parts = strUserPermissions.Split('|'); + Dictionary userPermissions = new Dictionary(); + + for (int i = 0; i < parts.Length; i += 4) + { + if (parts[i].Length == 0) + continue; + + User user = _dnsWebService.AuthManager.GetUser(parts[i]); + bool canView = bool.Parse(parts[i + 1]); + bool canModify = bool.Parse(parts[i + 2]); + bool canDelete = bool.Parse(parts[i + 3]); + + if (user is not null) + { + PermissionFlag permissionFlag = PermissionFlag.None; + + if (canView) + permissionFlag |= PermissionFlag.View; + + if (canModify) + permissionFlag |= PermissionFlag.Modify; + + if (canDelete) + permissionFlag |= PermissionFlag.Delete; + + userPermissions[user] = permissionFlag; + } + } + + permission.SyncPermissions(userPermissions); + } + + string strGroupPermissions = request.QueryString["groupPermissions"]; + if (strGroupPermissions is not null) + { + string[] parts = strGroupPermissions.Split('|'); + Dictionary groupPermissions = new Dictionary(); + + for (int i = 0; i < parts.Length; i += 4) + { + if (parts[i].Length == 0) + continue; + + Group group = _dnsWebService.AuthManager.GetGroup(parts[i]); + bool canView = bool.Parse(parts[i + 1]); + bool canModify = bool.Parse(parts[i + 2]); + bool canDelete = bool.Parse(parts[i + 3]); + + if (group is not null) + { + PermissionFlag permissionFlag = PermissionFlag.None; + + if (canView) + permissionFlag |= PermissionFlag.View; + + if (canModify) + permissionFlag |= PermissionFlag.Modify; + + if (canDelete) + permissionFlag |= PermissionFlag.Delete; + + groupPermissions[group] = permissionFlag; + } + } + + //ensure administrators group always has all permissions + Group admins = _dnsWebService.AuthManager.GetGroup(Group.ADMINISTRATORS); + groupPermissions[admins] = PermissionFlag.ViewModifyDelete; + + switch (section) + { + case PermissionSection.Zones: + //ensure DNS administrators group always has all permissions + Group dnsAdmins = _dnsWebService.AuthManager.GetGroup(Group.DNS_ADMINISTRATORS); + groupPermissions[dnsAdmins] = PermissionFlag.ViewModifyDelete; + break; + + case PermissionSection.DhcpServer: + //ensure DHCP administrators group always has all permissions + Group dhcpAdmins = _dnsWebService.AuthManager.GetGroup(Group.DHCP_ADMINISTRATORS); + groupPermissions[dhcpAdmins] = PermissionFlag.ViewModifyDelete; + break; + } + + permission.SyncPermissions(groupPermissions); + } + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Permissions were updated successfully for section: " + section.ToString() + (string.IsNullOrEmpty(strSubItem) ? "" : "/" + strSubItem)); + + _dnsWebService.AuthManager.SaveConfigFile(); + + WritePermissionDetails(jsonWriter, permission, strSubItem, false); + } + + #endregion + } +} From b801ae6f9f7487eef43483710d939fa506131588 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:49:36 +0530 Subject: [PATCH 18/53] WebServiceAppsApi: updated implementation to support automatic updates for DNS apps. Updated DownloadAndInstallAppAsync() to not use system proxy. Updated SetAppConfigAsync() to unescape config data. --- DnsServerCore/WebServiceAppsApi.cs | 279 +++++++++++++++++++++++------ 1 file changed, 226 insertions(+), 53 deletions(-) diff --git a/DnsServerCore/WebServiceAppsApi.cs b/DnsServerCore/WebServiceAppsApi.cs index 18fa6de6..e234ca6d 100644 --- a/DnsServerCore/WebServiceAppsApi.cs +++ b/DnsServerCore/WebServiceAppsApi.cs @@ -25,13 +25,14 @@ using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using TechnitiumLibrary; using TechnitiumLibrary.IO; namespace DnsServerCore { - class WebServiceAppsApi + sealed class WebServiceAppsApi : IDisposable { #region variables @@ -42,6 +43,10 @@ namespace DnsServerCore DateTime _storeAppsJsonDataUpdatedOn; const int STORE_APPS_JSON_DATA_CACHE_TIME_SECONDS = 300; + Timer _appUpdateTimer; + const int APP_UPDATE_TIMER_INITIAL_INTERVAL = 10000; + const int APP_UPDATE_TIMER_PERIODIC_INTERVAL = 86400000; + #endregion #region constructor @@ -54,14 +59,113 @@ namespace DnsServerCore #endregion + #region IDisposable + + bool _disposed; + + public void Dispose() + { + if (_disposed) + return; + + if (_appUpdateTimer is not null) + _appUpdateTimer.Dispose(); + + _disposed = true; + } + + #endregion + #region private + private void StartAutomaticUpdate() + { + if (_appUpdateTimer is null) + { + _appUpdateTimer = new Timer(async delegate (object state) + { + try + { + if (_dnsWebService.DnsServer.DnsApplicationManager.Applications.Count < 1) + return; + + string storeAppsJsonData = await GetStoreAppsJsonData().WithTimeout(5000); + dynamic jsonStoreAppsArray = JsonConvert.DeserializeObject(storeAppsJsonData); + + foreach (DnsApplication application in _dnsWebService.DnsServer.DnsApplicationManager.Applications.Values) + { + foreach (dynamic jsonStoreApp in jsonStoreAppsArray) + { + string name = jsonStoreApp.name.Value; + if (name.Equals(application.Name)) + { + string url = null; + Version storeAppVersion = null; + Version lastServerVersion = null; + + foreach (dynamic jsonVersion in jsonStoreApp.versions) + { + string strServerVersion = jsonVersion.serverVersion.Value; + Version requiredServerVersion = new Version(strServerVersion); + + if (_dnsWebService.ServerVersion < requiredServerVersion) + continue; + + if ((lastServerVersion is not null) && (lastServerVersion > requiredServerVersion)) + continue; + + string version = jsonVersion.version.Value; + url = jsonVersion.url.Value; + + storeAppVersion = new Version(version); + lastServerVersion = requiredServerVersion; + } + + if ((storeAppVersion is not null) && (storeAppVersion > application.Version)) + { + try + { + await DownloadAndUpdateAppAsync(application.Name, url); + + _dnsWebService.Log.Write("DNS application '" + application.Name + "' was automatically updated successfully from: " + url); + } + catch (Exception ex) + { + _dnsWebService.Log.Write("Failed to automatically download and update DNS application '" + application.Name + "': " + ex.ToString()); + } + } + + break; + } + } + } + } + catch (Exception ex) + { + _dnsWebService.Log.Write(ex); + } + }); + + _appUpdateTimer.Change(APP_UPDATE_TIMER_INITIAL_INTERVAL, APP_UPDATE_TIMER_PERIODIC_INTERVAL); + } + } + + private void StopAutomaticUpdate() + { + if (_appUpdateTimer is not null) + { + _appUpdateTimer.Dispose(); + _appUpdateTimer = null; + } + } + private async Task GetStoreAppsJsonData() { if ((_storeAppsJsonData == null) || (DateTime.UtcNow > _storeAppsJsonDataUpdatedOn.AddSeconds(STORE_APPS_JSON_DATA_CACHE_TIME_SECONDS))) { SocketsHttpHandler handler = new SocketsHttpHandler(); handler.Proxy = _dnsWebService.DnsServer.Proxy; + handler.UseProxy = _dnsWebService.DnsServer.Proxy is not null; handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; using (HttpClient http = new HttpClient(handler)) @@ -74,6 +178,45 @@ namespace DnsServerCore return _storeAppsJsonData; } + private async Task DownloadAndUpdateAppAsync(string applicationName, string url) + { + string tmpFile = Path.GetTempFileName(); + try + { + using (FileStream fS = new FileStream(tmpFile, FileMode.Create, FileAccess.ReadWrite)) + { + //download to temp file + SocketsHttpHandler handler = new SocketsHttpHandler(); + handler.Proxy = _dnsWebService.DnsServer.Proxy; + handler.UseProxy = _dnsWebService.DnsServer.Proxy is not null; + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + + using (HttpClient http = new HttpClient(handler)) + { + using (Stream httpStream = await http.GetStreamAsync(url)) + { + await httpStream.CopyToAsync(fS); + } + } + + //update app + fS.Position = 0; + await _dnsWebService.DnsServer.DnsApplicationManager.UpdateApplicationAsync(applicationName, fS); + } + } + finally + { + try + { + File.Delete(tmpFile); + } + catch (Exception ex) + { + _dnsWebService.Log.Write(ex); + } + } + } + #endregion #region public @@ -81,7 +224,6 @@ namespace DnsServerCore public async Task ListInstalledAppsAsync(JsonTextWriter jsonWriter) { List apps = new List(_dnsWebService.DnsServer.DnsApplicationManager.Applications.Keys); - apps.Sort(); dynamic jsonStoreAppsArray = null; @@ -119,8 +261,31 @@ namespace DnsServerCore string name = jsonStoreApp.name.Value; if (name.Equals(application.Name)) { - string version = jsonStoreApp.version.Value; - string url = jsonStoreApp.url.Value; + string version = null; + string url = null; + Version storeAppVersion = null; + Version lastServerVersion = null; + + foreach (dynamic jsonVersion in jsonStoreApp.versions) + { + string strServerVersion = jsonVersion.serverVersion.Value; + Version requiredServerVersion = new Version(strServerVersion); + + if (_dnsWebService.ServerVersion < requiredServerVersion) + continue; + + if ((lastServerVersion is not null) && (lastServerVersion > requiredServerVersion)) + continue; + + version = jsonVersion.version.Value; + url = jsonVersion.url.Value; + + storeAppVersion = new Version(version); + lastServerVersion = requiredServerVersion; + } + + if (storeAppVersion is null) + break; //no compatible update available jsonWriter.WritePropertyName("updateVersion"); jsonWriter.WriteValue(version); @@ -129,7 +294,7 @@ namespace DnsServerCore jsonWriter.WriteValue(url); jsonWriter.WritePropertyName("updateAvailable"); - jsonWriter.WriteValue(new Version(version) > application.Version); + jsonWriter.WriteValue(storeAppVersion > application.Version); break; } } @@ -196,22 +361,46 @@ namespace DnsServerCore foreach (dynamic jsonStoreApp in jsonStoreAppsArray) { string name = jsonStoreApp.name.Value; - string version = jsonStoreApp.version.Value; string description = jsonStoreApp.description.Value; - string url = jsonStoreApp.url.Value; - string size = jsonStoreApp.size.Value; + string version = null; + string url = null; + string size = null; + Version storeAppVersion = null; + Version lastServerVersion = null; + + foreach (dynamic jsonVersion in jsonStoreApp.versions) + { + string strServerVersion = jsonVersion.serverVersion.Value; + Version requiredServerVersion = new Version(strServerVersion); + + if (_dnsWebService.ServerVersion < requiredServerVersion) + continue; + + if ((lastServerVersion is not null) && (lastServerVersion > requiredServerVersion)) + continue; + + version = jsonVersion.version.Value; + url = jsonVersion.url.Value; + size = jsonVersion.size.Value; + + storeAppVersion = new Version(version); + lastServerVersion = requiredServerVersion; + } + + if (storeAppVersion is null) + continue; //app is not compatible jsonWriter.WriteStartObject(); jsonWriter.WritePropertyName("name"); jsonWriter.WriteValue(name); - jsonWriter.WritePropertyName("version"); - jsonWriter.WriteValue(version); - jsonWriter.WritePropertyName("description"); jsonWriter.WriteValue(description); + jsonWriter.WritePropertyName("version"); + jsonWriter.WriteValue(version); + jsonWriter.WritePropertyName("url"); jsonWriter.WriteValue(url); @@ -229,7 +418,7 @@ namespace DnsServerCore jsonWriter.WriteValue(DnsWebService.GetCleanVersion(installedApp.Version)); jsonWriter.WritePropertyName("updateAvailable"); - jsonWriter.WriteValue(new Version(version) > installedApp.Version); + jsonWriter.WriteValue(storeAppVersion > installedApp.Version); } jsonWriter.WriteEndObject(); @@ -261,6 +450,7 @@ namespace DnsServerCore //download to temp file SocketsHttpHandler handler = new SocketsHttpHandler(); handler.Proxy = _dnsWebService.DnsServer.Proxy; + handler.UseProxy = _dnsWebService.DnsServer.Proxy is not null; handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; using (HttpClient http = new HttpClient(handler)) @@ -275,7 +465,7 @@ namespace DnsServerCore fS.Position = 0; await _dnsWebService.DnsServer.DnsApplicationManager.InstallApplicationAsync(name, fS); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DNS application '" + name + "' was installed successfully from: " + url); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DNS application '" + name + "' was installed successfully from: " + url); } } finally @@ -306,42 +496,9 @@ namespace DnsServerCore if (!url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) throw new DnsWebServiceException("Parameter 'url' value must start with 'https://'."); - string tmpFile = Path.GetTempFileName(); - try - { - using (FileStream fS = new FileStream(tmpFile, FileMode.Create, FileAccess.ReadWrite)) - { - //download to temp file - SocketsHttpHandler handler = new SocketsHttpHandler(); - handler.Proxy = _dnsWebService.DnsServer.Proxy; - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + await DownloadAndUpdateAppAsync(name, url); - using (HttpClient http = new HttpClient(handler)) - { - using (Stream httpStream = await http.GetStreamAsync(url)) - { - await httpStream.CopyToAsync(fS); - } - } - - //update app - fS.Position = 0; - await _dnsWebService.DnsServer.DnsApplicationManager.UpdateApplicationAsync(name, fS); - - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DNS application '" + name + "' was updated successfully from: " + url); - } - } - finally - { - try - { - File.Delete(tmpFile); - } - catch (Exception ex) - { - _dnsWebService.Log.Write(ex); - } - } + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DNS application '" + name + "' was updated successfully from: " + url); } public async Task InstallAppAsync(HttpListenerRequest request) @@ -387,7 +544,7 @@ namespace DnsServerCore fS.Position = 0; await _dnsWebService.DnsServer.DnsApplicationManager.InstallApplicationAsync(name, fS); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DNS application '" + name + "' was installed successfully."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DNS application '" + name + "' was installed successfully."); } } finally @@ -446,7 +603,7 @@ namespace DnsServerCore fS.Position = 0; await _dnsWebService.DnsServer.DnsApplicationManager.UpdateApplicationAsync(name, fS); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DNS application '" + name + "' was updated successfully."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DNS application '" + name + "' was updated successfully."); } } finally @@ -471,7 +628,7 @@ namespace DnsServerCore name = name.Trim(); _dnsWebService.DnsServer.DnsApplicationManager.UninstallApplication(name); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DNS application '" + name + "' was uninstalled successfully."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DNS application '" + name + "' was uninstalled successfully."); } public async Task GetAppConfigAsync(HttpListenerRequest request, JsonTextWriter jsonWriter) @@ -514,14 +671,14 @@ namespace DnsServerCore { if (formPart.StartsWith("config=")) { - string config = formPart.Substring(7); + string config = Uri.UnescapeDataString(formPart.Substring(7)); if (config.Length == 0) config = null; await application.SetConfigAsync(config); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DNS application '" + name + "' app config was saved successfully."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DNS application '" + name + "' app config was saved successfully."); return; } } @@ -530,5 +687,21 @@ namespace DnsServerCore } #endregion + + #region properties + + public bool EnableAutomaticUpdate + { + get { return _appUpdateTimer is not null; } + set + { + if (value) + StartAutomaticUpdate(); + else + StopAutomaticUpdate(); + } + } + + #endregion } } From a5b1e859e32b6c4e627a48826b9c6079bb735857 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:50:30 +0530 Subject: [PATCH 19/53] WebServiceDhcpApi: added AddReservedLease() and RemoveReservedLease() API support. --- DnsServerCore/WebServiceDhcpApi.cs | 72 ++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/DnsServerCore/WebServiceDhcpApi.cs b/DnsServerCore/WebServiceDhcpApi.cs index eeaac0c9..1f582f63 100644 --- a/DnsServerCore/WebServiceDhcpApi.cs +++ b/DnsServerCore/WebServiceDhcpApi.cs @@ -638,13 +638,13 @@ namespace DnsServerCore { _dnsWebService.DhcpServer.SaveScope(scopeName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DHCP scope was updated successfully: " + scopeName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope was updated successfully: " + scopeName); } else { await _dnsWebService.DhcpServer.AddScopeAsync(scope); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DHCP scope was added successfully: " + scopeName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope was added successfully: " + scopeName); } string newName = request.QueryString["newName"]; @@ -652,10 +652,64 @@ namespace DnsServerCore { _dnsWebService.DhcpServer.RenameScope(scopeName, newName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DHCP scope was renamed successfully: '" + scopeName + "' to '" + newName + "'"); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope was renamed successfully: '" + scopeName + "' to '" + newName + "'"); } } + public void AddReservedLease(HttpListenerRequest request) + { + string scopeName = request.QueryString["name"]; + if (string.IsNullOrEmpty(scopeName)) + throw new DnsWebServiceException("Parameter 'name' missing."); + + Scope scope = _dnsWebService.DhcpServer.GetScope(scopeName); + if (scope is null) + throw new DnsWebServiceException("No such scope exists: " + scopeName); + + string hostName = request.QueryString["hostName"]; + + string hardwareAddress = request.QueryString["hardwareAddress"]; + if (string.IsNullOrEmpty(hardwareAddress)) + throw new DnsWebServiceException("Parameter 'hardwareAddress' missing."); + + string strIpAddress = request.QueryString["ipAddress"]; + if (string.IsNullOrEmpty(strIpAddress)) + throw new DnsWebServiceException("Parameter 'ipAddress' missing."); + + string comments = request.QueryString["comments"]; + + Lease reservedLease = new Lease(LeaseType.Reserved, hostName, DhcpMessageHardwareAddressType.Ethernet, hardwareAddress, IPAddress.Parse(strIpAddress), comments); + + if (!scope.AddReservedLease(reservedLease)) + throw new DnsWebServiceException("Failed to add reserved lease for scope: " + scopeName); + + _dnsWebService.DhcpServer.SaveScope(scopeName); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope reserved lease was added successfully: " + scopeName); + } + + public void RemoveReservedLease(HttpListenerRequest request) + { + string scopeName = request.QueryString["name"]; + if (string.IsNullOrEmpty(scopeName)) + throw new DnsWebServiceException("Parameter 'name' missing."); + + Scope scope = _dnsWebService.DhcpServer.GetScope(scopeName); + if (scope is null) + throw new DnsWebServiceException("No such scope exists: " + scopeName); + + string hardwareAddress = request.QueryString["hardwareAddress"]; + if (string.IsNullOrEmpty(hardwareAddress)) + throw new DnsWebServiceException("Parameter 'hardwareAddress' missing."); + + if (!scope.RemoveReservedLease(hardwareAddress)) + throw new DnsWebServiceException("Failed to remove reserved lease for scope: " + scopeName); + + _dnsWebService.DhcpServer.SaveScope(scopeName); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope reserved lease was removed successfully: " + scopeName); + } + public async Task EnableDhcpScopeAsync(HttpListenerRequest request) { string scopeName = request.QueryString["name"]; @@ -665,7 +719,7 @@ namespace DnsServerCore if (!await _dnsWebService.DhcpServer.EnableScopeAsync(scopeName)) throw new DnsWebServiceException("Failed to enable DHCP scope, please check logs for details: " + scopeName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DHCP scope was enabled successfully: " + scopeName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope was enabled successfully: " + scopeName); } public void DisableDhcpScope(HttpListenerRequest request) @@ -677,7 +731,7 @@ namespace DnsServerCore if (!_dnsWebService.DhcpServer.DisableScope(scopeName)) throw new DnsWebServiceException("Failed to disable DHCP scope, please check logs for details: " + scopeName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DHCP scope was disabled successfully: " + scopeName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope was disabled successfully: " + scopeName); } public void DeleteDhcpScope(HttpListenerRequest request) @@ -688,7 +742,7 @@ namespace DnsServerCore _dnsWebService.DhcpServer.DeleteScope(scopeName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DHCP scope was deleted successfully: " + scopeName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope was deleted successfully: " + scopeName); } public void RemoveDhcpLease(HttpListenerRequest request) @@ -713,7 +767,7 @@ namespace DnsServerCore _dnsWebService.DhcpServer.SaveScope(scopeName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DHCP scope's lease was removed successfully: " + scopeName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope's lease was removed successfully: " + scopeName); } public void ConvertToReservedLease(HttpListenerRequest request) @@ -738,7 +792,7 @@ namespace DnsServerCore _dnsWebService.DhcpServer.SaveScope(scopeName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DHCP scope's lease was reserved successfully: " + scopeName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope's lease was reserved successfully: " + scopeName); } public void ConvertToDynamicLease(HttpListenerRequest request) @@ -763,7 +817,7 @@ namespace DnsServerCore _dnsWebService.DhcpServer.SaveScope(scopeName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DHCP scope's lease was unreserved successfully: " + scopeName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] DHCP scope's lease was unreserved successfully: " + scopeName); } #endregion From eef2315fd86723c9f4d1865e8a3ce617f22b025e Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:50:58 +0530 Subject: [PATCH 20/53] WebServiceLogsApi: added DownloadLogAsync() implementation. --- DnsServerCore/WebServiceLogsApi.cs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/DnsServerCore/WebServiceLogsApi.cs b/DnsServerCore/WebServiceLogsApi.cs index a5b67fd4..dbbc33b3 100644 --- a/DnsServerCore/WebServiceLogsApi.cs +++ b/DnsServerCore/WebServiceLogsApi.cs @@ -1,6 +1,6 @@ /* Technitium DNS Server -Copyright (C) 2021 Shreyas Zare (shreyas@technitium.com) +Copyright (C) 2022 Shreyas Zare (shreyas@technitium.com) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -76,6 +76,22 @@ namespace DnsServerCore jsonWriter.WriteEndArray(); } + public Task DownloadLogAsync(HttpListenerRequest request, HttpListenerResponse response) + { + string strFileName = request.QueryString["fileName"]; + if (string.IsNullOrEmpty(strFileName)) + throw new DnsWebServiceException("Parameter 'fileName' missing."); + + int limit; + string strLimit = request.QueryString["limit"]; + if (string.IsNullOrEmpty(strLimit)) + limit = 0; + else + limit = int.Parse(strLimit); + + return _dnsWebService.Log.DownloadLogAsync(request, response, strFileName, limit * 1024 * 1024); + } + public void DeleteLog(HttpListenerRequest request) { string log = request.QueryString["log"]; @@ -84,21 +100,21 @@ namespace DnsServerCore _dnsWebService.Log.DeleteLog(log); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Log file was deleted: " + log); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Log file was deleted: " + log); } public void DeleteAllLogs(HttpListenerRequest request) { _dnsWebService.Log.DeleteAllLogs(); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] All log files were deleted."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] All log files were deleted."); } public void DeleteAllStats(HttpListenerRequest request) { _dnsWebService.DnsServer.StatsManager.DeleteAllStats(); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] All stats files were deleted."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] All stats files were deleted."); } public async Task QueryLogsAsync(HttpListenerRequest request, JsonTextWriter jsonWriter) From 4ca330e4a16a3938dbdce9393467e46722937e14 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 17:52:00 +0530 Subject: [PATCH 21/53] WebServiceOtherZonesApi: updated ImportAllowedZonesAsync() and ImportBlockedZonesAsync() to unescape POST parameter values. --- DnsServerCore/WebServiceOtherZonesApi.cs | 27 +++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/DnsServerCore/WebServiceOtherZonesApi.cs b/DnsServerCore/WebServiceOtherZonesApi.cs index 42fc336b..115743c8 100644 --- a/DnsServerCore/WebServiceOtherZonesApi.cs +++ b/DnsServerCore/WebServiceOtherZonesApi.cs @@ -19,6 +19,7 @@ along with this program. If not, see . using DnsServerCore.Dns.Zones; using Newtonsoft.Json; +using System; using System.Collections.Generic; using System.IO; using System.Net; @@ -53,7 +54,7 @@ namespace DnsServerCore { _dnsWebService.DnsServer.CacheZoneManager.Flush(); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Cache was flushed."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Cache was flushed."); } public void ListCachedZones(HttpListenerRequest request, JsonTextWriter jsonWriter) @@ -128,7 +129,7 @@ namespace DnsServerCore throw new DnsWebServiceException("Parameter 'domain' missing."); if (_dnsWebService.DnsServer.CacheZoneManager.DeleteZone(domain)) - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Cached zone was deleted: " + domain); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Cached zone was deleted: " + domain); } #endregion @@ -217,7 +218,8 @@ namespace DnsServerCore { if (formPart.StartsWith("allowedZones=")) { - string[] allowedZones = formPart.Substring(13).Split(','); + string value = Uri.UnescapeDataString(formPart.Substring(13)); + string[] allowedZones = value.Split(','); bool added = false; foreach (string allowedZone in allowedZones) @@ -228,7 +230,7 @@ namespace DnsServerCore if (added) { - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Total " + allowedZones.Length + " zones were imported into allowed zone successfully."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Total " + allowedZones.Length + " zones were imported into allowed zone successfully."); _dnsWebService.DnsServer.AllowedZoneManager.SaveZoneFile(); } @@ -261,7 +263,7 @@ namespace DnsServerCore if (_dnsWebService.DnsServer.AllowedZoneManager.DeleteZone(domain)) { - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Allowed zone was deleted: " + domain); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Allowed zone was deleted: " + domain); _dnsWebService.DnsServer.AllowedZoneManager.SaveZoneFile(); } } @@ -270,7 +272,7 @@ namespace DnsServerCore { _dnsWebService.DnsServer.AllowedZoneManager.Flush(); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Allowed zone was flushed successfully."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Allowed zone was flushed successfully."); _dnsWebService.DnsServer.AllowedZoneManager.SaveZoneFile(); } @@ -285,7 +287,7 @@ namespace DnsServerCore if (_dnsWebService.DnsServer.AllowedZoneManager.AllowZone(domain)) { - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Zone was allowed: " + domain); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Zone was allowed: " + domain); _dnsWebService.DnsServer.AllowedZoneManager.SaveZoneFile(); } } @@ -376,7 +378,8 @@ namespace DnsServerCore { if (formPart.StartsWith("blockedZones=")) { - string[] blockedZones = formPart.Substring(13).Split(','); + string value = Uri.UnescapeDataString(formPart.Substring(13)); + string[] blockedZones = value.Split(','); bool added = false; foreach (string blockedZone in blockedZones) @@ -387,7 +390,7 @@ namespace DnsServerCore if (added) { - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Total " + blockedZones.Length + " zones were imported into blocked zone successfully."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Total " + blockedZones.Length + " zones were imported into blocked zone successfully."); _dnsWebService.DnsServer.BlockedZoneManager.SaveZoneFile(); } @@ -420,7 +423,7 @@ namespace DnsServerCore if (_dnsWebService.DnsServer.BlockedZoneManager.DeleteZone(domain)) { - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Blocked zone was deleted: " + domain); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Blocked zone was deleted: " + domain); _dnsWebService.DnsServer.BlockedZoneManager.SaveZoneFile(); } } @@ -429,7 +432,7 @@ namespace DnsServerCore { _dnsWebService.DnsServer.BlockedZoneManager.Flush(); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Blocked zone was flushed successfully."); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Blocked zone was flushed successfully."); _dnsWebService.DnsServer.BlockedZoneManager.SaveZoneFile(); } @@ -444,7 +447,7 @@ namespace DnsServerCore if (_dnsWebService.DnsServer.BlockedZoneManager.BlockZone(domain)) { - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Domain was added to blocked zone: " + domain); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).User.Username + "] Domain was added to blocked zone: " + domain); _dnsWebService.DnsServer.BlockedZoneManager.SaveZoneFile(); } } From 6752f95469a9efdd7602009c1b6ae9117420cf26 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 18:00:21 +0530 Subject: [PATCH 22/53] WebServiceZonesApi: Updated implementation to support zone permissions. --- DnsServerCore/WebServiceZonesApi.cs | 390 ++++++++++++++++++++-------- 1 file changed, 280 insertions(+), 110 deletions(-) diff --git a/DnsServerCore/WebServiceZonesApi.cs b/DnsServerCore/WebServiceZonesApi.cs index 473940a2..2645ce7a 100644 --- a/DnsServerCore/WebServiceZonesApi.cs +++ b/DnsServerCore/WebServiceZonesApi.cs @@ -17,6 +17,7 @@ along with this program. If not, see . */ +using DnsServerCore.Auth; using DnsServerCore.Dns; using DnsServerCore.Dns.Dnssec; using DnsServerCore.Dns.ResourceRecords; @@ -859,17 +860,23 @@ namespace DnsServerCore #region public - public void ListZones(JsonTextWriter jsonWriter) + public void ListZones(HttpListenerRequest request, JsonTextWriter jsonWriter) { List zones = _dnsWebService.DnsServer.AuthZoneManager.ListZones(); - zones.Sort(); + UserSession session = _dnsWebService.GetSession(request); + jsonWriter.WritePropertyName("zones"); jsonWriter.WriteStartArray(); foreach (AuthZoneInfo zone in zones) + { + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zone.Name, session.User, PermissionFlag.View)) + continue; + WriteZoneInfoAsJson(zone, jsonWriter); + } jsonWriter.WriteEndArray(); } @@ -906,14 +913,27 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(strType)) type = Enum.Parse(strType, true); + AuthZoneInfo zoneInfo; + switch (type) { case AuthZoneType.Primary: - if (_dnsWebService.DnsServer.AuthZoneManager.CreatePrimaryZone(zoneName, _dnsWebService.DnsServer.ServerDomain, false) is null) - throw new DnsWebServiceException("Zone already exists: " + zoneName); + { + zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreatePrimaryZone(zoneName, _dnsWebService.DnsServer.ServerDomain, false); + if (zoneInfo is null) + throw new DnsWebServiceException("Zone already exists: " + zoneName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Authoritative primary zone was created: " + zoneName); - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); + UserSession session = _dnsWebService.GetSession(request); + + //set permissions + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SaveConfigFile(); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Authoritative primary zone was created: " + zoneName); + _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); + } break; case AuthZoneType.Secondary: @@ -934,11 +954,20 @@ namespace DnsServerCore if (string.IsNullOrEmpty(tsigKeyName)) tsigKeyName = null; - if (await _dnsWebService.DnsServer.AuthZoneManager.CreateSecondaryZoneAsync(zoneName, primaryNameServerAddresses, zoneTransferProtocol, tsigKeyName) is null) + zoneInfo = await _dnsWebService.DnsServer.AuthZoneManager.CreateSecondaryZoneAsync(zoneName, primaryNameServerAddresses, zoneTransferProtocol, tsigKeyName); + if (zoneInfo is null) throw new DnsWebServiceException("Zone already exists: " + zoneName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Authoritative secondary zone was created: " + zoneName); - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); + UserSession session = _dnsWebService.GetSession(request); + + //set permissions + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SaveConfigFile(); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Authoritative secondary zone was created: " + zoneName); + _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); } break; @@ -948,11 +977,20 @@ namespace DnsServerCore if (string.IsNullOrEmpty(strPrimaryNameServerAddresses)) strPrimaryNameServerAddresses = null; - if (await _dnsWebService.DnsServer.AuthZoneManager.CreateStubZoneAsync(zoneName, strPrimaryNameServerAddresses) is null) + zoneInfo = await _dnsWebService.DnsServer.AuthZoneManager.CreateStubZoneAsync(zoneName, strPrimaryNameServerAddresses); + if (zoneInfo is null) throw new DnsWebServiceException("Zone already exists: " + zoneName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Stub zone was created: " + zoneName); - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); + UserSession session = _dnsWebService.GetSession(request); + + //set permissions + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SaveConfigFile(); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Stub zone was created: " + zoneName); + _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); } break; @@ -997,11 +1035,20 @@ namespace DnsServerCore proxyPassword = request.QueryString["proxyPassword"]; } - if (_dnsWebService.DnsServer.AuthZoneManager.CreateForwarderZone(zoneName, forwarderProtocol, strForwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword, null) is null) + zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreateForwarderZone(zoneName, forwarderProtocol, strForwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword, null); + if (zoneInfo is null) throw new DnsWebServiceException("Zone already exists: " + zoneName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Forwarder zone was created: " + zoneName); - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); + UserSession session = _dnsWebService.GetSession(request); + + //set permissions + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SaveConfigFile(); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Forwarder zone was created: " + zoneName); + _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); } break; @@ -1010,10 +1057,10 @@ namespace DnsServerCore } //delete cache for this zone to allow rebuilding cache data as needed by stub or forwarder zones - _dnsWebService.DnsServer.CacheZoneManager.DeleteZone(zoneName); + _dnsWebService.DnsServer.CacheZoneManager.DeleteZone(zoneInfo.Name); jsonWriter.WritePropertyName("domain"); - jsonWriter.WriteValue(string.IsNullOrEmpty(zoneName) ? "." : zoneName); + jsonWriter.WriteValue(string.IsNullOrEmpty(zoneInfo.Name) ? "." : zoneInfo.Name); } public void SignPrimaryZone(HttpListenerRequest request) @@ -1024,6 +1071,11 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + string algorithm = request.QueryString["algorithm"]; if (string.IsNullOrEmpty(algorithm)) throw new DnsWebServiceException("Parameter 'algorithm' missing."); @@ -1116,7 +1168,7 @@ namespace DnsServerCore throw new NotSupportedException("Algorithm is not supported: " + algorithm); } - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Primary zone was signed successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Primary zone was signed successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1129,9 +1181,14 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + _dnsWebService.DnsServer.AuthZoneManager.UnsignPrimaryZone(zoneName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Primary zone was unsigned successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Primary zone was unsigned successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1154,6 +1211,11 @@ namespace DnsServerCore if (zoneInfo.Type != AuthZoneType.Primary) throw new DnsWebServiceException("The zone must be a primary zone."); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + jsonWriter.WritePropertyName("name"); jsonWriter.WriteValue(zoneInfo.Name); @@ -1254,9 +1316,14 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + _dnsWebService.DnsServer.AuthZoneManager.ConvertPrimaryZoneToNSEC(zoneName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Primary zone was converted to NSEC successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Primary zone was converted to NSEC successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1269,6 +1336,11 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + ushort iterations = 0; string strIterations = request.QueryString["iterations"]; if (!string.IsNullOrEmpty(strIterations)) @@ -1281,7 +1353,7 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.ConvertPrimaryZoneToNSEC3(zoneName, iterations, saltLength); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Primary zone was converted to NSEC3 successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Primary zone was converted to NSEC3 successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1294,6 +1366,11 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + ushort iterations = 0; string strIterations = request.QueryString["iterations"]; if (!string.IsNullOrEmpty(strIterations)) @@ -1306,7 +1383,7 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.UpdatePrimaryZoneNSEC3Parameters(zoneName, iterations, saltLength); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Primary zone NSEC3 parameters were updated successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Primary zone NSEC3 parameters were updated successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1319,6 +1396,11 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + string strDnsKeyTtl = request.QueryString["ttl"]; if (string.IsNullOrEmpty(strDnsKeyTtl)) throw new DnsWebServiceException("Parameter 'ttl' missing."); @@ -1327,7 +1409,7 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.UpdatePrimaryZoneDnsKeyTtl(zoneName, dnsKeyTtl); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Primary zone DNSKEY TTL was updated successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Primary zone DNSKEY TTL was updated successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1340,6 +1422,11 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + string strKeyType = request.QueryString["keyType"]; if (string.IsNullOrEmpty(strKeyType)) throw new DnsWebServiceException("Parameter 'keyType' missing."); @@ -1385,7 +1472,7 @@ namespace DnsServerCore throw new NotSupportedException("Algorithm is not supported: " + algorithm); } - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DNSSEC private key was generated and added to the primary zone successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] DNSSEC private key was generated and added to the primary zone successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1398,6 +1485,11 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + string strKeyTag = request.QueryString["keyTag"]; if (string.IsNullOrEmpty(strKeyTag)) throw new DnsWebServiceException("Parameter 'keyTag' missing."); @@ -1412,7 +1504,7 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.UpdatePrimaryZoneDnssecPrivateKey(zoneName, keyTag, rolloverDays); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Primary zone DNSSEC private key config was updated successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Primary zone DNSSEC private key config was updated successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1425,6 +1517,11 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + string strKeyTag = request.QueryString["keyTag"]; if (string.IsNullOrEmpty(strKeyTag)) throw new DnsWebServiceException("Parameter 'keyTag' missing."); @@ -1433,7 +1530,7 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.DeletePrimaryZoneDnssecPrivateKey(zoneName, keyTag); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] DNSSEC private key was deleted from primary zone successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] DNSSEC private key was deleted from primary zone successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1446,9 +1543,14 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + _dnsWebService.DnsServer.AuthZoneManager.PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(zoneName); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] All DNSSEC private keys from the primary zone were published successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] All DNSSEC private keys from the primary zone were published successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1461,6 +1563,11 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + string strKeyTag = request.QueryString["keyTag"]; if (string.IsNullOrEmpty(strKeyTag)) throw new DnsWebServiceException("Parameter 'keyTag' missing."); @@ -1469,7 +1576,7 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.RolloverPrimaryZoneDnsKey(zoneName, keyTag); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] The DNSKEY (" + keyTag + ") from the primary zone was rolled over successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] The DNSKEY (" + keyTag + ") from the primary zone was rolled over successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1482,6 +1589,11 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + string strKeyTag = request.QueryString["keyTag"]; if (string.IsNullOrEmpty(strKeyTag)) throw new DnsWebServiceException("Parameter 'keyTag' missing."); @@ -1490,7 +1602,7 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.RetirePrimaryZoneDnsKey(zoneName, keyTag); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] The DNSKEY (" + keyTag + ") from the primary zone was retired successfully: " + zoneName); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] The DNSKEY (" + keyTag + ") from the primary zone was retired successfully: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } @@ -1513,11 +1625,18 @@ namespace DnsServerCore if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); - if (!_dnsWebService.DnsServer.AuthZoneManager.DeleteZone(zoneName)) - throw new DnsWebServiceException("No authoritative zone was not found for domain: " + zoneName); + UserSession session = _dnsWebService.GetSession(request); - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] " + zoneInfo.Type.ToString() + " zone was deleted: " + zoneName); + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + if (!_dnsWebService.DnsServer.AuthZoneManager.DeleteZone(zoneInfo.Name)) + throw new DnsWebServiceException("No authoritative zone was not found for domain: " + zoneInfo.Name); + + _dnsWebService.AuthManager.RemoveAllPermissions(PermissionSection.Zones, zoneInfo.Name); + _dnsWebService.AuthManager.SaveConfigFile(); + + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] " + zoneInfo.Type.ToString() + " zone was deleted: " + zoneName); _dnsWebService.DnsServer.AuthZoneManager.DeleteZoneFile(zoneInfo.Name); } @@ -1533,15 +1652,20 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); - if (zoneInfo == null) + if (zoneInfo is null) throw new DnsWebServiceException("No authoritative zone was not found for domain: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + zoneInfo.Disabled = false; - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] " + zoneInfo.Type.ToString() + " zone was enabled: " + zoneInfo.Name); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] " + zoneInfo.Type.ToString() + " zone was enabled: " + zoneInfo.Name); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); @@ -1561,15 +1685,20 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); - if (zoneInfo == null) + if (zoneInfo is null) throw new DnsWebServiceException("No authoritative zone was not found for domain: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + zoneInfo.Disabled = true; - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] " + zoneInfo.Type.ToString() + " zone was disabled: " + zoneInfo.Name); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] " + zoneInfo.Type.ToString() + " zone was disabled: " + zoneInfo.Name); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); } @@ -1592,6 +1721,11 @@ namespace DnsServerCore if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + jsonWriter.WritePropertyName("name"); jsonWriter.WriteValue(zoneInfo.Name); @@ -1688,12 +1822,17 @@ namespace DnsServerCore zoneName = zoneName.TrimEnd('.'); AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); - if (zoneInfo == null) + if (zoneInfo is null) throw new DnsWebServiceException("No authoritative zone was not found for domain: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + string strDisabled = request.QueryString["disabled"]; if (!string.IsNullOrEmpty(strDisabled)) zoneInfo.Disabled = bool.Parse(strDisabled); @@ -1763,26 +1902,34 @@ namespace DnsServerCore } } - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] " + zoneInfo.Type.ToString() + " zone options were updated successfully: " + zoneInfo.Name); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] " + zoneInfo.Type.ToString() + " zone options were updated successfully: " + zoneInfo.Name); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); } public void ResyncZone(HttpListenerRequest request) { - string domain = request.QueryString["domain"]; - if (string.IsNullOrEmpty(domain)) - throw new DnsWebServiceException("Parameter 'domain' missing."); + string zoneName = request.QueryString["zone"]; + if (string.IsNullOrEmpty(zoneName)) + zoneName = request.QueryString["domain"]; - domain = domain.TrimEnd('.'); + if (string.IsNullOrEmpty(zoneName)) + throw new DnsWebServiceException("Parameter 'zone' missing."); - AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(domain); - if (zoneInfo == null) - throw new DnsWebServiceException("No authoritative zone was not found for domain: " + domain); + zoneName = zoneName.TrimEnd('.'); + + AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); + if (zoneInfo is null) + throw new DnsWebServiceException("No authoritative zone was not found for domain: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + switch (zoneInfo.Type) { case AuthZoneType.Secondary: @@ -1814,8 +1961,10 @@ namespace DnsServerCore if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); - if (string.IsNullOrEmpty(zoneName)) - zoneName = zoneInfo.Name; + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); string strType = request.QueryString["type"]; if (string.IsNullOrEmpty(strType)) @@ -1872,7 +2021,7 @@ namespace DnsServerCore string ptrDomain = Zone.GetReverseZone(ipAddress, type == DnsResourceRecordType.A ? 32 : 128); AuthZoneInfo reverseZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.FindAuthZoneInfo(ptrDomain); - if (reverseZoneInfo == null) + if (reverseZoneInfo is null) { bool createPtrZone = false; string strCreatePtrZone = request.QueryString["createPtrZone"]; @@ -1887,6 +2036,12 @@ namespace DnsServerCore reverseZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreatePrimaryZone(ptrZone, _dnsWebService.DnsServer.ServerDomain, false); if (reverseZoneInfo == null) throw new DnsServerException("Failed to create reverse zone to add PTR record: " + ptrZone); + + //set permissions + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SaveConfigFile(); } if (reverseZoneInfo.Internal) @@ -1908,9 +2063,9 @@ namespace DnsServerCore newRecord.SetComments(comments); if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -1938,9 +2093,9 @@ namespace DnsServerCore newRecord.SetComments(comments); if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -1948,7 +2103,7 @@ namespace DnsServerCore { if (!overwrite) { - IReadOnlyList existingRecords = _dnsWebService.DnsServer.AuthZoneManager.GetRecords(zoneName, domain, type); + IReadOnlyList existingRecords = _dnsWebService.DnsServer.AuthZoneManager.GetRecords(zoneInfo.Name, domain, type); if (existingRecords.Count > 0) throw new DnsWebServiceException("Record already exists. Use overwrite option if you wish to overwrite existing records."); } @@ -1967,7 +2122,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); } break; @@ -1988,9 +2143,9 @@ namespace DnsServerCore newRecord.SetComments(comments); if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2015,9 +2170,9 @@ namespace DnsServerCore newRecord.SetComments(comments); if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2038,9 +2193,9 @@ namespace DnsServerCore newRecord.SetComments(comments); if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2073,9 +2228,9 @@ namespace DnsServerCore newRecord.SetComments(comments); if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2083,7 +2238,7 @@ namespace DnsServerCore { if (!overwrite) { - IReadOnlyList existingRecords = _dnsWebService.DnsServer.AuthZoneManager.GetRecords(zoneName, domain, type); + IReadOnlyList existingRecords = _dnsWebService.DnsServer.AuthZoneManager.GetRecords(zoneInfo.Name, domain, type); if (existingRecords.Count > 0) throw new DnsWebServiceException("Record already exists. Use overwrite option if you wish to overwrite existing records."); } @@ -2102,7 +2257,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); } break; @@ -2135,9 +2290,9 @@ namespace DnsServerCore newRecord.SetComments(comments); if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2160,9 +2315,9 @@ namespace DnsServerCore newRecord.SetComments(comments); if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2183,9 +2338,9 @@ namespace DnsServerCore newRecord.SetComments(comments); if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2244,9 +2399,9 @@ namespace DnsServerCore newRecord.SetComments(comments); if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2271,7 +2426,7 @@ namespace DnsServerCore if (!overwrite) { - IReadOnlyList existingRecords = _dnsWebService.DnsServer.AuthZoneManager.GetRecords(zoneName, domain, type); + IReadOnlyList existingRecords = _dnsWebService.DnsServer.AuthZoneManager.GetRecords(zoneInfo.Name, domain, type); if (existingRecords.Count > 0) throw new DnsWebServiceException("Record already exists. Use overwrite option if you wish to overwrite existing records."); } @@ -2281,7 +2436,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); } break; @@ -2289,7 +2444,7 @@ namespace DnsServerCore throw new DnsWebServiceException("Type not supported for AddRecords()."); } - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] New record was added to authoritative zone {domain: " + domain + "; type: " + type + "; value: " + value + "; ttl: " + ttl + ";}"); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] New record was added to authoritative zone {domain: " + domain + "; type: " + type + "; value: " + value + "; ttl: " + ttl + ";}"); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); @@ -2312,6 +2467,11 @@ namespace DnsServerCore if (zoneInfo is null) throw new DnsWebServiceException("No authoritative zone was not found for domain: " + domain); + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + jsonWriter.WritePropertyName("zone"); WriteZoneInfoAsJson(zoneInfo, jsonWriter); @@ -2340,8 +2500,10 @@ namespace DnsServerCore if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); - if (string.IsNullOrEmpty(zoneName)) - zoneName = zoneInfo.Name; + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); string strType = request.QueryString["type"]; if (string.IsNullOrEmpty(strType)) @@ -2368,9 +2530,9 @@ namespace DnsServerCore IPAddress ipAddress = IPAddress.Parse(strIPAddress); if (type == DnsResourceRecordType.A) - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsARecordData(ipAddress)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsARecordData(ipAddress)); else - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsAAAARecordData(ipAddress)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsAAAARecordData(ipAddress)); string ptrDomain = Zone.GetReverseZone(ipAddress, type == DnsResourceRecordType.A ? 32 : 128); AuthZoneInfo reverseZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.FindAuthZoneInfo(ptrDomain); @@ -2405,12 +2567,12 @@ namespace DnsServerCore nameServer = value; } - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsNSRecordData(nameServer)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsNSRecordData(nameServer)); } break; case DnsResourceRecordType.CNAME: - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecords(zoneName, domain, type); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, domain, type); break; case DnsResourceRecordType.PTR: @@ -2424,7 +2586,7 @@ namespace DnsServerCore ptrName = value; } - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsPTRRecordData(ptrName)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsPTRRecordData(ptrName)); } break; @@ -2443,7 +2605,7 @@ namespace DnsServerCore exchange = value; } - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsMXRecordData(ushort.Parse(preference), exchange)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsMXRecordData(ushort.Parse(preference), exchange)); } break; @@ -2458,7 +2620,7 @@ namespace DnsServerCore text = value; } - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsTXTRecordData(text)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsTXTRecordData(text)); } break; @@ -2485,12 +2647,12 @@ namespace DnsServerCore target = value; } - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsSRVRecordData(ushort.Parse(priority), ushort.Parse(weight), ushort.Parse(port), target)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsSRVRecordData(ushort.Parse(priority), ushort.Parse(weight), ushort.Parse(port), target)); } break; case DnsResourceRecordType.DNAME: - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecords(zoneName, domain, type); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, domain, type); break; case DnsResourceRecordType.DS: @@ -2516,7 +2678,7 @@ namespace DnsServerCore digest = value; } - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsDSRecordData(ushort.Parse(strKeyTag), Enum.Parse(strAlgorithm, true), Enum.Parse(strDigestType, true), Convert.FromHexString(digest))); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsDSRecordData(ushort.Parse(strKeyTag), Enum.Parse(strAlgorithm, true), Enum.Parse(strDigestType, true), Convert.FromHexString(digest))); } break; @@ -2533,7 +2695,7 @@ namespace DnsServerCore if (string.IsNullOrEmpty(value)) throw new DnsWebServiceException("Parameter 'value' missing."); - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsCAARecordData(byte.Parse(flags), tag, value)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsCAARecordData(byte.Parse(flags), tag, value)); } break; @@ -2548,7 +2710,7 @@ namespace DnsServerCore aname = value; } - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsANAMERecordData(aname)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsANAMERecordData(aname)); } break; @@ -2567,19 +2729,19 @@ namespace DnsServerCore forwarder = value; } - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneName, domain, type, new DnsForwarderRecordData(Enum.Parse(strProtocol, true), forwarder)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsForwarderRecordData(Enum.Parse(strProtocol, true), forwarder)); } break; case DnsResourceRecordType.APP: - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecords(zoneName, domain, type); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, domain, type); break; default: throw new DnsWebServiceException("Type not supported for DeleteRecord()."); } - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Record was deleted from authoritative zone {domain: " + domain + "; type: " + type + ";}"); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Record was deleted from authoritative zone {domain: " + domain + "; type: " + type + ";}"); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); } @@ -2609,8 +2771,10 @@ namespace DnsServerCore if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); - if (string.IsNullOrEmpty(zoneName)) - zoneName = zoneInfo.Name; + UserSession session = _dnsWebService.GetSession(request); + + if (!_dnsWebService.AuthManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); string newDomain = request.QueryString["newDomain"]; if (string.IsNullOrEmpty(newDomain)) @@ -2686,8 +2850,14 @@ namespace DnsServerCore string ptrZone = Zone.GetReverseZone(newIpAddress, type == DnsResourceRecordType.A ? 24 : 64); reverseZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreatePrimaryZone(ptrZone, _dnsWebService.DnsServer.ServerDomain, false); - if (reverseZoneInfo == null) + if (reverseZoneInfo is null) throw new DnsServerException("Failed to create reverse zone to add PTR record: " + ptrZone); + + //set permissions + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _dnsWebService.AuthManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _dnsWebService.AuthManager.SaveConfigFile(); } if (reverseZoneInfo.Internal) @@ -2730,7 +2900,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -2767,7 +2937,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(glueAddresses)) newRecord.SetGlueRecords(glueAddresses); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -2791,7 +2961,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -2856,7 +3026,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newSOARecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneName, newSOARecord); + _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newSOARecord); newRecord = zoneInfo.GetRecords(DnsResourceRecordType.SOA)[0]; } @@ -2891,7 +3061,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -2932,7 +3102,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -2965,7 +3135,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3022,7 +3192,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3046,7 +3216,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3103,7 +3273,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3140,7 +3310,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3173,7 +3343,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3249,7 +3419,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3281,7 +3451,7 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(comments)) newRecord.SetComments(comments); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneName, oldRecord, newRecord); + _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3289,7 +3459,7 @@ namespace DnsServerCore throw new DnsWebServiceException("Type not supported for UpdateRecords()."); } - _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + _dnsWebService.GetSession(request).Username + "] Record was updated for authoritative zone {oldDomain: " + domain + "; domain: " + newDomain + "; type: " + type + "; oldValue: " + value + "; value: " + newValue + "; ttl: " + ttl + "; disabled: " + disable + ";}"); + _dnsWebService.Log.Write(DnsWebService.GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Record was updated for authoritative zone {oldDomain: " + domain + "; domain: " + newDomain + "; type: " + type + "; oldValue: " + value + "; value: " + newValue + "; ttl: " + ttl + "; disabled: " + disable + ";}"); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); From adbf13d4cc4d6ea5a0271d854f902e52d2d4fd40 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 18:04:28 +0530 Subject: [PATCH 23/53] DnsWebService: updated implementation to add multi-user support. API paths updated to reflect the permission section each call belongs. Added support to stop block list automatic update. Updated dns config file format. --- DnsServerCore/DnsWebService.cs | 3254 +++++++++++++++++++------------- 1 file changed, 1965 insertions(+), 1289 deletions(-) diff --git a/DnsServerCore/DnsWebService.cs b/DnsServerCore/DnsWebService.cs index 8f760e51..41e1164c 100644 --- a/DnsServerCore/DnsWebService.cs +++ b/DnsServerCore/DnsWebService.cs @@ -17,6 +17,7 @@ along with this program. If not, see . */ +using DnsServerCore.Auth; using DnsServerCore.Dhcp; using DnsServerCore.Dns; using DnsServerCore.Dns.ResourceRecords; @@ -25,7 +26,6 @@ using DnsServerCore.Dns.Zones; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.IO.Compression; @@ -67,6 +67,10 @@ namespace DnsServerCore readonly static RandomNumberGenerator _rng = RandomNumberGenerator.Create(); + readonly LogManager _log; + readonly AuthManager _authManager; + + readonly WebServiceAuthApi _authApi; readonly WebServiceDashboardApi _dashboardApi; readonly WebServiceZonesApi _zonesApi; readonly WebServiceOtherZonesApi _otherZonesApi; @@ -79,8 +83,6 @@ namespace DnsServerCore readonly string _configFolder; readonly Uri _updateCheckUri; - readonly LogManager _log; - DnsServer _dnsServer; DhcpServer _dhcpServer; @@ -109,13 +111,6 @@ namespace DnsServerCore const int TLS_CERTIFICATE_UPDATE_TIMER_INITIAL_INTERVAL = 60000; const int TLS_CERTIFICATE_UPDATE_TIMER_INTERVAL = 60000; - const int MAX_LOGIN_ATTEMPTS = 5; - const int BLOCK_ADDRESS_INTERVAL = 5 * 60 * 1000; - readonly ConcurrentDictionary _failedLoginAttempts = new ConcurrentDictionary(); - readonly ConcurrentDictionary _blockedAddresses = new ConcurrentDictionary(); - readonly ConcurrentDictionary _credentials = new ConcurrentDictionary(); - readonly ConcurrentDictionary _sessions = new ConcurrentDictionary(); - volatile ServiceState _state = ServiceState.Stopped; Timer _blockListUpdateTimer; @@ -135,20 +130,12 @@ namespace DnsServerCore public DnsWebService(string configFolder = null, Uri updateCheckUri = null, Uri appStoreUri = null) { - _dashboardApi = new WebServiceDashboardApi(this); - _zonesApi = new WebServiceZonesApi(this); - _otherZonesApi = new WebServiceOtherZonesApi(this); - _appsApi = new WebServiceAppsApi(this, appStoreUri); - _dhcpApi = new WebServiceDhcpApi(this); - _logsApi = new WebServiceLogsApi(this); - Assembly assembly = Assembly.GetExecutingAssembly(); - AssemblyName assemblyName = assembly.GetName(); - _currentVersion = assemblyName.Version; + _currentVersion = assembly.GetName().Version; _appFolder = Path.GetDirectoryName(assembly.Location); - if (configFolder == null) + if (configFolder is null) _configFolder = Path.Combine(_appFolder, "config"); else _configFolder = configFolder; @@ -159,6 +146,15 @@ namespace DnsServerCore _updateCheckUri = updateCheckUri; _log = new LogManager(_configFolder); + _authManager = new AuthManager(_configFolder, _log); + + _authApi = new WebServiceAuthApi(this); + _dashboardApi = new WebServiceDashboardApi(this); + _zonesApi = new WebServiceZonesApi(this); + _otherZonesApi = new WebServiceOtherZonesApi(this); + _appsApi = new WebServiceAppsApi(this, appStoreUri); + _dhcpApi = new WebServiceDhcpApi(this); + _logsApi = new WebServiceLogsApi(this); string blockListsFolder = Path.Combine(_configFolder, "blocklists"); @@ -179,16 +175,22 @@ namespace DnsServerCore Stop(); - if (_webService != null) + if (_appsApi is not null) + _appsApi.Dispose(); + + if (_webService is not null) _webService.Close(); - if (_dnsServer != null) + if (_dnsServer is not null) _dnsServer.Dispose(); - if (_dhcpServer != null) + if (_dhcpServer is not null) _dhcpServer.Dispose(); - if (_log != null) + if (_authManager is not null) + _authManager.Dispose(); + + if (_log is not null) _log.Dispose(); _disposed = true; @@ -374,16 +376,26 @@ namespace DnsServerCore switch (path) { + case "/api/user/login": case "/api/login": - await LoginAsync(request, jsonWriter); + await _authApi.LoginAsync(request, jsonWriter, UserSessionType.Standard); break; + case "/api/user/createToken": + await _authApi.LoginAsync(request, jsonWriter, UserSessionType.ApiToken); + break; + + case "/api/user/logout": case "/api/logout": - Logout(request); + _authApi.Logout(request); + break; + + case "/api/user/session/get": + _authApi.GetCurrentSessionDetails(request, jsonWriter); break; default: - if (!IsSessionValid(request)) + if (!TryGetSession(request, out UserSession session)) throw new InvalidTokenWebServiceException("Invalid token or session expired."); jsonWriter.WritePropertyName("response"); @@ -393,315 +405,756 @@ namespace DnsServerCore { switch (path) { - case "/api/changePassword": - ChangePassword(request); + case "/api/user/session/delete": + _authApi.DeleteSession(request, false); break; + case "/api/user/changePassword": + case "/api/changePassword": + _authApi.ChangePassword(request); + break; + + case "/api/user/profile/get": + _authApi.GetProfile(request, jsonWriter); + break; + + case "/api/user/profile/set": + _authApi.SetProfile(request, jsonWriter); + break; + + case "/api/user/checkForUpdate": case "/api/checkForUpdate": await CheckForUpdateAsync(request, jsonWriter); break; - case "/api/getDnsSettings": - GetDnsSettings(jsonWriter); - break; - - case "/api/setDnsSettings": - SetDnsSettings(request, jsonWriter); - break; - - case "/api/forceUpdateBlockLists": - ForceUpdateBlockLists(request); - break; - - case "/api/temporaryDisableBlocking": - TemporaryDisableBlocking(request, jsonWriter); - break; - - case "/api/backupSettings": - await BackupSettingsAsync(request, response); - return; - - case "/api/restoreSettings": - await RestoreSettingsAsync(request, jsonWriter); - break; - + case "/api/dashboard/stats/get": case "/api/getStats": + if (!_authManager.IsPermitted(PermissionSection.Dashboard, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + await _dashboardApi.GetStats(request, jsonWriter); break; + case "/api/dashboard/stats/getTop": case "/api/getTopStats": + if (!_authManager.IsPermitted(PermissionSection.Dashboard, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + await _dashboardApi.GetTopStats(request, jsonWriter); break; - case "/api/flushDnsCache": - _otherZonesApi.FlushCache(request); - break; - - case "/api/listCachedZones": - _otherZonesApi.ListCachedZones(request, jsonWriter); - break; - - case "/api/deleteCachedZone": - _otherZonesApi.DeleteCachedZone(request); - break; - - case "/api/listAllowedZones": - _otherZonesApi.ListAllowedZones(request, jsonWriter); - break; - - case "/api/importAllowedZones": - await _otherZonesApi.ImportAllowedZonesAsync(request); - break; - - case "/api/exportAllowedZones": - _otherZonesApi.ExportAllowedZones(response); - return; - - case "/api/deleteAllowedZone": - _otherZonesApi.DeleteAllowedZone(request); - break; - - case "/api/flushAllowedZone": - _otherZonesApi.FlushAllowedZone(request); - break; - - case "/api/allowZone": - _otherZonesApi.AllowZone(request); - break; - - case "/api/listBlockedZones": - _otherZonesApi.ListBlockedZones(request, jsonWriter); - break; - - case "/api/importBlockedZones": - await _otherZonesApi.ImportBlockedZonesAsync(request); - break; - - case "/api/exportBlockedZones": - _otherZonesApi.ExportBlockedZones(response); - return; - - case "/api/deleteBlockedZone": - _otherZonesApi.DeleteBlockedZone(request); - break; - - case "/api/flushBlockedZone": - _otherZonesApi.FlushBlockedZone(request); - break; - - case "/api/blockZone": - _otherZonesApi.BlockZone(request); + case "/api/dashboard/stats/deleteAll": + case "/api/deleteAllStats": + if (!_authManager.IsPermitted(PermissionSection.Dashboard, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _logsApi.DeleteAllStats(request); break; + case "/api/zones/list": case "/api/zone/list": case "/api/listZones": - _zonesApi.ListZones(jsonWriter); + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.ListZones(request, jsonWriter); break; + case "/api/zones/create": case "/api/zone/create": case "/api/createZone": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + await _zonesApi.CreateZoneAsync(request, jsonWriter); break; - case "/api/zone/dnssec/sign": - _zonesApi.SignPrimaryZone(request); - break; - - case "/api/zone/dnssec/unsign": - _zonesApi.UnsignPrimaryZone(request); - break; - - case "/api/zone/dnssec/getProperties": - _zonesApi.GetPrimaryZoneDnssecProperties(request, jsonWriter); - break; - - case "/api/zone/dnssec/convertToNSEC": - _zonesApi.ConvertPrimaryZoneToNSEC(request); - break; - - case "/api/zone/dnssec/convertToNSEC3": - _zonesApi.ConvertPrimaryZoneToNSEC3(request); - break; - - case "/api/zone/dnssec/updateNSEC3Params": - _zonesApi.UpdatePrimaryZoneNSEC3Parameters(request); - break; - - case "/api/zone/dnssec/updateDnsKeyTtl": - _zonesApi.UpdatePrimaryZoneDnssecDnsKeyTtl(request); - break; - - case "/api/zone/dnssec/generatePrivateKey": - _zonesApi.GenerateAndAddPrimaryZoneDnssecPrivateKey(request); - break; - - case "/api/zone/dnssec/updatePrivateKey": - _zonesApi.UpdatePrimaryZoneDnssecPrivateKey(request); - break; - - case "/api/zone/dnssec/deletePrivateKey": - _zonesApi.DeletePrimaryZoneDnssecPrivateKey(request); - break; - - case "/api/zone/dnssec/publishAllPrivateKeys": - _zonesApi.PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(request); - break; - - case "/api/zone/dnssec/rolloverDnsKey": - _zonesApi.RolloverPrimaryZoneDnsKey(request); - break; - - case "/api/zone/dnssec/retireDnsKey": - _zonesApi.RetirePrimaryZoneDnsKey(request); - break; - - case "/api/zone/delete": - case "/api/deleteZone": - _zonesApi.DeleteZone(request); - break; - + case "/api/zones/enable": case "/api/zone/enable": case "/api/enableZone": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + _zonesApi.EnableZone(request); break; + case "/api/zones/disable": case "/api/zone/disable": case "/api/disableZone": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + _zonesApi.DisableZone(request); break; - case "/api/zone/options/get": - _zonesApi.GetZoneOptions(request, jsonWriter); - break; - - case "/api/zone/options/set": - _zonesApi.SetZoneOptions(request); + case "/api/zones/delete": + case "/api/zone/delete": + case "/api/deleteZone": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.DeleteZone(request); break; + case "/api/zones/resync": case "/api/zone/resync": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + _zonesApi.ResyncZone(request); break; + case "/api/zones/options/get": + case "/api/zone/options/get": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.GetZoneOptions(request, jsonWriter); + break; + + case "/api/zones/options/set": + case "/api/zone/options/set": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.SetZoneOptions(request); + break; + + case "/api/zones/permissions/get": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.GetPermissionDetails(request, jsonWriter, PermissionSection.Zones); + break; + + case "/api/zones/permissions/set": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.SetPermissionsDetails(request, jsonWriter, PermissionSection.Zones); + break; + + case "/api/zones/dnssec/sign": + case "/api/zone/dnssec/sign": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.SignPrimaryZone(request); + break; + + case "/api/zones/dnssec/unsign": + case "/api/zone/dnssec/unsign": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.UnsignPrimaryZone(request); + break; + + case "/api/zones/dnssec/properties/get": + case "/api/zone/dnssec/getProperties": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.GetPrimaryZoneDnssecProperties(request, jsonWriter); + break; + + case "/api/zones/dnssec/properties/convertToNSEC": + case "/api/zone/dnssec/convertToNSEC": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.ConvertPrimaryZoneToNSEC(request); + break; + + case "/api/zones/dnssec/properties/convertToNSEC3": + case "/api/zone/dnssec/convertToNSEC3": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.ConvertPrimaryZoneToNSEC3(request); + break; + + case "/api/zones/dnssec/properties/updateNSEC3Params": + case "/api/zone/dnssec/updateNSEC3Params": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.UpdatePrimaryZoneNSEC3Parameters(request); + break; + + case "/api/zones/dnssec/properties/updateDnsKeyTtl": + case "/api/zone/dnssec/updateDnsKeyTtl": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.UpdatePrimaryZoneDnssecDnsKeyTtl(request); + break; + + case "/api/zones/dnssec/properties/generatePrivateKey": + case "/api/zone/dnssec/generatePrivateKey": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.GenerateAndAddPrimaryZoneDnssecPrivateKey(request); + break; + + case "/api/zones/dnssec/properties/updatePrivateKey": + case "/api/zone/dnssec/updatePrivateKey": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.UpdatePrimaryZoneDnssecPrivateKey(request); + break; + + case "/api/zones/dnssec/properties/deletePrivateKey": + case "/api/zone/dnssec/deletePrivateKey": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.DeletePrimaryZoneDnssecPrivateKey(request); + break; + + case "/api/zones/dnssec/properties/publishAllPrivateKeys": + case "/api/zone/dnssec/publishAllPrivateKeys": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(request); + break; + + case "/api/zones/dnssec/properties/rolloverDnsKey": + case "/api/zone/dnssec/rolloverDnsKey": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.RolloverPrimaryZoneDnsKey(request); + break; + + case "/api/zones/dnssec/properties/retireDnsKey": + case "/api/zone/dnssec/retireDnsKey": + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _zonesApi.RetirePrimaryZoneDnsKey(request); + break; + + case "/api/zones/records/add": case "/api/zone/addRecord": case "/api/addRecord": _zonesApi.AddRecord(request, jsonWriter); break; + case "/api/zones/records/get": case "/api/zone/getRecords": case "/api/getRecords": _zonesApi.GetRecords(request, jsonWriter); break; - case "/api/zone/deleteRecord": - case "/api/deleteRecord": - _zonesApi.DeleteRecord(request); - break; - + case "/api/zones/records/update": case "/api/zone/updateRecord": case "/api/updateRecord": _zonesApi.UpdateRecord(request, jsonWriter); break; + case "/api/zones/records/delete": + case "/api/zone/deleteRecord": + case "/api/deleteRecord": + _zonesApi.DeleteRecord(request); + break; + + case "/api/cache/list": + case "/api/listCachedZones": + if (!_authManager.IsPermitted(PermissionSection.Cache, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.ListCachedZones(request, jsonWriter); + break; + + case "/api/cache/delete": + case "/api/deleteCachedZone": + if (!_authManager.IsPermitted(PermissionSection.Cache, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.DeleteCachedZone(request); + break; + + case "/api/cache/flush": + case "/api/flushDnsCache": + if (!_authManager.IsPermitted(PermissionSection.Cache, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.FlushCache(request); + break; + + case "/api/allowed/list": + case "/api/listAllowedZones": + if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.ListAllowedZones(request, jsonWriter); + break; + + case "/api/allowed/add": + case "/api/allowZone": + if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.AllowZone(request); + break; + + case "/api/allowed/delete": + case "/api/deleteAllowedZone": + if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.DeleteAllowedZone(request); + break; + + case "/api/allowed/flush": + case "/api/flushAllowedZone": + if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.FlushAllowedZone(request); + break; + + case "/api/allowed/import": + case "/api/importAllowedZones": + if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + await _otherZonesApi.ImportAllowedZonesAsync(request); + break; + + case "/api/allowed/export": + case "/api/exportAllowedZones": + if (!_authManager.IsPermitted(PermissionSection.Allowed, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.ExportAllowedZones(response); + return; + + case "/api/blocked/list": + case "/api/listBlockedZones": + if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.ListBlockedZones(request, jsonWriter); + break; + + case "/api/blocked/add": + case "/api/blockZone": + if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.BlockZone(request); + break; + + case "/api/blocked/delete": + case "/api/deleteBlockedZone": + if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.DeleteBlockedZone(request); + break; + + case "/api/blocked/flush": + case "/api/flushBlockedZone": + if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.FlushBlockedZone(request); + break; + + case "/api/blocked/import": + case "/api/importBlockedZones": + if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + await _otherZonesApi.ImportBlockedZonesAsync(request); + break; + + case "/api/blocked/export": + case "/api/exportBlockedZones": + if (!_authManager.IsPermitted(PermissionSection.Blocked, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _otherZonesApi.ExportBlockedZones(response); + return; + case "/api/apps/list": - await _appsApi.ListInstalledAppsAsync(jsonWriter); + if (_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.View) || + _authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.View) || + _authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View)) + { + await _appsApi.ListInstalledAppsAsync(jsonWriter); + } + else + { + throw new DnsWebServiceException("Access was denied."); + } + break; case "/api/apps/listStoreApps": + if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + await _appsApi.ListStoreApps(jsonWriter); break; case "/api/apps/downloadAndInstall": + if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + await _appsApi.DownloadAndInstallAppAsync(request); break; case "/api/apps/downloadAndUpdate": + if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + await _appsApi.DownloadAndUpdateAppAsync(request); break; case "/api/apps/install": + if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + await _appsApi.InstallAppAsync(request); break; case "/api/apps/update": + if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + await _appsApi.UpdateAppAsync(request); break; case "/api/apps/uninstall": + if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + _appsApi.UninstallApp(request); break; + case "/api/apps/config/get": case "/api/apps/getConfig": + if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + await _appsApi.GetAppConfigAsync(request, jsonWriter); break; + case "/api/apps/config/set": case "/api/apps/setConfig": + if (!_authManager.IsPermitted(PermissionSection.Apps, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + await _appsApi.SetAppConfigAsync(request); break; + case "/api/dnsClient/resolve": case "/api/resolveQuery": + if (!_authManager.IsPermitted(PermissionSection.DnsClient, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + await ResolveQueryAsync(request, jsonWriter); break; - case "/api/listLogs": - _logsApi.ListLogs(jsonWriter); + case "/api/settings/get": + case "/api/getDnsSettings": + if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + GetDnsSettings(jsonWriter); break; - case "/api/deleteLog": - _logsApi.DeleteLog(request); + case "/api/settings/set": + case "/api/setDnsSettings": + if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + SetDnsSettings(request, jsonWriter); break; - case "/api/deleteAllLogs": - _logsApi.DeleteAllLogs(request); + case "/api/settings/forceUpdateBlockLists": + case "/api/forceUpdateBlockLists": + if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + ForceUpdateBlockLists(request); break; - case "/api/deleteAllStats": - _logsApi.DeleteAllStats(request); + case "/api/settings/temporaryDisableBlocking": + case "/api/temporaryDisableBlocking": + if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + TemporaryDisableBlocking(request, jsonWriter); break; - case "/api/queryLogs": - await _logsApi.QueryLogsAsync(request, jsonWriter); - break; - - case "/api/listDhcpScopes": - _dhcpApi.ListDhcpScopes(jsonWriter); + case "/api/settings/backup": + case "/api/backupSettings": + if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + await BackupSettingsAsync(request, response); + return; + + case "/api/settings/restore": + case "/api/restoreSettings": + if (!_authManager.IsPermitted(PermissionSection.Settings, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + await RestoreSettingsAsync(request, jsonWriter); break; + case "/api/dhcp/leases/list": case "/api/listDhcpLeases": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + _dhcpApi.ListDhcpLeases(jsonWriter); break; - case "/api/getDhcpScope": - _dhcpApi.GetDhcpScope(request, jsonWriter); - break; - - case "/api/setDhcpScope": - await _dhcpApi.SetDhcpScopeAsync(request); - break; - - case "/api/enableDhcpScope": - await _dhcpApi.EnableDhcpScopeAsync(request); - break; - - case "/api/disableDhcpScope": - _dhcpApi.DisableDhcpScope(request); - break; - - case "/api/deleteDhcpScope": - _dhcpApi.DeleteDhcpScope(request); - break; - + case "/api/dhcp/leases/remove": case "/api/removeDhcpLease": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + _dhcpApi.RemoveDhcpLease(request); break; + case "/api/dhcp/leases/convertToReserved": case "/api/convertToReservedLease": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + _dhcpApi.ConvertToReservedLease(request); break; + case "/api/dhcp/leases/convertToDynamic": case "/api/convertToDynamicLease": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + _dhcpApi.ConvertToDynamicLease(request); break; + case "/api/dhcp/scopes/list": + case "/api/listDhcpScopes": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _dhcpApi.ListDhcpScopes(jsonWriter); + break; + + case "/api/dhcp/scopes/get": + case "/api/getDhcpScope": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _dhcpApi.GetDhcpScope(request, jsonWriter); + break; + + case "/api/dhcp/scopes/set": + case "/api/setDhcpScope": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + await _dhcpApi.SetDhcpScopeAsync(request); + break; + + case "/api/dhcp/scopes/addReservedLease": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _dhcpApi.AddReservedLease(request); + break; + + case "/api/dhcp/scopes/removeReservedLease": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _dhcpApi.RemoveReservedLease(request); + break; + + case "/api/dhcp/scopes/enable": + case "/api/enableDhcpScope": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + await _dhcpApi.EnableDhcpScopeAsync(request); + break; + + case "/api/dhcp/scopes/disable": + case "/api/disableDhcpScope": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _dhcpApi.DisableDhcpScope(request); + break; + + case "/api/dhcp/scopes/delete": + case "/api/deleteDhcpScope": + if (!_authManager.IsPermitted(PermissionSection.DhcpServer, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _dhcpApi.DeleteDhcpScope(request); + break; + + case "/api/admin/sessions/list": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.ListSessions(request, jsonWriter); + break; + + case "/api/admin/sessions/createToken": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.CreateApiToken(request, jsonWriter); + break; + + case "/api/admin/sessions/delete": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.DeleteSession(request, true); + break; + + case "/api/admin/users/list": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.ListUsers(jsonWriter); + break; + + case "/api/admin/users/create": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.CreateUser(request, jsonWriter); + break; + + case "/api/admin/users/get": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.GetUserDetails(request, jsonWriter); + break; + + case "/api/admin/users/set": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.SetUserDetails(request, jsonWriter); + break; + + case "/api/admin/users/delete": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.DeleteUser(request); + break; + + case "/api/admin/groups/list": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.ListGroups(jsonWriter); + break; + + case "/api/admin/groups/create": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.CreateGroup(request, jsonWriter); + break; + + case "/api/admin/groups/get": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.GetGroupDetails(request, jsonWriter); + break; + + case "/api/admin/groups/set": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.SetGroupDetails(request, jsonWriter); + break; + + case "/api/admin/groups/delete": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.DeleteGroup(request); + break; + + case "/api/admin/permissions/list": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.ListPermissions(jsonWriter); + break; + + case "/api/admin/permissions/get": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.GetPermissionDetails(request, jsonWriter, PermissionSection.Unknown); + break; + + case "/api/admin/permissions/set": + if (!_authManager.IsPermitted(PermissionSection.Administration, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _authApi.SetPermissionsDetails(request, jsonWriter, PermissionSection.Unknown); + break; + + case "/api/logs/list": + case "/api/listLogs": + if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + _logsApi.ListLogs(jsonWriter); + break; + + case "/api/logs/download": + if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + await _logsApi.DownloadLogAsync(request, response); + return; + + case "/api/logs/delete": + case "/api/deleteLog": + if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _logsApi.DeleteLog(request); + break; + + case "/api/logs/deleteAll": + case "/api/deleteAllLogs": + if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.Delete)) + throw new DnsWebServiceException("Access was denied."); + + _logsApi.DeleteAllLogs(request); + break; + + case "/api/logs/query": + case "/api/queryLogs": + if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + + await _logsApi.QueryLogsAsync(request, jsonWriter); + break; + default: await SendErrorAsync(response, 404); return; @@ -737,7 +1190,16 @@ namespace DnsServerCore } catch (Exception ex) { - _log.Write(GetRequestRemoteEndPoint(request), ex); + UserSession session = null; + + string strToken = request.QueryString["token"]; + if (!string.IsNullOrEmpty(strToken)) + session = _authManager.GetSession(strToken); + + if (session is null) + _log.Write(GetRequestRemoteEndPoint(request), ex); + else + _log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] " + ex.ToString()); mS.SetLength(0); JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS)); @@ -775,12 +1237,15 @@ namespace DnsServerCore } else if (path.StartsWith("/log/")) { - if (!IsSessionValid(request)) + if (!TryGetSession(request, out UserSession session)) { await SendErrorAsync(response, 403, "Invalid token or session expired."); return; } + if (!_authManager.IsPermitted(PermissionSection.Logs, session.User, PermissionFlag.View)) + throw new DnsWebServiceException("Access was denied."); + string[] pathParts = path.Split('/'); string logFileName = pathParts[2]; @@ -820,7 +1285,16 @@ namespace DnsServerCore if ((_state == ServiceState.Stopping) || (_state == ServiceState.Stopped)) return; //web service stopping - _log.Write(GetRequestRemoteEndPoint(request), ex); + UserSession session = null; + + string strToken = request.QueryString["token"]; + if (!string.IsNullOrEmpty(strToken)) + session = _authManager.GetSession(strToken); + + if (session is null) + _log.Write(GetRequestRemoteEndPoint(request), ex); + else + _log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] " + ex.ToString()); await SendError(response, ex); } @@ -923,198 +1397,34 @@ namespace DnsServerCore } } - #endregion - - #region user session - - private string CreateSession(string username) - { - string token = BinaryNumber.GenerateRandomNumber256().ToString(); - - if (!_sessions.TryAdd(token, new UserSession(username))) - throw new DnsWebServiceException("Error while creating session. Please try again."); - - return token; - } - - private UserSession GetSession(string token) - { - if (_sessions.TryGetValue(token, out UserSession session)) - return session; - - return null; - } - internal UserSession GetSession(HttpListenerRequest request) { string strToken = request.QueryString["token"]; if (string.IsNullOrEmpty(strToken)) throw new DnsWebServiceException("Parameter 'token' missing."); - return GetSession(strToken); + return _authManager.GetSession(strToken); } - private UserSession DeleteSession(string token) + internal bool TryGetSession(HttpListenerRequest request, out UserSession session) { - if (_sessions.TryRemove(token, out UserSession session)) - return session; - - return null; - } - - private UserSession DeleteSession(HttpListenerRequest request) - { - string strToken = request.QueryString["token"]; - if (string.IsNullOrEmpty(strToken)) - throw new DnsWebServiceException("Parameter 'token' missing."); - - return DeleteSession(strToken); - } - - private void FailedLoginAttempt(IPAddress address) - { - _failedLoginAttempts.AddOrUpdate(address, 1, delegate (IPAddress key, int attempts) - { - return attempts + 1; - }); - } - - private bool LoginAttemptsExceedLimit(IPAddress address, int limit) - { - if (!_failedLoginAttempts.TryGetValue(address, out int attempts)) - return false; - - return attempts >= limit; - } - - private void ResetFailedLoginAttempt(IPAddress address) - { - _failedLoginAttempts.TryRemove(address, out _); - } - - private void BlockAddress(IPAddress address, int interval) - { - _blockedAddresses.TryAdd(address, DateTime.UtcNow.AddMilliseconds(interval)); - } - - private bool IsAddressBlocked(IPAddress address) - { - if (!_blockedAddresses.TryGetValue(address, out DateTime expiry)) - return false; - - if (expiry > DateTime.UtcNow) - { - return true; - } - else - { - UnblockAddress(address); - ResetFailedLoginAttempt(address); - - return false; - } - } - - private void UnblockAddress(IPAddress address) - { - _blockedAddresses.TryRemove(address, out _); - } - - #endregion - - #region auth api - - private async Task LoginAsync(HttpListenerRequest request, JsonTextWriter jsonWriter) - { - string strUsername = request.QueryString["user"]; - if (string.IsNullOrEmpty(strUsername)) - throw new DnsWebServiceException("Parameter 'user' missing."); - - string strPassword = request.QueryString["pass"]; - if (string.IsNullOrEmpty(strPassword)) - throw new DnsWebServiceException("Parameter 'pass' missing."); - - IPEndPoint remoteEP = GetRequestRemoteEndPoint(request); - - if (IsAddressBlocked(remoteEP.Address)) - throw new DnsWebServiceException("Max limit of " + MAX_LOGIN_ATTEMPTS + " attempts exceeded. Access blocked for " + (BLOCK_ADDRESS_INTERVAL / 1000) + " seconds."); - - strUsername = strUsername.Trim().ToLower(); - string strPasswordHash = GetPasswordHash(strUsername, strPassword); - - if (!_credentials.TryGetValue(strUsername, out string passwordHash) || (passwordHash != strPasswordHash)) - { - if (strPassword != "admin") //exception for default password - { - FailedLoginAttempt(remoteEP.Address); - - if (LoginAttemptsExceedLimit(remoteEP.Address, MAX_LOGIN_ATTEMPTS)) - BlockAddress(remoteEP.Address, BLOCK_ADDRESS_INTERVAL); - - await Task.Delay(1000); - } - - throw new DnsWebServiceException("Invalid username or password for user: " + strUsername); - } - - ResetFailedLoginAttempt(remoteEP.Address); - - _log.Write(remoteEP, "[" + strUsername + "] User logged in."); - - string token = CreateSession(strUsername); - - jsonWriter.WritePropertyName("token"); - jsonWriter.WriteValue(token); - } - - private bool IsSessionValid(HttpListenerRequest request) - { - UserSession session = GetSession(request); - if (session == null) + session = GetSession(request); + if ((session is null) || session.User.Disabled) return false; if (session.HasExpired()) { - DeleteSession(request); + _authManager.DeleteSession(session.Token); + _authManager.SaveConfigFile(); return false; } - session.UpdateLastSeen(); + IPEndPoint remoteEP = GetRequestRemoteEndPoint(request); + + session.UpdateLastSeen(remoteEP.Address, request.UserAgent); return true; } - private void ChangePassword(HttpListenerRequest request) - { - string strToken = request.QueryString["token"]; - if (string.IsNullOrEmpty(strToken)) - throw new DnsWebServiceException("Parameter 'token' missing."); - - string strPassword = request.QueryString["pass"]; - if (string.IsNullOrEmpty(strPassword)) - throw new DnsWebServiceException("Parameter 'pass' missing."); - - UserSession session = GetSession(strToken); - if (session == null) - throw new DnsWebServiceException("User session does not exists."); - - SetCredentials(session.Username, strPassword); - SaveConfigFile(); - - _log.Write(GetRequestRemoteEndPoint(request), "[" + session.Username + "] Password was changed for user."); - } - - private void Logout(HttpListenerRequest request) - { - string strToken = request.QueryString["token"]; - if (string.IsNullOrEmpty(strToken)) - throw new DnsWebServiceException("Parameter 'token' missing."); - - UserSession session = DeleteSession(strToken); - - if (session != null) - _log.Write(GetRequestRemoteEndPoint(request), "[" + session.Username + "] User logged out."); - } - #endregion #region update api @@ -1132,6 +1442,7 @@ namespace DnsServerCore { SocketsHttpHandler handler = new SocketsHttpHandler(); handler.Proxy = _dnsServer.Proxy; + handler.UseProxy = _dnsServer.Proxy is not null; using (HttpClient http = new HttpClient(handler)) { @@ -1218,6 +1529,11 @@ namespace DnsServerCore return strVersion; } + internal string GetServerVersion() + { + return GetCleanVersion(_currentVersion); + } + #endregion #region settings api @@ -1225,7 +1541,7 @@ namespace DnsServerCore private void GetDnsSettings(JsonTextWriter jsonWriter) { jsonWriter.WritePropertyName("version"); - jsonWriter.WriteValue(GetCleanVersion(_currentVersion)); + jsonWriter.WriteValue(GetServerVersion()); jsonWriter.WritePropertyName("dnsServerDomain"); jsonWriter.WriteValue(_dnsServer.ServerDomain); @@ -1316,6 +1632,9 @@ namespace DnsServerCore jsonWriter.WritePropertyName("defaultRecordTtl"); jsonWriter.WriteValue(_zonesApi.DefaultRecordTtl); + jsonWriter.WritePropertyName("dnsAppsEnableAutomaticUpdate"); + jsonWriter.WriteValue(_appsApi.EnableAutomaticUpdate); + jsonWriter.WritePropertyName("preferIPv6"); jsonWriter.WriteValue(_dnsServer.PreferIPv6); @@ -1825,6 +2144,10 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(strDefaultRecordTtl)) _zonesApi.DefaultRecordTtl = uint.Parse(strDefaultRecordTtl); + string strDnsAppsEnableAutomaticUpdate = request.QueryString["dnsAppsEnableAutomaticUpdate"]; + if (!string.IsNullOrEmpty(strDnsAppsEnableAutomaticUpdate)) + _appsApi.EnableAutomaticUpdate = bool.Parse(strDnsAppsEnableAutomaticUpdate); + string strPreferIPv6 = request.QueryString["preferIPv6"]; if (!string.IsNullOrEmpty(strPreferIPv6)) _dnsServer.PreferIPv6 = bool.Parse(strPreferIPv6); @@ -2141,21 +2464,18 @@ namespace DnsServerCore } } + bool blockListUrlsUpdated = false; string strBlockListUrls = request.QueryString["blockListUrls"]; if (!string.IsNullOrEmpty(strBlockListUrls)) { if (strBlockListUrls == "false") { - StopBlockListUpdateTimer(); - _dnsServer.BlockListZoneManager.AllowListUrls.Clear(); _dnsServer.BlockListZoneManager.BlockListUrls.Clear(); _dnsServer.BlockListZoneManager.Flush(); } else { - bool updated = false; - string[] strBlockListUrlList = strBlockListUrls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (oldWebServiceHttpPort != _webServiceHttpPort) @@ -2165,17 +2485,17 @@ namespace DnsServerCore if (strBlockListUrlList[i].Contains("http://localhost:" + oldWebServiceHttpPort + "/blocklist.txt")) { strBlockListUrlList[i] = "http://localhost:" + _webServiceHttpPort + "/blocklist.txt"; - updated = true; + blockListUrlsUpdated = true; break; } } } - if (!updated) + if (!blockListUrlsUpdated) { if (strBlockListUrlList.Length != (_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count)) { - updated = true; + blockListUrlsUpdated = true; } else { @@ -2187,7 +2507,7 @@ namespace DnsServerCore if (!_dnsServer.BlockListZoneManager.AllowListUrls.Contains(new Uri(strAllowListUrl))) { - updated = true; + blockListUrlsUpdated = true; break; } } @@ -2195,7 +2515,7 @@ namespace DnsServerCore { if (!_dnsServer.BlockListZoneManager.BlockListUrls.Contains(new Uri(strBlockListUrl))) { - updated = true; + blockListUrlsUpdated = true; break; } } @@ -2203,7 +2523,7 @@ namespace DnsServerCore } } - if (updated) + if (blockListUrlsUpdated) { _dnsServer.BlockListZoneManager.AllowListUrls.Clear(); _dnsServer.BlockListZoneManager.BlockListUrls.Clear(); @@ -2225,8 +2545,6 @@ namespace DnsServerCore _dnsServer.BlockListZoneManager.BlockListUrls.Add(blockListUrl); } } - - ForceUpdateBlockLists(); } } } @@ -2236,12 +2554,24 @@ namespace DnsServerCore { int blockListUpdateIntervalHours = int.Parse(strBlockListUpdateIntervalHours); - if ((blockListUpdateIntervalHours < 1) || (blockListUpdateIntervalHours > 168)) - throw new DnsWebServiceException("Parameter `blockListUpdateIntervalHours` must be between 1 hour and 168 hours (7 days)."); + if ((blockListUpdateIntervalHours < 0) || (blockListUpdateIntervalHours > 168)) + throw new DnsWebServiceException("Parameter `blockListUpdateIntervalHours` must be between 1 hour and 168 hours (7 days) or 0 to disable automatic update."); _blockListUpdateIntervalHours = blockListUpdateIntervalHours; } + if ((_blockListUpdateIntervalHours > 0) && ((_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count) > 0)) + { + if (blockListUrlsUpdated || (_blockListUpdateTimer is null)) + ForceUpdateBlockLists(); + + StartBlockListUpdateTimer(); + } + else + { + StopBlockListUpdateTimer(); + } + if ((_webServiceTlsCertificatePath == null) && (_dnsTlsCertificatePath == null)) StopTlsCertificateUpdateTimer(); @@ -2257,7 +2587,7 @@ namespace DnsServerCore SaveConfigFile(); _log.Save(); - _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] DNS Settings were updated {dnsServerDomain: " + _dnsServer.ServerDomain + "; dnsServerLocalEndPoints: " + strDnsServerLocalEndPoints + "; webServiceLocalAddresses: " + strWebServiceLocalAddresses + "; webServiceHttpPort: " + _webServiceHttpPort + "; webServiceEnableTls: " + strWebServiceEnableTls + "; webServiceHttpToTlsRedirect: " + strWebServiceHttpToTlsRedirect + "; webServiceTlsPort: " + strWebServiceTlsPort + "; webServiceUseSelfSignedTlsCertificate: " + _webServiceUseSelfSignedTlsCertificate + "; webServiceTlsCertificatePath: " + strWebServiceTlsCertificatePath + "; enableDnsOverHttp: " + _dnsServer.EnableDnsOverHttp + "; enableDnsOverTls: " + _dnsServer.EnableDnsOverTls + "; enableDnsOverHttps: " + _dnsServer.EnableDnsOverHttps + "; dnsTlsCertificatePath: " + _dnsTlsCertificatePath + "; defaultRecordTtl: " + _zonesApi.DefaultRecordTtl + "; preferIPv6: " + _dnsServer.PreferIPv6 + "; enableLogging: " + strEnableLogging + "; logQueries: " + (_dnsServer.QueryLogManager != null) + "; useLocalTime: " + strUseLocalTime + "; logFolder: " + strLogFolder + "; maxLogFileDays: " + strMaxLogFileDays + "; recursion: " + _dnsServer.Recursion.ToString() + "; randomizeName: " + strRandomizeName + "; qnameMinimization: " + strQnameMinimization + "; serveStale: " + strServeStale + "; serveStaleTtl: " + strServeStaleTtl + "; cachePrefetchEligibility: " + strCachePrefetchEligibility + "; cachePrefetchTrigger: " + strCachePrefetchTrigger + "; cachePrefetchSampleIntervalInMinutes: " + strCachePrefetchSampleIntervalInMinutes + "; cachePrefetchSampleEligibilityHitsPerHour: " + strCachePrefetchSampleEligibilityHitsPerHour + "; proxyType: " + strProxyType + "; forwarders: " + strForwarders + "; forwarderProtocol: " + strForwarderProtocol + "; enableBlocking: " + _dnsServer.EnableBlocking + "; allowTxtBlockingReport: " + _dnsServer.AllowTxtBlockingReport + "; blockingType: " + _dnsServer.BlockingType.ToString() + "; blockListUrl: " + strBlockListUrls + "; blockListUpdateIntervalHours: " + strBlockListUpdateIntervalHours + ";}"); + _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).User.Username + "] DNS Settings were updated {dnsServerDomain: " + _dnsServer.ServerDomain + "; dnsServerLocalEndPoints: " + strDnsServerLocalEndPoints + "; webServiceLocalAddresses: " + strWebServiceLocalAddresses + "; webServiceHttpPort: " + _webServiceHttpPort + "; webServiceEnableTls: " + strWebServiceEnableTls + "; webServiceHttpToTlsRedirect: " + strWebServiceHttpToTlsRedirect + "; webServiceTlsPort: " + strWebServiceTlsPort + "; webServiceUseSelfSignedTlsCertificate: " + _webServiceUseSelfSignedTlsCertificate + "; webServiceTlsCertificatePath: " + strWebServiceTlsCertificatePath + "; enableDnsOverHttp: " + _dnsServer.EnableDnsOverHttp + "; enableDnsOverTls: " + _dnsServer.EnableDnsOverTls + "; enableDnsOverHttps: " + _dnsServer.EnableDnsOverHttps + "; dnsTlsCertificatePath: " + _dnsTlsCertificatePath + "; defaultRecordTtl: " + _zonesApi.DefaultRecordTtl + "; preferIPv6: " + _dnsServer.PreferIPv6 + "; enableLogging: " + strEnableLogging + "; logQueries: " + (_dnsServer.QueryLogManager != null) + "; useLocalTime: " + strUseLocalTime + "; logFolder: " + strLogFolder + "; maxLogFileDays: " + strMaxLogFileDays + "; recursion: " + _dnsServer.Recursion.ToString() + "; randomizeName: " + strRandomizeName + "; qnameMinimization: " + strQnameMinimization + "; serveStale: " + strServeStale + "; serveStaleTtl: " + strServeStaleTtl + "; cachePrefetchEligibility: " + strCachePrefetchEligibility + "; cachePrefetchTrigger: " + strCachePrefetchTrigger + "; cachePrefetchSampleIntervalInMinutes: " + strCachePrefetchSampleIntervalInMinutes + "; cachePrefetchSampleEligibilityHitsPerHour: " + strCachePrefetchSampleEligibilityHitsPerHour + "; proxyType: " + strProxyType + "; forwarders: " + strForwarders + "; forwarderProtocol: " + strForwarderProtocol + "; enableBlocking: " + _dnsServer.EnableBlocking + "; allowTxtBlockingReport: " + _dnsServer.AllowTxtBlockingReport + "; blockingType: " + _dnsServer.BlockingType.ToString() + "; blockListUrl: " + strBlockListUrls + "; blockListUpdateIntervalHours: " + strBlockListUpdateIntervalHours + ";}"); GetDnsSettings(jsonWriter); @@ -2358,6 +2688,7 @@ namespace DnsServerCore bool allowedZones = false; bool blockedZones = false; bool dnsSettings = false; + bool authConfig = false; bool logSettings = false; string strBlockLists = request.QueryString["blockLists"]; @@ -2396,6 +2727,10 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(strDnsSettings)) dnsSettings = bool.Parse(strDnsSettings); + string strAuthConfig = request.QueryString["authConfig"]; + if (!string.IsNullOrEmpty(strAuthConfig)) + authConfig = bool.Parse(strAuthConfig); + string strLogSettings = request.QueryString["logSettings"]; if (!string.IsNullOrEmpty(strLogSettings)) logSettings = bool.Parse(strLogSettings); @@ -2427,15 +2762,7 @@ namespace DnsServerCore if (logFile.Equals(_log.CurrentLogFile, StringComparison.OrdinalIgnoreCase)) { - using (FileStream fS = new FileStream(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - ZipArchiveEntry entry = backupZip.CreateEntry(entryName); - - using (Stream s = entry.Open()) - { - await fS.CopyToAsync(s); - } - } + await CreateBackupEntryFromFileAsync(backupZip, logFile, entryName); } else { @@ -2466,7 +2793,7 @@ namespace DnsServerCore entryName = entryName.TrimStart('/'); - backupZip.CreateEntryFromFile(appFile, entryName); + await CreateBackupEntryFromFileAsync(backupZip, appFile, entryName); } } @@ -2521,6 +2848,14 @@ namespace DnsServerCore backupZip.CreateEntryFromFile(dnsSettingsFile, "dns.config"); } + if (authConfig) + { + string authSettingsFile = Path.Combine(_configFolder, "auth.config"); + + if (File.Exists(authSettingsFile)) + backupZip.CreateEntryFromFile(authSettingsFile, "auth.config"); + } + if (logSettings) { string logSettingsFile = Path.Combine(_configFolder, "log.config"); @@ -2555,7 +2890,29 @@ namespace DnsServerCore } } - _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Settings backup zip file was exported."); + _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).User.Username + "] Settings backup zip file was exported."); + } + + private static async Task CreateBackupEntryFromFileAsync(ZipArchive backupZip, string sourceFileName, string entryName) + { + using (FileStream fS = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + ZipArchiveEntry entry = backupZip.CreateEntry(entryName); + + DateTime lastWrite = File.GetLastWriteTime(sourceFileName); + + // If file to be archived has an invalid last modified time, use the first datetime representable in the Zip timestamp format + // (midnight on January 1, 1980): + if (lastWrite.Year < 1980 || lastWrite.Year > 2107) + lastWrite = new DateTime(1980, 1, 1, 0, 0, 0); + + entry.LastWriteTime = lastWrite; + + using (Stream sE = entry.Open()) + { + await fS.CopyToAsync(sE); + } + } } private async Task RestoreSettingsAsync(HttpListenerRequest request, JsonTextWriter jsonWriter) @@ -2569,6 +2926,7 @@ namespace DnsServerCore bool allowedZones = false; bool blockedZones = false; bool dnsSettings = false; + bool authConfig = false; bool logSettings = false; bool deleteExistingFiles = false; @@ -2609,6 +2967,10 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(strDnsSettings)) dnsSettings = bool.Parse(strDnsSettings); + string strAuthConfig = request.QueryString["authConfig"]; + if (!string.IsNullOrEmpty(strAuthConfig)) + authConfig = bool.Parse(strAuthConfig); + string strLogSettings = request.QueryString["logSettings"]; if (!string.IsNullOrEmpty(strLogSettings)) logSettings = bool.Parse(strLogSettings); @@ -2651,6 +3013,8 @@ namespace DnsServerCore fS.Position = 0; using (ZipArchive backupZip = new ZipArchive(fS, ZipArchiveMode.Read, false, Encoding.UTF8)) { + UserSession session = GetSession(request); + if (logSettings || logs) { //stop logging @@ -2699,6 +3063,27 @@ namespace DnsServerCore } } + if (authConfig) + { + ZipArchiveEntry entry = backupZip.GetEntry("auth.config"); + if (entry != null) + entry.ExtractToFile(Path.Combine(_configFolder, entry.Name), true); + + //reload auth config + _authManager.LoadConfigFile(session); + } + + if (dnsSettings) + { + ZipArchiveEntry entry = backupZip.GetEntry("dns.config"); + if (entry != null) + entry.ExtractToFile(Path.Combine(_configFolder, entry.Name), true); + + //reload settings and block list zone + LoadConfigFile(); + _dnsServer.BlockListZoneManager.LoadBlockLists(); + } + if (blockLists) { if (deleteExistingFiles) @@ -2719,37 +3104,6 @@ namespace DnsServerCore } } - if (scopes) - { - //stop dhcp server - _dhcpServer.Stop(); - - try - { - if (deleteExistingFiles) - { - //delete existing scope files - string[] scopeFiles = Directory.GetFiles(Path.Combine(_configFolder, "scopes"), "*.scope", SearchOption.TopDirectoryOnly); - foreach (string scopeFile in scopeFiles) - { - File.Delete(scopeFile); - } - } - - //extract scope files from backup - foreach (ZipArchiveEntry entry in backupZip.Entries) - { - if (entry.FullName.StartsWith("scopes/")) - entry.ExtractToFile(Path.Combine(_configFolder, "scopes", entry.Name), true); - } - } - finally - { - //start dhcp server - _dhcpServer.Start(); - } - } - if (apps) { //unload apps @@ -2788,35 +3142,6 @@ namespace DnsServerCore _dnsServer.DnsApplicationManager.LoadAllApplications(); } - if (stats) - { - if (deleteExistingFiles) - { - //delete existing stats files - string[] hourlyStatsFiles = Directory.GetFiles(Path.Combine(_configFolder, "stats"), "*.stat", SearchOption.TopDirectoryOnly); - foreach (string hourlyStatsFile in hourlyStatsFiles) - { - File.Delete(hourlyStatsFile); - } - - string[] dailyStatsFiles = Directory.GetFiles(Path.Combine(_configFolder, "stats"), "*.dstat", SearchOption.TopDirectoryOnly); - foreach (string dailyStatsFile in dailyStatsFiles) - { - File.Delete(dailyStatsFile); - } - } - - //extract stats files from backup - foreach (ZipArchiveEntry entry in backupZip.Entries) - { - if (entry.FullName.StartsWith("stats/")) - entry.ExtractToFile(Path.Combine(_configFolder, "stats", entry.Name), true); - } - - //reload stats - _dnsServer.StatsManager.ReloadStats(); - } - if (zones) { if (deleteExistingFiles) @@ -2838,6 +3163,7 @@ namespace DnsServerCore //reload zones _dnsServer.AuthZoneManager.LoadAllZoneFiles(); + InspectAndFixZonePermissions(); } if (allowedZones) @@ -2876,18 +3202,67 @@ namespace DnsServerCore _dnsServer.BlockedZoneManager.LoadBlockedZoneFile(); } - if (dnsSettings) + if (scopes) { - ZipArchiveEntry entry = backupZip.GetEntry("dns.config"); - if (entry != null) - entry.ExtractToFile(Path.Combine(_configFolder, entry.Name), true); + //stop dhcp server + _dhcpServer.Stop(); - //reload settings and block list zone - LoadConfigFile(); - _dnsServer.BlockListZoneManager.LoadBlockLists(); + try + { + if (deleteExistingFiles) + { + //delete existing scope files + string[] scopeFiles = Directory.GetFiles(Path.Combine(_configFolder, "scopes"), "*.scope", SearchOption.TopDirectoryOnly); + foreach (string scopeFile in scopeFiles) + { + File.Delete(scopeFile); + } + } + + //extract scope files from backup + foreach (ZipArchiveEntry entry in backupZip.Entries) + { + if (entry.FullName.StartsWith("scopes/")) + entry.ExtractToFile(Path.Combine(_configFolder, "scopes", entry.Name), true); + } + } + finally + { + //start dhcp server + _dhcpServer.Start(); + } } - _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Settings backup zip file was restored."); + if (stats) + { + if (deleteExistingFiles) + { + //delete existing stats files + string[] hourlyStatsFiles = Directory.GetFiles(Path.Combine(_configFolder, "stats"), "*.stat", SearchOption.TopDirectoryOnly); + foreach (string hourlyStatsFile in hourlyStatsFiles) + { + File.Delete(hourlyStatsFile); + } + + string[] dailyStatsFiles = Directory.GetFiles(Path.Combine(_configFolder, "stats"), "*.dstat", SearchOption.TopDirectoryOnly); + foreach (string dailyStatsFile in dailyStatsFiles) + { + File.Delete(dailyStatsFile); + } + } + + //extract stats files from backup + foreach (ZipArchiveEntry entry in backupZip.Entries) + { + if (entry.FullName.StartsWith("stats/")) + entry.ExtractToFile(Path.Combine(_configFolder, "stats", entry.Name), true); + } + + //reload stats + _dnsServer.StatsManager.ReloadStats(); + } + + _log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] Settings backup zip file was restored."); } } } @@ -2911,8 +3286,8 @@ namespace DnsServerCore private void ForceUpdateBlockLists(HttpListenerRequest request) { - if (ForceUpdateBlockLists()) - _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Block list update was triggered."); + ForceUpdateBlockLists(); + _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).User.Username + "] Block list update was triggered."); } private void TemporaryDisableBlocking(HttpListenerRequest request, JsonTextWriter jsonWriter) @@ -2932,7 +3307,7 @@ namespace DnsServerCore try { _dnsServer.EnableBlocking = true; - _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Blocking was enabled after " + minutes + " minute(s) being temporarily disabled."); + _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).User.Username + "] Blocking was enabled after " + minutes + " minute(s) being temporarily disabled."); } catch (Exception ex) { @@ -2947,7 +3322,7 @@ namespace DnsServerCore _dnsServer.EnableBlocking = false; _temporaryDisableBlockingTill = DateTime.UtcNow.AddMinutes(minutes); - _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Blocking was temporarily disabled for " + minutes + " minute(s)."); + _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).User.Username + "] Blocking was temporarily disabled for " + minutes + " minute(s)."); } else { @@ -3123,15 +3498,29 @@ namespace DnsServerCore if (importResponse) { + UserSession session = GetSession(request); + AuthZoneInfo zoneInfo = _dnsServer.AuthZoneManager.FindAuthZoneInfo(domain); if ((zoneInfo is null) || ((zoneInfo.Type == AuthZoneType.Secondary) && !zoneInfo.Name.Equals(domain, StringComparison.OrdinalIgnoreCase))) { + if (!_authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + zoneInfo = _dnsServer.AuthZoneManager.CreatePrimaryZone(domain, _dnsServer.ServerDomain, false); if (zoneInfo is null) throw new DnsServerException("Cannot import records: failed to create primary zone."); + + //set permissions + _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SaveConfigFile(); } else { + if (!_authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify)) + throw new DnsWebServiceException("Access was denied."); + switch (zoneInfo.Type) { case AuthZoneType.Primary: @@ -3183,7 +3572,7 @@ namespace DnsServerCore _dnsServer.AuthZoneManager.ImportRecords(zoneInfo.Name, importRecords); } - _log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] DNS Client imported record(s) for authoritative zone {server: " + server + "; zone: " + zoneInfo.Name + "; type: " + type + ";}"); + _log.Write(GetRequestRemoteEndPoint(request), "[" + session.User.Username + "] DNS Client imported record(s) for authoritative zone {server: " + server + "; zone: " + zoneInfo.Name + "; type: " + type + ";}"); _dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); } @@ -3200,53 +3589,25 @@ namespace DnsServerCore #endregion - #region auth - - private void SetCredentials(string username, string password) - { - username = username.ToLower(); - string passwordHash = GetPasswordHash(username, password); - - _credentials[username] = passwordHash; - } - - private void LoadCredentials(string username, string passwordHash) - { - username = username.ToLower(); - - _credentials[username] = passwordHash; - } - - private static string GetPasswordHash(string username, string password) - { - using (HMAC hmac = new HMACSHA256(Encoding.UTF8.GetBytes(password))) - { - return Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(username))).ToLower(); - } - } - - #endregion - #region block list - private bool ForceUpdateBlockLists() + private void ForceUpdateBlockLists() { - if ((_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count) > 0) + Task.Run(async delegate () { - _blockListLastUpdatedOn = new DateTime(); - - StopBlockListUpdateTimer(); - StartBlockListUpdateTimer(); - - return true; - } - - return false; + if (await _dnsServer.BlockListZoneManager.UpdateBlockListsAsync()) + { + //block lists were updated + //save last updated on time + _blockListLastUpdatedOn = DateTime.UtcNow; + SaveConfigFile(); + } + }); } private void StartBlockListUpdateTimer() { - if (_blockListUpdateTimer == null) + if (_blockListUpdateTimer is null) { _blockListUpdateTimer = new Timer(async delegate (object state) { @@ -3274,7 +3635,7 @@ namespace DnsServerCore private void StopBlockListUpdateTimer() { - if (_blockListUpdateTimer != null) + if (_blockListUpdateTimer is not null) { _blockListUpdateTimer.Dispose(); _blockListUpdateTimer = null; @@ -3380,613 +3741,16 @@ namespace DnsServerCore try { - bool passwordResetOption = false; - - if (!File.Exists(configFile)) - { - string passwordResetConfigFile = Path.Combine(_configFolder, "reset.config"); - - if (File.Exists(passwordResetConfigFile)) - { - passwordResetOption = true; - configFile = passwordResetConfigFile; - } - } - - byte version; + int version; using (FileStream fS = new FileStream(configFile, FileMode.Open, FileAccess.Read)) { - BinaryReader bR = new BinaryReader(fS); - - if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "DS") //format - throw new InvalidDataException("DnsServer config file format is invalid."); - - version = bR.ReadByte(); - switch (version) - { - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - case 25: - case 26: - case 27: - _dnsServer.ServerDomain = bR.ReadShortString(); - _webServiceHttpPort = bR.ReadInt32(); - - if (version >= 13) - { - { - int count = bR.ReadByte(); - if (count > 0) - { - IPAddress[] localAddresses = new IPAddress[count]; - - for (int i = 0; i < count; i++) - localAddresses[i] = IPAddressExtension.Parse(bR); - - _webServiceLocalAddresses = localAddresses; - } - } - - _webServiceTlsPort = bR.ReadInt32(); - _webServiceEnableTls = bR.ReadBoolean(); - _webServiceHttpToTlsRedirect = bR.ReadBoolean(); - _webServiceTlsCertificatePath = bR.ReadShortString(); - _webServiceTlsCertificatePassword = bR.ReadShortString(); - - if (_webServiceTlsCertificatePath.Length == 0) - _webServiceTlsCertificatePath = null; - - if (_webServiceTlsCertificatePath != null) - { - try - { - LoadWebServiceTlsCertificate(_webServiceTlsCertificatePath, _webServiceTlsCertificatePassword); - } - catch (Exception ex) - { - _log.Write("DNS Server encountered an error while loading Web Service TLS certificate: " + _webServiceTlsCertificatePath + "\r\n" + ex.ToString()); - } - - StartTlsCertificateUpdateTimer(); - } - } - else - { - _webServiceLocalAddresses = new IPAddress[] { IPAddress.Any, IPAddress.IPv6Any }; - - _webServiceTlsPort = 53443; - _webServiceEnableTls = false; - _webServiceHttpToTlsRedirect = false; - _webServiceTlsCertificatePath = string.Empty; - _webServiceTlsCertificatePassword = string.Empty; - } - - _dnsServer.PreferIPv6 = bR.ReadBoolean(); - - if (bR.ReadBoolean()) //logQueries - _dnsServer.QueryLogManager = _log; - - if (version >= 14) - _dnsServer.StatsManager.MaxStatFileDays = bR.ReadInt32(); - else - _dnsServer.StatsManager.MaxStatFileDays = 0; - - if (version >= 17) - { - _dnsServer.Recursion = (DnsServerRecursion)bR.ReadByte(); - - { - int count = bR.ReadByte(); - if (count > 0) - { - NetworkAddress[] networks = new NetworkAddress[count]; - - for (int i = 0; i < count; i++) - networks[i] = NetworkAddress.Parse(bR); - - _dnsServer.RecursionDeniedNetworks = networks; - } - } - - - { - int count = bR.ReadByte(); - if (count > 0) - { - NetworkAddress[] networks = new NetworkAddress[count]; - - for (int i = 0; i < count; i++) - networks[i] = NetworkAddress.Parse(bR); - - _dnsServer.RecursionAllowedNetworks = networks; - } - } - } - else - { - bool allowRecursion = bR.ReadBoolean(); - bool allowRecursionOnlyForPrivateNetworks; - - if (version >= 4) - allowRecursionOnlyForPrivateNetworks = bR.ReadBoolean(); - else - allowRecursionOnlyForPrivateNetworks = true; //default true for security reasons - - if (allowRecursion) - { - if (allowRecursionOnlyForPrivateNetworks) - _dnsServer.Recursion = DnsServerRecursion.AllowOnlyForPrivateNetworks; - else - _dnsServer.Recursion = DnsServerRecursion.Allow; - } - else - { - _dnsServer.Recursion = DnsServerRecursion.Deny; - } - } - - if (version >= 12) - _dnsServer.RandomizeName = bR.ReadBoolean(); - else - _dnsServer.RandomizeName = true; //default true to enable security feature - - if (version >= 15) - _dnsServer.QnameMinimization = bR.ReadBoolean(); - else - _dnsServer.QnameMinimization = true; //default true to enable privacy feature - - if (version >= 20) - { - _dnsServer.QpmLimitRequests = bR.ReadInt32(); - _dnsServer.QpmLimitErrors = bR.ReadInt32(); - _dnsServer.QpmLimitSampleMinutes = bR.ReadInt32(); - _dnsServer.QpmLimitIPv4PrefixLength = bR.ReadInt32(); - _dnsServer.QpmLimitIPv6PrefixLength = bR.ReadInt32(); - } - else if (version >= 17) - { - _dnsServer.QpmLimitRequests = bR.ReadInt32(); - _dnsServer.QpmLimitSampleMinutes = bR.ReadInt32(); - _ = bR.ReadInt32(); //read obsolete value _dnsServer.QpmLimitSamplingIntervalInMinutes - } - else - { - _dnsServer.QpmLimitRequests = 0; - _dnsServer.QpmLimitErrors = 0; - _dnsServer.QpmLimitSampleMinutes = 1; - _dnsServer.QpmLimitIPv4PrefixLength = 24; - _dnsServer.QpmLimitIPv6PrefixLength = 56; - } - - if (version >= 13) - { - _dnsServer.ServeStale = bR.ReadBoolean(); - _dnsServer.CacheZoneManager.ServeStaleTtl = bR.ReadUInt32(); - } - else - { - _dnsServer.ServeStale = true; - _dnsServer.CacheZoneManager.ServeStaleTtl = CacheZoneManager.SERVE_STALE_TTL; - } - - if (version >= 9) - { - _dnsServer.CachePrefetchEligibility = bR.ReadInt32(); - _dnsServer.CachePrefetchTrigger = bR.ReadInt32(); - _dnsServer.CachePrefetchSampleIntervalInMinutes = bR.ReadInt32(); - _dnsServer.CachePrefetchSampleEligibilityHitsPerHour = bR.ReadInt32(); - } - else - { - _dnsServer.CachePrefetchEligibility = 2; - _dnsServer.CachePrefetchTrigger = 9; - _dnsServer.CachePrefetchSampleIntervalInMinutes = 5; - _dnsServer.CachePrefetchSampleEligibilityHitsPerHour = 30; - } - - NetProxyType proxyType = (NetProxyType)bR.ReadByte(); - if (proxyType != NetProxyType.None) - { - string address = bR.ReadShortString(); - int port = bR.ReadInt32(); - NetworkCredential credential = null; - - if (bR.ReadBoolean()) //credential set - credential = new NetworkCredential(bR.ReadShortString(), bR.ReadShortString()); - - _dnsServer.Proxy = NetProxy.CreateProxy(proxyType, address, port, credential); - - if (version >= 10) - { - int count = bR.ReadByte(); - List bypassList = new List(count); - - for (int i = 0; i < count; i++) - bypassList.Add(new NetProxyBypassItem(bR.ReadShortString())); - - _dnsServer.Proxy.BypassList = bypassList; - } - else - { - _dnsServer.Proxy.BypassList = null; - } - } - else - { - _dnsServer.Proxy = null; - } - - { - int count = bR.ReadByte(); - if (count > 0) - { - NameServerAddress[] forwarders = new NameServerAddress[count]; - - for (int i = 0; i < count; i++) - forwarders[i] = new NameServerAddress(bR); - - _dnsServer.Forwarders = forwarders; - } - } - - if (version <= 10) - { - DnsTransportProtocol forwarderProtocol = (DnsTransportProtocol)bR.ReadByte(); - - if (_dnsServer.Forwarders != null) - { - List forwarders = new List(); - - foreach (NameServerAddress forwarder in _dnsServer.Forwarders) - { - if (forwarder.Protocol == forwarderProtocol) - forwarders.Add(forwarder); - else - forwarders.Add(forwarder.ChangeProtocol(forwarderProtocol)); - } - - _dnsServer.Forwarders = forwarders; - } - } - - { - int count = bR.ReadByte(); - if (count > 0) - { - if (version > 2) - { - for (int i = 0; i < count; i++) - LoadCredentials(bR.ReadShortString(), bR.ReadShortString()); - } - else - { - for (int i = 0; i < count; i++) - SetCredentials(bR.ReadShortString(), bR.ReadShortString()); - } - } - } - - if (version <= 6) - { - int count = bR.ReadInt32(); - _configDisabledZones = new List(count); - - for (int i = 0; i < count; i++) - { - string domain = bR.ReadShortString(); - _configDisabledZones.Add(domain); - } - } - - if (version >= 18) - _dnsServer.EnableBlocking = bR.ReadBoolean(); - else - _dnsServer.EnableBlocking = true; - - if (version >= 18) - _dnsServer.BlockingType = (DnsServerBlockingType)bR.ReadByte(); - else if (version >= 16) - _dnsServer.BlockingType = bR.ReadBoolean() ? DnsServerBlockingType.NxDomain : DnsServerBlockingType.AnyAddress; - else - _dnsServer.BlockingType = DnsServerBlockingType.AnyAddress; - - if (version >= 18) - { - //read custom blocking addresses - int count = bR.ReadByte(); - if (count > 0) - { - List dnsARecords = new List(); - List dnsAAAARecords = new List(); - - for (int i = 0; i < count; i++) - { - IPAddress customAddress = IPAddressExtension.Parse(bR); - - switch (customAddress.AddressFamily) - { - case AddressFamily.InterNetwork: - dnsARecords.Add(new DnsARecordData(customAddress)); - break; - - case AddressFamily.InterNetworkV6: - dnsAAAARecords.Add(new DnsAAAARecordData(customAddress)); - break; - } - } - - _dnsServer.CustomBlockingARecords = dnsARecords; - _dnsServer.CustomBlockingAAAARecords = dnsAAAARecords; - } - } - else - { - _dnsServer.CustomBlockingARecords = null; - _dnsServer.CustomBlockingAAAARecords = null; - } - - if (version > 4) - { - //read block list urls - int count = bR.ReadByte(); - - for (int i = 0; i < count; i++) - { - string listUrl = bR.ReadShortString(); - - if (listUrl.StartsWith("!")) - _dnsServer.BlockListZoneManager.AllowListUrls.Add(new Uri(listUrl.Substring(1))); - else - _dnsServer.BlockListZoneManager.BlockListUrls.Add(new Uri(listUrl)); - } - - _blockListLastUpdatedOn = bR.ReadDateTime(); - - if (version >= 13) - _blockListUpdateIntervalHours = bR.ReadInt32(); - } - else - { - _dnsServer.BlockListZoneManager.AllowListUrls.Clear(); - _dnsServer.BlockListZoneManager.BlockListUrls.Clear(); - _blockListLastUpdatedOn = DateTime.MinValue; - _blockListUpdateIntervalHours = 24; - } - - if (version >= 11) - { - int count = bR.ReadByte(); - if (count > 0) - { - IPEndPoint[] localEndPoints = new IPEndPoint[count]; - - for (int i = 0; i < count; i++) - localEndPoints[i] = (IPEndPoint)EndPointExtension.Parse(bR); - - _dnsServer.LocalEndPoints = localEndPoints; - } - } - else if (version >= 6) - { - int count = bR.ReadByte(); - if (count > 0) - { - IPEndPoint[] localEndPoints = new IPEndPoint[count]; - - for (int i = 0; i < count; i++) - localEndPoints[i] = new IPEndPoint(IPAddressExtension.Parse(bR), 53); - - _dnsServer.LocalEndPoints = localEndPoints; - } - } - else - { - _dnsServer.LocalEndPoints = new IPEndPoint[] { new IPEndPoint(IPAddress.Any, 53), new IPEndPoint(IPAddress.IPv6Any, 53) }; - } - - if (version >= 8) - { - _dnsServer.EnableDnsOverHttp = bR.ReadBoolean(); - _dnsServer.EnableDnsOverTls = bR.ReadBoolean(); - _dnsServer.EnableDnsOverHttps = bR.ReadBoolean(); - _dnsTlsCertificatePath = bR.ReadShortString(); - _dnsTlsCertificatePassword = bR.ReadShortString(); - - if (_dnsTlsCertificatePath.Length == 0) - _dnsTlsCertificatePath = null; - - if (_dnsTlsCertificatePath != null) - { - try - { - LoadDnsTlsCertificate(_dnsTlsCertificatePath, _dnsTlsCertificatePassword); - } - catch (Exception ex) - { - _log.Write("DNS Server encountered an error while loading DNS Server TLS certificate: " + _dnsTlsCertificatePath + "\r\n" + ex.ToString()); - } - - StartTlsCertificateUpdateTimer(); - } - } - else - { - _dnsServer.EnableDnsOverHttp = false; - _dnsServer.EnableDnsOverTls = false; - _dnsServer.EnableDnsOverHttps = false; - _dnsTlsCertificatePath = string.Empty; - _dnsTlsCertificatePassword = string.Empty; - } - - if (version >= 19) - { - _dnsServer.CacheZoneManager.MinimumRecordTtl = bR.ReadUInt32(); - _dnsServer.CacheZoneManager.MaximumRecordTtl = bR.ReadUInt32(); - _dnsServer.CacheZoneManager.NegativeRecordTtl = bR.ReadUInt32(); - _dnsServer.CacheZoneManager.FailureRecordTtl = bR.ReadUInt32(); - } - else - { - _dnsServer.CacheZoneManager.MinimumRecordTtl = CacheZoneManager.MINIMUM_RECORD_TTL; - _dnsServer.CacheZoneManager.MaximumRecordTtl = CacheZoneManager.MAXIMUM_RECORD_TTL; - _dnsServer.CacheZoneManager.NegativeRecordTtl = CacheZoneManager.NEGATIVE_RECORD_TTL; - _dnsServer.CacheZoneManager.FailureRecordTtl = CacheZoneManager.FAILURE_RECORD_TTL; - } - - if (version >= 21) - { - int count = bR.ReadByte(); - Dictionary tsigKeys = new Dictionary(count); - - for (int i = 0; i < count; i++) - { - string keyName = bR.ReadShortString(); - string sharedSecret = bR.ReadShortString(); - TsigAlgorithm algorithm = (TsigAlgorithm)bR.ReadByte(); - - tsigKeys.Add(keyName, new TsigKey(keyName, sharedSecret, algorithm)); - } - - _dnsServer.TsigKeys = tsigKeys; - } - else if (version >= 20) - { - int count = bR.ReadByte(); - Dictionary tsigKeys = new Dictionary(count); - - for (int i = 0; i < count; i++) - { - string keyName = bR.ReadShortString(); - string sharedSecret = bR.ReadShortString(); - - tsigKeys.Add(keyName, new TsigKey(keyName, sharedSecret, TsigAlgorithm.HMAC_SHA256)); - } - - _dnsServer.TsigKeys = tsigKeys; - } - else - { - _dnsServer.TsigKeys = null; - } - - if (version >= 22) - _dnsServer.NsRevalidation = bR.ReadBoolean(); - else - _dnsServer.NsRevalidation = true; //default true for security reasons - - if (version >= 23) - { - _dnsServer.AllowTxtBlockingReport = bR.ReadBoolean(); - _zonesApi.DefaultRecordTtl = bR.ReadUInt32(); - } - else - { - _dnsServer.AllowTxtBlockingReport = true; - _zonesApi.DefaultRecordTtl = 3600; - } - - if (version >= 24) - { - _webServiceUseSelfSignedTlsCertificate = bR.ReadBoolean(); - - SelfSignedCertCheck(false, false); - } - else - { - _webServiceUseSelfSignedTlsCertificate = false; - } - - if (version >= 25) - _dnsServer.UdpPayloadSize = bR.ReadUInt16(); - else - _dnsServer.UdpPayloadSize = DnsDatagram.EDNS_DEFAULT_UDP_PAYLOAD_SIZE; - - if (version >= 26) - { - _dnsServer.DnssecValidation = bR.ReadBoolean(); - - _dnsServer.ResolverRetries = bR.ReadInt32(); - _dnsServer.ResolverTimeout = bR.ReadInt32(); - _dnsServer.ResolverMaxStackCount = bR.ReadInt32(); - - _dnsServer.ForwarderRetries = bR.ReadInt32(); - _dnsServer.ForwarderTimeout = bR.ReadInt32(); - _dnsServer.ForwarderConcurrency = bR.ReadInt32(); - - _dnsServer.ClientTimeout = bR.ReadInt32(); - _dnsServer.TcpSendTimeout = bR.ReadInt32(); - _dnsServer.TcpReceiveTimeout = bR.ReadInt32(); - } - else - { - _dnsServer.DnssecValidation = true; - CreateForwarderZoneToDisableDnssecForNTP(); - - _dnsServer.ResolverRetries = 2; - _dnsServer.ResolverTimeout = 2000; - _dnsServer.ResolverMaxStackCount = 16; - - _dnsServer.ForwarderRetries = 3; - _dnsServer.ForwarderTimeout = 2000; - _dnsServer.ForwarderConcurrency = 2; - - _dnsServer.ClientTimeout = 4000; - _dnsServer.TcpSendTimeout = 10000; - _dnsServer.TcpReceiveTimeout = 10000; - } - - if (version >= 27) - _dnsServer.CacheZoneManager.MaximumEntries = bR.ReadInt32(); - else - _dnsServer.CacheZoneManager.MaximumEntries = 10000; - - break; - - default: - throw new InvalidDataException("DNS Server config version not supported."); - } + version = ReadConfigFrom(new BinaryReader(fS)); } _log.Write("DNS Server config file was loaded: " + configFile); - if (passwordResetOption) - { - SetCredentials("admin", "admin"); - - _log.Write("DNS Server reset password for user: admin"); - SaveConfigFile(); - - try - { - File.Delete(configFile); - } - catch - { } - } - - if (version <= 6) + if (version <= 27) SaveConfigFile(); //save as new config version to avoid loading old version next time } catch (FileNotFoundException) @@ -3994,47 +3758,26 @@ namespace DnsServerCore _log.Write("DNS Server config file was not found: " + configFile); _log.Write("DNS Server is restoring default config file."); + //general string serverDomain = Environment.GetEnvironmentVariable("DNS_SERVER_DOMAIN"); if (!string.IsNullOrEmpty(serverDomain)) _dnsServer.ServerDomain = serverDomain; - string adminPassword = Environment.GetEnvironmentVariable("DNS_SERVER_ADMIN_PASSWORD"); - string adminPasswordFile = Environment.GetEnvironmentVariable("DNS_SERVER_ADMIN_PASSWORD_FILE"); - - if (!string.IsNullOrEmpty(adminPassword)) - { - SetCredentials("admin", adminPassword); - } - else if (!string.IsNullOrEmpty(adminPasswordFile)) - { - try - { - using (StreamReader sR = new StreamReader(adminPasswordFile, true)) - { - string password = sR.ReadLine(); - SetCredentials("admin", password); - } - } - catch (Exception ex) - { - _log.Write(ex); - - SetCredentials("admin", "admin"); - } - } - else - { - SetCredentials("admin", "admin"); - } + _appsApi.EnableAutomaticUpdate = true; string strPreferIPv6 = Environment.GetEnvironmentVariable("DNS_SERVER_PREFER_IPV6"); if (!string.IsNullOrEmpty(strPreferIPv6)) _dnsServer.PreferIPv6 = bool.Parse(strPreferIPv6); + _dnsServer.DnssecValidation = true; + CreateForwarderZoneToDisableDnssecForNTP(); + + //optional protocols string strDnsOverHttp = Environment.GetEnvironmentVariable("DNS_SERVER_OPTIONAL_PROTOCOL_DNS_OVER_HTTP"); if (!string.IsNullOrEmpty(strDnsOverHttp)) _dnsServer.EnableDnsOverHttp = bool.Parse(strDnsOverHttp); + //recursion string strRecursion = Environment.GetEnvironmentVariable("DNS_SERVER_RECURSION"); if (!string.IsNullOrEmpty(strRecursion)) _dnsServer.Recursion = Enum.Parse(strRecursion, true); @@ -4065,6 +3808,14 @@ namespace DnsServerCore _dnsServer.RecursionAllowedNetworks = networks; } + _dnsServer.RandomizeName = true; //default true to enable security feature + _dnsServer.QnameMinimization = true; //default true to enable privacy feature + _dnsServer.NsRevalidation = true; //default true for security reasons + + //cache + _dnsServer.CacheZoneManager.MaximumEntries = 10000; + + //blocking string strEnableBlocking = Environment.GetEnvironmentVariable("DNS_SERVER_ENABLE_BLOCKING"); if (!string.IsNullOrEmpty(strEnableBlocking)) _dnsServer.EnableBlocking = bool.Parse(strEnableBlocking); @@ -4073,6 +3824,31 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(strAllowTxtBlockingReport)) _dnsServer.AllowTxtBlockingReport = bool.Parse(strAllowTxtBlockingReport); + string strBlockListUrls = Environment.GetEnvironmentVariable("DNS_SERVER_BLOCK_LIST_URLS"); + if (!string.IsNullOrEmpty(strBlockListUrls)) + { + string[] strBlockListUrlList = strBlockListUrls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (string strBlockListUrl in strBlockListUrlList) + { + if (strBlockListUrl.StartsWith("!")) + { + Uri allowListUrl = new Uri(strBlockListUrl.Substring(1)); + + if (!_dnsServer.BlockListZoneManager.AllowListUrls.Contains(allowListUrl)) + _dnsServer.BlockListZoneManager.AllowListUrls.Add(allowListUrl); + } + else + { + Uri blockListUrl = new Uri(strBlockListUrl); + + if (!_dnsServer.BlockListZoneManager.BlockListUrls.Contains(blockListUrl)) + _dnsServer.BlockListZoneManager.BlockListUrls.Add(blockListUrl); + } + } + } + + //proxy & forwarders string strForwarders = Environment.GetEnvironmentVariable("DNS_SERVER_FORWARDERS"); if (!string.IsNullOrEmpty(strForwarders)) { @@ -4093,19 +3869,11 @@ namespace DnsServerCore _dnsServer.Forwarders = forwarders; } + //logging string strUseLocalTime = Environment.GetEnvironmentVariable("DNS_SERVER_LOG_USING_LOCAL_TIME"); if (!string.IsNullOrEmpty(strUseLocalTime)) _log.UseLocalTime = bool.Parse(strUseLocalTime); - _dnsServer.RandomizeName = true; //default true to enable security feature - _dnsServer.QnameMinimization = true; //default true to enable privacy feature - _dnsServer.NsRevalidation = true; //default true for security reasons - - _dnsServer.DnssecValidation = true; - CreateForwarderZoneToDisableDnssecForNTP(); - - _dnsServer.CacheZoneManager.MaximumEntries = 10000; - SaveConfigFile(); } catch (Exception ex) @@ -4125,6 +3893,11 @@ namespace DnsServerCore string fwdRecordComments = "This forwarder zone was automatically created to disable DNSSEC validation for ntp.org to allow systems with no real-time clock (e.g. Raspberry Pi) to sync time via NTP when booting."; if (_dnsServer.AuthZoneManager.CreateForwarderZone(ntpDomain, DnsTransportProtocol.Udp, "this-server", false, NetProxyType.None, null, 0, null, null, fwdRecordComments) is not null) { + //set permissions + _authManager.SetPermission(PermissionSection.Zones, ntpDomain, _authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, ntpDomain, _authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); + _authManager.SaveConfigFile(); + Directory.CreateDirectory(Path.Combine(_dnsServer.ConfigFolder, "zones")); _dnsServer.AuthZoneManager.SaveZoneFile(ntpDomain); } @@ -4138,13 +3911,911 @@ namespace DnsServerCore using (MemoryStream mS = new MemoryStream()) { //serialize config - BinaryWriter bW = new BinaryWriter(mS); + WriteConfigTo(new BinaryWriter(mS)); - bW.Write(Encoding.ASCII.GetBytes("DS")); //format - bW.Write((byte)27); //version + //write config + mS.Position = 0; - bW.WriteShortString(_dnsServer.ServerDomain); + using (FileStream fS = new FileStream(configFile, FileMode.Create, FileAccess.Write)) + { + mS.CopyTo(fS); + } + } + + _log.Write("DNS Server config file was saved: " + configFile); + } + + private void InspectAndFixZonePermissions() + { + Permission permission = _authManager.GetPermission(PermissionSection.Zones); + IReadOnlyDictionary subItemPermissions = permission.SubItemPermissions; + + //remove ghost permissions + foreach (KeyValuePair subItemPermission in subItemPermissions) + { + string zoneName = subItemPermission.Key; + + if (_dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName) is null) + permission.RemoveAllSubItemPermissions(zoneName); //no such zone exists; remove permissions + } + + //add missing admin permissions + List zones = _dnsServer.AuthZoneManager.ListZones(); + Group admins = _authManager.GetGroup(Group.ADMINISTRATORS); + Group dnsAdmins = _authManager.GetGroup(Group.DNS_ADMINISTRATORS); + + foreach (AuthZoneInfo zone in zones) + { + if (zone.Internal) + { + _authManager.SetPermission(PermissionSection.Zones, zone.Name, admins, PermissionFlag.View); + _authManager.SetPermission(PermissionSection.Zones, zone.Name, dnsAdmins, PermissionFlag.View); + } + else + { + _authManager.SetPermission(PermissionSection.Zones, zone.Name, admins, PermissionFlag.ViewModifyDelete); + _authManager.SetPermission(PermissionSection.Zones, zone.Name, dnsAdmins, PermissionFlag.ViewModifyDelete); + } + } + + _authManager.SaveConfigFile(); + } + + private int ReadConfigFrom(BinaryReader bR) + { + if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "DS") //format + throw new InvalidDataException("DNS Server config file format is invalid."); + + int version = bR.ReadByte(); + + if (version == 28) + { + ReadConfigFrom(bR, version); + } + else if ((version >= 2) && (version <= 27)) + { + ReadOldConfigFrom(bR, version); + + //new default settings + _appsApi.EnableAutomaticUpdate = true; + } + else + { + throw new InvalidDataException("DNS Server config version not supported."); + } + + return version; + } + + private void ReadConfigFrom(BinaryReader bR, int version) + { + //web service + { + _webServiceHttpPort = bR.ReadInt32(); + _webServiceTlsPort = bR.ReadInt32(); + + { + int count = bR.ReadByte(); + if (count > 0) + { + IPAddress[] localAddresses = new IPAddress[count]; + + for (int i = 0; i < count; i++) + localAddresses[i] = IPAddressExtension.ReadFrom(bR); + + _webServiceLocalAddresses = localAddresses; + } + } + + _webServiceEnableTls = bR.ReadBoolean(); + _webServiceHttpToTlsRedirect = bR.ReadBoolean(); + _webServiceUseSelfSignedTlsCertificate = bR.ReadBoolean(); + + _webServiceTlsCertificatePath = bR.ReadShortString(); + _webServiceTlsCertificatePassword = bR.ReadShortString(); + } + + //dns + { + //general + _dnsServer.ServerDomain = bR.ReadShortString(); + + { + int count = bR.ReadByte(); + if (count > 0) + { + IPEndPoint[] localEndPoints = new IPEndPoint[count]; + + for (int i = 0; i < count; i++) + localEndPoints[i] = (IPEndPoint)EndPointExtension.ReadFrom(bR); + + _dnsServer.LocalEndPoints = localEndPoints; + } + } + + _zonesApi.DefaultRecordTtl = bR.ReadUInt32(); + _appsApi.EnableAutomaticUpdate = bR.ReadBoolean(); + + _dnsServer.PreferIPv6 = bR.ReadBoolean(); + _dnsServer.UdpPayloadSize = bR.ReadUInt16(); + _dnsServer.DnssecValidation = bR.ReadBoolean(); + + _dnsServer.QpmLimitRequests = bR.ReadInt32(); + _dnsServer.QpmLimitErrors = bR.ReadInt32(); + _dnsServer.QpmLimitSampleMinutes = bR.ReadInt32(); + _dnsServer.QpmLimitIPv4PrefixLength = bR.ReadInt32(); + _dnsServer.QpmLimitIPv6PrefixLength = bR.ReadInt32(); + + _dnsServer.ClientTimeout = bR.ReadInt32(); + _dnsServer.TcpSendTimeout = bR.ReadInt32(); + _dnsServer.TcpReceiveTimeout = bR.ReadInt32(); + + //optional protocols + _dnsServer.EnableDnsOverHttp = bR.ReadBoolean(); + _dnsServer.EnableDnsOverTls = bR.ReadBoolean(); + _dnsServer.EnableDnsOverHttps = bR.ReadBoolean(); + + _dnsTlsCertificatePath = bR.ReadShortString(); + _dnsTlsCertificatePassword = bR.ReadShortString(); + + if (_dnsTlsCertificatePath.Length == 0) + _dnsTlsCertificatePath = null; + + if (_dnsTlsCertificatePath != null) + { + try + { + LoadDnsTlsCertificate(_dnsTlsCertificatePath, _dnsTlsCertificatePassword); + } + catch (Exception ex) + { + _log.Write("DNS Server encountered an error while loading DNS Server TLS certificate: " + _dnsTlsCertificatePath + "\r\n" + ex.ToString()); + } + + StartTlsCertificateUpdateTimer(); + } + else + { + StopTlsCertificateUpdateTimer(); + } + + //tsig + { + int count = bR.ReadByte(); + Dictionary tsigKeys = new Dictionary(count); + + for (int i = 0; i < count; i++) + { + string keyName = bR.ReadShortString(); + string sharedSecret = bR.ReadShortString(); + TsigAlgorithm algorithm = (TsigAlgorithm)bR.ReadByte(); + + tsigKeys.Add(keyName, new TsigKey(keyName, sharedSecret, algorithm)); + } + + _dnsServer.TsigKeys = tsigKeys; + } + + //recursion + _dnsServer.Recursion = (DnsServerRecursion)bR.ReadByte(); + + { + int count = bR.ReadByte(); + if (count > 0) + { + NetworkAddress[] networks = new NetworkAddress[count]; + + for (int i = 0; i < count; i++) + networks[i] = NetworkAddress.ReadFrom(bR); + + _dnsServer.RecursionDeniedNetworks = networks; + } + } + + + { + int count = bR.ReadByte(); + if (count > 0) + { + NetworkAddress[] networks = new NetworkAddress[count]; + + for (int i = 0; i < count; i++) + networks[i] = NetworkAddress.ReadFrom(bR); + + _dnsServer.RecursionAllowedNetworks = networks; + } + } + + _dnsServer.RandomizeName = bR.ReadBoolean(); + _dnsServer.QnameMinimization = bR.ReadBoolean(); + _dnsServer.NsRevalidation = bR.ReadBoolean(); + + _dnsServer.ResolverRetries = bR.ReadInt32(); + _dnsServer.ResolverTimeout = bR.ReadInt32(); + _dnsServer.ResolverMaxStackCount = bR.ReadInt32(); + + //cache + _dnsServer.ServeStale = bR.ReadBoolean(); + _dnsServer.CacheZoneManager.ServeStaleTtl = bR.ReadUInt32(); + + _dnsServer.CacheZoneManager.MaximumEntries = bR.ReadInt64(); + _dnsServer.CacheZoneManager.MinimumRecordTtl = bR.ReadUInt32(); + _dnsServer.CacheZoneManager.MaximumRecordTtl = bR.ReadUInt32(); + _dnsServer.CacheZoneManager.NegativeRecordTtl = bR.ReadUInt32(); + _dnsServer.CacheZoneManager.FailureRecordTtl = bR.ReadUInt32(); + + _dnsServer.CachePrefetchEligibility = bR.ReadInt32(); + _dnsServer.CachePrefetchTrigger = bR.ReadInt32(); + _dnsServer.CachePrefetchSampleIntervalInMinutes = bR.ReadInt32(); + _dnsServer.CachePrefetchSampleEligibilityHitsPerHour = bR.ReadInt32(); + + //blocking + _dnsServer.EnableBlocking = bR.ReadBoolean(); + _dnsServer.AllowTxtBlockingReport = bR.ReadBoolean(); + + _dnsServer.BlockingType = (DnsServerBlockingType)bR.ReadByte(); + + { + //read custom blocking addresses + int count = bR.ReadByte(); + if (count > 0) + { + List dnsARecords = new List(); + List dnsAAAARecords = new List(); + + for (int i = 0; i < count; i++) + { + IPAddress customAddress = IPAddressExtension.ReadFrom(bR); + + switch (customAddress.AddressFamily) + { + case AddressFamily.InterNetwork: + dnsARecords.Add(new DnsARecordData(customAddress)); + break; + + case AddressFamily.InterNetworkV6: + dnsAAAARecords.Add(new DnsAAAARecordData(customAddress)); + break; + } + } + + _dnsServer.CustomBlockingARecords = dnsARecords; + _dnsServer.CustomBlockingAAAARecords = dnsAAAARecords; + } + } + + { + //read block list urls + int count = bR.ReadByte(); + + for (int i = 0; i < count; i++) + { + string listUrl = bR.ReadShortString(); + + if (listUrl.StartsWith("!")) + _dnsServer.BlockListZoneManager.AllowListUrls.Add(new Uri(listUrl.Substring(1))); + else + _dnsServer.BlockListZoneManager.BlockListUrls.Add(new Uri(listUrl)); + } + + _blockListUpdateIntervalHours = bR.ReadInt32(); + _blockListLastUpdatedOn = bR.ReadDateTime(); + } + + //proxy & forwarders + NetProxyType proxyType = (NetProxyType)bR.ReadByte(); + if (proxyType != NetProxyType.None) + { + string address = bR.ReadShortString(); + int port = bR.ReadInt32(); + NetworkCredential credential = null; + + if (bR.ReadBoolean()) //credential set + credential = new NetworkCredential(bR.ReadShortString(), bR.ReadShortString()); + + _dnsServer.Proxy = NetProxy.CreateProxy(proxyType, address, port, credential); + + int count = bR.ReadByte(); + List bypassList = new List(count); + + for (int i = 0; i < count; i++) + bypassList.Add(new NetProxyBypassItem(bR.ReadShortString())); + + _dnsServer.Proxy.BypassList = bypassList; + } + else + { + _dnsServer.Proxy = null; + } + + { + int count = bR.ReadByte(); + if (count > 0) + { + NameServerAddress[] forwarders = new NameServerAddress[count]; + + for (int i = 0; i < count; i++) + forwarders[i] = new NameServerAddress(bR); + + _dnsServer.Forwarders = forwarders; + } + } + + _dnsServer.ForwarderRetries = bR.ReadInt32(); + _dnsServer.ForwarderTimeout = bR.ReadInt32(); + _dnsServer.ForwarderConcurrency = bR.ReadInt32(); + + //logging + if (bR.ReadBoolean()) //log all queries + _dnsServer.QueryLogManager = _log; + else + _dnsServer.QueryLogManager = null; + + _dnsServer.StatsManager.MaxStatFileDays = bR.ReadInt32(); + } + } + + private void ReadOldConfigFrom(BinaryReader bR, int version) + { + _dnsServer.ServerDomain = bR.ReadShortString(); + _webServiceHttpPort = bR.ReadInt32(); + + if (version >= 13) + { + { + int count = bR.ReadByte(); + if (count > 0) + { + IPAddress[] localAddresses = new IPAddress[count]; + + for (int i = 0; i < count; i++) + localAddresses[i] = IPAddressExtension.ReadFrom(bR); + + _webServiceLocalAddresses = localAddresses; + } + } + + _webServiceTlsPort = bR.ReadInt32(); + _webServiceEnableTls = bR.ReadBoolean(); + _webServiceHttpToTlsRedirect = bR.ReadBoolean(); + _webServiceTlsCertificatePath = bR.ReadShortString(); + _webServiceTlsCertificatePassword = bR.ReadShortString(); + + if (_webServiceTlsCertificatePath.Length == 0) + _webServiceTlsCertificatePath = null; + + if (_webServiceTlsCertificatePath != null) + { + try + { + LoadWebServiceTlsCertificate(_webServiceTlsCertificatePath, _webServiceTlsCertificatePassword); + } + catch (Exception ex) + { + _log.Write("DNS Server encountered an error while loading Web Service TLS certificate: " + _webServiceTlsCertificatePath + "\r\n" + ex.ToString()); + } + + StartTlsCertificateUpdateTimer(); + } + } + else + { + _webServiceLocalAddresses = new IPAddress[] { IPAddress.Any, IPAddress.IPv6Any }; + + _webServiceTlsPort = 53443; + _webServiceEnableTls = false; + _webServiceHttpToTlsRedirect = false; + _webServiceTlsCertificatePath = string.Empty; + _webServiceTlsCertificatePassword = string.Empty; + } + + _dnsServer.PreferIPv6 = bR.ReadBoolean(); + + if (bR.ReadBoolean()) //logQueries + _dnsServer.QueryLogManager = _log; + + if (version >= 14) + _dnsServer.StatsManager.MaxStatFileDays = bR.ReadInt32(); + else + _dnsServer.StatsManager.MaxStatFileDays = 0; + + if (version >= 17) + { + _dnsServer.Recursion = (DnsServerRecursion)bR.ReadByte(); + + { + int count = bR.ReadByte(); + if (count > 0) + { + NetworkAddress[] networks = new NetworkAddress[count]; + + for (int i = 0; i < count; i++) + networks[i] = NetworkAddress.ReadFrom(bR); + + _dnsServer.RecursionDeniedNetworks = networks; + } + } + + + { + int count = bR.ReadByte(); + if (count > 0) + { + NetworkAddress[] networks = new NetworkAddress[count]; + + for (int i = 0; i < count; i++) + networks[i] = NetworkAddress.ReadFrom(bR); + + _dnsServer.RecursionAllowedNetworks = networks; + } + } + } + else + { + bool allowRecursion = bR.ReadBoolean(); + bool allowRecursionOnlyForPrivateNetworks; + + if (version >= 4) + allowRecursionOnlyForPrivateNetworks = bR.ReadBoolean(); + else + allowRecursionOnlyForPrivateNetworks = true; //default true for security reasons + + if (allowRecursion) + { + if (allowRecursionOnlyForPrivateNetworks) + _dnsServer.Recursion = DnsServerRecursion.AllowOnlyForPrivateNetworks; + else + _dnsServer.Recursion = DnsServerRecursion.Allow; + } + else + { + _dnsServer.Recursion = DnsServerRecursion.Deny; + } + } + + if (version >= 12) + _dnsServer.RandomizeName = bR.ReadBoolean(); + else + _dnsServer.RandomizeName = true; //default true to enable security feature + + if (version >= 15) + _dnsServer.QnameMinimization = bR.ReadBoolean(); + else + _dnsServer.QnameMinimization = true; //default true to enable privacy feature + + if (version >= 20) + { + _dnsServer.QpmLimitRequests = bR.ReadInt32(); + _dnsServer.QpmLimitErrors = bR.ReadInt32(); + _dnsServer.QpmLimitSampleMinutes = bR.ReadInt32(); + _dnsServer.QpmLimitIPv4PrefixLength = bR.ReadInt32(); + _dnsServer.QpmLimitIPv6PrefixLength = bR.ReadInt32(); + } + else if (version >= 17) + { + _dnsServer.QpmLimitRequests = bR.ReadInt32(); + _dnsServer.QpmLimitSampleMinutes = bR.ReadInt32(); + _ = bR.ReadInt32(); //read obsolete value _dnsServer.QpmLimitSamplingIntervalInMinutes + } + else + { + _dnsServer.QpmLimitRequests = 0; + _dnsServer.QpmLimitErrors = 0; + _dnsServer.QpmLimitSampleMinutes = 1; + _dnsServer.QpmLimitIPv4PrefixLength = 24; + _dnsServer.QpmLimitIPv6PrefixLength = 56; + } + + if (version >= 13) + { + _dnsServer.ServeStale = bR.ReadBoolean(); + _dnsServer.CacheZoneManager.ServeStaleTtl = bR.ReadUInt32(); + } + else + { + _dnsServer.ServeStale = true; + _dnsServer.CacheZoneManager.ServeStaleTtl = CacheZoneManager.SERVE_STALE_TTL; + } + + if (version >= 9) + { + _dnsServer.CachePrefetchEligibility = bR.ReadInt32(); + _dnsServer.CachePrefetchTrigger = bR.ReadInt32(); + _dnsServer.CachePrefetchSampleIntervalInMinutes = bR.ReadInt32(); + _dnsServer.CachePrefetchSampleEligibilityHitsPerHour = bR.ReadInt32(); + } + else + { + _dnsServer.CachePrefetchEligibility = 2; + _dnsServer.CachePrefetchTrigger = 9; + _dnsServer.CachePrefetchSampleIntervalInMinutes = 5; + _dnsServer.CachePrefetchSampleEligibilityHitsPerHour = 30; + } + + NetProxyType proxyType = (NetProxyType)bR.ReadByte(); + if (proxyType != NetProxyType.None) + { + string address = bR.ReadShortString(); + int port = bR.ReadInt32(); + NetworkCredential credential = null; + + if (bR.ReadBoolean()) //credential set + credential = new NetworkCredential(bR.ReadShortString(), bR.ReadShortString()); + + _dnsServer.Proxy = NetProxy.CreateProxy(proxyType, address, port, credential); + + if (version >= 10) + { + int count = bR.ReadByte(); + List bypassList = new List(count); + + for (int i = 0; i < count; i++) + bypassList.Add(new NetProxyBypassItem(bR.ReadShortString())); + + _dnsServer.Proxy.BypassList = bypassList; + } + else + { + _dnsServer.Proxy.BypassList = null; + } + } + else + { + _dnsServer.Proxy = null; + } + + { + int count = bR.ReadByte(); + if (count > 0) + { + NameServerAddress[] forwarders = new NameServerAddress[count]; + + for (int i = 0; i < count; i++) + forwarders[i] = new NameServerAddress(bR); + + _dnsServer.Forwarders = forwarders; + } + } + + if (version <= 10) + { + DnsTransportProtocol forwarderProtocol = (DnsTransportProtocol)bR.ReadByte(); + + if (_dnsServer.Forwarders != null) + { + List forwarders = new List(); + + foreach (NameServerAddress forwarder in _dnsServer.Forwarders) + { + if (forwarder.Protocol == forwarderProtocol) + forwarders.Add(forwarder); + else + forwarders.Add(forwarder.ChangeProtocol(forwarderProtocol)); + } + + _dnsServer.Forwarders = forwarders; + } + } + + { + int count = bR.ReadByte(); + if (count > 0) + { + if (version > 2) + { + for (int i = 0; i < count; i++) + { + string username = bR.ReadShortString(); + string passwordHash = bR.ReadShortString(); + + if (username.Equals("admin", StringComparison.OrdinalIgnoreCase)) + { + _authManager.LoadOldConfig(passwordHash, true); + break; + } + } + } + else + { + for (int i = 0; i < count; i++) + { + string username = bR.ReadShortString(); + string password = bR.ReadShortString(); + + if (username.Equals("admin", StringComparison.OrdinalIgnoreCase)) + { + _authManager.LoadOldConfig(password, false); + break; + } + } + } + } + } + + if (version <= 6) + { + int count = bR.ReadInt32(); + _configDisabledZones = new List(count); + + for (int i = 0; i < count; i++) + { + string domain = bR.ReadShortString(); + _configDisabledZones.Add(domain); + } + } + + if (version >= 18) + _dnsServer.EnableBlocking = bR.ReadBoolean(); + else + _dnsServer.EnableBlocking = true; + + if (version >= 18) + _dnsServer.BlockingType = (DnsServerBlockingType)bR.ReadByte(); + else if (version >= 16) + _dnsServer.BlockingType = bR.ReadBoolean() ? DnsServerBlockingType.NxDomain : DnsServerBlockingType.AnyAddress; + else + _dnsServer.BlockingType = DnsServerBlockingType.AnyAddress; + + if (version >= 18) + { + //read custom blocking addresses + int count = bR.ReadByte(); + if (count > 0) + { + List dnsARecords = new List(); + List dnsAAAARecords = new List(); + + for (int i = 0; i < count; i++) + { + IPAddress customAddress = IPAddressExtension.ReadFrom(bR); + + switch (customAddress.AddressFamily) + { + case AddressFamily.InterNetwork: + dnsARecords.Add(new DnsARecordData(customAddress)); + break; + + case AddressFamily.InterNetworkV6: + dnsAAAARecords.Add(new DnsAAAARecordData(customAddress)); + break; + } + } + + _dnsServer.CustomBlockingARecords = dnsARecords; + _dnsServer.CustomBlockingAAAARecords = dnsAAAARecords; + } + } + else + { + _dnsServer.CustomBlockingARecords = null; + _dnsServer.CustomBlockingAAAARecords = null; + } + + if (version > 4) + { + //read block list urls + int count = bR.ReadByte(); + + for (int i = 0; i < count; i++) + { + string listUrl = bR.ReadShortString(); + + if (listUrl.StartsWith("!")) + _dnsServer.BlockListZoneManager.AllowListUrls.Add(new Uri(listUrl.Substring(1))); + else + _dnsServer.BlockListZoneManager.BlockListUrls.Add(new Uri(listUrl)); + } + + _blockListLastUpdatedOn = bR.ReadDateTime(); + + if (version >= 13) + _blockListUpdateIntervalHours = bR.ReadInt32(); + } + else + { + _dnsServer.BlockListZoneManager.AllowListUrls.Clear(); + _dnsServer.BlockListZoneManager.BlockListUrls.Clear(); + _blockListLastUpdatedOn = DateTime.MinValue; + _blockListUpdateIntervalHours = 24; + } + + if (version >= 11) + { + int count = bR.ReadByte(); + if (count > 0) + { + IPEndPoint[] localEndPoints = new IPEndPoint[count]; + + for (int i = 0; i < count; i++) + localEndPoints[i] = (IPEndPoint)EndPointExtension.ReadFrom(bR); + + _dnsServer.LocalEndPoints = localEndPoints; + } + } + else if (version >= 6) + { + int count = bR.ReadByte(); + if (count > 0) + { + IPEndPoint[] localEndPoints = new IPEndPoint[count]; + + for (int i = 0; i < count; i++) + localEndPoints[i] = new IPEndPoint(IPAddressExtension.ReadFrom(bR), 53); + + _dnsServer.LocalEndPoints = localEndPoints; + } + } + else + { + _dnsServer.LocalEndPoints = new IPEndPoint[] { new IPEndPoint(IPAddress.Any, 53), new IPEndPoint(IPAddress.IPv6Any, 53) }; + } + + if (version >= 8) + { + _dnsServer.EnableDnsOverHttp = bR.ReadBoolean(); + _dnsServer.EnableDnsOverTls = bR.ReadBoolean(); + _dnsServer.EnableDnsOverHttps = bR.ReadBoolean(); + _dnsTlsCertificatePath = bR.ReadShortString(); + _dnsTlsCertificatePassword = bR.ReadShortString(); + + if (_dnsTlsCertificatePath.Length == 0) + _dnsTlsCertificatePath = null; + + if (_dnsTlsCertificatePath != null) + { + try + { + LoadDnsTlsCertificate(_dnsTlsCertificatePath, _dnsTlsCertificatePassword); + } + catch (Exception ex) + { + _log.Write("DNS Server encountered an error while loading DNS Server TLS certificate: " + _dnsTlsCertificatePath + "\r\n" + ex.ToString()); + } + + StartTlsCertificateUpdateTimer(); + } + } + else + { + _dnsServer.EnableDnsOverHttp = false; + _dnsServer.EnableDnsOverTls = false; + _dnsServer.EnableDnsOverHttps = false; + _dnsTlsCertificatePath = string.Empty; + _dnsTlsCertificatePassword = string.Empty; + } + + if (version >= 19) + { + _dnsServer.CacheZoneManager.MinimumRecordTtl = bR.ReadUInt32(); + _dnsServer.CacheZoneManager.MaximumRecordTtl = bR.ReadUInt32(); + _dnsServer.CacheZoneManager.NegativeRecordTtl = bR.ReadUInt32(); + _dnsServer.CacheZoneManager.FailureRecordTtl = bR.ReadUInt32(); + } + else + { + _dnsServer.CacheZoneManager.MinimumRecordTtl = CacheZoneManager.MINIMUM_RECORD_TTL; + _dnsServer.CacheZoneManager.MaximumRecordTtl = CacheZoneManager.MAXIMUM_RECORD_TTL; + _dnsServer.CacheZoneManager.NegativeRecordTtl = CacheZoneManager.NEGATIVE_RECORD_TTL; + _dnsServer.CacheZoneManager.FailureRecordTtl = CacheZoneManager.FAILURE_RECORD_TTL; + } + + if (version >= 21) + { + int count = bR.ReadByte(); + Dictionary tsigKeys = new Dictionary(count); + + for (int i = 0; i < count; i++) + { + string keyName = bR.ReadShortString(); + string sharedSecret = bR.ReadShortString(); + TsigAlgorithm algorithm = (TsigAlgorithm)bR.ReadByte(); + + tsigKeys.Add(keyName, new TsigKey(keyName, sharedSecret, algorithm)); + } + + _dnsServer.TsigKeys = tsigKeys; + } + else if (version >= 20) + { + int count = bR.ReadByte(); + Dictionary tsigKeys = new Dictionary(count); + + for (int i = 0; i < count; i++) + { + string keyName = bR.ReadShortString(); + string sharedSecret = bR.ReadShortString(); + + tsigKeys.Add(keyName, new TsigKey(keyName, sharedSecret, TsigAlgorithm.HMAC_SHA256)); + } + + _dnsServer.TsigKeys = tsigKeys; + } + else + { + _dnsServer.TsigKeys = null; + } + + if (version >= 22) + _dnsServer.NsRevalidation = bR.ReadBoolean(); + else + _dnsServer.NsRevalidation = true; //default true for security reasons + + if (version >= 23) + { + _dnsServer.AllowTxtBlockingReport = bR.ReadBoolean(); + _zonesApi.DefaultRecordTtl = bR.ReadUInt32(); + } + else + { + _dnsServer.AllowTxtBlockingReport = true; + _zonesApi.DefaultRecordTtl = 3600; + } + + if (version >= 24) + { + _webServiceUseSelfSignedTlsCertificate = bR.ReadBoolean(); + + SelfSignedCertCheck(false, false); + } + else + { + _webServiceUseSelfSignedTlsCertificate = false; + } + + if (version >= 25) + _dnsServer.UdpPayloadSize = bR.ReadUInt16(); + else + _dnsServer.UdpPayloadSize = DnsDatagram.EDNS_DEFAULT_UDP_PAYLOAD_SIZE; + + if (version >= 26) + { + _dnsServer.DnssecValidation = bR.ReadBoolean(); + + _dnsServer.ResolverRetries = bR.ReadInt32(); + _dnsServer.ResolverTimeout = bR.ReadInt32(); + _dnsServer.ResolverMaxStackCount = bR.ReadInt32(); + + _dnsServer.ForwarderRetries = bR.ReadInt32(); + _dnsServer.ForwarderTimeout = bR.ReadInt32(); + _dnsServer.ForwarderConcurrency = bR.ReadInt32(); + + _dnsServer.ClientTimeout = bR.ReadInt32(); + _dnsServer.TcpSendTimeout = bR.ReadInt32(); + _dnsServer.TcpReceiveTimeout = bR.ReadInt32(); + } + else + { + _dnsServer.DnssecValidation = true; + CreateForwarderZoneToDisableDnssecForNTP(); + + _dnsServer.ResolverRetries = 2; + _dnsServer.ResolverTimeout = 2000; + _dnsServer.ResolverMaxStackCount = 16; + + _dnsServer.ForwarderRetries = 3; + _dnsServer.ForwarderTimeout = 2000; + _dnsServer.ForwarderConcurrency = 2; + + _dnsServer.ClientTimeout = 4000; + _dnsServer.TcpSendTimeout = 10000; + _dnsServer.TcpReceiveTimeout = 10000; + } + + if (version >= 27) + _dnsServer.CacheZoneManager.MaximumEntries = bR.ReadInt32(); + else + _dnsServer.CacheZoneManager.MaximumEntries = 10000; + } + + private void WriteConfigTo(BinaryWriter bW) + { + bW.Write(Encoding.ASCII.GetBytes("DS")); //format + bW.Write((byte)28); //version + + //web service + { bW.Write(_webServiceHttpPort); + bW.Write(_webServiceTlsPort); { bW.Write(Convert.ToByte(_webServiceLocalAddresses.Count)); @@ -4153,25 +4824,83 @@ namespace DnsServerCore localAddress.WriteTo(bW); } - bW.Write(_webServiceTlsPort); bW.Write(_webServiceEnableTls); bW.Write(_webServiceHttpToTlsRedirect); + bW.Write(_webServiceUseSelfSignedTlsCertificate); - if (_webServiceTlsCertificatePath == null) + if (_webServiceTlsCertificatePath is null) bW.WriteShortString(string.Empty); else bW.WriteShortString(_webServiceTlsCertificatePath); - if (_webServiceTlsCertificatePassword == null) + if (_webServiceTlsCertificatePassword is null) bW.WriteShortString(string.Empty); else bW.WriteShortString(_webServiceTlsCertificatePassword); + } + + //dns + { + //general + bW.WriteShortString(_dnsServer.ServerDomain); + + { + bW.Write(Convert.ToByte(_dnsServer.LocalEndPoints.Count)); + + foreach (IPEndPoint localEP in _dnsServer.LocalEndPoints) + localEP.WriteTo(bW); + } + + bW.Write(_zonesApi.DefaultRecordTtl); + bW.Write(_appsApi.EnableAutomaticUpdate); bW.Write(_dnsServer.PreferIPv6); + bW.Write(_dnsServer.UdpPayloadSize); + bW.Write(_dnsServer.DnssecValidation); - bW.Write(_dnsServer.QueryLogManager != null); //logQueries - bW.Write(_dnsServer.StatsManager.MaxStatFileDays); + bW.Write(_dnsServer.QpmLimitRequests); + bW.Write(_dnsServer.QpmLimitErrors); + bW.Write(_dnsServer.QpmLimitSampleMinutes); + bW.Write(_dnsServer.QpmLimitIPv4PrefixLength); + bW.Write(_dnsServer.QpmLimitIPv6PrefixLength); + bW.Write(_dnsServer.ClientTimeout); + bW.Write(_dnsServer.TcpSendTimeout); + bW.Write(_dnsServer.TcpReceiveTimeout); + + //optional protocols + bW.Write(_dnsServer.EnableDnsOverHttp); + bW.Write(_dnsServer.EnableDnsOverTls); + bW.Write(_dnsServer.EnableDnsOverHttps); + + if (_dnsTlsCertificatePath == null) + bW.WriteShortString(string.Empty); + else + bW.WriteShortString(_dnsTlsCertificatePath); + + if (_dnsTlsCertificatePassword == null) + bW.WriteShortString(string.Empty); + else + bW.WriteShortString(_dnsTlsCertificatePassword); + + //tsig + if (_dnsServer.TsigKeys is null) + { + bW.Write((byte)0); + } + else + { + bW.Write(Convert.ToByte(_dnsServer.TsigKeys.Count)); + + foreach (KeyValuePair tsigKey in _dnsServer.TsigKeys) + { + bW.WriteShortString(tsigKey.Key); + bW.WriteShortString(tsigKey.Value.SharedSecret); + bW.Write((byte)tsigKey.Value.Algorithm); + } + } + + //recursion bW.Write((byte)_dnsServer.Recursion); if (_dnsServer.RecursionDeniedNetworks is null) @@ -4198,21 +4927,57 @@ namespace DnsServerCore bW.Write(_dnsServer.RandomizeName); bW.Write(_dnsServer.QnameMinimization); + bW.Write(_dnsServer.NsRevalidation); - bW.Write(_dnsServer.QpmLimitRequests); - bW.Write(_dnsServer.QpmLimitErrors); - bW.Write(_dnsServer.QpmLimitSampleMinutes); - bW.Write(_dnsServer.QpmLimitIPv4PrefixLength); - bW.Write(_dnsServer.QpmLimitIPv6PrefixLength); + bW.Write(_dnsServer.ResolverRetries); + bW.Write(_dnsServer.ResolverTimeout); + bW.Write(_dnsServer.ResolverMaxStackCount); + //cache bW.Write(_dnsServer.ServeStale); bW.Write(_dnsServer.CacheZoneManager.ServeStaleTtl); + bW.Write(_dnsServer.CacheZoneManager.MaximumEntries); + bW.Write(_dnsServer.CacheZoneManager.MinimumRecordTtl); + bW.Write(_dnsServer.CacheZoneManager.MaximumRecordTtl); + bW.Write(_dnsServer.CacheZoneManager.NegativeRecordTtl); + bW.Write(_dnsServer.CacheZoneManager.FailureRecordTtl); + bW.Write(_dnsServer.CachePrefetchEligibility); bW.Write(_dnsServer.CachePrefetchTrigger); bW.Write(_dnsServer.CachePrefetchSampleIntervalInMinutes); bW.Write(_dnsServer.CachePrefetchSampleEligibilityHitsPerHour); + //blocking + bW.Write(_dnsServer.EnableBlocking); + bW.Write(_dnsServer.AllowTxtBlockingReport); + + bW.Write((byte)_dnsServer.BlockingType); + + { + bW.Write(Convert.ToByte(_dnsServer.CustomBlockingARecords.Count + _dnsServer.CustomBlockingAAAARecords.Count)); + + foreach (DnsARecordData record in _dnsServer.CustomBlockingARecords) + record.Address.WriteTo(bW); + + foreach (DnsAAAARecordData record in _dnsServer.CustomBlockingAAAARecords) + record.Address.WriteTo(bW); + } + + { + bW.Write(Convert.ToByte(_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count)); + + foreach (Uri allowListUrl in _dnsServer.BlockListZoneManager.AllowListUrls) + bW.WriteShortString("!" + allowListUrl.AbsoluteUri); + + foreach (Uri blockListUrl in _dnsServer.BlockListZoneManager.BlockListUrls) + bW.WriteShortString(blockListUrl.AbsoluteUri); + + bW.Write(_blockListUpdateIntervalHours); + bW.Write(_blockListLastUpdatedOn); + } + + //proxy & forwarders if (_dnsServer.Proxy == null) { bW.Write((byte)NetProxyType.None); @@ -4257,117 +5022,14 @@ namespace DnsServerCore forwarder.WriteTo(bW); } - { - bW.Write(Convert.ToByte(_credentials.Count)); - - foreach (KeyValuePair credential in _credentials) - { - bW.WriteShortString(credential.Key); - bW.WriteShortString(credential.Value); - } - } - - //block list - bW.Write(_dnsServer.EnableBlocking); - bW.Write((byte)_dnsServer.BlockingType); - - { - bW.Write(Convert.ToByte(_dnsServer.CustomBlockingARecords.Count + _dnsServer.CustomBlockingAAAARecords.Count)); - - foreach (DnsARecordData record in _dnsServer.CustomBlockingARecords) - record.Address.WriteTo(bW); - - foreach (DnsAAAARecordData record in _dnsServer.CustomBlockingAAAARecords) - record.Address.WriteTo(bW); - } - - { - bW.Write(Convert.ToByte(_dnsServer.BlockListZoneManager.AllowListUrls.Count + _dnsServer.BlockListZoneManager.BlockListUrls.Count)); - - foreach (Uri allowListUrl in _dnsServer.BlockListZoneManager.AllowListUrls) - bW.WriteShortString("!" + allowListUrl.AbsoluteUri); - - foreach (Uri blockListUrl in _dnsServer.BlockListZoneManager.BlockListUrls) - bW.WriteShortString(blockListUrl.AbsoluteUri); - - bW.Write(_blockListLastUpdatedOn); - bW.Write(_blockListUpdateIntervalHours); - } - - - { - bW.Write(Convert.ToByte(_dnsServer.LocalEndPoints.Count)); - - foreach (IPEndPoint localEP in _dnsServer.LocalEndPoints) - localEP.WriteTo(bW); - } - - bW.Write(_dnsServer.EnableDnsOverHttp); - bW.Write(_dnsServer.EnableDnsOverTls); - bW.Write(_dnsServer.EnableDnsOverHttps); - - if (_dnsTlsCertificatePath == null) - bW.WriteShortString(string.Empty); - else - bW.WriteShortString(_dnsTlsCertificatePath); - - if (_dnsTlsCertificatePassword == null) - bW.WriteShortString(string.Empty); - else - bW.WriteShortString(_dnsTlsCertificatePassword); - - bW.Write(_dnsServer.CacheZoneManager.MinimumRecordTtl); - bW.Write(_dnsServer.CacheZoneManager.MaximumRecordTtl); - bW.Write(_dnsServer.CacheZoneManager.NegativeRecordTtl); - bW.Write(_dnsServer.CacheZoneManager.FailureRecordTtl); - - if (_dnsServer.TsigKeys is null) - { - bW.Write((byte)0); - } - else - { - bW.Write(Convert.ToByte(_dnsServer.TsigKeys.Count)); - - foreach (KeyValuePair tsigKey in _dnsServer.TsigKeys) - { - bW.WriteShortString(tsigKey.Key); - bW.WriteShortString(tsigKey.Value.SharedSecret); - bW.Write((byte)tsigKey.Value.Algorithm); - } - } - - bW.Write(_dnsServer.NsRevalidation); - bW.Write(_dnsServer.AllowTxtBlockingReport); - bW.Write(_zonesApi.DefaultRecordTtl); - bW.Write(_webServiceUseSelfSignedTlsCertificate); - bW.Write(_dnsServer.UdpPayloadSize); - bW.Write(_dnsServer.DnssecValidation); - - bW.Write(_dnsServer.ResolverRetries); - bW.Write(_dnsServer.ResolverTimeout); - bW.Write(_dnsServer.ResolverMaxStackCount); - bW.Write(_dnsServer.ForwarderRetries); bW.Write(_dnsServer.ForwarderTimeout); bW.Write(_dnsServer.ForwarderConcurrency); - bW.Write(_dnsServer.ClientTimeout); - bW.Write(_dnsServer.TcpSendTimeout); - bW.Write(_dnsServer.TcpReceiveTimeout); - - bW.Write(_dnsServer.CacheZoneManager.MaximumEntries); - - //write config - mS.Position = 0; - - using (FileStream fS = new FileStream(configFile, FileMode.Create, FileAccess.Write)) - { - mS.CopyTo(fS); - } + //logging + bW.Write(_dnsServer.QueryLogManager is not null); //log all queries + bW.Write(_dnsServer.StatsManager.MaxStatFileDays); } - - _log.Write("DNS Server config file was saved: " + configFile); } #endregion @@ -4548,6 +5210,10 @@ namespace DnsServerCore //init dhcp server _dhcpServer = new DhcpServer(Path.Combine(_configFolder, "scopes"), _log); _dhcpServer.AuthZoneManager = _dnsServer.AuthZoneManager; + _dhcpServer.AuthManager = _authManager; + + //load auth config + _authManager.LoadConfigFile(); //load config LoadConfigFile(); @@ -4557,6 +5223,7 @@ namespace DnsServerCore //load all zones files _dnsServer.AuthZoneManager.LoadAllZoneFiles(); + InspectAndFixZonePermissions(); //disable zones from old config format if (_configDisabledZones != null) @@ -4577,7 +5244,7 @@ namespace DnsServerCore _dnsServer.BlockedZoneManager.LoadBlockedZoneFile(); //load block list zone async - if (_dnsServer.BlockListZoneManager.BlockListUrls.Count > 0) + if ((_blockListUpdateIntervalHours > 0) && (_dnsServer.BlockListZoneManager.BlockListUrls.Count > 0)) { ThreadPool.QueueUserWorkItem(delegate (object state) { @@ -4648,12 +5315,21 @@ namespace DnsServerCore internal LogManager Log { get { return _log; } } + internal AuthManager AuthManager + { get { return _authManager; } } + + internal WebServiceZonesApi ZonesApi + { get { return _zonesApi; } } + internal DnsServer DnsServer { get { return _dnsServer; } } internal DhcpServer DhcpServer { get { return _dhcpServer; } } + internal Version ServerVersion + { get { return _currentVersion; } } + public string ConfigFolder { get { return _configFolder; } } From d767fd5e27774402b24d892c494137fea4e40c23 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 18 Sep 2022 18:05:11 +0530 Subject: [PATCH 24/53] webapp: updated html for multi-user support. --- DnsServerCore/www/index.html | 662 +++++++++++++++++++++++++++++++++-- 1 file changed, 630 insertions(+), 32 deletions(-) diff --git a/DnsServerCore/www/index.html b/DnsServerCore/www/index.html index aa43d5ad..63fab79a 100644 --- a/DnsServerCore/www/index.html +++ b/DnsServerCore/www/index.html @@ -25,6 +25,7 @@ + @@ -42,7 +43,10 @@ @@ -109,6 +113,7 @@ + @@ -347,7 +352,7 @@ DNSSEC Status Expiry - + @@ -364,7 +369,7 @@
-

example.com

+

example.com

Primary DNSSEC @@ -373,21 +378,22 @@
- - - - - + + + + + +
@@ -738,6 +744,18 @@
The default TTL value to use if not specified when adding or updating records in a Zone.
+ +
+ +
+
+ +
+
DNS server will check for DNS Apps update every day and will automatically download and install the updates.
+
+
@@ -1008,7 +1026,7 @@ Key Name Shared Secret Algorithm - + @@ -1350,7 +1368,7 @@
- hours (default 24, valid range 1-168) + hours (valid range 0-168; default 24; set 0 to disable)
The interval in hours to automatically download and update the block lists.
@@ -1667,7 +1685,7 @@ Host Name Lease Obtained Lease Expires - + @@ -1917,7 +1935,7 @@ Vendor Class Identifier Vendor Specific Information - + @@ -1936,7 +1954,7 @@ Starting Address Ending Address - + @@ -1986,6 +2004,130 @@ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + +
UsernameSessionLast SeenRemote AddressUser Agent
+
+
+ +
+
+ +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + +
UsernameDisplay NameStatusPrevious LoginRecent Login
+
+
+ +
+
+ +
+
+ +
+ +
+ + + + + + + + + + + + + + +
NameDescription
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + +
SectionUser PermissionsGroup Permissions
+
+
+ +
+ +
+
@@ -3402,7 +3704,7 @@ ns1.example.com ([2001:db8::])
- +
@@ -3521,7 +3823,7 @@ ns1.example.com ([2001:db8::]) (valid range 0-50, recommended 0)
- The number of iterations used by NSEC3 for hashing the domain names. It is recommended to use 0 iterations since more iterations will increase computational costs for both the DNS server and resolver while not providing much value against "zone walking" [draft-ietf-dnsop-nsec3-guidance]. + The number of iterations used by NSEC3 for hashing the domain names. It is recommended to use 0 iterations since more iterations will increase computational costs for both the DNS server and resolver while not providing much value against "zone walking" [RFC 9276].
@@ -3532,7 +3834,7 @@ ns1.example.com ([2001:db8::]) (valid range 0-32, recommended 0)
- The number of bytes of random salt to generate to be used with the NSEC3 hash computation. It is recommended to not use salt by setting the length to 0 [draft-ietf-dnsop-nsec3-guidance]. + The number of bytes of random salt to generate to be used with the NSEC3 hash computation. It is recommended to not use salt by setting the length to 0 [RFC 9276].
@@ -3775,6 +4077,12 @@ ns1.example.com ([2001:db8::])
+
+ +
+