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