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