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: