Index | Thread | Search

From:
Florian Obser <florian@openbsd.org>
Subject:
acme-client(1): handle badNonce
To:
tech <tech@openbsd.org>
Date:
Wed, 11 Jun 2025 20:34:29 +0200

Download raw body.

Thread
Found with pebble.

RFC 8555 6.5 has:

   When a server rejects a request because its nonce value was
   unacceptable (or not present), it MUST provide HTTP status code 400
   (Bad Request), and indicate the ACME error type
   "urn:ietf:params:acme:error:badNonce".  An error response with the
   "badNonce" error type MUST include a Replay-Nonce header field with a
   fresh nonce that the server will accept in a retry of the original
   query (and possibly in other requests, according to the server's
   nonce scoping policy).  On receiving such a response, a client SHOULD
   retry the request using the new nonce.
[...]
                                                          However, when
   retrying in response to a "badNonce" error, the client MUST use the
   nonce provided in the error response.

OK?

diff --git netproc.c netproc.c
index 00f24b530ad..11f57ab7088 100644
--- netproc.c
+++ netproc.c
@@ -260,6 +260,7 @@ sreq(struct conn *c, const char *addr, int kid, const char *req, char **loc)
 	struct httphead	*h;
 	ssize_t		 ssz;
 	long		 code;
+	int		 retry = 0;
 
 	if ((host = url2host(c->newnonce, &port, &path)) == NULL)
 		return -1;
@@ -288,6 +289,7 @@ sreq(struct conn *c, const char *addr, int kid, const char *req, char **loc)
 	}
 	http_get_free(g);
 
+ again:
 	/*
 	 * Send the url, nonce and request payload to the acctproc.
 	 * This will create the proper JSON object we need.
@@ -346,6 +348,48 @@ sreq(struct conn *c, const char *addr, int kid, const char *req, char **loc)
 	} else
 		memcpy(c->buf.buf, g->bodypart, c->buf.sz);
 
+	if (code == 400) {
+		struct jsmnn	*j;
+		char		*type;
+
+		j = json_parse(c->buf.buf, c->buf.sz);
+		if (j == NULL) {
+			code = -1;
+			goto out;
+		}
+
+		type = json_getstr(j, "type");
+		json_free(j);
+
+		if (type == NULL) {
+			code = -1;
+			goto out;
+		}
+
+		if (strcmp(type, "urn:ietf:params:acme:error:badNonce") != 0) {
+			free(type);
+			code = -1;
+			goto out;
+		}
+		free(type);
+
+		if (retry++ < RETRY_MAX) {
+			h = http_head_get("Replay-Nonce", g->head, g->headsz);
+			if (h == NULL) {
+				warnx("no replay nonce");
+				code = -1;
+				goto out;
+			} else  if ((nonce = strdup(h->val)) == NULL) {
+				warn("strdup");
+				code = -1;
+				goto out;
+			}
+			http_get_free(g);
+			warnx("GOTO AGAIN");
+			goto again;
+		}
+	}
+ out:
 	if (loc != NULL) {
 		free(*loc);
 		*loc = NULL;


-- 
In my defence, I have been left unsupervised.