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);