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