Exemplo n.º 1
0
        private async Task <DnsSecValidationResult> ValidateRrSigAsync <TRecord>(DomainName name, RecordType recordType, RecordClass recordClass, List <TRecord> resultRecords, List <RrSigRecord> rrSigRecords, DomainName zoneApex, DnsMessageBase msg, TState state, CancellationToken token)
            where TRecord : DnsRecordBase
        {
            DnsSecValidationResult res = DnsSecValidationResult.Bogus;

            foreach (var record in rrSigRecords.Where(x => x.Name.Equals(name) && (x.TypeCovered == recordType)))
            {
                res = await VerifyAsync(record, resultRecords, recordClass, state, token);

                if (res == DnsSecValidationResult.Signed)
                {
                    if ((record.Labels == name.LabelCount) ||
                        ((name.Labels[0] == "*") && (record.Labels == name.LabelCount - 1)))
                    {
                        return(DnsSecValidationResult.Signed);
                    }

                    if (await ValidateNonExistenceAsync(name, recordType, recordClass, rrSigRecords, DomainName.Asterisk + record.Name.GetParentName(record.Name.LabelCount - record.Labels), zoneApex, msg, state, token) == DnsSecValidationResult.Signed)
                    {
                        return(DnsSecValidationResult.Signed);
                    }
                }
            }

            return(res);
        }
Exemplo n.º 2
0
        private async Task <DnsSecValidationResult> ValidateNSecAsync(DomainName name, RecordType recordType, RecordClass recordClass, List <RrSigRecord> rrSigRecords, DomainName stop, DomainName zoneApex, DnsMessageBase msg, TState state, CancellationToken token)
        {
            List <NSecRecord> nsecRecords = msg.AuthorityRecords.OfType <NSecRecord>().ToList();

            if (nsecRecords.Count == 0)
            {
                return(DnsSecValidationResult.Indeterminate);
            }

            foreach (var nsecGroup in nsecRecords.GroupBy(x => x.Name))
            {
                DnsSecValidationResult validationResult = await ValidateRrSigAsync(nsecGroup.Key, RecordType.NSec, recordClass, nsecGroup.ToList(), rrSigRecords, zoneApex, msg, state, token);

                if (validationResult != DnsSecValidationResult.Signed)
                {
                    return(validationResult);
                }
            }

            DomainName current = name;

            while (true)
            {
                if (current.Equals(stop))
                {
                    return(DnsSecValidationResult.Signed);
                }

                NSecRecord nsecRecord = nsecRecords.FirstOrDefault(x => x.Name.Equals(current));
                if (nsecRecord != null)
                {
                    return(nsecRecord.Types.Contains(recordType) ? DnsSecValidationResult.Bogus : DnsSecValidationResult.Signed);
                }
                else
                {
                    nsecRecord = nsecRecords.FirstOrDefault(x => x.IsCovering(current, zoneApex));
                    if (nsecRecord == null)
                    {
                        return(DnsSecValidationResult.Bogus);
                    }
                }

                // Updated the following lines to fix an issue where an NSEC entry is present for a parent domain but is not a wildcard entry.
                if (current.Labels[0] == "*")
                {
                    current = current.GetParentName();
                }
                else
                {
                    current = DomainName.Asterisk + current.GetParentName();
                }
            }
        }
