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