]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add "realm_dir" configuration
authorAlan T. DeKok <aland@freeradius.org>
Sun, 25 Jul 2021 12:19:45 +0000 (08:19 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Sun, 25 Jul 2021 12:19:45 +0000 (08:19 -0400)
which will allow for loading multiple certificates from
a subdirectory, and choosing from them at run time

src/include/tls-h
src/main/tls.c

index 3e29e586c27f3a0c95213ceef2b720540dbc1de2..c0d3ff00360aa7d54a0b1c3a912a4b3d2cbd8c93 100644 (file)
@@ -432,6 +432,8 @@ struct fr_tls_server_conf_t {
        char const      *psk_query;
 #endif
 
+       char const      *realm_dir;
+       fr_hash_table_t *realms;
 };
 
 #ifdef __cplusplus
index 01a91f93851db8951c568ae351b714ac61122068..2ce164264588bf8a8c0f421ec99cfe285c1fcf09 100644 (file)
@@ -38,6 +38,10 @@ USES_APPLE_DEPRECATED_API    /* OpenSSL API has been deprecated by Apple */
 #include <fcntl.h>
 #endif
 
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
 #ifdef HAVE_UTIME_H
 #include <utime.h>
 #endif
@@ -1573,6 +1577,8 @@ static CONF_PARSER tls_server_config[] = {
        { "tls13_enable", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, tls13_enable_magic), NULL },
 #endif
 
+       { "realm_dir", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, realm_dir), NULL },
+
        { "cache", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) cache_config },
 
        { "verify", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) verify_config },
@@ -4319,6 +4325,8 @@ static int _tls_server_conf_free(fr_tls_server_conf_t *conf)
        conf->ocsp_store = NULL;
 #endif
 
+       if (conf->realms) fr_hash_table_free(conf->realms);
+
 #ifndef NDEBUG
        memset(conf, 0, sizeof(*conf));
 #endif
@@ -4351,9 +4359,110 @@ static int store_cmp(void const *a, void const *b)
        DICT_ATTR const *one = a;
        DICT_ATTR const *two = b;
 
-       return one - two;
+       return (one < two) - (one > two);
+}
+
+typedef struct {
+       char const      *name;
+       SSL_CTX         *ctx;
+} fr_realm_ctx_t;
+
+static uint32_t realm_hash(void const *data)
+{
+       fr_realm_ctx_t const *r = data;
+
+       return fr_hash_string(r->name);
+}
+
+static int realm_cmp(void const *a, void const *b)
+{
+       fr_realm_ctx_t const *one = a;
+       fr_realm_ctx_t const *two = b;
+
+       return strcmp(one->name, two->name);
+}
+
+static void realm_free(void *data)
+{
+       fr_realm_ctx_t *r = data;
+
+       SSL_CTX_free(r->ctx);
+}
+
+static int tls_realms_load(fr_tls_server_conf_t *conf)
+{
+       fr_hash_table_t *ht;
+       DIR             *dir;
+       struct dirent   *dp;
+       char            buffer[PATH_MAX];
+       char            buffer2[PATH_MAX];
+
+       ht = fr_hash_table_create(realm_hash, realm_cmp, realm_free);
+       if (!ht) return -1;
+
+       dir = opendir(conf->realm_dir);
+       if (!dir) {
+               ERROR("Error reading directory %s: %s", conf->realm_dir, fr_syserror(errno));
+       error:
+               fr_hash_table_free(ht);
+               return -1;
+       }
+
+       /*
+        *      Read only the PEM files
+        */
+       while ((dp = readdir(dir)) != NULL) {
+               char *p;
+               struct stat stat_buf;
+               SSL_CTX *ctx;
+               fr_realm_ctx_t *r;
+               char const *private_key_file = buffer;
+
+               if (dp->d_name[0] == '.') continue;
+
+               p = strrchr(dp->d_name, '.');
+               if (!p) continue;
+
+               if (memcmp(p, ".pem", 5) != 0) continue; /* must END in .pem */
+
+               snprintf(buffer, sizeof(buffer), "%s/%s", conf->realm_dir, dp->d_name); /* ignore directories */
+               if ((stat(buffer, &stat_buf) != 0) ||
+                   S_ISDIR(stat_buf.st_mode)) continue;
+
+               strcpy(buffer2, buffer);
+               p = strchr(buffer2, '.'); /* which must be there... */
+               if (!p) continue;
+
+               /*
+                *      If there's a key file, then use that.
+                *      Otherwise assume that the private key is in
+                *      the chain file.
+                */
+               strcpy(p, ".key");
+               if (stat(buffer2, &stat_buf) != 0) private_key_file = buffer2;
+
+               ctx = tls_init_ctx(conf, 1, buffer, private_key_file);
+               if (!ctx) goto error;
+
+               r = talloc_zero(conf, fr_realm_ctx_t);
+               if (!r) {
+                       SSL_CTX_free(ctx);
+                       goto error;
+               }
+
+               r->name = talloc_strdup(r, buffer);
+               r->ctx = ctx;
+
+               if (fr_hash_table_insert(ht, r) < 0) {
+                       ERROR("Failed inserting certificate file %s into hash table", buffer);
+                       goto error;
+               }
+       }
+
+       return 0;
 }
 
+
 fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
 {
        fr_tls_server_conf_t *conf;
@@ -4530,6 +4639,11 @@ skip_list:
 #endif
 #endif
 
+       /*
+        *      Load certificates and private keys from the realm directory.
+        */
+       if (conf->realm_dir && (tls_realms_load(conf) < 0)) goto error;
+
        /*
         *      Cache conf in cs in case we're asked to parse this again.
         */