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