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 {