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 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 /* 38 * publickey.c 39 * 40 * 41 * Public and Private (secret) key lookup routines. These functions 42 * are used by the secure RPC auth_des flavor to get the public and 43 * private keys for secure RPC principals. Originally designed to 44 * talk only to YP, AT&T modified them to talk to files, and now 45 * they can also talk to NIS+. The policy for these lookups is now 46 * defined in terms of the nameservice switch as follows : 47 * publickey: nis files 48 * 49 * Note : 50 * 1. NIS+ combines the netid.byname and publickey.byname maps 51 * into a single NIS+ table named cred.org_dir 52 * 2. To use NIS+, the publickey needs to be 53 * publickey: nisplus 54 * (or have nisplus as the first entry). 55 * The nsswitch.conf file should be updated after running nisinit 56 * to reflect this. 57 */ 58 #include "mt.h" 59 #include "../rpc/rpc_mt.h" 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <syslog.h> 64 #include <assert.h> 65 #include <sys/types.h> 66 #include <pwd.h> 67 #include "nsswitch.h" 68 #include <rpc/rpc.h> 69 #include <rpc/key_prot.h> 70 #include <rpcsvc/nis.h> 71 #include <rpcsvc/ypclnt.h> 72 #include <rpcsvc/nis_dhext.h> 73 #include <thread.h> 74 #include "../nis/gen/nis_clnt.h" 75 #include <nss_dbdefs.h> 76 77 static const char *PKTABLE = "cred.org_dir"; 78 static const char *PKMAP = "publickey.byname"; 79 static const char *PKFILE = "/etc/publickey"; 80 static const char dh_caps_str[] = "DH"; 81 static const char des_caps_str[] = AUTH_DES_AUTH_TYPE; 82 83 static char *netname2hashname(const char *, char *, int, keylen_t, 84 algtype_t); 85 86 #define PKTABLE_LEN 12 87 #define WORKBUFSIZE 1024 88 89 extern int xdecrypt(); 90 91 extern int __yp_match_cflookup(char *, char *, char *, int, char **, 92 int *, int *); 93 94 95 /* 96 * default publickey policy: 97 * publickey: nis [NOTFOUND = return] files 98 */ 99 100 101 /* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */ 102 #define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE} 103 104 static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL}, 105 lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files}; 106 static struct __nsw_switchconfig publickey_default = 107 {0, "publickey", 2, &lookup_nis}; 108 109 #ifndef NUL 110 #define NUL '\0' 111 #endif 112 113 extern mutex_t serialize_pkey; 114 115 static void pkey_cache_add(); 116 static int pkey_cache_get(); 117 static void pkey_cache_flush(); 118 119 static int extract_secret(); 120 121 /* 122 * db_root is used for switch backends. 123 */ 124 static DEFINE_NSS_DB_ROOT(db_root); 125 126 /* 127 * str2key 128 */ 129 /* ARGSUSED */ 130 static int 131 str2key(const char *instr, int lenstr, 132 void *ent, char *buffer, int buflen) { 133 if (lenstr + 1 > buflen) 134 return (NSS_STR_PARSE_ERANGE); 135 /* 136 * We copy the input string into the output buffer 137 */ 138 (void) memcpy(buffer, instr, lenstr); 139 buffer[lenstr] = '\0'; 140 141 return (NSS_STR_PARSE_SUCCESS); 142 } 143 /* 144 * These functions are the "backends" for the switch for public keys. They 145 * get both the public and private keys from each of the supported name 146 * services (nis, nisplus, files). They are passed the appropriate parameters 147 * and return 0 if unsuccessful with *errp set, or 1 when they got just the 148 * public key and 3 when they got both the public and private keys. 149 * 150 * 151 * getkey_nis() 152 * 153 * Internal implementation of getpublickey() using NIS (aka Yellow Pages, 154 * aka YP). 155 * 156 * NOTE : *** this function returns nsswitch codes and _not_ the 157 * value returned by getpublickey. 158 */ 159 static int 160 getkeys_nis(int *errp, char *netname, char *pkey, char *skey, char *passwd) 161 { 162 char *domain; 163 char *keyval = NULL; 164 int keylen, err, r = 0; 165 char *p; 166 int len; 167 168 p = strchr(netname, '@'); 169 if (!p) { 170 *errp = __NSW_UNAVAIL; 171 return (0); 172 } 173 174 domain = ++p; 175 176 177 /* 178 * Instead of calling yp_match(), we use __yp_match_cflookup() here 179 * which has time-out control for the binding operation to nis 180 * servers. 181 */ 182 err = __yp_match_cflookup(domain, (char *)PKMAP, netname, 183 strlen(netname), &keyval, &keylen, 0); 184 185 switch (err) { 186 case YPERR_KEY : 187 if (keyval) 188 free(keyval); 189 *errp = __NSW_NOTFOUND; 190 return (0); 191 default : 192 if (keyval) 193 free(keyval); 194 *errp = __NSW_UNAVAIL; 195 return (0); 196 case 0: 197 break; 198 } 199 200 p = strchr(keyval, ':'); 201 if (p == NULL) { 202 free(keyval); 203 *errp = __NSW_NOTFOUND; 204 return (0); 205 } 206 *p = 0; 207 if (pkey) { 208 len = strlen(keyval); 209 if (len > HEXKEYBYTES) { 210 free(keyval); 211 *errp = __NSW_NOTFOUND; 212 return (0); 213 } 214 (void) strcpy(pkey, keyval); 215 } 216 r = 1; 217 p++; 218 if (skey && extract_secret(p, skey, passwd)) 219 r |= 2; 220 free(keyval); 221 *errp = __NSW_SUCCESS; 222 return (r); 223 } 224 225 /* 226 * getkey_files() 227 * 228 * The files version of getpublickey. This function attempts to 229 * get the publickey from the file PKFILE . 230 * 231 * This function defines the format of the /etc/publickey file to 232 * be : 233 * netname <whitespace> publickey:privatekey 234 * 235 * NOTE : *** this function returns nsswitch codes and _not_ the 236 * value returned by getpublickey. 237 */ 238 239 static int 240 getkeys_files(int *errp, char *netname, char *pkey, char *skey, char *passwd) 241 { 242 char *mkey; 243 char *mval; 244 char buf[WORKBUFSIZE]; 245 int r = 0; 246 char *res; 247 FILE *fd; 248 char *p; 249 char *lasts; 250 251 fd = fopen(PKFILE, "rF"); 252 if (fd == NULL) { 253 *errp = __NSW_UNAVAIL; 254 return (0); 255 } 256 257 /* Search through the file linearly :-( */ 258 while ((res = fgets(buf, WORKBUFSIZE, fd)) != NULL) { 259 260 if ((res[0] == '#') || (res[0] == '\n')) 261 continue; 262 else { 263 mkey = strtok_r(buf, "\t ", &lasts); 264 if (mkey == NULL) { 265 syslog(LOG_INFO, 266 "getpublickey: Bad record in %s for %s", 267 PKFILE, netname); 268 continue; 269 } 270 mval = strtok_r(NULL, " \t#\n", &lasts); 271 if (mval == NULL) { 272 syslog(LOG_INFO, 273 "getpublickey: Bad record in %s for %s", 274 PKFILE, netname); 275 continue; 276 } 277 /* NOTE : Case insensitive compare. */ 278 if (strcasecmp(mkey, netname) == 0) { 279 p = strchr(mval, ':'); 280 if (p == NULL) { 281 syslog(LOG_INFO, 282 "getpublickey: Bad record in %s for %s", 283 PKFILE, netname); 284 continue; 285 } 286 287 *p = 0; 288 if (pkey) { 289 int len = strlen(mval); 290 291 if (len > HEXKEYBYTES) { 292 syslog(LOG_INFO, 293 "getpublickey: Bad record in %s for %s", 294 PKFILE, netname); 295 continue; 296 } 297 (void) strcpy(pkey, mval); 298 } 299 r = 1; 300 p++; 301 if (skey && extract_secret(p, skey, passwd)) 302 r |= 2; 303 (void) fclose(fd); 304 *errp = __NSW_SUCCESS; 305 return (r); 306 } 307 } 308 } 309 310 (void) fclose(fd); 311 *errp = __NSW_NOTFOUND; 312 return (0); 313 } 314 315 /* 316 * getpublickey(netname, key) 317 * 318 * This is the actual exported interface for this function. 319 */ 320 321 int 322 __getpublickey_cached(char *netname, char *pkey, int *from_cache) 323 { 324 return (__getpublickey_cached_g(netname, KEYSIZE, 0, pkey, 325 HEXKEYBYTES+1, from_cache)); 326 } 327 328 int 329 getpublickey(const char *netname, char *pkey) 330 { 331 return (__getpublickey_cached((char *)netname, pkey, (int *)0)); 332 } 333 334 void 335 __getpublickey_flush(const char *netname) 336 { 337 __getpublickey_flush_g(netname, 192, 0); 338 } 339 340 int 341 getsecretkey(const char *netname, char *skey, const char *passwd) 342 { 343 return (getsecretkey_g(netname, KEYSIZE, 0, skey, HEXKEYBYTES+1, 344 passwd)); 345 } 346 347 /* 348 * Routines to cache publickeys. 349 */ 350 351 static NIS_HASH_TABLE pkey_tbl; 352 struct pkey_item { 353 NIS_HASH_ITEM item; 354 char *pkey; 355 }; 356 357 static void 358 pkey_cache_add(const char *netname, char *pkey) 359 { 360 struct pkey_item *item; 361 362 if (!netname || !pkey) { 363 return; 364 } 365 366 item = calloc(1, sizeof (struct pkey_item)); 367 if (item == NULL) { 368 return; 369 } 370 item->item.name = strdup(netname); 371 if (item->item.name == NULL) { 372 free((void *)item); 373 return; 374 } 375 item->pkey = strdup(pkey); 376 if (item->pkey == 0) { 377 free(item->item.name); 378 free(item); 379 return; 380 } 381 382 (void) mutex_lock(&serialize_pkey); 383 if (!nis_insert_item((NIS_HASH_ITEM *)item, &pkey_tbl)) { 384 free(item->item.name); 385 free(item->pkey); 386 free((void *)item); 387 } 388 (void) mutex_unlock(&serialize_pkey); 389 } 390 391 static int 392 pkey_cache_get(const char *netname, char *pkey) 393 { 394 struct pkey_item *item; 395 396 if (!netname || !pkey) { 397 return (0); 398 } 399 400 (void) mutex_lock(&serialize_pkey); 401 item = (struct pkey_item *)nis_find_item((char *)netname, &pkey_tbl); 402 if (item) { 403 (void) strcpy(pkey, item->pkey); 404 } 405 406 (void) mutex_unlock(&serialize_pkey); 407 return (item != 0); 408 } 409 410 static void 411 pkey_cache_flush(const char *netname) 412 { 413 struct pkey_item *item; 414 415 (void) mutex_lock(&serialize_pkey); 416 417 item = (struct pkey_item *)nis_remove_item((char *)netname, &pkey_tbl); 418 if (item) { 419 free(item->item.name); 420 free(item->pkey); 421 free((void *)item); 422 } 423 (void) mutex_unlock(&serialize_pkey); 424 } 425 426 /* 427 * To avoid a potential deadlock with a single root domain NIS+ server 428 * talking to a sub-sub-domain server, the NIS+ directory cache manager 429 * code must be able to add public keys to the cache. Hence this routine. 430 */ 431 void 432 __pkey_cache_add(char *netname, char *pkey, keylen_t pkeylen, 433 algtype_t algtype) { 434 435 char hashname[MAXNETNAMELEN+1]; 436 437 pkey_cache_add(netname2hashname(netname, hashname, sizeof (hashname), 438 pkeylen, algtype), pkey); 439 } 440 441 /* 442 * Generic DH (any size keys) version of extract_secret. 443 */ 444 static int 445 extract_secret_g( 446 char *raw, /* in */ 447 char *private, /* out */ 448 int prilen, /* in */ 449 char *passwd, /* in */ 450 char *netname, /* in */ 451 keylen_t keylen, /* in */ 452 algtype_t algtype) /* in */ 453 454 { 455 char *buf = malloc(strlen(raw) + 1); /* private tmp buf */ 456 char *p; 457 458 if (!buf || !passwd || !raw || !private || !prilen || 459 !VALID_KEYALG(keylen, algtype)) { 460 if (private) 461 *private = NUL; 462 if (buf) 463 free(buf); 464 return (0); 465 } 466 467 (void) strcpy(buf, raw); 468 469 /* strip off pesky colon if it exists */ 470 p = strchr(buf, ':'); 471 if (p) { 472 *p = 0; 473 } 474 475 /* raw buf has chksum appended, so let's verify it too */ 476 if (!xdecrypt_g(buf, keylen, algtype, passwd, netname, TRUE)) { 477 private[0] = 0; 478 free(buf); 479 return (1); /* yes, return 1 even if xdecrypt fails */ 480 } 481 482 if (strlen(buf) >= prilen) { 483 private[0] = 0; 484 free(buf); 485 return (0); 486 } 487 488 (void) strcpy(private, buf); 489 free(buf); 490 return (1); 491 } 492 493 /* 494 * extract_secret() 495 * 496 * This generic function will extract the private key 497 * from a string using the given password. Note that 498 * it uses the DES based function xdecrypt() 499 */ 500 static int 501 extract_secret(char *raw, char *private, char *passwd) 502 { 503 return (extract_secret_g(raw, private, HEXKEYBYTES+1, passwd, 504 NULL, KEYSIZE, 0)); 505 } 506 507 /* 508 * getkeys_nisplus_g() 509 * 510 * Fetches the key pair from NIS+. This version handles any size 511 * DH keys. 512 */ 513 static int 514 getkeys_nisplus_g( 515 int *err, /* in */ 516 char *netname, /* in */ 517 char *pkey, /* out */ 518 int pkeylen, /* in */ 519 char *skey, /* out */ 520 int skeylen, /* in */ 521 char *passwd, /* in */ 522 keylen_t keylen, /* in */ 523 algtype_t algtype, /* in */ 524 int *retry_cache) /* in/out */ 525 { 526 nis_result *res; 527 int r = 0; 528 char *domain, *p; 529 char buf[NIS_MAXNAMELEN+1]; 530 char keytypename[NIS_MAXNAMELEN+1]; 531 int len; 532 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 533 534 domain = strchr(netname, '@'); 535 if (!domain) { 536 *err = __NSW_UNAVAIL; 537 return (0); 538 } 539 domain++; 540 541 if (retry_cache != NULL && *retry_cache == 1) { 542 nis_error bcerr; 543 directory_obj obj; 544 char hashname[MAXNETNAMELEN]; 545 546 bcerr = __nis_CacheBind(domain, &obj); 547 xdr_free(xdr_directory_obj, (char *)&obj); 548 /* 549 * Even if the __nis_CacheBind failed, we may have reached 550 * part-way to the goal, so have another look in the public 551 * key cache. 552 */ 553 if (pkey_cache_get(netname2hashname(netname, hashname, 554 MAXNETNAMELEN, pkeylen, 555 algtype), pkey)) { 556 *err = __NSW_SUCCESS; 557 return (1); 558 } 559 *retry_cache = 0; 560 } 561 562 if ((strlen(netname)+PKTABLE_LEN+strlen(domain)+32) > 563 (size_t)NIS_MAXNAMELEN) { 564 *err = __NSW_UNAVAIL; 565 return (0); 566 } 567 568 /* 569 * Cred table has following format for PK crypto entries: 570 * cname auth_type auth_name public private 571 * ---------------------------------------------------------- 572 * nisname AT netname pubkey prikey 573 * 574 * where AT can be "DES" for classic AUTH_DES, or something like 575 * "DH640-0" for a longer Diffie-Hellman key pair. 576 */ 577 if (classic_des) 578 (void) strcpy(keytypename, des_caps_str); 579 else 580 (void) sprintf(keytypename, "%s%d-%d", 581 dh_caps_str, keylen, algtype); 582 (void) sprintf(buf, "[auth_name=\"%s\",auth_type=%s],%s.%s", 583 netname, keytypename, PKTABLE, domain); 584 if (buf[strlen(buf)-1] != '.') 585 (void) strcat(buf, "."); 586 587 /* 588 * Because of some bootstrapping issues (keylogin, etc) the 589 * keys lookup needs to be done without auth. This is 590 * less-then-ideal from a security perspective and hopefully 591 * will be revisited soon... 592 */ 593 res = nis_list(buf, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, 594 NULL, NULL); 595 switch (res->status) { 596 case NIS_SUCCESS: 597 case NIS_S_SUCCESS: 598 break; 599 case NIS_NOTFOUND: 600 case NIS_PARTIAL: 601 case NIS_NOSUCHNAME: 602 case NIS_NOSUCHTABLE: 603 nis_freeresult(res); 604 *err = __NSW_NOTFOUND; 605 return (0); 606 case NIS_S_NOTFOUND: 607 case NIS_TRYAGAIN: 608 syslog(LOG_ERR, "getkeys: (nis+ key lookup): %s\n", 609 nis_sperrno(res->status)); 610 nis_freeresult(res); 611 *err = __NSW_TRYAGAIN; 612 return (0); 613 default: 614 *err = __NSW_UNAVAIL; 615 syslog(LOG_ERR, 616 "getkeys: (nis+ key lookup): %s\n", 617 nis_sperrno(res->status)); 618 nis_freeresult(res); 619 return (0); 620 } 621 622 if (pkey) { 623 char *key_start; 624 char *colon_pos; 625 626 /* 627 * For backward compatibility with the old 628 * cred.org_dir format, first locate the ":", if any, 629 * and prepare to null it out. 630 */ 631 key_start = (char *)ENTRY_VAL(res->objects.objects_val, 3); 632 colon_pos = strchr(key_start, ':'); 633 634 /* 635 * Set the value of len keeping in mind that both len 636 * and pklen include space for the terminating null. 637 */ 638 639 if (colon_pos != NULL) 640 /* 641 * Set len to include the colon because that is 642 * where the terminating null will be 643 * placed. 644 */ 645 len = (int)((uintptr_t)colon_pos - 646 (uintptr_t)key_start + 1); 647 else 648 /* 649 * ENTRY_LEN already includes the terminating 650 * null. 651 */ 652 len = ENTRY_LEN(res->objects.objects_val, 3); 653 654 if (len > pkeylen) { 655 *err = __NSW_UNAVAIL; 656 syslog(LOG_ERR, 657 "getkeys(nis+): pub key for '%s' (keytype = '%s') too long", 658 netname, keytypename); 659 nis_freeresult(res); 660 return (0); 661 } 662 663 (void) strncpy(pkey, key_start, len); 664 665 /* 666 * Now null out the colon if it exists 667 */ 668 if (colon_pos != NULL) 669 pkey[len-1] = NULL; 670 671 } 672 r = 1; /* At least public key was found; always true at this point */ 673 674 if (skey && extract_secret_g(ENTRY_VAL(res->objects.objects_val, 4), 675 skey, skeylen, passwd, netname, keylen, 676 algtype)) 677 r |= 2; 678 679 nis_freeresult(res); 680 *err = __NSW_SUCCESS; 681 return (r); 682 } 683 684 685 /* 686 * getkeys_ldap_g() 687 * 688 * Fetches the key pair from LDAP. This version handles any size 689 * DH keys. 690 */ 691 692 void 693 _nss_initf_publickey(nss_db_params_t *p) 694 { 695 p->name = NSS_DBNAM_PUBLICKEY; 696 p->default_config = NSS_DEFCONF_PUBLICKEY; 697 } 698 699 700 static int 701 getkeys_ldap_g( 702 int *err, /* in */ 703 char *netname, /* in */ 704 char *pkey, /* out */ 705 int pkeylen, /* in */ 706 char *skey, /* out */ 707 int skeylen, /* in */ 708 char *passwd, /* in */ 709 keylen_t keylen, /* in */ 710 algtype_t algtype) /* in */ 711 { 712 int r = 0; 713 char *p; 714 char keytypename[NIS_MAXNAMELEN+1]; 715 int len; 716 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 717 int rc = 0; 718 nss_XbyY_args_t arg; 719 nss_XbyY_buf_t *buf = NULL; 720 char *keyval; 721 722 NSS_XbyY_ALLOC(&buf, 0, NSS_BUFLEN_PUBLICKEY); 723 724 NSS_XbyY_INIT(&arg, buf->result, buf->buffer, buf->buflen, str2key); 725 arg.key.pkey.name = netname; 726 727 /* 728 * LDAP stores the public and secret key info in entries using 729 * nisKeyObject objectclass. Each key is tagged with the 730 * keytype, keylength, and algorithm. The tag has the following 731 * format: {<keytype><keylength>-<algorithm>}. For example, 732 * {DH192-0}. 733 */ 734 if (classic_des) 735 (void) strcpy(keytypename, "{DH192-0}"); 736 else 737 (void) sprintf(keytypename, "{%s%d-%d}", 738 dh_caps_str, keylen, algtype); 739 arg.key.pkey.keytype = keytypename; 740 741 if (nss_search(&db_root, _nss_initf_publickey, NSS_DBOP_KEYS_BYNAME, 742 &arg) != NSS_SUCCESS) { 743 NSS_XbyY_FREE(&buf); 744 *err = __NSW_NOTFOUND; 745 return (0); 746 } 747 keyval = buf->buffer; 748 p = strchr(keyval, ':'); 749 if (p == NULL) { 750 NSS_XbyY_FREE(&buf); 751 *err = __NSW_NOTFOUND; 752 return (0); 753 } 754 *p = 0; 755 if (pkey) { 756 len = strlen(keyval); 757 if (len > HEXKEYBYTES) { 758 NSS_XbyY_FREE(&buf); 759 *err = __NSW_NOTFOUND; 760 return (0); 761 } 762 (void) strcpy(pkey, keyval); 763 } 764 r = 1; 765 p++; 766 if (skey && extract_secret(p, skey, passwd)) 767 r |= 2; 768 NSS_XbyY_FREE(&buf); 769 *err = __NSW_SUCCESS; 770 return (r); 771 } 772 773 774 /* 775 * Convert a netname to a name we will hash on. For classic_des, 776 * just copy netname as is. But for new and improved ("now in 777 * new longer sizes!") DHEXT, add a ":keylen-algtype" suffix to hash on. 778 * 779 * Returns the hashname string on success or NULL on failure. 780 */ 781 static char * 782 netname2hashname( 783 const char *netname, 784 char *hashname, 785 int bufsiz, 786 keylen_t keylen, 787 algtype_t algtype) 788 { 789 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 790 791 if (!netname || !hashname || !bufsiz) 792 return (NULL); 793 794 if (classic_des) { 795 if (bufsiz > strlen(netname)) 796 (void) strcpy(hashname, netname); 797 else 798 return (NULL); 799 } else { 800 char tmp[128]; 801 (void) sprintf(tmp, ":%d-%d", keylen, algtype); 802 if (bufsiz > (strlen(netname) + strlen(tmp))) 803 (void) sprintf(hashname, "%s%s", netname, tmp); 804 else 805 return (NULL); 806 } 807 808 return (hashname); 809 } 810 811 /* 812 * Flush netname's publickey of the given key length and algorithm type. 813 */ 814 void 815 __getpublickey_flush_g(const char *netname, keylen_t keylen, algtype_t algtype) 816 { 817 char *p, hashname[MAXNETNAMELEN+1]; 818 819 p = netname2hashname(netname, hashname, MAXNETNAMELEN, keylen, algtype); 820 if (p) 821 pkey_cache_flush(hashname); 822 } 823 824 825 /* 826 * Generic DH (any size keys) version of __getpublickey_cached. 827 */ 828 int 829 __getpublickey_cached_g(const char netname[], /* in */ 830 keylen_t keylen, /* in */ 831 algtype_t algtype, /* in */ 832 char *pkey, /* out */ 833 size_t pkeylen, /* in */ 834 int *from_cache) /* in/out */ 835 { 836 int needfree = 1, res, err; 837 struct __nsw_switchconfig *conf; 838 struct __nsw_lookup *look; 839 enum __nsw_parse_err perr; 840 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 841 int retry_cache = 0; 842 843 if (!netname || !pkey) 844 return (0); 845 846 if (from_cache) { 847 char hashname[MAXNETNAMELEN]; 848 if (pkey_cache_get(netname2hashname(netname, hashname, 849 MAXNETNAMELEN, keylen, 850 algtype), pkey)) { 851 *from_cache = 1; 852 return (1); 853 } 854 *from_cache = 0; 855 retry_cache = 1; 856 } 857 858 conf = __nsw_getconfig("publickey", &perr); 859 if (!conf) { 860 conf = &publickey_default; 861 needfree = 0; 862 } 863 for (look = conf->lookups; look; look = look->next) { 864 if (strcmp(look->service_name, "nisplus") == 0) { 865 res = getkeys_nisplus_g(&err, (char *)netname, 866 pkey, pkeylen, NULL, 0, NULL, 867 keylen, algtype, &retry_cache); 868 if (retry_cache) 869 *from_cache = 1; 870 } else if (strcmp(look->service_name, "ldap") == 0) { 871 res = getkeys_ldap_g(&err, (char *)netname, 872 pkey, pkeylen, NULL, 0, NULL, 873 keylen, algtype); 874 /* long DH keys will not be in nis or files */ 875 } else if (classic_des && 876 strcmp(look->service_name, "nis") == 0) 877 res = getkeys_nis(&err, (char *)netname, pkey, 878 NULL, NULL); 879 else if (classic_des && 880 strcmp(look->service_name, "files") == 0) 881 res = getkeys_files(&err, (char *)netname, pkey, 882 NULL, NULL); 883 else { 884 syslog(LOG_INFO, "Unknown publickey nameservice '%s'", 885 look->service_name); 886 err = __NSW_UNAVAIL; 887 } 888 889 /* 890 * If we found the publickey, save it in the cache. 891 * However, if retry_cache == 1, it's already been 892 * added to the cache under our feet. 893 */ 894 if (err == __NSW_SUCCESS && !retry_cache) { 895 char hashname[MAXNETNAMELEN]; 896 pkey_cache_add(netname2hashname(netname, hashname, 897 MAXNETNAMELEN, keylen, 898 algtype), pkey); 899 } 900 901 switch (look->actions[err]) { 902 case __NSW_CONTINUE : 903 continue; 904 case __NSW_RETURN : 905 if (needfree) 906 __nsw_freeconfig(conf); 907 return ((res & 1) != 0); 908 default : 909 syslog(LOG_INFO, "Unknown action for nameservice %s", 910 look->service_name); 911 } 912 } 913 914 if (needfree) 915 __nsw_freeconfig(conf); 916 return (0); 917 } 918 919 920 /* 921 * The public key cache (used by nisd in this case) must be filled with 922 * the data in the NIS_COLD_START file in order for extended Diffie-Hellman 923 * operations to work. 924 */ 925 void 926 prime_pkey_cache(directory_obj *dobj) 927 { 928 int scount; 929 930 for (scount = 0; scount < dobj->do_servers.do_servers_len; scount++) { 931 nis_server *srv = &(dobj->do_servers.do_servers_val[scount]); 932 extdhkey_t *keylens = NULL; 933 char *pkey = NULL, hashname[MAXNETNAMELEN]; 934 char netname[MAXNETNAMELEN]; 935 int kcount, nkeys = 0; 936 937 (void) host2netname(netname, srv->name, NULL); 938 939 /* determine the number of keys to process */ 940 if (!(nkeys = __nis_dhext_extract_keyinfo(srv, &keylens))) 941 continue; 942 943 /* store them */ 944 if (srv->key_type == NIS_PK_DHEXT) { 945 for (kcount = 0; kcount < nkeys; kcount++) { 946 if (!netname2hashname(netname, hashname, 947 MAXNETNAMELEN, 948 keylens[kcount].keylen, 949 keylens[kcount].algtype)) 950 continue; 951 952 if (!(pkey = __nis_dhext_extract_pkey( 953 &srv->pkey, keylens[kcount].keylen, 954 keylens[kcount].algtype))) 955 continue; 956 957 if (!pkey_cache_get(hashname, pkey)) 958 pkey_cache_add(hashname, pkey); 959 } 960 } else if (srv->key_type == NIS_PK_DH) { 961 pkey = srv->pkey.n_bytes; 962 963 if (netname2hashname(netname, hashname, MAXNETNAMELEN, 964 KEYSIZE, 0) && 965 !pkey_cache_get(hashname, pkey)) 966 pkey_cache_add(hashname, pkey); 967 } 968 if (keylens != NULL) 969 free(keylens); 970 keylens = NULL; 971 } 972 } 973 974 /* 975 * Generic (all sizes) DH version of getpublickey. 976 */ 977 int 978 getpublickey_g( 979 const char *netname, /* in */ 980 int keylen, /* in */ 981 int algtype, /* in */ 982 char *pkey, /* out */ 983 size_t pkeylen) /* in */ 984 { 985 return (__getpublickey_cached_g(netname, keylen, algtype, pkey, 986 pkeylen, (int *)0)); 987 } 988 989 /* 990 * Generic (all sizes) DH version of getsecretkey_g. 991 */ 992 int 993 getsecretkey_g( 994 const char *netname, /* in */ 995 keylen_t keylen, /* in */ 996 algtype_t algtype, /* in */ 997 char *skey, /* out */ 998 size_t skeylen, /* in */ 999 const char *passwd) /* in */ 1000 { 1001 int needfree = 1, res, err; 1002 struct __nsw_switchconfig *conf; 1003 struct __nsw_lookup *look; 1004 enum __nsw_parse_err perr; 1005 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 1006 1007 if (!netname || !skey || !skeylen) 1008 return (0); 1009 1010 conf = __nsw_getconfig("publickey", &perr); 1011 1012 if (!conf) { 1013 conf = &publickey_default; 1014 needfree = 0; 1015 } 1016 1017 for (look = conf->lookups; look; look = look->next) { 1018 if (strcmp(look->service_name, "nisplus") == 0) 1019 res = getkeys_nisplus_g(&err, (char *)netname, 1020 NULL, 0, skey, skeylen, 1021 (char *)passwd, keylen, algtype, 0); 1022 else if (strcmp(look->service_name, "ldap") == 0) 1023 res = getkeys_ldap_g(&err, (char *)netname, 1024 NULL, 0, skey, skeylen, 1025 (char *)passwd, keylen, algtype); 1026 /* long DH keys will not be in nis or files */ 1027 else if (classic_des && strcmp(look->service_name, "nis") == 0) 1028 res = getkeys_nis(&err, (char *)netname, 1029 NULL, skey, (char *)passwd); 1030 else if (classic_des && 1031 strcmp(look->service_name, "files") == 0) 1032 res = getkeys_files(&err, (char *)netname, 1033 NULL, skey, (char *)passwd); 1034 else { 1035 syslog(LOG_INFO, "Unknown publickey nameservice '%s'", 1036 look->service_name); 1037 err = __NSW_UNAVAIL; 1038 } 1039 switch (look->actions[err]) { 1040 case __NSW_CONTINUE : 1041 continue; 1042 case __NSW_RETURN : 1043 if (needfree) 1044 __nsw_freeconfig(conf); 1045 return ((res & 2) != 0); 1046 default : 1047 syslog(LOG_INFO, "Unknown action for nameservice %s", 1048 look->service_name); 1049 } 1050 } 1051 if (needfree) 1052 __nsw_freeconfig(conf); 1053 return (0); 1054 } 1055