Download raw body.
acme-client(1): add support for let's encrypt iPAddress certificates
acme-client(1): add support for let's encrypt iPAddress certificates
acme-client(1): add support for let's encrypt iPAddress certificates
acme-client(1): add support for let's encrypt iPAddress certificates
acme-client(1): add support for let's encrypt iPAddress certificates
> Diff below merges this to -current. Works for me with a shortlived IP
> address cert on letsencrypt staging, with a standard cert on letsencrypt
> prod, and src/regress/usr.sbin/acme-client (using pebble) is still
> happy.
this is now live in prod on letsencrypt, but beware if testing, there
is a bug.
if you list IPv6 addresses, it hits "domain list changed, forcing
renewal" on every renewal.
this is because, when setting up found_altnames, it's doing a memcmp()
between the expanded v6 address and the compressed one, i.e.
memcmp("xxxx:xxxx:0001:0101:0000:0000:0000:0002", "xxxx:xxxx:1:101::2", 39
updated diff below uses inet_ntop, rather than hand-rolled functions,
to generate strings from the addresses in an existing cert.
I also dropped the "with cn" option and setting Subject in the CSR.
(I think v6 addresses probably ought to be normalised to the inet_ntop
form when read from the config file too - I haven't done that in this
diff. Looks like letsencrypt won't issue a cert if you try to use the
expanded form anyway).
Index: acme-client.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/acme-client.conf.5,v
diff -u -p -r1.32 acme-client.conf.5
--- acme-client.conf.5 16 Sep 2025 15:06:02 -0000 1.32
+++ acme-client.conf.5 17 Dec 2025 12:39:56 -0000
@@ -115,6 +115,12 @@ The certificates to be obtained through
Each domain section begins with the
.Ic domain
keyword followed by an identifier for this domain block.
+If requesting a certificate with only an IP address,
+or if requesting two certificates of different types (EC and RSA)
+for the same name,
+an additional
+.Cm domain name
+directive must follow.
.El
.Pp
It is followed by a block of options enclosed in curly brackets:
@@ -122,19 +128,20 @@ It is followed by a block of options enc
.It Ic domain name Ar name
The
.Ar name
-to be used as the common name component of the subject of the
-X.509 certificate.
-This is optional.
+to be used as the primary Subject Alternative Name
+in the X.509 certificate.
+This is optional for SANs of type DNS.
If not specified, the
.Ar handle
-of the domain block will be used as common name.
+of the domain block will be used as the primary SAN.
+To use a SAN of type iPAddress, specify an IP address prefixed with
+.Cm ip: .
.It Ic alternative names Brq ...
A list of alternative names,
comma or space separated,
for which the certificate will be valid.
-The common name is included automatically if this option is present,
-but there is no automatic conversion/inclusion between "www." and
-plain domain name forms.
+To use a SAN of type iPAddress, specify an IP address prefixed with
+.Cm ip: .
.It Ic domain key Ar file Op Ar keytype
The private key file for which the certificate will be obtained.
.Ar keytype
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/extern.h,v
diff -u -p -r1.22 extern.h
--- extern.h 16 Sep 2025 15:06:02 -0000 1.22
+++ extern.h 17 Dec 2025 12:39:56 -0000
@@ -204,15 +204,12 @@ int acctproc(int, const char *, enum k
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 *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 *,
Index: json.c
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/json.c,v
diff -u -p -r1.22 json.c
--- json.c 16 Sep 2025 15:06:02 -0000 1.22
+++ json.c 17 Dec 2025 12:39:56 -0000
@@ -647,20 +647,32 @@ 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;
+ char *p, *t;
+ struct altname_c *ac, *first;
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 = TAILQ_FIRST(&domain->altname_list);
+ TAILQ_FOREACH(ac, &domain->altname_list, entry) {
+ switch (ac->idtype) {
+ case ID_DNS:
+ c = asprintf(&p,
+ "%s%s { \"type\": \"dns\", "
+ "\"value\": \"%s\" }",
+ t, ac == first ? "" : ",", ac->domain);
+ break;
+ case ID_IP:
+ c = asprintf(&p,
+ "%s%s { \"type\": \"ip\", "
+ "\"value\": \"%s\" }",
+ t, ac == first ? "" : ",", ac->domain);
+ break;
+ }
free(t);
if (c == -1) {
warn("asprintf");
@@ -669,10 +681,11 @@ json_fmt_neworder(const char *const *alt
}
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");
Index: keyproc.c
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/keyproc.c,v
diff -u -p -r1.18 keyproc.c
--- keyproc.c 28 Aug 2022 18:30:29 -0000 1.18
+++ keyproc.c 17 Dec 2025 12:39:56 -0000
@@ -74,13 +74,12 @@ add_ext(STACK_OF(X509_EXTENSION) *sk, in
* 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;
@@ -88,6 +87,8 @@ keyproc(int netsock, const char *keyfile
int len, rc = 0, cc, nid, newkey = 0;
mode_t prev;
STACK_OF(X509_EXTENSION) *exts = NULL;
+ struct altname_c *ac, *first;
+ const char *keyfile = domain->key;
/*
* First, open our private key file read-only or write-only if
@@ -117,7 +118,7 @@ keyproc(int netsock, const char *keyfile
}
if (newkey) {
- switch (keytype) {
+ switch (domain->keytype) {
case KT_ECDSA:
if ((pkey = ec_key_create(f, keyfile)) == NULL)
goto out;
@@ -155,20 +156,6 @@ keyproc(int netsock, const char *keyfile
goto out;
}
- /* Now specify the common name that we'll request. */
-
- if ((name = X509_NAME_new()) == NULL) {
- warnx("X509_NAME_new");
- goto out;
- } else if (!X509_NAME_add_entry_by_txt(name, "CN",
- MBSTRING_ASC, (u_char *)alts[0], -1, -1, 0)) {
- warnx("X509_NAME_add_entry_by_txt: CN=%s", alts[0]);
- goto out;
- } else if (!X509_REQ_set_subject_name(x, name)) {
- warnx("X509_req_set_issuer_name");
- goto out;
- }
-
/*
* Now add the SAN extensions.
* This was lifted more or less directly from demos/x509/mkreq.c
@@ -195,9 +182,18 @@ keyproc(int netsock, const char *keyfile
* domains: NOT an entry per domain!
*/
- for (i = 0; i < altsz; i++) {
- cc = asprintf(&san, "%sDNS:%s",
- i ? "," : "", alts[i]);
+ first = TAILQ_FIRST(&domain->altname_list);
+ TAILQ_FOREACH(ac, &domain->altname_list, entry) {
+ switch (ac->idtype) {
+ case ID_DNS:
+ cc = asprintf(&san, "%sDNS:%s",
+ ac == first ? "" : ",", ac->domain);
+ break;
+ case ID_IP:
+ cc = asprintf(&san, "%sIP:%s",
+ ac == first ? "" : ",", ac->domain);
+ break;
+ }
if (cc == -1) {
warn("asprintf");
goto out;
Index: main.c
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/main.c,v
diff -u -p -r1.57 main.c
--- main.c 16 Sep 2025 15:06:02 -0000 1.57
+++ main.c 17 Dec 2025 12:39:56 -0000
@@ -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,12 @@ 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)
- 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 = calloc(1, sizeof(struct altname_c));
+ 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 +219,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 +244,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 +347,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);
}
Index: netproc.c
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/netproc.c,v
diff -u -p -r1.45 netproc.c
--- netproc.c 16 Sep 2025 15:06:02 -0000 1.45
+++ netproc.c 17 Dec 2025 12:39:56 -0000
@@ -492,15 +492,15 @@ dochkacc(struct conn *c, const struct ca
* 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,
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 c
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));
Index: parse.h
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/parse.h,v
diff -u -p -r1.17 parse.h
--- parse.h 16 Sep 2025 15:06:02 -0000 1.17
+++ parse.h 17 Dec 2025 12:39:56 -0000
@@ -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(stru
struct domain_c *domain_find_handle(struct acme_conf *, char *);
int domain_valid(const char *);
+int altname_valid(const char *);
#endif /* PARSE_H */
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/parse.y,v
diff -u -p -r1.47 parse.y
--- parse.y 16 Sep 2025 15:06:02 -0000 1.47
+++ parse.y 17 Dec 2025 12:39:56 -0000
@@ -103,7 +103,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 +220,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");
@@ -299,8 +298,21 @@ domainoptsl : ALTERNATIVE NAMES '{' optn
yyerror("duplicate domain name");
YYERROR;
}
- if ((s = strdup($3)) == NULL)
- err(EXIT_FAILURE, "strdup");
+ if (!altname_valid($3)) {
+ yyerror("bad domain name syntax");
+ YYERROR;
+ }
+ if ((strncmp($3, "ip:", 3) == 0) &&
+ (strlen($3) > 3)) {
+ domain->idtype = ID_IP;
+ if ((s = strdup(($3) + 3)) == NULL)
+ err(EXIT_FAILURE, "strdup");
+ }
+ else {
+ domain->idtype = ID_DNS;
+ if ((s = strdup($3)) == NULL)
+ err(EXIT_FAILURE, "strdup");
+ }
domain->domain = s;
}
| DOMAIN KEY STRING keytype {
@@ -408,6 +420,16 @@ domainoptsl : ALTERNATIVE NAMES '{' optn
err(EXIT_FAILURE, "strdup");
domain->profile = s;
}
+ | PROFILE STRING {
+ char *s;
+ if (domain->profile != NULL) {
+ yyerror("duplicate profile");
+ YYERROR;
+ }
+ if ((s = strdup($2)) == NULL)
+ err(EXIT_FAILURE, "strdup");
+ domain->profile = s;
+ }
;
altname_l : altname optcommanl altname_l
@@ -417,15 +439,26 @@ altname_l : altname optcommanl altname_l
altname : STRING {
char *s;
struct altname_c *ac;
- if (!domain_valid($1)) {
- yyerror("bad domain syntax");
+ if (!altname_valid($1)) {
+ yyerror("bad altname 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 ((strncmp($1, "ip:", 3) == 0) &&
+ (strlen($1) > 3)) {
+ ac->idtype = ID_IP;
+ if ((s = strdup(($1) + 3)) == NULL) {
+ free(ac);
+ err(EXIT_FAILURE, "strdup");
+ }
+ }
+ else {
+ ac->idtype = ID_DNS;
+ if ((s = strdup($1)) == NULL) {
+ free(ac);
+ err(EXIT_FAILURE, "strdup");
+ }
}
ac->domain = s;
TAILQ_INSERT_TAIL(&domain->altname_list, ac, entry);
@@ -1079,12 +1112,15 @@ print_config(struct acme_conf *xconf)
f = 0;
printf("domain %s {\n", d->handle);
if (d->domain != NULL)
- printf("\tdomain name \"%s\"\n", d->domain);
+ printf("\tdomain name \"%s%s\"\n",
+ (d->idtype == ID_IP) ? "ip:" : "", d->domain);
TAILQ_FOREACH(ac, &d->altname_list, entry) {
if (!f)
printf("\talternative names {");
if (ac->domain != NULL) {
- printf("%s%s", f ? ", " : " ", ac->domain);
+ printf("%s%s%s", f ? ", " : " ",
+ (d->idtype == ID_IP) ? "ip:" : "",
+ ac->domain);
f = 1;
}
}
@@ -1123,6 +1159,17 @@ domain_valid(const char *cp)
for ( ; *cp != '\0'; cp++)
if (!(*cp == '.' || *cp == '-' ||
*cp == '_' || isalnum((unsigned char)*cp)))
+ return 0;
+ return 1;
+}
+
+int
+altname_valid(const char *cp)
+{
+
+ for ( ; *cp != '\0'; cp++)
+ if (!(*cp == '.' || *cp == '-' ||
+ *cp == ':' || isalnum((unsigned char)*cp)))
return 0;
return 1;
}
Index: revokeproc.c
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/revokeproc.c,v
diff -u -p -r1.26 revokeproc.c
--- revokeproc.c 18 Sep 2025 13:22:36 -0000 1.26
+++ revokeproc.c 17 Dec 2025 12:39:56 -0000
@@ -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,
/* 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,49 +174,80 @@ revokeproc(int fd, const char *certfile,
* 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;
+ unsigned char *name_buf;
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[INET6_ADDRSTRLEN];
+ name_len = gen_name->d.iPAddress->length;
+ if (name_len == 4)
+ asprintf((char **)&name_buf, "%s",
+ inet_ntop(AF_INET,
+ gen_name->d.iPAddress->data,
+ ip, INET6_ADDRSTRLEN));
+ else if (name_len == 16)
+ asprintf((char **)&name_buf, "%s",
+ inet_ntop(AF_INET6,
+ gen_name->d.iPAddress->data,
+ ip, INET6_ADDRSTRLEN));
+ else {
+ warnx("invalid name_len %d", name_len);
+ continue;
+ }
+ }
+ else if (gen_name->type == GEN_DNS) {
+ name_len = gen_name->d.dNSName->length;
+ asprintf((char **)&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);
+ /* now we have a real C string */
+ name_len = strlen(name_buf);
- 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) {
+ int xx;
+ xx = memcmp(name_buf, ac->domain, name_len);
+ warnx("<< memcmp(%s, %s, %d) = %d >>", name_buf, ac->domain, name_len, xx);
+ if (xx == 0) {
+ found_altnames[j]++;
break;
+ }
+ /* increment if didn't match */
+ j++;
}
- if (j == altsz) {
+ if (j >= domain->altname_count) {
+ warnx("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);
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);
goto out;
}
force = 2;
continue;
}
- if (found[j]++) {
+ /* should not reach here if j is out of bounds */
+ if (found_altnames[j] > 1) {
+ warnx("<< dup san entry %.*s >>", name_len, name_buf);
if (revocate) {
warnx("%s: duplicate SAN entry: %.*s",
certfile, name_len, name_buf);
@@ -224,13 +257,23 @@ revokeproc(int fd, const char *certfile,
}
}
- 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;
}
+ warnx("<< altname not found, force=2 >>");
force = 2;
}
@@ -340,7 +383,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();
acme-client(1): add support for let's encrypt iPAddress certificates
acme-client(1): add support for let's encrypt iPAddress certificates
acme-client(1): add support for let's encrypt iPAddress certificates
acme-client(1): add support for let's encrypt iPAddress certificates
acme-client(1): add support for let's encrypt iPAddress certificates