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