ssu.c revision 1.5
1/* $NetBSD: ssu.c,v 1.5 2021/02/19 16:42:16 christos Exp $ */ 2 3/* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 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 16#include <stdbool.h> 17 18#include <isc/magic.h> 19#include <isc/mem.h> 20#include <isc/netaddr.h> 21#include <isc/print.h> 22#include <isc/refcount.h> 23#include <isc/result.h> 24#include <isc/string.h> 25#include <isc/util.h> 26 27#include <dns/dlz.h> 28#include <dns/fixedname.h> 29#include <dns/name.h> 30#include <dns/ssu.h> 31 32#include <dst/dst.h> 33#include <dst/gssapi.h> 34 35#define SSUTABLEMAGIC ISC_MAGIC('S', 'S', 'U', 'T') 36#define VALID_SSUTABLE(table) ISC_MAGIC_VALID(table, SSUTABLEMAGIC) 37 38#define SSURULEMAGIC ISC_MAGIC('S', 'S', 'U', 'R') 39#define VALID_SSURULE(table) ISC_MAGIC_VALID(table, SSURULEMAGIC) 40 41struct dns_ssurule { 42 unsigned int magic; 43 bool grant; /*%< is this a grant or a deny? */ 44 dns_ssumatchtype_t matchtype; /*%< which type of pattern match? 45 * */ 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_rdatatype_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 55struct 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 63isc_result_t 64dns_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(dns_ssutable_t)); 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 return (ISC_R_SUCCESS); 78} 79 80static inline 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, sizeof(dns_name_t)); 92 } 93 if (rule->name != NULL) { 94 dns_name_free(rule->name, mctx); 95 isc_mem_put(mctx, rule->name, sizeof(dns_name_t)); 96 } 97 if (rule->types != NULL) { 98 isc_mem_put(mctx, rule->types, 99 rule->ntypes * sizeof(dns_rdatatype_t)); 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 110void 111dns_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 120void 121dns_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 134isc_result_t 135dns_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_rdatatype_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(dns_ssurule_t)); 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(dns_name_t)); 163 dns_name_init(rule->identity, NULL); 164 dns_name_dup(identity, mctx, rule->identity); 165 166 rule->name = isc_mem_get(mctx, sizeof(dns_name_t)); 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_get(mctx, 175 ntypes * sizeof(dns_rdatatype_t)); 176 memmove(rule->types, types, ntypes * sizeof(dns_rdatatype_t)); 177 } else { 178 rule->types = NULL; 179 } 180 181 rule->magic = SSURULEMAGIC; 182 ISC_LIST_INITANDAPPEND(table->rules, rule, link); 183 184 return (ISC_R_SUCCESS); 185} 186 187static inline bool 188isusertype(dns_rdatatype_t type) { 189 return (type != dns_rdatatype_ns && type != dns_rdatatype_soa && 190 type != dns_rdatatype_rrsig); 191} 192 193static void 194reverse_from_address(dns_name_t *tcpself, const isc_netaddr_t *tcpaddr) { 195 char buf[16 * 4 + sizeof("IP6.ARPA.")]; 196 isc_result_t result; 197 const unsigned char *ap; 198 isc_buffer_t b; 199 unsigned long l; 200 201 switch (tcpaddr->family) { 202 case AF_INET: 203 l = ntohl(tcpaddr->type.in.s_addr); 204 result = snprintf(buf, sizeof(buf), 205 "%lu.%lu.%lu.%lu.IN-ADDR.ARPA.", 206 (l >> 0) & 0xff, (l >> 8) & 0xff, 207 (l >> 16) & 0xff, (l >> 24) & 0xff); 208 RUNTIME_CHECK(result < sizeof(buf)); 209 break; 210 case AF_INET6: 211 ap = tcpaddr->type.in6.s6_addr; 212 result = snprintf( 213 buf, sizeof(buf), 214 "%x.%x.%x.%x.%x.%x.%x.%x." 215 "%x.%x.%x.%x.%x.%x.%x.%x." 216 "%x.%x.%x.%x.%x.%x.%x.%x." 217 "%x.%x.%x.%x.%x.%x.%x.%x." 218 "IP6.ARPA.", 219 ap[15] & 0x0f, (ap[15] >> 4) & 0x0f, ap[14] & 0x0f, 220 (ap[14] >> 4) & 0x0f, ap[13] & 0x0f, 221 (ap[13] >> 4) & 0x0f, ap[12] & 0x0f, 222 (ap[12] >> 4) & 0x0f, ap[11] & 0x0f, 223 (ap[11] >> 4) & 0x0f, ap[10] & 0x0f, 224 (ap[10] >> 4) & 0x0f, ap[9] & 0x0f, (ap[9] >> 4) & 0x0f, 225 ap[8] & 0x0f, (ap[8] >> 4) & 0x0f, ap[7] & 0x0f, 226 (ap[7] >> 4) & 0x0f, ap[6] & 0x0f, (ap[6] >> 4) & 0x0f, 227 ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, ap[4] & 0x0f, 228 (ap[4] >> 4) & 0x0f, ap[3] & 0x0f, (ap[3] >> 4) & 0x0f, 229 ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, ap[1] & 0x0f, 230 (ap[1] >> 4) & 0x0f, ap[0] & 0x0f, (ap[0] >> 4) & 0x0f); 231 RUNTIME_CHECK(result < sizeof(buf)); 232 break; 233 default: 234 INSIST(0); 235 ISC_UNREACHABLE(); 236 } 237 isc_buffer_init(&b, buf, strlen(buf)); 238 isc_buffer_add(&b, strlen(buf)); 239 result = dns_name_fromtext(tcpself, &b, dns_rootname, 0, NULL); 240 RUNTIME_CHECK(result == ISC_R_SUCCESS); 241} 242 243static void 244stf_from_address(dns_name_t *stfself, const isc_netaddr_t *tcpaddr) { 245 char buf[sizeof("X.X.X.X.Y.Y.Y.Y.2.0.0.2.IP6.ARPA.")]; 246 isc_result_t result; 247 const unsigned char *ap; 248 isc_buffer_t b; 249 unsigned long l; 250 251 switch (tcpaddr->family) { 252 case AF_INET: 253 l = ntohl(tcpaddr->type.in.s_addr); 254 result = snprintf(buf, sizeof(buf), 255 "%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx" 256 "2.0.0.2.IP6.ARPA.", 257 l & 0xf, (l >> 4) & 0xf, (l >> 8) & 0xf, 258 (l >> 12) & 0xf, (l >> 16) & 0xf, 259 (l >> 20) & 0xf, (l >> 24) & 0xf, 260 (l >> 28) & 0xf); 261 RUNTIME_CHECK(result < sizeof(buf)); 262 break; 263 case AF_INET6: 264 ap = tcpaddr->type.in6.s6_addr; 265 result = snprintf( 266 buf, sizeof(buf), 267 "%x.%x.%x.%x.%x.%x.%x.%x." 268 "%x.%x.%x.%x.IP6.ARPA.", 269 ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, ap[4] & 0x0f, 270 (ap[4] >> 4) & 0x0f, ap[3] & 0x0f, (ap[3] >> 4) & 0x0f, 271 ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, ap[1] & 0x0f, 272 (ap[1] >> 4) & 0x0f, ap[0] & 0x0f, (ap[0] >> 4) & 0x0f); 273 RUNTIME_CHECK(result < sizeof(buf)); 274 break; 275 default: 276 INSIST(0); 277 ISC_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