kore,-acme is the only port using the X509V3_EXT_add_alias() API, which
I am going to remove from libcrypto. This undocumented API is a gross
hack that allows abusing an X509v3 extension method for something else.

kore aliases the subject key identifier method to build the RFC 8737
ACME Identifier extension since both happen to be encoded as octet
strings. kore uses the OpenSSL config mini language to build this
extension and the SAN. This language is responsible for various holes
and is best avoided altogether.

So the below patch uses the proper types to build up the two extensions
and adds them to the certs. This is admittedly a bit more complicated
than using strings, but it avoids lots of very ugly code in libcrypto.

I also added a bit of logic to avoid adding a copy of the acme OID if it
should happen to be added to some libcrypto of the future (it would most
likely be called NID_acmeIdentifier). This will confuse libcrypto and is
therefore best avoided. I do plan on adding this OID to libcrypto, but I
do not plan on adding a proper extension method, although that would not
be hard.

I checked that the DER encoding of the two extensions is as expected,
but I did not do further testing. Unfortunately, there are no tests
shipped with the port.

Index: Makefile
===================================================================
RCS file: /cvs/ports/www/kore/Makefile,v
diff -u -p -r1.36 Makefile
--- Makefile    27 Sep 2023 19:13:02 -0000      1.36
+++ Makefile    24 Jan 2024 22:02:00 -0000
@@ -1,7 +1,7 @@
 COMMENT =      web application framework for writing scalable web APIs in C
 
 DISTNAME =     kore-4.2.3
-REVISION =     0
+REVISION =     1
 
 CATEGORIES =   www
 
