Index: parse.h =================================================================== RCS file: /cvs/src/usr.sbin/acme-client/parse.h,v retrieving revision 1.15 diff -u -p -r1.15 parse.h --- parse.h 14 Sep 2020 16:00:17 -0000 1.15 +++ parse.h 20 Jul 2022 04:55:24 -0000 @@ -21,6 +21,7 @@ #define AUTH_MAXLEN 120 /* max length of an authority_c name */ #define DOMAIN_MAXLEN 255 /* max len of a domain name (rfc2181) */ +#define EAB_KEY_MAXLEN 512 /* max bytes in an eab key */ /* * XXX other size limits needed? @@ -39,6 +40,10 @@ struct authority_c { char *account; enum keytype keytype; char *contact; + + char *eab_kid; + void *eab_key; + size_t eab_keylen; }; struct domain_c { Index: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/acme-client/parse.y,v retrieving revision 1.42 diff -u -p -r1.42 parse.y --- parse.y 14 Sep 2020 16:00:17 -0000 1.42 +++ parse.y 20 Jul 2022 04:55:24 -0000 @@ -36,6 +36,10 @@ #include #include #include +#include + +#include +#include #include "parse.h" #include "extern.h" @@ -73,6 +77,7 @@ void clear_config(struct acme_conf *) const char* kt2txt(enum keytype); void print_config(struct acme_conf *); int conf_check_file(char *); +static int eab_key_read(struct authority_c *, int, const char *); TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); struct sym { @@ -89,6 +94,7 @@ static struct acme_conf *conf; static struct authority_c *auth; static struct domain_c *domain; static int errors = 0; +static int etc_acme; typedef struct { union { @@ -100,7 +106,7 @@ typedef struct { %} -%token AUTHORITY URL API ACCOUNT CONTACT +%token AUTHORITY URL API ACCOUNT CONTACT EAB KID ALGORITHM %token DOMAIN ALTERNATIVE NAME NAMES CERT FULL CHAIN KEY SIGN WITH CHALLENGEDIR %token YES NO %token INCLUDE @@ -240,8 +246,55 @@ authorityoptsl : API URL STRING { err(EXIT_FAILURE, "strdup"); auth->contact = s; } + | EAB KID STRING KEY STRING { + const char *fname = $5; + struct stat st; + int fd; + + if (auth->eab_kid != NULL) { + free($3); + free($5); + yyerror("eab already defined"); + YYERROR; + } + + fd = openat(etc_acme, fname, O_RDONLY); + if (fd == -1) { + yyerror("eab key %s: %s", fname, + strerror(errno)); + free($3); + free($5); + YYERROR; + } + + if (fstat(fd, &st) == -1) + err(1, "stat %s", $5); + + if (st.st_mode & (S_IRWXG | S_IRWXO)) { + yyerror("eab key %s: group read/writable or " + "world read/writable", fname); + free($3); + free($5); + YYERROR; + } + + if (eab_key_read(auth, fd, fname) == -1) { + /* yyerror is already called */ + free($3); + free($5); + YYERROR; + } + + close(fd); + free($5); + + auth->eab_kid = $3; + warnx("kid %s len %zu", + auth->eab_kid, auth->eab_keylen); + } ; + domain : DOMAIN STRING { char *s; if ((s = strdup($2)) == NULL) @@ -456,6 +509,7 @@ lookup(char *s) /* this has to be sorted always */ static const struct keywords keywords[] = { {"account", ACCOUNT}, + {"algorithm", ALGORITHM}, {"alternative", ALTERNATIVE}, {"api", API}, {"authority", AUTHORITY}, @@ -464,10 +518,12 @@ lookup(char *s) {"challengedir", CHALLENGEDIR}, {"contact", CONTACT}, {"domain", DOMAIN}, + {"eab", EAB}, {"ecdsa", ECDSA}, {"full", FULL}, {"include", INCLUDE}, {"key", KEY}, + {"kid", KID}, {"name", NAME}, {"names", NAMES}, {"rsa", RSA}, @@ -816,10 +872,17 @@ parse_config(const char *filename, int o TAILQ_INIT(&conf->authority_list); TAILQ_INIT(&conf->domain_list); + etc_acme = open("/etc/acme", O_RDONLY|O_DIRECTORY); + if (etc_acme == -1) + etc_acme = AT_FDCWD; + yyparse(); errors = file->errors; popfile(); + if (etc_acme != AT_FDCWD) + close(etc_acme); + /* Free macros and check which have not been used. */ TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { if ((conf->opts & ACME_OPT_VERBOSE) && !sym->used) @@ -1126,4 +1189,60 @@ conf_check_file(char *s) return 0; } return 1; +} + +static int +eab_key_read(struct authority_c *a, int fd, const char *fname) +{ + BIO *bfd; + BIO *b64; + char *buf = NULL; + size_t len = 0; + int rv; + + b64 = BIO_new(BIO_f_base64()); + if (b64 == NULL) + errx(1, "%s: BIO_new base64", __func__); + + bfd = BIO_new_fd(fd, BIO_NOCLOSE); + if (bfd == NULL) + errx(1, "%s: BIO_new fd", __func__); + + BIO_push(b64, bfd); + + buf = malloc(EAB_KEY_MAXLEN); + if (buf == NULL) + err(1, "%s read", fname); + + for (;;) { + rv = BIO_read(b64, buf + len, EAB_KEY_MAXLEN - len); + if (rv == -2) + errx(1, "%s: method not implemented", __func__); + if (rv == -1) { + yyerror("eab key %s: read or base64 decode error", + fname); + return (-1); + } + if (rv == 0) + break; + + len += rv; + } + + BIO_free_all(bfd); + + if (len == 0) { + free(buf); + yyerror("eab key %s: no key found", fname); + return (-1); + } + + a->eab_key = buf; + a->eab_keylen = len; + + for (size_t i = 0; i < len; i++) + printf("%c", buf[i]); + printf("\n"); + + return (0); }