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