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