def _poll_challenges(
            self, chall_update, best_effort, min_sleep=3, max_rounds=15):
        """Wait for all challenge results to be determined."""
        dom_to_check = set(chall_update.keys())
        comp_domains = set()
        rounds = 0

        while dom_to_check and rounds < max_rounds:
            # TODO: Use retry-after...
            time.sleep(min_sleep)
            for domain in dom_to_check:
                comp_challs, failed_challs = self._handle_check(
                    domain, chall_update[domain])

                if len(comp_challs) == len(chall_update[domain]):
                    comp_domains.add(domain)
                elif not failed_challs:
                    for chall in comp_challs:
                        chall_update[domain].remove(chall)
                # We failed some challenges... damage control
                else:
                    # Right now... just assume a loss and carry on...
                    if best_effort:
                        comp_domains.add(domain)
                    else:
                        raise errors.AuthorizationError(
                            "Failed Authorization procedure for %s" % domain)

            dom_to_check -= comp_domains
            comp_domains.clear()
            rounds += 1
Example #2
0
    def verify_authzr_complete(self):
        """Verifies that all authorizations have been decided.

        :returns: Whether all authzr are complete
        :rtype: bool

        """
        for authzr in self.authzr.values():
            if (authzr.body.status != messages.STATUS_VALID
                    and authzr.body.status != messages.STATUS_INVALID):
                raise errors.AuthorizationError("Incomplete authorizations")
Example #3
0
    def _find_updated_challb(self, authzr, achall):  # pylint: disable=no-self-use
        """Find updated challenge body within Authorization Resource.

        .. warning:: This assumes only one instance of type of challenge in
            each challenge resource.

        :param .AuthorizationResource authzr: Authorization Resource
        :param .AnnotatedChallenge achall: Annotated challenge for which
            to get status

        """
        for authzr_challb in authzr.body.challenges:
            if type(authzr_challb.chall) is type(achall.challb.chall):  # noqa
                return authzr_challb
        raise errors.AuthorizationError(
            "Target challenge not found in authorization resource")
    def _get_chall_status(self, authzr, achall):  # pylint: disable=no-self-use
        """Get the status of the challenge.

        .. warning:: This assumes only one instance of type of challenge in
            each challenge resource.

        :param authzr: Authorization Resource
        :type authzr: :class:`acme.messages2.AuthorizationResource`

        :param achall: Annotated challenge for which to get status
        :type achall: :class:`letsencrypt.achallenges.AnnotatedChallenge`

        """
        for authzr_challb in authzr.body.challenges:
            if type(authzr_challb.chall) is type(achall.challb.chall):
                return authzr_challb.status
        raise errors.AuthorizationError(
            "Target challenge not found in authorization resource")
Example #5
0
    def get_authorizations(self, domains, best_effort=False):
        """Retrieve all authorizations for challenges.

        :param list domains: Domains for authorization
        :param bool best_effort: Whether or not all authorizations are
             required (this is useful in renewal)

        :returns: List of authorization resources
        :rtype: list

        :raises .AuthorizationError: If unable to retrieve all
            authorizations

        """
        for domain in domains:
            self.authzr[domain] = self.acme.request_domain_challenges(
                domain, self.account.regr.new_authzr_uri)

        self._choose_challenges(domains)

        # While there are still challenges remaining...
        while self.achalls:
            resp = self._solve_challenges()
            logger.info("Waiting for verification...")

            # Send all Responses - this modifies achalls
            self._respond(resp, best_effort)

        # Just make sure all decisions are complete.
        self.verify_authzr_complete()

        # Only return valid authorizations
        retVal = [
            authzr for authzr in self.authzr.values()
            if authzr.body.status == messages.STATUS_VALID
        ]

        if not retVal:
            raise errors.AuthorizationError(
                "Challenges failed for all domains")

        return retVal
Example #6
0
def _find_smart_path(challbs, preferences, combinations):
    """Find challenge path with server hints.

    Can be called if combinations is included. Function uses a simple
    ranking system to choose the combo with the lowest cost.

    """
    chall_cost = {}
    max_cost = 1
    for i, chall_cls in enumerate(preferences):
        chall_cost[chall_cls] = i
        max_cost += i

    # max_cost is now equal to sum(indices) + 1

    best_combo = []
    # Set above completing all of the available challenges
    best_combo_cost = max_cost

    combo_total = 0
    for combo in combinations:
        for challenge_index in combo:
            combo_total += chall_cost.get(
                challbs[challenge_index].chall.__class__, max_cost)

        if combo_total < best_combo_cost:
            best_combo = combo
            best_combo_cost = combo_total

        combo_total = 0

    if not best_combo:
        msg = ("Client does not support any combination of challenges that "
               "will satisfy the CA.")
        logger.fatal(msg)
        raise errors.AuthorizationError(msg)

    return best_combo
Example #7
0
def _report_no_chall_path():
    """Logs and raises an error that no satisfiable chall path exists."""
    msg = ("Client with the currently selected authenticator does not support "
           "any combination of challenges that will satisfy the CA.")
    logger.fatal(msg)
    raise errors.AuthorizationError(msg)