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