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 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 ** 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 ** 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 * 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 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 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 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 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 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 ** 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 * 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 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 * 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 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 ** 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 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 * 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 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 * 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 * 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 * 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 * 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 * 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 * 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 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 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 ** 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 * 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 * 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 * 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