Exemplo n.º 3
0
        private async Task <DnsSecValidationResult> ValidateNSec3Async(DomainName name, RecordType recordType, RecordClass recordClass, List <RrSigRecord> rrSigRecords, bool checkWildcard, DomainName zoneApex, DnsMessageBase msg, TState state, CancellationToken token)
        {
            List <NSec3Record> nsecRecords = msg.AuthorityRecords.OfType <NSec3Record>().ToList();

            if (nsecRecords.Count == 0)
            {
                return(DnsSecValidationResult.Indeterminate);
            }

            foreach (var nsecGroup in nsecRecords.GroupBy(x => x.Name))
            {
                DnsSecValidationResult validationResult = await ValidateRrSigAsync(nsecGroup.Key, RecordType.NSec3, recordClass, nsecGroup.ToList(), rrSigRecords, zoneApex, msg, state, token);

                if (validationResult != DnsSecValidationResult.Signed)
                {
                    return(validationResult);
                }
            }

            var nsec3Parameter = nsecRecords.Where(x => x.Name.GetParentName().Equals(zoneApex)).Where(x => x.HashAlgorithm.IsSupported()).Select(x => new { x.HashAlgorithm, x.Iterations, x.Salt }).OrderBy(x => x.HashAlgorithm.GetPriority()).First();

            DomainName hashedName = name.GetNsec3HashName(nsec3Parameter.HashAlgorithm, nsec3Parameter.Iterations, nsec3Parameter.Salt, zoneApex);

            if (recordType == RecordType.Ds && nsecRecords.Any(x => (x.Flags == 1) && (x.IsCovering(hashedName))))
            {
                return(DnsSecValidationResult.Unsigned);
            }

            var directMatch = nsecRecords.FirstOrDefault(x => x.Name.Equals(hashedName));

            if (directMatch != null)
            {
                return(directMatch.Types.Contains(recordType) ? DnsSecValidationResult.Bogus : DnsSecValidationResult.Signed);
            }

            // find closest encloser
            DomainName current            = name;
            DomainName previousHashedName = hashedName;

            while (true)
            {
                if (nsecRecords.Any(x => x.Name == hashedName))
                {
                    break;
                }

                if (current == zoneApex)
                {
                    return(DnsSecValidationResult.Bogus);                    // closest encloser could not be found, but at least the zone apex must be found as
                }
                current            = current.GetParentName();
                previousHashedName = hashedName;
                hashedName         = current.GetNsec3HashName(nsec3Parameter.HashAlgorithm, nsec3Parameter.Iterations, nsec3Parameter.Salt, zoneApex);
            }

            if (!nsecRecords.Any(x => x.IsCovering(previousHashedName)))
            {
                return(DnsSecValidationResult.Bogus);
            }

            if (checkWildcard)
            {
                DomainName wildcardHashName = (DomainName.Asterisk + current).GetNsec3HashName(nsec3Parameter.HashAlgorithm, nsec3Parameter.Iterations, nsec3Parameter.Salt, zoneApex);

                var wildcardDirectMatch = nsecRecords.FirstOrDefault(x => x.Name.Equals(wildcardHashName));
                if ((wildcardDirectMatch != null) && (!wildcardDirectMatch.Types.Contains(recordType)))
                {
                    return(wildcardDirectMatch.Types.Contains(recordType) ? DnsSecValidationResult.Bogus : DnsSecValidationResult.Signed);
                }

                var wildcardCoveringMatch = nsecRecords.FirstOrDefault(x => x.IsCovering(wildcardHashName));
                return((wildcardCoveringMatch != null) ? DnsSecValidationResult.Signed : DnsSecValidationResult.Bogus);
            }
            else
            {
                return(DnsSecValidationResult.Signed);
            }
        }
