Index | Thread | Search

From:
Florian Obser <florian@openbsd.org>
Subject:
Re: draft-aaron-acme-profiles support for acme-client
To:
tech@openbsd.org
Date:
Tue, 14 Jan 2025 16:54:35 +0100

Download raw body.

Thread
On 2025-01-11 11:12 UTC, Stuart Henderson <stu@spacehopper.org> 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.