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