~graywolf/acme-client-portable

46bc29a47e65e78f260ea0e4746621dc93aa8c2f — Wolf 8 months ago 1f43106 + ffd0f7d
Merge updates from openbsd

Handle certificates with \r\n line endings:

	Relax parsing of pem files a bit. Apparently there are CAs that use
	\r\n line endings.
	From Bartosz Kuzma (bartosz.kuzma AT release11.com) as part of a
	larger diff.
	OK beck

Allow providing contacts information:

	We need to be able to provide contact information to use the
	buypass.com acme api.
	From Bartosz Kuzma (bartosz.kuzma AT release11.com), thanks!
	OK beck, deraadt
8 files changed, 92 insertions(+), 45 deletions(-)

M CVS/Entries
M acme-client.conf.5
M certproc.c
M extern.h
M json.c
M netproc.c
M parse.h
M parse.y
M CVS/Entries => CVS/Entries +20 -20
@@ 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//
/Makefile/1.9/Sat Jun 20 20:24:29 2020//
/acctproc.c/1.20/Sat Jun 20 20:24:29 2020//
/acme-client.1/1.34/Mon May 18 09:16:56 2020//
/acme-client.conf.5/1.25/Mon May 18 09:16:56 2020//
/base64.c/1.9/Sat Jun 20 20:24:29 2020//
/certproc.c/1.12/Sat Jun 20 20:24:29 2020//
/chngproc.c/1.15/Sat Jun 20 20:24:29 2020//
/dbg.c/1.4/Sat Jun 20 20:24:29 2020//
/dnsproc.c/1.11/Sat Jun 20 20:24:29 2020//
/extern.h/1.18/Sat Jun 20 20:24:29 2020//
/fileproc.c/1.16/Sat Jun 20 20:24:29 2020//
/http.c/1.29/Sat Jun 20 20:24:29 2020//
/json.c/1.19/Sat Jun 20 20:24:30 2020//
/key.c/1.2/Sat Jun 20 20:24:29 2020//
/keyproc.c/1.15/Sat Jun 20 20:24:29 2020//
/main.c/1.54/Sat Jun 20 20:24:29 2020//
/netproc.c/1.26/Sat Jun 20 20:24:29 2020//
/parse.h/1.14/Sat Jun 20 20:24:29 2020//
/parse.y/1.41/Sat Jun 20 20:24:29 2020//
/revokeproc.c/1.15/Sat Jun 20 20:24:29 2020//
/util.c/1.12/Sat Jun 20 20:24:29 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//
D

M acme-client.conf.5 => acme-client.conf.5 +7 -2
@@ 1,4 1,4 @@
.\"	$OpenBSD: acme-client.conf.5,v 1.24 2020/05/12 20:46:30 jmc Exp $
.\"	$OpenBSD: acme-client.conf.5,v 1.25 2020/05/16 16:58:11 jmc 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: May 16 2020 $
.Dd $Mdocdate: September 14 2020 $
.Dt ACME-CLIENT.CONF 5
.Os
.Sh NAME


@@ 98,6 98,11 @@ It defaults to
Specify the
.Ar url
under which the ACME API is reachable.
.It Ic contact Ar contact
Optional
.Ar contact
URLs that the authority can use to contact the client for issues related to
this account.
.El
.Sh DOMAINS
The certificates to be obtained through ACME.

