From: Florian Obser Subject: Re: draft-aaron-acme-profiles support for acme-client To: tech@openbsd.org Date: Tue, 14 Jan 2025 16:54:35 +0100 On 2025-01-11 11:12 UTC, Stuart Henderson wrote: > https://letsencrypt.org/2025/01/09/acme-profiles/ > https://datatracker.ietf.org/doc/draft-aaron-acme-profiles/ individual submission, eh? > > letsencrypt are allowing selection of different certificate profiles via an > extension to newOrder. Initially the options are "classic" (as now) and > "tlsserver" which follows some CA/B recommendations, resulting in these > changes: > > ... > Signature Algorithm: sha256WithRSAEncryption > Issuer: C=US, O=(STAGING) Let's Encrypt, CN=(STAGING) Wannabe Watercress R11 > Validity > Not Before: xxx > Not After : xxx > - Subject: CN=xxx > + Subject: > Subject Public Key Info: > ... > X509v3 extensions: > X509v3 Key Usage: critical > - Digital Signature, Key Encipherment > + Digital Signature > X509v3 Extended Key Usage: > - TLS Web Server Authentication, TLS Web Client Authentication > + TLS Web Server Authentication > X509v3 Basic Constraints: critical > CA:FALSE > - X509v3 Subject Key Identifier: > - xxx > X509v3 Authority Key Identifier: > keyid:xxx > > As explained in their announcement, it's currently supported by > letsencrypt's staging environment but not the production one yet. > In the future they will also be allowing an *optional* selection > of "shortlived" with the above changes and also cutting validity > to 6 days. > > (This is probably a precursor to cutting validity by default, but > my gut feeling is that they'd probably drop to ~30 days but still > allow sites to select shorter validity if they want). > > Here's an implementation of "profile" for acme-client done as a > setting for the domain. > > Thoughts? > > Do we want this yet? it feels a bit early, but the diff is not too invasive. If you want to put it in and play around with it, OK florian > > Is "domain" the right place? It might make sense to allow this under > "authority" instead of, or as well as, under "domain". as well as would be neat, with domain overriding authority. > > Index: acme-client.conf.5 > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/acme-client.conf.5,v > diff -u -p -r1.29 acme-client.conf.5 > --- acme-client.conf.5 11 Jan 2021 07:23:42 -0000 1.29 > +++ acme-client.conf.5 11 Jan 2025 10:50:51 -0000 > @@ -187,6 +187,9 @@ A backup with name > is created if > .Ar file > exists. > +.It Ic profile Ar profile > +The certificate profile to be requested. > +If this setting is absent, no profile request is made. > .It Ic sign with Ar authority > The certificate authority (as declared above in the > .Sx AUTHORITIES > Index: extern.h > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/extern.h,v > diff -u -p -r1.21 extern.h > --- extern.h 21 May 2024 05:00:48 -0000 1.21 > +++ extern.h 11 Jan 2025 10:50:51 -0000 > @@ -212,7 +212,7 @@ int keyproc(int, const char *, const c > enum keytype); > int netproc(int, int, int, int, int, int, int, > struct authority_c *, const char *const *, > - size_t); > + size_t, const char *); > > /* > * Debugging functions. > @@ -263,7 +263,7 @@ 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_neworder(const char *const *, size_t); > +char *json_fmt_neworder(const char *const *, size_t, const char *); > char *json_fmt_protected_rsa(const char *, > const char *, const char *, const char *); > char *json_fmt_protected_ec(const char *, const char *, const char *, > Index: json.c > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/json.c,v > diff -u -p -r1.21 json.c > --- json.c 14 Sep 2020 16:00:17 -0000 1.21 > +++ json.c 11 Jan 2025 10:50:51 -0000 > @@ -647,7 +647,7 @@ json_fmt_newacc(const char *contact) > * Format the "newOrder" resource request > */ > char * > -json_fmt_neworder(const char *const *alts, size_t altsz) > +json_fmt_neworder(const char *const *alts, size_t altsz, const char *profile) > { > size_t i; > int c; > @@ -669,7 +669,10 @@ json_fmt_neworder(const char *const *alt > } > t = p; > } > - c = asprintf(&p, "%s ] }", t); > + if (profile == NULL) > + c = asprintf(&p, "%s ] }", t); > + else > + c = asprintf(&p, "%s ], \"profile\": \"%s\" }", t, profile); > free(t); > if (c == -1) { > warn("asprintf"); > Index: main.c > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/main.c,v > diff -u -p -r1.56 main.c > --- main.c 19 Jun 2024 13:13:25 -0000 1.56 > +++ main.c 11 Jan 2025 10:50:51 -0000 > @@ -224,7 +224,8 @@ main(int argc, char *argv[]) > chng_fds[1], cert_fds[1], > dns_fds[1], rvk_fds[1], > revocate, authority, > - (const char *const *)alts, altsz); > + (const char *const *)alts, altsz, > + domain->profile); > exit(c ? EXIT_SUCCESS : EXIT_FAILURE); > } > > Index: netproc.c > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/netproc.c,v > diff -u -p -r1.37 netproc.c > --- netproc.c 10 Oct 2024 09:39:35 -0000 1.37 > +++ netproc.c 11 Jan 2025 10:50:51 -0000 > @@ -441,14 +441,14 @@ dochkacc(struct conn *c, const struct ca > */ > static int > doneworder(struct conn *c, const char *const *alts, size_t altsz, > - struct order *order, const struct capaths *p) > + struct order *order, const struct capaths *p, const char *profile) > { > struct jsmnn *j = NULL; > int rc = 0; > char *req; > long lc; > > - if ((req = json_fmt_neworder(alts, altsz)) == NULL) > + if ((req = json_fmt_neworder(alts, altsz, profile)) == NULL) > warnx("json_fmt_neworder"); > else if ((lc = sreq(c, p->neworder, 1, req, &order->uri)) < 0) > warnx("%s: bad comm", p->neworder); > @@ -671,7 +671,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, > - const char *const *alts, size_t altsz) > + const char *const *alts, size_t altsz, const char *profile) > { > int rc = 0; > size_t i; > @@ -762,7 +762,7 @@ netproc(int kfd, int afd, int Cfd, int c > * Following that, submit the request to the CA then notify the > * certproc, which will in turn notify the fileproc. > * XXX currently we can only sign with the account key, the RFC > - * also mentions signing with the privat key of the cert itself. > + * also mentions signing with the private key of the cert itself. > */ > if (revocate) { > if ((cert = readstr(rfd, COMM_CSR)) == NULL) > @@ -776,7 +776,7 @@ netproc(int kfd, int afd, int Cfd, int c > > memset(&order, 0, sizeof(order)); > > - if (!doneworder(&c, alts, altsz, &order, &paths)) > + if (!doneworder(&c, alts, altsz, &order, &paths, profile)) > goto out; > > chngs = calloc(order.authsz, sizeof(struct chng)); > Index: parse.h > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/parse.h,v > diff -u -p -r1.15 parse.h > --- parse.h 14 Sep 2020 16:00:17 -0000 1.15 > +++ parse.h 11 Jan 2025 10:50:51 -0000 > @@ -54,6 +54,7 @@ struct domain_c { > char *fullchain; > char *auth; > char *challengedir; > + char *profile; > }; > > struct altname_c { > Index: parse.y > =================================================================== > RCS file: /cvs/src/usr.sbin/acme-client/parse.y,v > diff -u -p -r1.45 parse.y > --- parse.y 15 Dec 2022 08:06:13 -0000 1.45 > +++ parse.y 11 Jan 2025 10:50:51 -0000 > @@ -101,7 +101,8 @@ typedef struct { > %} > > %token AUTHORITY URL API ACCOUNT CONTACT > -%token DOMAIN ALTERNATIVE NAME NAMES CERT FULL CHAIN KEY SIGN WITH CHALLENGEDIR > +%token DOMAIN ALTERNATIVE NAME NAMES CERT FULL CHAIN KEY SIGN WITH > +%token CHALLENGEDIR PROFILE > %token YES NO > %token INCLUDE > %token ERROR > @@ -393,6 +394,16 @@ domainoptsl : ALTERNATIVE NAMES '{' optn > err(EXIT_FAILURE, "strdup"); > domain->challengedir = s; > } > + | PROFILE STRING { > + char *s; > + if (domain->profile != NULL) { > + yyerror("duplicate profile"); > + YYERROR; > + } > + if ((s = strdup($2)) == NULL) > + err(EXIT_FAILURE, "strdup"); > + domain->profile = s; > + } > ; > > altname_l : altname optcommanl altname_l > @@ -470,6 +481,7 @@ lookup(char *s) > {"key", KEY}, > {"name", NAME}, > {"names", NAMES}, > + {"profile", PROFILE}, > {"rsa", RSA}, > {"sign", SIGN}, > {"url", URL}, > @@ -1081,6 +1093,8 @@ print_config(struct acme_conf *xconf) > if (d->fullchain != NULL) > printf("\tdomain full chain certificate \"%s\"\n", > d->fullchain); > + if (d->profile != NULL) > + printf("\tprofile \"%s\"\n", d->profile); > if (d->auth != NULL) > printf("\tsign with \"%s\"\n", d->auth); > if (d->challengedir != NULL) > -- In my defence, I have been left unsupervised.