MINOR: acme: check if the account exist
This patch implements the retrival of the KID (account identifier) using the pkey. A request is sent to the newAccount URL using the onlyReturnExisting option, which allow to get the kid of an existing account. acme_jws_payload() implement a way to generate a JWS payload using the nonce, pkey and provided URI.
This commit is contained in:
parent
0aa6dedf72
commit
7f9bf4d5f7
@ -32,6 +32,7 @@ struct acme_cfg {
|
|||||||
enum acme_st {
|
enum acme_st {
|
||||||
ACME_RESSOURCES = 0,
|
ACME_RESSOURCES = 0,
|
||||||
ACME_NEWNONCE,
|
ACME_NEWNONCE,
|
||||||
|
ACME_CHKACCOUNT,
|
||||||
ACME_END
|
ACME_END
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -54,5 +55,6 @@ struct acme_ctx {
|
|||||||
struct ist newOrder;
|
struct ist newOrder;
|
||||||
} ressources;
|
} ressources;
|
||||||
struct ist nonce;
|
struct ist nonce;
|
||||||
|
struct ist kid;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
182
src/acme.c
182
src/acme.c
@ -502,7 +502,7 @@ static void acme_httpclient_end(struct httpclient *hc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int acme_http_req(struct task *task, struct acme_ctx *ctx, struct ist url, enum http_meth_t meth)
|
int acme_http_req(struct task *task, struct acme_ctx *ctx, struct ist url, enum http_meth_t meth, const struct http_hdr *hdrs, struct ist payload)
|
||||||
{
|
{
|
||||||
struct httpclient *hc;
|
struct httpclient *hc;
|
||||||
|
|
||||||
@ -510,7 +510,7 @@ int acme_http_req(struct task *task, struct acme_ctx *ctx, struct ist url, enum
|
|||||||
if (!hc)
|
if (!hc)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, IST_NULL) != ERR_NONE)
|
if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, hdrs, payload) != ERR_NONE)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
hc->ops.res_end = acme_httpclient_end;
|
hc->ops.res_end = acme_httpclient_end;
|
||||||
@ -529,6 +529,164 @@ error:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int acme_jws_payload(struct buffer *req, struct ist nonce, struct ist url, EVP_PKEY *pkey, struct buffer *output, char **errmsg)
|
||||||
|
{
|
||||||
|
struct buffer *b64payload = NULL;
|
||||||
|
struct buffer *b64prot = NULL;
|
||||||
|
struct buffer *b64sign = NULL;
|
||||||
|
struct buffer *jwk = NULL;
|
||||||
|
enum jwt_alg alg = JWS_ALG_NONE;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
|
||||||
|
if (req->data == 0) {
|
||||||
|
memprintf(errmsg, "no input data");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
b64payload = alloc_trash_chunk();
|
||||||
|
b64prot = alloc_trash_chunk();
|
||||||
|
jwk = alloc_trash_chunk();
|
||||||
|
b64sign = alloc_trash_chunk();
|
||||||
|
|
||||||
|
if (!b64payload || !b64prot || !jwk || !b64sign || !output) {
|
||||||
|
memprintf(errmsg, "out of memory");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
jwk->data = EVP_PKEY_to_pub_jwk(pkey, jwk->area, jwk->size);
|
||||||
|
alg = EVP_PKEY_to_jws_alg(pkey);
|
||||||
|
|
||||||
|
if (alg == JWS_ALG_NONE) {
|
||||||
|
memprintf(errmsg, "couldn't chose a JWK algorithm");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
b64payload->data = jws_b64_payload(req->area, b64payload->area, b64payload->size);
|
||||||
|
b64prot->data = jws_b64_protected(alg, NULL, jwk->area, nonce.ptr, url.ptr, b64prot->area, b64prot->size);
|
||||||
|
b64sign->data = jws_b64_signature(pkey, alg, b64prot->area, b64payload->area, b64sign->area, b64sign->size);
|
||||||
|
output->data = jws_flattened(b64prot->area, b64payload->area, b64sign->area, output->area, output->size);
|
||||||
|
|
||||||
|
if (output->data == 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free_trash_chunk(b64sign);
|
||||||
|
free_trash_chunk(jwk);
|
||||||
|
free_trash_chunk(b64prot);
|
||||||
|
free_trash_chunk(b64payload);
|
||||||
|
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int acme_req_account(struct task *task, struct acme_ctx *ctx, int newaccount, 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 }
|
||||||
|
};
|
||||||
|
char *accountreq = "{\n"
|
||||||
|
" \"termsOfServiceAgreed\": true,\n"
|
||||||
|
" \"onlyReturnExisting\": true\n"
|
||||||
|
"}\n";
|
||||||
|
char *newaccountreq = "{\n"
|
||||||
|
" \"termsOfServiceAgreed\": true,\n"
|
||||||
|
" \"contact\": [\n"
|
||||||
|
" \"mailto:%s\"\n"
|
||||||
|
" ]\n"
|
||||||
|
"}\n";
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
if ((req_in = alloc_trash_chunk()) == NULL)
|
||||||
|
goto error;
|
||||||
|
if ((req_out = alloc_trash_chunk()) == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (newaccount)
|
||||||
|
chunk_printf(req_in, newaccountreq, ctx->cfg->account.contact);
|
||||||
|
else
|
||||||
|
chunk_printf(req_in, "%s", accountreq);
|
||||||
|
|
||||||
|
if (acme_jws_payload(req_in, ctx->nonce, ctx->ressources.newAccount, ctx->cfg->account.pkey, req_out, errmsg) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (acme_http_req(task, ctx, ctx->ressources.newAccount, HTTP_METH_POST, hdrs, ist2(req_out->area, req_out->data)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
error:
|
||||||
|
memprintf(errmsg, "couldn't generate the newAccount request");
|
||||||
|
|
||||||
|
free_trash_chunk(req_in);
|
||||||
|
free_trash_chunk(req_out);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int acme_res_account(struct task *task, struct acme_ctx *ctx, 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("Location"))) {
|
||||||
|
istfree(&ctx->kid);
|
||||||
|
ctx->kid = istdup(hdr->v);
|
||||||
|
}
|
||||||
|
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||||
|
istfree(&ctx->nonce);
|
||||||
|
ctx->nonce = istdup(hdr->v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hc->res.status < 200 || hc->res.status >= 300) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* not an error, we only need to create a new account */
|
||||||
|
if (strcmp("urn:ietf:params:acme:error:accountDoesNotExist", t2->area) == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (t2->data && t1->data)
|
||||||
|
memprintf(errmsg, "invalid HTTP status code %d when getting Account 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 Account 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int acme_nonce(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
int acme_nonce(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||||
{
|
{
|
||||||
struct httpclient *hc;
|
struct httpclient *hc;
|
||||||
@ -645,7 +803,7 @@ struct task *acme_process(struct task *task, void *context, unsigned int state)
|
|||||||
switch (st) {
|
switch (st) {
|
||||||
case ACME_RESSOURCES:
|
case ACME_RESSOURCES:
|
||||||
if (http_st == ACME_HTTP_REQ) {
|
if (http_st == ACME_HTTP_REQ) {
|
||||||
if (acme_http_req(task, ctx, ist(ctx->cfg->uri), HTTP_METH_GET) != 0)
|
if (acme_http_req(task, ctx, ist(ctx->cfg->uri), HTTP_METH_GET, NULL, IST_NULL) != 0)
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,7 +819,7 @@ struct task *acme_process(struct task *task, void *context, unsigned int state)
|
|||||||
break;
|
break;
|
||||||
case ACME_NEWNONCE:
|
case ACME_NEWNONCE:
|
||||||
if (http_st == ACME_HTTP_REQ) {
|
if (http_st == ACME_HTTP_REQ) {
|
||||||
if (acme_http_req(task, ctx, ctx->ressources.newNonce, HTTP_METH_HEAD) != 0)
|
if (acme_http_req(task, ctx, ctx->ressources.newNonce, HTTP_METH_HEAD, NULL, IST_NULL) != 0)
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
if (http_st == ACME_HTTP_RES) {
|
if (http_st == ACME_HTTP_RES) {
|
||||||
@ -669,6 +827,22 @@ struct task *acme_process(struct task *task, void *context, unsigned int state)
|
|||||||
http_st = ACME_HTTP_REQ;
|
http_st = ACME_HTTP_REQ;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
st = ACME_CHKACCOUNT;
|
||||||
|
http_st = ACME_HTTP_REQ;
|
||||||
|
task_wakeup(task, TASK_WOKEN_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ACME_CHKACCOUNT:
|
||||||
|
if (http_st == ACME_HTTP_REQ) {
|
||||||
|
if (acme_req_account(task, ctx, 0, &errmsg) != 0)
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
if (http_st == ACME_HTTP_RES) {
|
||||||
|
if (acme_res_account(task, ctx, &errmsg) != 0) {
|
||||||
|
http_st = ACME_HTTP_REQ;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
st = ACME_END;
|
st = ACME_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user