1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 /*! \file */
13 
14 #include <inttypes.h>
15 
16 #include <isc/buffer.h>
17 #include <isc/string.h>
18 #include <isc/util.h>
19 
20 #include <dns/name.h>
21 #include <dns/transport.h>
22 
23 #include <isccfg/cfg.h>
24 
25 #include <named/log.h>
26 #include <named/transportconf.h>
27 
28 #define create_name(id, name)                                      \
29 	isc_buffer_t namesrc, namebuf;                             \
30 	char namedata[DNS_NAME_FORMATSIZE + 1];                    \
31 	dns_name_init(name, NULL);                                 \
32 	isc_buffer_constinit(&namesrc, id, strlen(id));            \
33 	isc_buffer_add(&namesrc, strlen(id));                      \
34 	isc_buffer_init(&namebuf, namedata, sizeof(namedata));     \
35 	result = (dns_name_fromtext(name, &namesrc, dns_rootname,  \
36 				    DNS_NAME_DOWNCASE, &namebuf)); \
37 	if (result != ISC_R_SUCCESS) {                             \
38 		goto failure;                                      \
39 	}
40 
41 #define parse_transport_option(map, transport, name, setter)      \
42 	{                                                         \
43 		const cfg_obj_t *obj = NULL;                      \
44 		cfg_map_get(map, name, &obj);                     \
45 		if (obj != NULL) {                                \
46 			setter(transport, cfg_obj_asstring(obj)); \
47 		}                                                 \
48 	}
49 
50 static isc_result_t
add_doh_transports(const cfg_obj_t * transportlist,dns_transport_list_t * list)51 add_doh_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) {
52 	const cfg_obj_t *doh = NULL;
53 	const char *dohid = NULL;
54 	isc_result_t result;
55 
56 	for (const cfg_listelt_t *element = cfg_list_first(transportlist);
57 	     element != NULL; element = cfg_list_next(element))
58 	{
59 		dns_name_t dohname;
60 		dns_transport_t *transport;
61 
62 		doh = cfg_listelt_value(element);
63 		dohid = cfg_obj_asstring(cfg_map_getname(doh));
64 
65 		create_name(dohid, &dohname);
66 
67 		transport = dns_transport_new(&dohname, DNS_TRANSPORT_HTTP,
68 					      list);
69 
70 		parse_transport_option(doh, transport, "key-file",
71 				       dns_transport_set_keyfile);
72 		parse_transport_option(doh, transport, "cert-file",
73 				       dns_transport_set_certfile);
74 		parse_transport_option(doh, transport, "ca-file",
75 				       dns_transport_set_cafile);
76 		parse_transport_option(doh, transport, "hostname",
77 				       dns_transport_set_hostname);
78 	}
79 
80 	return (ISC_R_SUCCESS);
81 failure:
82 	cfg_obj_log(doh, named_g_lctx, ISC_LOG_ERROR,
83 		    "configuring DoH '%s': %s", dohid,
84 		    isc_result_totext(result));
85 
86 	return (result);
87 }
88 
89 static isc_result_t
add_tls_transports(const cfg_obj_t * transportlist,dns_transport_list_t * list)90 add_tls_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) {
91 	const cfg_obj_t *tls = NULL;
92 	const char *tlsid = NULL;
93 	isc_result_t result;
94 
95 	for (const cfg_listelt_t *element = cfg_list_first(transportlist);
96 	     element != NULL; element = cfg_list_next(element))
97 	{
98 		dns_name_t tlsname;
99 		dns_transport_t *transport;
100 
101 		tls = cfg_listelt_value(element);
102 		tlsid = cfg_obj_asstring(cfg_map_getname(tls));
103 
104 		if (!strcmp(tlsid, "ephemeral")) {
105 			result = ISC_R_UNEXPECTEDTOKEN;
106 			goto failure;
107 		}
108 
109 		create_name(tlsid, &tlsname);
110 
111 		transport = dns_transport_new(&tlsname, DNS_TRANSPORT_TLS,
112 					      list);
113 
114 		parse_transport_option(tls, transport, "key-file",
115 				       dns_transport_set_keyfile);
116 		parse_transport_option(tls, transport, "cert-file",
117 				       dns_transport_set_certfile);
118 		parse_transport_option(tls, transport, "ca-file",
119 				       dns_transport_set_cafile);
120 		parse_transport_option(tls, transport, "hostname",
121 				       dns_transport_set_hostname);
122 	}
123 
124 	return (ISC_R_SUCCESS);
125 failure:
126 	cfg_obj_log(tls, named_g_lctx, ISC_LOG_ERROR,
127 		    "configuring tls '%s': %s", tlsid,
128 		    isc_result_totext(result));
129 
130 	return (result);
131 }
132 
133 #define CHECK(f)                             \
134 	if ((result = f) != ISC_R_SUCCESS) { \
135 		goto failure;                \
136 	}
137 
138 static isc_result_t
transport_list_fromconfig(const cfg_obj_t * config,dns_transport_list_t * list)139 transport_list_fromconfig(const cfg_obj_t *config, dns_transport_list_t *list) {
140 	const cfg_obj_t *obj = NULL;
141 	isc_result_t result = ISC_R_SUCCESS;
142 
143 	if (result == ISC_R_SUCCESS &&
144 	    cfg_map_get(config, "tls", &obj) == ISC_R_SUCCESS)
145 	{
146 		result = add_tls_transports(obj, list);
147 		obj = NULL;
148 	}
149 
150 	if (result == ISC_R_SUCCESS &&
151 	    cfg_map_get(config, "doh", &obj) == ISC_R_SUCCESS)
152 	{
153 		result = add_doh_transports(obj, list);
154 		obj = NULL;
155 	}
156 
157 	return (result);
158 }
159 
160 static void
transport_list_add_ephemeral(dns_transport_list_t * list)161 transport_list_add_ephemeral(dns_transport_list_t *list) {
162 	isc_result_t result;
163 	dns_name_t tlsname;
164 
165 	create_name("ephemeral", &tlsname);
166 
167 	(void)dns_transport_new(&tlsname, DNS_TRANSPORT_TLS, list);
168 
169 	return;
170 failure:
171 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
172 }
173 
174 isc_result_t
named_transports_fromconfig(const cfg_obj_t * config,const cfg_obj_t * vconfig,isc_mem_t * mctx,dns_transport_list_t ** listp)175 named_transports_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig,
176 			    isc_mem_t *mctx, dns_transport_list_t **listp) {
177 	isc_result_t result;
178 	dns_transport_list_t *list = dns_transport_list_new(mctx);
179 
180 	REQUIRE(listp != NULL && *listp == NULL);
181 
182 	transport_list_add_ephemeral(list);
183 
184 	if (config != NULL) {
185 		result = transport_list_fromconfig(config, list);
186 		if (result != ISC_R_SUCCESS) {
187 			goto failure;
188 		}
189 	}
190 
191 	if (vconfig != NULL) {
192 		config = cfg_tuple_get(vconfig, "options");
193 		transport_list_fromconfig(config, list);
194 	}
195 
196 	*listp = list;
197 	return (ISC_R_SUCCESS);
198 failure:
199 	dns_transport_list_detach(&list);
200 	return (result);
201 }
202