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