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