kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit 4cace25330f74057fe2e1b644970a6934108ca4d
parent cb0f5a4137affc9c4fd2779c2e8443687ef0259b
Author: Joris Vink <joris@coders.se>
Date:   Tue,  5 Jan 2021 22:01:36 +0100

Acme protocol updates.

- Adds POST-as-GET for all GET requests that would support it.

Diffstat:
src/acme.c | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 127 insertions(+), 42 deletions(-)

diff --git a/src/acme.c b/src/acme.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Joris Vink <joris@coders.se> + * Copyright (c) 2019-2021 Joris Vink <joris@coders.se> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -136,6 +136,7 @@ struct acme_sign_op { struct acme_auth { char *url; + struct acme_order *order; int status; struct acme_challenge *challenge; LIST_ENTRY(acme_auth) list; @@ -143,6 +144,12 @@ struct acme_auth { #define ACME_ORDER_STATE_RUNNING 1 #define ACME_ORDER_STATE_ERROR 2 +#define ACME_ORDER_STATE_CANCELLED 3 +#define ACME_ORDER_STATE_UPDATE 4 +#define ACME_ORDER_STATE_UPDATE_AUTH 5 +#define ACME_ORDER_STATE_WAITING 6 +#define ACME_ORDER_STATE_FETCH_CERT 7 +#define ACME_ORDER_STATE_COMPLETE 8 #define ACME_ORDER_TICK 1000 #define ACME_ORDER_TIMEOUT 60000 @@ -152,6 +159,7 @@ struct acme_order { int state; int status; int flags; + int auths; u_int64_t start; char *id; char *final; @@ -217,8 +225,12 @@ static void acme_account_reg_submit(struct acme_sign_op *, static void acme_order_retry(const char *); static void acme_order_process(void *, u_int64_t); static void acme_order_update(struct acme_order *); +static void acme_order_update_submit(struct acme_sign_op *, + struct kore_buf *); static void acme_order_request_csr(struct acme_order *); -static int acme_order_fetch_certificate(struct acme_order *); +static void acme_order_fetch_certificate(struct acme_order *); +static void acme_order_fetch_certificate_submit(struct acme_sign_op *, + struct kore_buf *); static void acme_order_create(struct kore_msg *, const void *); static void acme_order_remove(struct acme_order *, const char *); static void acme_order_csr_response(struct kore_msg *, const void *); @@ -229,8 +241,10 @@ static void acme_order_auth_log_error(struct acme_order *); static void acme_order_auth_deactivate(struct acme_order *); static int acme_order_auth_process(struct acme_order *, struct acme_auth *); -static int acme_order_auth_update(struct acme_order *, +static void acme_order_auth_update(struct acme_order *, struct acme_auth *); +static void acme_order_auth_update_submit(struct acme_sign_op *, + struct kore_buf *); static int acme_challenge_tls_alpn_01(struct acme_order *, struct acme_challenge *); @@ -616,8 +630,8 @@ acme_order_create_submit(struct acme_sign_op *op, struct kore_buf *payload) struct kore_json json; int stval; const u_int8_t *body; - struct acme_order *order; struct acme_auth *auth; + struct acme_order *order; const char *header; const char *domain; struct kore_json_item *item, *array, *final, *status; @@ -701,7 +715,7 @@ acme_order_create_submit(struct acme_sign_op *op, struct kore_buf *payload) order->start = kore_time_ms(); order->id = kore_strdup(header); order->domain = kore_strdup(domain); - order->state = ACME_ORDER_STATE_RUNNING; + order->state = ACME_ORDER_STATE_UPDATE; order->final = kore_strdup(final->data.string); kore_timer_add(acme_order_process, ACME_ORDER_TICK, @@ -712,6 +726,7 @@ acme_order_create_submit(struct acme_sign_op *op, struct kore_buf *payload) continue; auth = kore_calloc(1, sizeof(*auth)); + auth->order = order; auth->url = kore_strdup(item->data.string); LIST_INSERT_HEAD(&order->auth, auth, list); } @@ -730,14 +745,25 @@ cleanup: static void acme_order_update(struct acme_order *order) { + acme_sign_submit(NULL, order->id, order, acme_order_update_submit); +} + +static void +acme_order_update_submit(struct acme_sign_op *op, struct kore_buf *payload) +{ struct acme_request req; size_t len; struct kore_json json; + struct acme_order *order; const u_int8_t *body; int stval, ret; struct kore_json_item *status, *cert; - acme_request_prepare(&req, HTTP_METHOD_GET, order->id, NULL, 0); + order = op->udata; + op->udata = NULL; + + acme_request_prepare(&req, HTTP_METHOD_POST, order->id, + payload->data, payload->offset); if (!acme_request_run(&req)) { acme_request_cleanup(&req); @@ -798,6 +824,8 @@ acme_order_update(struct acme_order *order) cleanup: if (ret == KORE_RESULT_ERROR) order->state = ACME_ORDER_STATE_ERROR; + else + order->state = ACME_ORDER_STATE_UPDATE_AUTH; kore_json_cleanup(&json); acme_request_cleanup(&req); @@ -821,21 +849,19 @@ acme_order_retry(const char *domain) KORE_ACME_ORDER_FAILED); } +/* + * Process an order, step by step. + * + * This callback is called every second to check on an active order. + * It will first update the order if required, and updated any of its + * active awuthoritizations to get the latest data. + */ static void acme_order_process(void *udata, u_int64_t now) { struct acme_auth *auth; struct acme_order *order = udata; - acme_order_update(order); - - LIST_FOREACH(auth, &order->auth, list) { - if (!acme_order_auth_update(order, auth)) { - acme_order_remove(order, "cancelled"); - return; - } - } - if ((now - order->start) >= ACME_ORDER_TIMEOUT) { acme_order_auth_deactivate(order); acme_order_remove(order, "order ran too long"); @@ -843,6 +869,32 @@ acme_order_process(void *udata, u_int64_t now) } switch (order->state) { + case ACME_ORDER_STATE_WAITING: + break; + case ACME_ORDER_STATE_UPDATE: + acme_order_update(order); + order->state = ACME_ORDER_STATE_WAITING; + break; + case ACME_ORDER_STATE_UPDATE_AUTH: + order->auths = 0; + LIST_FOREACH(auth, &order->auth, list) { + acme_order_auth_update(order, auth); + order->auths++; + } + order->state = ACME_ORDER_STATE_WAITING; + break; + case ACME_ORDER_STATE_CANCELLED: + acme_order_remove(order, "cancelled"); + order = NULL; + break; + case ACME_ORDER_STATE_COMPLETE: + acme_order_remove(order, "completed"); + order = NULL; + break; + case ACME_ORDER_STATE_FETCH_CERT: + acme_order_fetch_certificate(order); + order->state = ACME_ORDER_STATE_WAITING; + break; case ACME_ORDER_STATE_RUNNING: switch (order->status) { case ACME_STATUS_PENDING: @@ -862,13 +914,7 @@ acme_order_process(void *udata, u_int64_t now) case ACME_STATUS_VALID: kore_log(LOG_INFO, "[%s] certificate available", order->domain); - if (!acme_order_fetch_certificate(order)) { - acme_order_remove(order, - "failed to fetch certificate"); - } else { - acme_order_remove(order, "completed"); - } - order = NULL; + order->state = ACME_ORDER_STATE_FETCH_CERT; break; case ACME_STATUS_INVALID: kore_log(LOG_INFO, "[%s] order authorization failed", @@ -894,6 +940,10 @@ acme_order_process(void *udata, u_int64_t now) } if (order != NULL) { + /* Do not go back to update if we are ready for the cert. */ + if (order->state != ACME_ORDER_STATE_FETCH_CERT) + order->state = ACME_ORDER_STATE_UPDATE; + kore_timer_add(acme_order_process, ACME_ORDER_TICK, order, KORE_TIMER_ONESHOT); } @@ -933,21 +983,32 @@ acme_order_remove(struct acme_order *order, const char *reason) kore_free(order); } -static int +static void acme_order_fetch_certificate(struct acme_order *order) { + acme_sign_submit(NULL, order->certloc, order, + acme_order_fetch_certificate_submit); +} + +static void +acme_order_fetch_certificate_submit(struct acme_sign_op *op, + struct kore_buf *payload) +{ struct acme_request req; size_t len; const u_int8_t *body; + struct acme_order *order; - if (order->certloc == NULL) - return (KORE_RESULT_ERROR); + order = op->udata; + op->udata = NULL; - acme_request_prepare(&req, HTTP_METHOD_GET, order->certloc, NULL, 0); + acme_request_prepare(&req, HTTP_METHOD_POST, order->certloc, + payload->data, payload->offset); if (!acme_request_run(&req)) { acme_request_cleanup(&req); - return (KORE_RESULT_ERROR); + order->state = ACME_ORDER_STATE_CANCELLED; + return; } if (req.curl.http.status != HTTP_STATUS_OK) { @@ -955,7 +1016,8 @@ acme_order_fetch_certificate(struct acme_order *order) "[%s] request to '%s' failed: got %ld - expected 200", order->domain, order->certloc, req.curl.http.status); acme_request_cleanup(&req); - return (KORE_RESULT_ERROR); + order->state = ACME_ORDER_STATE_CANCELLED; + return; } kore_curl_response_as_bytes(&req.curl, &body, &len); @@ -964,8 +1026,7 @@ acme_order_fetch_certificate(struct acme_order *order) acme_keymgr_key_req(order->domain, body, len, KORE_ACME_INSTALL_CERT); acme_request_cleanup(&req); - - return (KORE_RESULT_OK); + order->state = ACME_ORDER_STATE_COMPLETE; } static void @@ -1097,33 +1158,46 @@ acme_order_auth_process(struct acme_order *order, struct acme_auth *auth) return (ret); } -static int +static void acme_order_auth_update(struct acme_order *order, struct acme_auth *auth) { + acme_sign_submit(NULL, auth->url, auth, acme_order_auth_update_submit); +} + +static void +acme_order_auth_update_submit(struct acme_sign_op *op, struct kore_buf *payload) +{ const char *p; struct acme_request req; size_t len; struct kore_json json; const u_int8_t *body; - int ret, stval; + struct acme_auth *auth; + struct acme_order *order; struct acme_challenge *challenge; + int ret, stval; struct kore_json_item *status, *type, *url, *token; struct kore_json_item *array, *object, *err, *detail; ret = KORE_RESULT_ERROR; - acme_request_prepare(&req, HTTP_METHOD_GET, auth->url, NULL, 0); + memset(&json, 0, sizeof(json)); - if (!acme_request_run(&req)) { - acme_request_cleanup(&req); - return (ret); - } + auth = op->udata; + order = auth->order; + + op->udata = NULL; + + acme_request_prepare(&req, HTTP_METHOD_POST, auth->url, + payload->data, payload->offset); + + if (!acme_request_run(&req)) + goto cleanup; if (req.curl.http.status != HTTP_STATUS_OK) { kore_log(LOG_NOTICE, "[%s:auth] request to '%s' failed: got %ld - expected 200", order->domain, auth->url, req.curl.http.status); - acme_request_cleanup(&req); - return (ret); + goto cleanup; } kore_curl_response_as_bytes(&req.curl, &body, &len); @@ -1270,10 +1344,19 @@ acme_order_auth_update(struct acme_order *order, struct acme_auth *auth) ret = KORE_RESULT_OK; cleanup: + if (ret != KORE_RESULT_OK) { + order->state = ACME_ORDER_STATE_CANCELLED; + } else { + order->auths--; + if (order->auths == 0) { + kore_log(LOG_NOTICE, + "[%s:auth] authentications done", order->domain); + order->state = ACME_ORDER_STATE_RUNNING; + } + } + kore_json_cleanup(&json); acme_request_cleanup(&req); - - return (ret); } static int @@ -1500,7 +1583,9 @@ acme_sign_submit(struct kore_json_item *json, const char *url, void *udata, } kore_buf_init(&buf, 1024); - kore_json_item_tobuf(json, &buf); + + if (json != NULL) + kore_json_item_tobuf(json, &buf); op = kore_calloc(1, sizeof(*op)); LIST_INSERT_HEAD(&signops, op, list);