MINOR: acme: send the request for challenge ready

This patch sends the "{}" message to specify that a challenge is ready.
It iterates on every challenge URL in the authorization list from the
acme_ctx.

This allows the ACME server to procede to the challenge validation.
https://www.rfc-editor.org/rfc/rfc8555#section-7.5.1
This commit is contained in:
William Lallemand 2025-04-11 16:44:50 +02:00
parent ae0bc88f91
commit 711a13a4b4
2 changed files with 109 additions and 0 deletions

View File

@ -36,6 +36,7 @@ enum acme_st {
ACME_NEWACCOUNT,
ACME_NEWORDER,
ACME_AUTH,
ACME_CHALLENGE,
ACME_END
};

View File

@ -578,6 +578,95 @@ error:
return ret;
}
/*
* Send the READY request for the challenge
*/
int acme_req_challenge(struct task *task, struct acme_ctx *ctx, struct acme_auth *auth, char **errmsg)
{
struct buffer *req_in = NULL;
struct buffer *req_out = NULL;
const struct http_hdr hdrs[] = {
{ IST("Content-Type"), IST("application/jose+json") },
{ IST_NULL, IST_NULL }
};
int ret = 1;
if ((req_in = alloc_trash_chunk()) == NULL)
goto error;
if ((req_out = alloc_trash_chunk()) == NULL)
goto error;
chunk_printf(req_in, "{}");
if (acme_jws_payload(req_in, ctx->nonce, auth->chall, ctx->cfg->account.pkey, ctx->kid, req_out, errmsg) != 0)
goto error;
if (acme_http_req(task, ctx, auth->chall, HTTP_METH_POST, hdrs, ist2(req_out->area, req_out->data)))
goto error;
ret = 0;
error:
memprintf(errmsg, "couldn't generate the Challenge request");
free_trash_chunk(req_in);
free_trash_chunk(req_out);
return ret;
}
/* parse the challenge URL response */
int acme_res_challenge(struct task *task, struct acme_ctx *ctx, struct acme_auth *auth, char **errmsg)
{
struct httpclient *hc;
struct http_hdr *hdrs, *hdr;
struct buffer *t1 = NULL, *t2 = NULL;
int ret = 1;
hc = ctx->hc;
if (!hc)
goto error;
if ((t1 = alloc_trash_chunk()) == NULL)
goto error;
if ((t2 = alloc_trash_chunk()) == NULL)
goto error;
hdrs = hc->res.hdrs;
for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v);
}
}
if (hc->res.status < 200 || hc->res.status >= 300) {
/* XXX: need a generic URN error parser */
if ((ret = mjson_get_string(hc->res.buf.area, hc->res.buf.data, "$.detail", t1->area, t1->size)) > -1)
t1->data = ret;
if ((ret = mjson_get_string(hc->res.buf.area, hc->res.buf.data, "$.type", t2->area, t2->size)) > -1)
t2->data = ret;
if (t2->data && t1->data)
memprintf(errmsg, "invalid HTTP status code %d when getting Challenge URL: \"%.*s\" (%.*s)", hc->res.status, (int)t1->data, t1->area, (int)t2->data, t2->area);
else
memprintf(errmsg, "invalid HTTP status code %d when getting Challenge URL", hc->res.status);
goto error;
}
out:
ret = 0;
error:
free_trash_chunk(t1);
free_trash_chunk(t2);
httpclient_destroy(hc);
ctx->hc = NULL;
return ret;
}
/*
* Get an Auth URL
*/
@ -1170,6 +1259,25 @@ struct task *acme_process(struct task *task, void *context, unsigned int state)
http_st = ACME_HTTP_REQ;
goto retry;
}
http_st = ACME_HTTP_REQ;
if ((ctx->next_auth = ctx->next_auth->next) == NULL) {
st = ACME_CHALLENGE;
ctx->next_auth = ctx->auths;
}
/* call with next auth or do the challenge step */
task_wakeup(task, TASK_WOKEN_MSG);
}
break;
case ACME_CHALLENGE:
if (http_st == ACME_HTTP_REQ) {
if (acme_req_challenge(task, ctx, ctx->next_auth, &errmsg) != 0)
goto retry;
}
if (http_st == ACME_HTTP_RES) {
if (acme_res_challenge(task, ctx, ctx->next_auth, &errmsg) != 0) {
http_st = ACME_HTTP_REQ;
goto retry;
}
if ((ctx->next_auth = ctx->next_auth->next) == NULL)
goto end;