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 /* LINTLIBRARY */ 22 23 /* 24 * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 /* 28 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 29 * Use is subject to license terms. 30 */ 31 32 /* 33 * nfs security related library routines. 34 * 35 * Some of the routines in this file are adopted from 36 * lib/libnsl/netselect/netselect.c and are modified to be 37 * used for accessing /etc/nfssec.conf. 38 */ 39 40 /* SVr4.0 1.18 */ 41 42 #include <stdio.h> 43 #include <string.h> 44 #include <ctype.h> 45 #include <stdlib.h> 46 #include <syslog.h> 47 #include <synch.h> 48 #include <rpc/rpc.h> 49 #include <nfs/nfs_sec.h> 50 #include <rpc/rpcsec_gss.h> 51 #ifdef WNFS_SEC_NEGO 52 #include "webnfs.h" 53 #endif 54 55 #define GETBYNAME 1 56 #define GETBYNUM 2 57 58 /* 59 * mapping for /etc/nfssec.conf 60 */ 61 struct sc_data { 62 char *string; 63 int value; 64 }; 65 66 static struct sc_data sc_service[] = { 67 "default", rpc_gss_svc_default, 68 "-", rpc_gss_svc_none, 69 "none", rpc_gss_svc_none, 70 "integrity", rpc_gss_svc_integrity, 71 "privacy", rpc_gss_svc_privacy, 72 NULL, SC_FAILURE 73 }; 74 75 static mutex_t matching_lock = DEFAULTMUTEX; 76 static char *gettoken(char *, int); 77 extern int atoi(const char *str); 78 79 extern bool_t rpc_gss_get_principal_name(rpc_gss_principal_t *, char *, 80 char *, char *, char *); 81 82 extern bool_t rpc_gss_mech_to_oid(char *, rpc_gss_OID *); 83 extern bool_t rpc_gss_qop_to_num(char *, char *, uint_t *); 84 85 /* 86 * blank() returns true if the line is a blank line, 0 otherwise 87 */ 88 static int 89 blank(cp) 90 char *cp; 91 { 92 while (*cp && isspace(*cp)) { 93 cp++; 94 } 95 return (*cp == '\0'); 96 } 97 98 /* 99 * comment() returns true if the line is a comment, 0 otherwise. 100 */ 101 static int 102 comment(cp) 103 char *cp; 104 { 105 while (*cp && isspace(*cp)) { 106 cp++; 107 } 108 return (*cp == '#'); 109 } 110 111 112 /* 113 * getvalue() searches for the given string in the given array, 114 * and returns the integer value associated with the string. 115 */ 116 static unsigned long 117 getvalue(cp, sc_data) 118 char *cp; 119 struct sc_data sc_data[]; 120 { 121 int i; /* used to index through the given struct sc_data array */ 122 123 for (i = 0; sc_data[i].string; i++) { 124 if (strcmp(sc_data[i].string, cp) == 0) { 125 break; 126 } 127 } 128 return (sc_data[i].value); 129 } 130 131 /* 132 * shift1left() moves all characters in the string over 1 to 133 * the left. 134 */ 135 static void 136 shift1left(p) 137 char *p; 138 { 139 for (; *p; p++) 140 *p = *(p + 1); 141 } 142 143 144 /* 145 * gettoken() behaves much like strtok(), except that 146 * it knows about escaped space characters (i.e., space characters 147 * preceeded by a '\' are taken literally). 148 * 149 * XXX We should make this MT-hot by making it more like strtok_r(). 150 */ 151 static char * 152 gettoken(cp, skip) 153 char *cp; 154 int skip; 155 { 156 static char *savep; /* the place where we left off */ 157 register char *p; /* the beginning of the new token */ 158 register char *retp; /* the token to be returned */ 159 160 161 /* Determine if first or subsequent call */ 162 p = (cp == NULL)? savep: cp; 163 164 /* Return if no tokens remain. */ 165 if (p == 0) { 166 return (NULL); 167 } 168 169 while (isspace(*p)) 170 p++; 171 172 if (*p == '\0') { 173 return (NULL); 174 } 175 176 /* 177 * Save the location of the token and then skip past it 178 */ 179 180 retp = p; 181 while (*p) { 182 if (isspace(*p)) { 183 if (skip == TRUE) { 184 shift1left(p); 185 continue; 186 } else 187 break; 188 } 189 190 /* 191 * Only process the escape of the space separator; 192 * since the token may contain other separators, 193 * let the other routines handle the escape of 194 * specific characters in the token. 195 */ 196 197 if (*p == '\\' && *(p + 1) != '\n' && isspace(*(p + 1))) { 198 shift1left(p); 199 } 200 p++; 201 } 202 if (*p == '\0') { 203 savep = 0; /* indicate this is last token */ 204 } else { 205 *p = '\0'; 206 savep = ++p; 207 } 208 return (retp); 209 } 210 211 /* 212 * matchname() parses a line of the /etc/nfssec.conf file 213 * and match the sc_name with the given name. 214 * If there is a match, it fills the information into the given 215 * pointer of the seconfig_t structure. 216 * 217 * Returns TRUE if a match is found. 218 */ 219 static bool_t 220 matchname(char *line, char *name, seconfig_t *secp) 221 { 222 char *tok1, *tok2; /* holds a token from the line */ 223 char *secname, *gss_mech, *gss_qop; /* pointer to a secmode name */ 224 225 if ((secname = gettoken(line, FALSE)) == NULL) { 226 /* bad line */ 227 return (FALSE); 228 } 229 230 if (strcmp(secname, name) != 0) { 231 return (FALSE); 232 } 233 234 tok1 = tok2 = NULL; 235 if (((tok1 = gettoken(NULL, FALSE)) == NULL) || 236 ((gss_mech = gettoken(NULL, FALSE)) == NULL) || 237 ((gss_qop = gettoken(NULL, FALSE)) == NULL) || 238 ((tok2 = gettoken(NULL, FALSE)) == NULL) || 239 ((secp->sc_service = getvalue(tok2, sc_service)) 240 == SC_FAILURE)) { 241 return (FALSE); 242 } 243 secp->sc_nfsnum = atoi(tok1); 244 (void) strcpy(secp->sc_name, secname); 245 (void) strcpy(secp->sc_gss_mech, gss_mech); 246 secp->sc_gss_mech_type = NULL; 247 if (secp->sc_gss_mech[0] != '-') { 248 if (!rpc_gss_mech_to_oid(gss_mech, &secp->sc_gss_mech_type) || 249 !rpc_gss_qop_to_num(gss_qop, gss_mech, &secp->sc_qop)) { 250 return (FALSE); 251 } 252 } 253 254 return (TRUE); 255 } 256 257 /* 258 * matchnum() parses a line of the /etc/nfssec.conf file 259 * and match the sc_nfsnum with the given number. 260 * If it is a match, it fills the information in the given pointer 261 * of the seconfig_t structure. 262 * 263 * Returns TRUE if a match is found. 264 */ 265 static bool_t 266 matchnum(char *line, int num, seconfig_t *secp) 267 { 268 char *tok1, *tok2; /* holds a token from the line */ 269 char *secname, *gss_mech, *gss_qop; /* pointer to a secmode name */ 270 271 if ((secname = gettoken(line, FALSE)) == NULL) { 272 /* bad line */ 273 return (FALSE); 274 } 275 276 tok1 = tok2 = NULL; 277 if ((tok1 = gettoken(NULL, FALSE)) == NULL) { 278 /* bad line */ 279 return (FALSE); 280 } 281 282 if ((secp->sc_nfsnum = atoi(tok1)) != num) { 283 return (FALSE); 284 } 285 286 if (((gss_mech = gettoken(NULL, FALSE)) == NULL) || 287 ((gss_qop = gettoken(NULL, FALSE)) == NULL) || 288 ((tok2 = gettoken(NULL, FALSE)) == NULL) || 289 ((secp->sc_service = getvalue(tok2, sc_service)) 290 == SC_FAILURE)) { 291 return (FALSE); 292 } 293 294 (void) strcpy(secp->sc_name, secname); 295 (void) strcpy(secp->sc_gss_mech, gss_mech); 296 if (secp->sc_gss_mech[0] != '-') { 297 if (!rpc_gss_mech_to_oid(gss_mech, &secp->sc_gss_mech_type) || 298 !rpc_gss_qop_to_num(gss_qop, gss_mech, &secp->sc_qop)) { 299 return (FALSE); 300 } 301 } 302 303 return (TRUE); 304 } 305 306 /* 307 * Fill in the RPC Protocol security flavor number 308 * into the sc_rpcnum of seconfig_t structure. 309 * 310 * Mainly to map NFS secmod number to RPCSEC_GSS if 311 * a mechanism name is specified. 312 */ 313 static void 314 get_rpcnum(seconfig_t *secp) 315 { 316 if (secp->sc_gss_mech[0] != '-') { 317 secp->sc_rpcnum = RPCSEC_GSS; 318 } else { 319 secp->sc_rpcnum = secp->sc_nfsnum; 320 } 321 } 322 323 /* 324 * Parse a given hostname (nodename[.domain@realm]) to 325 * instant name (nodename[.domain]) and realm. 326 * 327 * Assuming user has allocated the space for inst and realm. 328 */ 329 static int 330 parsehostname(char *hostname, char *inst, char *realm) 331 { 332 char *h, *r; 333 334 if (!hostname) 335 return (0); 336 337 h = (char *)strdup(hostname); 338 if (!h) { 339 syslog(LOG_ERR, "parsehostname: no memory\n"); 340 return (0); 341 } 342 343 r = (char *)strchr(h, '@'); 344 if (!r) { 345 (void) strcpy(inst, h); 346 (void) strcpy(realm, ""); 347 } else { 348 *r++ = '\0'; 349 (void) strcpy(inst, h); 350 (void) strcpy(realm, r); 351 } 352 free(h); 353 return (1); 354 } 355 356 /* 357 * Get the name corresponding to a qop num. 358 */ 359 char * 360 nfs_get_qop_name(seconfig_t *entryp) 361 { 362 char *tok; /* holds a token from the line */ 363 char *secname, *gss_qop = NULL; /* pointer to a secmode name */ 364 char line[BUFSIZ]; /* holds each line of NFSSEC_CONF */ 365 FILE *fp; /* file stream for NFSSEC_CONF */ 366 367 (void) mutex_lock(&matching_lock); 368 if ((fp = fopen(NFSSEC_CONF, "r")) == NULL) { 369 (void) mutex_unlock(&matching_lock); 370 return (NULL); 371 } 372 373 while (fgets(line, BUFSIZ, fp)) { 374 if (!(blank(line) || comment(line))) { 375 if ((secname = gettoken(line, FALSE)) == NULL) { 376 /* bad line */ 377 continue; 378 } 379 if (strcmp(secname, entryp->sc_name) == 0) { 380 tok = NULL; 381 if ((tok = gettoken(NULL, FALSE)) == NULL) { 382 /* bad line */ 383 goto err; 384 } 385 386 if (atoi(tok) != entryp->sc_nfsnum) 387 goto err; 388 389 if ((gettoken(NULL, FALSE) == NULL) || 390 ((gss_qop = gettoken(NULL, FALSE)) 391 == NULL)) { 392 goto err; 393 } 394 break; 395 } 396 } 397 } 398 err: 399 (void) fclose(fp); 400 (void) mutex_unlock(&matching_lock); 401 return (gss_qop); 402 } 403 404 /* 405 * This routine creates an auth handle assocaited with the 406 * negotiated security flavor contained in nfs_sec. The auth 407 * handle will be used in the next LOOKUP request to fetch 408 * the filehandle. 409 */ 410 AUTH * 411 nfs_create_ah(CLIENT *cl, char *hostname, seconfig_t *nfs_sec) 412 { 413 char netname[MAXNETNAMELEN+1]; 414 char svc_name[MAXNETNAMELEN+1]; 415 char *gss_qop; 416 static int window = 60; 417 418 if (nfs_sec == NULL) 419 goto err; 420 421 switch (nfs_sec->sc_rpcnum) { 422 case AUTH_UNIX: 423 case AUTH_NONE: 424 return (NULL); 425 426 case AUTH_DES: 427 if (!host2netname(netname, hostname, NULL)) 428 goto err; 429 430 return (authdes_seccreate(netname, window, hostname, 431 NULL)); 432 433 case RPCSEC_GSS: 434 if (cl == NULL) 435 goto err; 436 437 if (nfs_sec->sc_gss_mech_type == NULL) { 438 syslog(LOG_ERR, 439 "nfs_create_ah: need mechanism information\n"); 440 goto err; 441 } 442 443 /* 444 * RPCSEC_GSS service names are of the form svc@host.dom 445 */ 446 (void) sprintf(svc_name, "nfs@%s", hostname); 447 448 gss_qop = nfs_get_qop_name(nfs_sec); 449 if (gss_qop == NULL) 450 goto err; 451 452 return (rpc_gss_seccreate(cl, svc_name, 453 nfs_sec->sc_gss_mech, nfs_sec->sc_service, gss_qop, 454 NULL, NULL)); 455 456 default: 457 syslog(LOG_ERR, "nfs_create_ah: unknown flavor\n"); 458 return (NULL); 459 } 460 err: 461 syslog(LOG_ERR, "nfs_create_ah: failed to make auth handle\n"); 462 return (NULL); 463 } 464 465 #ifdef WNFS_SEC_NEGO 466 /* 467 * This routine negotiates sec flavors with server and returns: 468 * SNEGO_SUCCESS: successful; sec flavors are 469 * returned in snego, 470 * SNEGO_DEF_VALID: default sec flavor valid; no need 471 * to negotiate flavors, 472 * SNEGO_ARRAY_TOO_SMALL: array too small, 473 * SNEGO_FAILURE: failure 474 */ 475 /* 476 * The following depicts how sec flavors are placed in an 477 * overloaded V2 fhandle: 478 * 479 * Note that the first four octets contain the length octet, 480 * the status octet, and two padded octets to make them XDR 481 * four-octet aligned. 482 * 483 * 1 2 3 4 32 484 * +---+---+---+---+---+---+---+---+ +---+---+---+---+ +---+ 485 * | l | s | | | sec_1 |...| sec_n |...| | 486 * +---+---+---+---+---+---+---+---+ +---+---+---+---+ +---+ 487 * 488 * where 489 * 490 * the status octet s indicates whether there are more security 491 * flavors(1 means yes, 0 means no) that require the client to 492 * perform another 0x81 LOOKUP to get them, 493 * 494 * the length octet l is the length describing the number of 495 * valid octets that follow. (l = 4 * n, where n is the number 496 * 497 * The following depicts how sec flavors are placed in an 498 * overloaded V3 fhandle: 499 * 500 * 1 4 501 * +--+--+--+--+ 502 * | len | 503 * +--+--+--+--+ 504 * up to 64 505 * +--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+ 506 * |s | | | | sec_1 | sec_2 | ... | sec_n | 507 * +--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+ 508 * 509 * len = 4 * (n+1), where n is the number of security flavors 510 * sent in the current overloaded filehandle. 511 * 512 * the status octet s indicates whether there are more security 513 * mechanisms(1 means yes, 0 means no) that require the client 514 * to perform another 0x81 LOOKUP to get them. 515 * 516 * Three octets are padded after the status octet. 517 */ 518 enum snego_stat 519 nfs_sec_nego(rpcprog_t vers, CLIENT *clnt, char *fspath, struct snego_t *snego) 520 { 521 enum clnt_stat rpc_stat; 522 static int MAX_V2_CNT = (WNL_FHSIZE/sizeof (int)) - 1; 523 static int MAX_V3_CNT = (WNL3_FHSIZE/sizeof (int)) - 1; 524 static struct timeval TIMEOUT = { 25, 0 }; 525 int status; 526 527 if (clnt == NULL || fspath == NULL || snego == NULL) 528 return (SNEGO_FAILURE); 529 530 if (vers == WNL_V2) { 531 wnl_diropargs arg; 532 wnl_diropres clnt_res; 533 534 memset((char *)&arg.dir, 0, sizeof (wnl_fh)); 535 arg.name = fspath; 536 memset((char *)&clnt_res, 0, sizeof (clnt_res)); 537 rpc_stat = clnt_call(clnt, WNLPROC_LOOKUP, 538 (xdrproc_t)xdr_wnl_diropargs, (caddr_t)&arg, 539 (xdrproc_t)xdr_wnl_diropres, (caddr_t)&clnt_res, 540 TIMEOUT); 541 if (rpc_stat == RPC_SUCCESS && clnt_res.status == WNL_OK) 542 return (SNEGO_DEF_VALID); 543 if (rpc_stat != RPC_AUTHERROR) 544 return (SNEGO_FAILURE); 545 546 { 547 struct rpc_err e; 548 wnl_diropres res; 549 char *p; 550 int tot = 0; 551 552 CLNT_GETERR(clnt, &e); 553 if (e.re_why != AUTH_TOOWEAK) 554 return (SNEGO_FAILURE); 555 556 if ((p = malloc(strlen(fspath)+3)) == NULL) { 557 syslog(LOG_ERR, "no memory\n"); 558 return (SNEGO_FAILURE); 559 } 560 /* 561 * Do an x81 LOOKUP 562 */ 563 p[0] = (char)WNL_SEC_NEGO; 564 strcpy(&p[2], fspath); 565 do { 566 p[1] = (char)(1+snego->cnt); /* sec index */ 567 arg.name = p; 568 memset((char *)&res, 0, sizeof (wnl_diropres)); 569 if (wnlproc_lookup_2(&arg, &res, clnt) != 570 RPC_SUCCESS || res.status != WNL_OK) { 571 free(p); 572 return (SNEGO_FAILURE); 573 } 574 575 /* 576 * retrieve flavors from filehandle: 577 * 1st byte: length 578 * 2nd byte: status 579 * 3rd & 4th: pad 580 * 5th and after: sec flavors. 581 */ 582 { 583 char *c = (char *)&res.wnl_diropres_u. 584 wnl_diropres.file; 585 int ii; 586 int cnt = ((int)*c)/sizeof (uint_t); 587 /* LINTED pointer alignment */ 588 int *ip = (int *)(c+sizeof (int)); 589 590 tot += cnt; 591 if (tot >= MAX_FLAVORS) { 592 free(p); 593 return (SNEGO_ARRAY_TOO_SMALL); 594 } 595 status = (int)*(c+1); 596 if (cnt > MAX_V2_CNT || cnt < 0) { 597 free(p); 598 return (SNEGO_FAILURE); 599 } 600 for (ii = 0; ii < cnt; ii++) 601 snego->array[snego->cnt+ii] = 602 ntohl(*(ip+ii)); 603 snego->cnt += cnt; 604 } 605 } while (status); 606 free(p); 607 return (SNEGO_SUCCESS); 608 } 609 } else if (vers == WNL_V3) { 610 WNL_LOOKUP3args arg; 611 WNL_LOOKUP3res clnt_res; 612 613 memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3)); 614 arg.what.name = fspath; 615 arg.what.dir.data.data_len = 0; 616 arg.what.dir.data.data_val = 0; 617 memset((char *)&clnt_res, 0, sizeof (clnt_res)); 618 rpc_stat = clnt_call(clnt, WNLPROC3_LOOKUP, 619 (xdrproc_t)xdr_WNL_LOOKUP3args, (caddr_t)&arg, 620 (xdrproc_t)xdr_WNL_LOOKUP3res, (caddr_t)&clnt_res, 621 TIMEOUT); 622 if (rpc_stat == RPC_SUCCESS && clnt_res.status == WNL3_OK) 623 return (SNEGO_DEF_VALID); 624 if (rpc_stat != RPC_AUTHERROR) 625 return (SNEGO_FAILURE); 626 627 { 628 struct rpc_err e; 629 WNL_LOOKUP3res res; 630 char *p; 631 int tot = 0; 632 633 CLNT_GETERR(clnt, &e); 634 if (e.re_why != AUTH_TOOWEAK) 635 return (SNEGO_FAILURE); 636 637 if ((p = malloc(strlen(fspath)+3)) == NULL) { 638 syslog(LOG_ERR, "no memory\n"); 639 return (SNEGO_FAILURE); 640 } 641 /* 642 * Do an x81 LOOKUP 643 */ 644 p[0] = (char)WNL_SEC_NEGO; 645 strcpy(&p[2], fspath); 646 do { 647 p[1] = (char)(1+snego->cnt); /* sec index */ 648 arg.what.name = p; 649 memset((char *)&res, 0, 650 sizeof (WNL_LOOKUP3res)); 651 if (wnlproc3_lookup_3(&arg, &res, clnt) != 652 RPC_SUCCESS || res.status != WNL3_OK) { 653 free(p); 654 return (SNEGO_FAILURE); 655 } 656 657 /* 658 * retrieve flavors from filehandle: 659 * 660 * 1st byte: status 661 * 2nd thru 4th: pad 662 * 5th and after: sec flavors. 663 */ 664 { 665 char *c = res.WNL_LOOKUP3res_u. 666 res_ok.object.data.data_val; 667 int ii; 668 int len = res.WNL_LOOKUP3res_u.res_ok. 669 object.data.data_len; 670 int cnt; 671 /* LINTED pointer alignment */ 672 int *ip = (int *)(c+sizeof (int)); 673 674 cnt = len/sizeof (uint_t) - 1; 675 tot += cnt; 676 if (tot >= MAX_FLAVORS) { 677 free(p); 678 return (SNEGO_ARRAY_TOO_SMALL); 679 } 680 status = (int)(*c); 681 if (cnt > MAX_V3_CNT || cnt < 0) { 682 free(p); 683 return (SNEGO_FAILURE); 684 } 685 for (ii = 0; ii < cnt; ii++) 686 snego->array[snego->cnt+ii] = 687 ntohl(*(ip+ii)); 688 snego->cnt += cnt; 689 } 690 } while (status); 691 free(p); 692 return (SNEGO_SUCCESS); 693 } 694 } 695 return (SNEGO_FAILURE); 696 } 697 #endif 698 699 /* 700 * Get seconfig from /etc/nfssec.conf by name or by number or 701 * by descriptior. 702 */ 703 /* ARGSUSED */ 704 static int 705 get_seconfig(int whichway, char *name, int num, 706 rpc_gss_service_t service, seconfig_t *entryp) 707 { 708 char line[BUFSIZ]; /* holds each line of NFSSEC_CONF */ 709 FILE *fp; /* file stream for NFSSEC_CONF */ 710 711 if ((whichway == GETBYNAME) && (name == NULL)) 712 return (SC_NOTFOUND); 713 714 (void) mutex_lock(&matching_lock); 715 if ((fp = fopen(NFSSEC_CONF, "r")) == NULL) { 716 (void) mutex_unlock(&matching_lock); 717 return (SC_OPENFAIL); 718 } 719 720 while (fgets(line, BUFSIZ, fp)) { 721 if (!(blank(line) || comment(line))) { 722 switch (whichway) { 723 case GETBYNAME: 724 if (matchname(line, name, entryp)) { 725 goto found; 726 } 727 break; 728 729 case GETBYNUM: 730 if (matchnum(line, num, entryp)) { 731 goto found; 732 } 733 break; 734 735 default: 736 break; 737 } 738 } 739 } 740 (void) fclose(fp); 741 (void) mutex_unlock(&matching_lock); 742 return (SC_NOTFOUND); 743 744 found: 745 (void) fclose(fp); 746 (void) mutex_unlock(&matching_lock); 747 (void) get_rpcnum(entryp); 748 return (SC_NOERROR); 749 } 750 751 752 /* 753 * NFS project private API. 754 * Get a seconfig entry from /etc/nfssec.conf by nfs specific sec name, 755 * e.g. des, krb5p, etc. 756 */ 757 int 758 nfs_getseconfig_byname(char *secmode_name, seconfig_t *entryp) 759 { 760 if (!entryp) 761 return (SC_NOMEM); 762 763 return (get_seconfig(GETBYNAME, secmode_name, 0, rpc_gss_svc_none, 764 entryp)); 765 } 766 767 /* 768 * NFS project private API. 769 * 770 * Get a seconfig entry from /etc/nfssec.conf by nfs specific sec number, 771 * e.g. AUTH_DES, AUTH_KRB5_P, etc. 772 */ 773 int 774 nfs_getseconfig_bynumber(int nfs_secnum, seconfig_t *entryp) 775 { 776 if (!entryp) 777 return (SC_NOMEM); 778 779 return (get_seconfig(GETBYNUM, NULL, nfs_secnum, rpc_gss_svc_none, 780 entryp)); 781 } 782 783 /* 784 * NFS project private API. 785 * 786 * Get a seconfig_t entry used as the default for NFS operations. 787 * The default flavor entry is defined in /etc/nfssec.conf. 788 * 789 * Assume user has allocate spaces for secp. 790 */ 791 int 792 nfs_getseconfig_default(seconfig_t *secp) 793 { 794 if (secp == NULL) 795 return (SC_NOMEM); 796 797 return (nfs_getseconfig_byname("default", secp)); 798 } 799 800 801 /* 802 * NFS project private API. 803 * 804 * Free an sec_data structure. 805 * Free the parts that nfs_clnt_secdata allocates. 806 */ 807 void 808 nfs_free_secdata(sec_data_t *secdata) 809 { 810 dh_k4_clntdata_t *dkdata; 811 gss_clntdata_t *gdata; 812 813 if (!secdata) 814 return; 815 816 switch (secdata->rpcflavor) { 817 case AUTH_UNIX: 818 case AUTH_NONE: 819 break; 820 821 case AUTH_DES: 822 /* LINTED pointer alignment */ 823 dkdata = (dh_k4_clntdata_t *)secdata->data; 824 if (dkdata) { 825 if (dkdata->netname) 826 free(dkdata->netname); 827 if (dkdata->syncaddr.buf) 828 free(dkdata->syncaddr.buf); 829 free(dkdata); 830 } 831 break; 832 833 case RPCSEC_GSS: 834 /* LINTED pointer alignment */ 835 gdata = (gss_clntdata_t *)secdata->data; 836 if (gdata) { 837 if (gdata->mechanism.elements) 838 free(gdata->mechanism.elements); 839 free(gdata); 840 } 841 break; 842 843 default: 844 break; 845 } 846 847 free(secdata); 848 } 849 850 /* 851 * Make an client side sec_data structure and fill in appropriate value 852 * based on its rpc security flavor. 853 * 854 * It is caller's responsibility to allocate space for seconfig_t, 855 * and this routine will allocate space for the sec_data structure 856 * and related data field. 857 * 858 * Return the sec_data_t on success. 859 * If fail, return NULL pointer. 860 */ 861 sec_data_t * 862 nfs_clnt_secdata(seconfig_t *secp, char *hostname, struct knetconfig *knconf, 863 struct netbuf *syncaddr, int flags) 864 { 865 char netname[MAXNETNAMELEN+1]; 866 sec_data_t *secdata; 867 dh_k4_clntdata_t *dkdata; 868 gss_clntdata_t *gdata; 869 870 secdata = malloc(sizeof (sec_data_t)); 871 if (!secdata) { 872 syslog(LOG_ERR, "nfs_clnt_secdata: no memory\n"); 873 return (NULL); 874 } 875 (void) memset(secdata, 0, sizeof (sec_data_t)); 876 877 secdata->secmod = secp->sc_nfsnum; 878 secdata->rpcflavor = secp->sc_rpcnum; 879 secdata->uid = secp->sc_uid; 880 secdata->flags = flags; 881 882 /* 883 * Now, fill in the information for client side secdata : 884 * 885 * For AUTH_UNIX, AUTH_DES 886 * hostname can be in the form of 887 * nodename or 888 * nodename.domain 889 * 890 * For RPCSEC_GSS security flavor 891 * hostname can be in the form of 892 * nodename or 893 * nodename.domain or 894 * nodename@realm (realm can be the same as the domain) or 895 * nodename.domain@realm 896 */ 897 switch (secp->sc_rpcnum) { 898 case AUTH_UNIX: 899 case AUTH_NONE: 900 secdata->data = NULL; 901 break; 902 903 case AUTH_DES: 904 /* 905 * If hostname is in the format of host.nisdomain 906 * the netname will be constructed with 907 * this nisdomain name rather than the default 908 * domain of the machine. 909 */ 910 if (!host2netname(netname, hostname, NULL)) { 911 syslog(LOG_ERR, "host2netname: %s: unknown\n", 912 hostname); 913 goto err_out; 914 } 915 dkdata = malloc(sizeof (dh_k4_clntdata_t)); 916 if (!dkdata) { 917 syslog(LOG_ERR, 918 "nfs_clnt_secdata: no memory\n"); 919 goto err_out; 920 } 921 (void) memset((char *)dkdata, 0, 922 sizeof (dh_k4_clntdata_t)); 923 if ((dkdata->netname = strdup(netname)) == NULL) { 924 syslog(LOG_ERR, 925 "nfs_clnt_secdata: no memory\n"); 926 goto err_out; 927 } 928 dkdata->netnamelen = strlen(netname); 929 dkdata->knconf = knconf; 930 dkdata->syncaddr = *syncaddr; 931 dkdata->syncaddr.buf = malloc(syncaddr->len); 932 if (dkdata->syncaddr.buf == NULL) { 933 syslog(LOG_ERR, 934 "nfs_clnt_secdata: no memory\n"); 935 goto err_out; 936 } 937 (void) memcpy(dkdata->syncaddr.buf, syncaddr->buf, 938 syncaddr->len); 939 secdata->data = (caddr_t)dkdata; 940 break; 941 942 case RPCSEC_GSS: 943 if (secp->sc_gss_mech_type == NULL) { 944 syslog(LOG_ERR, 945 "nfs_clnt_secdata: need mechanism information\n"); 946 goto err_out; 947 } 948 949 gdata = malloc(sizeof (gss_clntdata_t)); 950 if (!gdata) { 951 syslog(LOG_ERR, 952 "nfs_clnt_secdata: no memory\n"); 953 goto err_out; 954 } 955 956 (void) strcpy(gdata->uname, "nfs"); 957 if (!parsehostname(hostname, gdata->inst, 958 gdata->realm)) { 959 syslog(LOG_ERR, 960 "nfs_clnt_secdata: bad host name\n"); 961 goto err_out; 962 } 963 964 gdata->mechanism.length = 965 secp->sc_gss_mech_type->length; 966 if (!(gdata->mechanism.elements = 967 malloc(secp->sc_gss_mech_type->length))) { 968 syslog(LOG_ERR, 969 "nfs_clnt_secdata: no memory\n"); 970 goto err_out; 971 } 972 (void) memcpy(gdata->mechanism.elements, 973 secp->sc_gss_mech_type->elements, 974 secp->sc_gss_mech_type->length); 975 976 gdata->qop = secp->sc_qop; 977 gdata->service = secp->sc_service; 978 secdata->data = (caddr_t)gdata; 979 break; 980 981 default: 982 syslog(LOG_ERR, "nfs_clnt_secdata: unknown flavor\n"); 983 goto err_out; 984 } 985 986 return (secdata); 987 988 err_out: 989 free(secdata); 990 return (NULL); 991 } 992 993 /* 994 * nfs_get_root_principal() maps a host name to its principal name 995 * based on the given security information. 996 * 997 * input : seconfig - security configuration information 998 * host - the host name which could be in the following forms: 999 * node 1000 * node.namedomain 1001 * node@secdomain (e.g. kerberos realm is a secdomain) 1002 * node.namedomain@secdomain 1003 * output : rootname_p - address of the principal name for the host 1004 * 1005 * Currently, this routine is only used by share program. 1006 * 1007 */ 1008 bool_t 1009 nfs_get_root_principal(seconfig_t *seconfig, char *host, caddr_t *rootname_p) 1010 { 1011 char netname[MAXNETNAMELEN+1], node[MAX_NAME_LEN]; 1012 char secdomain[MAX_NAME_LEN]; 1013 rpc_gss_principal_t gssname; 1014 1015 switch (seconfig->sc_rpcnum) { 1016 case AUTH_DES: 1017 if (!host2netname(netname, host, NULL)) { 1018 syslog(LOG_ERR, 1019 "nfs_get_root_principal: unknown host: %s\n", host); 1020 return (FALSE); 1021 } 1022 *rootname_p = strdup(netname); 1023 if (!*rootname_p) { 1024 syslog(LOG_ERR, 1025 "nfs_get_root_principal: no memory\n"); 1026 return (FALSE); 1027 } 1028 break; 1029 1030 case RPCSEC_GSS: 1031 if (!parsehostname(host, node, secdomain)) { 1032 syslog(LOG_ERR, 1033 "nfs_get_root_principal: bad host name\n"); 1034 return (FALSE); 1035 } 1036 if (!rpc_gss_get_principal_name(&gssname, 1037 seconfig->sc_gss_mech, "root", node, secdomain)) { 1038 syslog(LOG_ERR, 1039 "nfs_get_root_principal: can not get principal name : %s\n", host); 1040 return (FALSE); 1041 } 1042 1043 *rootname_p = (caddr_t)gssname; 1044 break; 1045 1046 default: 1047 return (FALSE); 1048 } 1049 return (TRUE); 1050 } 1051 1052 1053 /* 1054 * SYSLOG SC_* errors. 1055 */ 1056 int 1057 nfs_syslog_scerr(int scerror, char msg[]) 1058 { 1059 switch (scerror) { 1060 case SC_NOMEM : 1061 sprintf(msg, "%s : no memory", NFSSEC_CONF); 1062 return (0); 1063 case SC_OPENFAIL : 1064 sprintf(msg, "can not open %s", NFSSEC_CONF); 1065 return (0); 1066 case SC_NOTFOUND : 1067 sprintf(msg, "has no entry in %s", NFSSEC_CONF); 1068 return (0); 1069 case SC_BADENTRIES : 1070 sprintf(msg, "bad entry in %s", NFSSEC_CONF); 1071 return (0); 1072 default: 1073 msg[0] = '\0'; 1074 return (-1); 1075 } 1076 } 1077