Hi tech@,

I decided to give it a shot at specifying a custom CA for DoT validation
in unwind(8). Patch at the bottom. Selfish reason for this is that I run
my own DoT resolver, using a self-signed certificate. Am able to use
unbound to query it, but the lack of support for a custom CA in unwind
stopped me from switching to it since its inception. Now that the system
is becoming more integrated (dhcpleased and resolved enabled by default,
deep-ish integration between resolvd and unwind), I gave it a shot.

The patch is just the code part, manpage changes are pending but wanted
to know first if the approach is correct and willing to be accepted.

I successfully tried:
- using a custom CA
- adding a DoT forwarder not covered by it and having it rejected
- removing my DoT forwarders and the custom CA to fallback to the
  standard CA

I haven't tried yet (but will do very soon):
- verifying that unwind works in early system, placing the custom CA
  file in the / mount (ofc won't work if the file resides in another
  mountpoint, thing to be documented)

Worth noting, currently, CA changes come into action when forwarders
are added or removed. If a config file undergoes only a CA change, it
won't be reflected in the running unwind. Am unsure if this could be
solved without much refactor, but haven't given up yet.

The default CA file was unveiled. I removed that call, but dunno if
instead I should be unveiling every new CA file or not. Blocklist aren't
managed in that way, fwiw.

Comments?


