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