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