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