1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * SPDX-License-Identifier: MPL-2.0
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 /*! \file
15  * \brief
16  * The built-in "version", "hostname", "id", "authors" and "empty" databases.
17  */
18 
19 #include <stdio.h>
20 #include <string.h>
21 
22 #include <isc/mem.h>
23 #include <isc/print.h>
24 #include <isc/result.h>
25 #include <isc/util.h>
26 
27 #include <dns/result.h>
28 #include <dns/sdb.h>
29 
30 #include <named/builtin.h>
31 #include <named/globals.h>
32 #include <named/os.h>
33 #include <named/server.h>
34 
35 typedef struct builtin builtin_t;
36 
37 static isc_result_t
38 do_version_lookup(dns_sdblookup_t *lookup);
39 static isc_result_t
40 do_hostname_lookup(dns_sdblookup_t *lookup);
41 static isc_result_t
42 do_authors_lookup(dns_sdblookup_t *lookup);
43 static isc_result_t
44 do_id_lookup(dns_sdblookup_t *lookup);
45 static isc_result_t
46 do_empty_lookup(dns_sdblookup_t *lookup);
47 static isc_result_t
48 do_dns64_lookup(dns_sdblookup_t *lookup);
49 
50 /*
51  * We can't use function pointers as the db_data directly
52  * because ANSI C does not guarantee that function pointers
53  * can safely be cast to void pointers and back.
54  */
55 
56 struct builtin {
57 	isc_result_t (*do_lookup)(dns_sdblookup_t *lookup);
58 	char *server;
59 	char *contact;
60 };
61 
62 static builtin_t version_builtin = { do_version_lookup, NULL, NULL };
63 static builtin_t hostname_builtin = { do_hostname_lookup, NULL, NULL };
64 static builtin_t authors_builtin = { do_authors_lookup, NULL, NULL };
65 static builtin_t id_builtin = { do_id_lookup, NULL, NULL };
66 static builtin_t empty_builtin = { do_empty_lookup, NULL, NULL };
67 static builtin_t dns64_builtin = { do_dns64_lookup, NULL, NULL };
68 
69 static dns_sdbimplementation_t *builtin_impl;
70 static dns_sdbimplementation_t *dns64_impl;
71 
72 /*
73  * Pre computed HEX * 16 or 1 table.
74  */
75 static const unsigned char hex16[256] = {
76 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*00*/
77 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*10*/
78 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*20*/
79 	0, 16,	32,  48,  64,  80,  96,	 112, 128, 144, 1, 1, 1, 1, 1, 1, /*30*/
80 	1, 160, 176, 192, 208, 224, 240, 1,   1,   1,	1, 1, 1, 1, 1, 1, /*40*/
81 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*50*/
82 	1, 160, 176, 192, 208, 224, 240, 1,   1,   1,	1, 1, 1, 1, 1, 1, /*60*/
83 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*70*/
84 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*80*/
85 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*90*/
86 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*A0*/
87 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*B0*/
88 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*C0*/
89 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*D0*/
90 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*E0*/
91 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1  /*F0*/
92 };
93 
94 const unsigned char decimal[] = "0123456789";
95 
96 static size_t
dns64_rdata(unsigned char * v,size_t start,unsigned char * rdata)97 dns64_rdata(unsigned char *v, size_t start, unsigned char *rdata) {
98 	size_t i, j = 0;
99 
100 	for (i = 0; i < 4U; i++) {
101 		unsigned char c = v[start++];
102 		if (start == 7U) {
103 			start++;
104 		}
105 		if (c > 99) {
106 			rdata[j++] = 3;
107 			rdata[j++] = decimal[c / 100];
108 			c = c % 100;
109 			rdata[j++] = decimal[c / 10];
110 			c = c % 10;
111 			rdata[j++] = decimal[c];
112 		} else if (c > 9) {
113 			rdata[j++] = 2;
114 			rdata[j++] = decimal[c / 10];
115 			c = c % 10;
116 			rdata[j++] = decimal[c];
117 		} else {
118 			rdata[j++] = 1;
119 			rdata[j++] = decimal[c];
120 		}
121 	}
122 	memmove(&rdata[j], "\07in-addr\04arpa", 14);
123 	return (j + 14);
124 }
125 
126 static isc_result_t
dns64_cname(const dns_name_t * zone,const dns_name_t * name,dns_sdblookup_t * lookup)127 dns64_cname(const dns_name_t *zone, const dns_name_t *name,
128 	    dns_sdblookup_t *lookup) {
129 	size_t zlen, nlen, j, len;
130 	unsigned char v[16], n;
131 	unsigned int i;
132 	unsigned char rdata[sizeof("123.123.123.123.in-addr.arpa.")];
133 	unsigned char *ndata;
134 
135 	/*
136 	 * The combined length of the zone and name is 74.
137 	 *
138 	 * The minimum zone length is 10 ((3)ip6(4)arpa(0)).
139 	 *
140 	 * The length of name should always be even as we are expecting
141 	 * a series of nibbles.
142 	 */
143 	zlen = zone->length;
144 	nlen = name->length;
145 	if ((zlen + nlen) > 74U || zlen < 10U || (nlen % 2) != 0U) {
146 		return (ISC_R_NOTFOUND);
147 	}
148 
149 	/*
150 	 * We assume the zone name is well formed.
151 	 */
152 
153 	/*
154 	 * XXXMPA We could check the dns64 suffix here if we need to.
155 	 */
156 	/*
157 	 * Check that name is a series of nibbles.
158 	 * Compute the byte values that correspond to the nibbles as we go.
159 	 *
160 	 * Shift the final result 4 bits, by setting 'i' to 1, if we if we
161 	 * have a odd number of nibbles so that "must be zero" tests below
162 	 * are byte aligned and we correctly return ISC_R_NOTFOUND or
163 	 * ISC_R_SUCCESS.  We will not generate a CNAME in this case.
164 	 */
165 	ndata = name->ndata;
166 	i = (nlen % 4) == 2U ? 1 : 0;
167 	j = nlen;
168 	memset(v, 0, sizeof(v));
169 	while (j != 0U) {
170 		INSIST((i / 2) < sizeof(v));
171 		if (ndata[0] != 1) {
172 			return (ISC_R_NOTFOUND);
173 		}
174 		n = hex16[ndata[1] & 0xff];
175 		if (n == 1) {
176 			return (ISC_R_NOTFOUND);
177 		}
178 		v[i / 2] = n | (v[i / 2] >> 4);
179 		j -= 2;
180 		ndata += 2;
181 		i++;
182 	}
183 
184 	/*
185 	 * If we get here then we know name only consisted of nibbles.
186 	 * Now we need to determine if the name exists or not and whether
187 	 * it corresponds to a empty node in the zone or there should be
188 	 * a CNAME.
189 	 */
190 #define ZLEN(x) (10 + (x) / 2)
191 	switch (zlen) {
192 	case ZLEN(32): /* prefix len 32 */
193 		/*
194 		 * The nibbles that map to this byte must be zero for 'name'
195 		 * to exist in the zone.
196 		 */
197 		if (nlen > 16U && v[(nlen - 1) / 4 - 4] != 0) {
198 			return (ISC_R_NOTFOUND);
199 		}
200 		/*
201 		 * If the total length is not 74 then this is a empty node
202 		 * so return success.
203 		 */
204 		if (nlen + zlen != 74U) {
205 			return (ISC_R_SUCCESS);
206 		}
207 		len = dns64_rdata(v, 8, rdata);
208 		break;
209 	case ZLEN(40): /* prefix len 40 */
210 		/*
211 		 * The nibbles that map to this byte must be zero for 'name'
212 		 * to exist in the zone.
213 		 */
214 		if (nlen > 12U && v[(nlen - 1) / 4 - 3] != 0) {
215 			return (ISC_R_NOTFOUND);
216 		}
217 		/*
218 		 * If the total length is not 74 then this is a empty node
219 		 * so return success.
220 		 */
221 		if (nlen + zlen != 74U) {
222 			return (ISC_R_SUCCESS);
223 		}
224 		len = dns64_rdata(v, 6, rdata);
225 		break;
226 	case ZLEN(48): /* prefix len 48 */
227 		/*
228 		 * The nibbles that map to this byte must be zero for 'name'
229 		 * to exist in the zone.
230 		 */
231 		if (nlen > 8U && v[(nlen - 1) / 4 - 2] != 0) {
232 			return (ISC_R_NOTFOUND);
233 		}
234 		/*
235 		 * If the total length is not 74 then this is a empty node
236 		 * so return success.
237 		 */
238 		if (nlen + zlen != 74U) {
239 			return (ISC_R_SUCCESS);
240 		}
241 		len = dns64_rdata(v, 5, rdata);
242 		break;
243 	case ZLEN(56): /* prefix len 56 */
244 		/*
245 		 * The nibbles that map to this byte must be zero for 'name'
246 		 * to exist in the zone.
247 		 */
248 		if (nlen > 4U && v[(nlen - 1) / 4 - 1] != 0) {
249 			return (ISC_R_NOTFOUND);
250 		}
251 		/*
252 		 * If the total length is not 74 then this is a empty node
253 		 * so return success.
254 		 */
255 		if (nlen + zlen != 74U) {
256 			return (ISC_R_SUCCESS);
257 		}
258 		len = dns64_rdata(v, 4, rdata);
259 		break;
260 	case ZLEN(64): /* prefix len 64 */
261 		/*
262 		 * The nibbles that map to this byte must be zero for 'name'
263 		 * to exist in the zone.
264 		 */
265 		if (v[(nlen - 1) / 4] != 0) {
266 			return (ISC_R_NOTFOUND);
267 		}
268 		/*
269 		 * If the total length is not 74 then this is a empty node
270 		 * so return success.
271 		 */
272 		if (nlen + zlen != 74U) {
273 			return (ISC_R_SUCCESS);
274 		}
275 		len = dns64_rdata(v, 3, rdata);
276 		break;
277 	case ZLEN(96): /* prefix len 96 */
278 		/*
279 		 * If the total length is not 74 then this is a empty node
280 		 * so return success.
281 		 */
282 		if (nlen + zlen != 74U) {
283 			return (ISC_R_SUCCESS);
284 		}
285 		len = dns64_rdata(v, 0, rdata);
286 		break;
287 	default:
288 		/*
289 		 * This should never be reached unless someone adds a
290 		 * zone declaration with this internal type to named.conf.
291 		 */
292 		return (ISC_R_NOTFOUND);
293 	}
294 	return (dns_sdb_putrdata(lookup, dns_rdatatype_cname, 600, rdata,
295 				 (unsigned int)len));
296 }
297 
298 static isc_result_t
builtin_lookup(const char * zone,const char * name,void * dbdata,dns_sdblookup_t * lookup,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)299 builtin_lookup(const char *zone, const char *name, void *dbdata,
300 	       dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
301 	       dns_clientinfo_t *clientinfo) {
302 	builtin_t *b = (builtin_t *)dbdata;
303 
304 	UNUSED(zone);
305 	UNUSED(methods);
306 	UNUSED(clientinfo);
307 
308 	if (strcmp(name, "@") == 0) {
309 		return (b->do_lookup(lookup));
310 	} else {
311 		return (ISC_R_NOTFOUND);
312 	}
313 }
314 
315 static isc_result_t
dns64_lookup(const dns_name_t * zone,const dns_name_t * name,void * dbdata,dns_sdblookup_t * lookup,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)316 dns64_lookup(const dns_name_t *zone, const dns_name_t *name, void *dbdata,
317 	     dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
318 	     dns_clientinfo_t *clientinfo) {
319 	builtin_t *b = (builtin_t *)dbdata;
320 
321 	UNUSED(methods);
322 	UNUSED(clientinfo);
323 
324 	if (name->labels == 0 && name->length == 0) {
325 		return (b->do_lookup(lookup));
326 	} else {
327 		return (dns64_cname(zone, name, lookup));
328 	}
329 }
330 
331 static isc_result_t
put_txt(dns_sdblookup_t * lookup,const char * text)332 put_txt(dns_sdblookup_t *lookup, const char *text) {
333 	unsigned char buf[256];
334 	unsigned int len = strlen(text);
335 	if (len > 255) {
336 		len = 255; /* Silently truncate */
337 	}
338 	buf[0] = len;
339 	memmove(&buf[1], text, len);
340 	return (dns_sdb_putrdata(lookup, dns_rdatatype_txt, 0, buf, len + 1));
341 }
342 
343 static isc_result_t
do_version_lookup(dns_sdblookup_t * lookup)344 do_version_lookup(dns_sdblookup_t *lookup) {
345 	if (named_g_server->version_set) {
346 		if (named_g_server->version == NULL) {
347 			return (ISC_R_SUCCESS);
348 		} else {
349 			return (put_txt(lookup, named_g_server->version));
350 		}
351 	} else {
352 		return (put_txt(lookup, named_g_version));
353 	}
354 }
355 
356 static isc_result_t
do_hostname_lookup(dns_sdblookup_t * lookup)357 do_hostname_lookup(dns_sdblookup_t *lookup) {
358 	if (named_g_server->hostname_set) {
359 		if (named_g_server->hostname == NULL) {
360 			return (ISC_R_SUCCESS);
361 		} else {
362 			return (put_txt(lookup, named_g_server->hostname));
363 		}
364 	} else {
365 		char buf[256];
366 		isc_result_t result = named_os_gethostname(buf, sizeof(buf));
367 		if (result != ISC_R_SUCCESS) {
368 			return (result);
369 		}
370 		return (put_txt(lookup, buf));
371 	}
372 }
373 
374 static isc_result_t
do_authors_lookup(dns_sdblookup_t * lookup)375 do_authors_lookup(dns_sdblookup_t *lookup) {
376 	isc_result_t result;
377 	const char **p;
378 	static const char *authors[] = {
379 		"Mark Andrews",	  "Curtis Blackburn",	"James Brister",
380 		"Ben Cottrell",	  "John H. DuBois III", "Francis Dupont",
381 		"Michael Graff",  "Andreas Gustafsson", "Bob Halley",
382 		"Evan Hunt",	  "JINMEI Tatuya",	"Witold Krecicki",
383 		"David Lawrence", "Scott Mann",		"Danny Mayer",
384 		"Damien Neil",	  "Matt Nelson",	"Jeremy C. Reed",
385 		"Michael Sawyer", "Brian Wellington",	NULL
386 	};
387 
388 	/*
389 	 * If a version string is specified, disable the authors.bind zone.
390 	 */
391 	if (named_g_server->version_set) {
392 		return (ISC_R_SUCCESS);
393 	}
394 
395 	for (p = authors; *p != NULL; p++) {
396 		result = put_txt(lookup, *p);
397 		if (result != ISC_R_SUCCESS) {
398 			return (result);
399 		}
400 	}
401 	return (ISC_R_SUCCESS);
402 }
403 
404 static isc_result_t
do_id_lookup(dns_sdblookup_t * lookup)405 do_id_lookup(dns_sdblookup_t *lookup) {
406 	if (named_g_server->sctx->gethostname != NULL) {
407 		char buf[256];
408 		isc_result_t result;
409 
410 		result = named_g_server->sctx->gethostname(buf, sizeof(buf));
411 		if (result != ISC_R_SUCCESS) {
412 			return (result);
413 		}
414 		return (put_txt(lookup, buf));
415 	} else if (named_g_server->sctx->server_id != NULL) {
416 		return (put_txt(lookup, named_g_server->sctx->server_id));
417 	} else {
418 		return (ISC_R_SUCCESS);
419 	}
420 }
421 
422 static isc_result_t
do_dns64_lookup(dns_sdblookup_t * lookup)423 do_dns64_lookup(dns_sdblookup_t *lookup) {
424 	UNUSED(lookup);
425 	return (ISC_R_SUCCESS);
426 }
427 
428 static isc_result_t
do_empty_lookup(dns_sdblookup_t * lookup)429 do_empty_lookup(dns_sdblookup_t *lookup) {
430 	UNUSED(lookup);
431 	return (ISC_R_SUCCESS);
432 }
433 
434 static isc_result_t
builtin_authority(const char * zone,void * dbdata,dns_sdblookup_t * lookup)435 builtin_authority(const char *zone, void *dbdata, dns_sdblookup_t *lookup) {
436 	isc_result_t result;
437 	const char *contact = "hostmaster";
438 	const char *server = "@";
439 	builtin_t *b = (builtin_t *)dbdata;
440 
441 	UNUSED(zone);
442 	UNUSED(dbdata);
443 
444 	if (b == &empty_builtin) {
445 		server = ".";
446 		contact = ".";
447 	} else {
448 		if (b->server != NULL) {
449 			server = b->server;
450 		}
451 		if (b->contact != NULL) {
452 			contact = b->contact;
453 		}
454 	}
455 
456 	result = dns_sdb_putsoa(lookup, server, contact, 0);
457 	if (result != ISC_R_SUCCESS) {
458 		return (ISC_R_FAILURE);
459 	}
460 
461 	result = dns_sdb_putrr(lookup, "ns", 0, server);
462 	if (result != ISC_R_SUCCESS) {
463 		return (ISC_R_FAILURE);
464 	}
465 
466 	return (ISC_R_SUCCESS);
467 }
468 
469 static isc_result_t
builtin_create(const char * zone,int argc,char ** argv,void * driverdata,void ** dbdata)470 builtin_create(const char *zone, int argc, char **argv, void *driverdata,
471 	       void **dbdata) {
472 	REQUIRE(argc >= 1);
473 
474 	UNUSED(zone);
475 	UNUSED(driverdata);
476 
477 	if (strcmp(argv[0], "empty") == 0 || strcmp(argv[0], "dns64") == 0) {
478 		if (argc != 3) {
479 			return (DNS_R_SYNTAX);
480 		}
481 	} else if (argc != 1) {
482 		return (DNS_R_SYNTAX);
483 	}
484 
485 	if (strcmp(argv[0], "version") == 0) {
486 		*dbdata = &version_builtin;
487 	} else if (strcmp(argv[0], "hostname") == 0) {
488 		*dbdata = &hostname_builtin;
489 	} else if (strcmp(argv[0], "authors") == 0) {
490 		*dbdata = &authors_builtin;
491 	} else if (strcmp(argv[0], "id") == 0) {
492 		*dbdata = &id_builtin;
493 	} else if (strcmp(argv[0], "empty") == 0 ||
494 		   strcmp(argv[0], "dns64") == 0) {
495 		builtin_t *empty;
496 		char *server;
497 		char *contact;
498 		/*
499 		 * We don't want built-in zones to fail.  Fallback to
500 		 * the static configuration if memory allocation fails.
501 		 */
502 		empty = isc_mem_get(named_g_mctx, sizeof(*empty));
503 		server = isc_mem_strdup(named_g_mctx, argv[1]);
504 		contact = isc_mem_strdup(named_g_mctx, argv[2]);
505 		if (empty == NULL || server == NULL || contact == NULL) {
506 			if (strcmp(argv[0], "empty") == 0) {
507 				*dbdata = &empty_builtin;
508 			} else {
509 				*dbdata = &dns64_builtin;
510 			}
511 			if (server != NULL) {
512 				isc_mem_free(named_g_mctx, server);
513 			}
514 			if (contact != NULL) {
515 				isc_mem_free(named_g_mctx, contact);
516 			}
517 			if (empty != NULL) {
518 				isc_mem_put(named_g_mctx, empty,
519 					    sizeof(*empty));
520 			}
521 		} else {
522 			if (strcmp(argv[0], "empty") == 0) {
523 				memmove(empty, &empty_builtin,
524 					sizeof(empty_builtin));
525 			} else {
526 				memmove(empty, &dns64_builtin,
527 					sizeof(empty_builtin));
528 			}
529 			empty->server = server;
530 			empty->contact = contact;
531 			*dbdata = empty;
532 		}
533 	} else {
534 		return (ISC_R_NOTIMPLEMENTED);
535 	}
536 	return (ISC_R_SUCCESS);
537 }
538 
539 static void
builtin_destroy(const char * zone,void * driverdata,void ** dbdata)540 builtin_destroy(const char *zone, void *driverdata, void **dbdata) {
541 	builtin_t *b = (builtin_t *)*dbdata;
542 
543 	UNUSED(zone);
544 	UNUSED(driverdata);
545 
546 	/*
547 	 * Don't free the static versions.
548 	 */
549 	if (*dbdata == &version_builtin || *dbdata == &hostname_builtin ||
550 	    *dbdata == &authors_builtin || *dbdata == &id_builtin ||
551 	    *dbdata == &empty_builtin || *dbdata == &dns64_builtin)
552 	{
553 		return;
554 	}
555 
556 	isc_mem_free(named_g_mctx, b->server);
557 	isc_mem_free(named_g_mctx, b->contact);
558 	isc_mem_put(named_g_mctx, b, sizeof(*b));
559 }
560 
561 static dns_sdbmethods_t builtin_methods = {
562 	builtin_lookup, builtin_authority, NULL, /* allnodes */
563 	builtin_create, builtin_destroy,   NULL
564 };
565 
566 static dns_sdbmethods_t dns64_methods = {
567 	NULL,		builtin_authority, NULL, /* allnodes */
568 	builtin_create, builtin_destroy,   dns64_lookup,
569 };
570 
571 isc_result_t
named_builtin_init(void)572 named_builtin_init(void) {
573 	RUNTIME_CHECK(dns_sdb_register("_builtin", &builtin_methods, NULL,
574 				       DNS_SDBFLAG_RELATIVEOWNER |
575 					       DNS_SDBFLAG_RELATIVERDATA,
576 				       named_g_mctx,
577 				       &builtin_impl) == ISC_R_SUCCESS);
578 	RUNTIME_CHECK(dns_sdb_register("_dns64", &dns64_methods, NULL,
579 				       DNS_SDBFLAG_RELATIVEOWNER |
580 					       DNS_SDBFLAG_RELATIVERDATA |
581 					       DNS_SDBFLAG_DNS64,
582 				       named_g_mctx,
583 				       &dns64_impl) == ISC_R_SUCCESS);
584 	return (ISC_R_SUCCESS);
585 }
586 
587 void
named_builtin_deinit(void)588 named_builtin_deinit(void) {
589 	dns_sdb_unregister(&builtin_impl);
590 	dns_sdb_unregister(&dns64_impl);
591 }
592