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