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