From: Florian Obser Subject: acme-client(1): handle badNonce To: tech Date: Wed, 11 Jun 2025 20:34:29 +0200 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.