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