ssu.c revision 1.6
118334Speter/*	$NetBSD: ssu.c,v 1.6 2022/09/23 12:15:30 christos Exp $	*/
218334Speter
318334Speter/*
418334Speter * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
518334Speter *
618334Speter * SPDX-License-Identifier: MPL-2.0
718334Speter *
818334Speter * This Source Code Form is subject to the terms of the Mozilla Public
918334Speter * License, v. 2.0. If a copy of the MPL was not distributed with this
1018334Speter * file, you can obtain one at https://mozilla.org/MPL/2.0/.
1118334Speter *
1218334Speter * See the COPYRIGHT file distributed with this work for additional
1318334Speter * information regarding copyright ownership.
1418334Speter */
1518334Speter
1618334Speter/*! \file */
1718334Speter
1818334Speter#include <stdbool.h>
1918334Speter
2018334Speter#include <isc/magic.h>
2118334Speter#include <isc/mem.h>
2218334Speter#include <isc/netaddr.h>
2318334Speter#include <isc/print.h>
2418334Speter#include <isc/refcount.h>
2518334Speter#include <isc/result.h>
2618334Speter#include <isc/string.h>
2718334Speter#include <isc/util.h>
2818334Speter
2918334Speter#include <dns/dlz.h>
3018334Speter#include <dns/fixedname.h>
3118334Speter#include <dns/name.h>
3218334Speter#include <dns/ssu.h>
3318334Speter
3418334Speter#include <dst/dst.h>
3518334Speter#include <dst/gssapi.h>
3618334Speter
3718334Speter#define SSUTABLEMAGIC	      ISC_MAGIC('S', 'S', 'U', 'T')
3818334Speter#define VALID_SSUTABLE(table) ISC_MAGIC_VALID(table, SSUTABLEMAGIC)
3918334Speter
4018334Speter#define SSURULEMAGIC	     ISC_MAGIC('S', 'S', 'U', 'R')
4118334Speter#define VALID_SSURULE(table) ISC_MAGIC_VALID(table, SSURULEMAGIC)
4218334Speter
4318334Speterstruct dns_ssurule {
4418334Speter	unsigned int magic;
4518334Speter	bool grant;		      /*%< is this a grant or a deny? */
4618334Speter	dns_ssumatchtype_t matchtype; /*%< which type of pattern match?
4718334Speter				       * */
4818334Speter	dns_name_t *identity;	      /*%< the identity to match */
4918334Speter	dns_name_t *name;	      /*%< the name being updated */
5018334Speter	unsigned int ntypes;	      /*%< number of data types covered */
5118334Speter	dns_rdatatype_t *types;	      /*%< the data types.  Can include */
5218334Speter				      /*   ANY. if NULL, defaults to all */
5318334Speter				      /*   types except SIG, SOA, and NS */
5418334Speter	ISC_LINK(dns_ssurule_t) link;
5518334Speter};
5618334Speter
5718334Speterstruct dns_ssutable {
5818334Speter	unsigned int magic;
5918334Speter	isc_mem_t *mctx;
6018334Speter	isc_refcount_t references;
6118334Speter	dns_dlzdb_t *dlzdatabase;
6218334Speter	ISC_LIST(dns_ssurule_t) rules;
6318334Speter};
6418334Speter
6518334Speterisc_result_t
6618334Speterdns_ssutable_create(isc_mem_t *mctx, dns_ssutable_t **tablep) {
6718334Speter	dns_ssutable_t *table;
6818334Speter
6918334Speter	REQUIRE(tablep != NULL && *tablep == NULL);
7018334Speter	REQUIRE(mctx != NULL);
7118334Speter
7218334Speter	table = isc_mem_get(mctx, sizeof(dns_ssutable_t));
7318334Speter	isc_refcount_init(&table->references, 1);
7418334Speter	table->mctx = NULL;
7518334Speter	isc_mem_attach(mctx, &table->mctx);
7618334Speter	ISC_LIST_INIT(table->rules);
7718334Speter	table->magic = SSUTABLEMAGIC;
7818334Speter	*tablep = table;
7918334Speter	return (ISC_R_SUCCESS);
8018334Speter}
8118334Speter
8218334Speterstatic void
8318334Speterdestroy(dns_ssutable_t *table) {
8418334Speter	isc_mem_t *mctx;
8518334Speter
8618334Speter	REQUIRE(VALID_SSUTABLE(table));
8718334Speter
8818334Speter	mctx = table->mctx;
8918334Speter	while (!ISC_LIST_EMPTY(table->rules)) {
9018334Speter		dns_ssurule_t *rule = ISC_LIST_HEAD(table->rules);
9118334Speter		if (rule->identity != NULL) {
9218334Speter			dns_name_free(rule->identity, mctx);
9318334Speter			isc_mem_put(mctx, rule->identity, sizeof(dns_name_t));
9418334Speter		}
9518334Speter		if (rule->name != NULL) {
9618334Speter			dns_name_free(rule->name, mctx);
9718334Speter			isc_mem_put(mctx, rule->name, sizeof(dns_name_t));
9818334Speter		}
9918334Speter		if (rule->types != NULL) {
10018334Speter			isc_mem_put(mctx, rule->types,
10118334Speter				    rule->ntypes * sizeof(dns_rdatatype_t));
10218334Speter		}
10318334Speter		ISC_LIST_UNLINK(table->rules, rule, link);
10418334Speter		rule->magic = 0;
10518334Speter		isc_mem_put(mctx, rule, sizeof(dns_ssurule_t));
10618334Speter	}
10718334Speter	isc_refcount_destroy(&table->references);
10818334Speter	table->magic = 0;
10918334Speter	isc_mem_putanddetach(&table->mctx, table, sizeof(dns_ssutable_t));
11018334Speter}
11118334Speter
11218334Spetervoid
11318334Speterdns_ssutable_attach(dns_ssutable_t *source, dns_ssutable_t **targetp) {
11418334Speter	REQUIRE(VALID_SSUTABLE(source));
11518334Speter	REQUIRE(targetp != NULL && *targetp == NULL);
11618334Speter
11718334Speter	isc_refcount_increment(&source->references);
11818334Speter
11918334Speter	*targetp = source;
12018334Speter}
12118334Speter
12218334Spetervoid
12318334Speterdns_ssutable_detach(dns_ssutable_t **tablep) {
12418334Speter	dns_ssutable_t *table;
12518334Speter
12618334Speter	REQUIRE(tablep != NULL);
12718334Speter	table = *tablep;
12818334Speter	*tablep = NULL;
12918334Speter	REQUIRE(VALID_SSUTABLE(table));
13018334Speter
13118334Speter	if (isc_refcount_decrement(&table->references) == 1) {
13218334Speter		destroy(table);
13318334Speter	}
13418334Speter}
13518334Speter
13618334Speterisc_result_t
13718334Speterdns_ssutable_addrule(dns_ssutable_t *table, bool grant,
13818334Speter		     const dns_name_t *identity, dns_ssumatchtype_t matchtype,
13918334Speter		     const dns_name_t *name, unsigned int ntypes,
14018334Speter		     dns_rdatatype_t *types) {
14118334Speter	dns_ssurule_t *rule;
14218334Speter	isc_mem_t *mctx;
14318334Speter
14418334Speter	REQUIRE(VALID_SSUTABLE(table));
14518334Speter	REQUIRE(dns_name_isabsolute(identity));
14618334Speter	REQUIRE(dns_name_isabsolute(name));
14718334Speter	REQUIRE(matchtype <= dns_ssumatchtype_max);
14818334Speter	if (matchtype == dns_ssumatchtype_wildcard) {
14918334Speter		REQUIRE(dns_name_iswildcard(name));
15018334Speter	}
15118334Speter	if (ntypes > 0) {
15218334Speter		REQUIRE(types != NULL);
15318334Speter	}
15418334Speter
15518334Speter	mctx = table->mctx;
15618334Speter	rule = isc_mem_get(mctx, sizeof(dns_ssurule_t));
15718334Speter
15818334Speter	rule->identity = NULL;
15918334Speter	rule->name = NULL;
16018334Speter	rule->types = NULL;
16118334Speter
16218334Speter	rule->grant = grant;
16318334Speter
16418334Speter	rule->identity = isc_mem_get(mctx, sizeof(dns_name_t));
16518334Speter	dns_name_init(rule->identity, NULL);
16618334Speter	dns_name_dup(identity, mctx, rule->identity);
16718334Speter
16818334Speter	rule->name = isc_mem_get(mctx, sizeof(dns_name_t));
16918334Speter	dns_name_init(rule->name, NULL);
17018334Speter	dns_name_dup(name, mctx, rule->name);
17118334Speter
17218334Speter	rule->matchtype = matchtype;
17318334Speter
17418334Speter	rule->ntypes = ntypes;
17518334Speter	if (ntypes > 0) {
17618334Speter		rule->types = isc_mem_get(mctx,
17718334Speter					  ntypes * sizeof(dns_rdatatype_t));
17818334Speter		memmove(rule->types, types, ntypes * sizeof(dns_rdatatype_t));
17918334Speter	} else {
18018334Speter		rule->types = NULL;
181	}
182
183	rule->magic = SSURULEMAGIC;
184	ISC_LIST_INITANDAPPEND(table->rules, rule, link);
185
186	return (ISC_R_SUCCESS);
187}
188
189static bool
190isusertype(dns_rdatatype_t type) {
191	return (type != dns_rdatatype_ns && type != dns_rdatatype_soa &&
192		type != dns_rdatatype_rrsig);
193}
194
195static void
196reverse_from_address(dns_name_t *tcpself, const isc_netaddr_t *tcpaddr) {
197	char buf[16 * 4 + sizeof("IP6.ARPA.")];
198	isc_result_t result;
199	const unsigned char *ap;
200	isc_buffer_t b;
201	unsigned long l;
202
203	switch (tcpaddr->family) {
204	case AF_INET:
205		l = ntohl(tcpaddr->type.in.s_addr);
206		result = snprintf(buf, sizeof(buf),
207				  "%lu.%lu.%lu.%lu.IN-ADDR.ARPA.",
208				  (l >> 0) & 0xff, (l >> 8) & 0xff,
209				  (l >> 16) & 0xff, (l >> 24) & 0xff);
210		RUNTIME_CHECK(result < sizeof(buf));
211		break;
212	case AF_INET6:
213		ap = tcpaddr->type.in6.s6_addr;
214		result = snprintf(
215			buf, sizeof(buf),
216			"%x.%x.%x.%x.%x.%x.%x.%x."
217			"%x.%x.%x.%x.%x.%x.%x.%x."
218			"%x.%x.%x.%x.%x.%x.%x.%x."
219			"%x.%x.%x.%x.%x.%x.%x.%x."
220			"IP6.ARPA.",
221			ap[15] & 0x0f, (ap[15] >> 4) & 0x0f, ap[14] & 0x0f,
222			(ap[14] >> 4) & 0x0f, ap[13] & 0x0f,
223			(ap[13] >> 4) & 0x0f, ap[12] & 0x0f,
224			(ap[12] >> 4) & 0x0f, ap[11] & 0x0f,
225			(ap[11] >> 4) & 0x0f, ap[10] & 0x0f,
226			(ap[10] >> 4) & 0x0f, ap[9] & 0x0f, (ap[9] >> 4) & 0x0f,
227			ap[8] & 0x0f, (ap[8] >> 4) & 0x0f, ap[7] & 0x0f,
228			(ap[7] >> 4) & 0x0f, ap[6] & 0x0f, (ap[6] >> 4) & 0x0f,
229			ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, ap[4] & 0x0f,
230			(ap[4] >> 4) & 0x0f, ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
231			ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, ap[1] & 0x0f,
232			(ap[1] >> 4) & 0x0f, ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
233		RUNTIME_CHECK(result < sizeof(buf));
234		break;
235	default:
236		UNREACHABLE();
237	}
238	isc_buffer_init(&b, buf, strlen(buf));
239	isc_buffer_add(&b, strlen(buf));
240	result = dns_name_fromtext(tcpself, &b, dns_rootname, 0, NULL);
241	RUNTIME_CHECK(result == ISC_R_SUCCESS);
242}
243
244static void
245stf_from_address(dns_name_t *stfself, const isc_netaddr_t *tcpaddr) {
246	char buf[sizeof("X.X.X.X.Y.Y.Y.Y.2.0.0.2.IP6.ARPA.")];
247	isc_result_t result;
248	const unsigned char *ap;
249	isc_buffer_t b;
250	unsigned long l;
251
252	switch (tcpaddr->family) {
253	case AF_INET:
254		l = ntohl(tcpaddr->type.in.s_addr);
255		result = snprintf(buf, sizeof(buf),
256				  "%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx"
257				  "2.0.0.2.IP6.ARPA.",
258				  l & 0xf, (l >> 4) & 0xf, (l >> 8) & 0xf,
259				  (l >> 12) & 0xf, (l >> 16) & 0xf,
260				  (l >> 20) & 0xf, (l >> 24) & 0xf,
261				  (l >> 28) & 0xf);
262		RUNTIME_CHECK(result < sizeof(buf));
263		break;
264	case AF_INET6:
265		ap = tcpaddr->type.in6.s6_addr;
266		result = snprintf(
267			buf, sizeof(buf),
268			"%x.%x.%x.%x.%x.%x.%x.%x."
269			"%x.%x.%x.%x.IP6.ARPA.",
270			ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, ap[4] & 0x0f,
271			(ap[4] >> 4) & 0x0f, ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
272			ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, ap[1] & 0x0f,
273			(ap[1] >> 4) & 0x0f, ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
274		RUNTIME_CHECK(result < sizeof(buf));
275		break;
276	default:
277		UNREACHABLE();
278	}
279	isc_buffer_init(&b, buf, strlen(buf));
280	isc_buffer_add(&b, strlen(buf));
281	result = dns_name_fromtext(stfself, &b, dns_rootname, 0, NULL);
282	RUNTIME_CHECK(result == ISC_R_SUCCESS);
283}
284
285bool
286dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
287			const dns_name_t *name, const isc_netaddr_t *addr,
288			bool tcp, const dns_aclenv_t *env, dns_rdatatype_t type,
289			const dst_key_t *key) {
290	dns_ssurule_t *rule;
291	unsigned int i;
292	dns_fixedname_t fixed;
293	dns_name_t *wildcard;
294	dns_name_t *tcpself;
295	dns_name_t *stfself;
296	isc_result_t result;
297	int match;
298
299	REQUIRE(VALID_SSUTABLE(table));
300	REQUIRE(signer == NULL || dns_name_isabsolute(signer));
301	REQUIRE(dns_name_isabsolute(name));
302	REQUIRE(addr == NULL || env != NULL);
303
304	if (signer == NULL && addr == NULL) {
305		return (false);
306	}
307
308	for (rule = ISC_LIST_HEAD(table->rules); rule != NULL;
309	     rule = ISC_LIST_NEXT(rule, link))
310	{
311		switch (rule->matchtype) {
312		case dns_ssumatchtype_name:
313		case dns_ssumatchtype_local:
314		case dns_ssumatchtype_subdomain:
315		case dns_ssumatchtype_wildcard:
316		case dns_ssumatchtype_self:
317		case dns_ssumatchtype_selfsub:
318		case dns_ssumatchtype_selfwild:
319			if (signer == NULL) {
320				continue;
321			}
322			if (dns_name_iswildcard(rule->identity)) {
323				if (!dns_name_matcheswildcard(signer,
324							      rule->identity)) {
325					continue;
326				}
327			} else {
328				if (!dns_name_equal(signer, rule->identity)) {
329					continue;
330				}
331			}
332			break;
333		case dns_ssumatchtype_selfkrb5:
334		case dns_ssumatchtype_selfms:
335		case dns_ssumatchtype_selfsubkrb5:
336		case dns_ssumatchtype_selfsubms:
337		case dns_ssumatchtype_subdomainkrb5:
338		case dns_ssumatchtype_subdomainms:
339			if (signer == NULL) {
340				continue;
341			}
342			break;
343		case dns_ssumatchtype_tcpself:
344		case dns_ssumatchtype_6to4self:
345			if (!tcp || addr == NULL) {
346				continue;
347			}
348			break;
349		case dns_ssumatchtype_external:
350		case dns_ssumatchtype_dlz:
351			break;
352		}
353
354		switch (rule->matchtype) {
355		case dns_ssumatchtype_name:
356			if (!dns_name_equal(name, rule->name)) {
357				continue;
358			}
359			break;
360		case dns_ssumatchtype_subdomain:
361			if (!dns_name_issubdomain(name, rule->name)) {
362				continue;
363			}
364			break;
365		case dns_ssumatchtype_local:
366			if (addr == NULL) {
367				continue;
368			}
369			if (!dns_name_issubdomain(name, rule->name)) {
370				continue;
371			}
372			dns_acl_match(addr, NULL, env->localhost, NULL, &match,
373				      NULL);
374			if (match == 0) {
375				if (signer != NULL) {
376					isc_log_write(dns_lctx,
377						      DNS_LOGCATEGORY_GENERAL,
378						      DNS_LOGMODULE_SSU,
379						      ISC_LOG_WARNING,
380						      "update-policy local: "
381						      "match on session "
382						      "key not from "
383						      "localhost");
384				}
385				continue;
386			}
387			break;
388		case dns_ssumatchtype_wildcard:
389			if (!dns_name_matcheswildcard(name, rule->name)) {
390				continue;
391			}
392			break;
393		case dns_ssumatchtype_self:
394			if (!dns_name_equal(signer, name)) {
395				continue;
396			}
397			break;
398		case dns_ssumatchtype_selfsub:
399			if (!dns_name_issubdomain(name, signer)) {
400				continue;
401			}
402			break;
403		case dns_ssumatchtype_selfwild:
404			wildcard = dns_fixedname_initname(&fixed);
405			result = dns_name_concatenate(dns_wildcardname, signer,
406						      wildcard, NULL);
407			if (result != ISC_R_SUCCESS) {
408				continue;
409			}
410			if (!dns_name_matcheswildcard(name, wildcard)) {
411				continue;
412			}
413			break;
414		case dns_ssumatchtype_selfkrb5:
415			if (dst_gssapi_identitymatchesrealmkrb5(
416				    signer, name, rule->identity, false)) {
417				break;
418			}
419			continue;
420		case dns_ssumatchtype_selfms:
421			if (dst_gssapi_identitymatchesrealmms(
422				    signer, name, rule->identity, false)) {
423				break;
424			}
425			continue;
426		case dns_ssumatchtype_selfsubkrb5:
427			if (dst_gssapi_identitymatchesrealmkrb5(
428				    signer, name, rule->identity, true)) {
429				break;
430			}
431			continue;
432		case dns_ssumatchtype_selfsubms:
433			if (dst_gssapi_identitymatchesrealmms(
434				    signer, name, rule->identity, true)) {
435				break;
436			}
437			continue;
438		case dns_ssumatchtype_subdomainkrb5:
439			if (!dns_name_issubdomain(name, rule->name)) {
440				continue;
441			}
442			if (dst_gssapi_identitymatchesrealmkrb5(
443				    signer, NULL, rule->identity, false)) {
444				break;
445			}
446			continue;
447		case dns_ssumatchtype_subdomainms:
448			if (!dns_name_issubdomain(name, rule->name)) {
449				continue;
450			}
451			if (dst_gssapi_identitymatchesrealmms(
452				    signer, NULL, rule->identity, false)) {
453				break;
454			}
455			continue;
456		case dns_ssumatchtype_tcpself:
457			tcpself = dns_fixedname_initname(&fixed);
458			reverse_from_address(tcpself, addr);
459			if (dns_name_iswildcard(rule->identity)) {
460				if (!dns_name_matcheswildcard(tcpself,
461							      rule->identity)) {
462					continue;
463				}
464			} else {
465				if (!dns_name_equal(tcpself, rule->identity)) {
466					continue;
467				}
468			}
469			if (!dns_name_equal(tcpself, name)) {
470				continue;
471			}
472			break;
473		case dns_ssumatchtype_6to4self:
474			stfself = dns_fixedname_initname(&fixed);
475			stf_from_address(stfself, addr);
476			if (dns_name_iswildcard(rule->identity)) {
477				if (!dns_name_matcheswildcard(stfself,
478							      rule->identity)) {
479					continue;
480				}
481			} else {
482				if (!dns_name_equal(stfself, rule->identity)) {
483					continue;
484				}
485			}
486			if (!dns_name_equal(stfself, name)) {
487				continue;
488			}
489			break;
490		case dns_ssumatchtype_external:
491			if (!dns_ssu_external_match(rule->identity, signer,
492						    name, addr, type, key,
493						    table->mctx))
494			{
495				continue;
496			}
497			break;
498		case dns_ssumatchtype_dlz:
499			if (!dns_dlz_ssumatch(table->dlzdatabase, signer, name,
500					      addr, type, key)) {
501				continue;
502			}
503			break;
504		}
505
506		if (rule->ntypes == 0) {
507			/*
508			 * If this is a DLZ rule, then the DLZ ssu
509			 * checks will have already checked
510			 * the type.
511			 */
512			if (rule->matchtype != dns_ssumatchtype_dlz &&
513			    !isusertype(type)) {
514				continue;
515			}
516		} else {
517			for (i = 0; i < rule->ntypes; i++) {
518				if (rule->types[i] == dns_rdatatype_any ||
519				    rule->types[i] == type) {
520					break;
521				}
522			}
523			if (i == rule->ntypes) {
524				continue;
525			}
526		}
527		return (rule->grant);
528	}
529
530	return (false);
531}
532
533bool
534dns_ssurule_isgrant(const dns_ssurule_t *rule) {
535	REQUIRE(VALID_SSURULE(rule));
536	return (rule->grant);
537}
538
539dns_name_t *
540dns_ssurule_identity(const dns_ssurule_t *rule) {
541	REQUIRE(VALID_SSURULE(rule));
542	return (rule->identity);
543}
544
545unsigned int
546dns_ssurule_matchtype(const dns_ssurule_t *rule) {
547	REQUIRE(VALID_SSURULE(rule));
548	return (rule->matchtype);
549}
550
551dns_name_t *
552dns_ssurule_name(const dns_ssurule_t *rule) {
553	REQUIRE(VALID_SSURULE(rule));
554	return (rule->name);
555}
556
557unsigned int
558dns_ssurule_types(const dns_ssurule_t *rule, dns_rdatatype_t **types) {
559	REQUIRE(VALID_SSURULE(rule));
560	REQUIRE(types != NULL && *types != NULL);
561	*types = rule->types;
562	return (rule->ntypes);
563}
564
565isc_result_t
566dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule) {
567	REQUIRE(VALID_SSUTABLE(table));
568	REQUIRE(rule != NULL && *rule == NULL);
569	*rule = ISC_LIST_HEAD(table->rules);
570	return (*rule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE);
571}
572
573isc_result_t
574dns_ssutable_nextrule(dns_ssurule_t *rule, dns_ssurule_t **nextrule) {
575	REQUIRE(VALID_SSURULE(rule));
576	REQUIRE(nextrule != NULL && *nextrule == NULL);
577	*nextrule = ISC_LIST_NEXT(rule, link);
578	return (*nextrule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE);
579}
580
581/*
582 * Create a specialised SSU table that points at an external DLZ database
583 */
584isc_result_t
585dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep,
586		       dns_dlzdb_t *dlzdatabase) {
587	isc_result_t result;
588	dns_ssurule_t *rule;
589	dns_ssutable_t *table = NULL;
590
591	REQUIRE(tablep != NULL && *tablep == NULL);
592
593	result = dns_ssutable_create(mctx, &table);
594	if (result != ISC_R_SUCCESS) {
595		return (result);
596	}
597
598	table->dlzdatabase = dlzdatabase;
599
600	rule = isc_mem_get(table->mctx, sizeof(dns_ssurule_t));
601
602	rule->identity = NULL;
603	rule->name = NULL;
604	rule->types = NULL;
605	rule->grant = true;
606	rule->matchtype = dns_ssumatchtype_dlz;
607	rule->ntypes = 0;
608	rule->types = NULL;
609	rule->magic = SSURULEMAGIC;
610
611	ISC_LIST_INITANDAPPEND(table->rules, rule, link);
612	*tablep = table;
613	return (ISC_R_SUCCESS);
614}
615
616isc_result_t
617dns_ssu_mtypefromstring(const char *str, dns_ssumatchtype_t *mtype) {
618	REQUIRE(str != NULL);
619	REQUIRE(mtype != NULL);
620
621	if (strcasecmp(str, "name") == 0) {
622		*mtype = dns_ssumatchtype_name;
623	} else if (strcasecmp(str, "subdomain") == 0) {
624		*mtype = dns_ssumatchtype_subdomain;
625	} else if (strcasecmp(str, "wildcard") == 0) {
626		*mtype = dns_ssumatchtype_wildcard;
627	} else if (strcasecmp(str, "self") == 0) {
628		*mtype = dns_ssumatchtype_self;
629	} else if (strcasecmp(str, "selfsub") == 0) {
630		*mtype = dns_ssumatchtype_selfsub;
631	} else if (strcasecmp(str, "selfwild") == 0) {
632		*mtype = dns_ssumatchtype_selfwild;
633	} else if (strcasecmp(str, "ms-self") == 0) {
634		*mtype = dns_ssumatchtype_selfms;
635	} else if (strcasecmp(str, "ms-selfsub") == 0) {
636		*mtype = dns_ssumatchtype_selfsubms;
637	} else if (strcasecmp(str, "krb5-self") == 0) {
638		*mtype = dns_ssumatchtype_selfkrb5;
639	} else if (strcasecmp(str, "krb5-selfsub") == 0) {
640		*mtype = dns_ssumatchtype_selfsubkrb5;
641	} else if (strcasecmp(str, "ms-subdomain") == 0) {
642		*mtype = dns_ssumatchtype_subdomainms;
643	} else if (strcasecmp(str, "krb5-subdomain") == 0) {
644		*mtype = dns_ssumatchtype_subdomainkrb5;
645	} else if (strcasecmp(str, "tcp-self") == 0) {
646		*mtype = dns_ssumatchtype_tcpself;
647	} else if (strcasecmp(str, "6to4-self") == 0) {
648		*mtype = dns_ssumatchtype_6to4self;
649	} else if (strcasecmp(str, "zonesub") == 0) {
650		*mtype = dns_ssumatchtype_subdomain;
651	} else if (strcasecmp(str, "external") == 0) {
652		*mtype = dns_ssumatchtype_external;
653	} else {
654		return (ISC_R_NOTFOUND);
655	}
656	return (ISC_R_SUCCESS);
657}
658