~graywolf/acme-client-portable

6f00ff1eab9ab783b8527fc03d2a09a6b23d96d1 — Wolf 5 months ago 82a7ed0 + c3d56bc
Merge updates from openbsd

Create backup files for certificates

	Create .1 backup files when acme-client is going to overwrite a
	certificate file.
	These files are not terribly big and they might become helpful if one
	re-creates a certificate with additional or removed domains and
	whishes to revoke the old cert (this part needs a bit of work to make
	it convenient to do).
	OK sthen

Improve handling of added/removed SANs

	If acme-client detects an added or removed SAN in the config file
	compared to the existing certificate on disk, automatically request a
	new certificate without requiring -F.

	(Previously the code using -F only coped with added SANs; if one was
	removed in config then the certificate needed manual removal vefore
	acme-client would work).

	Name checks for -r (revocation) are kept as-is for now.

First fulfill all challenges

	First fulfil all challenges then tell the CA that it should check.

	For http-01 this doesn't matter but I think this will be nicer for
	dns-01 because there are propagation delays to consider and it will be
	better to just put everything in DNS and then wait then wait after
	each challenge.

	Testing & OK sthen
6 files changed, 90 insertions(+), 38 deletions(-)

M CVS/Entries
M acme-client.1
M acme-client.conf.5
M fileproc.c
M netproc.c
M revokeproc.c
M CVS/Entries => CVS/Entries +21 -21
@@ 2,25 2,25 @@
/jsmn.c/1.1/Mon Jul  8 18:00:44 2019//
/jsmn.h/1.1/Mon Jul  8 18:00:44 2019//
/key.h/1.1/Mon Jul  8 18:00:44 2019//
/acme-client.1/1.34/Mon May 18 09:16:56 2020//
/Makefile/1.9/Thu Oct  1 13:16:36 2020//
/acctproc.c/1.20/Thu Oct  1 13:16:36 2020//
/acme-client.conf.5/1.26/Thu Oct  1 13:16:37 2020//
/base64.c/1.9/Thu Oct  1 13:16:36 2020//
/certproc.c/1.13/Thu Oct  1 13:16:37 2020//
/chngproc.c/1.15/Thu Oct  1 13:16:36 2020//
/dbg.c/1.4/Thu Oct  1 13:16:36 2020//
/dnsproc.c/1.11/Thu Oct  1 13:16:36 2020//
/extern.h/1.20/Thu Oct  1 13:16:37 2020//
/fileproc.c/1.16/Thu Oct  1 13:16:36 2020//
/http.c/1.29/Thu Oct  1 13:16:36 2020//
/json.c/1.21/Thu Oct  1 13:16:37 2020//
/key.c/1.2/Thu Oct  1 13:16:36 2020//
/keyproc.c/1.15/Thu Oct  1 13:16:36 2020//
/main.c/1.54/Thu Oct  1 13:16:36 2020//
/netproc.c/1.28/Thu Oct  1 13:16:37 2020//
/parse.h/1.15/Thu Oct  1 13:16:37 2020//
/parse.y/1.42/Thu Oct  1 13:16:37 2020//
/revokeproc.c/1.15/Thu Oct  1 13:16:36 2020//
/util.c/1.12/Thu Oct  1 13:16:36 2020//
/Makefile/1.9/Fri Jan  8 23:10:31 2021//
/acctproc.c/1.20/Fri Jan  8 23:10:31 2021//
/acme-client.1/1.39/Fri Jan  8 23:10:32 2021//
/acme-client.conf.5/1.28/Fri Jan  8 23:10:32 2021//
/base64.c/1.9/Fri Jan  8 23:10:31 2021//
/certproc.c/1.13/Fri Jan  8 23:10:31 2021//
/chngproc.c/1.15/Fri Jan  8 23:10:31 2021//
/dbg.c/1.4/Fri Jan  8 23:10:31 2021//
/dnsproc.c/1.11/Fri Jan  8 23:10:31 2021//
/extern.h/1.20/Fri Jan  8 23:10:31 2021//
/fileproc.c/1.17/Fri Jan  8 23:10:32 2021//
/http.c/1.29/Fri Jan  8 23:10:31 2021//
/json.c/1.21/Fri Jan  8 23:10:31 2021//
/key.c/1.2/Fri Jan  8 23:10:31 2021//
/keyproc.c/1.15/Fri Jan  8 23:10:31 2021//
/main.c/1.54/Fri Jan  8 23:10:31 2021//
/netproc.c/1.29/Fri Jan  8 23:10:32 2021//
/parse.h/1.15/Fri Jan  8 23:10:31 2021//
/parse.y/1.42/Fri Jan  8 23:10:31 2021//
/revokeproc.c/1.17/Fri Jan  8 23:10:32 2021//
/util.c/1.12/Fri Jan  8 23:10:31 2021//
D

M acme-client.1 => acme-client.1 +4 -3
@@ 1,4 1,4 @@
.\"	$OpenBSD: acme-client.1,v 1.33 2020/04/15 03:24:08 millert Exp $
.\"	$OpenBSD: acme-client.1,v 1.38 2020/12/19 18:05:44 tb Exp $
.\"
.\" Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
.\"


@@ 14,7 14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: May 10 2020 $
.Dd $Mdocdate: January 2 2021 $
.Dt ACME-CLIENT 1
.Os
.Sh NAME


