nis_subr.c revision 1219:f89f56c2d9ac
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 */
61static 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 */
70int __nisipbufsize = 8192;
71
72/* Error result returned by nis_make_error() when malloc fails */
73const nis_result	__nomem_nis_result = {NIS_NOMEMORY, {0, 0}, {0, 0},
74						0, 0, 0, 0};
75
76extern int __readColdStartFile();
77
78/*
79 * Static function prototypes.
80 */
81static struct local_names *__get_local_names(void);
82static char *__map_addr(struct netconfig *, char *, rpcprog_t, rpcvers_t);
83
84CLIENT * 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			*/
92static 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
100static 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 */
114int
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
125uint32_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 */
151name_pos
152nis_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
208struct principal_list {
209	uid_t uid;
210	char principal[LN_BUFSIZE];
211	struct principal_list *next;
212};
213
214
215struct 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
223static mutex_t ln_lock = DEFAULTMUTEX; /* lock level 2 */
224static struct local_names *ln = NULL;
225static struct local_names *__get_local_names1();
226
227static 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
238static char *
239get_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
273static 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 */
358nis_name
359nis_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 */
382nis_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 */
430nis_name
431nis_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 */
451nis_name
452nis_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
472static pthread_key_t buf_key;
473static char buf_main[LN_BUFSIZE];
474
475nis_name
476nis_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
496nis_name
497nis_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
546nis_name
547nis_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 */
565nis_name
566nis_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 */
583nis_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
615int
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 */
668nis_name
669nis_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 */
741nis_name
742nis_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 */
763void
764nis_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
772static void
773destroy_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 */
791nis_object *
792nis_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
839nis_object *
840nis_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 */
862char	**
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
933void
934__free_break_name(char **components, int levels)
935{
936	free(components[levels-1]);
937	free(components);
938}
939
940int
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
967int
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
979void
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 */
992nis_result *
993nis_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
1025nis_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 */
1081void
1082nis_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 */
1107nis_error
1108nis_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 */
1301nis_object *
1302nis_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
1333int
1334nis_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 */
1375static 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
1485char *
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
1493int
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
1577full:
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 */
1591static AUTH *
1592create_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
1690CLIENT *
1691nis_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
1704CLIENT *
1705nis_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
1719extern int __can_use_af(sa_family_t af);
1720
1721CLIENT *
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
1777typedef 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 */
1787static CLIENT *
1788nis_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 :
1968auth_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
1993CLIENT *
1994nis_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
2002CLIENT *
2003nis_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
2011static mutex_t __nis_ss_used_lock = DEFAULTMUTEX; /* lock level 3 */
2012int	__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 */
2024void *
2025nis_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
2066char *
2067nis_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
2108char *
2109nis_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
2122char *
2123nis_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
2162char *
2163nis_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
2193static nis_name local_root = 0;
2194static mutex_t local_root_lock = DEFAULTMUTEX;
2195
2196nis_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
2268extern	void __pkey_cache_add(char *, char *, keylen_t, algtype_t);
2269extern	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 */
2277void
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