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, 120 void *ent, char *buffer, int buflen) { 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 { 354 char *buf = malloc(strlen(raw) + 1); /* private tmp buf */ 355 char *p; 356 357 if (!buf || !passwd || !raw || !private || !prilen || 358 !VALID_KEYALG(keylen, algtype)) { 359 if (private) 360 *private = NUL; 361 if (buf) 362 free(buf); 363 return (0); 364 } 365 366 (void) strcpy(buf, raw); 367 368 /* strip off pesky colon if it exists */ 369 p = strchr(buf, ':'); 370 if (p) { 371 *p = 0; 372 } 373 374 /* raw buf has chksum appended, so let's verify it too */ 375 if (!xdecrypt_g(buf, keylen, algtype, passwd, netname, TRUE)) { 376 private[0] = 0; 377 free(buf); 378 return (1); /* yes, return 1 even if xdecrypt fails */ 379 } 380 381 if (strlen(buf) >= prilen) { 382 private[0] = 0; 383 free(buf); 384 return (0); 385 } 386 387 (void) strcpy(private, buf); 388 free(buf); 389 return (1); 390 } 391 392 /* 393 * extract_secret() 394 * 395 * This generic function will extract the private key 396 * from a string using the given password. Note that 397 * it uses the DES based function xdecrypt() 398 */ 399 static int 400 extract_secret(char *raw, char *private, char *passwd) 401 { 402 return (extract_secret_g(raw, private, HEXKEYBYTES+1, passwd, 403 NULL, KEYSIZE, 0)); 404 } 405 406 /* 407 * getkeys_ldap_g() 408 * 409 * Fetches the key pair from LDAP. This version handles any size 410 * DH keys. 411 */ 412 413 void 414 _nss_initf_publickey(nss_db_params_t *p) 415 { 416 p->name = NSS_DBNAM_PUBLICKEY; 417 p->default_config = NSS_DEFCONF_PUBLICKEY; 418 } 419 420 421 static int 422 getkeys_ldap_g( 423 int *err, /* in */ 424 char *netname, /* in */ 425 char *pkey, /* out */ 426 int pkeylen, /* in */ 427 char *skey, /* out */ 428 int skeylen, /* in */ 429 char *passwd, /* in */ 430 keylen_t keylen, /* in */ 431 algtype_t algtype) /* in */ 432 { 433 int r = 0; 434 char *p; 435 char keytypename[NIS_MAXNAMELEN+1]; 436 int len; 437 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 438 int rc = 0; 439 nss_XbyY_args_t arg; 440 nss_XbyY_buf_t *buf = NULL; 441 char *keyval; 442 443 NSS_XbyY_ALLOC(&buf, 0, NSS_BUFLEN_PUBLICKEY); 444 445 NSS_XbyY_INIT(&arg, buf->result, buf->buffer, buf->buflen, str2key); 446 arg.key.pkey.name = netname; 447 448 /* 449 * LDAP stores the public and secret key info in entries using 450 * nisKeyObject objectclass. Each key is tagged with the 451 * keytype, keylength, and algorithm. The tag has the following 452 * format: {<keytype><keylength>-<algorithm>}. For example, 453 * {DH192-0}. 454 */ 455 if (classic_des) 456 (void) strcpy(keytypename, "{DH192-0}"); 457 else 458 (void) sprintf(keytypename, "{%s%d-%d}", 459 dh_caps_str, keylen, algtype); 460 arg.key.pkey.keytype = keytypename; 461 462 if (nss_search(&db_root, _nss_initf_publickey, NSS_DBOP_KEYS_BYNAME, 463 &arg) != NSS_SUCCESS) { 464 NSS_XbyY_FREE(&buf); 465 *err = __NSW_NOTFOUND; 466 return (0); 467 } 468 keyval = buf->buffer; 469 p = strchr(keyval, ':'); 470 if (p == NULL) { 471 NSS_XbyY_FREE(&buf); 472 *err = __NSW_NOTFOUND; 473 return (0); 474 } 475 *p = 0; 476 if (pkey) { 477 len = strlen(keyval); 478 if (len > HEXKEYBYTES) { 479 NSS_XbyY_FREE(&buf); 480 *err = __NSW_NOTFOUND; 481 return (0); 482 } 483 (void) strcpy(pkey, keyval); 484 } 485 r = 1; 486 p++; 487 if (skey && extract_secret(p, skey, passwd)) 488 r |= 2; 489 NSS_XbyY_FREE(&buf); 490 *err = __NSW_SUCCESS; 491 return (r); 492 } 493 494 495 /* 496 * Convert a netname to a name we will hash on. For classic_des, 497 * just copy netname as is. But for new and improved ("now in 498 * new longer sizes!") DHEXT, add a ":keylen-algtype" suffix to hash on. 499 * 500 * Returns the hashname string on success or NULL on failure. 501 */ 502 static char * 503 netname2hashname( 504 const char *netname, 505 char *hashname, 506 int bufsiz, 507 keylen_t keylen, 508 algtype_t algtype) 509 { 510 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 511 512 if (!netname || !hashname || !bufsiz) 513 return (NULL); 514 515 if (classic_des) { 516 if (bufsiz > strlen(netname)) 517 (void) strcpy(hashname, netname); 518 else 519 return (NULL); 520 } else { 521 char tmp[128]; 522 (void) sprintf(tmp, ":%d-%d", keylen, algtype); 523 if (bufsiz > (strlen(netname) + strlen(tmp))) 524 (void) sprintf(hashname, "%s%s", netname, tmp); 525 else 526 return (NULL); 527 } 528 529 return (hashname); 530 } 531 532 /* 533 * Flush netname's publickey of the given key length and algorithm type. 534 */ 535 void 536 __getpublickey_flush_g(const char *netname, keylen_t keylen, algtype_t algtype) 537 { 538 char *p, hashname[MAXNETNAMELEN+1]; 539 p = netname2hashname(netname, hashname, MAXNETNAMELEN, keylen, algtype); 540 } 541 542 /* 543 * Generic DH (any size keys) version of __getpublickey_cached. 544 */ 545 int 546 __getpublickey_cached_g(const char netname[], /* in */ 547 keylen_t keylen, /* in */ 548 algtype_t algtype, /* in */ 549 char *pkey, /* out */ 550 size_t pkeylen, /* in */ 551 int *from_cache) /* in/out */ 552 { 553 int needfree = 1, res, err; 554 struct __nsw_switchconfig *conf; 555 struct __nsw_lookup *look; 556 enum __nsw_parse_err perr; 557 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 558 int retry_cache = 0; 559 560 if (!netname || !pkey) 561 return (0); 562 563 conf = __nsw_getconfig("publickey", &perr); 564 if (!conf) { 565 conf = &publickey_default; 566 needfree = 0; 567 } 568 for (look = conf->lookups; look; look = look->next) { 569 if (strcmp(look->service_name, "ldap") == 0) { 570 res = getkeys_ldap_g(&err, (char *)netname, 571 pkey, pkeylen, NULL, 0, NULL, 572 keylen, algtype); 573 /* long DH keys will not be in nis or files */ 574 } else if (classic_des && 575 strcmp(look->service_name, "nis") == 0) 576 res = getkeys_nis(&err, (char *)netname, pkey, 577 NULL, NULL); 578 else if (classic_des && 579 strcmp(look->service_name, "files") == 0) 580 res = getkeys_files(&err, (char *)netname, pkey, 581 NULL, NULL); 582 else { 583 syslog(LOG_INFO, "Unknown publickey nameservice '%s'", 584 look->service_name); 585 err = __NSW_UNAVAIL; 586 res = 0; 587 } 588 589 switch (look->actions[err]) { 590 case __NSW_CONTINUE : 591 continue; 592 case __NSW_RETURN : 593 if (needfree) 594 __nsw_freeconfig(conf); 595 return ((res & 1) != 0); 596 default : 597 syslog(LOG_INFO, "Unknown action for nameservice %s", 598 look->service_name); 599 } 600 } 601 602 if (needfree) 603 __nsw_freeconfig(conf); 604 return (0); 605 } 606 607 608 609 /* 610 * Generic (all sizes) DH version of getpublickey. 611 */ 612 int 613 getpublickey_g( 614 const char *netname, /* in */ 615 int keylen, /* in */ 616 int algtype, /* in */ 617 char *pkey, /* out */ 618 size_t pkeylen) /* in */ 619 { 620 return (__getpublickey_cached_g(netname, keylen, algtype, pkey, 621 pkeylen, (int *)0)); 622 } 623 624 /* 625 * Generic (all sizes) DH version of getsecretkey_g. 626 */ 627 int 628 getsecretkey_g( 629 const char *netname, /* in */ 630 keylen_t keylen, /* in */ 631 algtype_t algtype, /* in */ 632 char *skey, /* out */ 633 size_t skeylen, /* in */ 634 const char *passwd) /* in */ 635 { 636 int needfree = 1, res, err; 637 struct __nsw_switchconfig *conf; 638 struct __nsw_lookup *look; 639 enum __nsw_parse_err perr; 640 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype); 641 642 if (!netname || !skey || !skeylen) 643 return (0); 644 645 conf = __nsw_getconfig("publickey", &perr); 646 647 if (!conf) { 648 conf = &publickey_default; 649 needfree = 0; 650 } 651 652 for (look = conf->lookups; look; look = look->next) { 653 if (strcmp(look->service_name, "ldap") == 0) 654 res = getkeys_ldap_g(&err, (char *)netname, 655 NULL, 0, skey, skeylen, 656 (char *)passwd, keylen, algtype); 657 /* long DH keys will not be in nis or files */ 658 else if (classic_des && strcmp(look->service_name, "nis") == 0) 659 res = getkeys_nis(&err, (char *)netname, 660 NULL, skey, (char *)passwd); 661 else if (classic_des && 662 strcmp(look->service_name, "files") == 0) 663 res = getkeys_files(&err, (char *)netname, 664 NULL, skey, (char *)passwd); 665 else { 666 syslog(LOG_INFO, "Unknown publickey nameservice '%s'", 667 look->service_name); 668 err = __NSW_UNAVAIL; 669 res = 0; 670 } 671 switch (look->actions[err]) { 672 case __NSW_CONTINUE : 673 continue; 674 case __NSW_RETURN : 675 if (needfree) 676 __nsw_freeconfig(conf); 677 return ((res & 2) != 0); 678 default : 679 syslog(LOG_INFO, "Unknown action for nameservice %s", 680 look->service_name); 681 } 682 } 683 if (needfree) 684 __nsw_freeconfig(conf); 685 return (0); 686 } 687