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