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 2014 Nexenta Systems, Inc. All rights reserved. 28 */ 29 30 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 31 /* All Rights Reserved */ 32 33 /* 34 * Portions of this source code were derived from Berkeley 4.3 BSD 35 * under license from the Regents of the University of California. 36 */ 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 */ 51 #include "mt.h" 52 #include "../rpc/rpc_mt.h" 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <syslog.h> 57 #include <assert.h> 58 #include <sys/types.h> 59 #include <pwd.h> 60 #include "nsswitch.h" 61 #include <rpc/rpc.h> 62 #include <rpc/key_prot.h> 63 #include <rpcsvc/nis.h> 64 #include <rpcsvc/ypclnt.h> 65 #include <rpcsvc/nis_dhext.h> 66 #include <thread.h> 67 #include "../nis/gen/nis_clnt.h" 68 #include <nss_dbdefs.h> 69 70 71 static const char *PKMAP = "publickey.byname"; 72 static const char *PKFILE = "/etc/publickey"; 73 static const char dh_caps_str[] = "DH"; 74 static const char des_caps_str[] = AUTH_DES_AUTH_TYPE; 75 76 static char *netname2hashname(const char *, char *, int, keylen_t, 77 algtype_t); 78 79 #define WORKBUFSIZE 1024 80 81 extern int xdecrypt(); 82 83 extern int __yp_match_cflookup(char *, char *, char *, int, char **, 84 int *, int *); 85 86 87 /* 88 * default publickey policy: 89 * publickey: nis [NOTFOUND = return] files 90 */ 91 92 93 /* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */ 94 #define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE} 95 96 static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL}, 97 lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files}; 98 static struct __nsw_switchconfig publickey_default = 99 {0, "publickey", 2, &lookup_nis}; 100 101 #ifndef NUL 102 #define NUL '\0' 103 #endif 104 105 extern mutex_t serialize_pkey; 106 107 static int extract_secret(); 108 109 /* 110 * db_root is used for switch backends. 111 */ 112 static DEFINE_NSS_DB_ROOT(db_root); 113 114 /* 115 * str2key 116 */ 117 /* ARGSUSED */ 118 static int 119 str2key(const char *instr, int lenstr, void *ent, char *buffer, int buflen) 120 { 121 if (lenstr + 1 > buflen) 122 return (NSS_STR_PARSE_ERANGE); 123 /* 124 * We copy the input string into the output buffer 125 */ 126 (void) memcpy(buffer, instr, lenstr); 127 buffer[lenstr] = '\0'; 128 129 return (NSS_STR_PARSE_SUCCESS); 130 } 131 /* 132 * These functions are the "backends" for the switch for public keys. They 133 * get both the public and private keys from each of the supported name 134 * services (nis, files). They are passed the appropriate parameters 135 * and return 0 if unsuccessful with *errp set, or 1 when they got just the 136 * public key and 3 when they got both the public and private keys. 137 * 138 * 139 * getkey_nis() 140 * 141 * Internal implementation of getpublickey() using NIS (aka Yellow Pages, 142 * aka YP). 143 * 144 * NOTE : *** this function returns nsswitch codes and _not_ the 145 * value returned by getpublickey. 146 */ 147 static int 148 getkeys_nis(int *errp, char *netname, char *pkey, char *skey, char *passwd) 149 { 150 char *domain; 151 char *keyval = NULL; 152 int keylen, err, r = 0; 153 char *p; 154 int len; 155 156 p = strchr(netname, '@'); 157 if (!p) { 158 *errp = __NSW_UNAVAIL; 159 return (0); 160 } 161 162 domain = ++p; 163 164 165 /* 166 * Instead of calling yp_match(), we use __yp_match_cflookup() here 167 * which has time-out control for the binding operation to nis 168 * servers. 169 */ 170 err = __yp_match_cflookup(domain, (char *)PKMAP, netname, 171 strlen(netname), &keyval, &keylen, 0); 172 173 switch (err) { 174 case YPERR_KEY : 175 if (keyval) 176 free(keyval); 177 *errp = __NSW_NOTFOUND; 178 return (0); 179 default : 180 if (keyval) 181 free(keyval); 182 *errp = __NSW_UNAVAIL; 183 return (0); 184 case 0: 185 break; 186 } 187 188 p = strchr(keyval, ':'); 189 if (p == NULL) { 190 free(keyval); 191 *errp = __NSW_NOTFOUND; 192 return (0); 193 } 194 *p = 0; 195 if (pkey) { 196 len = strlen(keyval); 197 if (len > HEXKEYBYTES) { 198 free(keyval); 199 *errp = __NSW_NOTFOUND; 200 return (0); 201 } 202 (void) strcpy(pkey, keyval); 203 } 204 r = 1; 205 p++; 206 if (skey && extract_secret(p, skey, passwd)) 207 r |= 2; 208 free(keyval); 209 *errp = __NSW_SUCCESS; 210 return (r); 211 } 212 213 /* 214 * getkey_files() 215 * 216 * The files version of getpublickey. This function attempts to 217 * get the publickey from the file PKFILE . 218 * 219 * This function defines the format of the /etc/publickey file to 220 * be : 221 * netname <whitespace> publickey:privatekey 222 * 223 * NOTE : *** this function returns nsswitch codes and _not_ the 224 * value returned by getpublickey. 225 */ 226 227 static int 228 getkeys_files(int *errp, char *netname, char *pkey, char *skey, char *passwd) 229 { 230 char *mkey; 231 char *mval; 232 char buf[WORKBUFSIZE]; 233 int r = 0; 234 char *res; 235 FILE *fd; 236 char *p; 237 char *lasts; 238 239 fd = fopen(PKFILE, "rF"); 240 if (fd == NULL) { 241 *errp = __NSW_UNAVAIL; 242 return (0); 243 } 244 245 /* Search through the file linearly :-( */ 246 while ((res = fgets(buf, WORKBUFSIZE, fd)) != NULL) { 247 248 if ((res[0] == '#') || (res[0] == '\n')) 249 continue; 250 else { 251 mkey = strtok_r(buf, "\t ", &lasts); 252 if (mkey == NULL) { 253 syslog(LOG_INFO, 254 "getpublickey: Bad record in %s for %s", 255 PKFILE, netname); 256 continue; 257 } 258 mval = strtok_r(NULL, " \t#\n", &lasts); 259 if (mval == NULL) { 260 syslog(LOG_INFO, 261 "getpublickey: Bad record in %s for %s", 262 PKFILE, netname); 263 continue; 264 } 265 /* NOTE : Case insensitive compare. */ 266 if (strcasecmp(mkey, netname) == 0) { 267 p = strchr(mval, ':'); 268 if (p == NULL) { 269 syslog(LOG_INFO, 270 "getpublickey: Bad record in %s for %s", 271 PKFILE, netname); 272 continue; 273 } 274 275 *p = 0; 276 if (pkey) { 277 int len = strlen(mval); 278 279 if (len > HEXKEYBYTES) { 280 syslog(LOG_INFO, 281 "getpublickey: Bad record in %s for %s", 282 PKFILE, netname); 283 continue; 284 } 285 (void) strcpy(pkey, mval); 286 } 287 r = 1; 288 p++; 289 if (skey && extract_secret(p, skey, passwd)) 290 r |= 2; 291 (void) fclose(fd); 292 *errp = __NSW_SUCCESS; 293 return (r); 294 } 295 } 296 } 297 298 (void) fclose(fd); 299 *errp = __NSW_NOTFOUND; 300 return (0); 301 } 302 303 /* 304 * getpublickey(netname, key) 305 * 306 * This is the actual exported interface for this function. 307 */ 308 309 int 310 __getpublickey_cached(char *netname, char *pkey, int *from_cache) 311 { 312 return (__getpublickey_cached_g(netname, KEYSIZE, 0, pkey, 313 HEXKEYBYTES+1, from_cache)); 314 } 315 316 int 317 getpublickey(const char *netname, char *pkey) 318 { 319 return (__getpublickey_cached((char *)netname, pkey, (int *)0)); 320 } 321 322 void 323 __getpublickey_flush(const char *netname) 324 { 325 __getpublickey_flush_g(netname, 192, 0); 326 } 327 328 int 329 getsecretkey(const char *netname, char *skey, const char *passwd) 330 { 331 return (getsecretkey_g(netname, KEYSIZE, 0, skey, HEXKEYBYTES+1, 332 passwd)); 333 } 334 335 /* 336 * Routines to cache publickeys. 337 */ 338 339 340 /* 341 * Generic DH (any size keys) version of extract_secret. 342 */ 343 static int 344 extract_secret_g( 345 char *raw, /* in */ 346 char *private, /* out */ 347 int prilen, /* in */ 348 char *passwd, /* in */ 349 char *netname, /* in */ 350 keylen_t keylen, /* in */ 351 algtype_t algtype) /* in */ 352 { 353 char *buf = malloc(strlen(raw) + 1); /* private tmp buf */ 354 char *p; 355 356 if (!buf || !passwd || !raw || !private || !prilen || 357 !VALID_KEYALG(keylen, algtype)) { 358 if (private) 359 *private = NUL; 360 if (buf) 361 free(buf); 362 return (0); 363 } 364 365 (void) strcpy(buf, raw); 366 367 /* strip off pesky colon if it exists */ 368 p = strchr(buf, ':'); 369 if (p) { 370 *p = 0; 371 } 372 373 /* raw buf has chksum appended, so let's verify it too */ 374 if (!xdecrypt_g(buf, keylen, algtype, passwd, netname, TRUE)) { 375 private[0] = 0; 376 free(buf); 377 return (1); /* yes, return 1 even if xdecrypt fails */ 378 } 379 380 if (strlen(buf) >= prilen) { 381 private[0] = 0; 382 free(buf); 383 return (0); 384 } 385 386 (void) strcpy(private, buf); 387 free(buf); 388 return (1); 389 } 390 391 /* 392 * extract_secret() 393 * 394 * This generic function will extract the private key 395 * from a string using the given password. Note that 396 * it uses the DES based function xdecrypt() 397 */ 398 static int 399 extract_secret(char *raw, char *private, char *passwd) 400 { 401 return (extract_secret_g(raw, private, HEXKEYBYTES+1, passwd, 402 NULL, KEYSIZE, 0)); 403 } 404 405 /* 406 * getkeys_ldap_g() 407 * 408 * Fetches the key pair from LDAP. This version handles any size 409 * DH keys. 410 */ 411 412 void 413 _nss_initf_publickey(nss_db_params_t *p) 414 { 415 p->name = NSS_DBNAM_PUBLICKEY; 416 p->default_config = NSS_DEFCONF_PUBLICKEY; 417 } 418 419 420 static int 421 getkeys_ldap_g( 422 int *err, /* in */ 423 char *netname, /* in */ 424 char *pkey, /* out */ 425 int pkeylen, /* in */ 426 char *skey, /* out */ 427 int skeylen, /* in */ 428 char *passwd, /* in */ 429 keylen_t keylen, /* in */ 430 algtype_t algtype) /* in */ 431 { 432 int r = 0; 433 char *p; 434 char keytypename[NIS_MAXNAMELEN+1]; 435 int len; 436 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 437 int rc = 0; 438 nss_XbyY_args_t arg; 439 nss_XbyY_buf_t *buf = NULL; 440 char *keyval; 441 442 NSS_XbyY_ALLOC(&buf, 0, NSS_BUFLEN_PUBLICKEY); 443 444 NSS_XbyY_INIT(&arg, buf->result, buf->buffer, buf->buflen, str2key); 445 arg.key.pkey.name = netname; 446 447 /* 448 * LDAP stores the public and secret key info in entries using 449 * nisKeyObject objectclass. Each key is tagged with the 450 * keytype, keylength, and algorithm. The tag has the following 451 * format: {<keytype><keylength>-<algorithm>}. For example, 452 * {DH192-0}. 453 */ 454 if (classic_des) 455 (void) strcpy(keytypename, "{DH192-0}"); 456 else 457 (void) sprintf(keytypename, "{%s%d-%d}", 458 dh_caps_str, keylen, algtype); 459 arg.key.pkey.keytype = keytypename; 460 461 if (nss_search(&db_root, _nss_initf_publickey, NSS_DBOP_KEYS_BYNAME, 462 &arg) != NSS_SUCCESS) { 463 NSS_XbyY_FREE(&buf); 464 *err = __NSW_NOTFOUND; 465 return (0); 466 } 467 keyval = buf->buffer; 468 p = strchr(keyval, ':'); 469 if (p == NULL) { 470 NSS_XbyY_FREE(&buf); 471 *err = __NSW_NOTFOUND; 472 return (0); 473 } 474 *p = 0; 475 if (pkey) { 476 len = strlen(keyval); 477 if (len > HEXKEYBYTES) { 478 NSS_XbyY_FREE(&buf); 479 *err = __NSW_NOTFOUND; 480 return (0); 481 } 482 (void) strcpy(pkey, keyval); 483 } 484 r = 1; 485 p++; 486 if (skey && extract_secret(p, skey, passwd)) 487 r |= 2; 488 NSS_XbyY_FREE(&buf); 489 *err = __NSW_SUCCESS; 490 return (r); 491 } 492 493 494 /* 495 * Convert a netname to a name we will hash on. For classic_des, 496 * just copy netname as is. But for new and improved ("now in 497 * new longer sizes!") DHEXT, add a ":keylen-algtype" suffix to hash on. 498 * 499 * Returns the hashname string on success or NULL on failure. 500 */ 501 static char * 502 netname2hashname( 503 const char *netname, 504 char *hashname, 505 int bufsiz, 506 keylen_t keylen, 507 algtype_t algtype) 508 { 509 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 510 511 if (!netname || !hashname || !bufsiz) 512 return (NULL); 513 514 if (classic_des) { 515 if (bufsiz > strlen(netname)) 516 (void) strcpy(hashname, netname); 517 else 518 return (NULL); 519 } else { 520 char tmp[128]; 521 (void) sprintf(tmp, ":%d-%d", keylen, algtype); 522 if (bufsiz > (strlen(netname) + strlen(tmp))) 523 (void) sprintf(hashname, "%s%s", netname, tmp); 524 else 525 return (NULL); 526 } 527 528 return (hashname); 529 } 530 531 /* 532 * Flush netname's publickey of the given key length and algorithm type. 533 */ 534 void 535 __getpublickey_flush_g(const char *netname, keylen_t keylen, algtype_t algtype) 536 { 537 char hashname[MAXNETNAMELEN+1]; 538 539 (void) netname2hashname(netname, hashname, MAXNETNAMELEN, keylen, 540 algtype); 541 } 542 543 /* 544 * Generic DH (any size keys) version of __getpublickey_cached. 545 */ 546 int 547 __getpublickey_cached_g(const char netname[], /* in */ 548 keylen_t keylen, /* in */ 549 algtype_t algtype, /* in */ 550 char *pkey, /* out */ 551 size_t pkeylen, /* in */ 552 int *from_cache) /* in/out */ 553 { 554 int needfree = 1, res, err; 555 struct __nsw_switchconfig *conf; 556 struct __nsw_lookup *look; 557 enum __nsw_parse_err perr; 558 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 559 int retry_cache = 0; 560 561 if (!netname || !pkey) 562 return (0); 563 564 conf = __nsw_getconfig("publickey", &perr); 565 if (!conf) { 566 conf = &publickey_default; 567 needfree = 0; 568 } 569 for (look = conf->lookups; look; look = look->next) { 570 if (strcmp(look->service_name, "ldap") == 0) { 571 res = getkeys_ldap_g(&err, (char *)netname, 572 pkey, pkeylen, NULL, 0, NULL, 573 keylen, algtype); 574 /* long DH keys will not be in nis or files */ 575 } else if (classic_des && 576 strcmp(look->service_name, "nis") == 0) 577 res = getkeys_nis(&err, (char *)netname, pkey, 578 NULL, NULL); 579 else if (classic_des && 580 strcmp(look->service_name, "files") == 0) 581 res = getkeys_files(&err, (char *)netname, pkey, 582 NULL, NULL); 583 else { 584 syslog(LOG_INFO, "Unknown publickey nameservice '%s'", 585 look->service_name); 586 err = __NSW_UNAVAIL; 587 res = 0; 588 } 589 590 switch (look->actions[err]) { 591 case __NSW_CONTINUE : 592 continue; 593 case __NSW_RETURN : 594 if (needfree) 595 __nsw_freeconfig(conf); 596 return ((res & 1) != 0); 597 default : 598 syslog(LOG_INFO, "Unknown action for nameservice %s", 599 look->service_name); 600 } 601 } 602 603 if (needfree) 604 __nsw_freeconfig(conf); 605 return (0); 606 } 607 608 609 610 /* 611 * Generic (all sizes) DH version of getpublickey. 612 */ 613 int 614 getpublickey_g( 615 const char *netname, /* in */ 616 int keylen, /* in */ 617 int algtype, /* in */ 618 char *pkey, /* out */ 619 size_t pkeylen) /* in */ 620 { 621 return (__getpublickey_cached_g(netname, keylen, algtype, pkey, 622 pkeylen, (int *)0)); 623 } 624 625 /* 626 * Generic (all sizes) DH version of getsecretkey_g. 627 */ 628 int 629 getsecretkey_g( 630 const char *netname, /* in */ 631 keylen_t keylen, /* in */ 632 algtype_t algtype, /* in */ 633 char *skey, /* out */ 634 size_t skeylen, /* in */ 635 const char *passwd) /* in */ 636 { 637 int needfree = 1, res, err; 638 struct __nsw_switchconfig *conf; 639 struct __nsw_lookup *look; 640 enum __nsw_parse_err perr; 641 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 642 643 if (!netname || !skey || !skeylen) 644 return (0); 645 646 conf = __nsw_getconfig("publickey", &perr); 647 648 if (!conf) { 649 conf = &publickey_default; 650 needfree = 0; 651 } 652 653 for (look = conf->lookups; look; look = look->next) { 654 if (strcmp(look->service_name, "ldap") == 0) 655 res = getkeys_ldap_g(&err, (char *)netname, 656 NULL, 0, skey, skeylen, 657 (char *)passwd, keylen, algtype); 658 /* long DH keys will not be in nis or files */ 659 else if (classic_des && strcmp(look->service_name, "nis") == 0) 660 res = getkeys_nis(&err, (char *)netname, 661 NULL, skey, (char *)passwd); 662 else if (classic_des && 663 strcmp(look->service_name, "files") == 0) 664 res = getkeys_files(&err, (char *)netname, 665 NULL, skey, (char *)passwd); 666 else { 667 syslog(LOG_INFO, "Unknown publickey nameservice '%s'", 668 look->service_name); 669 err = __NSW_UNAVAIL; 670 res = 0; 671 } 672 switch (look->actions[err]) { 673 case __NSW_CONTINUE : 674 continue; 675 case __NSW_RETURN : 676 if (needfree) 677 __nsw_freeconfig(conf); 678 return ((res & 2) != 0); 679 default : 680 syslog(LOG_INFO, "Unknown action for nameservice %s", 681 look->service_name); 682 } 683 } 684 if (needfree) 685 __nsw_freeconfig(conf); 686 return (0); 687 } 688