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