M certproc.c => certproc.c +12 -5
@@ 1,4 1,4 @@
/*	$Id: certproc.c,v 1.12 2019/06/07 08:07:52 florian Exp $ */
/*	$Id: certproc.c,v 1.13 2020/09/14 15:58:50 florian Exp $ */
/*
 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
 *


@@ 29,7 29,8 @@
#include "compat.h"
#include "extern.h"

#define MARKER "-----END CERTIFICATE-----\n"
#define BEGIN_MARKER "-----BEGIN CERTIFICATE-----"
#define END_MARKER "-----END CERTIFICATE-----"

int
certproc(int netsock, int filesock)


@@ 84,19 85,25 @@ certproc(int netsock, int filesock)
	if ((csr = readbuf(netsock, COMM_CSR, &csrsz)) == NULL)
		goto out;

	if (csrsz < strlen(MARKER)) {
	if (csrsz < strlen(END_MARKER)) {
		warnx("invalid cert");
		goto out;
	}

	chaincp = strstr(csr, MARKER);
	chaincp = strstr(csr, END_MARKER);

	if (chaincp == NULL) {
		warnx("invalid cert");
		goto out;
	}

	chaincp += strlen(MARKER);
	chaincp += strlen(END_MARKER);

	if ((chaincp = strstr(chaincp, BEGIN_MARKER)) == NULL) {
		warnx("invalid certificate chain");
		goto out;
	}

	if ((chain = strdup(chaincp)) == NULL) {
		warn("strdup");
		goto out;

M extern.h => extern.h +3 -2
@@ 1,4 1,4 @@
/*	$Id: extern.h,v 1.18 2020/05/10 17:34:07 florian Exp $ */
/*	$Id: extern.h,v 1.20 2020/09/14 16:00:17 florian Exp $ */
/*
 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
 *


@@ 268,10 268,11 @@ int		 json_parse_order(struct jsmnn *, struct order *);
int		 json_parse_upd_order(struct jsmnn *, struct order *);
void		 json_free_capaths(struct capaths *);
int		 json_parse_capaths(struct jsmnn *, struct capaths *);
char		*json_getstr(struct jsmnn *, const char *);

char		*json_fmt_newcert(const char *);
char		*json_fmt_chkacc(void);
char		*json_fmt_newacc(void);
char		*json_fmt_newacc(const char *);
char		*json_fmt_neworder(const char *const *, size_t);
char		*json_fmt_protected_rsa(const char *,
			const char *, const char *, const char *);

M json.c => json.c +15 -5
@@ 1,4 1,4 @@
/*	$Id: json.c,v 1.19 2020/06/07 13:29:52 florian Exp $ */
/*	$Id: json.c,v 1.21 2020/09/14 16:00:17 florian Exp $ */
/*
 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
 *


@@ 298,7 298,7 @@ json_getobj(struct jsmnn *n, const char *name)
 * that it's the correct type.
 * Returns NULL on failure.
 */
