From: Florian Obser Subject: Re: acme-client: support for external account binding To: Jonathan Matthew Cc: tech@openbsd.org, sthen@openbsd.org Date: Sat, 02 May 2026 19:09:37 +0200 On 2026-05-01 14:09 +10, Jonathan Matthew wrote: > The ACME protocol includes a scheme allowing a client to bind an ACME > account key with a account in some non-ACME ("external") system run by > the CA. This is described in section 7.3.4 of RFC 8555. In short, the > CA gives you a key out-of-band and your ACME client HMACs your account > details with that key and sends that to the ACME server. I know some of these words! My Joo Janta 200 conveniently turned black when looking at the acctproc changes. That stuff is certainly over my head. In the past tb@ pointed out the errors of my ways, maybe we can trick him into reviewing those bits. > > The CA we use at work does ACME, but it requires this external account > binding before it will do anything, so I've implemented support for it in > acme-client in the diff below. > > Since it's only part of the new account process, when you're registering your > account key with the ACME service, I've made this a command line argument > rather than something you put in the config file. It's something you'd do > during initial setup of a new system, so I'm not really concerned about > exposing the key details to untrusted users on the system. Other clients > don't seem to be either. > > While building the new account request, the net process asks the account > process to generate the binding JWS using the CA-provided key, includes > that in the request, and then asks the account process to sign the whole > thing. Seems a bit fiddly but, to my mind, better than having the account > process add it into the new account request itself. > > Sadly this adds yet another base64 decoder implementation, because there > wasn't one using the base64url encoding that acme-client could get to. > This one is based on the libc bcrypt implementation, because it's short and > uncomplicated and doesn't care about padding. > > I've tested this against pebble and digicert's acme service, and > the existing regress tests pass. I can also add regress test coverage > for this feature later. That would be neat. > > ok? > Two more things inline... > Index: acctproc.c > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/acctproc.c,v > diff -u -p -u -p -r1.32 acctproc.c > --- acctproc.c 29 Aug 2023 14:44:53 -0000 1.32 > +++ acctproc.c 30 Apr 2026 05:10:56 -0000 > @@ -28,6 +28,7 @@ > #include > #include > #include > +#include > #include > #include > > @@ -182,18 +183,13 @@ out: > } > > static int > -op_sign_rsa(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url) > +op_sign_rsa(char **jwk, EVP_PKEY *pkey) > { > char *exp = NULL, *mod = NULL; > int rc = 0; > RSA *r; > > - *prot = NULL; > - > - /* > - * First, extract relevant portions of our private key. > - * Finally, format the header combined with the nonce. > - */ > + *jwk = NULL; > > if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL) > warnx("EVP_PKEY_get0_RSA"); > @@ -201,8 +197,8 @@ op_sign_rsa(char **prot, EVP_PKEY *pkey, > warnx("bn2string"); > else if ((exp = bn2string(RSA_get0_e(r))) == NULL) > warnx("bn2string"); > - else if ((*prot = json_fmt_protected_rsa(exp, mod, nonce, url)) == NULL) > - warnx("json_fmt_protected_rsa"); > + else if ((*jwk = json_fmt_jwk_rsa(exp, mod)) == NULL) > + warnx("json_fmt_jwk_rsa"); > else > rc = 1; > > @@ -212,14 +208,14 @@ op_sign_rsa(char **prot, EVP_PKEY *pkey, > } > > static int > -op_sign_ec(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url) > +op_sign_ec(char **jwk, EVP_PKEY *pkey) > { > BIGNUM *X = NULL, *Y = NULL; > EC_KEY *ec = NULL; > char *x = NULL, *y = NULL; > int rc = 0; > > - *prot = NULL; > + *jwk = NULL; > > if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) > warnx("EVP_PKEY_get0_EC_KEY"); > @@ -234,8 +230,8 @@ op_sign_ec(char **prot, EVP_PKEY *pkey, > warnx("bn2string"); > else if ((y = bn2string(Y)) == NULL) > warnx("bn2string"); > - else if ((*prot = json_fmt_protected_ec(x, y, nonce, url)) == NULL) > - warnx("json_fmt_protected_ec"); > + else if ((*jwk = json_fmt_jwk_ec(x, y)) == NULL) > + warnx("json_fmt_jwk_ec"); > else > rc = 1; > > @@ -262,6 +258,7 @@ op_sign(int fd, EVP_PKEY *pkey, enum acc > char *prot = NULL, *prot64 = NULL; > char *sign = NULL, *dig64 = NULL, *fin = NULL; > char *url = NULL, *kid = NULL, *alg = NULL; > + char *jwk = NULL; > const unsigned char *digp; > unsigned char *dig = NULL, *buf = NULL; > size_t digsz; > @@ -309,17 +306,21 @@ op_sign(int fd, EVP_PKEY *pkey, enum acc > } else { > switch (EVP_PKEY_base_id(pkey)) { > case EVP_PKEY_RSA: > - if (!op_sign_rsa(&prot, pkey, nonce, url)) > + if (!op_sign_rsa(&jwk, pkey)) > goto out; > break; > case EVP_PKEY_EC: > - if (!op_sign_ec(&prot, pkey, nonce, url)) > + if (!op_sign_ec(&jwk, pkey)) > goto out; > break; > default: > warnx("EVP_PKEY_base_id"); > goto out; > } > + > + prot = json_fmt_protected_jwk(alg, jwk, nonce, url); > + if (prot == NULL) > + goto out; > } > > /* The header combined with the nonce, base64. */ > @@ -434,6 +435,7 @@ out: > free(pay64); > free(url); > free(nonce); > + free(jwk); > free(kid); > free(prot); > free(prot64); > @@ -444,8 +446,94 @@ out: > return rc; > } > > +static int > +op_eab(int fd, EVP_PKEY *pkey, const char *eab_kid, const unsigned char *eab_key, > + int eab_key_len) > +{ > + char *url = NULL; > + char *prot = NULL, *prot64 = NULL; > + char *jwk = NULL, *pay64 = NULL; > + char *sign = NULL; > + char *dig64 = NULL, *fin = NULL; > + unsigned char *dig = NULL; > + unsigned int digsz; > + int sign_len, rc = 0; > + > + if ((url = readstr(fd, COMM_URL)) == NULL) > + goto out; > + > + /* protected component: algorithm, EAB key ID, new account URL */ > + if ((prot = json_fmt_protected_eab(eab_kid, url)) == NULL) { > + warnx("json_fmt_protected_eab"); > + goto out; > + } > + > + if ((prot64 = base64buf_url(prot, strlen(prot))) == NULL) { > + warnx("base64buf_url"); > + goto out; > + } > + > + /* payload component: account public key */ > + switch (EVP_PKEY_base_id(pkey)) { > + case EVP_PKEY_RSA: > + if (!op_sign_rsa(&jwk, pkey)) > + goto out; > + break; > + case EVP_PKEY_EC: > + if (!op_sign_ec(&jwk, pkey)) > + goto out; > + break; > + default: > + warnx("EVP_PKEY_base_id"); > + goto out; > + } > + > + if ((pay64 = base64buf_url(jwk, strlen(jwk))) == NULL) { > + warnx("base64buf_url"); > + goto out; > + } > + > + sign_len = asprintf(&sign, "%s.%s", prot64, pay64); > + if (sign_len == -1) { > + warn("asprintf"); > + sign = NULL; > + goto out; > + } > + > + /* sign with the EAB key */ > + dig = malloc(eab_key_len); > + HMAC(EVP_sha256(), eab_key, eab_key_len, sign, sign_len, dig, &digsz); > + > + if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) { > + warnx("base64buf_url"); > + goto out; > + } > + > + if ((fin = json_fmt_signed(prot64, pay64, dig64)) == NULL) { > + warnx("json_fmt_signed"); > + goto out; > + } else if (writestr(fd, COMM_REQ, fin) < 0) { > + goto out; > + } > + > + rc = 1; > +out: > + > + free(fin); > + free(dig64); > + free(dig); > + free(sign); > + free(pay64); > + free(jwk); > + free(prot64); > + free(prot); > + free(url); > + return rc; > +} > + > int > -acctproc(int netsock, const char *acctkey, enum keytype keytype) > +acctproc(int netsock, const char *acctkey, enum keytype keytype, > + const char *eab_kid, const unsigned char *eab_key, int eab_key_len) > { > FILE *f = NULL; > EVP_PKEY *pkey = NULL; > @@ -522,7 +610,7 @@ acctproc(int netsock, const char *acctke > if ((lval = readop(netsock, COMM_ACCT)) == 0) > op = ACCT_STOP; > else if (lval == ACCT_SIGN || lval == ACCT_KID_SIGN || > - lval == ACCT_THUMBPRINT) > + lval == ACCT_THUMBPRINT || lval == ACCT_EAB) > op = lval; > > if (ACCT__MAX == op) { > @@ -542,6 +630,12 @@ acctproc(int netsock, const char *acctke > if (op_thumbprint(netsock, pkey)) > break; > warnx("op_thumbprint"); > + goto out; > + case ACCT_EAB: > + if (op_eab(netsock, pkey, eab_kid, eab_key, > + eab_key_len)) > + break; > + warnx("op_eab"); > goto out; > default: > abort(); > Index: acme-client.1 > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/acme-client.1,v > diff -u -p -u -p -r1.43 acme-client.1 > --- acme-client.1 18 Sep 2025 14:13:27 -0000 1.43 > +++ acme-client.1 30 Apr 2026 05:10:56 -0000 > @@ -67,10 +67,25 @@ location "/.well-known/acme-challenge/*" > } > .Ed > .Pp > +Some signing authorities require the client to perform external account > +binding to associate the ACME account key generated by > +.Nm > +with an existing account in a non-ACME system such as a CA customer database. > +To enable ACME account binding, the CA operating the ACME server needs to provide > +the ACME client with a MAC key and a key identifier, using some mechanism outside > +of ACME. > +These can be supplied to > +.Nm > +using the > +.Fl e > +command line argument. > +.Pp > The options are as follows: > .Bl -tag -width Ds > .It Fl F > Force certificate renewal, regardless of remaining lifetime. > +.It Fl e Ar key-id:key > +Specify MAC key and key identifier for external account binding. could you be more explicit how a key-id and key actually look like? > .It Fl f Ar configfile > Specify an alternative configuration file. > .It Fl n > Index: base64.c > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/base64.c,v > diff -u -p -u -p -r1.9 base64.c > --- base64.c 24 Jan 2017 13:32:55 -0000 1.9 > +++ base64.c 30 Apr 2026 05:10:56 -0000 > @@ -19,6 +19,7 @@ > #include > > #include > +#include > > #include "extern.h" > > @@ -64,4 +65,70 @@ base64buf_url(const char *data, size_t l > } > > return buf; > +} > + > +static const u_int8_t index_64[128] = { > + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, > + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, > + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, > + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, > + 255, 255, 255, 255, 255, 62, 255, 255, 52, 53, > + 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, > + 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, > + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, > + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, > + 25, 255, 255, 255, 255, 63, 255, 26, 27, 28, > + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, > + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, > + 49, 50, 51, 255, 255, 255, 255, 255 > +}; > +#define CHAR64(c) ( (c) > 127 ? 255 : index_64[(c)]) > + > +int > +unbase64buf_url(const unsigned char *data, unsigned char **decoded) > +{ > + unsigned char *out, *bp; > + const unsigned char *p; > + unsigned char c1, c2, c3, c4; > + int len; > + > + len = strlen(data); > + out = malloc(len); > + if (out == NULL) > + return -1; > + > + p = data; > + bp = out; > + while (p < data + len) { > + c1 = CHAR64(*p); > + /* Invalid data */ > + if (c1 == 255) > + return -1; > + > + c2 = CHAR64(*(p + 1)); > + if (c2 == 255) > + return -1; > + > + *bp++ = (c1 << 2) | ((c2 & 0x30) >> 4); > + if ((p + 2) >= data + len) > + break; > + > + c3 = CHAR64(*(p + 2)); > + if (c3 == 255) > + return -1; > + > + *bp++ = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2); > + if ((p + 3) >= data + len) > + break; > + > + c4 = CHAR64(*(p + 3)); > + if (c4 == 255) > + return -1; > + *bp++ = ((c3 & 0x03) << 6) | c4; > + > + p += 4; > + } > + > + *decoded = out; > + return (bp - out); > } > Index: extern.h > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/extern.h,v > diff -u -p -u -p -r1.23 extern.h > --- extern.h 23 Feb 2026 10:27:49 -0000 1.23 > +++ extern.h 30 Apr 2026 05:10:56 -0000 > @@ -34,6 +34,7 @@ enum acctop { > ACCT_SIGN, > ACCT_KID_SIGN, > ACCT_THUMBPRINT, > + ACCT_EAB, > ACCT__MAX > }; > > @@ -200,7 +201,8 @@ __BEGIN_DECLS > * Start with our components. > * These are all isolated and talk to each other using sockets. > */ > -int acctproc(int, const char *, enum keytype); > +int acctproc(int, const char *, enum keytype, const char *, > + const unsigned char *, int); > int certproc(int, int); > int chngproc(int, const char *); > int dnsproc(int); > @@ -209,7 +211,7 @@ int fileproc(int, const char *, const > const char *); > int keyproc(int, struct domain_c *); > int netproc(int, int, int, int, int, int, int, > - struct authority_c *, struct domain_c *); > + struct authority_c *, struct domain_c *, int); > > /* > * Debugging functions. > @@ -240,6 +242,7 @@ int checkexit_ext(int *, pid_t, enum c > */ > size_t base64len(size_t); > char *base64buf_url(const char *, size_t); > +int unbase64buf_url(const unsigned char *, unsigned char **); > > /* > * JSON parsing routines. > @@ -259,18 +262,19 @@ char *json_getstr(struct jsmnn *, const > > char *json_fmt_newcert(const char *); > char *json_fmt_chkacc(void); > -char *json_fmt_newacc(const char *); > +char *json_fmt_newacc(const char *, const char *); > char *json_fmt_neworder(struct domain_c *); > -char *json_fmt_protected_rsa(const char *, > - const char *, const char *, const char *); > -char *json_fmt_protected_ec(const char *, const char *, const char *, > - const char *); > -char *json_fmt_protected_kid(const char*, const char *, const char *, > - const char *); > +char *json_fmt_jwk_rsa(const char *, const char *); > +char *json_fmt_jwk_ec(const char *, const char *); > +char *json_fmt_protected_jwk(const char *, const char *, > + const char *, const char *); > +char *json_fmt_protected_kid(const char *, const char *, > + const char *, const char *); > char *json_fmt_revokecert(const char *); > char *json_fmt_thumb_rsa(const char *, const char *); > char *json_fmt_thumb_ec(const char *, const char *); > char *json_fmt_signed(const char *, const char *, const char *); > +char *json_fmt_protected_eab(const char *, const char *); > > /* > * Should we print debugging messages? > Index: json.c > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/json.c,v > diff -u -p -u -p -r1.23 json.c > --- json.c 23 Feb 2026 10:27:49 -0000 1.23 > +++ json.c 30 Apr 2026 05:10:56 -0000 > @@ -618,7 +618,7 @@ json_fmt_chkacc(void) > * Format the "newAccount" resource request. > */ > char * > -json_fmt_newacc(const char *contact) > +json_fmt_newacc(const char *contact, const char *eab) > { > int c; > char *p, *cnt = NULL; > @@ -630,6 +630,14 @@ json_fmt_newacc(const char *contact) > return NULL; > } > } > + if (eab != NULL) { > + char *ecnt = NULL; > + c = asprintf(&ecnt, "%s\"externalAccountBinding\": %s, ", > + cnt == NULL ? "" : cnt, eab); > + > + free(cnt); > + cnt = ecnt; > + } > > c = asprintf(&p, "{" > "%s" > @@ -737,23 +745,35 @@ json_fmt_newcert(const char *cert) > } > > /* > - * Protected component of json_fmt_signed(). > + * Format an RSA public key in JWK format. > */ > char * > -json_fmt_protected_rsa(const char *exp, const char *mod, const char *nce, > - const char *url) > +json_fmt_jwk_rsa(const char *exp, const char *mod) > { > int c; > char *p; > > - c = asprintf(&p, "{" > - "\"alg\": \"RS256\", " > - "\"jwk\": " > - "{\"e\": \"%s\", \"kty\": \"RSA\", \"n\": \"%s\"}, " > - "\"nonce\": \"%s\", " > - "\"url\": \"%s\"" > - "}", > - exp, mod, nce, url); > + c = asprintf(&p, "{\"e\": \"%s\", \"kty\": \"RSA\", \"n\": \"%s\"}", > + exp, mod); > + if (c == -1) { > + warn("asprintf"); > + p = NULL; > + } > + return p; > +} > + > +/* > + * Format an EC public key in JWK format. > + */ > +char * > +json_fmt_jwk_ec(const char *x, const char *y) > +{ > + int c; > + char *p; > + > + c = asprintf(&p, "{\"crv\": \"P-384\", \"kty\": \"EC\"," > + " \"x\": \"%s\", \"y\": \"%s\"}", > + x, y); > if (c == -1) { > warn("asprintf"); > p = NULL; > @@ -765,19 +785,19 @@ json_fmt_protected_rsa(const char *exp, > * Protected component of json_fmt_signed(). > */ > char * > -json_fmt_protected_ec(const char *x, const char *y, const char *nce, > +json_fmt_protected_jwk(const char *alg, const char *jwk, const char *nce, > const char *url) > { > int c; > char *p; > > c = asprintf(&p, "{" > - "\"alg\": \"ES384\", " > - "\"jwk\": " > - "{\"crv\": \"P-384\", \"kty\": \"EC\", \"x\": \"%s\", " > - "\"y\": \"%s\"}, \"nonce\": \"%s\", \"url\": \"%s\"" > + "\"alg\": \"%s\", " > + "\"jwk\": %s, " > + "\"nonce\": \"%s\", " > + "\"url\": \"%s\"" > "}", > - x, y, nce, url); > + alg, jwk, nce, url); > if (c == -1) { > warn("asprintf"); > p = NULL; > @@ -871,6 +891,28 @@ json_fmt_thumb_ec(const char *x, const c > c = asprintf(&p, "{\"crv\":\"P-384\",\"kty\":\"EC\",\"x\":\"%s\"," > "\"y\":\"%s\"}", > x, y); > + if (c == -1) { > + warn("asprintf"); > + p = NULL; > + } > + return p; > +} > + > +/* > + * Protected component of external account binding. > + */ > +char * > +json_fmt_protected_eab(const char *keyid, const char *url) > +{ > + int c; > + char *p; > + > + c = asprintf(&p, "{" > + "\"alg\": \"HS256\", " > + "\"kid\": \"%s\", " > + "\"url\": \"%s\"" > + "}", > + keyid, url); > if (c == -1) { > warn("asprintf"); > p = NULL; > Index: main.c > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/main.c,v > diff -u -p -u -p -r1.58 main.c > --- main.c 23 Feb 2026 10:27:49 -0000 1.58 > +++ main.c 30 Apr 2026 05:10:56 -0000 > @@ -43,12 +43,14 @@ main(int argc, char *argv[]) > char *certdir = NULL; > char *chngdir = NULL, *auth = NULL; > char *conffile = CONF_FILE; > + char *eab = NULL; > char *tmps, *tmpsd; > + unsigned char *eab_key = NULL; > int key_fds[2], acct_fds[2], chng_fds[2], cert_fds[2]; > int file_fds[2], dns_fds[2], rvk_fds[2]; > int force = 0; > int c, rc, revocate = 0; > - int popts = 0; > + int popts = 0, i, eab_len, eab_key_len; > pid_t pids[COMP__MAX]; > size_t ne; > > @@ -60,11 +62,14 @@ main(int argc, char *argv[]) > if (setlocale(LC_CTYPE, "C") == NULL) > errx(1, "setlocale"); > > - while ((c = getopt(argc, argv, "Fnrvf:")) != -1) > + while ((c = getopt(argc, argv, "Fe:k:nrvf:")) != -1) > switch (c) { > case 'F': > force = 1; > break; > + case 'e': > + eab = strdup(optarg); > + break; > case 'f': > if ((conffile = strdup(optarg)) == NULL) > err(EXIT_FAILURE, "strdup"); > @@ -166,6 +171,27 @@ main(int argc, char *argv[]) > ne++; > } > > + if (eab != NULL) { > + eab_len = strlen(eab); > + for (i = 0; i < eab_len; i++) { > + if (eab[i] == ':') { > + eab[i] = '\0'; > + break; > + } > + } this is a weird spelling of strsep(3), isn't it? > + > + if (i == eab_len) { > + warnx("EAB data must be in the format keyid:key"); > + ne++; > + } > + > + eab_key_len = unbase64buf_url((unsigned char *)eab + i + 1, &eab_key); > + if (eab_key_len == -1) { > + warnx("unable to decode EAB key"); > + ne++; > + } > + } > + > if (ne > 0) > return EXIT_FAILURE; > > @@ -222,7 +248,7 @@ main(int argc, char *argv[]) > c = netproc(key_fds[1], acct_fds[1], > chng_fds[1], cert_fds[1], > dns_fds[1], rvk_fds[1], > - revocate, authority, domain); > + revocate, authority, domain, eab != NULL); > exit(c ? EXIT_SUCCESS : EXIT_FAILURE); > } > > @@ -267,7 +293,7 @@ main(int argc, char *argv[]) > close(file_fds[0]); > close(file_fds[1]); > c = acctproc(acct_fds[0], authority->account, > - authority->keytype); > + authority->keytype, eab, eab_key, eab_key_len); > exit(c ? EXIT_SUCCESS : EXIT_FAILURE); > } > > @@ -378,6 +404,6 @@ main(int argc, char *argv[]) > return rc != COMP__MAX ? EXIT_FAILURE : (c == 2 ? EXIT_SUCCESS : 2); > usage: > fprintf(stderr, > - "usage: acme-client [-Fnrv] [-f configfile] handle\n"); > + "usage: acme-client [-Fnrv] [-f configfile] [-e kid:key] handle\n"); > return EXIT_FAILURE; > } > Index: netproc.c > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/netproc.c,v > diff -u -p -u -p -r1.46 netproc.c > --- netproc.c 23 Feb 2026 10:27:49 -0000 1.46 > +++ netproc.c 30 Apr 2026 05:10:56 -0000 > @@ -408,14 +408,30 @@ sreq(struct conn *c, const char *addr, i > * Returns non-zero on success. > */ > static int > -donewacc(struct conn *c, const struct capaths *p, const char *contact) > +donewacc(struct conn *c, const struct capaths *p, const char *contact, > + int eab) > { > struct jsmnn *j = NULL; > int rc = 0; > char *req, *detail, *error = NULL, *accturi = NULL; > + char *eab_json = NULL; > long lc; > > - if ((req = json_fmt_newacc(contact)) == NULL) > + if (eab) { > + /* ask acct proc to produce eab json */ > + if (writeop(c->fd, COMM_ACCT, ACCT_EAB) <= 0) { > + return -1; > + } else if (writestr(c->fd, COMM_URL, p->newaccount) <= 0) { > + return -1; > + } > + > + /* Now read back the signed payload. */ > + if ((eab_json = readstr(c->fd, COMM_REQ)) == NULL) { > + return -1; > + } > + } > + > + if ((req = json_fmt_newacc(contact, eab_json)) == NULL) > warnx("json_fmt_newacc"); > else if ((lc = sreq(c, p->newaccount, 0, req, &c->kid)) < 0) > warnx("%s: bad comm", p->newaccount); > @@ -455,7 +471,8 @@ donewacc(struct conn *c, const struct ca > * Returns non-zero on success. > */ > static int > -dochkacc(struct conn *c, const struct capaths *p, const char *contact) > +dochkacc(struct conn *c, const struct capaths *p, const char *contact, > + int eab) > { > int rc = 0; > char *req, *accturi = NULL; > @@ -470,10 +487,12 @@ dochkacc(struct conn *c, const struct ca > else if (c->buf.buf == NULL || c->buf.sz == 0) > warnx("%s: empty response", p->newaccount); > else if (lc == 400) > - rc = donewacc(c, p, contact); > + rc = donewacc(c, p, contact, eab); > else > rc = 1; > > + rc = donewacc(c, p, contact, eab); > + > if (c->kid == NULL) > rc = 0; > else { > @@ -723,7 +742,7 @@ dodirs(struct conn *c, const char *addr, > int > netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd, > int revocate, struct authority_c *authority, > - struct domain_c *domain) > + struct domain_c *domain, int eab) > { > int rc = 0, retries = 0; > size_t i; > @@ -805,7 +824,7 @@ netproc(int kfd, int afd, int Cfd, int c > c.newnonce = paths.newnonce; > > /* Check if our account already exists or create it. */ > - if (!dochkacc(&c, &paths, authority->contact)) > + if (!dochkacc(&c, &paths, authority->contact, eab)) > goto out; > > /* > -- In my defence, I have been left unsupervised.