ssu.c revision 1.7
1/* $NetBSD: ssu.c,v 1.7 2023/01/25 21:43:30 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 * */ 48 dns_name_t *identity; /*%< the identity to match */ 49 dns_name_t *name; /*%< the name being updated */ 50 unsigned int ntypes; /*%< number of data types covered */ 51 dns_rdatatype_t *types; /*%< the data types. Can include */ 52 /* ANY. if NULL, defaults to all */ 53 /* types except SIG, SOA, and NS */ 54 ISC_LINK(dns_ssurule_t) link; 55}; 56 57struct dns_ssutable { 58 unsigned int magic; 59 isc_mem_t *mctx; 60 isc_refcount_t references; 61 dns_dlzdb_t *dlzdatabase; 62 ISC_LIST(dns_ssurule_t) rules; 63}; 64 65isc_result_t 66dns_ssutable_create(isc_mem_t *mctx, dns_ssutable_t **tablep) { 67 dns_ssutable_t *table; 68 69 REQUIRE(tablep != NULL && *tablep == NULL); 70 REQUIRE(mctx != NULL); 71 72 table = isc_mem_get(mctx, sizeof(dns_ssutable_t)); 73 isc_refcount_init(&table->references, 1); 74 table->mctx = NULL; 75 isc_mem_attach(mctx, &table->mctx); 76 ISC_LIST_INIT(table->rules); 77 table->magic = SSUTABLEMAGIC; 78 *tablep = table; 79 return (ISC_R_SUCCESS); 80} 81 82static void 83destroy(dns_ssutable_t *table) { 84 isc_mem_t *mctx; 85 86 REQUIRE(VALID_SSUTABLE(table)); 87 88 mctx = table->mctx; 89 while (!ISC_LIST_EMPTY(table->rules)) { 90 dns_ssurule_t *rule = ISC_LIST_HEAD(table->rules); 91 if (rule->identity != NULL) { 92 dns_name_free(rule->identity, mctx); 93 isc_mem_put(mctx, rule->identity, sizeof(dns_name_t)); 94 } 95 if (rule->name != NULL) { 96 dns_name_free(rule->name, mctx); 97 isc_mem_put(mctx, rule->name, sizeof(dns_name_t)); 98 } 99 if (rule->types != NULL) { 100 isc_mem_put(mctx, rule->types, 101 rule->ntypes * sizeof(dns_rdatatype_t)); 102 } 103 ISC_LIST_UNLINK(table->rules, rule, link); 104 rule->magic = 0; 105 isc_mem_put(mctx, rule, sizeof(dns_ssurule_t)); 106 } 107 isc_refcount_destroy(&table->references); 108 table->magic = 0; 109 isc_mem_putanddetach(&table->mctx, table, sizeof(dns_ssutable_t)); 110} 111 112void 113dns_ssutable_attach(dns_ssutable_t *source, dns_ssutable_t **targetp) { 114 REQUIRE(VALID_SSUTABLE(source)); 115 REQUIRE(targetp != NULL && *targetp == NULL); 116 117 isc_refcount_increment(&source->references); 118 119 *targetp = source; 120} 121 122void 123dns_ssutable_detach(dns_ssutable_t **tablep) { 124 dns_ssutable_t *table; 125 126 REQUIRE(tablep != NULL); 127 table = *tablep; 128 *tablep = NULL; 129 REQUIRE(VALID_SSUTABLE(table)); 130 131 if (isc_refcount_decrement(&table->references) == 1) { 132 destroy(table); 133 } 134} 135 136isc_result_t 137dns_ssutable_addrule(dns_ssutable_t *table, bool grant, 138 const dns_name_t *identity, dns_ssumatchtype_t matchtype, 139 const dns_name_t *name, unsigned int ntypes, 140 dns_rdatatype_t *types) { 141 dns_ssurule_t *rule; 142 isc_mem_t *mctx; 143 144 REQUIRE(VALID_SSUTABLE(table)); 145 REQUIRE(dns_name_isabsolute(identity)); 146 REQUIRE(dns_name_isabsolute(name)); 147 REQUIRE(matchtype <= dns_ssumatchtype_max); 148 if (matchtype == dns_ssumatchtype_wildcard) { 149 REQUIRE(dns_name_iswildcard(name)); 150 } 151 if (ntypes > 0) { 152 REQUIRE(types != NULL); 153 } 154 155 mctx = table->mctx; 156 rule = isc_mem_get(mctx, sizeof(dns_ssurule_t)); 157 158 rule->identity = NULL; 159 rule->name = NULL; 160 rule->types = NULL; 161 162 rule->grant = grant; 163 164 rule->identity = isc_mem_get(mctx, sizeof(dns_name_t)); 165 dns_name_init(rule->identity, NULL); 166 dns_name_dup(identity, mctx, rule->identity); 167 168 rule->name = isc_mem_get(mctx, sizeof(dns_name_t)); 169 dns_name_init(rule->name, NULL); 170 dns_name_dup(name, mctx, rule->name); 171 172 rule->matchtype = matchtype; 173 174 rule->ntypes = ntypes; 175 if (ntypes > 0) { 176 rule->types = isc_mem_get(mctx, 177 ntypes * sizeof(dns_rdatatype_t)); 178 memmove(rule->types, types, ntypes * sizeof(dns_rdatatype_t)); 179 } else { 180 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 { 326 continue; 327 } 328 } else { 329 if (!dns_name_equal(signer, rule->identity)) { 330 continue; 331 } 332 } 333 break; 334 case dns_ssumatchtype_selfkrb5: 335 case dns_ssumatchtype_selfms: 336 case dns_ssumatchtype_selfsubkrb5: 337 case dns_ssumatchtype_selfsubms: 338 case dns_ssumatchtype_subdomainkrb5: 339 case dns_ssumatchtype_subdomainms: 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 dns_acl_match(addr, NULL, env->localhost, NULL, &match, 374 NULL); 375 if (match == 0) { 376 if (signer != NULL) { 377 isc_log_write(dns_lctx, 378 DNS_LOGCATEGORY_GENERAL, 379 DNS_LOGMODULE_SSU, 380 ISC_LOG_WARNING, 381 "update-policy local: " 382 "match on session " 383 "key not from " 384 "localhost"); 385 } 386 continue; 387 } 388 break; 389 case dns_ssumatchtype_wildcard: 390 if (!dns_name_matcheswildcard(name, rule->name)) { 391 continue; 392 } 393 break; 394 case dns_ssumatchtype_self: 395 if (!dns_name_equal(signer, name)) { 396 continue; 397 } 398 break; 399 case dns_ssumatchtype_selfsub: 400 if (!dns_name_issubdomain(name, signer)) { 401 continue; 402 } 403 break; 404 case dns_ssumatchtype_selfwild: 405 wildcard = dns_fixedname_initname(&fixed); 406 result = dns_name_concatenate(dns_wildcardname, signer, 407 wildcard, NULL); 408 if (result != ISC_R_SUCCESS) { 409 continue; 410 } 411 if (!dns_name_matcheswildcard(name, wildcard)) { 412 continue; 413 } 414 break; 415 case dns_ssumatchtype_selfkrb5: 416 if (dst_gssapi_identitymatchesrealmkrb5( 417 signer, name, rule->identity, false)) 418 { 419 break; 420 } 421 continue; 422 case dns_ssumatchtype_selfms: 423 if (dst_gssapi_identitymatchesrealmms( 424 signer, name, rule->identity, false)) 425 { 426 break; 427 } 428 continue; 429 case dns_ssumatchtype_selfsubkrb5: 430 if (dst_gssapi_identitymatchesrealmkrb5( 431 signer, name, rule->identity, true)) 432 { 433 break; 434 } 435 continue; 436 case dns_ssumatchtype_selfsubms: 437 if (dst_gssapi_identitymatchesrealmms( 438 signer, name, rule->identity, true)) 439 { 440 break; 441 } 442 continue; 443 case dns_ssumatchtype_subdomainkrb5: 444 if (!dns_name_issubdomain(name, rule->name)) { 445 continue; 446 } 447 if (dst_gssapi_identitymatchesrealmkrb5( 448 signer, NULL, rule->identity, false)) 449 { 450 break; 451 } 452 continue; 453 case dns_ssumatchtype_subdomainms: 454 if (!dns_name_issubdomain(name, rule->name)) { 455 continue; 456 } 457 if (dst_gssapi_identitymatchesrealmms( 458 signer, NULL, rule->identity, false)) 459 { 460 break; 461 } 462 continue; 463 case dns_ssumatchtype_tcpself: 464 tcpself = dns_fixedname_initname(&fixed); 465 reverse_from_address(tcpself, addr); 466 if (dns_name_iswildcard(rule->identity)) { 467 if (!dns_name_matcheswildcard(tcpself, 468 rule->identity)) 469 { 470 continue; 471 } 472 } else { 473 if (!dns_name_equal(tcpself, rule->identity)) { 474 continue; 475 } 476 } 477 if (!dns_name_equal(tcpself, name)) { 478 continue; 479 } 480 break; 481 case dns_ssumatchtype_6to4self: 482 stfself = dns_fixedname_initname(&fixed); 483 stf_from_address(stfself, addr); 484 if (dns_name_iswildcard(rule->identity)) { 485 if (!dns_name_matcheswildcard(stfself, 486 rule->identity)) 487 { 488 continue; 489 } 490 } else { 491 if (!dns_name_equal(stfself, rule->identity)) { 492 continue; 493 } 494 } 495 if (!dns_name_equal(stfself, name)) { 496 continue; 497 } 498 break; 499 case dns_ssumatchtype_external: 500 if (!dns_ssu_external_match(rule->identity, signer, 501 name, addr, type, key, 502 table->mctx)) 503 { 504 continue; 505 } 506 break; 507 case dns_ssumatchtype_dlz: 508 if (!dns_dlz_ssumatch(table->dlzdatabase, signer, name, 509 addr, type, key)) 510 { 511 continue; 512 } 513 break; 514 } 515 516 if (rule->ntypes == 0) { 517 /* 518 * If this is a DLZ rule, then the DLZ ssu 519 * checks will have already checked 520 * the type. 521 */ 522 if (rule->matchtype != dns_ssumatchtype_dlz && 523 !isusertype(type)) 524 { 525 continue; 526 } 527 } else { 528 for (i = 0; i < rule->ntypes; i++) { 529 if (rule->types[i] == dns_rdatatype_any || 530 rule->types[i] == type) 531 { 532 break; 533 } 534 } 535 if (i == rule->ntypes) { 536 continue; 537 } 538 } 539 return (rule->grant); 540 } 541 542 return (false); 543} 544 545bool 546dns_ssurule_isgrant(const dns_ssurule_t *rule) { 547 REQUIRE(VALID_SSURULE(rule)); 548 return (rule->grant); 549} 550 551dns_name_t * 552dns_ssurule_identity(const dns_ssurule_t *rule) { 553 REQUIRE(VALID_SSURULE(rule)); 554 return (rule->identity); 555} 556 557unsigned int 558dns_ssurule_matchtype(const dns_ssurule_t *rule) { 559 REQUIRE(VALID_SSURULE(rule)); 560 return (rule->matchtype); 561} 562 563dns_name_t * 564dns_ssurule_name(const dns_ssurule_t *rule) { 565 REQUIRE(VALID_SSURULE(rule)); 566 return (rule->name); 567} 568 569unsigned int 570dns_ssurule_types(const dns_ssurule_t *rule, dns_rdatatype_t **types) { 571 REQUIRE(VALID_SSURULE(rule)); 572 REQUIRE(types != NULL && *types != NULL); 573 *types = rule->types; 574 return (rule->ntypes); 575} 576 577isc_result_t 578dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule) { 579 REQUIRE(VALID_SSUTABLE(table)); 580 REQUIRE(rule != NULL && *rule == NULL); 581 *rule = ISC_LIST_HEAD(table->rules); 582 return (*rule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE); 583} 584 585isc_result_t 586dns_ssutable_nextrule(dns_ssurule_t *rule, dns_ssurule_t **nextrule) { 587 REQUIRE(VALID_SSURULE(rule)); 588 REQUIRE(nextrule != NULL && *nextrule == NULL); 589 *nextrule = ISC_LIST_NEXT(rule, link); 590 return (*nextrule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE); 591} 592 593/* 594 * Create a specialised SSU table that points at an external DLZ database 595 */ 596isc_result_t 597dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep, 598 dns_dlzdb_t *dlzdatabase) { 599 isc_result_t result; 600 dns_ssurule_t *rule; 601 dns_ssutable_t *table = NULL; 602 603 REQUIRE(tablep != NULL && *tablep == NULL); 604 605 result = dns_ssutable_create(mctx, &table); 606 if (result != ISC_R_SUCCESS) { 607 return (result); 608 } 609 610 table->dlzdatabase = dlzdatabase; 611 612 rule = isc_mem_get(table->mctx, sizeof(dns_ssurule_t)); 613 614 rule->identity = NULL; 615 rule->name = NULL; 616 rule->types = NULL; 617 rule->grant = true; 618 rule->matchtype = dns_ssumatchtype_dlz; 619 rule->ntypes = 0; 620 rule->types = NULL; 621 rule->magic = SSURULEMAGIC; 622 623 ISC_LIST_INITANDAPPEND(table->rules, rule, link); 624 *tablep = table; 625 return (ISC_R_SUCCESS); 626} 627 628isc_result_t 629dns_ssu_mtypefromstring(const char *str, dns_ssumatchtype_t *mtype) { 630 REQUIRE(str != NULL); 631 REQUIRE(mtype != NULL); 632 633 if (strcasecmp(str, "name") == 0) { 634 *mtype = dns_ssumatchtype_name; 635 } else if (strcasecmp(str, "subdomain") == 0) { 636 *mtype = dns_ssumatchtype_subdomain; 637 } else if (strcasecmp(str, "wildcard") == 0) { 638 *mtype = dns_ssumatchtype_wildcard; 639 } else if (strcasecmp(str, "self") == 0) { 640 *mtype = dns_ssumatchtype_self; 641 } else if (strcasecmp(str, "selfsub") == 0) { 642 *mtype = dns_ssumatchtype_selfsub; 643 } else if (strcasecmp(str, "selfwild") == 0) { 644 *mtype = dns_ssumatchtype_selfwild; 645 } else if (strcasecmp(str, "ms-self") == 0) { 646 *mtype = dns_ssumatchtype_selfms; 647 } else if (strcasecmp(str, "ms-selfsub") == 0) { 648 *mtype = dns_ssumatchtype_selfsubms; 649 } else if (strcasecmp(str, "krb5-self") == 0) { 650 *mtype = dns_ssumatchtype_selfkrb5; 651 } else if (strcasecmp(str, "krb5-selfsub") == 0) { 652 *mtype = dns_ssumatchtype_selfsubkrb5; 653 } else if (strcasecmp(str, "ms-subdomain") == 0) { 654 *mtype = dns_ssumatchtype_subdomainms; 655 } else if (strcasecmp(str, "krb5-subdomain") == 0) { 656 *mtype = dns_ssumatchtype_subdomainkrb5; 657 } else if (strcasecmp(str, "tcp-self") == 0) { 658 *mtype = dns_ssumatchtype_tcpself; 659 } else if (strcasecmp(str, "6to4-self") == 0) { 660 *mtype = dns_ssumatchtype_6to4self; 661 } else if (strcasecmp(str, "zonesub") == 0) { 662 *mtype = dns_ssumatchtype_subdomain; 663 } else if (strcasecmp(str, "external") == 0) { 664 *mtype = dns_ssumatchtype_external; 665 } else { 666 return (ISC_R_NOTFOUND); 667 } 668 return (ISC_R_SUCCESS); 669} 670