Index: frontend.c
===================================================================
RCS file: /home/cvs/src/sbin/unwind/frontend.c,v
retrieving revision 1.68
diff -u -p -r1.68 frontend.c
--- frontend.c  6 Feb 2021 18:01:02 -0000       1.68
+++ frontend.c  20 Jul 2021 18:21:30 -0000
@@ -365,6 +365,7 @@ frontend_dispatch_main(int fd, short eve
                case IMSG_RECONF_FORWARDER:
                case IMSG_RECONF_DOT_FORWARDER:
                case IMSG_RECONF_FORCE:
+               case IMSG_RECONF_CA_FILE:
                        imsg_receive_config(&imsg, &nconf);
                        break;
                case IMSG_RECONF_END:
Index: parse.y
===================================================================
RCS file: /home/cvs/src/sbin/unwind/parse.y,v
retrieving revision 1.25
diff -u -p -r1.25 parse.y
--- parse.y     27 Feb 2021 10:32:28 -0000      1.25
+++ parse.y     20 Jul 2021 20:40:34 -0000
@@ -103,6 +103,7 @@ typedef struct {
 %token FORWARDER DOT PORT ODOT_FORWARDER ODOT_DHCP
 %token AUTHENTICATION NAME PREFERENCE RECURSOR DHCP STUB
 %token BLOCK LIST LOG FORCE ACCEPT BOGUS
+%token CA FILE
 
 %token <v.string>      STRING
 %token <v.number>      NUMBER
@@ -120,6 +121,7 @@ grammar             : /* empty */
                | grammar uw_forwarder '\n'
                | grammar block_list '\n'
                | grammar force '\n'
+               | grammar cafile '\n'
                | grammar error '\n'            { file->errors++; }
                ;
 
@@ -343,7 +345,7 @@ acceptbogus:        ACCEPT BOGUS    { $$ = 1; }
 
 force_list:    force_list optnl STRING {
                        struct force_tree_entry *e;
-                       size_t                           len;
+                       size_t                   len;
 
                        len = strlen($3);
                        e = malloc(sizeof(*e));
@@ -374,6 +376,20 @@ force_list:        force_list optnl STRING {
                }
        ;
 
+cafile :       CA FILE STRING {
+                       if (conf->ca_file != NULL) {
+                               yyerror("CA file already configured");
+                               free($3);
+                               YYERROR;
+                       } else {
+                               conf->ca_file = strdup($3);
+                               if (conf->ca_file == NULL)
+                                       err(1, "strdup");
+                               free($3);
+                       }
+               }
+       ;
+
 %%
 
 struct keywords {
@@ -413,8 +429,10 @@ lookup(char *s)
                {"authentication",      AUTHENTICATION},
                {"block",               BLOCK},
                {"bogus",               BOGUS},
+               {"ca",                  CA},
                {"dhcp",                DHCP},
                {"dot",                 DOT},
+               {"file",                FILE},
                {"force",               FORCE},
                {"forwarder",           FORWARDER},
                {"include",             INCLUDE},
Index: printconf.c
===================================================================
RCS file: /home/cvs/src/sbin/unwind/printconf.c,v
retrieving revision 1.16
diff -u -p -r1.16 printconf.c
--- printconf.c 1 Dec 2019 14:37:34 -0000       1.16
+++ printconf.c 20 Jul 2021 20:41:25 -0000
@@ -33,6 +33,9 @@ print_config(struct uw_conf *conf)
        int                      i;
        enum uw_resolver_type    j;
 
+       if (conf->ca_file != NULL)
+               printf("ca file %s\n", conf->ca_file);
+
        if (conf->res_pref.len > 0) {
                printf("preference {");
                for (i = 0; i < conf->res_pref.len; i++) {
Index: resolver.c
===================================================================
RCS file: /home/cvs/src/sbin/unwind/resolver.c,v
retrieving revision 1.144
diff -u -p -r1.144 resolver.c
--- resolver.c  12 Jul 2021 15:09:19 -0000      1.144
+++ resolver.c  20 Jul 2021 21:58:43 -0000
@@ -127,6 +127,7 @@ struct running_query {
 };
 
 TAILQ_HEAD(, running_query)     running_queries;
+static const char              *ca_file;
 
 typedef void (*resolve_cb_t)(struct uw_resolver *, void *, int, void *, int,
     int, char *);
@@ -376,9 +377,6 @@ resolver(int debug, int verbose)
            setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
                fatal("can't drop privileges");
 
-       if (unveil(TLS_DEFAULT_CA_CERT_FILE, "r") == -1)
-               fatal("unveil %s", TLS_DEFAULT_CA_CERT_FILE);
-
        if (pledge("stdio inet dns rpath recvfd", NULL) == -1)
                fatal("pledge");
 
@@ -664,6 +662,7 @@ resolver_dispatch_main(int fd, short eve
                case IMSG_RECONF_FORWARDER:
                case IMSG_RECONF_DOT_FORWARDER:
                case IMSG_RECONF_FORCE:
+               case IMSG_RECONF_CA_FILE:
                        imsg_receive_config(&imsg, &nconf);
                        break;
                case IMSG_RECONF_END:
@@ -672,6 +671,8 @@ resolver_dispatch_main(int fd, short eve
                                    "IMSG_RECONF_CONF", __func__);
                        restart = resolvers_to_restart(resolver_conf, nconf);
                        merge_config(resolver_conf, nconf);
+                       ca_file = resolver_conf->ca_file == NULL ?
+                           TLS_DEFAULT_CA_CERT_FILE : resolver_conf->ca_file;
                        memset(enabled_resolvers, 0, sizeof(enabled_resolvers));
                        for (i = 0; i < resolver_conf->res_pref.len; i++)
                                enabled_resolvers[
@@ -1320,8 +1321,7 @@ create_resolver(enum uw_resolver_type ty
                break;
        case UW_RES_ODOT_DHCP:
                set_forwarders(res, &autoconf_forwarder_list, 853);
-               ub_ctx_set_option(res->ctx, "tls-cert-bundle:",
-                   TLS_DEFAULT_CA_CERT_FILE);
+               ub_ctx_set_option(res->ctx, "tls-cert-bundle:", ca_file);
                ub_ctx_set_tls(res->ctx, 1);
                break;
        case UW_RES_FORWARDER:
@@ -1329,14 +1329,12 @@ create_resolver(enum uw_resolver_type ty
                break;
        case UW_RES_ODOT_FORWARDER:
                set_forwarders(res, &resolver_conf->uw_forwarder_list, 853);
-               ub_ctx_set_option(res->ctx, "tls-cert-bundle:",
-                   TLS_DEFAULT_CA_CERT_FILE);
+               ub_ctx_set_option(res->ctx, "tls-cert-bundle:", ca_file);
                ub_ctx_set_tls(res->ctx, 1);
                break;
        case UW_RES_DOT:
                set_forwarders(res, &resolver_conf->uw_dot_forwarder_list, 0);
-               ub_ctx_set_option(res->ctx, "tls-cert-bundle:",
-                   TLS_DEFAULT_CA_CERT_FILE);
+               ub_ctx_set_option(res->ctx, "tls-cert-bundle:", ca_file);
                ub_ctx_set_tls(res->ctx, 1);
                break;
        default:
Index: unwind.c
===================================================================
RCS file: /home/cvs/src/sbin/unwind/unwind.c,v
retrieving revision 1.61
diff -u -p -r1.61 unwind.c
--- unwind.c    27 Feb 2021 10:32:28 -0000      1.61
+++ unwind.c    20 Jul 2021 20:59:20 -0000
@@ -586,7 +586,7 @@ main_reload(void)
 int
 main_imsg_send_config(struct uw_conf *xconf)
 {
-       struct uw_forwarder             *uw_forwarder;
+       struct uw_forwarder     *uw_forwarder;
        struct force_tree_entry *force_entry;
 
        /* Send fixed part of config to children. */
@@ -599,6 +599,11 @@ main_imsg_send_config(struct uw_conf *xc
                    == -1)
                        return (-1);
        }
+       if (xconf->ca_file != NULL) {
+               if (main_sendall(IMSG_RECONF_CA_FILE, xconf->ca_file,
+                   strlen(xconf->ca_file) + 1) == -1)
+                       return (-1);
+       }
 
        /* send static forwarders to children */
        TAILQ_FOREACH(uw_forwarder, &xconf->uw_forwarder_list, entry) {
@@ -679,6 +684,9 @@ merge_config(struct uw_conf *conf, struc
                RB_INSERT(force_tree, &conf->force, n);
        }
 
+       free(conf->ca_file);
+       conf->ca_file = xconf->ca_file;
+
        free(xconf);
 }
 
@@ -952,6 +960,13 @@ imsg_receive_config(struct imsg *imsg, s
                        fatalx("%s: IMSG_RECONF_FORCE duplicate entry",
                            __func__);
                }
+               break;
+       case IMSG_RECONF_CA_FILE:
+               /* make sure this is a string */
+               ((char *)imsg->data)[IMSG_DATA_SIZE(*imsg) - 1] = '\0';
+               if ((nconf->ca_file = strdup(imsg->data)) ==
+                   NULL)
+                       fatal("%s: strdup", __func__);
                break;
        default:
                log_debug("%s: error handling imsg %d", __func__,
Index: unwind.h
===================================================================
RCS file: /home/cvs/src/sbin/unwind/unwind.h,v
retrieving revision 1.54
diff -u -p -r1.54 unwind.h
--- unwind.h    27 Feb 2021 10:32:28 -0000      1.54
+++ unwind.h    20 Jul 2021 21:57:24 -0000
@@ -94,6 +94,7 @@ enum imsg_type {
        IMSG_RECONF_FORWARDER,
        IMSG_RECONF_DOT_FORWARDER,
        IMSG_RECONF_FORCE,
+       IMSG_RECONF_CA_FILE,
        IMSG_RECONF_END,
        IMSG_UDP4SOCK,
        IMSG_UDP6SOCK,
@@ -156,6 +157,7 @@ struct uw_conf {
        struct resolver_preference       res_pref;
        char                            *blocklist_file;
        int                              blocklist_log;
+       char                            *ca_file;
 };
 
 struct query_imsg {

Reply via email to