]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/resolve: load authority whitelist into persistent kr_context structure
authorFrantisek Tobias <frantisek.tobias@nic.cz>
Mon, 10 Feb 2025 12:03:31 +0000 (13:03 +0100)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 7 Jul 2025 14:15:18 +0000 (16:15 +0200)
lib/resolve.c
lib/resolve.h

index bc00471bc746a2e1167e519b06fb88d2a1f0a88a..feee74a6f3eddadb9f9b81abfd6094dfff637d86 100644 (file)
@@ -13,6 +13,7 @@
 #include <libknot/descriptor.h>
 #include <ucw/mempool.h>
 #include <sys/socket.h>
+#include <gnutls/x509.h>
 #include "lib/resolve.h"
 #include "lib/layer.h"
 #include "lib/rplan.h"
@@ -469,6 +470,146 @@ static int query_finalize(struct kr_request *request, struct kr_query *qry, knot
        return kr_ok();
 }
 
+void certs_free(gnutls_x509_crt_t *array, size_t count)
+{
+       if (!array)
+               return;
+       for (int i = 0; i < count; i++)
+               gnutls_free(array[i]);
+       gnutls_free(array);
+}
+
+void whitelist_free(struct issuer_whitelist *iss)
+{
+       if (!iss || !iss->names)
+               return;
+       // WARNING: assume count is initialized?
+       for (int i = 0; i < iss->count; i++)
+               free(iss->names[i]);
+       free(iss->names);
+       free(iss);
+}
+
+
+int read_certs(const char *filepath, char **out)
+{
+       size_t size;
+       char *certs = NULL;
+       int fd = open(filepath, O_RDONLY);
+       if (fd == -1)
+               goto fail;
+
+       if ((size = lseek(fd, 0, SEEK_END)) == -1 || lseek(fd, 0, SEEK_SET) == -1)
+               goto fail;
+
+       certs = calloc(1, size);
+       if (!certs)
+               goto fail;
+
+       int ret = read(fd, certs, size);
+       if (ret != size)
+               goto fail;
+
+       close(fd);
+       *out = certs;
+       return kr_ok();
+
+fail:
+       kr_log_warning(TLS, "failed to read %s (%s)\n", filepath, strerror(errno));
+       if (certs)
+               free(certs);
+
+       if (fd != -1)
+               close(fd);
+
+       return EXIT_FAILURE;
+}
+
+gnutls_x509_crt_t *parse_certificates(char *raw_certs, size_t size, int *count)
+{
+       *count = 0;
+
+       gnutls_datum_t cert_datum = { (unsigned char *)raw_certs, size };
+       gnutls_x509_crt_t *cert_list = NULL;
+       unsigned int cert_count = 0;
+       int ret = gnutls_x509_crt_list_import2(&cert_list, &cert_count,
+                       &cert_datum, GNUTLS_X509_FMT_PEM, 0);
+
+       if (ret < 0) {
+               kr_log_warning(TLS, "failed to import certificates (%s)\n",
+                               gnutls_strerror(ret));
+               certs_free(cert_list, cert_count);
+               return NULL;
+       }
+
+       *count = cert_count;
+       return cert_list;
+}
+
+int get_auth_name_array(struct issuer_whitelist *iss, const char *filepath)
+{
+       int rv = EXIT_FAILURE;
+       char *raw_certs = NULL;
+       if (read_certs(filepath, &raw_certs) != kr_ok())
+               goto cleanup;
+
+       int count = 0;
+       gnutls_x509_crt_t *certs = parse_certificates(raw_certs, strlen(raw_certs), &count);
+       if (count <= 0)
+               goto cleanup;
+
+       char iss_org_name[256] = { 0 };
+       size_t iss_org_name_size = sizeof(iss_org_name);
+       iss->count = 0;
+       iss->names = calloc(count, sizeof(char *));
+       if (!iss->names)
+               goto cleanup;
+
+       for (int i = 0; i < count; i++) {
+               iss_org_name_size = sizeof(iss_org_name);
+               int ret = gnutls_x509_crt_get_issuer_dn_by_oid(
+                               certs[i], GNUTLS_OID_X520_ORGANIZATION_NAME,
+                               0, 0, iss_org_name, &iss_org_name_size);
+
+               if (ret == GNUTLS_E_SUCCESS) {
+                       iss->names[iss->count] = strndup(iss_org_name, iss_org_name_size);
+                       if (!iss->names[iss->count])
+                               kr_error(ENOMEM);
+                               // TODO: deal with ENOMEM
+                               // ;
+
+                       // TODO: Delete
+                       kr_log_warning(TLS, "whitelisted %s\n", iss->names[iss->count]);
+                       iss->count++;
+               }
+       }
+
+       iss->count = iss->count;
+       rv = kr_ok();
+
+cleanup:
+       if (raw_certs)
+               free(raw_certs);
+
+       certs_free(certs, count);
+       return rv;
+}
+
+int kr_init_whitelist(const char *whitelistpath)
+{
+       the_resolver->issuers = calloc(sizeof(struct issuer_whitelist), 1);
+       if (!the_resolver->issuers)
+               return ENOMEM;
+
+       int ret = get_auth_name_array(the_resolver->issuers, whitelistpath);
+       if (ret != kr_ok()) {
+               whitelist_free(the_resolver->issuers);
+               the_resolver->issuers = NULL;
+       }
+
+       return ret;
+}
+
 int kr_resolver_init(module_array_t *modules, knot_mm_t *pool)
 {
        the_resolver = &the_resolver_value;
@@ -512,6 +653,7 @@ void kr_resolver_deinit(void)
        trie_free(the_resolver->trust_anchors);
        kr_ta_clear(the_resolver->negative_anchors);
        trie_free(the_resolver->negative_anchors);
+       whitelist_free(the_resolver->issuers);
 
        the_resolver = NULL;
 }
index c463118a343c14093e697142e0073c560a312e2a..a5123bc563ddab8ca567141e62e13339874a63c8 100644 (file)
@@ -142,6 +142,13 @@ static inline void kr_rank_set(uint8_t *rank, uint8_t kr_flag)
 typedef array_t(struct kr_module *) module_array_t;
 /* @endcond */
 
+struct issuer_whitelist {
+       int count;
+       char **names;
+};
+KR_EXPORT
+int kr_init_whitelist(const char *whitelistpath);
+
 /**
  * Name resolution context.
  *
@@ -175,6 +182,7 @@ struct kr_context
        kr_cookie_lru_t *cache_cookie;
        int32_t tls_padding; /**< See net.tls_padding in ../daemon/README.rst -- -1 is "true" (default policy), 0 is "false" (no padding) */
        knot_mm_t *pool;
+       struct issuer_whitelist *issuers;
 };
 
 /** Pointer to the singleton resolver context. NULL if not initialized */