1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 /*
31  * This contains miscellaneous functions moved from commands to the library.
32  */
33 
34 #include "mt.h"
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <syslog.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <gssapi/gssapi.h>
41 #include <rpc/rpc.h>
42 #include <rpcsvc/nis.h>
43 #include <rpcsvc/nis_dhext.h>
44 #include <rpc/auth.h>
45 #include <rpc/auth_sys.h>
46 #include <rpc/auth_des.h>
47 #include <rpc/key_prot.h>
48 #include <netdir.h>
49 #include <netconfig.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <netdb.h>
53 #include <dlfcn.h>
54 #include <gssapi/gssapi.h>
55 #include "nis_local.h"
56 
57 extern int bin2hex(int len, unsigned char *binnum, char *hexnum);
58 extern int hex2bin(int len, char *hexnum, char *binnum);
59 
60 /*
61  * Returns the NIS principal name of the person making the request
62  * XXX This is set up to use Secure RPC only at the moment, it should
63  * be possible for any authentication scheme to be incorporated if it
64  * has a "full name" that we can return as the principal name.
65  */
66 static const nis_name nobody = "nobody";
67 
68 static NIS_HASH_TABLE credtbl;
69 struct creditem {
70 	NIS_HASH_ITEM	item;
71 	char	pname[1024];
72 };
73 
74 static void
75 add_cred_item(char *netname, char *pname)
76 {
77 	struct creditem *foo = NULL, *old = NULL;
78 
79 	if (strlen(pname) >= sizeof (foo->pname)) {
80 		syslog(LOG_ERR,
81 		"add_cred_item: principal name too long '%s'",
82 				pname);
83 		return;
84 	}
85 
86 	old = (struct creditem *)nis_find_item(netname, &credtbl);
87 	if (old != NULL)
88 		return;
89 
90 	foo = calloc(1, sizeof (struct creditem));
91 	if (foo == NULL)
92 		return;
93 
94 	foo->item.name = strdup(netname);
95 	if (foo->item.name == NULL) {
96 		free(foo);
97 		return;
98 	}
99 
100 	(void) strcpy(foo->pname, pname);
101 	(void) nis_insert_item((NIS_HASH_ITEM *)foo, &credtbl);
102 }
103 
104 static bool_t
105 find_cred_item(char *netname, char *pname)
106 {
107 	struct creditem	*old = NULL;
108 
109 	if (strlen(pname) >= sizeof (old->pname))
110 		return (FALSE);
111 
112 	old = (struct creditem *)nis_find_item(netname, &credtbl);
113 	if (old == NULL)
114 		return (FALSE);
115 	(void) strcpy(pname, old->pname);
116 	return (TRUE);
117 }
118 
119 static bool_t
120 delete_cred_item(char *netname)
121 {
122 	struct creditem *toremove = NULL;
123 
124 	if (toremove = (struct creditem *)nis_remove_item(netname,
125 	    &credtbl)) {
126 		free(toremove->item.name);
127 		free(toremove);
128 		return (TRUE);
129 	} else
130 		return (FALSE);
131 }
132 
133 void
134 __nis_auth2princ(
135 	char *name,
136 	int flavor,
137 	caddr_t auth,
138 	bool_t refresh,
139 	int verbose)
140 {
141 	struct authsys_parms	*au;
142 	struct authdes_cred	*ad;
143 	char			*rmtdomain;
144 	char			srch[2048]; /* search criteria */
145 	nis_result		*res;
146 
147 	srch[0] = '\0';
148 
149 	(void) strcpy(name, nobody); /* default is "nobody" */
150 	if (flavor == AUTH_NONE) {
151 		if (verbose) {
152 			syslog(LOG_INFO,
153 		    "__nis_auth2princ: flavor = NONE: returning '%s'", nobody);
154 		}
155 		return;
156 	} else if (flavor == AUTH_SYS) { /* XXX ifdef this for 4.1 */
157 		/* LINTED pointer cast */
158 		au = (struct authsys_parms *)(auth);
159 		rmtdomain = nis_domain_of(au->aup_machname);
160 		if (au->aup_uid == 0) {
161 			(void) snprintf(name, MAX_MACHINE_NAME,
162 							"%s", au->aup_machname);
163 			if (!rmtdomain)
164 				(void) strcat(name, __nis_rpc_domain());
165 			if (name[strlen(name) - 1] != '.')
166 				(void) strcat(name, ".");
167 			if (verbose) {
168 				syslog(LOG_INFO,
169 		    "__nis_auth2princ: flavor = SYS: returning '%s'", name);
170 			}
171 			return;
172 		}
173 		(void) snprintf(srch,
174 				sizeof (srch) - 1,
175 		    "[auth_name=\"%d\", auth_type=LOCAL], cred.org_dir.%s",
176 				(int)au->aup_uid, (*rmtdomain == '.') ?
177 				(char *)nis_local_directory() : rmtdomain);
178 		if (srch[strlen(srch) - 1] != '.') {
179 			(void) strcat(srch, ".");
180 		}
181 	} else if (flavor == AUTH_DES) {
182 		/* LINTED pointer cast */
183 		ad = (struct authdes_cred *)(auth);
184 		if (refresh)
185 			(void) delete_cred_item(ad->adc_fullname.name);
186 		else
187 			if (find_cred_item(ad->adc_fullname.name, name)) {
188 				if (verbose)
189 					syslog(LOG_INFO,
190 		"__nis_auth2princ: flavor = DES: returning from cache '%s'",
191 					name);
192 				return;
193 			}
194 
195 		rmtdomain = strchr(ad->adc_fullname.name, '@');
196 		if (rmtdomain) {
197 			rmtdomain++;
198 			(void) snprintf(srch,
199 					sizeof (srch) - 1,
200 			    "[auth_name=%s, auth_type=DES], cred.org_dir.%s",
201 					ad->adc_fullname.name, rmtdomain);
202 			if (srch[strlen(srch) - 1] != '.') {
203 				(void) strcat(srch, ".");
204 			}
205 		} else {
206 			if (verbose) {
207 				syslog(LOG_INFO,
208 			    "__nis_auth2princ: flavor = DES: returning '%s'",
209 					nobody);
210 			}
211 			return;
212 		}
213 	} else {
214 		syslog(LOG_WARNING,
215 		"__nis_auth2princ: flavor = %d(unknown): returning '%s'",
216 							flavor, nobody);
217 		return;
218 	}
219 	if (verbose)
220 		syslog(LOG_INFO,
221 			"__nis_auth2princ: calling list with name '%s'",
222 							name);
223 	res = nis_list(srch, NO_AUTHINFO+USE_DGRAM+FOLLOW_LINKS, NULL, NULL);
224 	if (res->status != NIS_SUCCESS) {
225 		if (verbose)
226 			syslog(LOG_INFO,
227 				"__nis_auth2princ: error doing nis_list: %s",
228 						nis_sperrno(res->status));
229 	} else {
230 		(void) strncpy(name, ENTRY_VAL(res->objects.objects_val, 0),
231 				1024);
232 		if (flavor == AUTH_DES)
233 			add_cred_item(ad->adc_fullname.name, name);
234 	}
235 
236 	nis_freeresult(res);
237 	if (verbose)
238 		syslog(LOG_INFO,
239 		"__nis_auth2princ: flavor = %s: returning : '%s'",
240 			flavor == AUTH_SYS? "SYS" : "DES", name);
241 }
242 
243 #define	MECH_LIB_PREFIX1	"/usr/lib/"
244 
245 #ifdef  _LP64
246 
247 #define	MECH_LIB_PREFIX2	"64/"
248 
249 #else   /* _LP64 */
250 
251 #define	MECH_LIB_PREFIX2	""
252 
253 #endif  /* _LP64 */
254 
255 #define	MECH_LIB_DIR		"gss/"
256 
257 #define	MECH_LIB_PREFIX MECH_LIB_PREFIX1 MECH_LIB_PREFIX2
258 
259 #define	MECHDH		MECH_LIB_PREFIX MECH_LIB_DIR "mech_dh.so.1"
260 #define	LIBGSS		MECH_LIB_PREFIX "libgss.so.1"
261 
262 static gss_OID_desc __dh_gss_c_nt_netname = {
263 	9,  "\053\006\004\001\052\002\032\001\001"
264 };
265 
266 mutex_t gss_load_lock = DEFAULTMUTEX;
267 static gss_OID GSS_EXPORT_NAME = 0;
268 static gss_OID DH_NETNAME = &__dh_gss_c_nt_netname;
269 
270 typedef OM_uint32 (*gss_fptr)();
271 OM_uint32 (*g_import_name)();
272 OM_uint32 (*g_display_name)();
273 OM_uint32 (*g_release_name)();
274 OM_uint32 (*g_release_buffer)();
275 OM_uint32 (*g_release_oid)();
276 
277 /*
278  * gss_OID_load()
279  *
280  * This routine is called by __nis_gssprin2netname to define values for
281  * the gss-api-export-name OID, the Diffie-Hellman netname OID, and
282  * the gss support routines that it needs.
283  * The reason for this support routine is that libnsl cannot have an
284  * explicit dependency on libgss. Callers of __nisgssprin2netname are
285  * expected to have loaded libgss through the rpcsec layer. The work around
286  * is to dlopen the needed shared objects and grab the symbols with dlsym.
287  * This routine opens libgss RTLD_NOLOAD. If this fails then libgss.so.1
288  * is not loaded and we return error. Otherwise it uses dlsym to
289  * defines GSS_EXPORT_NAME to have the value of GSS_C_NT_EXPORT_NAME and
290  * to assign the above fuction pointers.
291  * If this succeeds then the routine will attempt to load mech_dh.so.1
292  * and over ride DH_NETNAME with the value of __DH_GSS_C_NT_NETNAME from
293  * that shared object. We don't consider it an error if this fails because
294  * its conceivable that another mechanism backend will support the netname
295  * name type and mech_dh.so.1 not be available.
296  *
297  * Return 0 on failer, 1 on success.
298  */
299 
300 static int
301 gss_OID_load()
302 {
303 	void *dh;
304 	gss_OID *OIDptr;
305 	int stat = 0;
306 
307 	(void) mutex_lock(&gss_load_lock);
308 	if (GSS_EXPORT_NAME) {
309 		(void) mutex_unlock(&gss_load_lock);
310 		return (0);
311 	}
312 
313 	/* if LIBGSS is not loaded return an error */
314 	if ((dh = dlopen(LIBGSS, RTLD_NOLOAD)) == NULL) {
315 		(void) mutex_unlock(&gss_load_lock);
316 		return (0);
317 	}
318 
319 	OIDptr = (gss_OID *)dlsym(dh, "GSS_C_NT_EXPORT_NAME");
320 	if (OIDptr)
321 		GSS_EXPORT_NAME = *OIDptr;
322 	else
323 		goto Done;
324 
325 	g_import_name = (gss_fptr)dlsym(dh, "gss_import_name");
326 	if (g_import_name == 0)
327 		goto Done;
328 
329 	g_display_name = (gss_fptr)dlsym(dh, "gss_display_name");
330 	if (g_display_name == 0)
331 		goto Done;
332 
333 	g_release_name = (gss_fptr)dlsym(dh, "gss_release_name");
334 	if (g_release_name == 0)
335 		goto Done;
336 
337 	g_release_buffer = (gss_fptr)dlsym(dh, "gss_release_buffer");
338 	if (g_release_buffer == 0)
339 		goto Done;
340 
341 	g_release_oid = (gss_fptr)dlsym(dh, "gss_release_oid");
342 	if (g_release_oid == 0)
343 		goto Done;
344 
345 	stat = 1;
346 	/*
347 	 * Try and get the official netname oid from mech_dh.so.
348 	 * If this fails will just keep our default from above.
349 	 */
350 
351 	if ((dh = dlopen(MECHDH, RTLD_LAZY)) != NULL) {
352 
353 		OIDptr = (gss_OID *)dlsym(dh, "__DH_GSS_C_NT_NETNAME");
354 		if (OIDptr)
355 			DH_NETNAME = *OIDptr;
356 	}
357 
358 Done:
359 	(void) mutex_unlock(&gss_load_lock);
360 
361 	if (stat == 0)
362 		GSS_EXPORT_NAME = 0;
363 
364 	return (stat);
365 }
366 
367 
368 /*
369  * int
370  * __nis_gssprin2netname(rpc_gss_principal_t prin,
371  *			 char netname[MAXNETNAMELEN+1])
372  *
373  * This routine attempts to extract the netname from an rpc_gss_principal_t
374  * which is in { gss-api-exorted-name } format. Return 0 if a netname was
375  * found, else return -1.
376  */
377 
378 /*
379  * This routine has a dependency on libgss.so. So we will pragma weak
380  * the interfaces that we need. When this routine is called libgss
381  * should have been loaded by the rpcsec layer. We will call gss_OID_load
382  * to get the value for GSS_EXPORT_NAME. If gss_OID_load failes return -1.
383  */
384 
385 #define	OID_IS_EQUAL(o1, o2) ((o1) && (o2) && \
386 	((o1)->length == (o2)->length) && \
387 	(memcmp((o1)->elements, (o2)->elements, (o1)->length) == 0))
388 
389 int
390 __nis_gssprin2netname(rpc_gss_principal_t prin, char netname[MAXNETNAMELEN+1])
391 {
392 	gss_buffer_desc display_name;
393 	gss_name_t name;
394 	gss_OID name_type;
395 	gss_buffer_desc expName;
396 	int stat = -1;
397 	OM_uint32 major, minor;
398 
399 	/* See if we already got the OID */
400 	if (GSS_EXPORT_NAME == 0) {
401 		/* Nope. See if GSS is loaded and get the OIDs */
402 		if (!gss_OID_load())
403 			return (-1);	/* if libgss.so.1 isn't loaded */
404 	}
405 
406 	expName.length = prin->len;
407 	expName.value = prin->name;
408 
409 	major = (*g_import_name)(&minor, &expName,
410 				(gss_OID) GSS_EXPORT_NAME, &name);
411 
412 	if (major == GSS_S_COMPLETE) {
413 		major = (*g_display_name)(&minor, name,
414 				    &display_name, &name_type);
415 
416 		/* We're done with the gss_internal name */
417 		(void) (*g_release_name)(&minor, &name);
418 
419 		if (major == GSS_S_COMPLETE) {
420 			/*
421 			 * Check if we've got a netname. If we do we copy it
422 			 * and make sure that its null terminated.
423 			 */
424 			if (OID_IS_EQUAL(DH_NETNAME, name_type)) {
425 				(void) strncpy(netname,
426 					(char *)display_name.value,
427 					MAXNETNAMELEN);
428 				netname[MAXNETNAMELEN] = '\0';
429 				stat = 0;
430 			}
431 			/*
432 			 * If there are other display formats that can
433 			 * be converted to netnames easily, insert here.
434 			 *
435 			 * else if (OID_IS_EQUAL(OTHER_NT_OID, name_type)) {
436 			 *	convert2netname(display_name.value, netname);
437 			 * } ...
438 			 */
439 
440 			/* Release temporty storage */
441 			(void) (*g_release_buffer)(&minor, &display_name);
442 			(void) (*g_release_oid)(&minor, &name_type);
443 		}
444 	}
445 
446 	if (stat == 0)
447 		return (stat);
448 
449 	/*
450 	 * If we got here then prin is not a gss netname type. Currently
451 	 * other types are not supported. To support the general case the
452 	 * prin type needs to be looked up in a table that will map an
453 	 * gss-api-export-name to a netname. The guts of the routine to do
454 	 * this, would look like this:
455 	 *
456 	 * char xport[NIS_MAXNAMELEN];
457 	 * char query[NIS_MAXNAMELEN];
458 	 * stat = -1;
459 	 * nis_result *r;
460 	 *
461 	 * bin2hex(expName.length, expName.value, xport);
462 	 * sprintf(query, "[gssprincipal=%s],gssprin.org_dir.%s", xport,
463 	 *		nis_local_directory());
464 	 * r = nis_list(query, 0, 0, 0);
465 	 * if (r->status == NIS_SUCCESS) {
466 	 *	stat = 0;
467 	 *	strcpy(netname, ENTRY_VAL(r->objects.object_val, 1);
468 	 * }
469 	 * nis_freeresult(r);
470 	 * return (stat);
471 	 *
472 	 * Here it is assumed that the gssprin table containes two columns.
473 	 * The first, gssprincipal, contains the exported gss principal name
474 	 * in hex format. And the second, netname, that contains the secure
475 	 * rpc netname.
476 	 */
477 
478 	return (stat);
479 }
480 
481 static char *
482 flavor2str(int flavor)
483 {
484 	switch (flavor) {
485 	case AUTH_NONE:
486 		return ("NONE");
487 	case AUTH_SYS:
488 		return ("SYS");
489 	case AUTH_DES:
490 		return ("DES");
491 	case RPCSEC_GSS:
492 		return ("GSS");
493 	default:
494 		return ("unknown");
495 	}
496 }
497 
498 /*
499  * Based on __nis_auth2princ but this one has RPCSEC_GSS support.
500  */
501 void
502 __nis_auth2princ_rpcgss(
503 	char	*name,		/* out */
504 	struct svc_req *req,	/* in */
505 	bool_t	refresh,	/* in */
506 	int	verbose)	/* in */
507 {
508 	struct authsys_parms	*au;
509 	struct authdes_cred	*ad;
510 	char			*rmtdomain;
511 	char			srch[2048]; /* search criteria */
512 	nis_result		*res;
513 	caddr_t			auth;
514 	char			auth_type[MECH_MAXATNAME]; /* cred tbl field */
515 	char			netname[MAXNETNAMELEN+1] = {0}; /* RPC netnm */
516 	int			flavor;	/* secure RPC flavor */
517 
518 	srch[0] = '\0';
519 
520 	if (req) {
521 		flavor = req->rq_cred.oa_flavor;
522 		auth = req->rq_clntcred;
523 	} else {
524 		if (verbose)
525 			syslog(LOG_ERR,
526 			"_auth2princ_rpcgss: req = NULL: returning '%s'",
527 				nobody);
528 		return;
529 	}
530 	(void) strcpy(name, nobody); /* default is "nobody" */
531 	if (flavor == AUTH_NONE) {
532 		if (verbose) {
533 			syslog(LOG_INFO,
534 		    "__nis_auth2princ_rpcgss: flavor = NONE: returning '%s'",
535 				nobody);
536 		}
537 		return;
538 	} else if (flavor == AUTH_SYS) { /* XXX ifdef this for 4.1 */
539 		/* LINTED pointer cast */
540 		au = (struct authsys_parms *)(auth);
541 		rmtdomain = nis_domain_of(au->aup_machname);
542 		if (au->aup_uid == 0) {
543 			(void) snprintf(name, MAX_MACHINE_NAME,
544 						"%s", au->aup_machname);
545 			if (!rmtdomain)
546 				(void) strcat(name, __nis_rpc_domain());
547 			if (name[strlen(name) - 1] != '.')
548 				(void) strcat(name, ".");
549 			if (verbose) {
550 				syslog(LOG_INFO,
551 	"__nis_auth2princ_rpcgss: flavor = SYS: returning '%s'", name);
552 			}
553 			return;
554 		}
555 		(void) snprintf(srch,
556 				sizeof (srch) - 1,
557 		    "[auth_name=\"%ld\", auth_type=LOCAL], cred.org_dir.%s",
558 				au->aup_uid, (*rmtdomain == '.') ?
559 				(char *)nis_local_directory() : rmtdomain);
560 		if (srch[strlen(srch) - 1] != '.') {
561 			(void) strcat(srch, ".");
562 		}
563 	} else if (flavor == AUTH_DES) {
564 		/* LINTED pointer cast */
565 		ad = (struct authdes_cred *)(auth);
566 		if (refresh)
567 			(void) delete_cred_item(ad->adc_fullname.name);
568 		else
569 			if (find_cred_item(ad->adc_fullname.name, name)) {
570 				if (verbose)
571 					syslog(LOG_INFO,
572 	"__nis_auth2princ_rpcgss: flavor = DES: returning from cache '%s'",
573 					name);
574 				return;
575 			}
576 
577 		rmtdomain = strchr(ad->adc_fullname.name, '@');
578 		if (rmtdomain) {
579 			rmtdomain++;
580 			(void) snprintf(srch,
581 					sizeof (srch) - 1,
582 			    "[auth_name=%s, auth_type=DES], cred.org_dir.%s",
583 					ad->adc_fullname.name, rmtdomain);
584 			if (srch[strlen(srch) - 1] != '.') {
585 				(void) strcat(srch, ".");
586 				(void) strncpy(netname, ad->adc_fullname.name,
587 						sizeof (netname));
588 				netname[sizeof (netname) - 1] = '\0';
589 			}
590 		} else {
591 			if (verbose) {
592 				syslog(LOG_INFO,
593 		"__nis_auth2princ_rpcgss: flavor = DES: returning '%s'",
594 					nobody);
595 			}
596 			return;
597 		}
598 	} else if (flavor == RPCSEC_GSS) {
599 		rpc_gss_rawcred_t	*rcred;
600 		void			*cookie;
601 
602 		if (!rpc_gss_getcred(req, &rcred, NULL, &cookie)) {
603 			if (verbose) {
604 				syslog(LOG_WARNING,
605 	"__nis_auth2princ_rpcgss: GSS getcred failure:  returning '%s'",
606 					nobody);
607 			}
608 			return;
609 		}
610 
611 		if (__nis_gssprin2netname(rcred->client_principal, netname)
612 					< 0) {
613 			syslog(LOG_ERR,
614 "__nis_auth2princ_rpcgss: can't extract netname from gss cred: returning '%s'",
615 				nobody);
616 			return;
617 		}
618 
619 		if (refresh)
620 			(void) delete_cred_item(netname);
621 		else
622 			if (find_cred_item(netname, name)) {
623 				if (verbose)
624 					syslog(LOG_INFO,
625 "__nis_auth2princ_rpcgss: flavor = RPCSEC_GSS: returning from cache '%s'",
626 					name);
627 				return;
628 			}
629 
630 		rmtdomain = strchr(netname, '@');
631 		if (rmtdomain) {
632 			char alias[MECH_MAXALIASNAME+1] = { 0 };
633 
634 			rmtdomain++;
635 			if (!__nis_mechname2alias(rcred->mechanism, alias,
636 			    sizeof (alias))) {
637 				syslog(LOG_ERR,
638 	"__nis_auth2princ_rpcgss: mechname '%s' not found: returning 'nobody'",
639 					rcred->mechanism);
640 				return;
641 			}
642 
643 			if (alias[0] != '\0') {
644 				(void) __nis_mechalias2authtype(alias,
645 						auth_type, sizeof (auth_type));
646 
647 				(void) snprintf(srch, sizeof (srch) - 1,
648 			    "[auth_name=%s, auth_type=%s], cred.org_dir.%s",
649 					netname, auth_type, rmtdomain);
650 
651 				if (srch[strlen(srch) - 1] != '.') {
652 					(void) strcat(srch, ".");
653 				}
654 			} else {
655 				syslog(LOG_ERR,
656 "__nis_auth2princ_rpcgss: no alias found for mechname '%s': returning 'nobody'",
657 					rcred->mechanism);
658 				return;
659 			}
660 		} else {
661 			if (verbose) {
662 				syslog(LOG_INFO,
663 		"__nis_auth2princ_rpcgss: flavor = RPCSEC_GSS: returning '%s'",
664 				nobody);
665 			}
666 			return;
667 		}
668 	} else {
669 		syslog(LOG_WARNING,
670 		"__nis_auth2princ_rpcgss: flavor = %d(unknown): returning '%s'",
671 							flavor, nobody);
672 		return;
673 	}
674 	if (verbose)
675 		if (flavor == AUTH_DES || flavor == RPCSEC_GSS)
676 			syslog(LOG_INFO,
677 	"__nis_auth2princ_rpcgss: calling list with name '%s' and type '%s'",
678 							netname,
679 				flavor == AUTH_DES ? "DES" : auth_type);
680 		else /* AUTH_SYS */
681 			syslog(LOG_INFO,
682 		"__nis_auth2princ_rpcgss: calling list with uid (LOCAL) '%d'",
683 				au->aup_uid);
684 
685 	res = nis_list(srch, NO_AUTHINFO+USE_DGRAM+FOLLOW_LINKS, NULL, NULL);
686 	if (res->status != NIS_SUCCESS) {
687 		if (verbose)
688 			syslog(LOG_INFO,
689 			"__nis_auth2princ_rpcgss: error doing nis_list: %s",
690 						nis_sperrno(res->status));
691 	} else {
692 		(void) strncpy(name, ENTRY_VAL(res->objects.objects_val, 0),
693 				1024);
694 		if (flavor == AUTH_DES || flavor == RPCSEC_GSS) {
695 			if (verbose)
696 				syslog(LOG_INFO,
697 				"__nis_auth2princ_rpcgss: caching '%s'/'%s'\n",
698 					netname, name);
699 			add_cred_item(netname, name);
700 		}
701 	}
702 
703 	nis_freeresult(res);
704 	if (verbose)
705 		syslog(LOG_INFO,
706 		"__nis_auth2princ_rpcgss: flavor = %s: returning : '%s'",
707 			flavor2str(flavor), name);
708 
709 }
710 
711 /*
712  * This function returns true if the given principal has the right to
713  * do the requested function on the given object. It could be a define
714  * if that would save time. At the moment it is a function.
715  * NOTE: It recursively calls NIS by doing the lookup on the group if
716  * the conditional gets that far.
717  *
718  * N.B. If the principal passed is 'null' then we're recursing and don't
719  * need to check it. (we always let ourselves look at the data)
720  */
721 bool_t
722 __nis_ck_perms(
723 	unsigned int	right,	/* The Access right we desire 		*/
724 	unsigned int	mask,	/* The mask for World/Group/Owner 	*/
725 	nis_object	*obj,	/* A pointer to the object		*/
726 	nis_name	pr,	/* Principal making the request		*/
727 	int		level)	/* security level server is running at	*/
728 {
729 	if ((level == 0) || (*pr == 0))
730 		return (TRUE);
731 
732 	return (NIS_NOBODY(mask, right) ||
733 		(NIS_WORLD(mask, right) && (strcmp(pr, "nobody") != 0)) ||
734 		(NIS_OWNER(mask, right) &&
735 			(nis_dir_cmp(pr, obj->zo_owner) == SAME_NAME)) ||
736 		(NIS_GROUP(mask, right) &&
737 			(strlen(obj->zo_group) > (size_t)(1)) &&
738 			__do_ismember(pr, obj, nis_lookup)));
739 }
740 
741 /*
742  * is 'host' the master server for "org_dir."'domain' ?
743  */
744 bool_t
745 __nis_ismaster(char *host, char *domain)
746 {
747 	nis_server	**srvs;		/* servers that serve 'domain' */
748 	nis_server	*master_srv;
749 	char		buf[NIS_MAXNAMELEN];
750 	bool_t		res;
751 
752 	if (domain == NULL) {
753 		syslog(LOG_ERR, "__nis_ismaster(): null domain");
754 		return (FALSE);
755 	}
756 	/* strlen(".org_dir") + null + "." = 10 */
757 	if ((strlen(domain) + 10) > (size_t)NIS_MAXNAMELEN)
758 		return (FALSE);
759 
760 	(void) snprintf(buf, sizeof (buf), "org_dir.%s", domain);
761 	if (buf[strlen(buf) - 1] != '.')
762 		(void) strcat(buf, ".");
763 
764 	srvs = nis_getservlist(buf);
765 	if (srvs == NULL) {
766 		/* can't find any of the servers that serve this domain */
767 		/* something is very wrong ! */
768 		syslog(LOG_ERR,
769 			"cannot get a list of servers that serve '%s'",
770 			buf);
771 		return (FALSE);
772 	}
773 	master_srv = srvs[0];	/* the first one is always the master */
774 
775 	if (strcasecmp(host, master_srv->name) == 0)
776 		res = TRUE;
777 	else
778 		res = FALSE;
779 
780 	/* done with server list */
781 	nis_freeservlist(srvs);
782 
783 	return (res);
784 }
785 
786 /*
787  * check if this principal is the owner of the table
788  * or is a member of the table group owner
789  */
790 bool_t
791 __nis_isadmin(char *princ, char *table, char *domain)
792 {
793 	char	buf[NIS_MAXNAMELEN];
794 	struct	nis_result	*res;
795 	struct	nis_object	*obj;
796 	bool_t	ans = FALSE;
797 
798 	if ((princ == NULL || *princ == '\0') ||
799 		(table == NULL || *table == '\0') ||
800 		(domain == NULL || *domain == '\0'))
801 		return (FALSE);
802 
803 	/* strlen(".org_dir.") + null + "." = 11 */
804 	if ((strlen(table) + strlen(domain) + 11) >
805 			(size_t)NIS_MAXNAMELEN) {
806 		syslog(LOG_ERR, "__nis_isadmin: buffer too small");
807 		return (FALSE);
808 	}
809 	(void) snprintf(buf, sizeof (buf), "%s.org_dir.%s", table, domain);
810 	if (buf[strlen(buf) - 1] != '.')
811 		(void) strcat(buf, ".");
812 
813 	/* get the table object */
814 	res = nis_lookup(buf, FOLLOW_LINKS);
815 	if (res->status != NIS_SUCCESS) {
816 		syslog(LOG_ERR,
817 			"__nis_isadmin: could not lookup '%s' table",
818 			table);
819 		nis_freeresult(res);
820 		return (FALSE);
821 	}
822 	obj = NIS_RES_OBJECT(res);
823 	if (obj->zo_data.zo_type != NIS_TABLE_OBJ) {
824 		syslog(LOG_ERR, "__nis_isadmin: not a table object");
825 		nis_freeresult(res);
826 		return (FALSE);
827 	}
828 	if ((strcasecmp(obj->zo_owner, princ) == 0) ||
829 		((obj->zo_group) && (*(obj->zo_group)) &&
830 			nis_ismember(princ, obj->zo_group)))
831 		ans = TRUE;
832 
833 	nis_freeresult(res);
834 	return (ans);
835 }
836 
837 #define	NIS_NOHOSTNAME	48
838 #define	NIS_NOHOSTADDR	49
839 
840 nis_server *
841 __nis_host2nis_server(
842 	char	*host,		/* host name */
843 	bool_t	addpubkey,	/* add pub key info */
844 	int	*errcode)	/* error code */
845 {
846 	return (__nis_host2nis_server_g(host, addpubkey, TRUE,
847 						    errcode));
848 }
849 
850 static void
851 nis_free_endpoints(endpoint *ep, int n)
852 {
853 	int i;
854 
855 	if (ep != NULL) {
856 		for (i = 0; i < n; i++) {
857 			if (ep[i].uaddr != NULL)
858 				free(ep[i].uaddr);
859 			if (ep[i].family != NULL)
860 				free(ep[i].family);
861 			if (ep[i].proto != NULL)
862 				free(ep[i].proto);
863 		}
864 		free(ep);
865 	}
866 }
867 
868 void
869 __free_nis_server(nis_server *server)
870 {
871 	if (server != NULL) {
872 		free(server->name);
873 		nis_free_endpoints(server->ep.ep_val,
874 			server->ep.ep_len);
875 		free(server->pkey.n_bytes);
876 		free(server);
877 	}
878 }
879 
880 /*
881  * This function constructs a server description of the host
882  * given (or the local host) and returns it as a nis_server
883  * structure.
884  * Returns NULL on error, and sets the errcode.
885  */
886 nis_server *
887 __nis_host2nis_server_g(const char *host,
888 			bool_t	addpubkey,
889 			bool_t	loopback,
890 			int	*errcode)
891 {
892 #define	INC_SIZE 512
893 	int			addr_size = 0;
894 	endpoint		*addr, *oldaddr;
895 	nis_server		*hostinfo;
896 	int			num_ep = 0, i;
897 	struct netconfig	*nc;
898 	void			*nch;
899 	struct nd_hostserv	hs;
900 	struct nd_addrlist	*addrlist;
901 	char			hostnetname[NIS_MAXPATH];
902 	struct hostent		*he;
903 	char			netname[MAXNETNAMELEN];
904 	char			hostname[MAXHOSTNAMELEN+1];
905 	char			pkey[HEXKEYBYTES+1];
906 	mechanism_t		**mechlist;
907 	size_t			hlen;
908 
909 	if (host) {
910 		he = gethostbyname(host);
911 		if (!he) {
912 			if (errcode)
913 				*errcode = NIS_BADNAME;
914 			return (NULL);
915 		}
916 
917 		hlen = strlen(host);
918 		if (hlen + 1 >= sizeof (hostnetname) ||
919 				hlen >= sizeof (hostname)) {
920 			if (errcode)
921 				*errcode = NIS_BADNAME;
922 			return (NULL);
923 		}
924 		(void) strcpy(hostname, host);
925 		hs.h_host = hostname;
926 
927 		/*
928 		 * Make attempt to fully qualify hostname.  If hostname
929 		 * contains a dot, then assume it is already fully
930 		 * qualified and just add a trailing dot.  Otherwise,
931 		 * append local domain name.
932 		 */
933 
934 		(void) strcpy(hostnetname, host);
935 		if (strchr(hostnetname, '.') == 0) {
936 			char *localDir = nis_local_directory();
937 			size_t reqlen = hlen + strlen(localDir);
938 			if (*localDir != '.') {
939 				(void) strcat(hostnetname, ".");
940 				reqlen += 1;
941 			}
942 			if (reqlen >= sizeof (hostnetname)) {
943 				if (errcode)
944 					*errcode = NIS_BADNAME;
945 				return (NULL);
946 			}
947 			(void) strcat(hostnetname, localDir);
948 		}
949 		if (hostnetname[strlen(hostnetname)-1] != '.')
950 			(void) strcat(hostnetname, ".");
951 	} else {
952 		if (gethostname(hostname, sizeof (hostname)) != 0) {
953 			if (errcode)
954 				*errcode = NIS_NOHOSTNAME;
955 			return (NULL);
956 		}
957 		hs.h_host = HOST_SELF_CONNECT;
958 	}
959 
960 	if (!(nch = setnetconfig()))
961 		return (NULL);
962 
963 	hs.h_serv = "rpcbind";
964 
965 	addr = NULL;
966 	while (nc = getnetconfig(nch)) {
967 		if (!loopback && (strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0))
968 			continue;
969 		if (netdir_getbyname(nc, &hs, &addrlist))
970 			continue;
971 		for (i = 0; i < addrlist->n_cnt; i++, num_ep++) {
972 			if (num_ep == addr_size) {
973 				addr_size += INC_SIZE;
974 				oldaddr = addr;
975 				addr = realloc(addr,
976 					addr_size * sizeof (endpoint));
977 				if (addr == NULL) {
978 				    if (errcode)
979 					*errcode = NIS_NOMEMORY;
980 				    (void) endnetconfig(nch);
981 				    nis_free_endpoints(oldaddr, num_ep);
982 				    netdir_free((char *)addrlist, ND_ADDRLIST);
983 				    return (NULL);
984 				}
985 			}
986 			addr[num_ep].uaddr =
987 				taddr2uaddr(nc, &(addrlist->n_addrs[i]));
988 			if (!addr[num_ep].uaddr) {
989 				if (errcode)
990 					*errcode = NIS_NOMEMORY;
991 				(void) endnetconfig(nch);
992 				nis_free_endpoints(addr, num_ep);
993 				netdir_free((char *)addrlist, ND_ADDRLIST);
994 				return (NULL);
995 			}
996 			__nis_netconfig2ep(nc, &(addr[num_ep]));
997 		}
998 		netdir_free((char *)addrlist, ND_ADDRLIST);
999 	}
1000 	(void) endnetconfig(nch);
1001 
1002 	if ((hostinfo = calloc(1, sizeof (nis_server))) == NULL) {
1003 		nis_free_endpoints(addr, num_ep);
1004 		if (errcode)
1005 			*errcode = NIS_NOMEMORY;
1006 		return (NULL);
1007 	}
1008 
1009 	hostinfo->ep.ep_len = num_ep;
1010 	hostinfo->ep.ep_val = addr;
1011 
1012 	hostinfo->name = (host) ?
1013 		strdup(hostnetname) : strdup(nis_local_host());
1014 	if (hostinfo->name == NULL) {
1015 		__free_nis_server(hostinfo);
1016 		if (errcode)
1017 			*errcode = NIS_NOMEMORY;
1018 		return (NULL);
1019 	}
1020 
1021 	if (addpubkey) {
1022 		if (!host2netname(netname, hostinfo->name, NULL))
1023 			goto nocred;
1024 
1025 		if (mechlist = __nis_get_mechanisms(0)) {
1026 			bool_t		got192 = FALSE, gotothers = FALSE;
1027 			extdhkey_t	*keylist = NULL;
1028 			size_t		keylistsize = 0;
1029 			int		i;
1030 
1031 			for (i = 0; mechlist[i]; i++) {
1032 				size_t		binlen, binpadlen, hexkeylen,
1033 						keyoffset;
1034 				char		*hexkey, *entryoffset;
1035 				extdhkey_t	*curentry, *oldkeylist;
1036 				keylen_t	keylen = mechlist[i]->keylen;
1037 				algtype_t	algtype = mechlist[i]->algtype;
1038 
1039 				binlen = (keylen + 7) / 8;
1040 				binpadlen = ((binlen + 3) / 4) * 4;
1041 				hexkeylen = binlen * 2 + 1;
1042 
1043 				if (!(hexkey = malloc(hexkeylen))) {
1044 					__nis_release_mechanisms(mechlist);
1045 					__free_nis_server(hostinfo);
1046 					free(keylist);
1047 					if (errcode)
1048 						*errcode = NIS_NOMEMORY;
1049 					return (NULL);
1050 				}
1051 
1052 				if (getpublickey_g(netname, keylen, algtype,
1053 							hexkey,
1054 							hexkeylen) == 0) {
1055 					free(hexkey);
1056 					continue;
1057 				} else {
1058 					if (keylen == 192)
1059 						got192 = TRUE;
1060 					else
1061 						gotothers = TRUE;
1062 				}
1063 
1064 				keyoffset = keylistsize;
1065 				keylistsize += sizeof (ushort_t) * 2 +
1066 					binpadlen;
1067 				oldkeylist = keylist;
1068 				if (!(keylist = realloc(keylist,
1069 							    keylistsize))) {
1070 					free(oldkeylist);
1071 					free(hexkey);
1072 					__nis_release_mechanisms(mechlist);
1073 					__free_nis_server(hostinfo);
1074 					if (errcode)
1075 						*errcode = NIS_NOMEMORY;
1076 					return (NULL);
1077 				}
1078 
1079 				entryoffset = (char *)keylist + keyoffset;
1080 				/* LINTED pointer cast */
1081 				curentry = (extdhkey_t *)entryoffset;
1082 
1083 				curentry->keylen = htons(keylen);
1084 				curentry->algtype = htons(algtype);
1085 				hex2bin(binlen, hexkey, (char *)curentry->key);
1086 
1087 				free(hexkey);
1088 			}
1089 			__nis_release_mechanisms(mechlist);
1090 
1091 			/*
1092 			 * If there is only keys for DH192, then we pretend
1093 			 * that DHEXT doesn't exist.
1094 			 *
1095 			 * If no keys are returned, then we no nuthin'.
1096 			 */
1097 			if (!gotothers) {
1098 				free(keylist);
1099 				if (got192)
1100 					goto only192;
1101 				else
1102 					goto nocred;
1103 			}
1104 
1105 			hostinfo->key_type = NIS_PK_DHEXT;
1106 			hostinfo->pkey.n_len = (ushort_t)keylistsize;
1107 			hostinfo->pkey.n_bytes = (char *)keylist;
1108 		} else {
1109 			if (getpublickey(netname, pkey)) {
1110 only192:			hostinfo->key_type = NIS_PK_DH;
1111 				hostinfo->pkey.n_len = strlen(pkey)+1;
1112 				hostinfo->pkey.n_bytes = (char *)strdup(pkey);
1113 				if (hostinfo->pkey.n_bytes == NULL) {
1114 					__free_nis_server(hostinfo);
1115 					if (errcode)
1116 						*errcode = NIS_NOMEMORY;
1117 					return (NULL);
1118 				}
1119 			} else {
1120 nocred:				hostinfo->key_type = NIS_PK_NONE;
1121 				hostinfo->pkey.n_bytes = NULL;
1122 				hostinfo->pkey.n_len = 0;
1123 			}
1124 		}
1125 	} else
1126 		goto nocred;
1127 
1128 	return (hostinfo);
1129 }
1130 
1131 
1132 /*
1133  * Extract a public key given a key length and alg. type from a packed
1134  * netobj containing extended Diffie-Hellman keys.
1135  */
1136 char *
1137 __nis_dhext_extract_pkey(netobj *no, keylen_t keylen, algtype_t algtype)
1138 {
1139 	char		*hexkey;
1140 	/* LINTED pointer cast */
1141 	extdhkey_t	*keyent = (extdhkey_t *)no->n_bytes;
1142 
1143 	/* LINTED pointer cast */
1144 	while (keyent < (extdhkey_t *)(no->n_bytes + no->n_len)) {
1145 		char	*keyoffset;
1146 		size_t	binlen = (ntohs(keyent->keylen) + 7) / 8;
1147 		size_t	binpadlen = ((binlen + 3) / 4) * 4;
1148 		size_t	hexkeylen = binlen * 2 + 1;
1149 
1150 		if (keylen == ntohs(keyent->keylen) &&
1151 		    algtype == ntohs(keyent->algtype)) {
1152 
1153 			if (!(hexkey = malloc(hexkeylen)))
1154 				return (NULL);
1155 
1156 			(void) bin2hex(binlen, keyent->key, hexkey);
1157 			return (hexkey);
1158 		}
1159 		keyoffset = (char *)keyent + (sizeof (ushort_t) * 2) +
1160 			binpadlen;
1161 		/* LINTED pointer cast */
1162 		keyent = (extdhkey_t *)keyoffset;
1163 	}
1164 	return (NULL);
1165 }
1166 
1167 
1168 /*
1169  * Returns a list of key lengths and alg. types for a given nis_server
1170  * structure.
1171  */
1172 int
1173 __nis_dhext_extract_keyinfo(nis_server *ns, extdhkey_t **retdat)
1174 {
1175 	extdhkey_t	*keyinfolist = NULL, *tmplist = NULL;
1176 	int		count = 0;
1177 	/* LINTED pointer cast */
1178 	extdhkey_t	*keyent = (extdhkey_t *)ns->pkey.n_bytes;
1179 
1180 	switch (ns->key_type) {
1181 	case	NIS_PK_DH:
1182 		if (!(keyinfolist = malloc(sizeof (extdhkey_t))))
1183 			return (0);
1184 		keyinfolist[0].keylen = 192;
1185 		keyinfolist[0].algtype = 0;
1186 
1187 		*retdat = keyinfolist;
1188 		return (1);
1189 
1190 	case	NIS_PK_DHEXT:
1191 		/* LINTED pointer cast */
1192 		while (keyent < (extdhkey_t *)(ns->pkey.n_bytes +
1193 			ns->pkey.n_len)) {
1194 			size_t	binlen = (keyent->keylen + 7) / 8;
1195 			size_t	binpadlen = ((binlen + 3) / 4) * 4;
1196 			char	*keyoffset;
1197 
1198 			tmplist = keyinfolist;
1199 
1200 			if (!(keyinfolist = realloc(keyinfolist,
1201 						    (count + 1) *
1202 						    sizeof (extdhkey_t)))) {
1203 				free(tmplist);
1204 				return (0);
1205 			}
1206 			keyinfolist[count].keylen = ntohs(keyent->keylen);
1207 			keyinfolist[count].algtype = ntohs(keyent->algtype);
1208 
1209 			keyoffset = (char *)keyent + (sizeof (ushort_t) * 2) +
1210 				binpadlen;
1211 			/* LINTED pointer cast */
1212 			keyent = (extdhkey_t *)keyoffset;
1213 			count++;
1214 		}
1215 		*retdat = keyinfolist;
1216 		return (count);
1217 
1218 		default:
1219 			return (0);
1220 	}
1221 }
1222