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 2009 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 /* 36 * publickey.c 37 * 38 * 39 * Public and Private (secret) key lookup routines. These functions 40 * are used by the secure RPC auth_des flavor to get the public and 41 * private keys for secure RPC principals. Originally designed to 42 * talk only to YP, AT&T modified them to talk to files, and now 43 * they can also talk to NIS+. The policy for these lookups is now 44 * defined in terms of the nameservice switch as follows : 45 * publickey: nis files 46 * 47 */ 48 #include "mt.h" 49 #include "../rpc/rpc_mt.h" 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <syslog.h> 54 #include <assert.h> 55 #include <sys/types.h> 56 #include <pwd.h> 57 #include "nsswitch.h" 58 #include <rpc/rpc.h> 59 #include <rpc/key_prot.h> 60 #include <rpcsvc/nis.h> 61 #include <rpcsvc/ypclnt.h> 62 #include <rpcsvc/nis_dhext.h> 63 #include <thread.h> 64 #include "../nis/gen/nis_clnt.h" 65 #include <nss_dbdefs.h> 66 67 68 static const char *PKMAP = "publickey.byname"; 69 static const char *PKFILE = "/etc/publickey"; 70 static const char dh_caps_str[] = "DH"; 71 static const char des_caps_str[] = AUTH_DES_AUTH_TYPE; 72 73 static char *netname2hashname(const char *, char *, int, keylen_t, 74 algtype_t); 75 76 #define WORKBUFSIZE 1024 77 78 extern int xdecrypt(); 79 80 extern int __yp_match_cflookup(char *, char *, char *, int, char **, 81 int *, int *); 82 83 84 /* 85 * default publickey policy: 86 * publickey: nis [NOTFOUND = return] files 87 */ 88 89 90 /* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */ 91 #define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE} 92 93 static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL}, 94 lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files}; 95 static struct __nsw_switchconfig publickey_default = 96 {0, "publickey", 2, &lookup_nis}; 97 98 #ifndef NUL 99 #define NUL '\0' 100 #endif 101 102 extern mutex_t serialize_pkey; 103 104 static int extract_secret(); 105 106 /* 107 * db_root is used for switch backends. 108 */ 109 static DEFINE_NSS_DB_ROOT(db_root); 110 111 /* 112 * str2key 113 */ 114 /* ARGSUSED */ 115 static int 116 str2key(const char *instr, int lenstr, 117 void *ent, char *buffer, int buflen) { 118 if (lenstr + 1 > buflen) 119 return (NSS_STR_PARSE_ERANGE); 120 /* 121 * We copy the input string into the output buffer 122 */ 123 (void) memcpy(buffer, instr, lenstr); 124 buffer[lenstr] = '\0'; 125 126 return (NSS_STR_PARSE_SUCCESS); 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, 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 FILE *fd; 233 char *p; 234 char *lasts; 235 236 fd = fopen(PKFILE, "rF"); 237 if (fd == NULL) { 238 *errp = __NSW_UNAVAIL; 239 return (0); 240 } 241 242 /* Search through the file linearly :-( */ 243 while ((res = 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) fclose(fd); 289 *errp = __NSW_SUCCESS; 290 return (r); 291 } 292 } 293 } 294 295 (void) 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 337 /* 338 * Generic DH (any size keys) version of extract_secret. 339 */ 340 static int 341 extract_secret_g( 342 char *raw, /* in */ 343 char *private, /* out */ 344 int prilen, /* in */ 345 char *passwd, /* in */ 346 char *netname, /* in */ 347 keylen_t keylen, /* in */ 348 algtype_t algtype) /* in */ 349 350 { 351 char *buf = malloc(strlen(raw) + 1); /* private tmp buf */ 352 char *p; 353 354 if (!buf || !passwd || !raw || !private || !prilen || 355 !VALID_KEYALG(keylen, algtype)) { 356 if (private) 357 *private = NUL; 358 if (buf) 359 free(buf); 360 return (0); 361 } 362 363 (void) strcpy(buf, raw); 364 365 /* strip off pesky colon if it exists */ 366 p = strchr(buf, ':'); 367 if (p) { 368 *p = 0; 369 } 370 371 /* raw buf has chksum appended, so let's verify it too */ 372 if (!xdecrypt_g(buf, keylen, algtype, passwd, netname, TRUE)) { 373 private[0] = 0; 374 free(buf); 375 return (1); /* yes, return 1 even if xdecrypt fails */ 376 } 377 378 if (strlen(buf) >= prilen) { 379 private[0] = 0; 380 free(buf); 381 return (0); 382 } 383 384 (void) strcpy(private, buf); 385 free(buf); 386 return (1); 387 } 388 389 /* 390 * extract_secret() 391 * 392 * This generic function will extract the private key 393 * from a string using the given password. Note that 394 * it uses the DES based function xdecrypt() 395 */ 396 static int 397 extract_secret(char *raw, char *private, char *passwd) 398 { 399 return (extract_secret_g(raw, private, HEXKEYBYTES+1, passwd, 400 NULL, KEYSIZE, 0)); 401 } 402 403 /* 404 * getkeys_ldap_g() 405 * 406 * Fetches the key pair from LDAP. This version handles any size 407 * DH keys. 408 */ 409 410 void 411 _nss_initf_publickey(nss_db_params_t *p) 412 { 413 p->name = NSS_DBNAM_PUBLICKEY; 414 p->default_config = NSS_DEFCONF_PUBLICKEY; 415 } 416 417 418 static int 419 getkeys_ldap_g( 420 int *err, /* in */ 421 char *netname, /* in */ 422 char *pkey, /* out */ 423 int pkeylen, /* in */ 424 char *skey, /* out */ 425 int skeylen, /* in */ 426 char *passwd, /* in */ 427 keylen_t keylen, /* in */ 428 algtype_t algtype) /* in */ 429 { 430 int r = 0; 431 char *p; 432 char keytypename[NIS_MAXNAMELEN+1]; 433 int len; 434 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 435 int rc = 0; 436 nss_XbyY_args_t arg; 437 nss_XbyY_buf_t *buf = NULL; 438 char *keyval; 439 440 NSS_XbyY_ALLOC(&buf, 0, NSS_BUFLEN_PUBLICKEY); 441 442 NSS_XbyY_INIT(&arg, buf->result, buf->buffer, buf->buflen, str2key); 443 arg.key.pkey.name = netname; 444 445 /* 446 * LDAP stores the public and secret key info in entries using 447 * nisKeyObject objectclass. Each key is tagged with the 448 * keytype, keylength, and algorithm. The tag has the following 449 * format: {<keytype><keylength>-<algorithm>}. For example, 450 * {DH192-0}. 451 */ 452 if (classic_des) 453 (void) strcpy(keytypename, "{DH192-0}"); 454 else 455 (void) sprintf(keytypename, "{%s%d-%d}", 456 dh_caps_str, keylen, algtype); 457 arg.key.pkey.keytype = keytypename; 458 459 if (nss_search(&db_root, _nss_initf_publickey, NSS_DBOP_KEYS_BYNAME, 460 &arg) != NSS_SUCCESS) { 461 NSS_XbyY_FREE(&buf); 462 *err = __NSW_NOTFOUND; 463 return (0); 464 } 465 keyval = buf->buffer; 466 p = strchr(keyval, ':'); 467 if (p == NULL) { 468 NSS_XbyY_FREE(&buf); 469 *err = __NSW_NOTFOUND; 470 return (0); 471 } 472 *p = 0; 473 if (pkey) { 474 len = strlen(keyval); 475 if (len > HEXKEYBYTES) { 476 NSS_XbyY_FREE(&buf); 477 *err = __NSW_NOTFOUND; 478 return (0); 479 } 480 (void) strcpy(pkey, keyval); 481 } 482 r = 1; 483 p++; 484 if (skey && extract_secret(p, skey, passwd)) 485 r |= 2; 486 NSS_XbyY_FREE(&buf); 487 *err = __NSW_SUCCESS; 488 return (r); 489 } 490 491 492 /* 493 * Convert a netname to a name we will hash on. For classic_des, 494 * just copy netname as is. But for new and improved ("now in 495 * new longer sizes!") DHEXT, add a ":keylen-algtype" suffix to hash on. 496 * 497 * Returns the hashname string on success or NULL on failure. 498 */ 499 static char * 500 netname2hashname( 501 const char *netname, 502 char *hashname, 503 int bufsiz, 504 keylen_t keylen, 505 algtype_t algtype) 506 { 507 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 508 509 if (!netname || !hashname || !bufsiz) 510 return (NULL); 511 512 if (classic_des) { 513 if (bufsiz > strlen(netname)) 514 (void) strcpy(hashname, netname); 515 else 516 return (NULL); 517 } else { 518 char tmp[128]; 519 (void) sprintf(tmp, ":%d-%d", keylen, algtype); 520 if (bufsiz > (strlen(netname) + strlen(tmp))) 521 (void) sprintf(hashname, "%s%s", netname, tmp); 522 else 523 return (NULL); 524 } 525 526 return (hashname); 527 } 528 529 /* 530 * Flush netname's publickey of the given key length and algorithm type. 531 */ 532 void 533 __getpublickey_flush_g(const char *netname, keylen_t keylen, algtype_t algtype) 534 { 535 char *p, hashname[MAXNETNAMELEN+1]; 536 p = netname2hashname(netname, hashname, MAXNETNAMELEN, keylen, algtype); 537 } 538 539 /* 540 * Generic DH (any size keys) version of __getpublickey_cached. 541 */ 542 int 543 __getpublickey_cached_g(const char netname[], /* in */ 544 keylen_t keylen, /* in */ 545 algtype_t algtype, /* in */ 546 char *pkey, /* out */ 547 size_t pkeylen, /* in */ 548 int *from_cache) /* in/out */ 549 { 550 int needfree = 1, res, err; 551 struct __nsw_switchconfig *conf; 552 struct __nsw_lookup *look; 553 enum __nsw_parse_err perr; 554 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 555 int retry_cache = 0; 556 557 if (!netname || !pkey) 558 return (0); 559 560 conf = __nsw_getconfig("publickey", &perr); 561 if (!conf) { 562 conf = &publickey_default; 563 needfree = 0; 564 } 565 for (look = conf->lookups; look; look = look->next) { 566 if (strcmp(look->service_name, "ldap") == 0) { 567 res = getkeys_ldap_g(&err, (char *)netname, 568 pkey, pkeylen, NULL, 0, NULL, 569 keylen, algtype); 570 /* long DH keys will not be in nis or files */ 571 } else if (classic_des && 572 strcmp(look->service_name, "nis") == 0) 573 res = getkeys_nis(&err, (char *)netname, pkey, 574 NULL, NULL); 575 else if (classic_des && 576 strcmp(look->service_name, "files") == 0) 577 res = getkeys_files(&err, (char *)netname, pkey, 578 NULL, NULL); 579 else { 580 syslog(LOG_INFO, "Unknown publickey nameservice '%s'", 581 look->service_name); 582 err = __NSW_UNAVAIL; 583 } 584 585 switch (look->actions[err]) { 586 case __NSW_CONTINUE : 587 continue; 588 case __NSW_RETURN : 589 if (needfree) 590 __nsw_freeconfig(conf); 591 return ((res & 1) != 0); 592 default : 593 syslog(LOG_INFO, "Unknown action for nameservice %s", 594 look->service_name); 595 } 596 } 597 598 if (needfree) 599 __nsw_freeconfig(conf); 600 return (0); 601 } 602 603 604 605 /* 606 * Generic (all sizes) DH version of getpublickey. 607 */ 608 int 609 getpublickey_g( 610 const char *netname, /* in */ 611 int keylen, /* in */ 612 int algtype, /* in */ 613 char *pkey, /* out */ 614 size_t pkeylen) /* in */ 615 { 616 return (__getpublickey_cached_g(netname, keylen, algtype, pkey, 617 pkeylen, (int *)0)); 618 } 619 620 /* 621 * Generic (all sizes) DH version of getsecretkey_g. 622 */ 623 int 624 getsecretkey_g( 625 const char *netname, /* in */ 626 keylen_t keylen, /* in */ 627 algtype_t algtype, /* in */ 628 char *skey, /* out */ 629 size_t skeylen, /* in */ 630 const char *passwd) /* in */ 631 { 632 int needfree = 1, res, err; 633 struct __nsw_switchconfig *conf; 634 struct __nsw_lookup *look; 635 enum __nsw_parse_err perr; 636 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 637 638 if (!netname || !skey || !skeylen) 639 return (0); 640 641 conf = __nsw_getconfig("publickey", &perr); 642 643 if (!conf) { 644 conf = &publickey_default; 645 needfree = 0; 646 } 647 648 for (look = conf->lookups; look; look = look->next) { 649 if (strcmp(look->service_name, "ldap") == 0) 650 res = getkeys_ldap_g(&err, (char *)netname, 651 NULL, 0, skey, skeylen, 652 (char *)passwd, keylen, algtype); 653 /* long DH keys will not be in nis or files */ 654 else if (classic_des && strcmp(look->service_name, "nis") == 0) 655 res = getkeys_nis(&err, (char *)netname, 656 NULL, skey, (char *)passwd); 657 else if (classic_des && 658 strcmp(look->service_name, "files") == 0) 659 res = getkeys_files(&err, (char *)netname, 660 NULL, skey, (char *)passwd); 661 else { 662 syslog(LOG_INFO, "Unknown publickey nameservice '%s'", 663 look->service_name); 664 err = __NSW_UNAVAIL; 665 } 666 switch (look->actions[err]) { 667 case __NSW_CONTINUE : 668 continue; 669 case __NSW_RETURN : 670 if (needfree) 671 __nsw_freeconfig(conf); 672 return ((res & 2) != 0); 673 default : 674 syslog(LOG_INFO, "Unknown action for nameservice %s", 675 look->service_name); 676 } 677 } 678 if (needfree) 679 __nsw_freeconfig(conf); 680 return (0); 681 } 682