1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <assert.h> 27 #include <errno.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <strings.h> 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 #include <netinet/in.h> 34 #include <arpa/inet.h> 35 #include <sys/time.h> 36 #include <unistd.h> 37 #include <string.h> 38 #include <arpa/nameser.h> 39 #include <resolv.h> 40 #include <netdb.h> 41 #include <rpc/rpc.h> 42 #include <syslog.h> 43 #include <gssapi/gssapi.h> 44 #include <kerberosv5/krb5.h> 45 #include <net/if.h> 46 47 #include <smbns_dyndns.h> 48 #include <smbns_krb.h> 49 50 /* internal use, in dyndns_add_entry */ 51 #define DEL_NONE 2 52 /* Maximum retires if not authoritative */ 53 #define MAX_AUTH_RETRIES 3 54 /* Number of times to retry a DNS query */ 55 #define DYNDNS_MAX_QUERY_RETRIES 3 56 /* Timeout value, in seconds, for DNS query responses */ 57 #define DYNDNS_QUERY_TIMEOUT 2 58 59 static uint16_t dns_msgid; 60 mutex_t dns_msgid_mtx; 61 62 /* 63 * dns_msgid_init 64 * 65 * Initializes the DNS message ID counter using the algorithm 66 * that resolver library uses to initialize the ID field of any res 67 * structure. 68 */ 69 void 70 dns_msgid_init(void) 71 { 72 struct timeval now; 73 74 (void) gettimeofday(&now, NULL); 75 (void) mutex_lock(&dns_msgid_mtx); 76 dns_msgid = (0xffff & (now.tv_sec ^ now.tv_usec ^ getpid())); 77 (void) mutex_unlock(&dns_msgid_mtx); 78 } 79 80 static int 81 dns_get_msgid(void) 82 { 83 uint16_t id; 84 85 (void) mutex_lock(&dns_msgid_mtx); 86 id = ++dns_msgid; 87 (void) mutex_unlock(&dns_msgid_mtx); 88 return (id); 89 } 90 91 /* 92 * XXX The following should be removed once head/arpa/nameser_compat.h 93 * defines BADSIG, BADKEY, BADTIME macros 94 */ 95 #ifndef BADSIG 96 #define BADSIG ns_r_badsig 97 #endif /* BADSIG */ 98 99 #ifndef BADKEY 100 #define BADKEY ns_r_badkey 101 #endif /* BADKEY */ 102 103 #ifndef BADTIME 104 #define BADTIME ns_r_badtime 105 #endif /* BADTIME */ 106 107 /* 108 * dyndns_msg_err 109 * Display error message for DNS error code found in the DNS header in 110 * reply message. 111 * Parameters: 112 * err: DNS errer code 113 * Returns: 114 * None 115 */ 116 static void 117 dyndns_msg_err(int err) 118 { 119 switch (err) { 120 case NOERROR: 121 break; 122 case FORMERR: 123 syslog(LOG_ERR, "DNS message format error\n"); 124 break; 125 case SERVFAIL: 126 syslog(LOG_ERR, "DNS server internal error\n"); 127 break; 128 case NXDOMAIN: 129 syslog(LOG_ERR, "DNS entry should exist but does not exist\n"); 130 break; 131 case NOTIMP: 132 syslog(LOG_ERR, "DNS opcode not supported\n"); 133 break; 134 case REFUSED: 135 syslog(LOG_ERR, "DNS operation refused\n"); 136 break; 137 case YXDOMAIN: 138 syslog(LOG_ERR, "DNS entry shouldn't exist but does exist\n"); 139 break; 140 case YXRRSET: 141 syslog(LOG_ERR, "DNS RRSet shouldn't exist but does exist\n"); 142 break; 143 case NXRRSET: 144 syslog(LOG_ERR, "DNS RRSet should exist but does not exist\n"); 145 break; 146 case NOTAUTH: 147 syslog(LOG_ERR, "DNS server is not authoritative " 148 "for specified zone\n"); 149 break; 150 case NOTZONE: 151 syslog(LOG_ERR, "Name in Prereq or Update section not " 152 "within specified zone\n"); 153 break; 154 case BADSIG: 155 syslog(LOG_ERR, "Bad transaction signature (TSIG)"); 156 break; 157 case BADKEY: 158 syslog(LOG_ERR, "Bad transaction key (TKEY)"); 159 break; 160 case BADTIME: 161 syslog(LOG_ERR, "Time not synchronized"); 162 break; 163 164 default: 165 syslog(LOG_ERR, "Unknown DNS error\n"); 166 } 167 } 168 169 /* 170 * display_stat 171 * Display GSS error message from error code. This routine is used to display 172 * the mechanism independent and mechanism specific error messages for GSS 173 * routines. The major status error code is the mechanism independent error 174 * code and the minor status error code is the mechanism specific error code. 175 * Parameters: 176 * maj: GSS major status 177 * min: GSS minor status 178 * Returns: 179 * None 180 */ 181 static void 182 display_stat(OM_uint32 maj, OM_uint32 min) 183 { 184 gss_buffer_desc msg; 185 OM_uint32 msg_ctx = 0; 186 OM_uint32 min2; 187 188 (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID, 189 &msg_ctx, &msg); 190 syslog(LOG_ERR, "dyndns: GSS major status error: %s\n", 191 (char *)msg.value); 192 (void) gss_release_buffer(&min2, &msg); 193 194 (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID, 195 &msg_ctx, &msg); 196 syslog(LOG_ERR, "dyndns: GSS minor status error: %s\n", 197 (char *)msg.value); 198 (void) gss_release_buffer(&min2, &msg); 199 } 200 201 static char * 202 dyndns_put_nshort(char *buf, uint16_t val) 203 { 204 uint16_t nval; 205 206 nval = htons(val); 207 (void) memcpy(buf, &nval, sizeof (uint16_t)); 208 buf += sizeof (uint16_t); 209 return (buf); 210 } 211 212 static char * 213 dyndns_get_nshort(char *buf, uint16_t *val) 214 { 215 uint16_t nval; 216 217 (void) memcpy(&nval, buf, sizeof (uint16_t)); 218 *val = ntohs(nval); 219 buf += sizeof (uint16_t); 220 return (buf); 221 } 222 223 static char * 224 dyndns_put_nlong(char *buf, uint32_t val) 225 { 226 uint32_t lval; 227 228 lval = htonl(val); 229 (void) memcpy(buf, &lval, sizeof (uint32_t)); 230 buf += sizeof (uint32_t); 231 return (buf); 232 } 233 234 static char * 235 dyndns_put_byte(char *buf, char val) 236 { 237 *buf = val; 238 buf++; 239 return (buf); 240 } 241 242 static char * 243 dyndns_put_int(char *buf, int val) 244 { 245 (void) memcpy(buf, &val, sizeof (int)); 246 buf += sizeof (int); 247 return (buf); 248 } 249 250 /* 251 * dyndns_stuff_str 252 * Converts a domain string by removing periods and replacing with a byte value 253 * of how many characters following period. A byte value is placed in front 254 * to indicate how many characters before first period. A NULL character is 255 * placed at the end. i.e. host.procom.com -> 4host5procom3com0 256 * Buffer space checking is done by caller. 257 * Parameters: 258 * ptr : address of pointer to buffer to store converted string 259 * zone: domain name string 260 * Returns: 261 * ptr: address of pointer to next available buffer space 262 * -1 : error 263 * 0 : success 264 */ 265 static int 266 dyndns_stuff_str(char **ptr, char *zone) 267 { 268 int len; 269 char *lenPtr, *zonePtr; 270 271 for (zonePtr = zone; *zonePtr; ) { 272 lenPtr = *ptr; 273 *ptr = *ptr + 1; 274 len = 0; 275 while (*zonePtr != '.' && *zonePtr != 0) { 276 *ptr = dyndns_put_byte(*ptr, *zonePtr); 277 zonePtr++; 278 len++; 279 } 280 *lenPtr = len; 281 if (*zonePtr == '.') 282 zonePtr++; 283 } 284 *ptr = dyndns_put_byte(*ptr, 0); 285 return (0); 286 } 287 288 /* 289 * dyndns_build_header 290 * Build the header for DNS query and DNS update request message. 291 * Parameters: 292 * ptr : address of pointer to buffer to store header 293 * buf_len : buffer length 294 * msg_id : message id 295 * query_req : use REQ_QUERY for query message or REQ_UPDATE for 296 * update message 297 * quest_zone_cnt : number of question record for query message or 298 * number of zone record for update message 299 * ans_prereq_cnt : number of answer record for query message or 300 * number of prerequisite record for update message 301 * nameser_update_cnt: number of name server for query message or 302 * number of update record for update message 303 * addit_cnt : number of additional record 304 * flags : query flags word 305 * Returns: 306 * ptr: address of pointer to next available buffer space 307 * -1 : error 308 * 0 : success 309 */ 310 static int 311 dyndns_build_header(char **ptr, int buf_len, uint16_t msg_id, int query_req, 312 uint16_t quest_zone_cnt, uint16_t ans_prereq_cnt, 313 uint16_t nameser_update_cnt, uint16_t addit_cnt, int flags) 314 { 315 uint16_t opcode; 316 317 if (buf_len < 12) { 318 syslog(LOG_ERR, "dyndns: no more buf for header section\n"); 319 return (-1); 320 } 321 322 *ptr = dyndns_put_nshort(*ptr, msg_id); /* mesg ID */ 323 if (query_req == REQ_QUERY) 324 opcode = ns_o_query; /* query msg */ 325 else 326 opcode = ns_o_update << 11; /* update msg */ 327 opcode |= flags; 328 /* mesg opcode */ 329 *ptr = dyndns_put_nshort(*ptr, opcode); 330 /* zone record count */ 331 *ptr = dyndns_put_nshort(*ptr, quest_zone_cnt); 332 /* prerequiste record count */ 333 *ptr = dyndns_put_nshort(*ptr, ans_prereq_cnt); 334 /* update record count */ 335 *ptr = dyndns_put_nshort(*ptr, nameser_update_cnt); 336 /* additional record count */ 337 *ptr = dyndns_put_nshort(*ptr, addit_cnt); 338 339 return (0); 340 } 341 342 /* 343 * dyndns_build_quest_zone 344 * Build the question section for query message or zone section for 345 * update message. 346 * Parameters: 347 * ptr : address of pointer to buffer to store question or zone section 348 * buf_len: buffer length 349 * name : question or zone name 350 * type : type of question or zone 351 * class : class of question or zone 352 * Returns: 353 * ptr: address of pointer to next available buffer space 354 * -1 : error 355 * 0 : success 356 */ 357 static int 358 dyndns_build_quest_zone(char **ptr, int buf_len, char *name, int type, 359 int class) 360 { 361 char *zonePtr; 362 363 if ((strlen(name) + 6) > buf_len) { 364 syslog(LOG_ERR, "dyndns: no more buf " 365 "for question/zone section\n"); 366 return (-1); 367 } 368 369 zonePtr = *ptr; 370 (void) dyndns_stuff_str(&zonePtr, name); 371 *ptr = zonePtr; 372 *ptr = dyndns_put_nshort(*ptr, type); 373 *ptr = dyndns_put_nshort(*ptr, class); 374 return (0); 375 } 376 377 /* 378 * dyndns_build_update 379 * Build update section of update message for adding and removing a record. 380 * If the ttl value is 0 then this message is for record deletion. 381 * 382 * Parameters: 383 * ptr : address of pointer to buffer to store update section 384 * buf_len : buffer length 385 * name : resource name of this record 386 * type : type of this record 387 * class : class of this record 388 * ttl : time-to-live, cached time of this entry by others and not 389 * within DNS database, a zero value for record(s) deletion 390 * data : data of this resource record 391 * forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone 392 * add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone 393 * del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all 394 * entries of the same resource name. Only valid for UPDATE_DEL. 395 * Returns: 396 * ptr: address of pointer to next available buffer space 397 * -1 : error 398 * 0 : success 399 */ 400 static int 401 dyndns_build_update(char **ptr, int buf_len, char *name, int type, int class, 402 uint32_t ttl, char *data, int forw_rev, int add_del, int del_type) 403 { 404 char *namePtr; 405 int rec_len, data_len; 406 407 rec_len = strlen(name) + 10; 408 if (add_del == UPDATE_ADD) { 409 if (forw_rev == UPDATE_FORW) 410 data_len = 4; 411 else 412 data_len = strlen(data) + 2; 413 } else { 414 if (del_type == DEL_ALL) 415 data_len = 0; 416 else if (forw_rev == UPDATE_FORW) 417 data_len = 4; 418 else 419 data_len = strlen(data) + 2; 420 } 421 422 if (rec_len + data_len > buf_len) { 423 syslog(LOG_ERR, "dyndns: no more buf for update section\n"); 424 return (-1); 425 } 426 427 namePtr = *ptr; 428 (void) dyndns_stuff_str(&namePtr, name); 429 *ptr = namePtr; 430 *ptr = dyndns_put_nshort(*ptr, type); 431 *ptr = dyndns_put_nshort(*ptr, class); 432 *ptr = dyndns_put_nlong(*ptr, ttl); 433 434 if (add_del == UPDATE_DEL && del_type == DEL_ALL) { 435 *ptr = dyndns_put_nshort(*ptr, 0); 436 return (0); 437 } 438 439 if (forw_rev == UPDATE_FORW) { 440 *ptr = dyndns_put_nshort(*ptr, 4); 441 *ptr = dyndns_put_int(*ptr, inet_addr(data)); /* ip address */ 442 } else { 443 *ptr = dyndns_put_nshort(*ptr, strlen(data)+2); 444 namePtr = *ptr; 445 (void) dyndns_stuff_str(&namePtr, data); /* hostname */ 446 *ptr = namePtr; 447 } 448 return (0); 449 } 450 451 /* 452 * dyndns_build_tkey 453 * Build TKEY section to establish security context for secure dynamic DNS 454 * update. DNS header and question sections need to be build before this 455 * section. The TKEY data are the tokens generated during security context 456 * establishment and the TKEY message is used to transmit those tokens, one 457 * at a time, to the DNS server. 458 * Parameters: 459 * ptr : address of pointer to buffer to store TKEY 460 * buf_len : buffer length 461 * name : key name, must be unique and same as for TSIG record 462 * key_expire: expiration time of this key in second 463 * data : TKEY data 464 * data_size : data size 465 * Returns: 466 * ptr: address of the pointer to the next available buffer space 467 * -1 : error 468 * 0 : success 469 */ 470 static int 471 dyndns_build_tkey(char **ptr, int buf_len, char *name, int key_expire, 472 char *data, int data_size) 473 { 474 char *namePtr; 475 struct timeval tp; 476 477 if (strlen(name)+2 + 45 + data_size > buf_len) { 478 syslog(LOG_ERR, "dyndns: no more buf for TKEY record\n"); 479 return (-1); 480 } 481 482 namePtr = *ptr; 483 (void) dyndns_stuff_str(&namePtr, name); /* unique global name */ 484 *ptr = namePtr; 485 *ptr = dyndns_put_nshort(*ptr, ns_t_tkey); 486 *ptr = dyndns_put_nshort(*ptr, ns_c_any); 487 *ptr = dyndns_put_nlong(*ptr, 0); 488 /* 19 + 14 + data_size + 2 */ 489 *ptr = dyndns_put_nshort(*ptr, 35 + data_size); 490 namePtr = *ptr; 491 (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com"); 492 *ptr = namePtr; 493 (void) gettimeofday(&tp, 0); 494 *ptr = dyndns_put_nlong(*ptr, tp.tv_sec); /* inception */ 495 /* expiration, 86400 */ 496 *ptr = dyndns_put_nlong(*ptr, tp.tv_sec + key_expire); 497 *ptr = dyndns_put_nshort(*ptr, MODE_GSS_API); /* mode: gss-api */ 498 *ptr = dyndns_put_nshort(*ptr, 0); /* error */ 499 *ptr = dyndns_put_nshort(*ptr, data_size); /* key size */ 500 (void) memcpy(*ptr, data, data_size); /* key data */ 501 *ptr += data_size; 502 *ptr = dyndns_put_nshort(*ptr, 0); /* other */ 503 return (0); 504 } 505 506 /* 507 * dyndns_build_tsig 508 * Build TSIG section for secure dynamic DNS update. This routine will be 509 * called twice. First called with TSIG_UNSIGNED, and second with TSIG_SIGNED. 510 * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request 511 * message encrypted for TSIG_SIGNED. The message id must be the same id as the 512 * one in the update request before it is encrypted. 513 * Parameters: 514 * ptr : address of pointer to buffer to store TSIG 515 * buf_len : buffer length 516 * msg_id : message id 517 * name : key name, must be the same as in TKEY record 518 * fudge_time : amount of error time allow in seconds 519 * data : TSIG data if TSIG_SIGNED, otherwise NULL 520 * data_size : size of data, otherwise 0 if data is NULL 521 * data_signed: TSIG_SIGNED to indicate data is signed and encrypted, 522 * otherwise TSIG_UNSIGNED 523 * Returns: 524 * ptr: address of pointer to next available buffer space 525 * -1 : error 526 * 0 : success 527 */ 528 static int 529 dyndns_build_tsig(char **ptr, int buf_len, int msg_id, char *name, 530 int fudge_time, char *data, int data_size, int data_signed) 531 { 532 char *namePtr; 533 struct timeval tp; 534 int signtime, fudge, rec_len; 535 536 if (data_signed == TSIG_UNSIGNED) 537 rec_len = strlen(name)+2 + 37; 538 else 539 rec_len = strlen(name)+2 + 45 + data_size; 540 541 if (rec_len > buf_len) { 542 syslog(LOG_ERR, "dyndns: no more buf for TSIG record\n"); 543 return (-1); 544 } 545 546 namePtr = *ptr; 547 (void) dyndns_stuff_str(&namePtr, name); /* unique global name */ 548 *ptr = namePtr; 549 if (data_signed == TSIG_SIGNED) 550 *ptr = dyndns_put_nshort(*ptr, ns_t_tsig); 551 *ptr = dyndns_put_nshort(*ptr, ns_c_any); 552 *ptr = dyndns_put_nlong(*ptr, 0); 553 if (data_signed == TSIG_SIGNED) { 554 /* 19 + 10 + data_size + 6 */ 555 *ptr = dyndns_put_nshort(*ptr, 35 + data_size); 556 } 557 namePtr = *ptr; 558 (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com"); 559 *ptr = namePtr; 560 (void) gettimeofday(&tp, 0); 561 signtime = tp.tv_sec >> 16; 562 *ptr = dyndns_put_nlong(*ptr, signtime); /* sign time */ 563 fudge = tp.tv_sec << 16; 564 fudge |= fudge_time; 565 *ptr = dyndns_put_nlong(*ptr, fudge); /* fudge time */ 566 if (data_signed == TSIG_SIGNED) { 567 /* signed data size */ 568 *ptr = dyndns_put_nshort(*ptr, data_size); 569 (void) memcpy(*ptr, data, data_size); /* signed data */ 570 *ptr += data_size; 571 *ptr = dyndns_put_nshort(*ptr, msg_id); /* original id */ 572 } 573 *ptr = dyndns_put_nshort(*ptr, 0); /* error */ 574 *ptr = dyndns_put_nshort(*ptr, 0); /* other */ 575 return (0); 576 } 577 578 /* 579 * dyndns_open_init_socket 580 * This routine creates a SOCK_STREAM or SOCK_DGRAM socket and initializes it 581 * by doing bind() and setting linger option to off. 582 * 583 * Parameters: 584 * sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP 585 * dest_addr: destination address in network byte order 586 * port : destination port number 587 * Returns: 588 * descriptor: descriptor referencing the created socket 589 * -1 : error 590 */ 591 static int 592 dyndns_open_init_socket(int sock_type, unsigned long dest_addr, int port) 593 { 594 int s; 595 struct sockaddr_in my_addr; 596 struct linger l; 597 struct sockaddr_in serv_addr; 598 599 if ((s = socket(AF_INET, sock_type, 0)) == -1) { 600 syslog(LOG_ERR, "dyndns: socket err\n"); 601 return (-1); 602 } 603 604 l.l_onoff = 0; 605 if (setsockopt(s, SOL_SOCKET, SO_LINGER, 606 (char *)&l, sizeof (l)) == -1) { 607 syslog(LOG_ERR, "dyndns: setsocket err\n"); 608 (void) close(s); 609 return (-1); 610 } 611 612 bzero(&my_addr, sizeof (my_addr)); 613 my_addr.sin_family = AF_INET; 614 my_addr.sin_port = htons(0); 615 my_addr.sin_addr.s_addr = htonl(INADDR_ANY); 616 617 if (bind(s, (struct sockaddr *)&my_addr, sizeof (my_addr)) < 0) { 618 syslog(LOG_ERR, "dyndns: client bind err\n"); 619 (void) close(s); 620 return (-1); 621 } 622 623 serv_addr.sin_family = AF_INET; 624 serv_addr.sin_port = htons(port); 625 serv_addr.sin_addr.s_addr = dest_addr; 626 627 if (connect(s, (struct sockaddr *)&serv_addr, 628 sizeof (struct sockaddr_in)) < 0) { 629 syslog(LOG_ERR, "dyndns: client connect err (%s)\n", 630 strerror(errno)); 631 (void) close(s); 632 return (-1); 633 } 634 635 return (s); 636 } 637 638 /* 639 * dyndns_build_tkey_msg 640 * This routine is used to build the TKEY message to transmit GSS tokens 641 * during GSS security context establishment for secure DNS update. The 642 * TKEY message format uses the DNS query message format. The TKEY section 643 * is the answer section of the query message format. 644 * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time. 645 * Parameters: 646 * buf : buffer to build and store TKEY message 647 * key_name: a unique key name, this same key name must be also be used in 648 * the TSIG message 649 * out_tok : TKEY message data (GSS tokens) 650 * Returns: 651 * id : message id of this TKEY message 652 * message size: the size of the TKEY message 653 * -1 : error 654 */ 655 static int 656 dyndns_build_tkey_msg(char *buf, char *key_name, uint16_t *id, 657 gss_buffer_desc *out_tok) 658 { 659 int queryReq, zoneCount, preqCount, updateCount, additionalCount; 660 int zoneType, zoneClass; 661 char *bufptr; 662 663 queryReq = REQ_QUERY; 664 /* query section of query request */ 665 zoneCount = 1; 666 /* answer section of query request */ 667 preqCount = 1; 668 updateCount = 0; 669 additionalCount = 0; 670 671 (void) memset(buf, 0, MAX_TCP_SIZE); 672 bufptr = buf; 673 *id = dns_get_msgid(); 674 675 /* add TCP length info that follows this field */ 676 bufptr = dyndns_put_nshort(bufptr, 677 26 + (strlen(key_name)+2)*2 + 35 + out_tok->length); 678 679 if (dyndns_build_header(&bufptr, BUFLEN_TCP(bufptr, buf), *id, queryReq, 680 zoneCount, preqCount, updateCount, additionalCount, 0) == -1) { 681 return (-1); 682 } 683 684 zoneType = ns_t_tkey; 685 zoneClass = ns_c_in; 686 if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name, 687 zoneType, zoneClass) == -1) { 688 return (-1); 689 } 690 691 if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name, 692 86400, out_tok->value, out_tok->length) == -1) { 693 return (-1); 694 } 695 696 return (bufptr - buf); 697 } 698 699 /* 700 * dyndns_establish_sec_ctx 701 * This routine is used to establish a security context with the DNS server 702 * by building TKEY messages and sending them to the DNS server. TKEY messages 703 * are also received from the DNS server for processing. The security context 704 * establishment is done with the GSS client on the system producing a token 705 * and sending the token within the TKEY message to the GSS server on the DNS 706 * server. The GSS server then processes the token and then send a TKEY reply 707 * message with a new token to be processed by the GSS client. The GSS client 708 * processes the new token and then generates a new token to be sent to the 709 * GSS server. This cycle is continued until the security establishment is 710 * done. TCP is used to send and receive TKEY messages. 711 * Parameters: 712 * cred_handle : handle to credential 713 * s : socket descriptor to DNS server 714 * key_name : TKEY key name 715 * dns_hostname: fully qualified DNS hostname 716 * oid : contains Kerberos 5 object identifier 717 * Returns: 718 * gss_context : handle to security context 719 */ 720 static int 721 dyndns_establish_sec_ctx(gss_ctx_id_t *gss_context, gss_cred_id_t cred_handle, 722 int s, char *key_name, char *dns_hostname, gss_OID oid) 723 { 724 uint16_t id, rid, rsz; 725 char buf[MAX_TCP_SIZE], buf2[MAX_TCP_SIZE]; 726 int ret; 727 char *service_name, *tmpptr; 728 int service_sz; 729 OM_uint32 min, maj, time_rec; 730 gss_buffer_desc service_buf, in_tok, out_tok; 731 gss_name_t target_name; 732 gss_buffer_desc *inputptr; 733 int gss_flags; 734 OM_uint32 ret_flags; 735 int buf_sz; 736 737 service_sz = strlen(dns_hostname) + 5; 738 service_name = (char *)malloc(sizeof (char) * service_sz); 739 if (service_name == NULL) { 740 syslog(LOG_ERR, "Malloc failed for %d bytes ", service_sz); 741 return (-1); 742 } 743 (void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname); 744 service_buf.value = service_name; 745 service_buf.length = strlen(service_name)+1; 746 if ((maj = gss_import_name(&min, &service_buf, 747 GSS_C_NT_HOSTBASED_SERVICE, &target_name)) != GSS_S_COMPLETE) { 748 display_stat(maj, min); 749 (void) free(service_name); 750 return (-1); 751 } 752 (void) free(service_name); 753 754 inputptr = GSS_C_NO_BUFFER; 755 *gss_context = GSS_C_NO_CONTEXT; 756 gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG | 757 GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG; 758 do { 759 maj = gss_init_sec_context(&min, cred_handle, gss_context, 760 target_name, oid, gss_flags, 0, NULL, inputptr, NULL, 761 &out_tok, &ret_flags, &time_rec); 762 763 if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) { 764 assert(gss_context); 765 if (*gss_context != GSS_C_NO_CONTEXT) 766 (void) gss_delete_sec_context(&min, 767 gss_context, NULL); 768 769 display_stat(maj, min); 770 (void) gss_release_name(&min, &target_name); 771 return (-1); 772 } 773 774 if ((maj == GSS_S_COMPLETE) && 775 !(ret_flags & GSS_C_REPLAY_FLAG)) { 776 syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG\n"); 777 if (out_tok.length > 0) 778 (void) gss_release_buffer(&min, &out_tok); 779 (void) gss_release_name(&min, &target_name); 780 return (-1); 781 } 782 783 if ((maj == GSS_S_COMPLETE) && 784 !(ret_flags & GSS_C_MUTUAL_FLAG)) { 785 syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_FLAG\n"); 786 if (out_tok.length > 0) 787 (void) gss_release_buffer(&min, &out_tok); 788 (void) gss_release_name(&min, &target_name); 789 return (-1); 790 } 791 792 if (out_tok.length > 0) { 793 if ((buf_sz = dyndns_build_tkey_msg(buf, key_name, 794 &id, &out_tok)) <= 0) { 795 (void) gss_release_buffer(&min, &out_tok); 796 (void) gss_release_name(&min, &target_name); 797 return (-1); 798 } 799 800 (void) gss_release_buffer(&min, &out_tok); 801 802 if (send(s, buf, buf_sz, 0) == -1) { 803 syslog(LOG_ERR, "dyndns: TKEY send error\n"); 804 (void) gss_release_name(&min, &target_name); 805 return (-1); 806 } 807 808 bzero(buf2, MAX_TCP_SIZE); 809 if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) { 810 syslog(LOG_ERR, "dyndns: TKEY " 811 "reply recv error\n"); 812 (void) gss_release_name(&min, &target_name); 813 return (-1); 814 } 815 816 ret = buf2[5] & 0xf; /* error field in TCP */ 817 if (ret != NOERROR) { 818 syslog(LOG_ERR, "dyndns: Error in " 819 "TKEY reply: %d: ", ret); 820 dyndns_msg_err(ret); 821 (void) gss_release_name(&min, &target_name); 822 return (-1); 823 } 824 825 tmpptr = &buf2[2]; 826 (void) dyndns_get_nshort(tmpptr, &rid); 827 if (id != rid) { 828 (void) gss_release_name(&min, &target_name); 829 return (-1); 830 } 831 832 tmpptr = &buf2[59+(strlen(key_name)+2)*2]; 833 (void) dyndns_get_nshort(tmpptr, &rsz); 834 in_tok.length = rsz; 835 836 /* bsd38 -> 2*7=14 */ 837 in_tok.value = &buf2[61+(strlen(key_name)+2)*2]; 838 inputptr = &in_tok; 839 } 840 841 } while (maj != GSS_S_COMPLETE); 842 843 (void) gss_release_name(&min, &target_name); 844 845 return (0); 846 } 847 848 /* 849 * dyndns_get_sec_context 850 * Get security context for secure dynamic DNS update. This routine opens 851 * a TCP socket to the DNS server and establishes a security context with 852 * the DNS server using host principal to perform secure dynamic DNS update. 853 * Parameters: 854 * hostname: fully qualified hostname 855 * dns_ip : ip address of hostname in network byte order 856 * Returns: 857 * gss_handle: gss credential handle 858 * gss_context: gss security context 859 * -1: error 860 * 0: success 861 */ 862 static gss_ctx_id_t 863 dyndns_get_sec_context(const char *hostname, int dns_ip) 864 { 865 int s; 866 gss_cred_id_t cred_handle; 867 gss_ctx_id_t gss_context; 868 gss_OID oid; 869 struct hostent *hentry; 870 char *key_name, dns_hostname[MAXHOSTNAMELEN]; 871 872 cred_handle = GSS_C_NO_CREDENTIAL; 873 oid = GSS_C_NO_OID; 874 key_name = (char *)hostname; 875 876 hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET); 877 if (hentry == NULL) { 878 syslog(LOG_ERR, "dyndns: Can't get DNS " 879 "hostname from DNS ip.\n"); 880 return (NULL); 881 } 882 (void) strcpy(dns_hostname, hentry->h_name); 883 884 if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) { 885 return (NULL); 886 } 887 888 if (dyndns_establish_sec_ctx(&gss_context, cred_handle, s, key_name, 889 dns_hostname, oid)) 890 gss_context = NULL; 891 892 (void) close(s); 893 return (gss_context); 894 } 895 896 /* 897 * dyndns_build_add_remove_msg 898 * This routine builds the update request message for adding and removing DNS 899 * entries which is used for non-secure and secure DNS update. 900 * This routine builds an UDP message. 901 * Parameters: 902 * buf : buffer to build message 903 * update_zone: the type of zone to update, use UPDATE_FORW for forward 904 * lookup zone, use UPDATE_REV for reverse lookup zone 905 * hostname : fully qualified hostname to update DNS with 906 * ip_addr : IP address of hostname 907 * life_time : cached time of this entry by others and not within DNS 908 * database 909 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry 910 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 911 * entries of the same resource name. Only valid for UPDATE_DEL. 912 * addit_cnt : Indicate how many record is in the additional section of 913 * the DNS message. A value of zero is always used with 914 * non-secure update message. For secure update message, 915 * the value will be one because the signed TSIG message 916 * is added as the additional record of the DNS update message. 917 * id : DNS message ID. If a positive value then this ID value is 918 * used, otherwise the next incremented value is used 919 * level : This is the domain level which we send the request to, level 920 * zero is the default level, it can go upto 2 in reverse zone 921 * and virtually to any level in forward zone. 922 * Returns: 923 * buf : buffer containing update message 924 * id : DNS message ID 925 * int : size of update message 926 * -1 : error 927 * 928 * This function is changed to handle dynamic DNS update retires to higher 929 * authoritative domains. 930 */ 931 static int 932 dyndns_build_add_remove_msg(char *buf, int update_zone, const char *hostname, 933 const char *ip_addr, int life_time, int update_type, int del_type, 934 int addit_cnt, uint16_t *id, int level) 935 { 936 int a, b, c, d; 937 char *bufptr; 938 int queryReq, zoneCount, preqCount, updateCount, additionalCount; 939 char *zone, *resource, *data, zone_buf[100], resrc_buf[100]; 940 int zoneType, zoneClass, type, class, ttl; 941 char *p; 942 943 queryReq = REQ_UPDATE; 944 zoneCount = 1; 945 preqCount = 0; 946 updateCount = 1; 947 additionalCount = addit_cnt; 948 949 (void) memset(buf, 0, NS_PACKETSZ); 950 bufptr = buf; 951 952 if (*id == 0) 953 *id = dns_get_msgid(); 954 955 if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq, 956 zoneCount, preqCount, updateCount, additionalCount, 0) == -1) { 957 return (-1); 958 } 959 960 zoneType = ns_t_soa; 961 zoneClass = ns_c_in; 962 963 if (update_zone == UPDATE_FORW) { 964 p = (char *)hostname; 965 966 /* Try higher domains according to the level requested */ 967 do { 968 /* domain */ 969 if ((zone = (char *)strchr(p, '.')) == NULL) 970 return (-1); 971 zone += 1; 972 p = zone; 973 } while (--level >= 0); 974 resource = (char *)hostname; 975 data = (char *)ip_addr; 976 } else { 977 (void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d); 978 (void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa", c, b, a); 979 zone = p = zone_buf; 980 981 /* Try higher domains according to the level requested */ 982 while (--level >= 0) { 983 /* domain */ 984 if ((zone = (char *)strchr(p, '.')) == NULL) { 985 return (-1); 986 } 987 zone += 1; 988 p = zone; 989 } 990 991 (void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa", 992 d, c, b, a); 993 resource = resrc_buf; /* ip info */ 994 data = (char *)hostname; 995 } 996 997 if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), zone, 998 zoneType, zoneClass) == -1) { 999 return (-1); 1000 } 1001 1002 if (update_zone == UPDATE_FORW) 1003 type = ns_t_a; 1004 else 1005 type = ns_t_ptr; 1006 1007 if (update_type == UPDATE_ADD) { 1008 class = ns_c_in; 1009 ttl = life_time; 1010 } else { 1011 if (del_type == DEL_ONE) 1012 class = ns_c_none; /* remove one */ 1013 else 1014 class = ns_c_any; /* remove all */ 1015 ttl = 0; 1016 } 1017 if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf), 1018 resource, type, class, ttl, data, update_zone, 1019 update_type, del_type) == -1) { 1020 return (-1); 1021 } 1022 1023 return (bufptr - buf); 1024 } 1025 1026 /* 1027 * dyndns_build_unsigned_tsig_msg 1028 * This routine is used to build the unsigned TSIG message for signing. The 1029 * unsigned TSIG message contains the update request message with certain TSIG 1030 * fields included. An error time of 300 seconds is used for fudge time. This 1031 * is the number used by Microsoft clients. 1032 * This routine builds a UDP message. 1033 * Parameters: 1034 * buf : buffer to build message 1035 * update_zone: the type of zone to update, use UPDATE_FORW for forward 1036 * lookup zone, use UPDATE_REV for reverse lookup zone 1037 * hostname : fully qualified hostname to update DNS with 1038 * ip_addr : IP address of hostname 1039 * life_time : cached time of this entry by others and not within DNS 1040 * database 1041 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry 1042 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1043 * entries of the same resource name. Only valid for UPDATE_DEL. 1044 * key_name : same key name used in TKEY message 1045 * id : DNS message ID. If a positive value then this ID value is 1046 * used, otherwise the next incremented value is used 1047 * level : This is the domain level which we send the request to, level 1048 * zero is the default level, it can go upto 2 in reverse zone 1049 * and virtually to any level in forward zone. 1050 * Returns: 1051 * buf : buffer containing update message 1052 * id : DNS message ID 1053 * int : size of update message 1054 * -1 : error 1055 */ 1056 static int 1057 dyndns_build_unsigned_tsig_msg(char *buf, int update_zone, const char *hostname, 1058 const char *ip_addr, int life_time, int update_type, int del_type, 1059 char *key_name, uint16_t *id, int level) 1060 { 1061 char *bufptr; 1062 int buf_sz; 1063 1064 if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname, 1065 ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) { 1066 return (-1); 1067 } 1068 1069 bufptr = buf + buf_sz; 1070 1071 if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 0, 1072 key_name, 300, NULL, 0, TSIG_UNSIGNED) == -1) { 1073 return (-1); 1074 } 1075 1076 return (bufptr - buf); 1077 } 1078 1079 /* 1080 * dyndns_build_signed_tsig_msg 1081 * This routine build the signed TSIG message which contains the update 1082 * request message encrypted. An error time of 300 seconds is used for fudge 1083 * time. This is the number used by Microsoft clients. 1084 * This routine builds a UDP message. 1085 * Parameters: 1086 * buf : buffer to build message 1087 * update_zone: the type of zone to update, use UPDATE_FORW for forward 1088 * lookup zone, use UPDATE_REV for reverse lookup zone 1089 * hostname : fully qualified hostname to update DNS with 1090 * ip_addr : IP address of hostname 1091 * life_time : cached time of this entry by others and not within DNS 1092 * database 1093 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry 1094 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1095 * entries of the same resource name. Only valid for UPDATE_DEL. 1096 * key_name : same key name used in TKEY message 1097 * id : DNS message ID. If a positive value then this ID value is 1098 * used, otherwise the next incremented value is used 1099 * in_mic : the update request message encrypted 1100 * level : This is the domain level which we send the request to, level 1101 * zero is the default level, it can go upto 2 in reverse zone 1102 * and virtually to any level in forward zone. 1103 * 1104 * Returns: 1105 * buf : buffer containing update message 1106 * id : DNS message ID 1107 * int : size of update message 1108 * -1 : error 1109 */ 1110 static int 1111 dyndns_build_signed_tsig_msg(char *buf, int update_zone, const char *hostname, 1112 const char *ip_addr, int life_time, int update_type, int del_type, 1113 char *key_name, uint16_t *id, gss_buffer_desc *in_mic, int level) 1114 { 1115 char *bufptr; 1116 int buf_sz; 1117 1118 if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname, 1119 ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) { 1120 return (-1); 1121 } 1122 1123 bufptr = buf + buf_sz; 1124 1125 if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 1126 *id, key_name, 300, in_mic->value, 1127 in_mic->length, TSIG_SIGNED) == -1) { 1128 return (-1); 1129 } 1130 1131 return (bufptr - buf); 1132 } 1133 1134 /* 1135 * dyndns_udp_send_recv 1136 * This routine sends and receives UDP DNS request and reply messages. 1137 * 1138 * Pre-condition: Caller must call dyndns_open_init_socket() before calling 1139 * this function. 1140 * 1141 * Parameters: 1142 * s : socket descriptor 1143 * buf : buffer containing data to send 1144 * buf_sz : size of data to send 1145 * Returns: 1146 * -1 : error 1147 * rec_buf: reply dat 1148 * 0 : success 1149 */ 1150 static int 1151 dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf) 1152 { 1153 int i, retval, addr_len; 1154 struct timeval tv, timeout; 1155 fd_set rfds; 1156 struct sockaddr_in from_addr; 1157 1158 timeout.tv_usec = 0; 1159 timeout.tv_sec = DYNDNS_QUERY_TIMEOUT; 1160 1161 for (i = 0; i <= DYNDNS_MAX_QUERY_RETRIES; i++) { 1162 if (send(s, buf, buf_sz, 0) == -1) { 1163 syslog(LOG_ERR, "dyndns: UDP send error (%s)\n", 1164 strerror(errno)); 1165 return (-1); 1166 } 1167 1168 FD_ZERO(&rfds); 1169 FD_SET(s, &rfds); 1170 1171 tv = timeout; 1172 1173 retval = select(s+1, &rfds, NULL, NULL, &tv); 1174 1175 if (retval == -1) { 1176 return (-1); 1177 } else if (retval > 0) { 1178 bzero(rec_buf, NS_PACKETSZ); 1179 /* required by recvfrom */ 1180 addr_len = sizeof (struct sockaddr_in); 1181 if (recvfrom(s, rec_buf, NS_PACKETSZ, 0, 1182 (struct sockaddr *)&from_addr, &addr_len) == -1) { 1183 syslog(LOG_ERR, "dyndns: UDP recv err\n"); 1184 return (-1); 1185 } 1186 break; 1187 } 1188 } 1189 1190 /* did not receive anything */ 1191 if (i == (DYNDNS_MAX_QUERY_RETRIES + 1)) { 1192 syslog(LOG_ERR, "dyndns: max retries for UDP recv reached\n"); 1193 return (-1); 1194 } 1195 1196 return (0); 1197 } 1198 1199 /* 1200 * dyndns_sec_add_remove_entry 1201 * Perform secure dynamic DNS update after getting security context. 1202 * This routine opens a UDP socket to the DNS sever, gets the security context, 1203 * builds the unsigned TSIG message and signed TSIG message. The signed TSIG 1204 * message containing the encrypted update request message is sent to the DNS 1205 * server. The response is received and check for error. If there is no 1206 * error then credential handle and security context are released and the local 1207 * NSS cached is purged. 1208 * Parameters: 1209 * update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone 1210 * hostname : fully qualified hostname 1211 * ip_addr : ip address of hostname in string format 1212 * life_time : cached time of this entry by others and not within DNS 1213 * database 1214 * max_retries : maximum retries for sending DNS update request 1215 * recv_timeout: receive timeout 1216 * update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry 1217 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1218 * entries of the same resource name. Only valid for UPDATE_DEL 1219 * dns_str : DNS IP address in string format 1220 * Returns: 1221 * -1: error 1222 * 0: success 1223 * 1224 * This function is enhanced to handle the case of NOTAUTH error when DNS server 1225 * is not authoritative for specified zone. In this case we need to resend the 1226 * same request to the higher authoritative domains. 1227 * This is true for both secure and unsecure dynamic DNS updates. 1228 */ 1229 static int 1230 dyndns_sec_add_remove_entry(int update_zone, const char *hostname, 1231 const char *ip_addr, int life_time, int update_type, int del_type, 1232 char *dns_str) 1233 { 1234 int s2; 1235 uint16_t id, rid; 1236 char buf[NS_PACKETSZ], buf2[NS_PACKETSZ]; 1237 int ret; 1238 OM_uint32 min, maj; 1239 gss_buffer_desc in_mic, out_mic; 1240 gss_ctx_id_t gss_context; 1241 int dns_ip; 1242 char *key_name; 1243 int buf_sz; 1244 int level = 0; 1245 1246 assert(dns_str); 1247 assert(*dns_str); 1248 1249 dns_ip = inet_addr(dns_str); 1250 1251 sec_retry_higher: 1252 1253 if ((gss_context = dyndns_get_sec_context(hostname, 1254 dns_ip)) == NULL) { 1255 return (-1); 1256 } 1257 1258 key_name = (char *)hostname; 1259 1260 if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) { 1261 if (gss_context != GSS_C_NO_CONTEXT) 1262 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1263 return (-1); 1264 } 1265 1266 id = 0; 1267 if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname, 1268 ip_addr, life_time, update_type, del_type, 1269 key_name, &id, level)) <= 0) { 1270 (void) close(s2); 1271 if (gss_context != GSS_C_NO_CONTEXT) 1272 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1273 return (-1); 1274 } 1275 1276 in_mic.length = buf_sz; 1277 in_mic.value = buf; 1278 1279 /* sign update message */ 1280 if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) != 1281 GSS_S_COMPLETE) { 1282 display_stat(maj, min); 1283 (void) close(s2); 1284 if (gss_context != GSS_C_NO_CONTEXT) 1285 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1286 return (-1); 1287 } 1288 1289 if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname, 1290 ip_addr, life_time, update_type, del_type, key_name, &id, 1291 &out_mic, level)) <= 0) { 1292 (void) close(s2); 1293 (void) gss_release_buffer(&min, &out_mic); 1294 if (gss_context != GSS_C_NO_CONTEXT) 1295 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1296 return (-1); 1297 } 1298 1299 (void) gss_release_buffer(&min, &out_mic); 1300 1301 if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) { 1302 (void) close(s2); 1303 if (gss_context != GSS_C_NO_CONTEXT) 1304 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1305 return (-1); 1306 } 1307 1308 (void) close(s2); 1309 1310 if (gss_context != GSS_C_NO_CONTEXT) 1311 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1312 1313 ret = buf2[3] & 0xf; /* error field in UDP */ 1314 1315 /* 1316 * If it is a NOTAUTH error we should retry with higher domains 1317 * until we get a successful reply or the maximum retries is met. 1318 */ 1319 if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES) 1320 goto sec_retry_higher; 1321 1322 /* check here for update request is successful */ 1323 if (ret != NOERROR) { 1324 syslog(LOG_ERR, "dyndns: Error in TSIG reply: %d: ", ret); 1325 dyndns_msg_err(ret); 1326 return (-1); 1327 } 1328 1329 (void) dyndns_get_nshort(buf2, &rid); 1330 if (id != rid) 1331 return (-1); 1332 1333 return (0); 1334 } 1335 1336 /* 1337 * dyndns_seach_entry 1338 * Query DNS server for entry. This routine can indicate if an entry exist 1339 * or not during forward or reverse lookup. Also can indicate if the data 1340 * of the entry matched. For example, for forward lookup, the entry is 1341 * searched using the hostname and the data is the IP address. For reverse 1342 * lookup, the entry is searched using the IP address and the data is the 1343 * hostname. 1344 * Parameters: 1345 * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone 1346 * hostname : fully qualified hostname 1347 * ip_addr : ip address of hostname in string format 1348 * update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry 1349 * Returns: 1350 * time_out: no use 1351 * is_match: is 1 for found matching entry, otherwise 0 1352 * 1 : an entry exist but not necessarily match 1353 * 0 : an entry does not exist 1354 */ 1355 /*ARGSUSED*/ 1356 static int 1357 dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr, 1358 int update_type, struct timeval *time_out, int *is_match) 1359 { 1360 struct hostent *hentry; 1361 struct in_addr in; 1362 in_addr_t ip; 1363 int i; 1364 1365 *is_match = 0; 1366 if (update_zone == UPDATE_FORW) { 1367 hentry = gethostbyname(hostname); 1368 if (hentry) { 1369 ip = inet_addr(ip_addr); 1370 for (i = 0; hentry->h_addr_list[i]; i++) { 1371 (void) memcpy(&in.s_addr, 1372 hentry->h_addr_list[i], sizeof (in.s_addr)); 1373 if (ip == in.s_addr) { 1374 *is_match = 1; 1375 break; 1376 } 1377 } 1378 return (1); 1379 } 1380 } else { 1381 int dns_ip = inet_addr(ip_addr); 1382 hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET); 1383 if (hentry) { 1384 if (strncasecmp(hentry->h_name, hostname, 1385 strlen(hostname)) == 0) { 1386 *is_match = 1; 1387 } 1388 return (1); 1389 } 1390 } 1391 1392 /* entry does not exist */ 1393 return (0); 1394 } 1395 1396 /* 1397 * dyndns_add_remove_entry 1398 * Perform non-secure dynamic DNS update. If it fails and host principal 1399 * keys can be found in the local keytab file, secure update will be performed. 1400 * 1401 * This routine opens a UDP socket to the DNS sever, build the update request 1402 * message, and sends the message to the DNS server. The response is received 1403 * and check for error. If there is no error then the local NSS cached is 1404 * purged. DNS may be used to check to see if an entry already exist before 1405 * adding or to see if an entry does exist before removing it. Adding 1406 * duplicate entries or removing non-existing entries does not cause any 1407 * problems. DNS is not check when doing a delete all. 1408 * Parameters: 1409 * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone 1410 * hostname : fully qualified hostname 1411 * ip_addr : ip address of hostname in string format 1412 * life_time : cached time of this entry by others and not within DNS 1413 * database 1414 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry 1415 * do_check : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS 1416 * checking before update 1417 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1418 * entries of the same resource name. Only valid for UPDATE_DEL. 1419 * dns_str : DNS IP address in string format 1420 * Returns: 1421 * -1: error 1422 * 0: success 1423 * 1424 * This function is enhanced to handle the case of NOTAUTH error when DNS server 1425 * is not authoritative for specified zone. In this case we need to resend the 1426 * same request to the higher authoritative domains. 1427 * This is true for both secure and unsecure dynamic DNS updates. 1428 */ 1429 static int 1430 dyndns_add_remove_entry(int update_zone, const char *hostname, 1431 const char *ip_addr, int life_time, int update_type, 1432 int do_check, int del_type, char *dns_str) 1433 { 1434 int s; 1435 uint16_t id, rid; 1436 char buf[NS_PACKETSZ], buf2[NS_PACKETSZ]; 1437 int ret, dns_ip; 1438 int is_exist, is_match; 1439 struct timeval timeout; 1440 int buf_sz; 1441 int level = 0; 1442 1443 assert(dns_str); 1444 assert(*dns_str); 1445 1446 dns_ip = inet_addr(dns_str); 1447 1448 if (do_check == DNS_CHECK && del_type != DEL_ALL) { 1449 is_exist = dyndns_search_entry(update_zone, hostname, ip_addr, 1450 update_type, &timeout, &is_match); 1451 1452 if (update_type == UPDATE_ADD && is_exist && is_match) { 1453 return (0); 1454 } else if (update_type == UPDATE_DEL && !is_exist) { 1455 return (0); 1456 } 1457 } 1458 1459 retry_higher: 1460 if ((s = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) { 1461 return (-1); 1462 } 1463 1464 id = 0; 1465 if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname, 1466 ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) { 1467 (void) close(s); 1468 return (-1); 1469 } 1470 1471 if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) { 1472 (void) close(s); 1473 return (-1); 1474 } 1475 1476 (void) close(s); 1477 1478 ret = buf2[3] & 0xf; /* error field in UDP */ 1479 1480 /* 1481 * If it is a NOTAUTH error we should retry with higher domains 1482 * until we get a successful reply 1483 */ 1484 if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES) 1485 goto retry_higher; 1486 1487 /* check here for update request is successful */ 1488 if (ret == NOERROR) { 1489 (void) dyndns_get_nshort(buf2, &rid); 1490 if (id != rid) 1491 return (-1); 1492 return (0); 1493 } 1494 1495 if (ret == NOTIMP) { 1496 syslog(LOG_ERR, "dyndns: DNS does not " 1497 "support dynamic update\n"); 1498 return (-1); 1499 } else if (ret == NOTAUTH) { 1500 syslog(LOG_ERR, "dyndns: DNS is not authoritative for " 1501 "zone name in zone section\n"); 1502 return (-1); 1503 } 1504 1505 if (smb_krb5_find_keytab_entries(hostname, SMBNS_KRB5_KEYTAB)) 1506 ret = dyndns_sec_add_remove_entry(update_zone, hostname, 1507 ip_addr, life_time, update_type, del_type, dns_str); 1508 1509 return (ret); 1510 } 1511 1512 /* 1513 * dyndns_add_entry 1514 * Main routine to add an entry into DNS. The attempt will be made on the 1515 * the servers returned by smb_get_nameserver(). Upon a successful 1516 * attempt on any one of the server, the function will exit with 0. 1517 * Otherwise, -1 is retuned to indicate the update attempt on all the 1518 * nameservers has failed. 1519 * 1520 * Parameters: 1521 * update_zone: the type of zone to update, use UPDATE_FORW for forward 1522 * lookup zone, use UPDATE_REV for reverse lookup zone 1523 * hostname : fully qualified hostname 1524 * ip_addr : ip address of hostname in string format 1525 * life_time : cached time of this entry by others and not within DNS 1526 * database 1527 * Returns: 1528 * -1: error 1529 * 0: success 1530 */ 1531 static int 1532 dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr, 1533 int life_time) 1534 { 1535 char *dns_str; 1536 struct in_addr ns_list[MAXNS]; 1537 int i, cnt; 1538 int addr, rc = 0; 1539 1540 if (hostname == NULL || ip_addr == NULL) { 1541 return (-1); 1542 } 1543 1544 addr = (int)inet_addr(ip_addr); 1545 if ((addr == -1) || (addr == 0)) { 1546 return (-1); 1547 } 1548 1549 cnt = smb_get_nameservers(ns_list, MAXNS); 1550 1551 for (i = 0; i < cnt; i++) { 1552 dns_str = inet_ntoa(ns_list[i]); 1553 if ((dns_str == NULL) || 1554 (strcmp(dns_str, "0.0.0.0") == 0)) { 1555 continue; 1556 } 1557 1558 if (update_zone == UPDATE_FORW) { 1559 syslog(LOG_DEBUG, "Dynamic update on forward lookup " 1560 "zone for %s (%s)...\n", hostname, ip_addr); 1561 } else { 1562 syslog(LOG_DEBUG, "Dynamic update on reverse lookup " 1563 "zone for %s (%s)...\n", hostname, ip_addr); 1564 } 1565 if (dyndns_add_remove_entry(update_zone, hostname, 1566 ip_addr, life_time, 1567 UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_str) != -1) { 1568 rc = 1; 1569 break; 1570 } 1571 } 1572 1573 return (rc ? 0 : -1); 1574 } 1575 1576 /* 1577 * dyndns_remove_entry 1578 * Main routine to remove an entry or all entries of the same resource name 1579 * from DNS. The update attempt will be made on the primary DNS server. If 1580 * there is a failure then another attempt will be made on the secondary DNS 1581 * server. 1582 * Parameters: 1583 * update_zone: the type of zone to update, use UPDATE_FORW for forward 1584 * lookup zone, use UPDATE_REV for reverse lookup zone 1585 * hostname : fully qualified hostname 1586 * ip_addr : ip address of hostname in string format 1587 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1588 * entries of the same resource name. Only valid for UPDATE_DEL 1589 * Returns: 1590 * -1: error 1591 * 0: success 1592 */ 1593 static int 1594 dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr, 1595 int del_type) 1596 { 1597 char *dns_str; 1598 struct in_addr ns_list[MAXNS]; 1599 int i, cnt, scnt; 1600 int addr; 1601 1602 if ((hostname == NULL || ip_addr == NULL)) { 1603 return (-1); 1604 } 1605 1606 addr = (int)inet_addr(ip_addr); 1607 if ((addr == -1) || (addr == 0)) { 1608 return (-1); 1609 } 1610 1611 cnt = smb_get_nameservers(ns_list, MAXNS); 1612 scnt = 0; 1613 1614 for (i = 0; i < cnt; i++) { 1615 dns_str = inet_ntoa(ns_list[i]); 1616 if ((dns_str == NULL) || 1617 (strcmp(dns_str, "0.0.0.0") == 0)) { 1618 continue; 1619 } 1620 1621 if (update_zone == UPDATE_FORW) { 1622 if (del_type == DEL_ONE) { 1623 syslog(LOG_DEBUG, "Dynamic update " 1624 "on forward lookup " 1625 "zone for %s (%s)...\n", hostname, ip_addr); 1626 } else { 1627 syslog(LOG_DEBUG, "Removing all " 1628 "entries of %s " 1629 "in forward lookup zone...\n", hostname); 1630 } 1631 } else { 1632 if (del_type == DEL_ONE) { 1633 syslog(LOG_DEBUG, "Dynamic update " 1634 "on reverse lookup " 1635 "zone for %s (%s)...\n", hostname, ip_addr); 1636 } else { 1637 syslog(LOG_DEBUG, "Removing all " 1638 "entries of %s " 1639 "in reverse lookup zone...\n", ip_addr); 1640 } 1641 } 1642 if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0, 1643 UPDATE_DEL, DNS_NOCHECK, del_type, dns_str) != -1) { 1644 scnt++; 1645 break; 1646 } 1647 } 1648 if (scnt) 1649 return (0); 1650 return (-1); 1651 } 1652 1653 /* 1654 * dyndns_update 1655 * 1656 * Dynamic DNS update API for kclient. 1657 * 1658 * Returns 0 upon success. Otherwise, returns -1. 1659 */ 1660 int 1661 dyndns_update(char *fqdn) 1662 { 1663 int rc; 1664 1665 if (smb_nic_init() != 0) 1666 return (-1); 1667 1668 dns_msgid_init(); 1669 rc = dyndns_update_core(fqdn); 1670 smb_nic_fini(); 1671 return (rc); 1672 } 1673 1674 /* 1675 * dyndns_update_core 1676 * Perform dynamic update on both forward and reverse lookup zone using 1677 * the specified hostname and IP addresses. Before updating DNS, existing 1678 * host entries with the same hostname in the forward lookup zone are removed 1679 * and existing pointer entries with the same IP addresses in the reverse 1680 * lookup zone are removed. After DNS update, host entries for current 1681 * hostname will show current IP addresses and pointer entries for current 1682 * IP addresses will show current hostname. 1683 * Parameters: 1684 * fqhn - fully-qualified hostname 1685 * 1686 * Returns: 1687 * -1: some dynamic DNS updates errors 1688 * 0: successful or DDNS disabled. 1689 */ 1690 int 1691 dyndns_update_core(char *fqdn) 1692 { 1693 int forw_update_ok, error; 1694 char *my_ip; 1695 struct in_addr addr; 1696 smb_niciter_t ni; 1697 int rc; 1698 char fqhn[MAXHOSTNAMELEN]; 1699 1700 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE)) 1701 return (0); 1702 1703 if (smb_gethostname(fqhn, MAXHOSTNAMELEN, 0) != 0) 1704 return (-1); 1705 1706 (void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn); 1707 error = 0; 1708 forw_update_ok = 0; 1709 1710 /* 1711 * Dummy IP is okay since we are removing all using the hostname. 1712 */ 1713 if (dyndns_remove_entry(UPDATE_FORW, fqhn, "1.1.1.1", DEL_ALL) == 0) { 1714 forw_update_ok = 1; 1715 } else { 1716 error++; 1717 } 1718 1719 if (smb_nic_getfirst(&ni) != 0) 1720 return (-1); 1721 1722 do { 1723 if (ni.ni_nic.nic_sysflags & (IFF_STANDBY | IFF_PRIVATE)) 1724 continue; 1725 1726 addr.s_addr = ni.ni_nic.nic_ip; 1727 my_ip = (char *)strdup(inet_ntoa(addr)); 1728 if (my_ip == NULL) { 1729 error++; 1730 continue; 1731 } 1732 1733 if (forw_update_ok) { 1734 rc = dyndns_add_entry(UPDATE_FORW, fqhn, my_ip, 1735 DDNS_TTL); 1736 1737 if (rc == -1) 1738 error++; 1739 } 1740 1741 rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL); 1742 if (rc == 0) { 1743 rc = dyndns_add_entry(UPDATE_REV, fqhn, my_ip, 1744 DDNS_TTL); 1745 } 1746 1747 if (rc == -1) 1748 error++; 1749 1750 (void) free(my_ip); 1751 } while (smb_nic_getnext(&ni) == 0); 1752 1753 return ((error == 0) ? 0 : -1); 1754 } 1755 1756 /* 1757 * dyndns_clear_rev_zone 1758 * Clear the rev zone records. Must be called to clear the OLD if list 1759 * of down records prior to updating the list with new information. 1760 * 1761 * Parameters: 1762 * fqhn - fully-qualified hostname 1763 * Returns: 1764 * -1: some dynamic DNS updates errors 1765 * 0: successful or DDNS disabled. 1766 */ 1767 int 1768 dyndns_clear_rev_zone(char *fqdn) 1769 { 1770 int error; 1771 char *my_ip; 1772 struct in_addr addr; 1773 smb_niciter_t ni; 1774 int rc; 1775 char fqhn[MAXHOSTNAMELEN]; 1776 1777 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE)) 1778 return (0); 1779 1780 if (smb_gethostname(fqhn, MAXHOSTNAMELEN, 0) != 0) 1781 return (-1); 1782 1783 (void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn); 1784 error = 0; 1785 1786 if (smb_nic_getfirst(&ni) != 0) 1787 return (-1); 1788 1789 do { 1790 if (ni.ni_nic.nic_sysflags & (IFF_STANDBY | IFF_PRIVATE)) 1791 continue; 1792 1793 addr.s_addr = ni.ni_nic.nic_ip; 1794 my_ip = (char *)strdup(inet_ntoa(addr)); 1795 if (my_ip == NULL) { 1796 error++; 1797 continue; 1798 } 1799 1800 rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL); 1801 if (rc != 0) 1802 error++; 1803 1804 (void) free(my_ip); 1805 } while (smb_nic_getnext(&ni) == 0); 1806 1807 return ((error == 0) ? 0 : -1); 1808 } 1809