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