Exemplo n.º 4
0
        private async Task <DnsSecResult <T> > ResolveAsyncInternal <T>(DomainName name, RecordType recordType, RecordClass recordClass, State state, CancellationToken token)
            where T : DnsRecordBase
        {
            DnsCacheRecordList <T> cachedResults;

            if (_cache.TryGetRecords(name, recordType, recordClass, out cachedResults))
            {
                return(new DnsSecResult <T>(cachedResults, cachedResults.ValidationResult));
            }

            DnsCacheRecordList <CNameRecord> cachedCNames;

            if (_cache.TryGetRecords(name, RecordType.CName, recordClass, out cachedCNames))
            {
                var cNameResult = await ResolveAsyncInternal <T>(cachedCNames.First().CanonicalName, recordType, recordClass, state, token);

                return(new DnsSecResult <T>(cNameResult.Records, cachedCNames.ValidationResult == cNameResult.ValidationResult ? cachedCNames.ValidationResult : DnsSecValidationResult.Unsigned));
            }

            DnsMessage msg = await ResolveMessageAsync(name, recordType, recordClass, state, token);

            // check for cname
            List <DnsRecordBase> cNameRecords = msg.AnswerRecords.Where(x => (x.RecordType == RecordType.CName) && (x.RecordClass == recordClass) && x.Name.Equals(name)).ToList();

            if (cNameRecords.Count > 0)
            {
                DnsSecValidationResult cNameValidationResult = await _validator.ValidateAsync(name, RecordType.CName, recordClass, msg, cNameRecords, state, token);

                if ((cNameValidationResult == DnsSecValidationResult.Bogus) || (cNameValidationResult == DnsSecValidationResult.Indeterminate))
                {
                    throw new DnsSecValidationException("CNAME record could not be validated");
                }

                _cache.Add(name, RecordType.CName, recordClass, cNameRecords, cNameValidationResult, cNameRecords.Min(x => x.TimeToLive));

                DomainName canonicalName = ((CNameRecord)cNameRecords.First()).CanonicalName;

                List <DnsRecordBase> matchingAdditionalRecords = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(canonicalName)).ToList();
                if (matchingAdditionalRecords.Count > 0)
                {
                    DnsSecValidationResult matchingValidationResult = await _validator.ValidateAsync(canonicalName, recordType, recordClass, msg, matchingAdditionalRecords, state, token);

                    if ((matchingValidationResult == DnsSecValidationResult.Bogus) || (matchingValidationResult == DnsSecValidationResult.Indeterminate))
                    {
                        throw new DnsSecValidationException("CNAME matching records could not be validated");
                    }

                    DnsSecValidationResult validationResult = cNameValidationResult == matchingValidationResult ? cNameValidationResult : DnsSecValidationResult.Unsigned;
                    _cache.Add(canonicalName, recordType, recordClass, matchingAdditionalRecords, validationResult, matchingAdditionalRecords.Min(x => x.TimeToLive));

                    return(new DnsSecResult <T>(matchingAdditionalRecords.OfType <T>().ToList(), validationResult));
                }

                var cNameResults = await ResolveAsyncInternal <T>(canonicalName, recordType, recordClass, state, token);

                return(new DnsSecResult <T>(cNameResults.Records, cNameValidationResult == cNameResults.ValidationResult ? cNameValidationResult : DnsSecValidationResult.Unsigned));
            }

            // check for "normal" answer
            List <DnsRecordBase> answerRecords = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(name)).ToList();

            if (answerRecords.Count > 0)
            {
                DnsSecValidationResult validationResult = await _validator.ValidateAsync(name, recordType, recordClass, msg, answerRecords, state, token);

                if ((validationResult == DnsSecValidationResult.Bogus) || (validationResult == DnsSecValidationResult.Indeterminate))
                {
                    throw new DnsSecValidationException("Response records could not be validated");
                }

                _cache.Add(name, recordType, recordClass, answerRecords, validationResult, answerRecords.Min(x => x.TimeToLive));
                return(new DnsSecResult <T>(answerRecords.OfType <T>().ToList(), validationResult));
            }

            // check for negative answer
            SoaRecord soaRecord = msg.AuthorityRecords
                                  .Where(x =>
                                         (x.RecordType == RecordType.Soa) &&
                                         (name.Equals(x.Name) || name.IsSubDomainOf(x.Name)))
                                  .OfType <SoaRecord>()
                                  .FirstOrDefault();

            if (soaRecord != null)
            {
                DnsSecValidationResult validationResult = await _validator.ValidateAsync(name, recordType, recordClass, msg, answerRecords, state, token);

                if ((validationResult == DnsSecValidationResult.Bogus) || (validationResult == DnsSecValidationResult.Indeterminate))
                {
                    throw new DnsSecValidationException("Negative answer could not be validated");
                }

                _cache.Add(name, recordType, recordClass, new List <DnsRecordBase>(), validationResult, soaRecord.NegativeCachingTTL);
                return(new DnsSecResult <T>(new List <T>(), validationResult));
            }

            // authoritive response does not contain answer
            throw new Exception("Could not resolve " + name);
        }
Exemplo n.º 5
0
 internal DnsSecResult(List <T> records, DnsSecValidationResult validationResult)
 {
     Records          = records;
     ValidationResult = validationResult;
 }
Exemplo n.º 6
0
        public void Add <TRecord>(DomainName name, RecordType recordType, RecordClass recordClass, IEnumerable <TRecord> records, DnsSecValidationResult validationResult, int timeToLive)
            where TRecord : DnsRecordBase
        {
            DnsCacheRecordList <DnsRecordBase> cacheValues = new DnsCacheRecordList <DnsRecordBase>();

            cacheValues.AddRange(records);
            cacheValues.ValidationResult = validationResult;

            Add(name, recordType, recordClass, cacheValues, timeToLive);
        }
