xref: /netbsd-src/external/mpl/bind/dist/lib/dns/ssu.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: ssu.c,v 1.10 2025/01/26 16:25:25 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <stdbool.h>
19 
20 #include <isc/magic.h>
21 #include <isc/mem.h>
22 #include <isc/netaddr.h>
23 #include <isc/refcount.h>
24 #include <isc/result.h>
25 #include <isc/string.h>
26 #include <isc/util.h>
27 
28 #include <dns/dlz.h>
29 #include <dns/fixedname.h>
30 #include <dns/name.h>
31 #include <dns/ssu.h>
32 
33 #include <dst/dst.h>
34 #include <dst/gssapi.h>
35 
36 #define SSUTABLEMAGIC	      ISC_MAGIC('S', 'S', 'U', 'T')
37 #define VALID_SSUTABLE(table) ISC_MAGIC_VALID(table, SSUTABLEMAGIC)
38 
39 #define SSURULEMAGIC	     ISC_MAGIC('S', 'S', 'U', 'R')
40 #define VALID_SSURULE(table) ISC_MAGIC_VALID(table, SSURULEMAGIC)
41 
42 struct dns_ssurule {
43 	unsigned int magic;
44 	bool grant;		      /*%< is this a grant or a deny? */
45 	dns_ssumatchtype_t matchtype; /*%< which type of pattern match? */
46 	dns_name_t *identity;	      /*%< the identity to match */
47 	dns_name_t *name;	      /*%< the name being updated */
48 	unsigned int ntypes;	      /*%< number of data types covered */
49 	dns_ssuruletype_t *types;     /*%< the data types.  Can include */
50 				      /*   ANY. if NULL, defaults to all */
51 				      /*   types except SIG, SOA, and NS */
52 	ISC_LINK(dns_ssurule_t) link;
53 };
54 
55 struct dns_ssutable {
56 	unsigned int magic;
57 	isc_mem_t *mctx;
58 	isc_refcount_t references;
59 	dns_dlzdb_t *dlzdatabase;
60 	ISC_LIST(dns_ssurule_t) rules;
61 };
62 
63 void
64 dns_ssutable_create(isc_mem_t *mctx, dns_ssutable_t **tablep) {
65 	dns_ssutable_t *table;
66 
67 	REQUIRE(tablep != NULL && *tablep == NULL);
68 	REQUIRE(mctx != NULL);
69 
70 	table = isc_mem_get(mctx, sizeof(*table));
71 	isc_refcount_init(&table->references, 1);
72 	table->mctx = NULL;
73 	isc_mem_attach(mctx, &table->mctx);
74 	ISC_LIST_INIT(table->rules);
75 	table->magic = SSUTABLEMAGIC;
76 	*tablep = table;
77 }
78 
79 static void
80 destroy(dns_ssutable_t *table) {
81 	isc_mem_t *mctx;
82 
83 	REQUIRE(VALID_SSUTABLE(table));
84 
85 	mctx = table->mctx;
86 	while (!ISC_LIST_EMPTY(table->rules)) {
87 		dns_ssurule_t *rule = ISC_LIST_HEAD(table->rules);
88 		if (rule->identity != NULL) {
89 			dns_name_free(rule->identity, mctx);
90 			isc_mem_put(mctx, rule->identity,
91 				    sizeof(*rule->identity));
92 		}
93 		if (rule->name != NULL) {
94 			dns_name_free(rule->name, mctx);
95 			isc_mem_put(mctx, rule->name, sizeof(*rule->name));
96 		}
97 		if (rule->types != NULL) {
98 			isc_mem_cput(mctx, rule->types, rule->ntypes,
99 				     sizeof(*rule->types));
100 		}
101 		ISC_LIST_UNLINK(table->rules, rule, link);
102 		rule->magic = 0;
103 		isc_mem_put(mctx, rule, sizeof(dns_ssurule_t));
104 	}
105 	isc_refcount_destroy(&table->references);
106 	table->magic = 0;
107 	isc_mem_putanddetach(&table->mctx, table, sizeof(dns_ssutable_t));
108 }
109 
110 void
111 dns_ssutable_attach(dns_ssutable_t *source, dns_ssutable_t **targetp) {
112 	REQUIRE(VALID_SSUTABLE(source));
113 	REQUIRE(targetp != NULL && *targetp == NULL);
114 
115 	isc_refcount_increment(&source->references);
116 
117 	*targetp = source;
118 }
119 
120 void
121 dns_ssutable_detach(dns_ssutable_t **tablep) {
122 	dns_ssutable_t *table;
123 
124 	REQUIRE(tablep != NULL);
125 	table = *tablep;
126 	*tablep = NULL;
127 	REQUIRE(VALID_SSUTABLE(table));
128 
129 	if (isc_refcount_decrement(&table->references) == 1) {
130 		destroy(table);
131 	}
132 }
133 
134 void
135 dns_ssutable_addrule(dns_ssutable_t *table, bool grant,
136 		     const dns_name_t *identity, dns_ssumatchtype_t matchtype,
137 		     const dns_name_t *name, unsigned int ntypes,
138 		     dns_ssuruletype_t *types) {
139 	dns_ssurule_t *rule;
140 	isc_mem_t *mctx;
141 
142 	REQUIRE(VALID_SSUTABLE(table));
143 	REQUIRE(dns_name_isabsolute(identity));
144 	REQUIRE(dns_name_isabsolute(name));
145 	REQUIRE(matchtype <= dns_ssumatchtype_max);
146 	if (matchtype == dns_ssumatchtype_wildcard) {
147 		REQUIRE(dns_name_iswildcard(name));
148 	}
149 	if (ntypes > 0) {
150 		REQUIRE(types != NULL);
151 	}
152 
153 	mctx = table->mctx;
154 	rule = isc_mem_get(mctx, sizeof(*rule));
155 
156 	rule->identity = NULL;
157 	rule->name = NULL;
158 	rule->types = NULL;
159 
160 	rule->grant = grant;
161 
162 	rule->identity = isc_mem_get(mctx, sizeof(*rule->identity));
163 	dns_name_init(rule->identity, NULL);
164 	dns_name_dup(identity, mctx, rule->identity);
165 
166 	rule->name = isc_mem_get(mctx, sizeof(*rule->name));
167 	dns_name_init(rule->name, NULL);
168 	dns_name_dup(name, mctx, rule->name);
169 
170 	rule->matchtype = matchtype;
171 
172 	rule->ntypes = ntypes;
173 	if (ntypes > 0) {
174 		rule->types = isc_mem_cget(mctx, ntypes, sizeof(*rule->types));
175 		memmove(rule->types, types, ntypes * sizeof(*rule->types));
176 	} else {
177 		rule->types = NULL;
178 	}
179 
180 	rule->magic = SSURULEMAGIC;
181 	ISC_LIST_INITANDAPPEND(table->rules, rule, link);
182 }
183 
184 static bool
185 isusertype(dns_rdatatype_t type) {
186 	return type != dns_rdatatype_ns && type != dns_rdatatype_soa &&
187 	       type != dns_rdatatype_rrsig;
188 }
189 
190 static void
191 reverse_from_address(dns_name_t *tcpself, const isc_netaddr_t *tcpaddr) {
192 	char buf[16 * 4 + sizeof("IP6.ARPA.")];
193 	isc_result_t result;
194 	const unsigned char *ap;
195 	isc_buffer_t b;
196 	unsigned long l;
197 
198 	switch (tcpaddr->family) {
199 	case AF_INET:
200 		l = ntohl(tcpaddr->type.in.s_addr);
201 		result = snprintf(buf, sizeof(buf),
202 				  "%lu.%lu.%lu.%lu.IN-ADDR.ARPA.",
203 				  (l >> 0) & 0xff, (l >> 8) & 0xff,
204 				  (l >> 16) & 0xff, (l >> 24) & 0xff);
205 		RUNTIME_CHECK(result < sizeof(buf));
206 		break;
207 	case AF_INET6:
208 		ap = tcpaddr->type.in6.s6_addr;
209 		result = snprintf(
210 			buf, sizeof(buf),
211 			"%x.%x.%x.%x.%x.%x.%x.%x."
212 			"%x.%x.%x.%x.%x.%x.%x.%x."
213 			"%x.%x.%x.%x.%x.%x.%x.%x."
214 			"%x.%x.%x.%x.%x.%x.%x.%x."
215 			"IP6.ARPA.",
216 			ap[15] & 0x0f, (ap[15] >> 4) & 0x0f, ap[14] & 0x0f,
217 			(ap[14] >> 4) & 0x0f, ap[13] & 0x0f,
218 			(ap[13] >> 4) & 0x0f, ap[12] & 0x0f,
219 			(ap[12] >> 4) & 0x0f, ap[11] & 0x0f,
220 			(ap[11] >> 4) & 0x0f, ap[10] & 0x0f,
221 			(ap[10] >> 4) & 0x0f, ap[9] & 0x0f, (ap[9] >> 4) & 0x0f,
222 			ap[8] & 0x0f, (ap[8] >> 4) & 0x0f, ap[7] & 0x0f,
223 			(ap[7] >> 4) & 0x0f, ap[6] & 0x0f, (ap[6] >> 4) & 0x0f,
224 			ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, ap[4] & 0x0f,
225 			(ap[4] >> 4) & 0x0f, ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
226 			ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, ap[1] & 0x0f,
227 			(ap[1] >> 4) & 0x0f, ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
228 		RUNTIME_CHECK(result < sizeof(buf));
229 		break;
230 	default:
231 		UNREACHABLE();
232 	}
233 	isc_buffer_init(&b, buf, strlen(buf));
234 	isc_buffer_add(&b, strlen(buf));
235 	result = dns_name_fromtext(tcpself, &b, dns_rootname, 0, NULL);
236 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
237 }
238 
239 static void
240 stf_from_address(dns_name_t *stfself, const isc_netaddr_t *tcpaddr) {
241 	char buf[sizeof("X.X.X.X.Y.Y.Y.Y.2.0.0.2.IP6.ARPA.")];
242 	isc_result_t result;
243 	const unsigned char *ap;
244 	isc_buffer_t b;
245 	unsigned long l;
246 
247 	switch (tcpaddr->family) {
248 	case AF_INET:
249 		l = ntohl(tcpaddr->type.in.s_addr);
250 		result = snprintf(
251 			buf, sizeof(buf),
252 			"%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.2.0.0.2.IP6.ARPA.",
253 			l & 0xf, (l >> 4) & 0xf, (l >> 8) & 0xf,
254 			(l >> 12) & 0xf, (l >> 16) & 0xf, (l >> 20) & 0xf,
255 			(l >> 24) & 0xf, (l >> 28) & 0xf);
256 		RUNTIME_CHECK(result < sizeof(buf));
257 		break;
258 	case AF_INET6:
259 		ap = tcpaddr->type.in6.s6_addr;
260 		result = snprintf(
261 			buf, sizeof(buf),
262 			"%x.%x.%x.%x.%x.%x.%x.%x."
263 			"%x.%x.%x.%x.IP6.ARPA.",
264 			ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, ap[4] & 0x0f,
265 			(ap[4] >> 4) & 0x0f, ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
266 			ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, ap[1] & 0x0f,
267 			(ap[1] >> 4) & 0x0f, ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
268 		RUNTIME_CHECK(result < sizeof(buf));
269 		break;
270 	default:
271 		UNREACHABLE();
272 	}
273 	isc_buffer_init(&b, buf, strlen(buf));
274 	isc_buffer_add(&b, strlen(buf));
275 	result = dns_name_fromtext(stfself, &b, dns_rootname, 0, NULL);
276 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
277 }
278 
279 bool
280 dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
281 			const dns_name_t *name, const isc_netaddr_t *addr,
282 			bool tcp, dns_aclenv_t *env, dns_rdatatype_t type,
283 			const dns_name_t *target, const dst_key_t *key,
284 			const dns_ssurule_t **rulep) {
285 	dns_fixedname_t fixed;
286 	dns_name_t *stfself;
287 	dns_name_t *tcpself;
288 	dns_name_t *wildcard;
289 	dns_ssurule_t *rule;
290 	const dns_name_t *tname;
291 	int match;
292 	isc_result_t result;
293 	unsigned int i;
294 
295 	REQUIRE(VALID_SSUTABLE(table));
296 	REQUIRE(signer == NULL || dns_name_isabsolute(signer));
297 	REQUIRE(dns_name_isabsolute(name));
298 	REQUIRE(addr == NULL || env != NULL);
299 
300 	if (signer == NULL && addr == NULL) {
301 		return false;
302 	}
303 
304 	for (rule = ISC_LIST_HEAD(table->rules); rule != NULL;
305 	     rule = ISC_LIST_NEXT(rule, link))
306 	{
307 		switch (rule->matchtype) {
308 		case dns_ssumatchtype_local:
309 		case dns_ssumatchtype_name:
310 		case dns_ssumatchtype_self:
311 		case dns_ssumatchtype_selfsub:
312 		case dns_ssumatchtype_selfwild:
313 		case dns_ssumatchtype_subdomain:
314 		case dns_ssumatchtype_wildcard:
315 			if (signer == NULL) {
316 				continue;
317 			}
318 			if (dns_name_iswildcard(rule->identity)) {
319 				if (!dns_name_matcheswildcard(signer,
320 							      rule->identity))
321 				{
322 					continue;
323 				}
324 			} else {
325 				if (!dns_name_equal(signer, rule->identity)) {
326 					continue;
327 				}
328 			}
329 			break;
330 		case dns_ssumatchtype_selfkrb5:
331 		case dns_ssumatchtype_selfms:
332 		case dns_ssumatchtype_selfsubkrb5:
333 		case dns_ssumatchtype_selfsubms:
334 		case dns_ssumatchtype_subdomainkrb5:
335 		case dns_ssumatchtype_subdomainms:
336 		case dns_ssumatchtype_subdomainselfkrb5rhs:
337 		case dns_ssumatchtype_subdomainselfmsrhs:
338 			if (signer == NULL) {
339 				continue;
340 			}
341 			break;
342 		case dns_ssumatchtype_tcpself:
343 		case dns_ssumatchtype_6to4self:
344 			if (!tcp || addr == NULL) {
345 				continue;
346 			}
347 			break;
348 		case dns_ssumatchtype_external:
349 		case dns_ssumatchtype_dlz:
350 			break;
351 		}
352 
353 		switch (rule->matchtype) {
354 		case dns_ssumatchtype_name:
355 			if (!dns_name_equal(name, rule->name)) {
356 				continue;
357 			}
358 			break;
359 		case dns_ssumatchtype_subdomain:
360 			if (!dns_name_issubdomain(name, rule->name)) {
361 				continue;
362 			}
363 			break;
364 		case dns_ssumatchtype_local:
365 			if (addr == NULL) {
366 				continue;
367 			}
368 			if (!dns_name_issubdomain(name, rule->name)) {
369 				continue;
370 			}
371 			rcu_read_lock();
372 			dns_acl_t *localhost = rcu_dereference(env->localhost);
373 			dns_acl_match(addr, NULL, localhost, NULL, &match,
374 				      NULL);
375 			rcu_read_unlock();
376 			if (match == 0) {
377 				if (signer != NULL) {
378 					isc_log_write(dns_lctx,
379 						      DNS_LOGCATEGORY_GENERAL,
380 						      DNS_LOGMODULE_SSU,
381 						      ISC_LOG_WARNING,
382 						      "update-policy local: "
383 						      "match on session "
384 						      "key not from "
385 						      "localhost");
386 				}
387 				continue;
388 			}
389 			break;
390 		case dns_ssumatchtype_wildcard:
391 			if (!dns_name_matcheswildcard(name, rule->name)) {
392 				continue;
393 			}
394 			break;
395 		case dns_ssumatchtype_self:
396 			if (!dns_name_equal(signer, name)) {
397 				continue;
398 			}
399 			break;
400 		case dns_ssumatchtype_selfsub:
401 			if (!dns_name_issubdomain(name, signer)) {
402 				continue;
403 			}
404 			break;
405 		case dns_ssumatchtype_selfwild:
406 			wildcard = dns_fixedname_initname(&fixed);
407 			result = dns_name_concatenate(dns_wildcardname, signer,
408 						      wildcard, NULL);
409 			if (result != ISC_R_SUCCESS) {
410 				continue;
411 			}
412 			if (!dns_name_matcheswildcard(name, wildcard)) {
413 				continue;
414 			}
415 			break;
416 		case dns_ssumatchtype_selfkrb5:
417 			if (dst_gssapi_identitymatchesrealmkrb5(
418 				    signer, name, rule->identity, false))
419 			{
420 				break;
421 			}
422 			continue;
423 		case dns_ssumatchtype_selfms:
424 			if (dst_gssapi_identitymatchesrealmms(
425 				    signer, name, rule->identity, false))
426 			{
427 				break;
428 			}
429 			continue;
430 		case dns_ssumatchtype_selfsubkrb5:
431 			if (dst_gssapi_identitymatchesrealmkrb5(
432 				    signer, name, rule->identity, true))
433 			{
434 				break;
435 			}
436 			continue;
437 		case dns_ssumatchtype_selfsubms:
438 			if (dst_gssapi_identitymatchesrealmms(
439 				    signer, name, rule->identity, true))
440 			{
441 				break;
442 			}
443 			continue;
444 		case dns_ssumatchtype_subdomainkrb5:
445 		case dns_ssumatchtype_subdomainselfkrb5rhs:
446 			if (!dns_name_issubdomain(name, rule->name)) {
447 				continue;
448 			}
449 			tname = NULL;
450 			switch (rule->matchtype) {
451 			case dns_ssumatchtype_subdomainselfkrb5rhs:
452 				if (type == dns_rdatatype_ptr) {
453 					tname = target;
454 				}
455 				if (type == dns_rdatatype_srv) {
456 					tname = target;
457 				}
458 				break;
459 			default:
460 				break;
461 			}
462 			if (dst_gssapi_identitymatchesrealmkrb5(
463 				    signer, tname, rule->identity, false))
464 			{
465 				break;
466 			}
467 			continue;
468 		case dns_ssumatchtype_subdomainms:
469 		case dns_ssumatchtype_subdomainselfmsrhs:
470 			if (!dns_name_issubdomain(name, rule->name)) {
471 				continue;
472 			}
473 			tname = NULL;
474 			switch (rule->matchtype) {
475 			case dns_ssumatchtype_subdomainselfmsrhs:
476 				if (type == dns_rdatatype_ptr) {
477 					tname = target;
478 				}
479 				if (type == dns_rdatatype_srv) {
480 					tname = target;
481 				}
482 				break;
483 			default:
484 				break;
485 			}
486 			if (dst_gssapi_identitymatchesrealmms(
487 				    signer, tname, rule->identity, false))
488 			{
489 				break;
490 			}
491 			continue;
492 		case dns_ssumatchtype_tcpself:
493 			tcpself = dns_fixedname_initname(&fixed);
494 			reverse_from_address(tcpself, addr);
495 			if (dns_name_iswildcard(rule->identity)) {
496 				if (!dns_name_matcheswildcard(tcpself,
497 							      rule->identity))
498 				{
499 					continue;
500 				}
501 			} else {
502 				if (!dns_name_equal(tcpself, rule->identity)) {
503 					continue;
504 				}
505 			}
506 			if (!dns_name_equal(tcpself, name)) {
507 				continue;
508 			}
509 			break;
510 		case dns_ssumatchtype_6to4self:
511 			stfself = dns_fixedname_initname(&fixed);
512 			stf_from_address(stfself, addr);
513 			if (dns_name_iswildcard(rule->identity)) {
514 				if (!dns_name_matcheswildcard(stfself,
515 							      rule->identity))
516 				{
517 					continue;
518 				}
519 			} else {
520 				if (!dns_name_equal(stfself, rule->identity)) {
521 					continue;
522 				}
523 			}
524 			if (!dns_name_equal(stfself, name)) {
525 				continue;
526 			}
527 			break;
528 		case dns_ssumatchtype_external:
529 			if (!dns_ssu_external_match(rule->identity, signer,
530 						    name, addr, type, key,
531 						    table->mctx))
532 			{
533 				continue;
534 			}
535 			break;
536 		case dns_ssumatchtype_dlz:
537 			if (!dns_dlz_ssumatch(table->dlzdatabase, signer, name,
538 					      addr, type, key))
539 			{
540 				continue;
541 			}
542 			break;
543 		}
544 
545 		if (rule->ntypes == 0) {
546 			/*
547 			 * If this is a DLZ rule, then the DLZ ssu
548 			 * checks will have already checked the type.
549 			 */
550 			if (rule->matchtype != dns_ssumatchtype_dlz &&
551 			    !isusertype(type))
552 			{
553 				continue;
554 			}
555 		} else {
556 			for (i = 0; i < rule->ntypes; i++) {
557 				if (rule->types[i].type == dns_rdatatype_any ||
558 				    rule->types[i].type == type)
559 				{
560 					break;
561 				}
562 			}
563 			if (i == rule->ntypes) {
564 				continue;
565 			}
566 		}
567 		if (rule->grant && rulep != NULL) {
568 			*rulep = rule;
569 		}
570 		return rule->grant;
571 	}
572 
573 	return false;
574 }
575 
576 bool
577 dns_ssurule_isgrant(const dns_ssurule_t *rule) {
578 	REQUIRE(VALID_SSURULE(rule));
579 	return rule->grant;
580 }
581 
582 dns_name_t *
583 dns_ssurule_identity(const dns_ssurule_t *rule) {
584 	REQUIRE(VALID_SSURULE(rule));
585 	return rule->identity;
586 }
587 
588 unsigned int
589 dns_ssurule_matchtype(const dns_ssurule_t *rule) {
590 	REQUIRE(VALID_SSURULE(rule));
591 	return rule->matchtype;
592 }
593 
594 dns_name_t *
595 dns_ssurule_name(const dns_ssurule_t *rule) {
596 	REQUIRE(VALID_SSURULE(rule));
597 	return rule->name;
598 }
599 
600 unsigned int
601 dns_ssurule_types(const dns_ssurule_t *rule, dns_ssuruletype_t **types) {
602 	REQUIRE(VALID_SSURULE(rule));
603 	REQUIRE(types != NULL && *types != NULL);
604 	*types = rule->types;
605 	return rule->ntypes;
606 }
607 
608 unsigned int
609 dns_ssurule_max(const dns_ssurule_t *rule, dns_rdatatype_t type) {
610 	unsigned int i;
611 	unsigned int max = 0;
612 
613 	REQUIRE(VALID_SSURULE(rule));
614 
615 	for (i = 0; i < rule->ntypes; i++) {
616 		if (rule->types[i].type == dns_rdatatype_any) {
617 			max = rule->types[i].max;
618 		}
619 		if (rule->types[i].type == type) {
620 			return rule->types[i].max;
621 		}
622 	}
623 	return max;
624 }
625 
626 isc_result_t
627 dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule) {
628 	REQUIRE(VALID_SSUTABLE(table));
629 	REQUIRE(rule != NULL && *rule == NULL);
630 	*rule = ISC_LIST_HEAD(table->rules);
631 	return *rule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE;
632 }
633 
634 isc_result_t
635 dns_ssutable_nextrule(dns_ssurule_t *rule, dns_ssurule_t **nextrule) {
636 	REQUIRE(VALID_SSURULE(rule));
637 	REQUIRE(nextrule != NULL && *nextrule == NULL);
638 	*nextrule = ISC_LIST_NEXT(rule, link);
639 	return *nextrule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE;
640 }
641 
642 /*
643  * Create a specialised SSU table that points at an external DLZ database
644  */
645 void
646 dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep,
647 		       dns_dlzdb_t *dlzdatabase) {
648 	dns_ssurule_t *rule;
649 	dns_ssutable_t *table = NULL;
650 
651 	REQUIRE(tablep != NULL && *tablep == NULL);
652 
653 	dns_ssutable_create(mctx, &table);
654 
655 	table->dlzdatabase = dlzdatabase;
656 
657 	rule = isc_mem_get(table->mctx, sizeof(dns_ssurule_t));
658 
659 	rule->identity = NULL;
660 	rule->name = NULL;
661 	rule->grant = true;
662 	rule->matchtype = dns_ssumatchtype_dlz;
663 	rule->ntypes = 0;
664 	rule->types = NULL;
665 	rule->magic = SSURULEMAGIC;
666 
667 	ISC_LIST_INITANDAPPEND(table->rules, rule, link);
668 	*tablep = table;
669 }
670 
671 isc_result_t
672 dns_ssu_mtypefromstring(const char *str, dns_ssumatchtype_t *mtype) {
673 	REQUIRE(str != NULL);
674 	REQUIRE(mtype != NULL);
675 
676 	if (strcasecmp(str, "name") == 0) {
677 		*mtype = dns_ssumatchtype_name;
678 	} else if (strcasecmp(str, "subdomain") == 0) {
679 		*mtype = dns_ssumatchtype_subdomain;
680 	} else if (strcasecmp(str, "wildcard") == 0) {
681 		*mtype = dns_ssumatchtype_wildcard;
682 	} else if (strcasecmp(str, "self") == 0) {
683 		*mtype = dns_ssumatchtype_self;
684 	} else if (strcasecmp(str, "selfsub") == 0) {
685 		*mtype = dns_ssumatchtype_selfsub;
686 	} else if (strcasecmp(str, "selfwild") == 0) {
687 		*mtype = dns_ssumatchtype_selfwild;
688 	} else if (strcasecmp(str, "ms-self") == 0) {
689 		*mtype = dns_ssumatchtype_selfms;
690 	} else if (strcasecmp(str, "ms-selfsub") == 0) {
691 		*mtype = dns_ssumatchtype_selfsubms;
692 	} else if (strcasecmp(str, "krb5-self") == 0) {
693 		*mtype = dns_ssumatchtype_selfkrb5;
694 	} else if (strcasecmp(str, "krb5-selfsub") == 0) {
695 		*mtype = dns_ssumatchtype_selfsubkrb5;
696 	} else if (strcasecmp(str, "ms-subdomain") == 0) {
697 		*mtype = dns_ssumatchtype_subdomainms;
698 	} else if (strcasecmp(str, "ms-subdomain-self-rhs") == 0) {
699 		*mtype = dns_ssumatchtype_subdomainselfmsrhs;
700 	} else if (strcasecmp(str, "krb5-subdomain") == 0) {
701 		*mtype = dns_ssumatchtype_subdomainkrb5;
702 	} else if (strcasecmp(str, "krb5-subdomain-self-rhs") == 0) {
703 		*mtype = dns_ssumatchtype_subdomainselfkrb5rhs;
704 	} else if (strcasecmp(str, "tcp-self") == 0) {
705 		*mtype = dns_ssumatchtype_tcpself;
706 	} else if (strcasecmp(str, "6to4-self") == 0) {
707 		*mtype = dns_ssumatchtype_6to4self;
708 	} else if (strcasecmp(str, "zonesub") == 0) {
709 		*mtype = dns_ssumatchtype_subdomain;
710 	} else if (strcasecmp(str, "external") == 0) {
711 		*mtype = dns_ssumatchtype_external;
712 	} else {
713 		return ISC_R_NOTFOUND;
714 	}
715 	return ISC_R_SUCCESS;
716 }
717