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