@@ 67,7 67,8 @@ location "/.well-known/acme-challenge/*" {
The options are as follows:
.Bl -tag -width Ds
.It Fl F
Force certificate renewal, even if it's too soon.
Force certificate renewal, even if it has more than 30 days
validity.
.It Fl f Ar configfile
Specify an alternative configuration file.
.It Fl n

M acme-client.conf.5 => acme-client.conf.5 +23 -2
@@ 1,4 1,4 @@
.\"	$OpenBSD: acme-client.conf.5,v 1.25 2020/05/16 16:58:11 jmc Exp $
.\"	$OpenBSD: acme-client.conf.5,v 1.27 2020/11/06 20:31:58 sthen Exp $
.\"
.\" Copyright (c) 2005 Esben Norby <norby@openbsd.org>
.\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>


@@ 17,7 17,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: September 14 2020 $
.Dd $Mdocdate: January 3 2021 $
.Dt ACME-CLIENT.CONF 5
.Os
.Sh NAME


@@ 138,11 138,22 @@ or
.Cm ecdsa .
It defaults to
.Cm rsa .
If the key file does not exist,
.Xr acme-client 1
will generate a key itself (4096-bit for
.Cm rsa
or secp384r1 for
.Cm ecdsa ) .
.It Ic domain certificate Ar file
The filename of the certificate that will be issued.
This is optional if
.Ar domain full chain certificate
is specified.
A backup with name
.Ar file.1
is created if
.Ar file
exists.
.It Ic domain chain certificate Ar file
The filename in which to store the certificate chain
that will be returned by the certificate authority.


@@ 150,6 161,11 @@ It needs to be in the same directory as the
.Ar domain certificate
(or in a subdirectory) and can be specified as a relative or absolute path.
This setting is optional.
A backup with name
.Ar file.1
is created if
.Ar file
exists.
.It Ic domain full chain certificate Ar file
The filename in which to store the full certificate chain
that will be returned by the certificate authority.


@@ 164,6 180,11 @@ in one file, and is required by most browsers.
This is optional if
.Ar domain certificate
is specified.
A backup with name
.Ar file.1
is created if
.Ar file
exists.
.It Ic sign with Ar authority
The certificate authority (as declared above in the
.Sx AUTHORITIES

M fileproc.c => fileproc.c +14 -1
@@ 1,4 1,4 @@
/*	$Id: fileproc.c,v 1.16 2019/06/16 19:49:13 florian Exp $ */
/*	$Id: fileproc.c,v 1.17 2021/01/03 16:32:38 florian Exp $ */
/*
 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
 *


@@ 35,6 35,19 @@ serialise(const char *real, const char *v, size_t vsz, const char *v2, size_t v2
	int	  fd;
	char	 *tmp;

	/* create backup hardlink */
	if (asprintf(&tmp, "%s.1", real) == -1) {
		warn("asprintf");
		return 0;
	}
	(void) unlink(tmp);
	if (link(real, tmp) == -1 && errno != ENOENT) {
		warn("link");
		free(tmp);
		return 0;
	}
	free(tmp);

	/*
	 * Write into backup location, overwriting.
	 * Then atomically do the rename.

M netproc.c => netproc.c +7 -2
@@ 1,4 1,4 @@
/*	$Id: netproc.c,v 1.28 2020/09/14 16:00:17 florian Exp $ */
/*	$Id: netproc.c,v 1.29 2020/12/24 08:17:49 florian Exp $ */
/*
 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
 *


@@ 844,7 844,12 @@ netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd,
				if (readop(Cfd, COMM_CHNG_ACK) != CHNG_ACK)
					goto out;

				/* Write to the CA that it's ready. */
			}
			/* Write to the CA that it's ready. */
			for (i = 0; i < order.authsz; i++) {
				if (chngs[i].status == CHNG_VALID ||
				    chngs[i].status == CHNG_INVALID)
					continue;
				if (!dochngresp(&c, &chngs[i]))
					goto out;
			}

M revokeproc.c => revokeproc.c +21 -9
@@ 1,4 1,4 @@
/*	$Id: revokeproc.c,v 1.15 2019/06/16 19:49:13 florian Exp $ */
/*	$Id: revokeproc.c,v 1.17 2021/01/02 19:04:21 sthen Exp $ */
/*
 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
 *


@@ 209,7 209,9 @@ revokeproc(int fd, const char *certfile, int force,

	if (san == NULL) {
		warnx("%s: does not have a SAN entry", certfile);
		goto out;
		if (revocate)
			goto out;
		force = 2;
	}

	/* An array of buckets: the number of entries found. */


@@ 237,20 239,29 @@ revokeproc(int fd, const char *certfile, int force,
			if (strcmp(tok, alts[j]) == 0)
				break;
		if (j == altsz) {
			warnx("%s: unknown SAN entry: %s", certfile, tok);
			goto out;
			if (revocate) {
				warnx("%s: unknown SAN entry: %s", certfile, tok);
				goto out;
			}
			force = 2;
		}
		if (found[j]++) {
			warnx("%s: duplicate SAN entry: %s", certfile, tok);
			goto out;
			if (revocate) {
				warnx("%s: duplicate SAN entry: %s", certfile, tok);
				goto out;
			}
			force = 2;
		}
	}

	for (j = 0; j < altsz; j++) {
		if (found[j])
			continue;
		warnx("%s: domain not listed: %s", certfile, alts[j]);
		goto out;
		if (revocate) {
			warnx("%s: domain not listed: %s", certfile, alts[j]);
			goto out;
		}
		force = 2;
	}

	/*


@@ 301,7 312,8 @@ revokeproc(int fd, const char *certfile, int force,
		    certfile, (long long)(t - time(NULL)) / 24 / 60 / 60);

	if (rop == REVOKE_OK && force) {
		warnx("%s: forcing renewal", certfile);
		warnx("%s: %sforcing renewal", certfile,
		    force == 2 ? "domain list changed, " : "");
		rop = REVOKE_EXP;
	}