Exemplo n.º 1
0
        /// <summary>
        /// Simulates responding to a challenge, performs a sample configuration and attempts to
        /// verify it.
        /// </summary>
        /// <param name="iisManager"></param>
        /// <param name="managedSite"></param>
        /// <returns> APIResult </returns>
        /// <remarks>
        /// The purpose of this method is to test the options (permissions, configuration) before
        /// submitting a request to the ACME server, to avoid creating failed requests and hitting
        /// usage limits.
        /// </remarks>
        public async Task <APIResult> TestChallengeResponse(IISManager iisManager, ManagedSite managedSite, bool isPreviewMode)
        {
            return(await Task.Run(() =>
            {
                ActionLogs.Clear(); // reset action logs

                var requestConfig = managedSite.RequestConfig;
                var result = new APIResult();
                var domains = new List <string> {
                    requestConfig.PrimaryDomain
                };

                if (requestConfig.SubjectAlternativeNames != null)
                {
                    domains.AddRange(requestConfig.SubjectAlternativeNames);
                }

                var generatedAuthorizations = new List <PendingAuthorization>();

                try
                {
                    // if DNS checks enabled, attempt them here
                    if (isPreviewMode && CoreAppSettings.Current.EnableDNSValidationChecks)
                    {
                        // check all domain configs
                        Parallel.ForEach(domains.Distinct(), new ParallelOptions
                        {
                            // check 8 domains at a time
                            MaxDegreeOfParallelism = 8
                        },
                                         domain =>
                        {
                            var(ok, message) = NetUtil.CheckDNS(domain);
                            if (!ok)
                            {
                                result.IsOK = false;
                                result.FailedItemSummary.Add(message);
                            }
                        });
                        if (!result.IsOK)
                        {
                            return result;
                        }
                    }

                    if (requestConfig.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP)
                    {
                        foreach (var domain in domains.Distinct())
                        {
                            string challengeFileUrl = $"http://{domain}/.well-known/acme-challenge/configcheck";

                            var simulatedAuthorization = new PendingAuthorization
                            {
                                Challenge = new AuthorizeChallengeItem
                                {
                                    ChallengeData = new ACMESharp.ACME.HttpChallenge(ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP,
                                                                                     new ACMESharp.ACME.HttpChallengeAnswer {
                                        KeyAuthorization = GenerateSimulatedKeyAuth()
                                    })
                                    {
                                        FilePath = ".well-known/acme-challenge/configcheck",
                                        FileContent = "Extensionless File Config Test - OK",
                                        FileUrl = challengeFileUrl
                                    }
                                }
                            };

                            generatedAuthorizations.Add(simulatedAuthorization);

                            var resultOK = PrepareChallengeResponse_Http01(
                                iisManager, domain, managedSite, simulatedAuthorization
                                )();

                            if (!resultOK)
                            {
                                result.IsOK = false;
                                result.FailedItemSummary.Add($"Config checks failed to verify http://{domain} is both publicly accessible and can serve extensionless files e.g. {challengeFileUrl}");
                            }
                        }
                    }
                    else if (requestConfig.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_SNI)
                    {
                        if (iisManager.GetIisVersion().Major < 8)
                        {
                            result.IsOK = false;
                            result.FailedItemSummary.Add($"The {ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_SNI} challenge is only available for IIS versions 8+.");
                            return result;
                        }

                        result.IsOK = domains.Distinct().All(domain =>
                        {
                            var simulatedAuthorization = new PendingAuthorization
                            {
                                Challenge = new AuthorizeChallengeItem()
                                {
                                    ChallengeData = new ACMESharp.ACME.TlsSniChallenge(ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_SNI,
                                                                                       new ACMESharp.ACME.TlsSniChallengeAnswer {
                                        KeyAuthorization = GenerateSimulatedKeyAuth()
                                    })
                                    {
                                        IterationCount = 1
                                    }
                                }
                            };
                            generatedAuthorizations.Add(simulatedAuthorization);
                            return PrepareChallengeResponse_TlsSni01(
                                iisManager, domain, managedSite, simulatedAuthorization
                                )();
                        });
                    }
                    else
                    {
                        throw new NotSupportedException($"ChallengeType not supported: {requestConfig.ChallengeType}");
                    }
                }
                finally
                {
                    //FIXME: needs to be filtered by managed site: result.Message = String.Join("\r\n", GetActionLogSummary());
                    generatedAuthorizations.ForEach(ga => ga.Cleanup());
                }
                return result;
            }));
        }