From: Florian Obser Subject: Re: [stu@spacehopper.org: Re: acme-client(1): add support for let's encrypt iPAddress certificates] To: tech@openbsd.org Date: Sat, 21 Feb 2026 19:02:36 +0100 On 2026-02-21 14:40 +01, Florian Obser wrote: > On 2026-02-20 14:20 UTC, Stuart Henderson wrote: >> just updated the machine where I'm running acme-client, which reminded >> me that I still have this diff.. any interest? > > I'm reviewing the diff and trying it out. > This is on top of the CN removal diff. I tweaked a bunch of things: + keyproc.c & json.c: KNF; made "first" less awkward + revokeproc.c: asprintf & inet_ntop can fail, add vis, prevent asprintf leak + main.c: calloc error check + parse.y: getaddrinfo(3) with AI_NUMERICHOST is the canonical way to figure out if a thing is an ip address; fixed duplicate "DOMAIN PROFILE" (bad merge) With the parse.y change we no longer need to prefix IP addresses with IP:, which I found to be a nuisance. I've also added a regress test. This one's OK florian diff --git regress/usr.sbin/acme-client/Makefile regress/usr.sbin/acme-client/Makefile index caa05c3f804..aedbdf9c376 100644 --- regress/usr.sbin/acme-client/Makefile +++ regress/usr.sbin/acme-client/Makefile @@ -40,6 +40,12 @@ etc/acme-client-profile.conf: acme-client-profile.conf ${.CURDIR}/acme-client-profile.conf \ > etc/acme-client-profile.conf +etc/acme-client-ipaddress.conf: acme-client-ipaddress.conf + mkdir -p etc + sed 's,$${.OBJDIR},${.OBJDIR},'\ + ${.CURDIR}/acme-client-ipaddress.conf \ + > etc/acme-client-ipaddress.conf + etc/httpd.conf: httpd.conf mkdir -p etc sed 's,$${.OBJDIR},${.OBJDIR},'\ @@ -85,6 +91,24 @@ run-regress-acme-profile: etc/acme-client-profile.conf -f ${.OBJDIR}/etc/acme-client-profile.conf \ -r -v localhost +REGRESS_TARGETS += run-regress-acme-ipaddress +run-regress-acme-ipaddress: etc/acme-client-ipaddress.conf + ${SUDO} /usr/sbin/acme-client \ + -f ${.OBJDIR}/etc/acme-client-ipaddress.conf \ + -v localhost + ${SUDO} /usr/sbin/acme-client \ + -f ${.OBJDIR}/etc/acme-client-ipaddress.conf \ + -r -v localhost + +REGRESS_TARGETS += run-regress-acme-ipaddress2 +run-regress-acme-ipaddress2: etc/acme-client-ipaddress.conf + ${SUDO} /usr/sbin/acme-client \ + -f ${.OBJDIR}/etc/acme-client-ipaddress.conf \ + -v ::1 + ${SUDO} /usr/sbin/acme-client \ + -f ${.OBJDIR}/etc/acme-client-ipaddress.conf \ + -r -v ::1 + REGRESS_TARGETS += run-regress-cleanup run-regress-cleanup: ${.MAKE} -C ${.CURDIR} httpd-stop diff --git regress/usr.sbin/acme-client/acme-client-ipaddress.conf regress/usr.sbin/acme-client/acme-client-ipaddress.conf new file mode 100644 index 00000000000..15cea54a7a4 --- /dev/null +++ regress/usr.sbin/acme-client/acme-client-ipaddress.conf @@ -0,0 +1,22 @@ +authority pebble { + account key "${.OBJDIR}/etc/acme/privkey.pem" + api url https://127.0.0.1:14000/dir + insecure +} +domain localhost { + domain key "${.OBJDIR}/etc/ssl/acme/private/privkey.pem" + domain certificate "${.OBJDIR}/etc/ssl/acme/cert.pem" + domain name 127.0.0.1 + alternative names {::1, "localhost"} + profile "shortlived" + sign with "pebble" + challengedir "${.OBJDIR}/www/acme" +} + +domain ::1 { + domain key "${.OBJDIR}/etc/ssl/acme/private/privkey2.pem" + domain certificate "${.OBJDIR}/etc/ssl/acme/cert2.pem" + profile "shortlived" + sign with "pebble" + challengedir "${.OBJDIR}/www/acme" +} diff --git usr.sbin/acme-client/extern.h usr.sbin/acme-client/extern.h index 647ff0d332f..01b03495c6a 100644 --- usr.sbin/acme-client/extern.h +++ usr.sbin/acme-client/extern.h @@ -204,15 +204,12 @@ int acctproc(int, const char *, enum keytype); int certproc(int, int); int chngproc(int, const char *); int dnsproc(int); -int revokeproc(int, const char *, int, int, const char *const *, - size_t); +int revokeproc(int, const char *, int, int, struct domain_c *); int fileproc(int, const char *, const char *, const char *, const char *); -int keyproc(int, const char *, const char **, size_t, - enum keytype); +int keyproc(int, struct domain_c *); int netproc(int, int, int, int, int, int, int, - struct authority_c *, const char *const *, - size_t, const char *); + struct authority_c *, struct domain_c *); /* * Debugging functions. @@ -263,7 +260,7 @@ char *json_getstr(struct jsmnn *, const char *); 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, const char *); +char *json_fmt_neworder(struct domain_c *); char *json_fmt_protected_rsa(const char *, const char *, const char *, const char *); char *json_fmt_protected_ec(const char *, const char *, const char *, diff --git usr.sbin/acme-client/json.c usr.sbin/acme-client/json.c index 9b2a46b05fe..aaf8c55e467 100644 --- usr.sbin/acme-client/json.c +++ usr.sbin/acme-client/json.c @@ -647,20 +647,31 @@ json_fmt_newacc(const char *contact) * Format the "newOrder" resource request */ char * -json_fmt_neworder(const char *const *alts, size_t altsz, const char *profile) +json_fmt_neworder(struct domain_c *domain) { - size_t i; - int c; - char *p, *t; + int c, first; + char *p, *t; + struct altname_c *ac; if ((p = strdup("{ \"identifiers\": [")) == NULL) goto err; t = p; - for (i = 0; i < altsz; i++) { - c = asprintf(&p, - "%s { \"type\": \"dns\", \"value\": \"%s\" }%s", - t, alts[i], i + 1 == altsz ? "" : ","); + first = 1; + TAILQ_FOREACH(ac, &domain->altname_list, entry) { + switch (ac->idtype) { + case ID_DNS: + c = asprintf(&p, "%s%s { \"type\": \"dns\", " + "\"value\": \"%s\" }", t, first ? "" : ",", + ac->domain); + break; + case ID_IP: + c = asprintf(&p, "%s%s { \"type\": \"ip\", " + "\"value\": \"%s\" }", t, first ? "" : ",", + ac->domain); + break; + } + first = 0; free(t); if (c == -1) { warn("asprintf"); @@ -669,10 +680,11 @@ json_fmt_neworder(const char *const *alts, size_t altsz, const char *profile) } t = p; } - if (profile == NULL) + if (domain->profile == NULL) c = asprintf(&p, "%s ] }", t); else - c = asprintf(&p, "%s ], \"profile\": \"%s\" }", t, profile); + c = asprintf(&p, "%s ], \"profile\": \"%s\" }", t, + domain->profile); free(t); if (c == -1) { warn("asprintf"); diff --git usr.sbin/acme-client/keyproc.c usr.sbin/acme-client/keyproc.c index 977bb3224ec..f640b03a1b6 100644 --- usr.sbin/acme-client/keyproc.c +++ usr.sbin/acme-client/keyproc.c @@ -74,19 +74,20 @@ add_ext(STACK_OF(X509_EXTENSION) *sk, int nid, const char *value) * jail and, on success, ship it to "netsock" as an X509 request. */ int -keyproc(int netsock, const char *keyfile, const char **alts, size_t altsz, - enum keytype keytype) +keyproc(int netsock, struct domain_c *domain) { char *der64 = NULL, *der = NULL, *dercp; char *sans = NULL, *san = NULL; FILE *f; - size_t i, sansz; + size_t sansz; void *pp; EVP_PKEY *pkey = NULL; X509_REQ *x = NULL; - int len, rc = 0, cc, nid, newkey = 0; + int len, rc = 0, cc, nid, newkey = 0, first; mode_t prev; STACK_OF(X509_EXTENSION) *exts = NULL; + struct altname_c *ac; + const char *keyfile = domain->key; /* * First, open our private key file read-only or write-only if @@ -116,7 +117,7 @@ keyproc(int netsock, const char *keyfile, const char **alts, size_t altsz, } if (newkey) { - switch (keytype) { + switch (domain->keytype) { case KT_ECDSA: if ((pkey = ec_key_create(f, keyfile)) == NULL) goto out; @@ -180,9 +181,19 @@ keyproc(int netsock, const char *keyfile, const char **alts, size_t altsz, * domains: NOT an entry per domain! */ - for (i = 0; i < altsz; i++) { - cc = asprintf(&san, "%sDNS:%s", - i ? "," : "", alts[i]); + first = 1; + TAILQ_FOREACH(ac, &domain->altname_list, entry) { + switch (ac->idtype) { + case ID_DNS: + cc = asprintf(&san, "%sDNS:%s", first ? "" : ",", + ac->domain); + break; + case ID_IP: + cc = asprintf(&san, "%sIP:%s", first ? "" : ",", + ac->domain); + break; + } + first = 0; if (cc == -1) { warn("asprintf"); goto out; diff --git usr.sbin/acme-client/main.c usr.sbin/acme-client/main.c index 2478adb2103..ae92e475fda 100644 --- usr.sbin/acme-client/main.c +++ usr.sbin/acme-client/main.c @@ -40,7 +40,6 @@ enum comp proccomp; int main(int argc, char *argv[]) { - const char **alts = NULL; char *certdir = NULL; char *chngdir = NULL, *auth = NULL; char *conffile = CONF_FILE; @@ -51,7 +50,7 @@ main(int argc, char *argv[]) int c, rc, revocate = 0; int popts = 0; pid_t pids[COMP__MAX]; - size_t i, altsz, ne; + size_t ne; struct acme_conf *conf = NULL; struct authority_c *authority = NULL; @@ -112,7 +111,7 @@ main(int argc, char *argv[]) if ((tmpsd = dirname(tmps)) == NULL) err(EXIT_FAILURE, "dirname"); if ((certdir = strdup(tmpsd)) == NULL) - err(EXIT_FAILURE, "strdup"); + err(EXIT_FAILURE, "strdup"); free(tmps); tmps = tmpsd = NULL; @@ -174,15 +173,15 @@ main(int argc, char *argv[]) return EXIT_SUCCESS; /* Set the zeroth altname as our domain. */ - altsz = domain->altname_count + 1; - alts = calloc(altsz, sizeof(char *)); - if (alts == NULL) + + ac = calloc(1, sizeof(struct altname_c)); + if (ac == NULL) err(EXIT_FAILURE, "calloc"); - alts[0] = domain->domain; - i = 1; - /* XXX get rid of alts[] later */ - TAILQ_FOREACH(ac, &domain->altname_list, entry) - alts[i++] = ac->domain; + + ac->domain = domain->domain; + ac->idtype = domain->idtype; + TAILQ_INSERT_HEAD(&domain->altname_list, ac, entry); + domain->altname_count++; /* * Open channels between our components. @@ -223,9 +222,7 @@ main(int argc, char *argv[]) c = netproc(key_fds[1], acct_fds[1], chng_fds[1], cert_fds[1], dns_fds[1], rvk_fds[1], - revocate, authority, - (const char *const *)alts, altsz, - domain->profile); + revocate, authority, domain); exit(c ? EXIT_SUCCESS : EXIT_FAILURE); } @@ -250,9 +247,7 @@ main(int argc, char *argv[]) close(chng_fds[0]); close(file_fds[0]); close(file_fds[1]); - c = keyproc(key_fds[0], domain->key, - (const char **)alts, altsz, - domain->keytype); + c = keyproc(key_fds[0], domain); exit(c ? EXIT_SUCCESS : EXIT_FAILURE); } @@ -355,8 +350,7 @@ main(int argc, char *argv[]) if (pids[COMP_REVOKE] == 0) { proccomp = COMP_REVOKE; c = revokeproc(rvk_fds[0], domain->cert != NULL ? domain->cert : - domain->fullchain, force, revocate, - (const char *const *)alts, altsz); + domain->fullchain, force, revocate, domain); exit(c ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git usr.sbin/acme-client/netproc.c usr.sbin/acme-client/netproc.c index 02fc0af6cb8..69e355838ce 100644 --- usr.sbin/acme-client/netproc.c +++ usr.sbin/acme-client/netproc.c @@ -492,15 +492,15 @@ dochkacc(struct conn *c, const struct capaths *p, const char *contact) * Submit a new order for a certificate. */ static int -doneworder(struct conn *c, const char *const *alts, size_t altsz, - struct order *order, const struct capaths *p, const char *profile) +doneworder(struct conn *c, struct domain_c *domain, struct order *order, + const struct capaths *p) { struct jsmnn *j = NULL; int rc = 0; char *req; long lc; - if ((req = json_fmt_neworder(alts, altsz, profile)) == NULL) + if ((req = json_fmt_neworder(domain)) == NULL) warnx("json_fmt_neworder"); else if ((lc = sreq(c, p->neworder, 1, req, &order->uri)) < 0) warnx("%s: bad comm", p->neworder); @@ -723,7 +723,7 @@ dodirs(struct conn *c, const char *addr, struct capaths *paths) 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 *profile) + struct domain_c *domain) { int rc = 0, retries = 0; size_t i; @@ -828,7 +828,7 @@ netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd, memset(&order, 0, sizeof(order)); - if (!doneworder(&c, alts, altsz, &order, &paths, profile)) + if (!doneworder(&c, domain, &order, &paths)) goto out; chngs = calloc(order.authsz, sizeof(struct chng)); diff --git usr.sbin/acme-client/parse.h usr.sbin/acme-client/parse.h index b30b7e9d43a..120c727b9f4 100644 --- usr.sbin/acme-client/parse.h +++ usr.sbin/acme-client/parse.h @@ -32,6 +32,11 @@ enum keytype { KT_ECDSA }; +enum identifiertype { + ID_DNS = 0, /* RFC 8555 */ + ID_IP /* RFC 8738 */ +}; + struct authority_c { TAILQ_ENTRY(authority_c) entry; char *name; @@ -47,6 +52,7 @@ struct domain_c { TAILQ_HEAD(, altname_c) altname_list; int altname_count; enum keytype keytype; + enum identifiertype idtype; char *handle; char *domain; char *key; @@ -60,7 +66,8 @@ struct domain_c { struct altname_c { TAILQ_ENTRY(altname_c) entry; - char *domain; + char *domain; + enum identifiertype idtype; }; struct keyfile { @@ -87,5 +94,6 @@ struct authority_c *authority_find0(struct acme_conf *); struct domain_c *domain_find_handle(struct acme_conf *, char *); int domain_valid(const char *); +const char *ip_valid(const char *); #endif /* PARSE_H */ diff --git usr.sbin/acme-client/parse.y usr.sbin/acme-client/parse.y index 561879f7931..8bed7ee00bf 100644 --- usr.sbin/acme-client/parse.y +++ usr.sbin/acme-client/parse.y @@ -25,12 +25,15 @@ %{ #include +#include #include #include +#include #include #include #include #include +#include #include #include #include @@ -103,7 +106,6 @@ typedef struct { %token AUTHORITY URL API ACCOUNT CONTACT %token DOMAIN ALTERNATIVE NAME NAMES CERT FULL CHAIN KEY SIGN WITH %token CHALLENGEDIR PROFILE -%token YES NO %token INCLUDE %token ERROR %token RSA ECDSA @@ -221,7 +223,7 @@ authorityoptsl : API URL STRING { err(EXIT_FAILURE, "strdup"); auth->api = s; } - | ACCOUNT KEY STRING keytype{ + | ACCOUNT KEY STRING keytype { char *s; if (auth->account != NULL) { yyerror("duplicate account"); @@ -251,7 +253,7 @@ domain : DOMAIN STRING { char *s; if ((s = strdup($2)) == NULL) err(EXIT_FAILURE, "strdup"); - if (!domain_valid(s)) { + if (!ip_valid(s) && !domain_valid(s)) { yyerror("%s: bad domain syntax", s); free(s); YYERROR; @@ -262,10 +264,21 @@ domain : DOMAIN STRING { YYERROR; } } '{' optnl domainopts_l '}' { + char *s; + const char *ip; + if (domain->domain == NULL) { - if ((domain->domain = strdup(domain->handle)) - == NULL) - err(EXIT_FAILURE, "strdup"); + if ((ip = ip_valid(domain->handle)) != NULL) { + domain->idtype = ID_IP; + if ((s = strdup(ip)) == NULL) + err(EXIT_FAILURE, "strdup"); + } else { + if ((s = strdup(domain->handle)) == + NULL) + err(EXIT_FAILURE, "strdup"); + domain->idtype = ID_DNS; + } + domain->domain = s; } /* enforce minimum config here */ if (domain->key == NULL) { @@ -294,13 +307,27 @@ domainopts_l : domainopts_l domainoptsl nl domainoptsl : ALTERNATIVE NAMES '{' optnl altname_l '}' | DOMAIN NAME STRING { - char *s; + char *s; + const char *ip; + if (domain->domain != NULL) { yyerror("duplicate domain name"); YYERROR; } - if ((s = strdup($3)) == NULL) - err(EXIT_FAILURE, "strdup"); + + if ((ip = ip_valid($3)) != NULL) { + domain->idtype = ID_IP; + if ((s = strdup(ip)) == NULL) + err(EXIT_FAILURE, "strdup"); + } else { + if (!domain_valid($3)) { + yyerror("bad domain name syntax"); + YYERROR; + } + domain->idtype = ID_DNS; + if ((s = strdup($3)) == NULL) + err(EXIT_FAILURE, "strdup"); + } domain->domain = s; } | DOMAIN KEY STRING keytype { @@ -416,17 +443,26 @@ altname_l : altname optcommanl altname_l altname : STRING { char *s; + const char *ip; struct altname_c *ac; - if (!domain_valid($1)) { - yyerror("bad domain syntax"); - YYERROR; - } + if ((ac = calloc(1, sizeof(struct altname_c))) == NULL) err(EXIT_FAILURE, "calloc"); - if ((s = strdup($1)) == NULL) { - free(ac); - err(EXIT_FAILURE, "strdup"); + + if ((ip = ip_valid($1)) != NULL) { + ac->idtype = ID_IP; + if ((s = strdup(ip)) == NULL) + err(EXIT_FAILURE, "strdup"); + } else { + if (!domain_valid($1)) { + yyerror("bad domain name syntax"); + YYERROR; + } + ac->idtype = ID_DNS; + if ((s = strdup($1)) == NULL) + err(EXIT_FAILURE, "strdup"); } + ac->domain = s; TAILQ_INSERT_TAIL(&domain->altname_list, ac, entry); domain->altname_count++; @@ -1127,6 +1163,31 @@ domain_valid(const char *cp) return 1; } +const char * +ip_valid(const char *ip) +{ + static char ip_buf[NI_MAXHOST]; + struct addrinfo hints, *res; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + + error = getaddrinfo(ip, NULL, &hints, &res); + if (error) + return NULL; + + error = getnameinfo(res->ai_addr, res->ai_addrlen, ip_buf, + sizeof(ip_buf), NULL, 0, NI_NUMERICHOST); + + if (error) + return NULL; + + return ip_buf; +} + + int conf_check_file(char *s) { diff --git usr.sbin/acme-client/revokeproc.c usr.sbin/acme-client/revokeproc.c index 65170078226..84a923baa3e 100644 --- usr.sbin/acme-client/revokeproc.c +++ usr.sbin/acme-client/revokeproc.c @@ -15,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include #include #include #include @@ -74,19 +76,18 @@ X509notbefore(X509 *x) int revokeproc(int fd, const char *certfile, int force, - int revocate, const char *const *alts, size_t altsz) + int revocate, struct domain_c *domain) { GENERAL_NAMES *sans = NULL; char *der = NULL, *dercp, *der64 = NULL; - int rc = 0, cc, i, len; - size_t *found = NULL; + int rc = 0, cc, sanidx, len, j, k; + int *found_altnames = NULL; FILE *f = NULL; X509 *x = NULL; long lval; enum revokeop op, rop; time_t notafter, notbefore, cert_validity; time_t remaining_validity, renew_allow; - size_t j; /* * First try to open the certificate before we drop privileges @@ -162,7 +163,8 @@ revokeproc(int fd, const char *certfile, int force, /* An array of buckets: the number of entries found. */ - if ((found = calloc(altsz, sizeof(size_t))) == NULL) { + if ((found_altnames = (int *)calloc(domain->altname_count, + sizeof(int))) == NULL) { warn("calloc"); goto out; } @@ -172,63 +174,120 @@ revokeproc(int fd, const char *certfile, int force, * configuration file and that all domains are represented only once. */ - for (i = 0; i < sk_GENERAL_NAME_num(sans); i++) { + for (sanidx = 0; sanidx < sk_GENERAL_NAME_num(sans); sanidx++) { GENERAL_NAME *gen_name; - const ASN1_IA5STRING *name; - const unsigned char *name_buf; + char *name_buf = NULL; int name_len; - int name_type; + struct altname_c *ac; - gen_name = sk_GENERAL_NAME_value(sans, i); + gen_name = sk_GENERAL_NAME_value(sans, sanidx); assert(gen_name != NULL); - name = GENERAL_NAME_get0_value(gen_name, &name_type); - if (name_type != GEN_DNS) + if (gen_name->type == GEN_IPADD) { + char ip_buf[INET6_ADDRSTRLEN]; + const char *ip; + + name_len = gen_name->d.iPAddress->length; + switch (name_len) { + case 4: + ip = inet_ntop(AF_INET, + gen_name->d.iPAddress->data, + ip_buf, INET6_ADDRSTRLEN); + break; + case 16: + ip = inet_ntop(AF_INET6, + gen_name->d.iPAddress->data, + ip_buf, INET6_ADDRSTRLEN); + break; + default: + ip = NULL; + break; + } + if (ip == NULL) { + warnx("invalid IP address"); + continue; + } + name_len = asprintf(&name_buf, "%s", ip); + } else if (gen_name->type == GEN_DNS) { + name_len = gen_name->d.dNSName->length; + name_len = asprintf(&name_buf, "%.*s", + name_len, gen_name->d.dNSName->data); + } else continue; - /* name_buf isn't a C string and could contain embedded NULs. */ - name_buf = ASN1_STRING_get0_data(name); - name_len = ASN1_STRING_length(name); + if (name_len == -1) { + warn("asprintf"); + continue; + } - for (j = 0; j < altsz; j++) { - if ((size_t)name_len != strlen(alts[j])) - continue; - if (memcmp(name_buf, alts[j], name_len) == 0) + j = 0; + TAILQ_FOREACH(ac, &domain->altname_list, entry) { + if (strcmp(name_buf, ac->domain) == 0) { + found_altnames[j]++; break; + } + /* increment if didn't match */ + j++; } - if (j == altsz) { + if (j >= domain->altname_count) { + /* we haven't matched any */ if (revocate) { char *visbuf; visbuf = calloc(4, name_len + 1); if (visbuf == NULL) { - warn("%s: unexpected SAN", certfile); + warn("%s: unexpected SAN in " + "certificate", certfile); + free(name_buf); goto out; } strvisx(visbuf, name_buf, name_len, VIS_SAFE); - warnx("%s: unexpected SAN entry: %s", - certfile, visbuf); + warnx("%s: unexpected SAN entry in " + "certificate: %s", certfile, visbuf); free(visbuf); + free(name_buf); goto out; } force = 2; continue; } - if (found[j]++) { + /* should not reach here if j is out of bounds */ + if (found_altnames[j] > 1) { if (revocate) { - warnx("%s: duplicate SAN entry: %.*s", - certfile, name_len, name_buf); + char *visbuf; + visbuf = calloc(4, name_len + 1); + if (visbuf == NULL) { + warn("%s: duplicate SAN in " + "certificate", certfile); + free(name_buf); + goto out; + } + warnx("%s: duplicate SAN entry in " + "certificate: %s", certfile, visbuf); + free(name_buf); + free(visbuf); goto out; } force = 2; } + + free(name_buf); } - for (j = 0; j < altsz; j++) { - if (found[j]) + for (j = 0; j < domain->altname_count; j++) { + struct altname_c *ac; + + if (found_altnames[j]) continue; if (revocate) { - warnx("%s: domain not listed: %s", certfile, alts[j]); + k = 0; + TAILQ_FOREACH(ac, &domain->altname_list, entry) { + if (j == k) + break; + k++; + } + warnx("%s: domain not listed: %s", certfile, + ac->domain); goto out; } force = 2; @@ -340,7 +399,7 @@ out: X509_free(x); GENERAL_NAMES_free(sans); free(der); - free(found); + free(found_altnames); free(der64); ERR_print_errors_fp(stderr); ERR_free_strings(); -- In my defence, I have been left unsupervised.