Bartosz Kuzma([email protected]) on 2020.04.20 18:51:17 +0200:
> Hello,
>
> I've tried to get a certificate from Buypass Go SSL provider using
> acme-client(1) but it ends with the following error:
>
> acme-client: https://api.buypass.com/acme-v02/new-acct: bad HTTP: 400
> acme-client: transfer buffer:
> [{"type":"urn:ietf:params:acme:error:malformed","d
> etail":"Email is a required
> contact","code":400,"message":"MALFORMED_BAD_REQUEST
> ","details":"HTTP 400 Bad Request"}] (164 bytes)
>
> Buypass Go SSL requires that optional field contact in Account Objects
> will contain email address.
>
> I've added new option "contact" to acme-client.conf to fullfil this
> requirement.
Thanks, that looks ok.
> It is also required to change MARKER in certproc.c to fix
> line endings handling in pem files.
Can you explain what happens there?
/Benno
> --
> Kind regards, Bartosz Kuzma.
> Index: usr.sbin/acme-client/acme-client.conf.5
> ===================================================================
> RCS file: /cvs/src/usr.sbin/acme-client/acme-client.conf.5,v
> retrieving revision 1.22
> diff -u -p -r1.22 acme-client.conf.5
> --- usr.sbin/acme-client/acme-client.conf.5 10 Feb 2020 13:18:21 -0000
> 1.22
> +++ usr.sbin/acme-client/acme-client.conf.5 20 Apr 2020 16:24:46 -0000
> @@ -98,6 +98,10 @@ 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.
> Index: usr.sbin/acme-client/certproc.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/acme-client/certproc.c,v
> retrieving revision 1.12
> diff -u -p -r1.12 certproc.c
> --- usr.sbin/acme-client/certproc.c 7 Jun 2019 08:07:52 -0000 1.12
> +++ usr.sbin/acme-client/certproc.c 20 Apr 2020 16:24:46 -0000
> @@ -28,7 +28,7 @@
>
> #include "extern.h"
>
> -#define MARKER "-----END CERTIFICATE-----\n"
> +#define MARKER "-----END CERTIFICATE-----"
>
> int
> certproc(int netsock, int filesock)
> @@ -94,6 +94,12 @@ certproc(int netsock, int filesock)
> }
>
> chaincp += strlen(MARKER);
> +
> + if ((chaincp = strchr(chaincp, '-')) == NULL) {
> + warn("strchr");
> + goto out;
> + }
> +
> if ((chain = strdup(chaincp)) == NULL) {
> warn("strdup");
> goto out;
> Index: usr.sbin/acme-client/extern.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/acme-client/extern.h,v
> retrieving revision 1.17
> diff -u -p -r1.17 extern.h
> --- usr.sbin/acme-client/extern.h 7 Feb 2020 14:34:15 -0000 1.17
> +++ usr.sbin/acme-client/extern.h 20 Apr 2020 16:24:46 -0000
> @@ -261,13 +261,13 @@ int json_parse_capaths(struct jsmnn *,
>
> 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 *);
> char *json_fmt_protected_ec(const char *, const char *, const char *,
> const char *);
> -char *json_fmt_protected_kid(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 *);
> Index: usr.sbin/acme-client/json.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/acme-client/json.c,v
> retrieving revision 1.16
> diff -u -p -r1.16 json.c
> --- usr.sbin/acme-client/json.c 22 Jan 2020 22:25:22 -0000 1.16
> +++ usr.sbin/acme-client/json.c 20 Apr 2020 16:24:46 -0000
> @@ -616,19 +616,30 @@ 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;
> +
> + c = asprintf(&cnt, "\"contact\": [ \"%s\" ], ", contact);
> + if (c == -1) {
> + warn("asprintf");
> + goto err;
> + }
>
> c = asprintf(&p, "{"
> + "%s"
> "\"termsOfServiceAgreed\": true"
> - "}");
> + "}", contact == NULL ? "" : cnt);
> + free(cnt);
> if (c == -1) {
> warn("asprintf");
> p = NULL;
> }
> return p;
> +
> +err:
> + return NULL;
> }
>
> /*
> Index: usr.sbin/acme-client/netproc.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/acme-client/netproc.c,v
> retrieving revision 1.25
> diff -u -p -r1.25 netproc.c
> --- usr.sbin/acme-client/netproc.c 11 Aug 2019 19:44:25 -0000 1.25
> +++ usr.sbin/acme-client/netproc.c 20 Apr 2020 16:24:46 -0000
> @@ -368,13 +368,13 @@ sreq(struct conn *c, const char *addr, i
> * 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)
> {
> int rc = 0;
> char *req;
> 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);
> @@ -397,7 +397,7 @@ donewacc(struct conn *c, const struct ca
> * 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;
> @@ -412,7 +412,7 @@ 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);
> + rc = donewacc(c, p, contact);
> else
> rc = 1;
>
> @@ -742,7 +742,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))
> + if (!dochkacc(&c, &paths, authority->contact))
> goto out;
>
> /*
> Index: usr.sbin/acme-client/parse.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/acme-client/parse.h,v
> retrieving revision 1.13
> diff -u -p -r1.13 parse.h
> --- usr.sbin/acme-client/parse.h 17 Jun 2019 12:42:52 -0000 1.13
> +++ usr.sbin/acme-client/parse.h 20 Apr 2020 16:24:46 -0000
> @@ -38,6 +38,7 @@ struct authority_c {
> char *api;
> char *account;
> enum keytype keytype;
> + char *contact;
> };
>
> struct domain_c {
> Index: usr.sbin/acme-client/parse.y
> ===================================================================
> RCS file: /cvs/src/usr.sbin/acme-client/parse.y,v
> retrieving revision 1.39
> diff -u -p -r1.39 parse.y
> --- usr.sbin/acme-client/parse.y 27 Dec 2019 16:56:40 -0000 1.39
> +++ usr.sbin/acme-client/parse.y 20 Apr 2020 16:24:47 -0000
> @@ -100,7 +100,7 @@ typedef struct {
>
> %}
>
> -%token AUTHORITY URL API ACCOUNT
> +%token AUTHORITY URL API ACCOUNT CONTACT
> %token DOMAIN ALTERNATIVE NAMES CERT FULL CHAIN KEY SIGN WITH
> CHALLENGEDIR
> %token YES NO
> %token INCLUDE
> @@ -230,6 +230,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 {
> @@ -437,6 +447,7 @@ lookup(char *s)
> {"certificate", CERT},
> {"chain", CHAIN},
> {"challengedir", CHALLENGEDIR},
> + {"contact", CONTACT},
> {"domain", DOMAIN},
> {"ecdsa", ECDSA},
> {"full", FULL},
> Index: etc/examples/acme-client.conf
> ===================================================================
> RCS file: /cvs/src/etc/examples/acme-client.conf,v
> retrieving revision 1.2
> diff -u -p -r1.2 acme-client.conf
> --- etc/examples/acme-client.conf 7 Jun 2019 08:08:30 -0000 1.2
> +++ etc/examples/acme-client.conf 20 Apr 2020 16:24:56 -0000
> @@ -11,6 +11,18 @@ authority letsencrypt-staging {
> account key "/etc/acme/letsencrypt-staging-privkey.pem"
> }
>
> +authority buypass {
> + api url "https://api.buypass.com/acme/directory"
> + account key "/etc/acme/buypass-privkey.pem"
> + contact "mailto:[email protected]"
> +}
> +
> +authority buypass-test {
> + api url "https://api.test4.buypass.no/acme/directory"
> + account key "/etc/acme/buypass-test-privkey.pem"
> + contact "mailto:[email protected]"
> +}
> +
> domain example.com {
> alternative names { secure.example.com }
> domain key "/etc/ssl/private/example.com.key"