Exemplo n.º 7
0
        /// <summary>
        ///   Resolves specified records as an asynchronous operation.
        /// </summary>
        /// <typeparam name="T"> Type of records, that should be returned </typeparam>
        /// <param name="name"> Domain, that should be queried </param>
        /// <param name="recordType"> Type the should be queried </param>
        /// <param name="recordClass"> Class the should be queried </param>
        /// <param name="token"> The token to monitor cancellation requests </param>
        /// <returns> A list of matching <see cref="DnsRecordBase">records</see> </returns>
        public async Task <DnsSecResult <T> > ResolveSecureAsync <T>(DomainName name, RecordType recordType = RecordType.A, RecordClass recordClass = RecordClass.INet, CancellationToken token = default(CancellationToken))
            where T : DnsRecordBase
        {
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name), "Name must be provided");
            }

            DnsCacheRecordList <T> cacheResult;

            if (_cache.TryGetRecords(name, recordType, recordClass, out cacheResult))
            {
                return(new DnsSecResult <T>(cacheResult, cacheResult.ValidationResult));
            }

            DnsMessage msg = await _dnsClient.ResolveAsync(name, recordType, recordClass, new DnsQueryOptions()
            {
                IsEDnsEnabled      = true,
                IsDnsSecOk         = true,
                IsCheckingDisabled = true,
                IsRecursionDesired = true
            }, token).ConfigureAwait(false);

            if ((msg == null) || ((msg.ReturnCode != ReturnCode.NoError) && (msg.ReturnCode != ReturnCode.NxDomain)))
            {
                throw new Exception("DNS request failed");
            }

            DnsSecValidationResult validationResult;

            CNameRecord cName = msg.AnswerRecords.Where(x => (x.RecordType == RecordType.CName) && (x.RecordClass == recordClass) && x.Name.Equals(name)).OfType <CNameRecord>().FirstOrDefault();

            if (cName != null)
            {
                DnsSecValidationResult cNameValidationResult = await _validator.ValidateAsync(name, RecordType.CName, recordClass, msg, new List <CNameRecord>() { cName }, null, token).ConfigureAwait(false);

                if ((cNameValidationResult == DnsSecValidationResult.Bogus) || (cNameValidationResult == DnsSecValidationResult.Indeterminate))
                {
                    throw new DnsSecValidationException("CNAME record could not be validated");
                }

                var records = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(cName.CanonicalName)).OfType <T>().ToList();
                if (records.Count > 0)
                {
                    DnsSecValidationResult recordsValidationResult = await _validator.ValidateAsync(cName.CanonicalName, recordType, recordClass, msg, records, null, token).ConfigureAwait(false);

                    if ((recordsValidationResult == DnsSecValidationResult.Bogus) || (recordsValidationResult == DnsSecValidationResult.Indeterminate))
                    {
                        throw new DnsSecValidationException("CNAME matching records could not be validated");
                    }

                    validationResult = cNameValidationResult == recordsValidationResult ? cNameValidationResult : DnsSecValidationResult.Unsigned;
                    _cache.Add(name, recordType, recordClass, records, validationResult, Math.Min(cName.TimeToLive, records.Min(x => x.TimeToLive)));

                    return(new DnsSecResult <T>(records, validationResult));
                }

                var cNameResults = await ResolveSecureAsync <T>(cName.CanonicalName, recordType, recordClass, token).ConfigureAwait(false);

                validationResult = cNameValidationResult == cNameResults.ValidationResult ? cNameValidationResult : DnsSecValidationResult.Unsigned;

                if (cNameResults.Records.Count > 0)
                {
                    _cache.Add(name, recordType, recordClass, cNameResults.Records, validationResult, Math.Min(cName.TimeToLive, cNameResults.Records.Min(x => x.TimeToLive)));
                }

                return(new DnsSecResult <T>(cNameResults.Records, validationResult));
            }

            List <T> res = msg.AnswerRecords.Where(x => (x.RecordType == recordType) && (x.RecordClass == recordClass) && x.Name.Equals(name)).OfType <T>().ToList();

            validationResult = await _validator.ValidateAsync(name, recordType, recordClass, msg, res, null, token).ConfigureAwait(false);

            if ((validationResult == DnsSecValidationResult.Bogus) || (validationResult == DnsSecValidationResult.Indeterminate))
            {
                throw new DnsSecValidationException("Response records could not be validated");
            }

            if (res.Count > 0)
            {
                _cache.Add(name, recordType, recordClass, res, validationResult, res.Min(x => x.TimeToLive));
            }

            return(new DnsSecResult <T>(res, validationResult));
        }
Exemplo n.º 8
0
 public DnsSecResult(ReturnCode returnCode, List <T> records, DnsSecValidationResult validationResult)
 {
     ReturnCode       = returnCode;
     Records          = records;
     ValidationResult = validationResult;
 }