xref: /titanic_50/usr/src/lib/libnsl/nis/gen/nis_subr.c (revision c77a61a72b5ecdc507d6cf104142edd371a16c84)
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 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This module contains the subroutines used by the server to manipulate
31  * objects and names.
32  */
33 #include "mt.h"
34 #include <pwd.h>
35 #include <grp.h>
36 #include <syslog.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <sys/time.h>
43 #include <sys/fcntl.h>
44 #include <netinet/in.h>
45 #include <rpc/rpc.h>	/* Must be ahead of rpcb_clnt.h */
46 #include <rpc/svc.h>
47 #include <tiuser.h>
48 #include <netconfig.h>
49 #include <netdir.h>
50 #include <rpc/rpcb_clnt.h>
51 #include <rpc/pmap_clnt.h>
52 #include <rpcsvc/nis.h>
53 #include <rpcsvc/nis_dhext.h>
54 #include "nis_clnt.h"
55 #include <sys/systeminfo.h>
56 #include "nis_local.h"
57 #include <nsswitch.h>
58 
59 #define	MAXIPRINT	(11)	/* max length of printed integer */
60 static char    *PKTABLE	  = "cred.org_dir";
61 #define	PKTABLE_LEN	12
62 /*
63  * send and receive buffer size used for clnt_tli_create if not specified.
64  * This is only used for "UDP" connection.
65  * This limit can be changed from the application if this value is too
66  * small for the application.  To use the maximum value for the transport,
67  * set this value to 0.
68  */
69 int __nisipbufsize = 8192;
70 
71 /* Error result returned by nis_make_error() when malloc fails */
72 const nis_result	__nomem_nis_result = {NIS_NOMEMORY, {0, 0}, {0, 0},
73 						0, 0, 0, 0};
74 
75 extern int __readColdStartFile();
76 
77 /*
78  * Static function prototypes.
79  */
80 static struct local_names *__get_local_names(void);
81 static char *__map_addr(struct netconfig *, char *, rpcprog_t, rpcvers_t);
82 
83 CLIENT * nis_make_rpchandle_uaddr(nis_server *,
84 		int, rpcprog_t, rpcvers_t, uint_t, int, int, char *);
85 
86 #define	COMMA	','	/* Avoid cstyle bug */
87 
88 /* __nis_data_directory is READ ONLY, so no locking is needed    */
89 /* Note: We make it static, so external caller can not access it */
90 /*	i.e we make sure it stay read only			*/
91 static char	__nis_data_directory[1024] = {"/var/nis/"};
92 
93 /* These macros make the code easier to read */
94 
95 #ifdef NOTIME
96 #define	__start_clock(n)
97 #define	__stop_clock(n)		n
98 #else
99 static struct timeval clocks[MAXCLOCKS];
100 
101 #define	LOOP_UADDR "127.0.0.1.0.0"
102 
103 /*
104  * __start_clock()
105  *
106  * This function will start the "stopwatch" on the function calls.
107  * It uses an array of time vals to keep track of the time. The
108  * sister call __stop_clock() will return the number of microseconds
109  * since the clock was started. This allows for keeping statistics
110  * on the NIS calls and tuning the service. If the clock in question
111  * is not "stopped" this function returns an error.
112  */
113 int
114 __start_clock(
115 	int	clk)	/* The clock we want to start */
116 {
117 	if ((clk >= MAXCLOCKS) || (clk < 0) || (clocks[clk].tv_sec))
118 		return (FALSE);
119 
120 	(void) gettimeofday(&clocks[clk], NULL);
121 	return (TRUE);
122 }
123 
124 uint32_t
125 __stop_clock(int clk)
126 {
127 	struct timeval 		now;
128 	uint32_t		secs, micros;
129 
130 	if ((clk >= MAXCLOCKS) || (clk < 0) || (!clocks[clk].tv_sec))
131 		return (0);
132 	(void) gettimeofday(&now, NULL);
133 	secs = (int)(now.tv_sec - clocks[clk].tv_sec);
134 	if (now.tv_usec < clocks[clk].tv_usec) {
135 		micros = (int)((now.tv_usec + 1000000) - clocks[clk].tv_usec);
136 		secs--; /* adjusted 'cuz we added a second above */
137 	} else {
138 		micros = (int)(now.tv_usec - clocks[clk].tv_usec);
139 	}
140 	micros = micros + (secs * 1000000); /* All micros now */
141 	clocks[clk].tv_sec = 0; /* Stop the clock. */
142 	return (micros);
143 }
144 #endif /* no time */
145 
146 /*
147  * nis_dir_cmp() -- the results can be read as:
148  * 	"Name 'n1' is a $result than name 'n2'"
149  */
150 name_pos
151 nis_dir_cmp(
152 	nis_name	n1,
153 	nis_name	n2)	/* See if these are the same domain */
154 {
155 	size_t		l1, l2;
156 	name_pos	result;
157 
158 	if ((n1 == NULL) || (n2 == NULL))
159 		return (BAD_NAME);
160 
161 	l1 = strlen(n1);
162 	l2 = strlen(n2);
163 
164 	/* In this routine we're lenient and don't require a trailing '.' */
165 	/*   so we need to ignore it if it does appear.			  */
166 	/* ==== That's what the previous version did so this one does	  */
167 	/*   too, but why?  Is this inconsistent with rest of system?	  */
168 	if (l1 != 0 && n1[l1 - 1] == '.') {
169 		--l1;
170 	}
171 	if (l2 != 0 && n2[l2 - 1] == '.') {
172 		--l2;
173 	}
174 
175 	if (l1 > l2) {
176 		result = LOWER_NAME;
177 	} else if (l1 == l2) {
178 		result = SAME_NAME;
179 	} else /* (l1 < l2); swap l1/l2 and n1/n2 */ {
180 		nis_name	ntmp;
181 		size_t		ltmp;
182 		ntmp = n1; n1 = n2; n2 = ntmp;
183 		ltmp = l1; l1 = l2; l2 = ltmp;
184 
185 		result = HIGHER_NAME;
186 	}
187 
188 	/* Now l1 >= l2 in all cases */
189 	if (l2 == 0) {
190 		/* Special case for n2 == "." or "" */
191 		return (result);
192 	}
193 	if (l1 > l2) {
194 		n1 += l1 - l2;
195 		if (n1[-1] != '.') {
196 			return (NOT_SEQUENTIAL);
197 		}
198 	}
199 	if (strncasecmp(n1, n2, l2) == 0) {
200 		return (result);
201 	}
202 	return (NOT_SEQUENTIAL);
203 }
204 
205 #define	LN_BUFSIZE	(size_t)1024
206 
207 struct principal_list {
208 	uid_t uid;
209 	char principal[LN_BUFSIZE];
210 	struct principal_list *next;
211 };
212 
213 
214 struct local_names {
215 	char domain[LN_BUFSIZE];
216 	char host[LN_BUFSIZE];
217 	char *rpcdomain;
218 	struct principal_list *principal_map;
219 	char group[LN_BUFSIZE];
220 };
221 
222 static mutex_t ln_lock = DEFAULTMUTEX; /* lock level 2 */
223 static struct local_names *ln = NULL;
224 static struct local_names *__get_local_names1();
225 
226 static struct local_names *
227 __get_local_names(void)
228 {
229 	struct local_names *names;
230 
231 	sig_mutex_lock(&ln_lock);
232 	names = __get_local_names1();
233 	sig_mutex_unlock(&ln_lock);
234 	return (names);
235 }
236 
237 static char *
238 get_nis_domain(void)
239 {
240 	directory_obj dobj;
241 	enum __nsw_parse_err pserr;
242 	struct __nsw_switchconfig *conf;
243 	static int checked_domain = 0;
244 	static char *nisdomain = 0;
245 
246 	if (!checked_domain) {
247 		checked_domain = 1;
248 		/*
249 		 * Check that nisplus is first in nsswitch.conf for publickey.
250 		 */
251 		conf = __nsw_getconfig("publickey", &pserr);
252 		if (conf == NULL)
253 			return (NULL);
254 		if (conf->num_lookups <= 0)
255 			return (NULL);
256 		if (strcasecmp(conf->lookups[0].service_name, "nisplus") != 0)
257 			return (NULL);
258 
259 		/*
260 		 * Read cold-start file to determine directory where
261 		 * the machine's credentials are stored.
262 		 */
263 		if (!__readColdStartFile(&dobj))
264 			return (NULL);
265 		nisdomain = strdup(dobj.do_name);
266 		xdr_free((xdrproc_t)xdr_directory_obj, (char *)&dobj);
267 	}
268 
269 	return (nisdomain);
270 }
271 
272 static struct local_names *
273 __get_local_names1(void)
274 {
275 	char		*t;
276 
277 	if (ln != NULL) {
278 		/* Second and subsequent calls go this way */
279 		return (ln);
280 	}
281 	/* First call goes this way */
282 	ln = calloc(1, sizeof (*ln));
283 	if (ln == NULL) {
284 		syslog(LOG_ERR, "__get_local_names: Out of heap.");
285 		return (NULL);
286 	}
287 	ln->principal_map = NULL;
288 
289 	if (sysinfo(SI_SRPC_DOMAIN, ln->domain, LN_BUFSIZE) < 0)
290 		return (ln);
291 	/* If no dot exists, add one. */
292 	if (ln->domain[strlen(ln->domain)-1] != '.')
293 		(void) strcat(ln->domain, ".");
294 	if (sysinfo(SI_HOSTNAME, ln->host, LN_BUFSIZE) < 0)
295 		return (ln);
296 
297 	/*
298 	 * Check for fully qualified hostname.  If it's a fully qualified
299 	 * hostname, strip off the domain part.  We always use the local
300 	 * domainname for the host principal name.
301 	 */
302 	t = strchr(ln->host, '.');
303 	if (t)
304 		*t = 0;
305 	if (ln->domain[0] != '.')
306 		(void) strcat(ln->host, ".");
307 	if ((ln->rpcdomain = get_nis_domain()) != NULL) {
308 		(void) strcat(ln->host, ln->rpcdomain);
309 	} else {
310 		ln->rpcdomain = strdup(ln->domain);
311 		(void) strcat(ln->host, ln->domain);
312 	}
313 
314 	t = getenv("NIS_GROUP");
315 	if (t == NULL) {
316 		ln->group[0] = '\0';
317 	} else {
318 		size_t maxlen = LN_BUFSIZE-1;	/* max chars to copy */
319 		char *temp;			/* temp marker */
320 
321 		/*
322 		 * Copy <= maximum characters from NIS_GROUP; strncpy()
323 		 * doesn't terminate, so we do that manually. #1223323
324 		 * Also check to see if it's "".  If it's the null string,
325 		 * we return because we don't want to add ".domain".
326 		 */
327 		(void) strncpy(ln->group, t, maxlen);
328 		if (strcmp(ln->group, "") == 0) {
329 			return (ln);
330 		}
331 		ln->group[maxlen] = '\0';
332 
333 		/* Is the group name somewhat fully-qualified? */
334 		temp = strrchr(ln->group, '.');
335 
336 		/* If not, we need to add ".domain" to the group */
337 		if ((temp == NULL) || (temp[1] != '\0')) {
338 
339 			/* truncate to make room for ".domain" */
340 			ln->group[maxlen - (strlen(ln->domain)+1)] = '\0';
341 
342 			/* concat '.' if domain doesn't already have it */
343 			if (ln->domain[0] != '.') {
344 				(void) strcat(ln->group, ".");
345 			}
346 			(void) strcat(ln->group, ln->domain);
347 		}
348 	}
349 	return (ln);
350 }
351 
352 /*
353  * nis_local_group()
354  *
355  * Return's the group name of the current user.
356  */
357 nis_name
358 nis_local_group(void)
359 {
360 	struct local_names	*ln = __get_local_names();
361 
362 	/* LOCK NOTE: Warning, after initialization, "ln" is expected	 */
363 	/* to stay constant, So no need to lock here. If this assumption */
364 	/* is changed, this code must be protected.			 */
365 	if (!ln)
366 		return (NULL);
367 	return (ln->group);
368 }
369 
370 /*
371  * __nis_nextsep_of()
372  *
373  * This internal funtion will accept a pointer to a NIS name string and
374  * return a pointer to the next separator occurring in it (it will point
375  * just past the first label).  It allows for labels to be "quoted" to
376  * prevent the the dot character within them to be interpreted as a
377  * separator, also the quote character itself can be quoted by using
378  * it twice.  If the the name contains only one label and no trailing
379  * dot character, a pointer to the terminating NULL is returned.
380  */
381 nis_name
382 __nis_nextsep_of(char *s)
383 {
384 	char	*d;
385 	int	in_quotes = FALSE,
386 		quote_quote = FALSE;
387 
388 	if (!s)
389 		return (NULL);
390 
391 	for (d = s; (in_quotes && (*d != '\0')) ||
392 	    (!in_quotes && (*d != '.') && (*d != '\0')); d++) {
393 		if (quote_quote && in_quotes && (*d != '"')) {
394 			quote_quote = FALSE;
395 			in_quotes = FALSE;
396 			if (*d == '.')
397 				break;
398 		} else if (quote_quote && in_quotes && (*d == '"')) {
399 			quote_quote = FALSE;
400 		} else if (quote_quote && (*d != '"')) {
401 			quote_quote = FALSE;
402 			in_quotes = TRUE;
403 		} else if (quote_quote && (*d == '"')) {
404 			quote_quote = FALSE;
405 		} else if (in_quotes && (*d == '"')) {
406 			quote_quote = TRUE;
407 		} else if (!in_quotes && (*d == '"')) {
408 			quote_quote = TRUE;
409 		}
410 	}
411 
412 	if (quote_quote || in_quotes) {
413 		syslog(LOG_DEBUG, "__nis_nextsep_of: "
414 		    "Mismatched quotes in %s", s);
415 	}
416 
417 	return (d);
418 }
419 
420 /*
421  * nis_domain_of()
422  *
423  * This internal funtion will accept a pointer to a NIS name string and
424  * return a pointer to the "domain" part of it.
425  *
426  * ==== We don't need nis_domain_of_r(), but should we provide one for
427  *	uniformity?
428  */
429 nis_name
430 nis_domain_of(char *s)
431 {
432 	char	*d;
433 
434 	d = __nis_nextsep_of(s);
435 	if (d == NULL)
436 		return (NULL);
437 	if (*d == '.')
438 		d++;
439 	if (*d == '\0')	/* Don't return a zero length string */
440 		return ("."); /* return root domain instead */
441 	return (d);
442 }
443 
444 
445 /*
446  * nis_leaf_of()
447  *
448  * Returns the first label of a name. (other half of __domain_of)
449  */
450 nis_name
451 nis_leaf_of_r(
452 	const nis_name	s,
453 	char		*buf,
454 	size_t		bufsize)
455 {
456 	size_t		nchars;
457 	const char	*d = __nis_nextsep_of((char *)s);
458 
459 	if (d == 0) {
460 		return (0);
461 	}
462 	nchars = d - s;
463 	if (bufsize < nchars + 1) {
464 		return (0);
465 	}
466 	(void) strncpy(buf, s, nchars);
467 	buf[nchars] = '\0';
468 	return (buf);
469 }
470 
471 static pthread_key_t buf_key;
472 static char buf_main[LN_BUFSIZE];
473 
474 nis_name
475 nis_leaf_of(char *s)
476 {
477 	char *buf = thr_main()? buf_main :
478 		thr_get_storage(&buf_key, LN_BUFSIZE, free);
479 
480 	if (buf == NULL)
481 		return (NULL);
482 	return (nis_leaf_of_r(s, buf,  LN_BUFSIZE));
483 }
484 
485 /*
486  * nis_name_of()
487  * This internal function will remove from the NIS name, the domain
488  * name of the current server, this will leave the unique part in
489  * the name this becomes the "internal" version of the name. If this
490  * function returns NULL then the name we were given to resolve is
491  * bad somehow.
492  * NB: Uses static storage and this is a no-no with threads. XXX
493  */
494 
495 nis_name
496 nis_name_of_r(
497 	char	*s,	/* string with the name in it. */
498 	char		*buf,
499 	size_t		bufsize)
500 {
501 	char			*d;
502 	struct local_names 	*ln = __get_local_names();
503 	size_t			dl, sl;
504 	name_pos		p;
505 
506 #ifdef lint
507 	bufsize = bufsize;
508 #endif /* lint */
509 	if ((!s) || (!ln))
510 		return (NULL);		/* No string, this can't continue */
511 
512 	d  = &(ln->domain[0]);
513 	dl = strlen(ln->domain); 	/* _always dot terminated_   */
514 
515 	sl = strlen(s);
516 	if (sl >= bufsize || (s[sl-1] != '.' && sl >= bufsize-1))
517 		return (NULL);
518 	(void) strcpy(buf, s);		/* Make a private copy of 's'   */
519 	if (buf[sl-1] != '.') {	/* Add a dot if necessary.  */
520 		(void) strcat(buf, ".");
521 		sl++;
522 	}
523 
524 	if (dl == 1) {			/* We're the '.' directory   */
525 		buf[sl-1] = '\0';	/* Lose the 'dot'	  */
526 		return (buf);
527 	}
528 
529 	p = nis_dir_cmp(buf, d);
530 
531 	/* 's' is above 'd' in the tree */
532 	if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == SAME_NAME))
533 		return (NULL);
534 
535 	/* Insert a NUL where the domain name starts in the string */
536 	buf[(sl - dl) - 1] = '\0';
537 
538 	/* Don't return a zero length name */
539 	if (buf[0] == '\0')
540 		return (NULL);
541 
542 	return (buf);
543 }
544 
545 nis_name
546 nis_name_of(
547 	char	*s)	/* string with the name in it. */
548 {
549 	char *buf = thr_main()? buf_main :
550 		thr_get_storage(&buf_key, LN_BUFSIZE, free);
551 
552 	if (!buf)
553 		return (NULL);
554 	return (nis_name_of_r(s, buf,  LN_BUFSIZE));
555 }
556 
557 
558 
559 /*
560  * nis_local_directory()
561  *
562  * Return a pointer to a string with the local directory name in it.
563  */
564 nis_name
565 nis_local_directory(void)
566 {
567 	struct local_names	*ln = __get_local_names();
568 
569 	/* LOCK NOTE: Warning, after initialization, "ln" is expected	 */
570 	/* to stay constant, So no need to lock here. If this assumption */
571 	/* is changed, this code must be protected.			 */
572 	if (ln == NULL)
573 		return (NULL);
574 	return (ln->domain);
575 }
576 
577 /*
578  * __nis_rpc_domain()
579  *
580  * Return a pointer to a string with the rpc domain name in it.
581  */
582 nis_name
583 __nis_rpc_domain()
584 {
585 	struct local_names	*ln = __get_local_names();
586 
587 	/* LOCK NOTE: Warning, after initialization, "ln" is expected	 */
588 	/* to stay constant, So no need to lock here. If this assumption */
589 	/* is changed, this code must be protected.			 */
590 	if (ln == NULL)
591 		return (NULL);
592 	return (ln->rpcdomain);
593 }
594 
595 /*
596  * nis_getprincipal:
597  *   Return the prinicipal name of the given uid in string supplied.
598  *   Returns status obtained from nis+.
599  *
600  * Look up the LOCAL mapping in the local cred table. Note that if the
601  * server calls this, then the version of nis_list that will
602  * will be bound here is the 'safe' one in the server code.
603  *
604  * The USE_DGRAM + NO_AUTHINFO is required to prevent a
605  * recursion through the getnetname() interface which is
606  * called by authseccreate_pk and authdes_pk_create().
607  *
608  * NOTE that if you really want to get the nis+ principal name,
609  * you should not use this call.  You should do something similar
610  * but use an authenticated handle.
611  */
612 
613 
614 int
615 __nis_principal(char *principal_name, uid_t uid, char *directory)
616 {
617 	nis_result	*res;
618 	char		buf[NIS_MAXNAMELEN];
619 	int		status;
620 
621 	if ((strlen(directory)+MAXIPRINT+PKTABLE_LEN+32) >
622 		(size_t)NIS_MAXNAMELEN) {
623 		return (NIS_BADNAME);
624 	}
625 
626 	(void) snprintf(buf, sizeof (buf),
627 		"[auth_name=%d,auth_type=LOCAL],%s.%s",
628 		(int)uid, PKTABLE, directory);
629 
630 	if (buf[strlen(buf)-1] != '.')
631 		(void) strcat(buf, ".");
632 
633 	res = nis_list(buf,
634 			USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH,
635 			NULL, NULL);
636 	status = res->status;
637 	if (status == NIS_SUCCESS || status == NIS_S_SUCCESS) {
638 		if (res->objects.objects_len > 1) {
639 			/*
640 			 * More than one principal with same uid?
641 			 * something wrong with cred table. Should be unique
642 			 * Warn user and continue.
643 			 */
644 			syslog(LOG_ERR,
645 		"nis_principal: LOCAL entry for %d in directory %s not unique",
646 				uid, directory);
647 		}
648 		(void) strcpy(principal_name,
649 			ENTRY_VAL(res->objects.objects_val, 0));
650 	}
651 	nis_freeresult(res);
652 
653 	return (status);
654 }
655 
656 /*
657  * nis_local_principal()
658  * Generate the principal name for this user by looking it up its LOCAL
659  * entry in the cred table of the local direectory.
660  * Does not use an authenticated call (to prevent recursion because
661  * this is called by user2netname).
662  *
663  * NOTE: the principal strings returned by nis_local_principal are
664  * never changed and never freed, so there is no need to copy them.
665  * Also note that nis_local_principal can return NULL.
666  */
667 nis_name
668 nis_local_principal(void)
669 {
670 	struct local_names *ln = __get_local_names();
671 	uid_t		uid;
672 	int 		status;
673 	char		*dirname;
674 	static mutex_t local_principal_lock = DEFAULTMUTEX;
675 	struct principal_list *p;
676 
677 	if (ln == NULL)
678 		return (NULL);
679 
680 	sig_mutex_lock(&local_principal_lock);
681 	uid = geteuid();
682 	p = ln->principal_map;
683 	while (p) {
684 		if (p->uid == uid) {
685 			ASSERT(*(p->principal) != 0);
686 			sig_mutex_unlock(&local_principal_lock);
687 			return (p->principal);
688 		}
689 		p = p->next;
690 	}
691 	if (uid == 0) {
692 		sig_mutex_unlock(&local_principal_lock);
693 		return (ln->host);
694 	}
695 	p = calloc(1, sizeof (*p));
696 	if (p == NULL)
697 		return (NULL);
698 	if (!ln->principal_map) {
699 		ln->principal_map = p;
700 	}
701 	dirname = nis_local_directory();
702 	if ((dirname == NULL) || (dirname[0] == NULL)) {
703 		(void) strcpy(p->principal, "nobody");
704 		p->uid = uid;
705 		sig_mutex_unlock(&local_principal_lock);
706 		return (p->principal);
707 	}
708 	switch (status = __nis_principal(p->principal, uid, dirname)) {
709 	case NIS_SUCCESS:
710 	case NIS_S_SUCCESS:
711 		break;
712 	case NIS_NOTFOUND:
713 	case NIS_PARTIAL:
714 	case NIS_NOSUCHNAME:
715 	case NIS_NOSUCHTABLE:
716 		(void) strcpy(p->principal, "nobody");
717 		break;
718 	default:
719 		/*
720 		 * XXX We should return 'nobody', but
721 		 * should we be remembering 'nobody' as our
722 		 * principal name here?  Some errors might be
723 		 * transient.
724 		 */
725 		syslog(LOG_ERR,
726 			"nis_local_principal: %s",
727 			nis_sperrno(status));
728 		(void) strcpy(p->principal, "nobody");
729 	}
730 	p->uid = uid;
731 	sig_mutex_unlock(&local_principal_lock);
732 	return (p->principal);
733 }
734 
735 /*
736  * nis_local_host()
737  * Generate the principal name for this host, "hostname"+"domainname"
738  * unless the hostname already has "dots" in its name.
739  */
740 nis_name
741 nis_local_host(void)
742 {
743 	struct local_names	*ln = __get_local_names();
744 
745 	/* LOCK NOTE: Warning, after initialization, "ln" is expected	 */
746 	/* to stay constant, So no need to lock here. If this assumption */
747 	/* is changed, this code must be protected.			 */
748 	if (ln == NULL)
749 		return (NULL);
750 
751 	return (ln->host);
752 }
753 
754 /*
755  * nis_destroy_object()
756  * This function takes a pointer to a NIS object and deallocates it. This
757  * is the inverse of __clone_object below. It must be able to correctly
758  * deallocate partially allocated objects because __clone_object will call
759  * it if it runs out of memory and has to abort. Everything is freed,
760  * INCLUDING the pointer that is passed.
761  */
762 void
763 nis_destroy_object(nis_object *obj)	/* The object to clone */
764 {
765 	if (obj == 0)
766 		return;
767 	xdr_free(xdr_nis_object, (char *)obj);
768 	free(obj);
769 } /* nis_destroy_object */
770 
771 static void
772 destroy_nis_sdata(void *p)
773 {
774 	struct nis_sdata	*ns = p;
775 
776 	if (ns->buf != 0)
777 		free(ns->buf);
778 	free(ns);
779 }
780 
781 /* XXX Why are these static ? */
782 /* static XDR in_xdrs, out_xdrs; */
783 
784 
785 /*
786  * __clone_object_r()
787  * This function takes a pointer to a NIS object and clones it. This
788  * duplicate object is now available for use in the local context.
789  */
790 nis_object *
791 nis_clone_object_r(
792 	nis_object	*obj,	/* The object to clone */
793 	nis_object	*dest,	/* Use this pointer if non-null */
794 	struct nis_sdata *clone_buf_ptr)
795 {
796 	nis_object	*result; /* The clone itself */
797 	int		status; /* a counter variable */
798 	XDR		in_xdrs, out_xdrs;
799 
800 	if (!nis_get_static_storage(clone_buf_ptr, 1,
801 					    xdr_sizeof(xdr_nis_object, obj)))
802 		return (NULL);
803 
804 	(void) memset(&in_xdrs, 0, sizeof (in_xdrs));
805 	(void) memset(&out_xdrs, 0, sizeof (out_xdrs));
806 	xdrmem_create(&in_xdrs, clone_buf_ptr->buf, clone_buf_ptr->size,
807 			XDR_ENCODE);
808 	xdrmem_create(&out_xdrs, clone_buf_ptr->buf, clone_buf_ptr->size,
809 			XDR_DECODE);
810 
811 	/* Allocate a basic NIS object structure */
812 	if (dest) {
813 		(void) memset(dest, 0, sizeof (nis_object));
814 		result = dest;
815 	} else
816 		result = calloc(1, sizeof (nis_object));
817 
818 	if (result == NULL)
819 		return (NULL);
820 
821 	/* Encode our object into the clone buffer */
822 	(void) xdr_setpos(&in_xdrs, 0);
823 	status = xdr_nis_object(&in_xdrs, obj);
824 	if (status == FALSE)
825 		return (NULL);
826 
827 	/* Now decode the buffer into our result pointer ... */
828 	(void) xdr_setpos(&out_xdrs, 0);
829 	status = xdr_nis_object(&out_xdrs, result);
830 	if (status == FALSE)
831 		return (NULL);
832 
833 	/* presto changeo, a new object */
834 	return (result);
835 } /* __clone_object_r */
836 
837 
838 nis_object *
839 nis_clone_object(
840 	nis_object	*obj,	/* The object to clone */
841 	nis_object	*dest)	/* Use this pointer if non-null */
842 {
843 	static pthread_key_t clone_buf_key;
844 	static struct nis_sdata clone_buf_main;
845 	struct nis_sdata *clone_buf_ptr;
846 
847 	clone_buf_ptr = thr_main()? &clone_buf_main :
848 		thr_get_storage(&clone_buf_key, sizeof (struct nis_sdata),
849 		    destroy_nis_sdata);
850 	return (nis_clone_object_r(obj, dest, clone_buf_ptr));
851 } /* __clone_object */
852 
853 
854 /*
855  * __break_name() converts a NIS name into it's components, returns an
856  * array of char pointers pointing to the components and INVERTS there
857  * order so that they are root first, then down. The list is terminated
858  * with a null pointer. Returned memory can be freed by freeing the last
859  * pointer in the list and the pointer returned.
860  */
861 char	**
862 __break_name(
863 	nis_name	name,
864 	int		*levels)
865 {
866 	char	**pieces;	/* pointer to the pieces */
867 	char	*s;		/* Temporary */
868 	char	*data;		/* actual data and first piece pointer. */
869 	int	components;	/* Number of estimated components */
870 	size_t	namelen;	/* Length of the original name. */
871 	int 	i;
872 
873 	/* First check to see that name is not NULL */
874 	if (!name)
875 		return (NULL);
876 	if ((namelen = strlen(name)) == 0)
877 		return (NULL);	/* Null string */
878 
879 	namelen = strlen(name);
880 
881 	data = strdup(name);
882 	if (!data)
883 		return (NULL);	/* No memory! */
884 
885 	/* Kill the optional trailing dot */
886 	if (*(data+namelen-1) == '.') {
887 		*(data+namelen-1) = '\0';
888 		namelen--;
889 	}
890 	s = data;
891 	components = 1;
892 	while (*s != '\0') {
893 		if (*s == '.') {
894 			*s = '\0';
895 			components++;
896 			s++;
897 		} else if (*s == '"') {
898 			if (*(s+1) == '"') { /* escaped quote */
899 				s += 2;
900 			} else {
901 				/* skip quoted string */
902 				s++;
903 				while ((*s != '"') && (*s != '\0'))
904 					s++;
905 				if (*s == '"') {
906 					s++;
907 				}
908 			}
909 		} else {
910 			s++;
911 		}
912 	}
913 	pieces = calloc(components+1, sizeof (char *));
914 	if (!pieces) {
915 		free(data);
916 		return (NULL);
917 	}
918 
919 	/* store in pieces in inverted order */
920 	for (i = (components-1), s = data; i > -1; i--) {
921 		*(pieces+i) = s;
922 		while (*s != '\0')
923 			s++;
924 		s++;
925 	}
926 	*(pieces+components) = NULL;
927 	*levels = components;
928 
929 	return (pieces);
930 }
931 
932 void
933 __free_break_name(char **components, int levels)
934 {
935 	free(components[levels-1]);
936 	free(components);
937 }
938 
939 int
940 __name_distance(
941 	char	**targ,	/* The target name */
942 	char	**test) /* the test name */
943 {
944 	int	distance = 0;
945 
946 	/* Don't count common components */
947 	while ((*targ && *test) && (strcasecmp(*targ, *test) == 0)) {
948 		targ++;
949 		test++;
950 	}
951 
952 	/* count off the legs of each name */
953 	while (*test != NULL) {
954 		test++;
955 		distance++;
956 	}
957 
958 	while (*targ != NULL) {
959 		targ++;
960 		distance++;
961 	}
962 
963 	return (distance);
964 }
965 
966 int
967 __dir_same(char **test, char **targ)
968 {
969 	/* skip common components */
970 	while ((*targ && *test) && (strcasecmp(*targ, *test) == 0)) {
971 		targ++;
972 		test++;
973 	}
974 
975 	return (*test == NULL && *targ == NULL);
976 }
977 
978 void
979 __broken_name_print(char **name, int levels)
980 {
981 	int i;
982 
983 	for (i = levels-1; i >= 0; --i)
984 		(void) printf("%s.", name[i]);
985 }
986 
987 
988 /*
989  * For returning errors in a NIS result structure
990  */
991 nis_result *
992 nis_make_error(
993 	nis_error	err,
994 	uint32_t	aticks,	/* Profile information for client */
995 	uint32_t	cticks,
996 	uint32_t	dticks,
997 	uint32_t	zticks)
998 {
999 	nis_result	*nres;
1000 
1001 	nres = malloc(sizeof (nis_result));
1002 	if (!nres)
1003 		return ((nis_result *)&__nomem_nis_result);
1004 	(void) memset(nres, 0, sizeof (nis_result));
1005 	nres->status = err;
1006 	nres->aticks = aticks;
1007 	nres->zticks = zticks;
1008 	nres->dticks = dticks;
1009 	nres->cticks = cticks;
1010 	return (nres);
1011 }
1012 
1013 #define	ZVAL zattr_val.zattr_val_val
1014 #define	ZLEN zattr_val.zattr_val_len
1015 
1016 /*
1017  * __cvt2attr()
1018  *
1019  * This function converts a search criteria of the form :
1020  *	[ <key>=<value>, <key>=<value>, ... ]
1021  * Into an array of nis_attr structures.
1022  */
1023 
1024 nis_attr *
1025 __cvt2attr(
1026 	int	*na, 		/* Number of attributes 	*/
1027 	char	**attrs) 	/* Strings associated with them */
1028 
1029 {
1030 	int		i;
1031 	nis_attr	*zattrs;
1032 	char		*s;
1033 
1034 	zattrs = calloc(*na, sizeof (nis_attr));
1035 	if (!zattrs)
1036 		return (NULL);
1037 
1038 	for (i = 0; i < *na; i++) {
1039 		zattrs[i].zattr_ndx = *(attrs+i);
1040 		for (s = zattrs[i].zattr_ndx; *s != '\0'; s++) {
1041 			if (*s == '=') {
1042 				*s = '\0';
1043 				s++;
1044 				zattrs[i].ZVAL = s;
1045 				zattrs[i].ZLEN = (uint_t)strlen(s) + 1;
1046 				break;
1047 			} else if (*s == '"') {
1048 				/* advance s to matching quote */
1049 				s++;
1050 				while ((*s != '"') && (*s != '\0'))
1051 					s++;
1052 				if (*s == '\0') {
1053 					/* unterminated quote */
1054 					free(zattrs);
1055 					return (NULL);
1056 				}
1057 			}
1058 		}
1059 		/*
1060 		 * POLICY : Missing value for an index name is an
1061 		 *	    error. The other alternative is the missing
1062 		 *	    value means "is present" unfortunately there
1063 		 *	    is no standard "is present" indicator in the
1064 		 *	    existing databases.
1065 		 * ANSWER : Always return an error.
1066 		 */
1067 		if (!zattrs[i].ZVAL) {
1068 			free(zattrs);
1069 			return (NULL);
1070 		}
1071 	}
1072 	return (zattrs);
1073 }
1074 
1075 /*
1076  * nis_free_request()
1077  *
1078  * Free memory associated with a constructed list request.
1079  */
1080 void
1081 nis_free_request(ib_request *req)
1082 {
1083 	if (req->ibr_srch.ibr_srch_len) {
1084 		/* free the string memory */
1085 		free(req->ibr_srch.ibr_srch_val[0].zattr_ndx);
1086 		/* free the nis_attr array */
1087 		free(req->ibr_srch.ibr_srch_val);
1088 	}
1089 
1090 	if (req->ibr_name)
1091 		free(req->ibr_name);
1092 }
1093 
1094 /*
1095  * nis_get_request()
1096  *
1097  * This function takes a NIS name, and converts it into an ib_request
1098  * structure. The request can then be used in a call to the nis service
1099  * functions. If the name wasn't parseable it returns an appropriate
1100  * error. This function ends up allocating an array of nis_attr structures
1101  * and a duplicate of the name string passed. To free this memory you
1102  * can call nis_free_request(), or you can simply free the first nis_attr
1103  * zattr_ndx pointer (the string memory) and the nis_attr pointer which
1104  * is the array.
1105  */
1106 nis_error
1107 nis_get_request(
1108 	nis_name	name,		/* search criteria + Table name	*/
1109 	nis_object	*obj,		/* Object for (rem/modify/add)	*/
1110 	netobj		*cookie,	/* Pointer to a cookie		*/
1111 	ib_request	*req)		/* Request structure to fill in */
1112 {
1113 	char	*s, *t; 		/* Some string pointer temps */
1114 	char	*p;			/* temp var */
1115 	char	**attr;			/* Intermediate attributes */
1116 	int	i;			/* Counter variable */
1117 	char		*data;		/* pointer to malloc'd string */
1118 	int		zn = 0;		/* Count of attributes		*/
1119 	size_t datalen;			/* length of malloc'd data	*/
1120 	char		namebuf[NIS_MAXNAMELEN];
1121 
1122 	uchar_t		within_attr_val;
1123 				/*
1124 				 * a boolean to indicate the current parse
1125 				 * location is within the attribute value
1126 				 * - so that we can stop deleting white
1127 				 * space within an attribute value
1128 				 */
1129 
1130 	(void) memset(req, 0, sizeof (ib_request));
1131 
1132 	/*
1133 	 * if we're passed an object but no name, use the name from
1134 	 * the object instead.
1135 	 */
1136 	if (obj && !name) {
1137 		if ((strlen(obj->zo_name)+strlen(obj->zo_domain)+2) >
1138 			sizeof (namebuf)) {
1139 			return (NIS_BADNAME);
1140 		}
1141 		(void) snprintf(namebuf, sizeof (namebuf),
1142 					"%s.%s", obj->zo_name, obj->zo_domain);
1143 		name = namebuf;
1144 	}
1145 	if (!name || (name[0] == '\0'))
1146 		return (NIS_BADNAME);
1147 
1148 	s = name;
1149 
1150 	/* Move to the start of the components */
1151 	while (isspace(*s))
1152 		s++;
1153 
1154 	if (*s == '[') {
1155 
1156 		s++; /* Point past the opening bracket */
1157 
1158 		datalen = strlen(s);
1159 		data = calloc(1, datalen+1);
1160 		if (!data)
1161 			return (NIS_NOMEMORY);
1162 
1163 		t = data; /* Point to the databuffer */
1164 		while ((*s != '\0') && (*s != ']')) {
1165 			while (isspace(*s)) {
1166 				s++;
1167 			}
1168 			/* Check to see if we finished off the string */
1169 			if ((*s == '\0') || (*s == ']'))
1170 				break;
1171 
1172 			/* If *s == comma its a null criteria */
1173 			if (*s == COMMA) {
1174 				s++;
1175 				continue;
1176 			}
1177 			/* Not a space and not a comma, process an attr */
1178 			zn++;
1179 			within_attr_val = 0; /* not within attr_val right now */
1180 			while ((*s != COMMA) && (*s != ']') && (*s != '\0')) {
1181 				if (*s == '"') {
1182 					if (*(s+1) == '"') { /* escaped quote */
1183 						*t++ = *s; /* copy one quote */
1184 						s += 2;
1185 					} else {
1186 						/* skip quoted string */
1187 						s++;
1188 						while ((*s != '"') &&
1189 							(*s != '\0'))
1190 							*t++ = *s++;
1191 						if (*s == '"') {
1192 							s++;
1193 						}
1194 					}
1195 				} else if (*s == '=') {
1196 					*t++ = *s++;
1197 					within_attr_val = 1;
1198 				} else if (isspace(*s) && !within_attr_val) {
1199 					s++;
1200 				} else
1201 					*t++ = *s++;
1202 			}
1203 			*t++ = '\0'; /* terminate the attribute */
1204 			if (*s == COMMA)
1205 				s++;
1206 		}
1207 		if (*s == '\0') {
1208 			free(data);
1209 			return (NIS_BADATTRIBUTE);
1210 		}
1211 
1212 		/* It wasn't a '\0' so it must be the closing bracket. */
1213 		s++;
1214 		/* Skip any intervening white space and "comma" */
1215 		while (isspace(*s) || (*s == COMMA)) {
1216 			s++;
1217 		}
1218 		/* Copy the name into our malloc'd buffer */
1219 		(void) strcpy(t, s);
1220 
1221 		/*
1222 		 * If we found any attributes we process them, the
1223 		 * data string at this point is completely nulled
1224 		 * out except for attribute data. We recover this
1225 		 * data by scanning the string (we know how long it
1226 		 * is) and saving to each chunk of non-null data.
1227 		 */
1228 		if (zn) {
1229 			/* Save this as the table name */
1230 			req->ibr_name = strdup(t);
1231 			attr = calloc(zn+1, sizeof (char *));
1232 			if (!attr) {
1233 				free(data);
1234 				free(req->ibr_name);
1235 				req->ibr_name = 0;
1236 				return (NIS_NOMEMORY);
1237 			}
1238 
1239 			/* store in pieces in attr array */
1240 			for (i = 0, s = data; i < zn; i++) {
1241 				*(attr+i) = s;
1242 				/* Advance s past this component */
1243 				while (*s != '\0')
1244 					s++;
1245 				s++;
1246 			}
1247 			*(attr+zn) = NULL;
1248 		} else {
1249 			free(data);
1250 			req->ibr_name = strdup(s);
1251 		}
1252 	} else {
1253 		/* Null search criteria */
1254 		req->ibr_name = strdup(s);
1255 		data = NULL;
1256 	}
1257 
1258 	if (zn) {
1259 		req->ibr_srch.ibr_srch_len = zn;
1260 		req->ibr_srch.ibr_srch_val = __cvt2attr(&zn, attr);
1261 		free(attr); /* don't need this any more */
1262 		if (!(req->ibr_srch.ibr_srch_val)) {
1263 			req->ibr_srch.ibr_srch_len = 0;
1264 			free(req->ibr_name);
1265 			req->ibr_name = 0;
1266 			free(data);
1267 			return (NIS_BADATTRIBUTE);
1268 		}
1269 	}
1270 
1271 	/* check for correct quotes in ibr_name (but leave them in) */
1272 	for (p = req->ibr_name; *p; p++) {
1273 		if (*p == '"') {
1274 			/* advance p to the matching quote */
1275 			p++;
1276 			while (*p != '"' && *p != '\0') {
1277 				p++;
1278 			}
1279 			if (*p == '\0') {
1280 				req->ibr_srch.ibr_srch_len = 0;
1281 				free(req->ibr_name);
1282 				req->ibr_name = 0;
1283 				free(data);
1284 				return (NIS_BADNAME);
1285 			}
1286 		}
1287 	}
1288 
1289 	if (obj) {
1290 		req->ibr_obj.ibr_obj_len = 1;
1291 		req->ibr_obj.ibr_obj_val = obj;
1292 	}
1293 	if (cookie) {
1294 		req->ibr_cookie = *cookie;
1295 	}
1296 	return (NIS_SUCCESS);
1297 }
1298 
1299 /* Various subroutines used by the server code */
1300 nis_object *
1301 nis_read_obj(char *f)	/* name of the object to read */
1302 {
1303 	FILE	*rootfile;
1304 	int	status;	/* Status of the XDR decoding */
1305 	XDR	xdrs;	/* An xdr stream handle */
1306 	nis_object	*res;
1307 
1308 	res = calloc(1, sizeof (nis_object));
1309 	if (!res)
1310 		return (NULL);
1311 
1312 	rootfile = fopen(f, "rF");
1313 	if (rootfile == NULL) {
1314 		/* This is ok if we are the root of roots. */
1315 		free(res);
1316 		return (NULL);
1317 	}
1318 	/* Now read in the object */
1319 	xdrstdio_create(&xdrs, rootfile, XDR_DECODE);
1320 	status = xdr_nis_object(&xdrs, res);
1321 	xdr_destroy(&xdrs);
1322 	(void) fclose(rootfile);
1323 	if (!status) {
1324 		syslog(LOG_ERR, "Object file %s is corrupt!", f);
1325 		xdr_free(xdr_nis_object, (char *)res);
1326 		free(res);
1327 		return (NULL);
1328 	}
1329 	return (res);
1330 }
1331 
1332 int
1333 nis_write_obj(
1334 	char	*f,	/* name of the object to read */
1335 	nis_object *o)	/* The object to write */
1336 {
1337 	FILE	*rootfile;
1338 	int	status;	/* Status of the XDR decoding */
1339 	XDR	xdrs;	/* An xdr stream handle */
1340 
1341 	rootfile = fopen(f, "wF");
1342 	if (rootfile == NULL) {
1343 		return (0);
1344 	}
1345 	/* Now encode the object */
1346 	xdrstdio_create(&xdrs, rootfile, XDR_ENCODE);
1347 	status = xdr_nis_object(&xdrs, o);
1348 	xdr_destroy(&xdrs);
1349 	(void) fclose(rootfile);
1350 	return (status);
1351 }
1352 
1353 /*
1354  * nis_make_rpchandle()
1355  *
1356  * This is a generic version of clnt_creat() for NIS. It localizes
1357  * _all_ of the changes needed to port to TLI RPC into this one
1358  * section of code.
1359  */
1360 
1361 /*
1362  * Transport INDEPENDENT RPC code. This code assumes you
1363  * are using the new RPC/tli code and will build
1364  * a ping handle on top of a datagram transport.
1365  */
1366 
1367 /*
1368  * __map_addr()
1369  *
1370  * This is our internal function that replaces rpcb_getaddr(). We
1371  * build our own to prevent calling netdir_getbyname() which could
1372  * recurse to the nameservice.
1373  */
1374 static char *
1375 __map_addr(
1376 	struct netconfig	*nc,		/* Our transport	*/
1377 	char			*uaddr,		/* RPCBIND address */
1378 	rpcprog_t		prog,		/* Name service Prog */
1379 	rpcvers_t		ver)
1380 {
1381 	CLIENT *client;
1382 	RPCB 		parms;		/* Parameters for RPC binder	  */
1383 	enum clnt_stat	clnt_st;	/* Result from the rpc call	  */
1384 	char 		*ua = NULL;	/* Universal address of service	  */
1385 	char		*res = NULL;	/* Our result to the parent	  */
1386 	struct timeval	tv;		/* Timeout for our rpcb call	  */
1387 	int		ilen, olen;	/* buffer length for clnt_tli_create */
1388 
1389 	/*
1390 	 * If using "udp", use __nisipbufsize if inbuf and outbuf are set to 0.
1391 	 */
1392 	if (strcmp(NC_UDP, nc->nc_proto) == 0) {
1393 			/* for udp only */
1394 		ilen = olen = __nisipbufsize;
1395 	} else {
1396 		ilen = olen = 0;
1397 	}
1398 	client = __nis_clnt_create(RPC_ANYFD, nc, uaddr, 0, 0,
1399 					RPCBPROG, RPCBVERS, ilen, olen);
1400 	if (!client)
1401 		return (NULL);
1402 
1403 	(void) clnt_control(client, CLSET_FD_CLOSE, NULL);
1404 
1405 	/*
1406 	 * Now make the call to get the NIS service address.
1407 	 * We set the retry timeout to 3 seconds so that we
1408 	 * will retry a few times.  Retries should be rare
1409 	 * because we are usually only called when we know
1410 	 * a server is available.
1411 	 */
1412 	tv.tv_sec = 3;
1413 	tv.tv_usec = 0;
1414 	(void) clnt_control(client, CLSET_RETRY_TIMEOUT, (char *)&tv);
1415 
1416 	tv.tv_sec = 10;
1417 	tv.tv_usec = 0;
1418 	parms.r_prog = prog;
1419 	parms.r_vers = ver;
1420 	parms.r_netid = nc->nc_netid;	/* not needed */
1421 	parms.r_addr = "";	/* not needed; just for xdring */
1422 	parms.r_owner = "";	/* not needed; just for xdring */
1423 	clnt_st = clnt_call(client, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms,
1424 					    xdr_wrapstring, (char *)&ua, tv);
1425 
1426 	if (clnt_st == RPC_SUCCESS) {
1427 		clnt_destroy(client);
1428 		if (*ua == '\0') {
1429 			free(ua);
1430 			return (NULL);
1431 		}
1432 		res = strdup(ua);
1433 		xdr_free(xdr_wrapstring, (char *)&ua);
1434 		return (res);
1435 	} else if (((clnt_st == RPC_PROGVERSMISMATCH) ||
1436 			(clnt_st == RPC_PROGUNAVAIL)) &&
1437 			(strcmp(nc->nc_protofmly, NC_INET) == 0)) {
1438 		/*
1439 		 * version 3 not available. Try version 2
1440 		 * The assumption here is that the netbuf
1441 		 * is arranged in the sockaddr_in
1442 		 * style for IP cases.
1443 		 *
1444 		 * Note:	If the remote host doesn't support version 3,
1445 		 *		we assume it doesn't know IPv6 either.
1446 		 */
1447 		ushort_t 		port;
1448 		struct sockaddr_in	*sa;
1449 		struct netbuf 		remote;
1450 		int			protocol;
1451 		char			buf[32];
1452 
1453 		(void) clnt_control(client, CLGET_SVC_ADDR, (char *)&remote);
1454 		/* LINTED pointer cast */
1455 		sa = (struct sockaddr_in *)(remote.buf);
1456 		protocol = strcmp(nc->nc_proto, NC_TCP) ?
1457 				IPPROTO_UDP : IPPROTO_TCP;
1458 		port = (ushort_t)pmap_getport(sa, prog, ver, protocol);
1459 
1460 		if (port != 0) {
1461 			port = htons(port);
1462 			(void) sprintf(buf, "%d.%d.%d.%d.%d.%d",
1463 				(sa->sin_addr.s_addr >> 24) & 0xff,
1464 				(sa->sin_addr.s_addr >> 16) & 0xff,
1465 				(sa->sin_addr.s_addr >>  8) & 0xff,
1466 				(sa->sin_addr.s_addr) & 0xff,
1467 				(port >> 8) & 0xff,
1468 				port & 0xff);
1469 			res = strdup(buf);
1470 		} else
1471 			res = NULL;
1472 		clnt_destroy(client);
1473 		return (res);
1474 	}
1475 	if (clnt_st == RPC_TIMEDOUT)
1476 		syslog(LOG_ERR, "NIS+ server not responding");
1477 	else
1478 		syslog(LOG_ERR, "NIS+ server could not be contacted: %s",
1479 					clnt_sperrno(clnt_st));
1480 	clnt_destroy(client);
1481 	return (NULL);
1482 }
1483 
1484 char *
1485 __nis_get_server_address(struct netconfig *nc, endpoint *ep)
1486 {
1487 	return (__map_addr(nc, ep->uaddr, NIS_PROG, NIS_VERSION));
1488 }
1489 
1490 #define	MAX_EP (20)
1491 
1492 int
1493 __nis_get_callback_addresses(endpoint *ep, endpoint **ret_eps)
1494 {
1495 	int i;
1496 	int n;
1497 	int st;
1498 	int nep = 0;
1499 	endpoint *eps;
1500 	struct nd_hostserv hs;
1501 	struct nd_addrlist *addrs;
1502 	struct nd_mergearg ma;
1503 	void *lh;
1504 	void *nch;
1505 	struct netconfig *nc;
1506 
1507 	eps = malloc(MAX_EP * sizeof (endpoint));
1508 	if (eps == 0)
1509 		return (0);
1510 
1511 	hs.h_host = HOST_SELF;
1512 	hs.h_serv = "rpcbind";	/* as good as any */
1513 
1514 	lh = __inet_get_local_interfaces();
1515 
1516 	nch = setnetconfig();
1517 	while ((nc = getnetconfig(nch)) != NULL) {
1518 		if (strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0)
1519 			continue;
1520 		if (nc->nc_semantics != NC_TPI_COTS &&
1521 		    nc->nc_semantics != NC_TPI_COTS_ORD)
1522 			continue;
1523 		st = netdir_getbyname(nc, &hs, &addrs);
1524 		if (st != 0)
1525 			continue;
1526 
1527 		/*
1528 		 *  The netdir_merge code does not work very well
1529 		 *  for inet if the client and server are not
1530 		 *  on the same network.  Instead, we try each local
1531 		 *  address.
1532 		 *
1533 		 *  For other protocol families and for servers on a
1534 		 *  local network, we use the regular merge code.
1535 		 */
1536 
1537 		if (strcmp(nc->nc_protofmly, NC_INET) == 0 &&
1538 		    !__inet_uaddr_is_local(lh, nc, ep->uaddr)) {
1539 			n = __inet_address_count(lh);
1540 			for (i = 0; i < n; i++) {
1541 				if (nep >= MAX_EP) {
1542 					syslog(LOG_INFO,
1543 		"__nis_get_callback_addresses: too many endpoints");
1544 					goto full;
1545 				}
1546 				eps[nep].uaddr = __inet_get_uaddr(lh, nc, i);
1547 				if (eps[nep].uaddr == 0)
1548 					continue;
1549 				if (strcmp(eps[nep].uaddr, LOOP_UADDR) == 0) {
1550 					free(eps[nep].uaddr);
1551 					continue;
1552 				}
1553 				__nis_netconfig2ep(nc, &(eps[nep]));
1554 				nep++;
1555 			}
1556 		} else {
1557 			ma.s_uaddr = ep->uaddr;
1558 			ma.c_uaddr = taddr2uaddr(nc, addrs->n_addrs);
1559 			ma.m_uaddr = 0;
1560 			(void) netdir_options(nc, ND_MERGEADDR, 0, (void *)&ma);
1561 			free(ma.s_uaddr);
1562 			free(ma.c_uaddr);
1563 
1564 			if (nep >= MAX_EP) {
1565 					syslog(LOG_INFO,
1566 		"__nis_get_callback_addresses: too many endpoints");
1567 				goto full;
1568 			}
1569 			eps[nep].uaddr = ma.m_uaddr;
1570 			__nis_netconfig2ep(nc, &(eps[nep]));
1571 			nep++;
1572 		}
1573 		netdir_free((void *)addrs, ND_ADDRLIST);
1574 	}
1575 
1576 full:
1577 	(void) endnetconfig(nch);
1578 	__inet_free_local_interfaces(lh);
1579 
1580 	*ret_eps = eps;
1581 	return (nep);
1582 }
1583 
1584 /*
1585  * Try to create a RPC GSS security context (flavor RPCSEC_GSS).
1586  * Returns auth handle on success, else NULL.  Set flag 'try_auth_des'
1587  * to TRUE if the AUTH_DES compat line is found in the security conf file
1588  * or no valid mech entries are found in the conf file.
1589  */
1590 static AUTH *
1591 create_rpcgss_secctx(
1592 	CLIENT		*clnt,		/* out */
1593 	nis_server	*srv,
1594 	char 		*gss_svc,
1595 	bool_t		*try_auth_des)	/* out */
1596 {
1597 	mechanism_t		**mechs;	/* list of mechanisms	*/
1598 
1599 	*try_auth_des = FALSE;
1600 	if (mechs = __nis_get_mechanisms(TRUE)) {
1601 		mechanism_t **mpp;
1602 		char svc_name[NIS_MAXNAMELEN+1] = {0};
1603 
1604 		/* Check RPC GSS service name buf size. */
1605 		if ((strlen(gss_svc ? gss_svc : NIS_SVCNAME_NISD) + 1
1606 			+ strlen(srv->name) + 1) > sizeof (svc_name)) {
1607 			syslog(LOG_ERR,
1608 		"nis_make_rpchandle_gss_svc: RPC GSS service name too long");
1609 			__nis_release_mechanisms(mechs);
1610 			return (NULL);
1611 		}
1612 
1613 		/* RPC GSS service names are of the form svc@host.dom */
1614 		(void) snprintf(svc_name, sizeof (svc_name),
1615 				"%s@%s", gss_svc ? gss_svc : NIS_SVCNAME_NISD,
1616 				srv->name);
1617 
1618 		/*
1619 		 * Loop thru all the available mech entries until an
1620 		 * RPC GSS security context is established or until
1621 		 * the AUTH_DES compat entry is found.
1622 		 */
1623 		for (mpp = mechs; *mpp; mpp++) {
1624 			mechanism_t *mp = *mpp;
1625 
1626 			if (AUTH_DES_COMPAT_CHK(mp)) {
1627 				__nis_release_mechanisms(mechs);
1628 				*try_auth_des = TRUE;
1629 				return (NULL);
1630 			}
1631 
1632 			if (!VALID_MECH_ENTRY(mp)) {
1633 				syslog(LOG_ERR,
1634 					"%s: invalid mechanism entry name '%s'",
1635 					NIS_SEC_CF_PATHNAME,
1636 					mp->mechname ? mp->mechname : "NULL");
1637 				continue;
1638 			}
1639 
1640 			/*
1641 			 * If the mechanism is of the public key crypto
1642 			 * technology variety, let's make sure the server's
1643 			 * public key exists and the clients secret key is set
1644 			 * before going thru the expense of a RPC GSS security
1645 			 * context creation attempt.
1646 			 */
1647 			if (MECH_PK_TECH(mp) &&
1648 				((srv->key_type == NIS_PK_DHEXT &&
1649 					!__nis_dhext_extract_pkey(&(srv->pkey),
1650 					mp->keylen,  mp->algtype)) ||
1651 					!key_secretkey_is_set_g(mp->keylen,
1652 							mp->algtype))) {
1653 #ifdef DHEXT_DEBUG
1654 					(void) fprintf(stderr,
1655 "nis_make_rpchandle_gss_svc: srv keytype = %d: No keys, skip mech '%s' ...\n",
1656 							srv->key_type,
1657 							mp->alias);
1658 #endif
1659 					continue;
1660 			}
1661 
1662 			clnt->cl_auth = rpc_gss_seccreate(clnt, svc_name,
1663 						mp->mechname, mp->secserv,
1664 						mp->qop, NULL, NULL);
1665 			if (clnt->cl_auth) {
1666 				__nis_release_mechanisms(mechs);
1667 				return (clnt->cl_auth); /* we're in bizness */
1668 #ifdef DHEXT_DEBUG
1669 			} else {
1670 				rpc_gss_error_t	err;
1671 
1672 				rpc_gss_get_error(&err);
1673 				(void) fprintf(stderr,
1674 "nis_make_rpchandle_gss_svc: RPCGSS_SecCreat fail: gerr = %d serr = %d\n",
1675 					err.rpc_gss_error, err.system_error);
1676 #endif /* DHEXT_DEBUG */
1677 			}
1678 		}
1679 		__nis_release_mechanisms(mechs);
1680 	} else {
1681 		/* no valid mechs, fallback to AUTH_DES */
1682 		*try_auth_des = TRUE;
1683 	}
1684 
1685 	return (NULL);
1686 }
1687 
1688 
1689 CLIENT *
1690 nis_make_rpchandle(
1691 	nis_server	*srv,	/* NIS Server description 		*/
1692 	int		cback,	/* Boolean indicating callback address	*/
1693 	rpcprog_t	prog,	/* Program number			*/
1694 	rpcvers_t	ver,	/* Version				*/
1695 	uint_t		flags,	/* Flags, {VC, DG, AUTH}  		*/
1696 	int		inbuf,	/* Preferred buffer sizes 		*/
1697 	int		outbuf)	/* for input and output   		*/
1698 {
1699 	return (nis_make_rpchandle_uaddr(srv, cback, prog, ver, flags,
1700 			inbuf, outbuf, 0));
1701 }
1702 
1703 CLIENT *
1704 nis_make_rpchandle_uaddr(
1705 	nis_server	*srv,	/* NIS Server description 		*/
1706 	int		cback,	/* Boolean indicating callback address	*/
1707 	rpcprog_t	prog,	/* Program number			*/
1708 	rpcvers_t	ver,	/* Version				*/
1709 	uint_t		flags,	/* Flags, {VC, DG, AUTH}  		*/
1710 	int		inbuf,	/* Preferred buffer sizes 		*/
1711 	int		outbuf,	/* for input and output   		*/
1712 	char		*uaddr)	/* optional address of server		*/
1713 {
1714 	return (nis_make_rpchandle_gss_svc(srv, cback, prog, ver, flags,
1715 			inbuf, outbuf, uaddr, NULL));
1716 }
1717 
1718 extern int __can_use_af(sa_family_t af);
1719 
1720 CLIENT *
1721 __nis_clnt_create(int fd, struct netconfig *nc, char *uaddr,
1722 			struct netbuf *addr, int domapaddr,
1723 			int prog, int ver, int inbuf, int outbuf) {
1724 
1725 	char		*svc_addr;
1726 	CLIENT		*clnt;
1727 	int		freeaddr = 0;
1728 
1729 	/* Sanity check */
1730 	if (nc == 0 || (addr == 0 && uaddr == 0)) {
1731 		return (0);
1732 	}
1733 
1734 	/*
1735 	 * Check if we have a useable interface for this address family.
1736 	 * This check properly belongs in RPC (or even further down),
1737 	 * but until they provide it, we roll our own.
1738 	 */
1739 	if (__can_use_af((strcmp(nc->nc_protofmly, NC_INET6) == 0) ?
1740 			AF_INET6 : AF_INET) == 0) {
1741 		return (0);
1742 	}
1743 
1744 	if (domapaddr) {
1745 		svc_addr = __map_addr(nc, uaddr, prog, ver);
1746 		if (svc_addr == 0)
1747 			return (0);
1748 		addr = uaddr2taddr(nc, svc_addr);
1749 		freeaddr = 1;
1750 		free(svc_addr);
1751 	} else if (addr == 0) {
1752 		addr = uaddr2taddr(nc, uaddr);
1753 		freeaddr = 1;
1754 	}
1755 
1756 	if (addr == 0) {
1757 		return (0);
1758 	}
1759 
1760 	clnt = clnt_tli_create(fd, nc, addr, prog, ver, outbuf, inbuf);
1761 
1762 	if (clnt) {
1763 		if (clnt_control(clnt, CLGET_FD, (char *)&fd))
1764 			/* make it "close on exec" */
1765 			(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
1766 		(void) clnt_control(clnt, CLSET_FD_CLOSE, NULL);
1767 	}
1768 
1769 	if (freeaddr)
1770 		netdir_free(addr, ND_ADDR);
1771 
1772 	return (clnt);
1773 }
1774 
1775 
1776 typedef struct {
1777 	endpoint		*ep;
1778 	struct netconfig	*nc;
1779 } alt_ep_t;
1780 
1781 /*
1782  * Construct an rpc handle.
1783  *
1784  * If the gss_svc arg is NULL, then default to "nisd" (rpc.nisd).
1785  */
1786 static CLIENT *
1787 nis_make_rpchandle_gss_svc_ext(
1788 	nis_server	*srv,	  /* NIS Server description 		*/
1789 	int		cback,	  /* Boolean indicating callback address */
1790 	rpcprog_t	prog,	  /* Program number			*/
1791 	rpcvers_t	ver,	  /* Version				*/
1792 	uint_t		flags,	  /* Flags, {VC, DG, AUTH}  		*/
1793 	int		inbuf,	  /* Preferred buffer sizes 		*/
1794 	int		outbuf,	  /* for input and output   		*/
1795 	char		*uaddr,	  /* optional address of server		*/
1796 	char		*gss_svc, /* RPC GSS service name		*/
1797 	int		use_realid) /* 1: Use REAL id, 0: use Eff. ids	*/
1798 {
1799 	CLIENT			*clnt = 0;	/* Client handle 	*/
1800 	void			*nc_handle;	/* Netconfig "state"	*/
1801 	struct netconfig	*nc;		/* Various handles	*/
1802 	endpoint		*ep;		/* useful endpoints	*/
1803 	int			epl, i;		/* counters		*/
1804 	int			uid, gid;	/* Effective uid/gid	*/
1805 	char			netname[MAXNETNAMELEN+1]; /* our netname */
1806 	char			*hexkey = NULL; /* hex public key for DHEXT */
1807 	netobj			xpkey = { NULL, 0};
1808 	bool_t			try_auth_des;
1809 	alt_ep_t		*altep = 0;
1810 
1811 
1812 	nc_handle = (void *) setnetconfig();
1813 	if (!nc_handle)
1814 		return (NULL);
1815 
1816 	ep = srv->ep.ep_val;
1817 	epl = srv->ep.ep_len;
1818 
1819 	if (uaddr) {
1820 
1821 		char	*fmly = (strchr(uaddr, ':') == 0) ? NC_INET : NC_INET6;
1822 
1823 		while ((nc = getnetconfig(nc_handle)) != NULL) {
1824 			/* Is it a visible transport ? */
1825 			if ((nc->nc_flag & NC_VISIBLE) == 0)
1826 				continue;
1827 			/* Does the protocol family match the uaddr ? */
1828 			if (strcmp(nc->nc_protofmly, fmly) != 0)
1829 				continue;
1830 			for (i = 0; i < epl; i++) {
1831 				if (__nis_netconfig_matches_ep(nc, &ep[i])) {
1832 					break;
1833 				}
1834 			}
1835 			/* Did we find a matching endpoint ? */
1836 			if (i < epl)
1837 				break;
1838 		}
1839 		if (nc == 0) {
1840 			syslog(LOG_ERR,
1841 	"nis_make_rpchandle: can't find netconfig entry for %s, %s",
1842 				uaddr, fmly);
1843 			return (0);
1844 		}
1845 
1846 		clnt = __nis_clnt_create(RPC_ANYFD, nc, uaddr, 0, 0, prog, ver,
1847 					inbuf, outbuf);
1848 
1849 	} else {
1850 
1851 		altep = calloc(epl, sizeof (*altep));
1852 
1853 		/*
1854 		 * The transport policies :
1855 		 * Selected transport must be visible.
1856 		 * Must have requested or better semantics.
1857 		 * Must be correct protocol.
1858 		 */
1859 		while ((nc = getnetconfig(nc_handle)) != 0) {
1860 
1861 			/* Is it a visible transport ? */
1862 			if ((nc->nc_flag & NC_VISIBLE) == 0)
1863 				continue;
1864 
1865 			/* If we asked for a virtual circuit, is it ? */
1866 			if (((flags & ZMH_VC) != 0) &&
1867 			    (nc->nc_semantics != NC_TPI_COTS) &&
1868 			    (nc->nc_semantics != NC_TPI_COTS_ORD))
1869 				continue;
1870 
1871 			/* Check to see is we talk this protofmly, protocol */
1872 			for (i = 0; i < epl; i++) {
1873 				if (__nis_netconfig_matches_ep(nc, &(ep[i])))
1874 					break;
1875 			}
1876 
1877 			/* Was it one of our transports ? */
1878 			if (i == epl)
1879 				continue;	/* No */
1880 
1881 			/*
1882 			 * If it is one of our supported transports, but isn't
1883 			 * a datagram and we want a datagram, keep looking but
1884 			 * remember this one as a possibility.
1885 			 */
1886 			if (((flags & ZMH_DG) != 0) &&
1887 				(nc->nc_semantics != NC_TPI_CLTS) &&
1888 					altep != 0) {
1889 				altep[i].nc = nc;
1890 				altep[i].ep = &ep[i]; /* This endpoint */
1891 				continue;
1892 			}
1893 
1894 			/* We've got a candidate; see if it works */
1895 			clnt = __nis_clnt_create(RPC_ANYFD, nc, ep[i].uaddr, 0,
1896 						(cback == 0), prog, ver, inbuf,
1897 						outbuf);
1898 
1899 			if (clnt != 0)
1900 				break;
1901 		}
1902 
1903 		if (altep != 0 && (!(flags & ZMH_NOFALLBACK))) {
1904 			/* If primary choices failed, try the alternates */
1905 			for (i = 0; clnt == 0 && i < epl; i++) {
1906 				if (altep[i].ep == 0)
1907 					continue;
1908 				clnt = __nis_clnt_create(RPC_ANYFD,
1909 					altep[i].nc, altep[i].ep->uaddr, 0,
1910 					(cback == 0), prog, ver, inbuf,
1911 					outbuf);
1912 			}
1913 			free(altep);
1914 		}
1915 
1916 	}
1917 
1918 	/* Done with the netconfig handle regardless */
1919 	(void) endnetconfig(nc_handle);
1920 
1921 	/* If we still don't have a client handle, we're sunk */
1922 	if (clnt == 0) {
1923 		return (0);
1924 	}
1925 
1926 	/*
1927 	 * No auth requested or it's a callback (which is not authenticated),
1928 	 * so we're done.
1929 	 */
1930 	if (!(flags & ZMH_AUTH) || cback)
1931 		return (clnt);
1932 
1933 	/*
1934 	 * Setup authentication.  Try the RPCSEC_GSS flavor first, then
1935 	 * fallback to AUTH_DES (if requested) and, if need be, AUTH_SYS.
1936 	 */
1937 	if (create_rpcgss_secctx(clnt, srv, gss_svc, &try_auth_des))
1938 		return (clnt);
1939 
1940 	if (!try_auth_des)
1941 		/* XXXX what's the meaning of going into a switch stmt??? */
1942 		goto auth_sys;
1943 
1944 	switch (srv->key_type) {
1945 	case NIS_PK_DHEXT :
1946 		/*
1947 		 * We're doing AUTH_DES, but the server might
1948 		 * have multiple keys so let's get the 192-0 one.
1949 		 */
1950 		if ((hexkey = __nis_dhext_extract_pkey(&(srv->pkey),
1951 							192, 0)) == NULL)
1952 			goto auth_sys;
1953 		xpkey.n_len = strlen(hexkey) + 1;
1954 		xpkey.n_bytes = hexkey;
1955 		/*FALLTHROUGH*/
1956 	case NIS_PK_DH :
1957 		(void) host2netname(netname, srv->name, NULL);
1958 		clnt->cl_auth = (AUTH *)authdes_pk_seccreate(netname,
1959 					xpkey.n_len ? &xpkey : &(srv->pkey),
1960 					15, NULL, NULL, srv);
1961 		if (xpkey.n_len)
1962 			free(xpkey.n_bytes);
1963 		if (clnt->cl_auth)
1964 			break;
1965 		/*FALLTHROUGH*/
1966 	case NIS_PK_NONE :
1967 auth_sys:
1968 	uid = use_realid ? getuid() : geteuid();
1969 	gid = use_realid ? getgid() : getegid();
1970 
1971 	clnt->cl_auth = authsys_create(nis_local_host(), uid, gid, 0, NULL);
1972 	if (clnt->cl_auth)
1973 		break;
1974 	/*FALLTHROUGH*/
1975 	default :
1976 		clnt->cl_auth = authnone_create();
1977 		if (clnt->cl_auth)
1978 			break;
1979 		syslog(LOG_CRIT,
1980 			"nis_make_rpchandle_uaddr: cannot create cred.");
1981 		abort();
1982 		break;
1983 	}
1984 
1985 	if (clnt->cl_auth)
1986 		return (clnt);
1987 
1988 	clnt_destroy(clnt);
1989 	return (NULL);
1990 }
1991 
1992 CLIENT *
1993 nis_make_rpchandle_gss_svc(nis_server *srv, int cback, rpcprog_t prog,
1994     rpcvers_t ver, uint_t flags, int inbuf, int outbuf, char *uaddr,
1995     char *gss_svc)
1996 {
1997 	return (nis_make_rpchandle_gss_svc_ext(srv, cback, prog, ver, flags,
1998 	    inbuf, outbuf, uaddr, gss_svc, 0));
1999 }
2000 
2001 CLIENT *
2002 nis_make_rpchandle_gss_svc_ruid(nis_server *srv, int cback, rpcprog_t prog,
2003     rpcvers_t ver, uint_t flags, int inbuf, int outbuf, char *uaddr,
2004     char *gss_svc)
2005 {
2006 	return (nis_make_rpchandle_gss_svc_ext(srv, cback, prog, ver, flags,
2007 	    inbuf, outbuf, uaddr, gss_svc, 1));
2008 }
2009 
2010 static mutex_t __nis_ss_used_lock = DEFAULTMUTEX; /* lock level 3 */
2011 int	__nis_ss_used = 0;
2012 
2013 /*
2014  * nis_get_static_storage()
2015  *
2016  * This function is used by various functions in their effort to minimize the
2017  * hassles of memory management in an RPC daemon. Because the service doesn't
2018  * implement any hard limits, this function allows people to get automatically
2019  * growing buffers that meet their storage requirements. It returns the
2020  * pointer in the nis_sdata structure.
2021  *
2022  */
2023 void *
2024 nis_get_static_storage(
2025 	struct nis_sdata 	*bs, 	/* User buffer structure */
2026 	uint_t			el,	/* Sizeof elements	 */
2027 	uint_t			nel)	/* Number of elements	 */
2028 {
2029 	uint_t	sz;
2030 
2031 	sz = nel * el;
2032 	if (!bs)
2033 		return (NULL);
2034 
2035 	if (!bs->buf) {
2036 		bs->buf = malloc(sz);
2037 		if (!bs->buf)
2038 			return (NULL);
2039 		bs->size = sz;
2040 		sig_mutex_lock(&__nis_ss_used_lock);
2041 		__nis_ss_used += sz;
2042 		sig_mutex_unlock(&__nis_ss_used_lock);
2043 	} else if (bs->size < sz) {
2044 		int 	size_delta;
2045 
2046 		free(bs->buf);
2047 		size_delta = - (bs->size);
2048 		bs->buf = malloc(sz);
2049 
2050 		/* check the result of malloc() first	*/
2051 		/* then update the statistic.		*/
2052 		if (!bs->buf)
2053 			return (NULL);
2054 		bs->size = sz;
2055 		size_delta += sz;
2056 		sig_mutex_lock(&__nis_ss_used_lock);
2057 		__nis_ss_used += size_delta;
2058 		sig_mutex_unlock(&__nis_ss_used_lock);
2059 	}
2060 
2061 	(void) memset(bs->buf, 0, sz); /* SYSV version of bzero() */
2062 	return (bs->buf);
2063 }
2064 
2065 char *
2066 nis_old_data_r(
2067 	char	*s,
2068 	struct nis_sdata	*bs_ptr)
2069 {
2070 	char			*buf;
2071 	char			temp[1024];
2072 	size_t			len = 0;
2073 
2074 	buf = (char *)nis_get_static_storage(bs_ptr, 1, 1024);
2075 
2076 	if (!buf)
2077 		return (NULL);
2078 
2079 	/*
2080 	 * this saving of 's' is because the routines that call nis_data()
2081 	 * are not very careful about what they pass in.  Sometimes what they
2082 	 * pass in are 'static' returned from some of the routines called
2083 	 * below nis_leaf_of(),  nis_local_host() and so on.
2084 	 */
2085 	if (s) {
2086 		len = strlen(s) + 1;
2087 		if (len >= sizeof (temp))
2088 			return (NULL);
2089 		(void) snprintf(temp, sizeof (temp), "/%s", s);
2090 	}
2091 	if (len + strlen(__nis_data_directory) +
2092 		strlen(nis_leaf_of(nis_local_host())) >= bs_ptr->size)
2093 		return (NULL);
2094 	(void) strcpy(buf, __nis_data_directory);
2095 	(void) strcat(buf, nis_leaf_of(nis_local_host()));
2096 	if (s)
2097 		(void) strcat(buf, temp);
2098 
2099 	for (s = buf; *s; s++) {
2100 		if (isupper(*s))
2101 			*s = tolower(*s);
2102 	}
2103 
2104 	return (buf);
2105 }
2106 
2107 char *
2108 nis_old_data(char *s)
2109 {
2110 	static pthread_key_t 	bs_key;
2111 	static struct nis_sdata	bs_main;
2112 	struct nis_sdata	*bs_ptr;
2113 
2114 	bs_ptr = thr_main()? &bs_main :
2115 		thr_get_storage(&bs_key, sizeof (struct nis_sdata),
2116 		    destroy_nis_sdata);
2117 	return (nis_old_data_r(s, bs_ptr));
2118 }
2119 
2120 
2121 char *
2122 nis_data_r(char *s, struct nis_sdata *bs_ptr)
2123 {
2124 	char			*buf;
2125 	char			temp[1024];
2126 	size_t			len = 0;
2127 
2128 	buf = (char *)nis_get_static_storage(bs_ptr, 1, 1024);
2129 
2130 	if (!buf)
2131 		return (NULL);
2132 
2133 	/*
2134 	 * this saving of 's' is because the routines that call nis_data()
2135 	 * are not very careful about what they pass in.  Sometimes what they
2136 	 * pass in are 'static' returned from some of the routines called
2137 	 * below nis_leaf_of(),  nis_local_host() and so on.
2138 	 */
2139 	if (s) {
2140 		len = strlen(s) + 1;
2141 		if (len >= sizeof (temp))
2142 			return (NULL);
2143 		(void) snprintf(temp, sizeof (temp), "/%s", s);
2144 	}
2145 	if (len + strlen(__nis_data_directory) +
2146 		strlen(NIS_DIR) >= bs_ptr->size)
2147 		return (NULL);
2148 	(void) strcpy(buf, __nis_data_directory);
2149 	(void) strcat(buf, NIS_DIR);
2150 	if (s)
2151 		(void) strcat(buf, temp);
2152 
2153 	for (s = buf; *s; s++) {
2154 		if (isupper(*s))
2155 			*s = tolower(*s);
2156 	}
2157 
2158 	return (buf);
2159 }
2160 
2161 char *
2162 nis_data(char *s)
2163 {
2164 	static pthread_key_t 	bs_key;
2165 	static struct nis_sdata	bs_main;
2166 	struct nis_sdata	*bs_ptr;
2167 
2168 	bs_ptr = thr_main()? &bs_main :
2169 		thr_get_storage(&bs_key, sizeof (struct nis_sdata),
2170 		    destroy_nis_sdata);
2171 	return (nis_data_r(s, bs_ptr));
2172 }
2173 
2174 /*
2175  * Return the directory name of the root_domain of the caller's NIS+
2176  * domain.
2177  *
2178  * This routine is a temporary implementation and should be
2179  * provided as part of the the NIS+ project.  See RFE:  1103216
2180  * Required for root replication.
2181  *
2182  * XXX MT safing: local_root_lock protects the local_root structure.
2183  *
2184  * It tries to determine the root domain
2185  * name by "walking" the path up the NIS+ directory tree, starting
2186  * at nis_local_directory() until a NIS_NOSUCHNAME or NIS_NOTFOUND error
2187  * is obtained.  Returns 0 on fatal errors obtained before this point,
2188  * or if it exhausts the domain name without ever obtaining one of
2189  * of these errors.
2190  */
2191 
2192 static nis_name local_root = 0;
2193 static mutex_t local_root_lock = DEFAULTMUTEX;
2194 
2195 nis_name
2196 __nis_local_root(void)
2197 {
2198 	char *dir;
2199 	int found_root = 0;
2200 	int try_count = 0;
2201 	int fatal_error = 0;
2202 	char *prev_testdir;
2203 	char *testdir;
2204 
2205 	sig_mutex_lock(&local_root_lock);
2206 	if (local_root) {
2207 		sig_mutex_unlock(&local_root_lock);
2208 		return (local_root);
2209 	}
2210 	local_root = calloc(1, LN_BUFSIZE);
2211 
2212 	if (!local_root) {
2213 		sig_mutex_unlock(&local_root_lock);
2214 		return (0);
2215 	}
2216 	/*  walk up NIS+ tree till we find the root. */
2217 	dir = strdup(__nis_rpc_domain());
2218 	prev_testdir = dir;
2219 	testdir = nis_domain_of(prev_testdir);
2220 
2221 	while (testdir && !found_root && !fatal_error) {
2222 	    /* try lookup */
2223 	    nis_result* nis_ret = nis_lookup(testdir, 0);
2224 	    /* handle return status */
2225 	    switch (nis_ret->status) {
2226 	    case NIS_SUCCESS:
2227 	    case NIS_S_SUCCESS:
2228 		try_count = 0;
2229 		prev_testdir = testdir;
2230 		testdir = nis_domain_of(prev_testdir);
2231 		break;
2232 	    case NIS_NOSUCHNAME:
2233 	    case NIS_NOTFOUND:
2234 	    case NIS_NOT_ME:
2235 	    case NIS_FOREIGNNS:
2236 		found_root = 1;
2237 		break;
2238 	    case NIS_TRYAGAIN:
2239 	    case NIS_CACHEEXPIRED:
2240 		/* sleep 1 second and try same name again, up to 10 times */
2241 		/* REMIND: This is arbitrary! BAD! */
2242 		(void) sleep(1);
2243 		fatal_error = (try_count++ > 9);
2244 		break;
2245 	    case NIS_NAMEUNREACHABLE:
2246 	    case NIS_SYSTEMERROR:
2247 	    case NIS_RPCERROR:
2248 	    case NIS_NOMEMORY:
2249 	    default:
2250 		fatal_error = 1;
2251 		break;
2252 	    }
2253 	    if (nis_ret) nis_freeresult(nis_ret);
2254 	}
2255 
2256 	if (!found_root) {
2257 		free(dir);
2258 		sig_mutex_unlock(&local_root_lock);
2259 		return (0);
2260 	}
2261 	(void) strcpy(local_root, prev_testdir);
2262 	free(dir);
2263 	sig_mutex_unlock(&local_root_lock);
2264 	return (local_root);
2265 }
2266 
2267 extern	void __pkey_cache_add(char *, char *, keylen_t, algtype_t);
2268 extern	int bin2hex(int, unsigned char *, char *);
2269 
2270 /*
2271  * __nis_cache_server_pkeys
2272  *
2273  * Add the public keys for the servers of the directory object to the
2274  * per-process public key cache.
2275  */
2276 void
2277 __nis_cache_server_pkeys(directory_obj *dir) {
2278 
2279 	int		i;
2280 	nis_server	*srv;
2281 	char		netname[MAXNETNAMELEN+1];
2282 	char		pkey[MAX_NETOBJ_SZ+1];
2283 	extdhkey_t	*key;
2284 	uint_t		s;
2285 
2286 	if (dir == NULL)
2287 		return;
2288 
2289 	for (i = 0; i < dir->do_servers.do_servers_len; i++) {
2290 
2291 		srv = &(dir->do_servers.do_servers_val[i]);
2292 
2293 		switch (srv->key_type) {
2294 		case NIS_PK_DH:
2295 			if (srv->pkey.n_len < sizeof (pkey) &&
2296 				host2netname(netname, srv->name, NULL)) {
2297 				(void) memcpy(pkey, srv->pkey.n_bytes,
2298 					srv->pkey.n_len);
2299 				pkey[srv->pkey.n_len] = '\0';
2300 				__pkey_cache_add(netname, pkey, 192, 0);
2301 			}
2302 			break;
2303 		case NIS_PK_DHEXT:
2304 			if (!host2netname(netname, srv->name, NULL))
2305 				break;
2306 			for (s = 0; s < srv->pkey.n_len; ) {
2307 				keylen_t	k, kpadlen;
2308 				algtype_t	a;
2309 				/* LINTED pointer cast */
2310 				key = (extdhkey_t *)&(srv->pkey.n_bytes[s]);
2311 				k = ntohs(key->keylen);
2312 				if (k == 0)
2313 					break;
2314 				kpadlen = ((((k+7)/8)+3)/4)*4;
2315 				a = ntohs(key->algtype);
2316 				if (kpadlen <= sizeof (pkey)) {
2317 					(void) bin2hex((k+7)/8, key->key, pkey);
2318 					__pkey_cache_add(netname, pkey, k, a);
2319 				}
2320 				s += 2*sizeof (ushort_t) + kpadlen;
2321 			}
2322 			break;
2323 		default:
2324 			break;
2325 		}
2326 
2327 	}
2328 }
2329