Index: patches/patch-src_keymgr_openssl_c
===================================================================
RCS file: patches/patch-src_keymgr_openssl_c
diff -N patches/patch-src_keymgr_openssl_c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-src_keymgr_openssl_c  24 Jan 2024 22:01:48 -0000
@@ -0,0 +1,184 @@
+Index: src/keymgr_openssl.c
+--- src/keymgr_openssl.c.orig
++++ src/keymgr_openssl.c
+@@ -196,8 +196,12 @@ static int                        acmeproc_ready = 0;
+ /* Renewal timer for all domains under acme control. */
+ static struct kore_timer      *acme_renewal = NULL;
+ 
++#ifndef NID_acmeIdentifier
++#define NID_acmeIdentifier -1
++#endif
++
+ /* oid for acme extension. */
+-static int                    acme_oid = -1;
++static int                    acme_oid = NID_acmeIdentifier;
+ 
+ struct acme_order {
+       int                     state;
+@@ -218,9 +222,10 @@ static void       keymgr_acme_order_create(const char *);
+ static void   keymgr_acme_order_redo(void *, u_int64_t);
+ static void   keymgr_acme_order_start(void *, u_int64_t);
+ 
+-static void   keymgr_x509_ext(STACK_OF(X509_EXTENSION) *,
+-                  int, const char *, ...)
+-                  __attribute__((format (printf, 3, 4)));
++static void   keymgr_x509_ext_alt_name_dns(STACK_OF(X509_EXTENSION *),
++                  const char *);
++static void   keymgr_x509_ext_acme_id(STACK_OF(X509_EXTENSION) *,
++                  const void *data, size_t len);
+ 
+ static void   keymgr_acme_csr(const struct kore_keyreq *, struct key *);
+ static void   keymgr_acme_install_cert(const void *, size_t, struct key *);
+@@ -316,8 +321,9 @@ kore_keymgr_run(void)
+ #endif
+ 
+ #if defined(KORE_USE_ACME)
+-      acme_oid = OBJ_create(ACME_TLS_ALPN_01_OID, "acme", "acmeIdentifier");
+-      X509V3_EXT_add_alias(acme_oid, NID_subject_key_identifier);
++      if (acme_oid == -1)
++              acme_oid = OBJ_create(ACME_TLS_ALPN_01_OID, "acme",
++                  "acmeIdentifier");
+ #endif
+ 
+       kore_worker_started();
+@@ -1078,15 +1084,12 @@ static void
+ keymgr_acme_challenge_cert(const void *data, size_t len, struct key *key)
+ {
+       STACK_OF(X509_EXTENSION)        *sk;
+-      size_t                          idx;
+       time_t                          now;
+       X509_EXTENSION                  *ext;
+       X509_NAME                       *name;
+       X509                            *x509;
+-      const u_int8_t                  *digest;
+       int                             slen, i;
+       u_int8_t                        *cert, *uptr;
+-      char                            hex[(SHA256_DIGEST_LENGTH * 2) + 1];
+ 
+       kore_log(LOG_INFO, "[%s] generating tls-alpn-01 challenge cert",
+           key->dom->domain);
+@@ -1094,15 +1097,6 @@ keymgr_acme_challenge_cert(const void *data, size_t le
+       if (len != SHA256_DIGEST_LENGTH)
+               fatalx("invalid digest length of %zu bytes", len);
+ 
+-      digest = data;
+-
+-      for (idx = 0; idx < SHA256_DIGEST_LENGTH; idx++) {
+-              slen = snprintf(hex + (idx * 2), sizeof(hex) - (idx * 2),
+-                  "%02x", digest[idx]);
+-              if (slen == -1 || (size_t)slen >= sizeof(hex))
+-                      fatalx("failed to convert digest to hex");
+-      }
+-
+       if ((x509 = X509_new()) == NULL)
+               fatalx("X509_new(): %s", ssl_errno_s);
+ 
+@@ -1133,8 +1127,8 @@ keymgr_acme_challenge_cert(const void *data, size_t le
+               fatalx("X509_set_issuer_name(): %s", ssl_errno_s);
+ 
+       sk = sk_X509_EXTENSION_new_null();
+-      keymgr_x509_ext(sk, acme_oid, "critical,%s", hex);
+-      keymgr_x509_ext(sk, NID_subject_alt_name, "DNS:%s", key->dom->domain);
++      keymgr_x509_ext_acme_id(sk, data, len);
++      keymgr_x509_ext_alt_name_dns(sk, key->dom->domain);
+ 
+       for (i = 0; i < sk_X509_EXTENSION_num(sk); i++) {
+               ext = sk_X509_EXTENSION_value(sk, i);
+@@ -1190,7 +1184,7 @@ keymgr_acme_csr(const struct kore_keyreq *req, struct 
+               fatalx("X509_NAME_add_entry_by_txt(): %s", ssl_errno_s);
+ 
+       sk = sk_X509_EXTENSION_new_null();
+-      keymgr_x509_ext(sk, NID_subject_alt_name, "DNS:%s", key->dom->domain);
++      keymgr_x509_ext_alt_name_dns(sk, key->dom->domain);
+ 
+       if (!X509_REQ_add_extensions(csr, sk))
+               fatalx("X509_REQ_add_extensions(): %s", ssl_errno_s);
+@@ -1217,26 +1211,74 @@ keymgr_acme_csr(const struct kore_keyreq *req, struct 
+ }
+ 
+ static void
+-keymgr_x509_ext(STACK_OF(X509_EXTENSION) *sk, int extnid, const char *fmt, 
...)
++keymgr_x509_ext_alt_name_dns(STACK_OF(X509_EXTENSION) *sk,
++    const char *dns_name)
+ {
+-      int                     len;
+-      va_list                 args;
++      ASN1_IA5STRING          *ia5;
++      GENERAL_NAME            *gen;
++      GENERAL_NAMES           *gens;
+       X509_EXTENSION          *ext;
+-      char                    buf[1024];
+ 
+-      va_start(args, fmt);
+-      len = vsnprintf(buf, sizeof(buf), fmt, args);
+-      va_end(args);
++      ia5 = ASN1_IA5STRING_new();
++      if (ia5 == NULL)
++              fatalx("ASN1_IA5STRING_new(): %s", ssl_errno_s);
++      if (!ASN1_STRING_set(ia5, dns_name, -1))
++              fatalx("ASN1_STRING_set(): %s", ssl_errno_s);
+ 
+-      if (len == -1 || (size_t)len >= sizeof(buf))
+-              fatalx("failed to create buffer for extension %d", extnid);
++      gen = GENERAL_NAME_new();
++      if (gen == NULL)
++              fatalx("GENERAL_NAME_new(): %s", ssl_errno_s);
++      GENERAL_NAME_set0_value(gen, GEN_DNS, ia5);
+ 
+-      if ((ext = X509V3_EXT_conf_nid(NULL, NULL, extnid, buf)) == NULL) {
+-              fatalx("X509V3_EXT_conf_nid(%d, %s): %s",
+-                  extnid, buf, ssl_errno_s);
+-      }
++      gens = GENERAL_NAMES_new();
++      if (gens == NULL)
++              fatalx("GENERAL_NAMES_new(): %s", ssl_errno_s);
++      if (!sk_GENERAL_NAME_push(gens, gen))
++              fatalx("sk_GENERAL_NAME_push(): %s", ssl_errno_s);
+ 
+-      sk_X509_EXTENSION_push(sk, ext);
++      ext = X509V3_EXT_i2d(NID_subject_alt_name, 0, gens);
++      if (ext == NULL)
++              fatalx("X509V3_EXT_i2d(): %s", ssl_errno_s);
++
++      GENERAL_NAMES_free(gens);
++
++      if (!sk_X509_EXTENSION_push(sk, ext))
++              fatalx("sk_X509_EXTENSION_push(): %s", ssl_errno_s);
++}
++
++static void
++keymgr_x509_ext_acme_id(STACK_OF(X509_EXTENSION) *sk, const void *data,
++    size_t len)
++{
++      ASN1_OCTET_STRING       *aos;
++      X509_EXTENSION          *ext;
++      unsigned char           *der = NULL;
++      int                      der_len;
++
++      if (len != SHA256_DIGEST_LENGTH)
++              fatalx("invalid digest length of %zu bytes", len);
++
++      aos = ASN1_OCTET_STRING_new();
++      if (aos == NULL)
++              fatalx("ASN1_OCTET_STRING_new(): %s", ssl_errno_s);
++      if (!ASN1_STRING_set(aos, data, len))
++              fatalx("ASN1_STRING_set(): %s", ssl_errno_s);
++
++      der_len = i2d_ASN1_OCTET_STRING(aos, &der);
++      if (der_len <= 0)
++              fatalx("i2d_ASN1_OCTET_STRING(): %s", ssl_errno_s);
++      if (!ASN1_STRING_set(aos, der, der_len))
++              fatalx("ASN1_STRING_set(): %s", ssl_errno_s);
++      free(der);
++
++      ext = X509_EXTENSION_create_by_NID(NULL, acme_oid, 1, aos);
++      if (ext == NULL)
++              fatalx("X509_EXTENSION_create_by_NID(): %s", ssl_errno_s);
++
++      ASN1_OCTET_STRING_free(aos);
++
++      if (!sk_X509_EXTENSION_push(sk, ext))
++              fatalx("sk_X509_EXTENSION_push(): %s", ssl_errno_s);
+ }
+ 
+ static char *

Reply via email to