-/* $Id: revokeproc.c,v 1.23 2022/12/15 17:36:56 tb Exp $ */
+/* $Id: revokeproc.c,v 1.24 2022/12/17 13:53:38 tb Exp $ */
/*
* Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
*
revokeproc(int fd, const char *certfile, int force,
int revocate, const char *const *alts, size_t altsz)
{
+ GENERAL_NAMES *sans = NULL;
char *der = NULL, *dercp, *der64 = NULL;
- char *san = NULL, *str, *tok;
- int rc = 0, cc, i, ssz, len;
+ int rc = 0, cc, i, len;
size_t *found = NULL;
- BIO *bio = NULL;
FILE *f = NULL;
X509 *x = NULL;
long lval;
enum revokeop op, rop;
time_t t;
- const STACK_OF(X509_EXTENSION) *exts;
- X509_EXTENSION *ex;
- ASN1_OBJECT *obj;
size_t j;
/*
goto out;
}
+ /* Cache and sanity check X509v3 extensions. */
+
+ if (X509_check_purpose(x, -1, -1) <= 0) {
+ warnx("%s: invalid X509v3 extensions", certfile);
+ goto out;
+ }
+
/* Read out the expiration date. */
if ((t = X509expires(x)) == -1) {
goto out;
}
- /*
- * Next, the long process to make sure that the SAN entries
- * listed with the certificate fully cover those passed on the
- * command line.
- */
-
- exts = X509_get0_extensions(x);
-
- /* Scan til we find the SAN NID. */
+ /* Extract list of SAN entries from the certificate. */
- for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
- ex = sk_X509_EXTENSION_value(exts, i);
- assert(ex != NULL);
- obj = X509_EXTENSION_get_object(ex);
- assert(obj != NULL);
- if (NID_subject_alt_name != OBJ_obj2nid(obj))
- continue;
-
- if (san != NULL) {
- warnx("%s: two SAN entries", certfile);
- goto out;
- }
-
- bio = BIO_new(BIO_s_mem());
- if (bio == NULL) {
- warnx("BIO_new");
- goto out;
- }
- if (!X509V3_EXT_print(bio, ex, 0, 0)) {
- warnx("X509V3_EXT_print");
- goto out;
- }
- if ((san = calloc(1, BIO_number_written(bio) + 1)) == NULL) {
- warn("calloc");
- goto out;
- }
- ssz = BIO_read(bio, san, BIO_number_written(bio));
- if (ssz < 0 || (unsigned)ssz != BIO_number_written(bio)) {
- warnx("BIO_read");
- goto out;
- }
- }
-
- if (san == NULL) {
+ sans = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
+ if (sans == NULL) {
warnx("%s: does not have a SAN entry", certfile);
if (revocate)
goto out;
}
/*
- * Parse the SAN line.
- * Make sure that all of the domains are represented only once.
+ * Ensure the certificate's SAN entries fully cover those from the
+ * configuration file and that all domains are represented only once.
*/
- str = san;
- while ((tok = strsep(&str, ",")) != NULL) {
- if (*tok == '\0')
- continue;
- while (isspace((unsigned char)*tok))
- tok++;
- if (strncmp(tok, "DNS:", 4))
+ for (i = 0; i < sk_GENERAL_NAME_num(sans); i++) {
+ GENERAL_NAME *gen_name;
+ const ASN1_IA5STRING *name;
+ const unsigned char *name_buf;
+ int name_len;
+ int name_type;
+
+ gen_name = sk_GENERAL_NAME_value(sans, i);
+ assert(gen_name != NULL);
+
+ name = GENERAL_NAME_get0_value(gen_name, &name_type);
+ if (name_type != GEN_DNS)
continue;
- tok += 4;
- for (j = 0; j < altsz; j++)
- if (strcmp(tok, alts[j]) == 0)
+
+ /* 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);
+
+ for (j = 0; j < altsz; j++) {
+ if ((size_t)name_len != strlen(alts[j]))
+ continue;
+ if (memcmp(name_buf, alts[j], name_len) == 0)
break;
+ }
if (j == altsz) {
if (revocate) {
- warnx("%s: unknown SAN entry: %s", certfile, tok);
+ /* XXX strnvis? */
+ warnx("%s: unexpected SAN entry: %.*s",
+ certfile, name_len, name_buf);
goto out;
}
force = 2;
}
if (found[j]++) {
if (revocate) {
- warnx("%s: duplicate SAN entry: %s", certfile, tok);
+ /* XXX strnvis? */
+ warnx("%s: duplicate SAN entry: %.*s",
+ certfile, name_len, name_buf);
goto out;
}
force = 2;
if (f != NULL)
fclose(f);
X509_free(x);
- BIO_free(bio);
- free(san);
+ GENERAL_NAMES_free(sans);
free(der);
free(found);
free(der64);