1 /* $NetBSD: ssu.c,v 1.6 2014/12/10 04:37:58 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2004-2008, 2010, 2011, 2013, 2014 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 2000, 2001, 2003 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /*! \file */ 21 /* 22 * Id: ssu.c,v 1.38 2011/01/06 23:47:00 tbox Exp 23 * Principal Author: Brian Wellington 24 */ 25 26 #include <config.h> 27 28 #include <isc/magic.h> 29 #include <isc/mem.h> 30 #include <isc/netaddr.h> 31 #include <isc/result.h> 32 #include <isc/string.h> 33 #include <isc/util.h> 34 35 #include <dns/dlz.h> 36 #include <dns/fixedname.h> 37 #include <dns/name.h> 38 #include <dns/ssu.h> 39 40 #include <dst/gssapi.h> 41 #include <dst/dst.h> 42 43 #define SSUTABLEMAGIC ISC_MAGIC('S', 'S', 'U', 'T') 44 #define VALID_SSUTABLE(table) ISC_MAGIC_VALID(table, SSUTABLEMAGIC) 45 46 #define SSURULEMAGIC ISC_MAGIC('S', 'S', 'U', 'R') 47 #define VALID_SSURULE(table) ISC_MAGIC_VALID(table, SSURULEMAGIC) 48 49 struct dns_ssurule { 50 unsigned int magic; 51 isc_boolean_t grant; /*%< is this a grant or a deny? */ 52 unsigned int matchtype; /*%< which type of pattern match? */ 53 dns_name_t *identity; /*%< the identity to match */ 54 dns_name_t *name; /*%< the name being updated */ 55 unsigned int ntypes; /*%< number of data types covered */ 56 dns_rdatatype_t *types; /*%< the data types. Can include ANY, */ 57 /*%< defaults to all but SIG,SOA,NS if NULL */ 58 ISC_LINK(dns_ssurule_t) link; 59 }; 60 61 struct dns_ssutable { 62 unsigned int magic; 63 isc_mem_t *mctx; 64 unsigned int references; 65 isc_mutex_t lock; 66 dns_dlzdb_t *dlzdatabase; 67 ISC_LIST(dns_ssurule_t) rules; 68 }; 69 70 isc_result_t 71 dns_ssutable_create(isc_mem_t *mctx, dns_ssutable_t **tablep) { 72 isc_result_t result; 73 dns_ssutable_t *table; 74 75 REQUIRE(tablep != NULL && *tablep == NULL); 76 REQUIRE(mctx != NULL); 77 78 table = isc_mem_get(mctx, sizeof(dns_ssutable_t)); 79 if (table == NULL) 80 return (ISC_R_NOMEMORY); 81 result = isc_mutex_init(&table->lock); 82 if (result != ISC_R_SUCCESS) { 83 isc_mem_put(mctx, table, sizeof(dns_ssutable_t)); 84 return (result); 85 } 86 table->references = 1; 87 table->mctx = NULL; 88 isc_mem_attach(mctx, &table->mctx); 89 ISC_LIST_INIT(table->rules); 90 table->magic = SSUTABLEMAGIC; 91 *tablep = table; 92 return (ISC_R_SUCCESS); 93 } 94 95 static inline void 96 destroy(dns_ssutable_t *table) { 97 isc_mem_t *mctx; 98 99 REQUIRE(VALID_SSUTABLE(table)); 100 101 mctx = table->mctx; 102 while (!ISC_LIST_EMPTY(table->rules)) { 103 dns_ssurule_t *rule = ISC_LIST_HEAD(table->rules); 104 if (rule->identity != NULL) { 105 dns_name_free(rule->identity, mctx); 106 isc_mem_put(mctx, rule->identity, sizeof(dns_name_t)); 107 } 108 if (rule->name != NULL) { 109 dns_name_free(rule->name, mctx); 110 isc_mem_put(mctx, rule->name, sizeof(dns_name_t)); 111 } 112 if (rule->types != NULL) 113 isc_mem_put(mctx, rule->types, 114 rule->ntypes * sizeof(dns_rdatatype_t)); 115 ISC_LIST_UNLINK(table->rules, rule, link); 116 rule->magic = 0; 117 isc_mem_put(mctx, rule, sizeof(dns_ssurule_t)); 118 } 119 DESTROYLOCK(&table->lock); 120 table->magic = 0; 121 isc_mem_putanddetach(&table->mctx, table, sizeof(dns_ssutable_t)); 122 } 123 124 void 125 dns_ssutable_attach(dns_ssutable_t *source, dns_ssutable_t **targetp) { 126 REQUIRE(VALID_SSUTABLE(source)); 127 REQUIRE(targetp != NULL && *targetp == NULL); 128 129 LOCK(&source->lock); 130 131 INSIST(source->references > 0); 132 source->references++; 133 INSIST(source->references != 0); 134 135 UNLOCK(&source->lock); 136 137 *targetp = source; 138 } 139 140 void 141 dns_ssutable_detach(dns_ssutable_t **tablep) { 142 dns_ssutable_t *table; 143 isc_boolean_t done = ISC_FALSE; 144 145 REQUIRE(tablep != NULL); 146 table = *tablep; 147 REQUIRE(VALID_SSUTABLE(table)); 148 149 LOCK(&table->lock); 150 151 INSIST(table->references > 0); 152 if (--table->references == 0) 153 done = ISC_TRUE; 154 UNLOCK(&table->lock); 155 156 *tablep = NULL; 157 158 if (done) 159 destroy(table); 160 } 161 162 isc_result_t 163 dns_ssutable_addrule(dns_ssutable_t *table, isc_boolean_t grant, 164 dns_name_t *identity, unsigned int matchtype, 165 dns_name_t *name, unsigned int ntypes, 166 dns_rdatatype_t *types) 167 { 168 dns_ssurule_t *rule; 169 isc_mem_t *mctx; 170 isc_result_t result; 171 172 REQUIRE(VALID_SSUTABLE(table)); 173 REQUIRE(dns_name_isabsolute(identity)); 174 REQUIRE(dns_name_isabsolute(name)); 175 REQUIRE(matchtype <= DNS_SSUMATCHTYPE_MAX); 176 if (matchtype == DNS_SSUMATCHTYPE_WILDCARD) 177 REQUIRE(dns_name_iswildcard(name)); 178 if (ntypes > 0) 179 REQUIRE(types != NULL); 180 181 mctx = table->mctx; 182 rule = isc_mem_get(mctx, sizeof(dns_ssurule_t)); 183 if (rule == NULL) 184 return (ISC_R_NOMEMORY); 185 186 rule->identity = NULL; 187 rule->name = NULL; 188 rule->types = NULL; 189 190 rule->grant = grant; 191 192 rule->identity = isc_mem_get(mctx, sizeof(dns_name_t)); 193 if (rule->identity == NULL) { 194 result = ISC_R_NOMEMORY; 195 goto failure; 196 } 197 dns_name_init(rule->identity, NULL); 198 result = dns_name_dup(identity, mctx, rule->identity); 199 if (result != ISC_R_SUCCESS) 200 goto failure; 201 202 rule->name = isc_mem_get(mctx, sizeof(dns_name_t)); 203 if (rule->name == NULL) { 204 result = ISC_R_NOMEMORY; 205 goto failure; 206 } 207 dns_name_init(rule->name, NULL); 208 result = dns_name_dup(name, mctx, rule->name); 209 if (result != ISC_R_SUCCESS) 210 goto failure; 211 212 rule->matchtype = matchtype; 213 214 rule->ntypes = ntypes; 215 if (ntypes > 0) { 216 rule->types = isc_mem_get(mctx, 217 ntypes * sizeof(dns_rdatatype_t)); 218 if (rule->types == NULL) { 219 result = ISC_R_NOMEMORY; 220 goto failure; 221 } 222 memmove(rule->types, types, ntypes * sizeof(dns_rdatatype_t)); 223 } else 224 rule->types = NULL; 225 226 rule->magic = SSURULEMAGIC; 227 ISC_LIST_INITANDAPPEND(table->rules, rule, link); 228 229 return (ISC_R_SUCCESS); 230 231 failure: 232 if (rule->identity != NULL) { 233 if (dns_name_dynamic(rule->identity)) 234 dns_name_free(rule->identity, mctx); 235 isc_mem_put(mctx, rule->identity, sizeof(dns_name_t)); 236 } 237 if (rule->name != NULL) { 238 if (dns_name_dynamic(rule->name)) 239 dns_name_free(rule->name, mctx); 240 isc_mem_put(mctx, rule->name, sizeof(dns_name_t)); 241 } 242 if (rule->types != NULL) 243 isc_mem_put(mctx, rule->types, 244 ntypes * sizeof(dns_rdatatype_t)); 245 isc_mem_put(mctx, rule, sizeof(dns_ssurule_t)); 246 247 return (result); 248 } 249 250 static inline isc_boolean_t 251 isusertype(dns_rdatatype_t type) { 252 return (ISC_TF(type != dns_rdatatype_ns && 253 type != dns_rdatatype_soa && 254 type != dns_rdatatype_rrsig)); 255 } 256 257 static void 258 reverse_from_address(dns_name_t *tcpself, isc_netaddr_t *tcpaddr) { 259 char buf[16 * 4 + sizeof("IP6.ARPA.")]; 260 isc_result_t result; 261 unsigned char *ap; 262 isc_buffer_t b; 263 unsigned long l; 264 265 switch (tcpaddr->family) { 266 case AF_INET: 267 l = ntohl(tcpaddr->type.in.s_addr); 268 result = isc_string_printf(buf, sizeof(buf), 269 "%lu.%lu.%lu.%lu.IN-ADDR.ARPA.", 270 (l >> 0) & 0xff, (l >> 8) & 0xff, 271 (l >> 16) & 0xff, (l >> 24) & 0xff); 272 RUNTIME_CHECK(result == ISC_R_SUCCESS); 273 break; 274 case AF_INET6: 275 ap = tcpaddr->type.in6.s6_addr; 276 result = isc_string_printf(buf, sizeof(buf), 277 "%x.%x.%x.%x.%x.%x.%x.%x." 278 "%x.%x.%x.%x.%x.%x.%x.%x." 279 "%x.%x.%x.%x.%x.%x.%x.%x." 280 "%x.%x.%x.%x.%x.%x.%x.%x." 281 "IP6.ARPA.", 282 ap[15] & 0x0f, (ap[15] >> 4) & 0x0f, 283 ap[14] & 0x0f, (ap[14] >> 4) & 0x0f, 284 ap[13] & 0x0f, (ap[13] >> 4) & 0x0f, 285 ap[12] & 0x0f, (ap[12] >> 4) & 0x0f, 286 ap[11] & 0x0f, (ap[11] >> 4) & 0x0f, 287 ap[10] & 0x0f, (ap[10] >> 4) & 0x0f, 288 ap[9] & 0x0f, (ap[9] >> 4) & 0x0f, 289 ap[8] & 0x0f, (ap[8] >> 4) & 0x0f, 290 ap[7] & 0x0f, (ap[7] >> 4) & 0x0f, 291 ap[6] & 0x0f, (ap[6] >> 4) & 0x0f, 292 ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, 293 ap[4] & 0x0f, (ap[4] >> 4) & 0x0f, 294 ap[3] & 0x0f, (ap[3] >> 4) & 0x0f, 295 ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, 296 ap[1] & 0x0f, (ap[1] >> 4) & 0x0f, 297 ap[0] & 0x0f, (ap[0] >> 4) & 0x0f); 298 RUNTIME_CHECK(result == ISC_R_SUCCESS); 299 break; 300 default: 301 INSIST(0); 302 } 303 isc_buffer_init(&b, buf, strlen(buf)); 304 isc_buffer_add(&b, strlen(buf)); 305 result = dns_name_fromtext(tcpself, &b, dns_rootname, 0, NULL); 306 RUNTIME_CHECK(result == ISC_R_SUCCESS); 307 } 308 309 static void 310 stf_from_address(dns_name_t *stfself, isc_netaddr_t *tcpaddr) { 311 char buf[sizeof("X.X.X.X.Y.Y.Y.Y.2.0.0.2.IP6.ARPA.")]; 312 isc_result_t result; 313 unsigned char *ap; 314 isc_buffer_t b; 315 unsigned long l; 316 317 switch(tcpaddr->family) { 318 case AF_INET: 319 l = ntohl(tcpaddr->type.in.s_addr); 320 result = isc_string_printf(buf, sizeof(buf), 321 "%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx" 322 "2.0.0.2.IP6.ARPA.", 323 l & 0xf, (l >> 4) & 0xf, 324 (l >> 8) & 0xf, (l >> 12) & 0xf, 325 (l >> 16) & 0xf, (l >> 20) & 0xf, 326 (l >> 24) & 0xf, (l >> 28) & 0xf); 327 RUNTIME_CHECK(result == ISC_R_SUCCESS); 328 break; 329 case AF_INET6: 330 ap = tcpaddr->type.in6.s6_addr; 331 result = isc_string_printf(buf, sizeof(buf), 332 "%x.%x.%x.%x.%x.%x.%x.%x." 333 "%x.%x.%x.%x.IP6.ARPA.", 334 ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, 335 ap[4] & 0x0f, (ap[4] >> 4) & 0x0f, 336 ap[3] & 0x0f, (ap[3] >> 4) & 0x0f, 337 ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, 338 ap[1] & 0x0f, (ap[1] >> 4) & 0x0f, 339 ap[0] & 0x0f, (ap[0] >> 4) & 0x0f); 340 RUNTIME_CHECK(result == ISC_R_SUCCESS); 341 break; 342 default: 343 INSIST(0); 344 } 345 isc_buffer_init(&b, buf, strlen(buf)); 346 isc_buffer_add(&b, strlen(buf)); 347 result = dns_name_fromtext(stfself, &b, dns_rootname, 0, NULL); 348 RUNTIME_CHECK(result == ISC_R_SUCCESS); 349 } 350 351 isc_boolean_t 352 dns_ssutable_checkrules(dns_ssutable_t *table, dns_name_t *signer, 353 dns_name_t *name, isc_netaddr_t *tcpaddr, 354 dns_rdatatype_t type, 355 const dst_key_t *key) 356 { 357 dns_ssurule_t *rule; 358 unsigned int i; 359 dns_fixedname_t fixed; 360 dns_name_t *wildcard; 361 dns_name_t *tcpself; 362 dns_name_t *stfself; 363 isc_result_t result; 364 365 REQUIRE(VALID_SSUTABLE(table)); 366 REQUIRE(signer == NULL || dns_name_isabsolute(signer)); 367 REQUIRE(dns_name_isabsolute(name)); 368 369 if (signer == NULL && tcpaddr == NULL) 370 return (ISC_FALSE); 371 372 for (rule = ISC_LIST_HEAD(table->rules); 373 rule != NULL; 374 rule = ISC_LIST_NEXT(rule, link)) 375 { 376 switch (rule->matchtype) { 377 case DNS_SSUMATCHTYPE_NAME: 378 case DNS_SSUMATCHTYPE_SUBDOMAIN: 379 case DNS_SSUMATCHTYPE_WILDCARD: 380 case DNS_SSUMATCHTYPE_SELF: 381 case DNS_SSUMATCHTYPE_SELFSUB: 382 case DNS_SSUMATCHTYPE_SELFWILD: 383 if (signer == NULL) 384 continue; 385 if (dns_name_iswildcard(rule->identity)) { 386 if (!dns_name_matcheswildcard(signer, 387 rule->identity)) 388 continue; 389 } else { 390 if (!dns_name_equal(signer, rule->identity)) 391 continue; 392 } 393 break; 394 case DNS_SSUMATCHTYPE_SELFKRB5: 395 case DNS_SSUMATCHTYPE_SELFMS: 396 case DNS_SSUMATCHTYPE_SUBDOMAINKRB5: 397 case DNS_SSUMATCHTYPE_SUBDOMAINMS: 398 if (signer == NULL) 399 continue; 400 break; 401 case DNS_SSUMATCHTYPE_TCPSELF: 402 case DNS_SSUMATCHTYPE_6TO4SELF: 403 if (tcpaddr == NULL) 404 continue; 405 break; 406 } 407 408 switch (rule->matchtype) { 409 case DNS_SSUMATCHTYPE_NAME: 410 if (!dns_name_equal(name, rule->name)) 411 continue; 412 break; 413 case DNS_SSUMATCHTYPE_SUBDOMAIN: 414 if (!dns_name_issubdomain(name, rule->name)) 415 continue; 416 break; 417 case DNS_SSUMATCHTYPE_WILDCARD: 418 if (!dns_name_matcheswildcard(name, rule->name)) 419 continue; 420 break; 421 case DNS_SSUMATCHTYPE_SELF: 422 if (!dns_name_equal(signer, name)) 423 continue; 424 break; 425 case DNS_SSUMATCHTYPE_SELFSUB: 426 if (!dns_name_issubdomain(name, signer)) 427 continue; 428 break; 429 case DNS_SSUMATCHTYPE_SELFWILD: 430 dns_fixedname_init(&fixed); 431 wildcard = dns_fixedname_name(&fixed); 432 result = dns_name_concatenate(dns_wildcardname, signer, 433 wildcard, NULL); 434 if (result != ISC_R_SUCCESS) 435 continue; 436 if (!dns_name_matcheswildcard(name, wildcard)) 437 continue; 438 break; 439 case DNS_SSUMATCHTYPE_SELFKRB5: 440 if (!dst_gssapi_identitymatchesrealmkrb5(signer, name, 441 rule->identity)) 442 continue; 443 break; 444 case DNS_SSUMATCHTYPE_SELFMS: 445 if (!dst_gssapi_identitymatchesrealmms(signer, name, 446 rule->identity)) 447 continue; 448 break; 449 case DNS_SSUMATCHTYPE_SUBDOMAINKRB5: 450 if (!dns_name_issubdomain(name, rule->name)) 451 continue; 452 if (!dst_gssapi_identitymatchesrealmkrb5(signer, NULL, 453 rule->identity)) 454 continue; 455 break; 456 case DNS_SSUMATCHTYPE_SUBDOMAINMS: 457 if (!dns_name_issubdomain(name, rule->name)) 458 continue; 459 if (!dst_gssapi_identitymatchesrealmms(signer, NULL, 460 rule->identity)) 461 continue; 462 break; 463 case DNS_SSUMATCHTYPE_TCPSELF: 464 dns_fixedname_init(&fixed); 465 tcpself = dns_fixedname_name(&fixed); 466 reverse_from_address(tcpself, tcpaddr); 467 if (dns_name_iswildcard(rule->identity)) { 468 if (!dns_name_matcheswildcard(tcpself, 469 rule->identity)) 470 continue; 471 } else { 472 if (!dns_name_equal(tcpself, rule->identity)) 473 continue; 474 } 475 if (!dns_name_equal(tcpself, name)) 476 continue; 477 break; 478 case DNS_SSUMATCHTYPE_6TO4SELF: 479 dns_fixedname_init(&fixed); 480 stfself = dns_fixedname_name(&fixed); 481 stf_from_address(stfself, tcpaddr); 482 if (dns_name_iswildcard(rule->identity)) { 483 if (!dns_name_matcheswildcard(stfself, 484 rule->identity)) 485 continue; 486 } else { 487 if (!dns_name_equal(stfself, rule->identity)) 488 continue; 489 } 490 if (!dns_name_equal(stfself, name)) 491 continue; 492 break; 493 case DNS_SSUMATCHTYPE_EXTERNAL: 494 if (!dns_ssu_external_match(rule->identity, signer, 495 name, tcpaddr, type, key, 496 table->mctx)) 497 continue; 498 break; 499 case DNS_SSUMATCHTYPE_DLZ: 500 if (!dns_dlz_ssumatch(table->dlzdatabase, signer, 501 name, tcpaddr, type, key)) 502 continue; 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 } else { 516 for (i = 0; i < rule->ntypes; i++) { 517 if (rule->types[i] == dns_rdatatype_any || 518 rule->types[i] == type) 519 break; 520 } 521 if (i == rule->ntypes) 522 continue; 523 } 524 return (rule->grant); 525 } 526 527 return (ISC_FALSE); 528 } 529 530 isc_boolean_t 531 dns_ssurule_isgrant(const dns_ssurule_t *rule) { 532 REQUIRE(VALID_SSURULE(rule)); 533 return (rule->grant); 534 } 535 536 dns_name_t * 537 dns_ssurule_identity(const dns_ssurule_t *rule) { 538 REQUIRE(VALID_SSURULE(rule)); 539 return (rule->identity); 540 } 541 542 unsigned int 543 dns_ssurule_matchtype(const dns_ssurule_t *rule) { 544 REQUIRE(VALID_SSURULE(rule)); 545 return (rule->matchtype); 546 } 547 548 dns_name_t * 549 dns_ssurule_name(const dns_ssurule_t *rule) { 550 REQUIRE(VALID_SSURULE(rule)); 551 return (rule->name); 552 } 553 554 unsigned int 555 dns_ssurule_types(const dns_ssurule_t *rule, dns_rdatatype_t **types) { 556 REQUIRE(VALID_SSURULE(rule)); 557 REQUIRE(types != NULL && *types != NULL); 558 *types = rule->types; 559 return (rule->ntypes); 560 } 561 562 isc_result_t 563 dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule) { 564 REQUIRE(VALID_SSUTABLE(table)); 565 REQUIRE(rule != NULL && *rule == NULL); 566 *rule = ISC_LIST_HEAD(table->rules); 567 return (*rule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE); 568 } 569 570 isc_result_t 571 dns_ssutable_nextrule(dns_ssurule_t *rule, dns_ssurule_t **nextrule) { 572 REQUIRE(VALID_SSURULE(rule)); 573 REQUIRE(nextrule != NULL && *nextrule == NULL); 574 *nextrule = ISC_LIST_NEXT(rule, link); 575 return (*nextrule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE); 576 } 577 578 /* 579 * Create a specialised SSU table that points at an external DLZ database 580 */ 581 isc_result_t 582 dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep, 583 dns_dlzdb_t *dlzdatabase) 584 { 585 isc_result_t result; 586 dns_ssurule_t *rule; 587 dns_ssutable_t *table = NULL; 588 589 REQUIRE(tablep != NULL && *tablep == NULL); 590 591 result = dns_ssutable_create(mctx, &table); 592 if (result != ISC_R_SUCCESS) 593 return (result); 594 595 table->dlzdatabase = dlzdatabase; 596 597 rule = isc_mem_get(table->mctx, sizeof(dns_ssurule_t)); 598 if (rule == NULL) { 599 dns_ssutable_detach(&table); 600 return (ISC_R_NOMEMORY); 601 } 602 603 rule->identity = NULL; 604 rule->name = NULL; 605 rule->types = NULL; 606 rule->grant = ISC_TRUE; 607 rule->matchtype = DNS_SSUMATCHTYPE_DLZ; 608 rule->ntypes = 0; 609 rule->types = NULL; 610 rule->magic = SSURULEMAGIC; 611 612 ISC_LIST_INITANDAPPEND(table->rules, rule, link); 613 *tablep = table; 614 return (ISC_R_SUCCESS); 615 } 616