def _sat_incremental(self): # Exact responses don't matter, just path/response match if self.iteration == 0: # Only solve one of "0" required challs self.handler.responses["0"][1] = "onecomplete" self.handler.responses["0"][3] = None self.handler.responses["1"] = [None, None, "goodresp"] self.handler.paths["0"] = [1, 3] self.handler.paths["1"] = [2] # This is probably overkill... but set it anyway dv_c, c_c = self.handler._challenge_factory("0", [1, 3]) self.handler.dv_c["0"], self.handler.cont_c["0"] = dv_c, c_c dv_c, c_c = self.handler._challenge_factory("1", [2]) self.handler.dv_c["1"], self.handler.cont_c["1"] = dv_c, c_c self.iteration += 1 elif self.iteration == 1: # Quick check to make sure it was actually completed. self.assertEqual(self.mock_acme_auth.call_args_list, [mock.call("1")]) self.handler.responses["0"][1] = "now_finish" self.handler.responses["0"][3] = "finally!" else: raise errors.LetsEncryptAuthHandlerError( "Failed incremental test: too many invocations")
def get_authorizations(self): """Retrieve all authorizations for challenges. :raises LetsEncryptAuthHandlerError: If unable to retrieve all authorizations """ progress = True while self.msgs and progress: progress = False self._satisfy_challenges() delete_list = [] for dom in self.domains: if self._path_satisfied(dom): self.acme_authorization(dom) delete_list.append(dom) # This avoids modifying while iterating over the list if delete_list: self._cleanup_state(delete_list) progress = True if not progress: raise errors.LetsEncryptAuthHandlerError( "Unable to solve challenges for requested names.")
def _satisfy_challenges(self): """Attempt to satisfy all saved challenge messages. .. todo:: It might be worth it to try different challenges to find one that doesn't throw an exception .. todo:: separate into more functions """ logging.info("Performing the following challenges:") for dom in self.domains: self.paths[dom] = gen_challenge_path( self.msgs[dom].challenges, self._get_chall_pref(dom), self.msgs[dom].combinations) self.dv_c[dom], self.cont_c[dom] = self._challenge_factory( dom, self.paths[dom]) # Flatten challs for authenticator functions and remove index # Order is important here as we will not expose the outside # Authenticator to our own indices. flat_cont = [] flat_dv = [] for dom in self.domains: flat_cont.extend(ichall.achall for ichall in self.cont_c[dom]) flat_dv.extend(ichall.achall for ichall in self.dv_c[dom]) cont_resp = [] dv_resp = [] try: if flat_cont: cont_resp = self.cont_auth.perform(flat_cont) if flat_dv: dv_resp = self.dv_auth.perform(flat_dv) # This will catch both specific types of errors. except errors.LetsEncryptAuthHandlerError as err: logging.critical("Failure in setting up challenges:") logging.critical(str(err)) logging.info("Attempting to clean up outstanding challenges...") for dom in self.domains: self._cleanup_challenges(dom) raise errors.LetsEncryptAuthHandlerError( "Unable to perform challenges") logging.info("Ready for verification...") # Assemble Responses if cont_resp: self._assign_responses(cont_resp, self.cont_c) if dv_resp: self._assign_responses(dv_resp, self.dv_c)
def add_chall_msg(self, domain, msg, authkey): """Add a challenge message to the AuthHandler. :param str domain: domain for authorization :param msg: ACME "challenge" message :type msg: :class:`letsencrypt.acme.message.Challenge` :param authkey: authorized key for the challenge :type authkey: :class:`letsencrypt.client.le_util.Key` """ if domain in self.domains: raise errors.LetsEncryptAuthHandlerError( "Multiple ACMEChallengeMessages for the same domain " "is not supported.") self.domains.append(domain) self.responses[domain] = [None] * len(msg.challenges) self.msgs[domain] = msg self.authkey[domain] = authkey
def _find_smart_path(challs, 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(challs[ challenge_index].__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.") logging.fatal(msg) raise errors.LetsEncryptAuthHandlerError(msg) return best_combo