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