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
list_free_all(void (* free_ent)(),void ** mpp)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 **
list_append_ent(void * ent,void ** list,uint_t cnt,void (* free_ent)())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 **
list_copy(void * (* cp_ent)(),void ** mpp)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 *
nextline(fd,line)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
nextfield(char ** cpp,char * op,int n)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
str_to_secserv_t(const char * s)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
isnumberstr(const char * s)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
sf_free_mech_ent(mechanism_t * mp)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
free_fields(char ** cpp,int cnt)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 **
parse_line(char * linep,int minflds,int maxflds,int bufsiz)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 *
get_secfile_ent(FILE * fptr)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
equal_entries(const mechanism_t * mp,const mechanism_t * tp)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 *
sf_copy_mech_ent(mechanism_t * mp)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
member_of_dups(mechanism_t ** t,const mechanism_t * mp)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 **
__nis_get_mechanisms(bool_t qop_secserv)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
__nis_translate_mechanism(const char * mechname,int * keylen,int * algtype)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 *
__nis_mechname2alias(const char * mechname,char * alias,size_t bufsize)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
__nis_release_mechanisms(mechanism_t ** mpp)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 *
__nis_authtype2mechalias(const char * authtype,char * mechalias,size_t mechaliaslen)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 *
__nis_mechalias2authtype(const char * mechalias,char * authtype,size_t authtypelen)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 *
__nis_keyalg2mechalias(keylen_t keylen,algtype_t algtype,char * alias,size_t alias_len)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 *
__nis_keyalg2authtype(keylen_t keylen,algtype_t algtype,char * authtype,size_t authtype_len)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 *
get_mechfile_ent(FILE * fptr)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 *
mf_copy_ent(mfent_t * mp)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
mf_free_ent(mfent_t * mp)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
mf_free_mechs(mfent_t ** mpp)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 **
mf_get_mechs()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 *
mechfile_name2lib(const char * mechname,char * libname,int len)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 *
__nis_get_mechanism_library(keylen_t keylen,algtype_t algtype,char * buffer,size_t buflen)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 *
__nis_get_mechanism_symbol(keylen_t keylen,algtype_t algtype,const char * symname)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