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