crl_pending_resume(thread);
}
+/** Callback for worker receiving CRL-Expire packet from coordinator
+ */
+static void recv_crl_expire(UNUSED fr_coord_worker_t *cw, UNUSED fr_coord_pair_reg_t *coord_pair_reg,
+ fr_pair_list_t const *list, UNUSED fr_time_t now, module_ctx_t *mctx, UNUSED void *uctx)
+{
+ fr_pair_t *vp;
+ rlm_crl_thread_t *thread = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
+ crl_entry_t *crl_entry, find;
+
+ vp = fr_pair_find_by_da(list, NULL, attr_crl_cdp_url);
+ if (!vp) return;
+
+ find = (crl_entry_t) {
+ .cdp_url = vp->vp_strvalue
+ };
+
+ crl_entry = fr_rb_find(&thread->crls, &find);
+
+ if (!crl_entry) return;
+
+ /*
+ * If the expired CRL has any deltas, remove them as well.
+ */
+ fr_value_box_list_foreach(&crl_entry->delta_urls, delta) {
+ crl_entry_t *delta_entry;
+
+ find.cdp_url = delta->vb_strvalue;
+ delta_entry = fr_rb_find(&thread->crls, &find);
+ if (!delta_entry) continue;
+
+ WARN("Delta CRL %s expired", delta_entry->cdp_url);
+ fr_rb_remove(&thread->crls, delta_entry);
+ talloc_free(delta_entry);
+ }
+
+ WARN("CRL %s expired", crl_entry->cdp_url);
+ fr_rb_remove(&thread->crls, crl_entry);
+ talloc_free(crl_entry);
+}
+
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
{
rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
static fr_coord_worker_pair_cb_reg_t worker_pair_callbacks[] = {
{ .packet_type = FR_CRL_FETCH_OK, .callback = recv_crl_ok },
{ .packet_type = FR_CRL_FETCH_FAIL, .callback = recv_crl_fail },
+ { .packet_type = FR_CRL_CRL_EXPIRE, .callback = recv_crl_expire },
FR_COORD_CALLBACK_TERMINATOR
};
#endif
RESUME_FLAG(send_crl_fail, UNUSED,)
{
process_crl_t *inst = talloc_get_type_abort(mctx->mi->data, process_crl_t);
+ process_thread_crl_t *thread = talloc_get_type_abort(mctx->thread, process_thread_crl_t);
process_crl_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_crl_rctx_t);
fr_pair_t *vp;
- /*
- * Refresh requests are local to the coordinator, so there
- * is nothing to send back on a failure.
- */
- if (rctx->refresh) return UNLANG_ACTION_CALCULATE_RESULT;
+ if (rctx->refresh) {
+ crl_fetching_entry_remove(&inst->mutable->fetching, rctx->cdp_url);
+
+ /*
+ * Set up retry of failed refresh.
+ */
+ RDEBUG2("Refresh of CRL from %s will retry in %pVs", rctx->cdp_url, fr_box_time_delta(inst->retry_delay));
+
+ if (fr_timer_in(rctx->crl_entry, thread->el->tl, &rctx->crl_entry->ev, inst->retry_delay,
+ false, crl_refresh_event, rctx->crl_entry) <0) {
+ RERROR("Failed to set timer to retry CRL fetch");
+ }
+
+ /*
+ * Refresh requests are local to the coordinator, so there
+ * is nothing to send back on a failure, unless the CRL has
+ * expired and expired CRLs are not allowed.
+ */
+ if (inst->allow_expired || fr_time_gt(fr_time_from_sec(rctx->crl_entry->next_update),
+ fr_time_from_sec(time(NULL)))) {
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
+
+ /*
+ * A refresh failed on an expired CRL, notify all workers with a CRL-Expire reply.
+ */
+ fr_pair_list_free(&request->reply_pairs);
+ if (fr_pair_append_by_da(request->reply_ctx, &vp, &request->reply_pairs,
+ attr_packet_type) < 0) RETURN_UNLANG_FAIL;
+ vp->vp_uint32 = FR_CRL_CRL_EXPIRE;
+
+ if (fr_pair_append_by_da(request->reply_ctx, &vp, &request->reply_pairs,
+ attr_crl_cdp_url) < 0) RETURN_UNLANG_FAIL;
+ fr_value_box_strdup(vp, &vp->data, NULL, rctx->cdp_url, false);
+
+ fr_coord_to_worker_reply_broadcast(request);
+
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
/*
* The fetch failed, ensure we don't send CRL data back