internal async Task <IChallengeValidationDetails> DecodeChallengeValidation(acme.Authorization auth, acme.Challenge challenge) { var client = await GetClient(); return(AuthorizationDecoder.DecodeChallengeValidation(auth, challenge.Type, client.Signer)); }
/// <summary> /// Make sure we have authorization for every host in target /// </summary> /// <param name="target"></param> /// <returns></returns> private async Task HandleChallenge(ExecutionContext context, TargetPart targetPart, acme.Authorization authorization) { var valid = false; var client = context.Scope.Resolve <AcmeClient>(); var identifier = authorization.Identifier.Value; var options = context.Renewal.ValidationPluginOptions; IValidationPlugin?validationPlugin = null; using var validation = _scopeBuilder.Validation(context.Scope, options, targetPart, identifier); try { if (authorization.Status == AcmeClient.AuthorizationValid) { _log.Information("Cached authorization result for {identifier}: {Status}", identifier, authorization.Status); if (!context.RunLevel.HasFlag(RunLevel.Test) && !context.RunLevel.HasFlag(RunLevel.IgnoreCache)) { return; } // Used to make --force or --test re-validation errors non-fatal _log.Information("Handling challenge anyway because --test and/or --force is active"); valid = true; } _log.Information("Authorize identifier {identifier}", identifier); _log.Verbose("Initial authorization status: {status}", authorization.Status); _log.Verbose("Challenge types available: {challenges}", authorization.Challenges.Select(x => x.Type ?? "[Unknown]")); var challenge = authorization.Challenges.FirstOrDefault(c => string.Equals(c.Type, options.ChallengeType, StringComparison.CurrentCultureIgnoreCase)); if (challenge == null) { if (valid) { var usedType = authorization.Challenges. Where(x => x.Status == AcmeClient.ChallengeValid). FirstOrDefault(); _log.Warning("Expected challenge type {type} not available for {identifier}, already validated using {valided}.", options.ChallengeType, authorization.Identifier.Value, usedType?.Type ?? "[unknown]"); return; } else { _log.Error("Expected challenge type {type} not available for {identifier}.", options.ChallengeType, authorization.Identifier.Value); context.Result.AddErrorMessage("Expected challenge type not available", !valid); return; } } else { _log.Verbose("Initial challenge status: {status}", challenge.Status); if (challenge.Status == AcmeClient.ChallengeValid) { // We actually should not get here because if one of the // challenges is valid, the authorization itself should also // be valid. if (!context.RunLevel.HasFlag(RunLevel.Test) && !context.RunLevel.HasFlag(RunLevel.IgnoreCache)) { _log.Information("Cached challenge result: {Status}", authorization.Status); return; } } } // We actually have to do validation now try { validationPlugin = validation.Resolve <IValidationPlugin>(); } catch (Exception ex) { _log.Error(ex, "Error resolving validation plugin"); } if (validationPlugin == null) { _log.Error("Validation plugin not found or not created"); context.Result.AddErrorMessage("Validation plugin not found or not created", !valid); return; } var(disabled, disabledReason) = validationPlugin.Disabled; if (disabled) { _log.Error($"Validation plugin is not available. {disabledReason}"); context.Result.AddErrorMessage("Validation plugin is not available", !valid); return; } _log.Information("Authorizing {dnsIdentifier} using {challengeType} validation ({name})", identifier, options.ChallengeType, options.Name); try { var details = await client.DecodeChallengeValidation(authorization, challenge); await validationPlugin.PrepareChallenge(details); } catch (Exception ex) { _log.Error(ex, "Error preparing for challenge answer"); context.Result.AddErrorMessage("Error preparing for challenge answer", !valid); return; } _log.Debug("Submitting challenge answer"); challenge = await client.AnswerChallenge(challenge); if (challenge.Status != AcmeClient.ChallengeValid) { if (challenge.Error != null) { _log.Error(challenge.Error.ToString()); } _log.Error("Authorization result: {Status}", challenge.Status); context.Result.AddErrorMessage(challenge.Error?.ToString() ?? "Unspecified error", !valid); return; } else { _log.Information("Authorization result: {Status}", challenge.Status); return; } } catch (Exception ex) { _log.Error("Error authorizing {renewal}", targetPart); var message = _exceptionHandler.HandleException(ex); context.Result.AddErrorMessage(message, !valid); } finally { if (validationPlugin != null) { try { _log.Verbose("Starting post-validation cleanup"); await validationPlugin.CleanUp(); _log.Verbose("Post-validation cleanup was succesful"); } catch (Exception ex) { _log.Warning("An error occured during post-validation cleanup: {ex}", ex.Message); } } } }