Hi,

According to
https://datatracker.ietf.org/doc/draft-ietf-acme-acme/?include_text=1 ,
section 6.5:

        An ACME server provides nonces to clients using the HTTP Replay-Nonce
        header field, as specified in Section 6.5.1 below.  The server MUST
        include a Replay-Nonce header field in every successful response
        to a POST request and SHOULD provide it in error responses as well.

But in netproc.c the acme-client is assuming that it will get the
Reply-Nonce header even for GET requests. Which is the case at the
moment, but since the specification says only SHOULD, I don't think it's
correct to rely on that behavior.

And from the same section:

   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 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.

So I think correct approach is to send request with intentionally bad
nonce and use the Reply-Nonce value from that. Patch fixing the issue in
my fork (not applicable directly since I did switch to using libcurl but
to illustrate the idea):

diff --git a/netproc.c b/netproc.c
index 47ec3be..2f362f2 100644
--- a/netproc.c
+++ b/netproc.c
@@ -236,11 +236,29 @@ sreq(struct conn *c, const char *addr, const char *req)
        char *nonce = NULL;
        char *reqsn = NULL;

+       /*
+        * Since server is obligated to send Reply-Nonce only on successful
+        * requests or if the nonce is wrong, let's provide wrong value
+        * `foobar' and use Reply-Nonce from what we get back.
+        */
+       /* Send the nonce and request payload to the acctproc. */
+       if (writeop(c->fd, COMM_ACCT, ACCT_SIGN) <= 0) {
+               return -1;
+       } else if (writestr(c->fd, COMM_PAY, req) <= 0) {
+               return -1;
+       } else if (writestr(c->fd, COMM_NONCE, "foobar") <= 0) {
+               return -1;
+       }
+       /* Now read back the signed payload. */
+       if ((reqsn = readstr(c->fd, COMM_REQ)) == NULL)
+               return -1;
+
        curl = prepare_curl(addr, NULL);
        if (!curl) { return -1; }

        curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_nonce_cb);
        curl_easy_setopt(curl, CURLOPT_HEADERDATA, &nonce);
+       curl_easy_setopt(curl, CURLOPT_POSTFIELDS, reqsn);

        if (curl_easy_perform(curl) != CURLE_OK) {
                curl_easy_cleanup(curl);



Just sending the nounce as empty should probably work too based on
specification, but I like `foobar' :)

Have a nice day,
W.

-- 
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.

Attachment: signature.asc
Description: PGP signature

Reply via email to