xref: /illumos-gate/usr/src/lib/libnsl/nis/gen/nis_sec_mechs.c (revision 2983dda76a6d296fdb560c88114fe41caad1b84f)
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 module contains the interfaces for the NIS+ security mechanisms.
31  */
32 
33 #include "mt.h"
34 #include <ctype.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <strings.h>
38 #include <rpc/rpc.h>
39 #include <netconfig.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/file.h>
43 #include <thread.h>
44 #include <synch.h>
45 #include <dlfcn.h>
46 #include <rpcsvc/nis_dhext.h>
47 
48 
49 /*
50  * NIS+ security file
51  */
52 
53 #define	NIS_SEC_CF_MAX_LINELEN		512
54 
55 /* the min number of fields allowable per line */
56 #define	NIS_SEC_CF_MIN_FIELDS		5
57 /* the max number of fields processed per line */
58 #define	NIS_SEC_CF_MAX_FIELDS		7
59 
60 /* field "Not Applicable" char */
61 #define	NIS_SEC_CF_NA_CHAR	'-'
62 #define	NIS_SEC_CF_NA_CMP(a)	((a)[0] == NIS_SEC_CF_NA_CHAR && (a)[1] == '\0')
63 
64 
65 static const char	*cf_entry_type_mech_str = "mech";
66 static const char	*cf_mech_des_str = NIS_SEC_CF_DES_ALIAS;
67 static const char	*cf_mech_dh1920_str = "dh192-0";
68 
69 static const char	*cf_secserv_default_str = "default";
70 static const char	*cf_secserv_none_str = "none";
71 static const char	*cf_secserv_integrity_str = "integrity";
72 static const char	*cf_secserv_privacy_str = "privacy";
73 
74 static mutex_t		nis_sec_cf_lock = DEFAULTMUTEX;
75 
76 
77 /*
78  * GSS mechanisms file
79  *
80  * This is currently a private NIS+ interface but at some point in the future
81  * can be broken out and made available to other apps that need to access
82  * GSS backends.
83  */
84 
85 #define	MF_MAX_LINELEN	256
86 #define	MF_MAX_FLDLEN	MAXDHNAME
87 
88 /* mech file entry type */
89 typedef struct {
90 	char *mechname;
91 	char *oid;
92 	char *libname;
93 	/* the 4th field is not used by user land apps */
94 } mfent_t;
95 
96 static const char	mech_file[] = "/etc/gss/mech";
97 static const int	mech_file_flds_max = 3;
98 static const int	mech_file_flds_min = 3;
99 static mutex_t		mech_file_lock = DEFAULTMUTEX;
100 static const char	dh_str[] = "diffie_hellman";
101 
102 
103 #define	MECH_LIB_PREFIX1	"/usr/lib/"
104 
105 #ifdef _LP64
106 
107 #define	MECH_LIB_PREFIX2	"64/"
108 
109 #else   /* _LP64 */
110 
111 #define	MECH_LIB_PREFIX2	""
112 
113 #endif  /* _LP64 */
114 
115 #define	MECH_LIB_DIR		"gss/"
116 
117 #define	MECH_LIB_PREFIX	MECH_LIB_PREFIX1 MECH_LIB_PREFIX2 MECH_LIB_DIR
118 
119 
120 static void
121 list_free_all(void (*free_ent)(), void **mpp)
122 {
123 	void **tpp = mpp;
124 
125 	if (tpp) {
126 		for (; *tpp; tpp++)
127 			(*free_ent)(*tpp);
128 		free(mpp);
129 	}
130 }
131 
132 static void **
133 list_append_ent(void *ent, void **list, uint_t cnt, void (*free_ent)())
134 {
135 	void **new_l;
136 
137 	if (!(new_l = realloc(list, sizeof (*list) * (cnt + 1)))) {
138 		list_free_all(free_ent, list);
139 		return (NULL);
140 	}
141 	*(new_l + cnt - 1) = ent;
142 	*(new_l + cnt) = NULL;
143 
144 	return (new_l);
145 }
146 
147 static void **
148 list_copy(void *(*cp_ent)(), void **mpp)
149 {
150 	void	**tpp_h;
151 	void	**tpp;
152 	void	*tp;
153 	int 	diff;
154 
155 	if (!mpp)
156 		return (NULL);
157 
158 	for (tpp = mpp; *tpp; tpp++)
159 		;
160 
161 	diff = tpp - mpp;
162 
163 	if (!(tpp_h = calloc(diff + 1, sizeof (*mpp))))
164 		return (NULL);
165 
166 	for (tpp = tpp_h; *mpp; mpp++) {
167 		if (!(tp = (*cp_ent)(*mpp))) {
168 			free(tpp_h);
169 			return (NULL);
170 		}
171 		*tpp++ = tp;
172 	}
173 
174 	return (tpp_h);
175 }
176 
177 static char *
178 nextline(fd, line)
179 	FILE *fd;
180 	char *line;
181 {
182 	char *cp;
183 
184 	if (fgets(line, NIS_SEC_CF_MAX_LINELEN, fd) == NULL)
185 		return (NULL);
186 	cp = index(line, '\n');
187 	if (cp)
188 		*cp = '\0';
189 	return (line);
190 }
191 
192 static int
193 nextfield(char **cpp, char *op, int n)
194 {
195 
196 	intptr_t max;
197 	char *dst = op;
198 	char *cp = *cpp;
199 
200 	while (*cp == ' ' || *cp == '\t')
201 		cp++;
202 	if (*cp == '\0' || *cp == '#')
203 		return (0);
204 
205 	max = (intptr_t)op + n;
206 	while (*cp && *cp != ' ' && *cp != '\t' && *cp != '#' &&
207 		(intptr_t)dst < max)
208 		*dst++ = *cp++;
209 	*dst = '\0';
210 
211 	if ((intptr_t)dst >= max)
212 		/* not much else to do but move past current field */
213 		while (*cp && *cp != ' ' && *cp != '\t' && *cp != '#')
214 			cp++;
215 
216 	*cpp = cp;
217 
218 	return (1);
219 }
220 
221 
222 static rpc_gss_service_t
223 str_to_secserv_t(const char *s)
224 {
225 
226 	if (s) {
227 		if (strncmp(cf_secserv_none_str, s,
228 			    strlen(cf_secserv_none_str)) == 0)
229 			return (rpc_gss_svc_none);
230 		if (strncmp(cf_secserv_integrity_str, s,
231 			    strlen(cf_secserv_integrity_str)) == 0)
232 			return (rpc_gss_svc_integrity);
233 		if (strncmp(cf_secserv_privacy_str, s,
234 			    strlen(cf_secserv_privacy_str)) == 0)
235 			return (rpc_gss_svc_privacy);
236 	}
237 
238 	return (rpc_gss_svc_default);
239 }
240 
241 /*
242  * Return TRUE if all the chars up to the NUL are of the digit type.
243  * Else return FALSE.
244  */
245 static bool_t
246 isnumberstr(const char *s)
247 {
248 
249 	for (; *s; s++)
250 		if (!isdigit(*s))
251 			return (FALSE);
252 
253 	return (TRUE);
254 }
255 
256 /*
257  * Free security file mechanism entry.
258  */
259 static void
260 sf_free_mech_ent(mechanism_t *mp)
261 {
262 	if (mp) {
263 		if (mp->mechname)
264 			free(mp->mechname);
265 		if (mp->alias)
266 			free(mp->alias);
267 		if (mp->qop)
268 			free(mp->qop);
269 		free(mp);
270 	}
271 }
272 
273 static void
274 free_fields(char **cpp, int cnt)
275 {
276 	char **tpp = cpp;
277 
278 	if (cpp) {
279 		if (cnt)
280 			for (; cnt > 0; cnt--, tpp++)
281 				if (*tpp)
282 					free(*tpp);
283 				else
284 					break;
285 		free(cpp);
286 	}
287 }
288 
289 /*
290  * Generic parse-linestr-of-config-file routine.  Arg linep is ptr
291  * (which will be modified) to the input string .  Arg minflds is the
292  * minimum number of fields expected.  Arg maxflds is the max number
293  * of fields that will be parsed.  Arg bufsiz is the max len of each
294  * field that will  be copied to the return area.
295  *
296  * If there are less fields in the entry than the max number,
297  * the remainding ptrs will be 0.
298  *
299  * Returns a ptr to an array of ptrs to strings on success else
300  * NULL on failure.
301  *
302  * The caller must free the storage (of a successful return only).
303  */
304 static char **
305 parse_line(char *linep, int minflds, int maxflds, int bufsiz)
306 {
307 	char **fpp = calloc(maxflds, sizeof (linep));
308 	char **tpp = fpp;
309 	char *cp;
310 	int	i;
311 
312 	if (!fpp)
313 		return (NULL);
314 
315 	if (!(cp = malloc(bufsiz))) {
316 		free(fpp);
317 		return (NULL);
318 	}
319 
320 	for (i = 0; i < maxflds; i++, tpp++) {
321 		char *tp;
322 		if (!nextfield(&linep, cp, bufsiz)) {
323 			free(cp);
324 			if (i < minflds) {
325 				free_fields(fpp, i);
326 				return (NULL);
327 			} else
328 				return (fpp);
329 		}
330 		if (!(tp = strdup(cp))) {
331 			free_fields(fpp, i);
332 			free(cp);
333 			return (NULL);
334 		}
335 		*tpp = tp;
336 	}
337 
338 	free(cp);
339 	return (fpp);
340 }
341 
342 /*
343  * Return a ptr to a mechanism entry read from a line of the sec conf file.
344  * Return NULL on EOF or error.
345  *
346  * An alias field of "des" (case not sig) will override any settings
347  * in the keylen or algtype fields like so:
348  *    keylen  = 192
349  *    algtype = 0
350  */
351 
352 static mechanism_t *
353 get_secfile_ent(FILE *fptr)
354 {
355 	mechanism_t	*m;
356 	char		*cp;
357 	char		**flds;  /* line fields */
358 	const int	num_flds_min = NIS_SEC_CF_MIN_FIELDS;
359 	const int	num_flds_max = NIS_SEC_CF_MAX_FIELDS;
360 	char		line[NIS_SEC_CF_MAX_LINELEN + 1 ] = {0};
361 	const int	line_len = NIS_SEC_CF_MAX_LINELEN + 1;
362 
363 	/*
364 	 * NIS+ security conf file layout
365 	 * <Entry_type>
366 	 * mech 	<GSS_mechanism_name> <Mech_bit_size> <Mech_alg_type>
367 	 * 		<Alias> <GSS_quality_of_protection> <GSS_sec_svc>
368 	 *
369 	 * QOP and sec_svc are optional.
370 	 */
371 	const int	mn_offset = 1; /* mechname */
372 	const int	kl_offset = 2; /* key length */
373 	const int	at_offset = 3; /* alg type */
374 	const int	al_offset = 4; /* mech alias */
375 	const int	qp_offset = 5; /* qop */
376 	const int	ss_offset = 6; /* security svc */
377 
378 cont:
379 	while (((cp = nextline(fptr, line)) != NULL) &&
380 		(*cp == '#' || *cp == '\0'))
381 		;
382 	if (cp == NULL)
383 		return (NULL);
384 
385 	if (!(flds = parse_line(cp, num_flds_min, num_flds_max,
386 					line_len)))
387 		goto cont;
388 
389 	if (strncmp(cf_entry_type_mech_str, *flds,
390 		    strlen(cf_entry_type_mech_str))) {
391 		free_fields(flds, num_flds_max);
392 		goto cont;
393 	}
394 
395 	if (!(m = malloc(sizeof (mechanism_t)))) {
396 		free_fields(flds, num_flds_max);
397 		return (NULL);
398 	}
399 
400 	/* mechanism name */
401 	m->mechname = NIS_SEC_CF_NA_CMP(*(flds + mn_offset)) ? NULL
402 		: strdup(*(flds + mn_offset));
403 
404 	/* mechanism alias */
405 	m->alias = NIS_SEC_CF_NA_CMP(*(flds + al_offset)) ? NULL
406 		: strdup(*(flds + al_offset));
407 
408 	/*
409 	 * qop: optional field
410 	 * Make qop NULL if the field was empty or was "default" or
411 	 * was '-'.
412 	 */
413 	if (!*(flds + qp_offset) ||
414 	    (strncasecmp(*(flds + qp_offset), cf_secserv_default_str,
415 				strlen(cf_secserv_default_str)) == 0) ||
416 	    NIS_SEC_CF_NA_CMP(*(flds + qp_offset)))
417 		m->qop = NULL;
418 	else
419 		m->qop = strdup(*(flds + qp_offset));
420 
421 	/* security service: optional field */
422 	m->secserv  = str_to_secserv_t(*(flds + ss_offset));
423 
424 	/* mech alias */
425 	if (*(flds + al_offset) &&
426 	    (strncasecmp(*(flds + al_offset), cf_mech_des_str,
427 				strlen(cf_mech_des_str)) == 0)) {
428 		/* we've got the AUTH_DES compat line */
429 		m->keylen = 192;
430 		m->algtype = 0;
431 	} else {
432 		/* key length (bits) */
433 		if (NIS_SEC_CF_NA_CMP(*(flds + kl_offset)))
434 			m->keylen = NIS_SEC_CF_NA_KA;
435 		else if (!isnumberstr(*(flds + kl_offset))) {
436 			free_fields(flds, num_flds_max);
437 			sf_free_mech_ent(m);
438 			goto cont;
439 		} else
440 			m->keylen =  atoi(*(flds + kl_offset));
441 
442 		/* algorithm type */
443 		if (NIS_SEC_CF_NA_CMP(*(flds + at_offset)))
444 			m->algtype = NIS_SEC_CF_NA_KA;
445 		else if (!isnumberstr(*(flds + at_offset))) {
446 			free_fields(flds, num_flds_max);
447 			sf_free_mech_ent(m);
448 			goto cont;
449 		} else
450 			m->algtype =  atoi(*(flds + at_offset));
451 	}
452 
453 	free_fields(flds, num_flds_max);
454 
455 	return (m);
456 }
457 
458 /*
459  * Return TRUE if both entries have the same
460  * mechname/alias/keylen/algotype combo.  Else return FALSE.
461  */
462 static bool_t
463 equal_entries(const mechanism_t *mp, const mechanism_t *tp)
464 {
465 	if (mp && tp) {
466 		if (mp->keylen != tp->keylen)
467 			return (FALSE);
468 		if (mp->algtype != tp->algtype)
469 			return (FALSE);
470 
471 		/* both NULL, the 2 are equal */
472 		if (!mp->mechname && !tp->mechname)
473 			return (TRUE);
474 		/* only one NULL, not equal */
475 		if (!mp->mechname || !tp->mechname)
476 			return (FALSE);
477 		if (strcmp(mp->mechname, tp->mechname) != 0)
478 			return (FALSE);
479 
480 		if (!mp->alias && !tp->alias)
481 			return (TRUE);
482 		if (!mp->alias || !tp->alias)
483 			return (FALSE);
484 		if (strcmp(mp->alias, tp->alias) != 0)
485 			return (FALSE);
486 	}
487 
488 	return (TRUE);
489 }
490 
491 static mechanism_t *
492 sf_copy_mech_ent(mechanism_t *mp)
493 {
494 	mechanism_t *tp = calloc(1, sizeof (*mp));
495 
496 	if (!mp || !tp)
497 		return (NULL);
498 
499 	tp->mechname = mp->mechname ? strdup(mp->mechname) : NULL;
500 	tp->alias = mp->alias ? strdup(mp->alias) : NULL;
501 	tp->qop = mp->qop ? strdup(mp->qop) : NULL;
502 	tp->keylen = mp->keylen;
503 	tp->algtype = mp->algtype;
504 	tp->secserv = mp->secserv;
505 
506 	return (tp);
507 }
508 
509 /*
510  * Return TRUE if the mechname/alias/keylen/algtype combo
511  * already exists in the no dups array.  Else return FALSE.
512  */
513 static bool_t
514 member_of_dups(mechanism_t **t, const mechanism_t *mp)
515 {
516 
517 	if (t)
518 		for (; *t; t++)
519 			if (equal_entries(mp, *t))
520 				return (TRUE);
521 
522 	return (FALSE);
523 }
524 
525 /*
526  * Return a list of valid mechanisms ranked by sequence in the NIS+
527  * security conf file.  Return NULL if there are no valid entries.
528  * On success, the last pointer of the array of pointers will be NULL.
529  *
530  * If input arg 'qop_secserv' is TRUE, include duplicate
531  * mechname/alias/keylen/algtype entries that differ only in the QOP
532  * and security service.  Else, duplicates are omitted.
533  *
534  * The list of mechanisms are gauranteed to be valid ones installed
535  * on the system.
536  *
537  * This implementation returns copies of the "master" list.  The "master"
538  * list will updated if the file is modified.
539  */
540 
541 mechanism_t **
542 __nis_get_mechanisms(bool_t qop_secserv)
543 {
544 	/*
545 	 * 'mechs' is the "master" list of valid mechanisms from
546 	 * the NIS+ security conf file.
547 	 * 'mechs_no_dups' is the "master" list of valid mechanisms
548 	 * that differ only in QOP/SecuritySvc fields.
549 	 */
550 	static mechanism_t	**mechs = NULL;
551 	static mechanism_t	**mechs_no_dups = NULL;
552 
553 	mechanism_t	*mp;
554 	mechanism_t	**tmechs = NULL;	 /* temp mechs */
555 	mechanism_t	**tmechs_no_dups = NULL; /* temp mechs sans dups */
556 	int		ent_cnt = 0;		 /* valid cf file entry count */
557 	int		ent_cnt_no_dups = 0;	 /* valid cf count, no dups */
558 	static uint_t	last = 0;
559 	struct stat	sbuf;
560 	FILE 		*fptr;
561 
562 	if (stat(NIS_SEC_CF_PATHNAME, &sbuf) != 0)
563 		return (NULL);
564 
565 	(void) mutex_lock(&nis_sec_cf_lock);
566 	if (sbuf.st_mtime > last) {
567 		last = sbuf.st_mtime;
568 
569 		if (mechs) {
570 			/* free old master lists */
571 			__nis_release_mechanisms(mechs);
572 			if (mechs_no_dups)
573 				free(mechs_no_dups);
574 		}
575 		mechs = mechs_no_dups = NULL;
576 
577 		if (!(fptr = fopen(NIS_SEC_CF_PATHNAME, "rF"))) {
578 			(void) mutex_unlock(&nis_sec_cf_lock);
579 			return (NULL);
580 		}
581 
582 		while (mp = get_secfile_ent(fptr)) {
583 			/*
584 			 * Make sure entry is either the AUTH_DES compat
585 			 * one or a valid GSS one that is installed.
586 			 */
587 			if (!(AUTH_DES_COMPAT_CHK(mp) ||
588 				(NIS_SEC_CF_GSS_MECH(mp) &&
589 					rpc_gss_is_installed(mp->mechname)))) {
590 				continue;
591 			}
592 
593 			ent_cnt++;
594 			tmechs = (mechanism_t **)
595 			    list_append_ent((void *)mp, (void **)tmechs,
596 			    ent_cnt, (void (*)())sf_free_mech_ent);
597 			if (tmechs == NULL) {
598 				(void) fclose(fptr);
599 				(void) mutex_unlock(&nis_sec_cf_lock);
600 				return (NULL);
601 			}
602 
603 			if (member_of_dups(tmechs_no_dups, mp))
604 				continue;
605 
606 			ent_cnt_no_dups++;
607 			tmechs_no_dups = (mechanism_t **)
608 			    list_append_ent((void *)mp, (void **)tmechs_no_dups,
609 			    ent_cnt_no_dups, (void (*)())sf_free_mech_ent);
610 			if (tmechs_no_dups == NULL) {
611 				(void) fclose(fptr);
612 				(void) mutex_unlock(&nis_sec_cf_lock);
613 				return (NULL);
614 			}
615 		}
616 		(void) fclose(fptr);
617 
618 		/* set master lists to point to new built ones */
619 		mechs = tmechs;
620 		mechs_no_dups = tmechs_no_dups;
621 	}
622 	(void) mutex_unlock(&nis_sec_cf_lock);
623 
624 	if (qop_secserv)
625 		/* return a copy of the list with possible dups */
626 		return (mechs ?
627 			(mechanism_t **)list_copy(
628 				(void *(*)()) sf_copy_mech_ent,
629 				(void **)mechs) :
630 			NULL);
631 
632 	/* return a copy of the list without dups */
633 	return (mechs_no_dups ?
634 		(mechanism_t **)list_copy((void *(*)()) sf_copy_mech_ent,
635 						(void **)mechs_no_dups) :
636 		NULL);
637 }
638 
639 /*
640  * Search the mechs (no dups array) for an entry (mechname or alias)
641  * that matches (case not sig) the given mechname.  On target match,
642  * load the given memory locations pointed to by args keylen and
643  * algtype with values from the matched entry.
644  *
645  * The AUTH_DES "compat" line (alias == "des") will return 192-0
646  * (overriding the fields in the conf file).
647  *
648  * For any other entry, a conf file field of '-' (not applicable),
649  * in the keylen or algtype field will result in the locations for
650  * keylen and algtype being set to -1. (this is actually done in
651  * __nis_get_mechanisms()).
652  *
653  * Returns 0 on success and -1 on failure.
654  */
655 int
656 __nis_translate_mechanism(const char *mechname, int *keylen, int *algtype)
657 {
658 	mechanism_t **mpp;
659 	mechanism_t **mpp_h;
660 
661 	if (!mechname || !keylen || !algtype)
662 		return (-1);
663 
664 	/* AUTH_DES */
665 	if (strcmp(mechname, NIS_SEC_CF_DES_ALIAS) == 0) {
666 		*keylen = AUTH_DES_KEYLEN;
667 		*algtype = AUTH_DES_ALGTYPE;
668 		return (0);
669 	}
670 
671 	if (!(mpp = __nis_get_mechanisms(FALSE)))
672 		return (-1);
673 
674 	mpp_h = mpp;
675 	for (; *mpp; mpp++) {
676 		mechanism_t *mp = *mpp;
677 		if (mp->mechname &&
678 		    (!strcasecmp(mechname, mp->mechname))) {
679 				*keylen = mp->keylen;
680 				*algtype = mp->algtype;
681 				__nis_release_mechanisms(mpp_h);
682 				return (0);
683 		}
684 		if (mp->alias &&
685 		    (!strcasecmp(mechname, mp->alias))) {
686 				*keylen = mp->keylen;
687 				*algtype = mp->algtype;
688 				__nis_release_mechanisms(mpp_h);
689 				return (0);
690 		}
691 	}
692 
693 	__nis_release_mechanisms(mpp_h);
694 	return (-1);
695 }
696 
697 
698 /*
699  * Translate a mechname to an alias name.
700  *
701  * Returns alias on success or NULL on failure.
702  *
703  * Note alias will be the nullstring CSTYLE(on success) if cf
704  * alias field was "Not Applicable".
705  */
706 char *
707 __nis_mechname2alias(const char *mechname,	/* in */
708 			char *alias,		/* out */
709 			size_t bufsize)		/* in */
710 {
711 	mechanism_t **mpp;
712 	mechanism_t **mpp_h;
713 
714 	if (!mechname || !alias)
715 		return (NULL);
716 
717 	if (!(mpp = __nis_get_mechanisms(FALSE)))
718 		return (NULL);
719 
720 	mpp_h = mpp;
721 	for (; *mpp; mpp++) {
722 		mechanism_t *mp = *mpp;
723 		int len;
724 
725 		if (mp->mechname &&
726 		    (strcasecmp(mechname, mp->mechname) == 0)) {
727 			if (mp->alias) {
728 				if ((len = strlen(mp->alias)) < bufsize) {
729 					(void) strncpy(alias, mp->alias,
730 							len + 1);
731 					__nis_release_mechanisms(mpp_h);
732 					return (alias);
733 				}
734 			} else { /* cf file entry alias field was NA */
735 				alias[0] = '\0';
736 				__nis_release_mechanisms(mpp_h);
737 				return (alias);
738 			}
739 
740 		}
741 	}
742 
743 	__nis_release_mechanisms(mpp_h);
744 	return (NULL);
745 }
746 
747 void
748 __nis_release_mechanisms(mechanism_t **mpp)
749 {
750 	list_free_all(sf_free_mech_ent, (void **)mpp);
751 }
752 
753 /*
754  * Convert an authtype (ie. DH640-0) to mechanism alias (ie. dh640-0).
755  * Input the authtype ptr, the mechalis ptr and the size of the mechalias
756  * buf.
757  *
758  * If mechalias buf is not large enough, truncate and don't indicate failure.
759  *
760  * Return the mechalias ptr on success or NULL on failure CSTYLE(any of
761  * the input args are NULL/0).
762  */
763 char *
764 __nis_authtype2mechalias(
765 	const char *authtype,	/* in */
766 	char *mechalias,	/* out */
767 	size_t mechaliaslen)	/* in */
768 {
769 	char *dst = mechalias;
770 	const char *src = authtype;
771 	const char *max = src + mechaliaslen;
772 
773 	if (!src || !dst || mechaliaslen == 0)
774 		return (NULL);
775 
776 	while (*src && src < max - 1)
777 		*dst++ = tolower(*src++);
778 
779 	*dst = '\0';
780 
781 	return (mechalias);
782 }
783 
784 /*
785  * Convert an mechalias (ie. dh640-0) to authtype (ie. DH640-0).
786  * Input the authtype ptr, the mechalis ptr and the size of the mechalias
787  * buf.
788  *
789  * A special mechalias of "dh192-0" will get converted to "DES".
790  *
791  * If authtype buf is not large enough, truncate and don't indicate failure.
792  *
793  * Return the authtype ptr on success or NULL on failure (any of
794  * the input args are NULL/0.
795  */
796 char *
797 __nis_mechalias2authtype(
798 	const char *mechalias,	/* in */
799 	char *authtype,		/* out */
800 	size_t authtypelen)	/* in */
801 
802 {
803 	const char *src = mechalias;
804 	char *dst = authtype;
805 	const char *max = src + authtypelen;
806 	const int slen = strlen(cf_mech_dh1920_str);
807 
808 	if (!src || !dst || !authtypelen)
809 		return (NULL);
810 
811 	if (strncasecmp(src, cf_mech_dh1920_str, slen + 1)
812 	    == 0) {
813 		if (slen >= authtypelen)
814 			return (NULL);
815 		(void) strcpy(authtype, AUTH_DES_AUTH_TYPE);
816 		return (authtype);
817 	}
818 
819 	while (*src && src < max - 1)
820 		*dst++ = toupper(*src++);
821 
822 	*dst = '\0';
823 
824 	return (authtype);
825 }
826 
827 /*
828  * Given a DH key length and algorithm type, return the mech
829  * alias string.  If the keyalg is not the classic AUTH_DES,
830  * then search the NIS+ security cf.
831  *
832  * On success return the mech alias string address.  Return
833  * NULL on failure.  Failure occurs if keylen or algtype is
834  * not found or the length of the input buf is too small
835  * or input args are bogus.  Alias buf will not be
836  * changed on failure.
837  */
838 char *
839 __nis_keyalg2mechalias(
840 	keylen_t	keylen,		/* in */
841 	algtype_t	algtype,	/* in */
842 	char		*alias,		/* out */
843 	size_t		alias_len)	/* in */
844 {
845 	mechanism_t	**mechs;  /* array of mechanisms */
846 
847 	if (!alias)
848 		return (NULL);
849 
850 	if (AUTH_DES_KEY(keylen, algtype)) {
851 		if (alias_len > strlen(NIS_SEC_CF_DES_ALIAS)) {
852 			(void) strcpy(alias, NIS_SEC_CF_DES_ALIAS);
853 			return (alias);
854 		}
855 		else
856 			return (NULL);
857 	} else
858 		if (mechs = __nis_get_mechanisms(FALSE)) {
859 			mechanism_t **mpp;
860 
861 			for (mpp = mechs; *mpp; mpp++) {
862 				mechanism_t *mp = *mpp;
863 
864 				if (!VALID_MECH_ENTRY(mp) ||
865 				    AUTH_DES_COMPAT_CHK(mp))
866 					continue;
867 
868 				if (keylen == mp->keylen &&
869 				    algtype == mp->algtype && mp->alias) {
870 					int al_len = strlen(mp->alias);
871 
872 					if (alias_len > al_len) {
873 						(void) strncpy(alias, mp->alias,
874 							al_len + 1);
875 						return (alias);
876 					} else {
877 						__nis_release_mechanisms(mechs);
878 						return (NULL);
879 					}
880 				}
881 			}
882 			__nis_release_mechanisms(mechs);
883 		}
884 
885 	return (NULL);
886 }
887 
888 /*
889  * Given the key length and algorithm type, return the auth type
890  * string suitable for the cred table.
891  *
892  * Return the authtype on success and NULL on failure.
893  */
894 char *
895 __nis_keyalg2authtype(
896 	keylen_t keylen,	/* in */
897 	algtype_t algtype,	/* in */
898 	char *authtype,		/* out */
899 	size_t authtype_len)	/* in */
900 {
901 	char alias[MECH_MAXALIASNAME+1] = {0};
902 
903 
904 	if (!authtype || authtype_len == 0)
905 		return (NULL);
906 
907 	if (!__nis_keyalg2mechalias(keylen, algtype, alias, sizeof (alias)))
908 		return (NULL);
909 
910 	if (!__nis_mechalias2authtype(alias, authtype, authtype_len))
911 		return (NULL);
912 
913 	return (authtype);
914 }
915 
916 /*
917  * Return a ptr to the next mech file entry or NULL on EOF.
918  * The caller should free the storage of a successful return.
919  */
920 static mfent_t *
921 get_mechfile_ent(FILE *fptr)
922 {
923 	mfent_t		*m;
924 	char		*cp;
925 	char		**flds;
926 	char		line[MF_MAX_LINELEN] = {0};
927 
928 
929 cont:
930 	while (((cp = nextline(fptr, line)) != NULL) &&
931 		(*cp == '#' || *cp == '\0'))
932 		;
933 	if (cp == NULL)
934 		return (NULL);
935 
936 	if (!(flds = parse_line(cp, mech_file_flds_min,
937 					mech_file_flds_max, MF_MAX_FLDLEN)))
938 		goto cont;
939 
940 	if (!(m = malloc(sizeof (mfent_t)))) {
941 		free_fields(flds, mech_file_flds_max);
942 		return (NULL);
943 	}
944 
945 	m->mechname = strdup(*flds);
946 	m->oid = strdup(*(flds + 1));
947 	m->libname = strdup(*(flds + 2));
948 
949 	free_fields(flds, mech_file_flds_max);
950 
951 	return (m);
952 }
953 
954 static mfent_t *
955 mf_copy_ent(mfent_t *mp)
956 {
957 	mfent_t *tp = calloc(1, sizeof (*mp));
958 
959 	if (!mp || !tp)
960 		return (NULL);
961 
962 	tp->mechname = mp->mechname ? strdup(mp->mechname) : NULL;
963 	tp->oid = mp->oid ? strdup(mp->oid) : NULL;
964 	tp->libname = mp->libname ? strdup(mp->libname) : NULL;
965 
966 	return (tp);
967 }
968 
969 
970 static void
971 mf_free_ent(mfent_t *mp)
972 {
973 	if (mp) {
974 		if (mp->mechname)
975 			free(mp->mechname);
976 		if (mp->oid)
977 			free(mp->oid);
978 		if (mp->libname)
979 			free(mp->libname);
980 		free(mp);
981 	}
982 }
983 
984 
985 static void
986 mf_free_mechs(mfent_t **mpp)
987 {
988 	list_free_all(mf_free_ent, (void **)mpp);
989 }
990 
991 /*
992  * Return a copy of the list of the mech file entries.  The ptr to the last
993  * entry will be NULL on success.  The master list will be updated when
994  * the mechs file is modified.
995  *
996  * Return NULL if the file does not exist or no valid mechs exist in the
997  * file.
998  */
999 static mfent_t **
1000 mf_get_mechs()
1001 {
1002 	static mfent_t	**mechs = NULL;		/* master mechs list */
1003 	mfent_t		*mp;			/* a mech entry */
1004 	mfent_t		**tmechs = NULL;	/* temp mechs list */
1005 	uint_t		ent_cnt = 0;		/* valid cf file entry count */
1006 	static uint_t	last = 0;		/* file last modified date */
1007 	struct stat	sbuf;
1008 	FILE 		*fptr;
1009 
1010 	if (stat(mech_file, &sbuf) != 0)
1011 		return (NULL);
1012 
1013 	(void) mutex_lock(&mech_file_lock);
1014 	if (sbuf.st_mtime > last) {
1015 		last = sbuf.st_mtime;
1016 
1017 		if (mechs) {
1018 			/* free old master list */
1019 			mf_free_mechs(mechs);
1020 			mechs = NULL;
1021 		}
1022 
1023 		if (!(fptr = fopen(mech_file, "rF"))) {
1024 			(void) mutex_unlock(&mech_file_lock);
1025 			return (NULL);
1026 		}
1027 
1028 		while (mp = get_mechfile_ent(fptr)) {
1029 			ent_cnt++;
1030 			tmechs = (mfent_t **)list_append_ent((void *)mp,
1031 			    (void **)tmechs, ent_cnt, (void (*)()) mf_free_ent);
1032 			if (tmechs == NULL) {
1033 				(void) fclose(fptr);
1034 				(void) mutex_unlock(&mech_file_lock);
1035 				return (NULL);
1036 			}
1037 		}
1038 		(void) fclose(fptr);
1039 
1040 		mechs = tmechs;  /* set master list to pt to newly built one */
1041 	}
1042 	(void) mutex_unlock(&mech_file_lock);
1043 
1044 	/* return a copy of the master list */
1045 	return (mechs ? (mfent_t **)list_copy((void *(*)()) mf_copy_ent,
1046 						(void **)mechs) : NULL);
1047 }
1048 
1049 /*
1050  * Translate a full mechname to it's corresponding library name
1051  * as specified in the mech file.
1052  */
1053 char *
1054 mechfile_name2lib(const char *mechname, char *libname, int len)
1055 {
1056 	mfent_t **mechs = mf_get_mechs();
1057 	mfent_t **mpp;
1058 
1059 	if (!mechs || !mechname || !libname || !len)
1060 		return (NULL);
1061 
1062 	for (mpp = mechs; *mpp; mpp++) {
1063 		mfent_t *mp = *mpp;
1064 
1065 		if (mp->mechname && strcasecmp(mechname, mp->mechname) == 0) {
1066 			if (strlen(mp->libname) < len) {
1067 				(void) strcpy(libname, mp->libname);
1068 					mf_free_mechs(mechs);
1069 					return (libname);
1070 				}
1071 		}
1072 	}
1073 	mf_free_mechs(mechs);
1074 
1075 	return (NULL);
1076 }
1077 
1078 /*
1079  * Given a key length and algo type, return the appro DH mech library
1080  * name.
1081  */
1082 char *
1083 __nis_get_mechanism_library(keylen_t keylen, algtype_t algtype,
1084 			    char *buffer, size_t buflen)
1085 {
1086 	char mechname[MAXDHNAME + 1];
1087 
1088 	if (keylen == 0 || !buffer || buflen == 0)
1089 		return (NULL);
1090 
1091 	(void) snprintf(mechname, sizeof (mechname),
1092 					"%s_%d_%d", dh_str, keylen, algtype);
1093 
1094 	if (!mechfile_name2lib(mechname, buffer, buflen))
1095 		return (NULL);
1096 
1097 	return (buffer);
1098 }
1099 
1100 /*
1101  * Input key length, algorithm type, and a string identifying a symbol
1102  * (example: "__gen_dhkeys").
1103  *
1104  * Returns a function pointer to the specified symbol in the appropriate
1105  * key length/algorithm type library, or NULL if the symbol isn't found.
1106  */
1107 void *
1108 __nis_get_mechanism_symbol(keylen_t keylen,
1109 				algtype_t algtype,
1110 				const char *symname)
1111 
1112 {
1113 	void *handle;
1114 	char libname[MAXDHNAME+1];
1115 	char libpath[MAXPATHLEN+1];
1116 
1117 	if (!__nis_get_mechanism_library(keylen, algtype, libname, MAXDHNAME))
1118 		return (NULL);
1119 
1120 	if (strlen(MECH_LIB_PREFIX) + strlen(libname) + 1 > sizeof (libpath))
1121 		return (NULL);
1122 
1123 	(void) snprintf(libpath, sizeof (libpath),
1124 					"%s%s", MECH_LIB_PREFIX, libname);
1125 
1126 	if (!(handle = dlopen(libpath, RTLD_LAZY)))
1127 		return (NULL);
1128 
1129 	return (dlsym(handle, symname));
1130 }
1131