From 2dd993c7077549884bc6225fa578929583df80be Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sat, 19 Feb 2022 13:03:02 +0530 Subject: [PATCH] AuthZoneManager: added GetZoneWithSubDomainZones(), FindZone(), FindNextSubDomainZone(), FindPreviousSubDomainZone(), and RemoveSubDomainZone() internal methods for use with DNSSEC implementation. Implemented DNSSEC related methods to allow access to API. Implemented FindAuthZoneInfo() and GetAuthZoneInfo(). Updated Query() with DNSSEC related changes. Code refactoring done. --- .../Dns/ZoneManagers/AuthZoneManager.cs | 239 +++++++++++++++--- 1 file changed, 206 insertions(+), 33 deletions(-) diff --git a/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs b/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs index 12aa3f91..5eaf98f5 100644 --- a/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs +++ b/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs @@ -17,6 +17,7 @@ along with this program. If not, see . */ +using DnsServerCore.Dns.Dnssec; using DnsServerCore.Dns.ResourceRecords; using DnsServerCore.Dns.Trees; using DnsServerCore.Dns.Zones; @@ -185,7 +186,7 @@ namespace DnsServerCore.Dns.ZoneManagers throw new DnsServerException("Zone already exists: " + zoneInfo.Name); } - private AuthZone GetOrAddSubDomainZone(string zoneName, string domain) + internal AuthZone GetOrAddSubDomainZone(string zoneName, string domain) { return _root.GetOrAddSubDomainZone(zoneName, domain, delegate () { @@ -315,7 +316,7 @@ namespace DnsServerCore.Dns.ZoneManagers } } - private DnsDatagram GetReferralResponse(DnsDatagram request, bool isZoneSigned, AuthZone delegationZone, bool isRecursionAllowed) + private DnsDatagram GetReferralResponse(DnsDatagram request, bool dnssecOk, AuthZone delegationZone, bool isRecursionAllowed) { IReadOnlyList authority; @@ -325,9 +326,9 @@ namespace DnsServerCore.Dns.ZoneManagers } else { - authority = delegationZone.QueryRecords(DnsResourceRecordType.NS, isZoneSigned); + authority = delegationZone.QueryRecords(DnsResourceRecordType.NS, dnssecOk); - if (isZoneSigned) + if (dnssecOk) { IReadOnlyList dsRecords = delegationZone.QueryRecords(DnsResourceRecordType.DS, true); if (dsRecords.Count > 0) @@ -347,7 +348,7 @@ namespace DnsServerCore.Dns.ZoneManagers } } - IReadOnlyList additional = GetAdditionalRecords(authority, isZoneSigned); + IReadOnlyList additional = GetAdditionalRecords(authority, dnssecOk); return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NoError, request.Question, null, authority, additional); } @@ -578,6 +579,39 @@ namespace DnsServerCore.Dns.ZoneManagers return null; } + internal IReadOnlyList GetZoneWithSubDomainZones(string zoneName) + { + return _root.GetZoneWithSubDomainZones(zoneName); + } + + internal AuthZone FindZone(string domain) + { + if (_root.TryGet(domain, out AuthZoneNode authZoneNode)) + { + if (authZoneNode.ApexZone is not null) + return authZoneNode.ApexZone; + + return authZoneNode.ParentSideZone; + } + + return null; + } + + internal AuthZone FindNextSubDomainZone(string domain) + { + return _root.FindNextSubDomainZone(domain); + } + + internal AuthZone FindPreviousSubDomainZone(string domain) + { + return _root.FindPreviousSubDomainZone(domain); + } + + internal void RemoveSubDomainZone(string domain) + { + _root.TryRemove(domain, out SubDomainZone _); + } + #endregion #region public @@ -745,6 +779,126 @@ namespace DnsServerCore.Dns.ZoneManagers return null; } + public void SignPrimaryZoneWithRsaNSEC(string zoneName, string hashAlgorithm, int kskKeySize, int zskKeySize, uint dnsKeyTtl) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.SignZoneWithRsaNSec(hashAlgorithm, kskKeySize, zskKeySize, dnsKeyTtl); + } + + public void SignPrimaryZoneWithRsaNSEC3(string zoneName, string hashAlgorithm, int kskKeySize, int zskKeySize, ushort iterations, byte saltLength, uint dnsKeyTtl) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.SignZoneWithRsaNSec3(hashAlgorithm, kskKeySize, zskKeySize, iterations, saltLength, dnsKeyTtl); + } + + public void SignPrimaryZoneWithEcdsaNSEC(string zoneName, string curve, uint dnsKeyTtl) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.SignZoneWithEcdsaNSec(curve, dnsKeyTtl); + } + + public void SignPrimaryZoneWithEcdsaNSEC3(string zoneName, string curve, ushort iterations, byte saltLength, uint dnsKeyTtl) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.SignZoneWithEcdsaNSec3(curve, iterations, saltLength, dnsKeyTtl); + } + + public void UnsignPrimaryZone(string zoneName) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.UnsignZone(); + } + + public void ConvertPrimaryZoneToNSEC(string zoneName) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.ConvertToNSec(); + } + + public void ConvertPrimaryZoneToNSEC3(string zoneName, ushort iterations, byte saltLength) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.ConvertToNSec3(iterations, saltLength); + } + + public void UpdatePrimaryZoneNSEC3Parameters(string zoneName, ushort iterations, byte saltLength) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.UpdateNSec3Parameters(iterations, saltLength); + } + + public void UpdatePrimaryZoneDnsKeyTtl(string zoneName, uint dnsKeyTtl) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.UpdateDnsKeyTtl(dnsKeyTtl); + } + + public void GenerateAndAddPrimaryZoneDnssecRsaPrivateKey(string zoneName, DnssecPrivateKeyType keyType, string hashAlgorithm, int keySize) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.GenerateAndAddRsaKey(keyType, hashAlgorithm, keySize); + } + + public void GenerateAndAddPrimaryZoneDnssecEcdsaPrivateKey(string zoneName, DnssecPrivateKeyType keyType, string curve) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.GenerateAndAddEcdsaKey(keyType, curve); + } + + public void DeletePrimaryZoneDnssecPrivateKey(string zoneName, ushort keyTag) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.DeletePrivateKey(keyTag); + } + + public void PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(string zoneName) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.PublishAllGeneratedKeys(); + } + + public void RolloverPrimaryZoneDnsKey(string zoneName, ushort keyTag) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.RolloverDnsKey(keyTag); + } + + public void RetirePrimaryZoneDnsKey(string zoneName, ushort keyTag) + { + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone) || (authZone is not PrimaryZone primaryZone) || primaryZone.Internal) + throw new DnsServerException("No such primary zone was found: " + zoneName); + + primaryZone.RetireDnsKey(keyTag); + } + public bool DeleteZone(string zoneName) { if (_root.TryRemove(zoneName, out ApexZone apexZone)) @@ -758,7 +912,15 @@ namespace DnsServerCore.Dns.ZoneManagers return false; } - public AuthZoneInfo GetAuthZoneInfo(string domain, bool loadHistory = false) + public AuthZoneInfo GetAuthZoneInfo(string zoneName, bool loadHistory = false) + { + if (_root.TryGet(zoneName, out AuthZoneNode authZoneNode)) + return new AuthZoneInfo(authZoneNode.ApexZone, loadHistory); + + return null; + } + + public AuthZoneInfo FindAuthZoneInfo(string domain, bool loadHistory = false) { _ = _root.FindZone(domain, out _, out _, out ApexZone apexZone, out _); if (apexZone is null) @@ -988,6 +1150,9 @@ namespace DnsServerCore.Dns.ZoneManagers else if ((zone is SubDomainZone subDomainZone) && subDomainZone.AuthoritativeZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase)) zone.SyncRecords(latestEntries.Value); } + + if (_root.TryGet(zoneName, zoneName, out AuthZone authZone)) + (authZone as ApexZone).UpdateDnssecStatus(); } public IReadOnlyList SyncIncrementalZoneTransferRecords(string zoneName, IReadOnlyList xfrRecords) @@ -1002,9 +1167,12 @@ namespace DnsServerCore.Dns.ZoneManagers return Array.Empty(); } - IReadOnlyList soaRecords = GetRecords(zoneName, zoneName, DnsResourceRecordType.SOA); + if (!_root.TryGet(zoneName, zoneName, out AuthZone authZone)) + throw new InvalidOperationException("No such zone was found: " + zoneName); + + IReadOnlyList soaRecords = authZone.GetRecords(DnsResourceRecordType.SOA); if (soaRecords.Count != 1) - throw new InvalidOperationException("No authoritative zone was found for the domain."); + throw new InvalidOperationException("No authoritative zone was found: " + zoneName); //process IXFR response DnsResourceRecord currentSoaRecord = soaRecords[0]; @@ -1177,6 +1345,8 @@ namespace DnsServerCore.Dns.ZoneManagers addedGlueRecords.Clear(); } + (authZone as ApexZone).UpdateDnssecStatus(); + //return history List historyRecords = new List(xfrRecords.Count - 2); @@ -1237,6 +1407,8 @@ namespace DnsServerCore.Dns.ZoneManagers subDomainZone.AutoUpdateState(); } } + + apexZone.UpdateDnssecStatus(); } internal void LoadRecords(ApexZone apexZone, IReadOnlyList records) @@ -1261,6 +1433,8 @@ namespace DnsServerCore.Dns.ZoneManagers subDomainZone.AutoUpdateState(); } } + + apexZone.UpdateDnssecStatus(); } public void SetRecords(string zoneName, string domain, DnsResourceRecordType type, uint ttl, DnsResourceRecordData[] records) @@ -1468,9 +1642,9 @@ namespace DnsServerCore.Dns.ZoneManagers _ = _root.FindZone(request.Question[0].Name, out _, out SubDomainZone delegation, out ApexZone apexZone, out _); if (delegation is not null) { - bool isZoneSigned = request.DnssecOk && apexZone.GetRecords(DnsResourceRecordType.DNSKEY).Count > 0; + bool dnssecOk = request.DnssecOk && (apexZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned); - return GetReferralResponse(request, isZoneSigned, delegation, true); + return GetReferralResponse(request, dnssecOk, delegation, true); } //no delegation found @@ -1486,14 +1660,13 @@ namespace DnsServerCore.Dns.ZoneManagers if ((apexZone is null) || !apexZone.IsActive) return null; //no authority for requested zone - bool isZoneSigned = request.DnssecOk && (apexZone.GetRecords(DnsResourceRecordType.DNSKEY).Count > 0); - bool isZoneNSEC3 = isZoneSigned && (apexZone.GetRecords(DnsResourceRecordType.NSEC3PARAM).Count > 0); + bool dnssecOk = request.DnssecOk && (apexZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned); if ((zone is null) || !zone.IsActive) { //zone not found if ((delegation is not null) && delegation.IsActive && (delegation.Name.Length > apexZone.Name.Length)) - return GetReferralResponse(request, isZoneSigned, delegation, isRecursionAllowed); + return GetReferralResponse(request, dnssecOk, delegation, isRecursionAllowed); if (apexZone is StubZone) return GetReferralResponse(request, false, apexZone, isRecursionAllowed); @@ -1507,10 +1680,10 @@ namespace DnsServerCore.Dns.ZoneManagers if (closest is not null) { - answer = closest.QueryRecords(DnsResourceRecordType.DNAME, isZoneSigned); + answer = closest.QueryRecords(DnsResourceRecordType.DNAME, dnssecOk); if ((answer.Count > 0) && (answer[0].Type == DnsResourceRecordType.DNAME)) { - if (!DoDNAMESubstitution(question, isZoneSigned, answer, out answer)) + if (!DoDNAMESubstitution(question, dnssecOk, answer, out answer)) rCode = DnsResponseCode.YXDomain; } else @@ -1522,10 +1695,10 @@ namespace DnsServerCore.Dns.ZoneManagers if (((answer is null) || (answer.Count == 0)) && ((authority is null) || (authority.Count == 0))) { - answer = apexZone.QueryRecords(DnsResourceRecordType.DNAME, isZoneSigned); + answer = apexZone.QueryRecords(DnsResourceRecordType.DNAME, dnssecOk); if ((answer.Count > 0) && (answer[0].Type == DnsResourceRecordType.DNAME)) { - if (!DoDNAMESubstitution(question, isZoneSigned, answer, out answer)) + if (!DoDNAMESubstitution(question, dnssecOk, answer, out answer)) rCode = DnsResponseCode.YXDomain; } else @@ -1537,17 +1710,17 @@ namespace DnsServerCore.Dns.ZoneManagers if (!hasSubDomains) rCode = DnsResponseCode.NxDomain; - authority = apexZone.QueryRecords(DnsResourceRecordType.SOA, isZoneSigned); + authority = apexZone.QueryRecords(DnsResourceRecordType.SOA, dnssecOk); - if (isZoneSigned) + if (dnssecOk) { - //add proof of non existence (NXDOMAIN/NODATA) to prove the qname does not exists + //add proof of non existence (NXDOMAIN) to prove the qname does not exists IReadOnlyList nsecRecords; - if (isZoneNSEC3) - nsecRecords = _root.FindNSEC3ProofOfNonExistenceNxDomain(question.Name, question.Type, false); + if (apexZone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3) + nsecRecords = _root.FindNSec3ProofOfNonExistenceNxDomain(question.Name, question.Type, false); else - nsecRecords = _root.FindNSECProofOfNonExistenceNxDomain(question.Name, question.Type, false); + nsecRecords = _root.FindNSecProofOfNonExistenceNxDomain(question.Name, question.Type, false); if (nsecRecords.Count > 0) { @@ -1579,7 +1752,7 @@ namespace DnsServerCore.Dns.ZoneManagers IReadOnlyList authority = null; IReadOnlyList additional; - IReadOnlyList answers = zone.QueryRecords(question.Type, isZoneSigned); + IReadOnlyList answers = zone.QueryRecords(question.Type, dnssecOk); if (answers.Count == 0) { //record type not found @@ -1603,7 +1776,7 @@ namespace DnsServerCore.Dns.ZoneManagers { //check for delegation, stub & forwarder if ((delegation is not null) && delegation.IsActive && (delegation.Name.Length > apexZone.Name.Length)) - return GetReferralResponse(request, isZoneSigned, delegation, isRecursionAllowed); + return GetReferralResponse(request, dnssecOk, delegation, isRecursionAllowed); if (apexZone is StubZone) return GetReferralResponse(request, false, apexZone, isRecursionAllowed); @@ -1623,9 +1796,9 @@ namespace DnsServerCore.Dns.ZoneManagers authority = apexZone.QueryRecords(DnsResourceRecordType.APP, false); if (authority.Count == 0) { - authority = apexZone.QueryRecords(DnsResourceRecordType.SOA, isZoneSigned); + authority = apexZone.QueryRecords(DnsResourceRecordType.SOA, dnssecOk); - if (isZoneSigned) + if (dnssecOk) authority = AddProofOfNonExistenceNoData(zone, authority); //add proof of non existence (NODATA) to prove that no such type or record exists } } @@ -1647,14 +1820,14 @@ namespace DnsServerCore.Dns.ZoneManagers answers = wildcardAnswers; //add proof of non existence (WILDCARD) to prove that the wildcard expansion was legit and the qname actually does not exists - if (isZoneSigned) + if (dnssecOk) { IReadOnlyList nsecRecords; - if (isZoneNSEC3) - nsecRecords = _root.FindNSEC3ProofOfNonExistenceNxDomain(question.Name, question.Type, true); + if (apexZone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3) + nsecRecords = _root.FindNSec3ProofOfNonExistenceNxDomain(question.Name, question.Type, true); else - nsecRecords = _root.FindNSECProofOfNonExistenceNxDomain(question.Name, question.Type, true); + nsecRecords = _root.FindNSecProofOfNonExistenceNxDomain(question.Name, question.Type, true); if (nsecRecords.Count > 0) authority = nsecRecords; @@ -1667,7 +1840,7 @@ namespace DnsServerCore.Dns.ZoneManagers List newAnswers = new List(answers.Count + 1); newAnswers.AddRange(answers); - ResolveCNAME(question, isZoneSigned, lastRR, newAnswers); + ResolveCNAME(question, dnssecOk, lastRR, newAnswers); answers = newAnswers; } @@ -1677,7 +1850,7 @@ namespace DnsServerCore.Dns.ZoneManagers case DnsResourceRecordType.NS: case DnsResourceRecordType.MX: case DnsResourceRecordType.SRV: - additional = GetAdditionalRecords(answers, isZoneSigned); + additional = GetAdditionalRecords(answers, dnssecOk); break; default: