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