1/* 2 * Copyright (C) 2004-2008, 2010, 2011, 2013, 2014 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 = NULL; 86 isc_mem_attach(mctx, &table->mctx); 87 ISC_LIST_INIT(table->rules); 88 table->magic = SSUTABLEMAGIC; 89 *tablep = table; 90 return (ISC_R_SUCCESS); 91} 92 93static inline void 94destroy(dns_ssutable_t *table) { 95 isc_mem_t *mctx; 96 97 REQUIRE(VALID_SSUTABLE(table)); 98 99 mctx = table->mctx; 100 while (!ISC_LIST_EMPTY(table->rules)) { 101 dns_ssurule_t *rule = ISC_LIST_HEAD(table->rules); 102 if (rule->identity != NULL) { 103 dns_name_free(rule->identity, mctx); 104 isc_mem_put(mctx, rule->identity, sizeof(dns_name_t)); 105 } 106 if (rule->name != NULL) { 107 dns_name_free(rule->name, mctx); 108 isc_mem_put(mctx, rule->name, sizeof(dns_name_t)); 109 } 110 if (rule->types != NULL) 111 isc_mem_put(mctx, rule->types, 112 rule->ntypes * sizeof(dns_rdatatype_t)); 113 ISC_LIST_UNLINK(table->rules, rule, link); 114 rule->magic = 0; 115 isc_mem_put(mctx, rule, sizeof(dns_ssurule_t)); 116 } 117 DESTROYLOCK(&table->lock); 118 table->magic = 0; 119 isc_mem_putanddetach(&table->mctx, table, sizeof(dns_ssutable_t)); 120} 121 122void 123dns_ssutable_attach(dns_ssutable_t *source, dns_ssutable_t **targetp) { 124 REQUIRE(VALID_SSUTABLE(source)); 125 REQUIRE(targetp != NULL && *targetp == NULL); 126 127 LOCK(&source->lock); 128 129 INSIST(source->references > 0); 130 source->references++; 131 INSIST(source->references != 0); 132 133 UNLOCK(&source->lock); 134 135 *targetp = source; 136} 137 138void 139dns_ssutable_detach(dns_ssutable_t **tablep) { 140 dns_ssutable_t *table; 141 isc_boolean_t done = ISC_FALSE; 142 143 REQUIRE(tablep != NULL); 144 table = *tablep; 145 REQUIRE(VALID_SSUTABLE(table)); 146 147 LOCK(&table->lock); 148 149 INSIST(table->references > 0); 150 if (--table->references == 0) 151 done = ISC_TRUE; 152 UNLOCK(&table->lock); 153 154 *tablep = NULL; 155 156 if (done) 157 destroy(table); 158} 159 160isc_result_t 161dns_ssutable_addrule(dns_ssutable_t *table, isc_boolean_t grant, 162 dns_name_t *identity, unsigned int matchtype, 163 dns_name_t *name, unsigned int ntypes, 164 dns_rdatatype_t *types) 165{ 166 dns_ssurule_t *rule; 167 isc_mem_t *mctx; 168 isc_result_t result; 169 170 REQUIRE(VALID_SSUTABLE(table)); 171 REQUIRE(dns_name_isabsolute(identity)); 172 REQUIRE(dns_name_isabsolute(name)); 173 REQUIRE(matchtype <= DNS_SSUMATCHTYPE_MAX); 174 if (matchtype == DNS_SSUMATCHTYPE_WILDCARD) 175 REQUIRE(dns_name_iswildcard(name)); 176 if (ntypes > 0) 177 REQUIRE(types != NULL); 178 179 mctx = table->mctx; 180 rule = isc_mem_get(mctx, sizeof(dns_ssurule_t)); 181 if (rule == NULL) 182 return (ISC_R_NOMEMORY); 183 184 rule->identity = NULL; 185 rule->name = NULL; 186 rule->types = NULL; 187 188 rule->grant = grant; 189 190 rule->identity = isc_mem_get(mctx, sizeof(dns_name_t)); 191 if (rule->identity == NULL) { 192 result = ISC_R_NOMEMORY; 193 goto failure; 194 } 195 dns_name_init(rule->identity, NULL); 196 result = dns_name_dup(identity, mctx, rule->identity); 197 if (result != ISC_R_SUCCESS) 198 goto failure; 199 200 rule->name = isc_mem_get(mctx, sizeof(dns_name_t)); 201 if (rule->name == NULL) { 202 result = ISC_R_NOMEMORY; 203 goto failure; 204 } 205 dns_name_init(rule->name, NULL); 206 result = dns_name_dup(name, mctx, rule->name); 207 if (result != ISC_R_SUCCESS) 208 goto failure; 209 210 rule->matchtype = matchtype; 211 212 rule->ntypes = ntypes; 213 if (ntypes > 0) { 214 rule->types = isc_mem_get(mctx, 215 ntypes * sizeof(dns_rdatatype_t)); 216 if (rule->types == NULL) { 217 result = ISC_R_NOMEMORY; 218 goto failure; 219 } 220 memmove(rule->types, types, ntypes * sizeof(dns_rdatatype_t)); 221 } else 222 rule->types = NULL; 223 224 rule->magic = SSURULEMAGIC; 225 ISC_LIST_INITANDAPPEND(table->rules, rule, link); 226 227 return (ISC_R_SUCCESS); 228 229 failure: 230 if (rule->identity != NULL) { 231 if (dns_name_dynamic(rule->identity)) 232 dns_name_free(rule->identity, mctx); 233 isc_mem_put(mctx, rule->identity, sizeof(dns_name_t)); 234 } 235 if (rule->name != NULL) { 236 if (dns_name_dynamic(rule->name)) 237 dns_name_free(rule->name, mctx); 238 isc_mem_put(mctx, rule->name, sizeof(dns_name_t)); 239 } 240 if (rule->types != NULL) 241 isc_mem_put(mctx, rule->types, 242 ntypes * sizeof(dns_rdatatype_t)); 243 isc_mem_put(mctx, rule, sizeof(dns_ssurule_t)); 244 245 return (result); 246} 247 248static inline isc_boolean_t 249isusertype(dns_rdatatype_t type) { 250 return (ISC_TF(type != dns_rdatatype_ns && 251 type != dns_rdatatype_soa && 252 type != dns_rdatatype_rrsig)); 253} 254 255static void 256reverse_from_address(dns_name_t *tcpself, isc_netaddr_t *tcpaddr) { 257 char buf[16 * 4 + sizeof("IP6.ARPA.")]; 258 isc_result_t result; 259 unsigned char *ap; 260 isc_buffer_t b; 261 unsigned long l; 262 263 switch (tcpaddr->family) { 264 case AF_INET: 265 l = ntohl(tcpaddr->type.in.s_addr); 266 result = isc_string_printf(buf, sizeof(buf), 267 "%lu.%lu.%lu.%lu.IN-ADDR.ARPA.", 268 (l >> 0) & 0xff, (l >> 8) & 0xff, 269 (l >> 16) & 0xff, (l >> 24) & 0xff); 270 RUNTIME_CHECK(result == ISC_R_SUCCESS); 271 break; 272 case AF_INET6: 273 ap = tcpaddr->type.in6.s6_addr; 274 result = isc_string_printf(buf, sizeof(buf), 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 "%x.%x.%x.%x.%x.%x.%x.%x." 279 "IP6.ARPA.", 280 ap[15] & 0x0f, (ap[15] >> 4) & 0x0f, 281 ap[14] & 0x0f, (ap[14] >> 4) & 0x0f, 282 ap[13] & 0x0f, (ap[13] >> 4) & 0x0f, 283 ap[12] & 0x0f, (ap[12] >> 4) & 0x0f, 284 ap[11] & 0x0f, (ap[11] >> 4) & 0x0f, 285 ap[10] & 0x0f, (ap[10] >> 4) & 0x0f, 286 ap[9] & 0x0f, (ap[9] >> 4) & 0x0f, 287 ap[8] & 0x0f, (ap[8] >> 4) & 0x0f, 288 ap[7] & 0x0f, (ap[7] >> 4) & 0x0f, 289 ap[6] & 0x0f, (ap[6] >> 4) & 0x0f, 290 ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, 291 ap[4] & 0x0f, (ap[4] >> 4) & 0x0f, 292 ap[3] & 0x0f, (ap[3] >> 4) & 0x0f, 293 ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, 294 ap[1] & 0x0f, (ap[1] >> 4) & 0x0f, 295 ap[0] & 0x0f, (ap[0] >> 4) & 0x0f); 296 RUNTIME_CHECK(result == ISC_R_SUCCESS); 297 break; 298 default: 299 INSIST(0); 300 } 301 isc_buffer_init(&b, buf, strlen(buf)); 302 isc_buffer_add(&b, strlen(buf)); 303 result = dns_name_fromtext(tcpself, &b, dns_rootname, 0, NULL); 304 RUNTIME_CHECK(result == ISC_R_SUCCESS); 305} 306 307static void 308stf_from_address(dns_name_t *stfself, isc_netaddr_t *tcpaddr) { 309 char buf[sizeof("X.X.X.X.Y.Y.Y.Y.2.0.0.2.IP6.ARPA.")]; 310 isc_result_t result; 311 unsigned char *ap; 312 isc_buffer_t b; 313 unsigned long l; 314 315 switch(tcpaddr->family) { 316 case AF_INET: 317 l = ntohl(tcpaddr->type.in.s_addr); 318 result = isc_string_printf(buf, sizeof(buf), 319 "%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx" 320 "2.0.0.2.IP6.ARPA.", 321 l & 0xf, (l >> 4) & 0xf, 322 (l >> 8) & 0xf, (l >> 12) & 0xf, 323 (l >> 16) & 0xf, (l >> 20) & 0xf, 324 (l >> 24) & 0xf, (l >> 28) & 0xf); 325 RUNTIME_CHECK(result == ISC_R_SUCCESS); 326 break; 327 case AF_INET6: 328 ap = tcpaddr->type.in6.s6_addr; 329 result = isc_string_printf(buf, sizeof(buf), 330 "%x.%x.%x.%x.%x.%x.%x.%x." 331 "%x.%x.%x.%x.IP6.ARPA.", 332 ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, 333 ap[4] & 0x0f, (ap[4] >> 4) & 0x0f, 334 ap[3] & 0x0f, (ap[3] >> 4) & 0x0f, 335 ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, 336 ap[1] & 0x0f, (ap[1] >> 4) & 0x0f, 337 ap[0] & 0x0f, (ap[0] >> 4) & 0x0f); 338 RUNTIME_CHECK(result == ISC_R_SUCCESS); 339 break; 340 default: 341 INSIST(0); 342 } 343 isc_buffer_init(&b, buf, strlen(buf)); 344 isc_buffer_add(&b, strlen(buf)); 345 result = dns_name_fromtext(stfself, &b, dns_rootname, 0, NULL); 346 RUNTIME_CHECK(result == ISC_R_SUCCESS); 347} 348 349isc_boolean_t 350dns_ssutable_checkrules(dns_ssutable_t *table, dns_name_t *signer, 351 dns_name_t *name, isc_netaddr_t *tcpaddr, 352 dns_rdatatype_t type, 353 const dst_key_t *key) 354{ 355 dns_ssurule_t *rule; 356 unsigned int i; 357 dns_fixedname_t fixed; 358 dns_name_t *wildcard; 359 dns_name_t *tcpself; 360 dns_name_t *stfself; 361 isc_result_t result; 362 363 REQUIRE(VALID_SSUTABLE(table)); 364 REQUIRE(signer == NULL || dns_name_isabsolute(signer)); 365 REQUIRE(dns_name_isabsolute(name)); 366 367 if (signer == NULL && tcpaddr == NULL) 368 return (ISC_FALSE); 369 370 for (rule = ISC_LIST_HEAD(table->rules); 371 rule != NULL; 372 rule = ISC_LIST_NEXT(rule, link)) 373 { 374 switch (rule->matchtype) { 375 case DNS_SSUMATCHTYPE_NAME: 376 case DNS_SSUMATCHTYPE_SUBDOMAIN: 377 case DNS_SSUMATCHTYPE_WILDCARD: 378 case DNS_SSUMATCHTYPE_SELF: 379 case DNS_SSUMATCHTYPE_SELFSUB: 380 case DNS_SSUMATCHTYPE_SELFWILD: 381 if (signer == NULL) 382 continue; 383 if (dns_name_iswildcard(rule->identity)) { 384 if (!dns_name_matcheswildcard(signer, 385 rule->identity)) 386 continue; 387 } else { 388 if (!dns_name_equal(signer, rule->identity)) 389 continue; 390 } 391 break; 392 case DNS_SSUMATCHTYPE_SELFKRB5: 393 case DNS_SSUMATCHTYPE_SELFMS: 394 case DNS_SSUMATCHTYPE_SUBDOMAINKRB5: 395 case DNS_SSUMATCHTYPE_SUBDOMAINMS: 396 if (signer == NULL) 397 continue; 398 break; 399 case DNS_SSUMATCHTYPE_TCPSELF: 400 case DNS_SSUMATCHTYPE_6TO4SELF: 401 if (tcpaddr == NULL) 402 continue; 403 break; 404 } 405 406 switch (rule->matchtype) { 407 case DNS_SSUMATCHTYPE_NAME: 408 if (!dns_name_equal(name, rule->name)) 409 continue; 410 break; 411 case DNS_SSUMATCHTYPE_SUBDOMAIN: 412 if (!dns_name_issubdomain(name, rule->name)) 413 continue; 414 break; 415 case DNS_SSUMATCHTYPE_WILDCARD: 416 if (!dns_name_matcheswildcard(name, rule->name)) 417 continue; 418 break; 419 case DNS_SSUMATCHTYPE_SELF: 420 if (!dns_name_equal(signer, name)) 421 continue; 422 break; 423 case DNS_SSUMATCHTYPE_SELFSUB: 424 if (!dns_name_issubdomain(name, signer)) 425 continue; 426 break; 427 case DNS_SSUMATCHTYPE_SELFWILD: 428 dns_fixedname_init(&fixed); 429 wildcard = dns_fixedname_name(&fixed); 430 result = dns_name_concatenate(dns_wildcardname, signer, 431 wildcard, NULL); 432 if (result != ISC_R_SUCCESS) 433 continue; 434 if (!dns_name_matcheswildcard(name, wildcard)) 435 continue; 436 break; 437 case DNS_SSUMATCHTYPE_SELFKRB5: 438 if (!dst_gssapi_identitymatchesrealmkrb5(signer, name, 439 rule->identity)) 440 continue; 441 break; 442 case DNS_SSUMATCHTYPE_SELFMS: 443 if (!dst_gssapi_identitymatchesrealmms(signer, name, 444 rule->identity)) 445 continue; 446 break; 447 case DNS_SSUMATCHTYPE_SUBDOMAINKRB5: 448 if (!dns_name_issubdomain(name, rule->name)) 449 continue; 450 if (!dst_gssapi_identitymatchesrealmkrb5(signer, NULL, 451 rule->identity)) 452 continue; 453 break; 454 case DNS_SSUMATCHTYPE_SUBDOMAINMS: 455 if (!dns_name_issubdomain(name, rule->name)) 456 continue; 457 if (!dst_gssapi_identitymatchesrealmms(signer, NULL, 458 rule->identity)) 459 continue; 460 break; 461 case DNS_SSUMATCHTYPE_TCPSELF: 462 dns_fixedname_init(&fixed); 463 tcpself = dns_fixedname_name(&fixed); 464 reverse_from_address(tcpself, tcpaddr); 465 if (dns_name_iswildcard(rule->identity)) { 466 if (!dns_name_matcheswildcard(tcpself, 467 rule->identity)) 468 continue; 469 } else { 470 if (!dns_name_equal(tcpself, rule->identity)) 471 continue; 472 } 473 if (!dns_name_equal(tcpself, name)) 474 continue; 475 break; 476 case DNS_SSUMATCHTYPE_6TO4SELF: 477 dns_fixedname_init(&fixed); 478 stfself = dns_fixedname_name(&fixed); 479 stf_from_address(stfself, tcpaddr); 480 if (dns_name_iswildcard(rule->identity)) { 481 if (!dns_name_matcheswildcard(stfself, 482 rule->identity)) 483 continue; 484 } else { 485 if (!dns_name_equal(stfself, rule->identity)) 486 continue; 487 } 488 if (!dns_name_equal(stfself, name)) 489 continue; 490 break; 491 case DNS_SSUMATCHTYPE_EXTERNAL: 492 if (!dns_ssu_external_match(rule->identity, signer, 493 name, tcpaddr, type, key, 494 table->mctx)) 495 continue; 496 break; 497 case DNS_SSUMATCHTYPE_DLZ: 498 if (!dns_dlz_ssumatch(table->dlzdatabase, signer, 499 name, tcpaddr, type, key)) 500 continue; 501 break; 502 } 503 504 if (rule->ntypes == 0) { 505 /* 506 * If this is a DLZ rule, then the DLZ ssu 507 * checks will have already checked 508 * the type. 509 */ 510 if (rule->matchtype != DNS_SSUMATCHTYPE_DLZ && 511 !isusertype(type)) 512 continue; 513 } else { 514 for (i = 0; i < rule->ntypes; i++) { 515 if (rule->types[i] == dns_rdatatype_any || 516 rule->types[i] == type) 517 break; 518 } 519 if (i == rule->ntypes) 520 continue; 521 } 522 return (rule->grant); 523 } 524 525 return (ISC_FALSE); 526} 527 528isc_boolean_t 529dns_ssurule_isgrant(const dns_ssurule_t *rule) { 530 REQUIRE(VALID_SSURULE(rule)); 531 return (rule->grant); 532} 533 534dns_name_t * 535dns_ssurule_identity(const dns_ssurule_t *rule) { 536 REQUIRE(VALID_SSURULE(rule)); 537 return (rule->identity); 538} 539 540unsigned int 541dns_ssurule_matchtype(const dns_ssurule_t *rule) { 542 REQUIRE(VALID_SSURULE(rule)); 543 return (rule->matchtype); 544} 545 546dns_name_t * 547dns_ssurule_name(const dns_ssurule_t *rule) { 548 REQUIRE(VALID_SSURULE(rule)); 549 return (rule->name); 550} 551 552unsigned int 553dns_ssurule_types(const dns_ssurule_t *rule, dns_rdatatype_t **types) { 554 REQUIRE(VALID_SSURULE(rule)); 555 REQUIRE(types != NULL && *types != NULL); 556 *types = rule->types; 557 return (rule->ntypes); 558} 559 560isc_result_t 561dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule) { 562 REQUIRE(VALID_SSUTABLE(table)); 563 REQUIRE(rule != NULL && *rule == NULL); 564 *rule = ISC_LIST_HEAD(table->rules); 565 return (*rule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE); 566} 567 568isc_result_t 569dns_ssutable_nextrule(dns_ssurule_t *rule, dns_ssurule_t **nextrule) { 570 REQUIRE(VALID_SSURULE(rule)); 571 REQUIRE(nextrule != NULL && *nextrule == NULL); 572 *nextrule = ISC_LIST_NEXT(rule, link); 573 return (*nextrule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE); 574} 575 576/* 577 * Create a specialised SSU table that points at an external DLZ database 578 */ 579isc_result_t 580dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep, 581 dns_dlzdb_t *dlzdatabase) 582{ 583 isc_result_t result; 584 dns_ssurule_t *rule; 585 dns_ssutable_t *table = NULL; 586 587 REQUIRE(tablep != NULL && *tablep == NULL); 588 589 result = dns_ssutable_create(mctx, &table); 590 if (result != ISC_R_SUCCESS) 591 return (result); 592 593 table->dlzdatabase = dlzdatabase; 594 595 rule = isc_mem_get(table->mctx, sizeof(dns_ssurule_t)); 596 if (rule == NULL) { 597 dns_ssutable_detach(&table); 598 return (ISC_R_NOMEMORY); 599 } 600 601 rule->identity = NULL; 602 rule->name = NULL; 603 rule->types = NULL; 604 rule->grant = ISC_TRUE; 605 rule->matchtype = DNS_SSUMATCHTYPE_DLZ; 606 rule->ntypes = 0; 607 rule->types = NULL; 608 rule->magic = SSURULEMAGIC; 609 610 ISC_LIST_INITANDAPPEND(table->rules, rule, link); 611 *tablep = table; 612 return (ISC_R_SUCCESS); 613} 614