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