static char *
char *
json_getstr(struct jsmnn *n, const char *name)
{
	size_t		 i;


@@ 619,14 619,24 @@ json_fmt_chkacc(void)
 * Format the "newAccount" resource request.
 */
char *
json_fmt_newacc(void)
json_fmt_newacc(const char *contact)
{
	int	 c;
	char	*p;
	char	*p, *cnt = NULL;

	if (contact != NULL) {
		c = asprintf(&cnt, "\"contact\": [ \"%s\" ], ", contact);
		if (c == -1) {
			warn("asprintf");
			return NULL;
		}
	}

	c = asprintf(&p, "{"
	    "%s"
	    "\"termsOfServiceAgreed\": true"
	    "}");
	    "}", cnt == NULL ? "" : cnt);
	free(cnt);
	if (c == -1) {
		warn("asprintf");
		p = NULL;

M netproc.c => netproc.c +20 -8
@@ 1,4 1,4 @@
/*	$Id: netproc.c,v 1.26 2020/05/10 17:34:07 florian Exp $ */
/*	$Id: netproc.c,v 1.28 2020/09/14 16:00:17 florian Exp $ */
/*
 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
 *


@@ 367,17 367,29 @@ sreq(struct conn *c, const char *addr, int kid, const char *req, char **loc)
 * Returns non-zero on success.
 */
static int
donewacc(struct conn *c, const struct capaths *p)
donewacc(struct conn *c, const struct capaths *p, const char *contact)
{
	struct jsmnn	*j = NULL;
	int		 rc = 0;
	char		*req;
	char		*req, *detail, *error = NULL;
	long		 lc;

	if ((req = json_fmt_newacc()) == NULL)
	if ((req = json_fmt_newacc(contact)) == NULL)
		warnx("json_fmt_newacc");
	else if ((lc = sreq(c, p->newaccount, 0, req, &c->kid)) < 0)
		warnx("%s: bad comm", p->newaccount);
	else if (lc != 200 && lc != 201)
	else if (lc == 400) {
		if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL)
			warnx("%s: bad JSON object", p->newaccount);
		else {
			detail = json_getstr(j, "detail");
			if (detail != NULL && stravis(&error, detail, VIS_SAFE)
			    != -1) {
				warnx("%s", error);
				free(error);
			}
		}
	} else if (lc != 200 && lc != 201)
		warnx("%s: bad HTTP: %ld", p->newaccount, lc);
	else if (c->buf.buf == NULL || c->buf.sz == 0)
		warnx("%s: empty response", p->newaccount);


@@ 396,7 408,7 @@ donewacc(struct conn *c, const struct capaths *p)
 * Returns non-zero on success.
 */
static int
dochkacc(struct conn *c, const struct capaths *p)
dochkacc(struct conn *c, const struct capaths *p, const char *contact)
{
	int		 rc = 0;
	char		*req;


@@ 411,7 423,7 @@ dochkacc(struct conn *c, const struct capaths *p)
	else if (c->buf.buf == NULL || c->buf.sz == 0)
		warnx("%s: empty response", p->newaccount);
	else if (lc == 400)
		rc = donewacc(c, p);
		rc = donewacc(c, p, contact);
	else
		rc = 1;



@@ 747,7 759,7 @@ netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd,
	c.newnonce = paths.newnonce;

	/* Check if our account already exists or create it. */
	if (!dochkacc(&c, &paths))
	if (!dochkacc(&c, &paths, authority->contact))
		goto out;

	/*

M parse.h => parse.h +2 -1
@@ 1,4 1,4 @@
/*	$OpenBSD: parse.h,v 1.13 2019/06/17 12:42:52 florian Exp $ */
/*	$OpenBSD: parse.h,v 1.14 2020/05/10 12:06:18 benno Exp $ */
/*
 * Copyright (c) 2016 Sebastian Benoit <benno@openbsd.org>
 *


@@ 36,6 36,7 @@ struct authority_c {
	char				*api;
	char				*account;
	enum keytype			 keytype;
	char				*contact;
};

struct domain_c {

M parse.y => parse.y +13 -2
@@ 1,4 1,4 @@
/*	$OpenBSD: parse.y,v 1.40 2020/05/10 12:06:18 benno Exp $ */
/*	$OpenBSD: parse.y,v 1.41 2020/05/16 20:19:23 sthen Exp $ */

/*
 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>


@@ 105,7 105,7 @@ typedef struct {

%}

%token	AUTHORITY URL API ACCOUNT
%token	AUTHORITY URL API ACCOUNT CONTACT
%token	DOMAIN ALTERNATIVE NAME NAMES CERT FULL CHAIN KEY SIGN WITH CHALLENGEDIR
%token	YES NO
%token	INCLUDE


@@ 235,6 235,16 @@ authorityoptsl	: API URL STRING {
			auth->account = s;
			auth->keytype = $4;
		}
		| CONTACT STRING {
			char *s;
			if (auth->contact != NULL) {
				yyerror("duplicate contact");
				YYERROR;
			}
			if ((s = strdup($2)) == NULL)
				err(EXIT_FAILURE, "strdup");
			auth->contact = s;
		}
		;

domain		: DOMAIN STRING {


@@ 457,6 467,7 @@ lookup(char *s)
		{"certificate",		CERT},
		{"chain",		CHAIN},
		{"challengedir",	CHALLENGEDIR},
		{"contact",		CONTACT},
		{"domain",		DOMAIN},
		{"ecdsa",		ECDSA},
		{"full",		FULL},