Download raw body.
[stu@spacehopper.org: Re: acme-client(1): add support for let's encrypt iPAddress certificates]
[stu@spacehopper.org: Re: acme-client(1): add support for let's encrypt iPAddress certificate
[stu@spacehopper.org: Re: acme-client(1): add support for let's encrypt iPAddress certificate
On 2026-02-21 14:40 +01, Florian Obser <florian@openbsd.org> wrote:
> On 2026-02-20 14:20 UTC, Stuart Henderson <stu@spacehopper.org> 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 <sys/types.h>
+#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/stat.h>
+#include <arpa/inet.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
+#include <netdb.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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 <sys/socket.h>
+#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
@@ -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.
[stu@spacehopper.org: Re: acme-client(1): add support for let's encrypt iPAddress certificates]
[stu@spacehopper.org: Re: acme-client(1): add support for let's encrypt iPAddress certificate
[stu@spacehopper.org: Re: acme-client(1): add support for let's encrypt iPAddress certificate