Print this page
    
Revert "OS-871 zone stuck in shutting_down - waiting for kernel thread nfsauth_refresh_thread to terminate"
This reverts commit 887d2a84c612cea61b6ad544f54cf790cfb9de3e.
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/cmd/fs.d/nfs/mountd/mountd.c
          +++ new/usr/src/cmd/fs.d/nfs/mountd/mountd.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  
    | 
      ↓ open down ↓ | 
    13 lines elided | 
    
      ↑ open up ↑ | 
  
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  24      - * Copyright 2012 Joyent, Inc. All rights reserved.
  25   24   * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
  26   25   * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  27   26   */
  28   27  
  29   28  /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
  30   29  /*        All Rights Reserved   */
  31   30  
  32   31  /*
  33   32   * Portions of this source code were derived from Berkeley 4.3 BSD
  34   33   * under license from the Regents of the University of California.
  35   34   */
  36   35  
  37   36  #include <stdio.h>
  38   37  #include <stdio_ext.h>
  39   38  #include <stdlib.h>
  40   39  #include <ctype.h>
  41   40  #include <sys/types.h>
  42   41  #include <string.h>
  43   42  #include <syslog.h>
  44   43  #include <sys/param.h>
  45   44  #include <rpc/rpc.h>
  46   45  #include <sys/stat.h>
  47   46  #include <netconfig.h>
  48   47  #include <netdir.h>
  49   48  #include <sys/file.h>
  50   49  #include <sys/time.h>
  51   50  #include <sys/errno.h>
  52   51  #include <rpcsvc/mount.h>
  53   52  #include <sys/pathconf.h>
  54   53  #include <sys/systeminfo.h>
  55   54  #include <sys/utsname.h>
  56   55  #include <sys/wait.h>
  57   56  #include <sys/resource.h>
  58   57  #include <signal.h>
  59   58  #include <locale.h>
  60   59  #include <unistd.h>
  61   60  #include <errno.h>
  62   61  #include <sys/socket.h>
  63   62  #include <netinet/in.h>
  64   63  #include <arpa/inet.h>
  65   64  #include <netdb.h>
  66   65  #include <thread.h>
  67   66  #include <assert.h>
  68   67  #include <priv_utils.h>
  69   68  #include <nfs/auth.h>
  70   69  #include <nfs/nfssys.h>
  71   70  #include <nfs/nfs.h>
  72   71  #include <nfs/nfs_sec.h>
  73   72  #include <rpcsvc/daemon_utils.h>
  74   73  #include <deflt.h>
  75   74  #include "../../fslib.h"
  76   75  #include <sharefs/share.h>
  77   76  #include <sharefs/sharetab.h>
  78   77  #include "../lib/sharetab.h"
  79   78  #include "mountd.h"
  80   79  #include <tsol/label.h>
  81   80  #include <sys/tsol/label_macro.h>
  82   81  #include <libtsnet.h>
  83   82  #include <sys/sdt.h>
  84   83  #include <libscf.h>
  85   84  #include <limits.h>
  86   85  #include <sys/nvpair.h>
  87   86  #include <attr.h>
  88   87  #include "smfcfg.h"
  89   88  #include <pwd.h>
  90   89  #include <grp.h>
  91   90  #include <alloca.h>
  92   91  
  93   92  extern int daemonize_init(void);
  94   93  extern void daemonize_fini(int);
  95   94  
  96   95  extern int _nfssys(int, void *);
  97   96  
  98   97  struct sh_list *share_list;
  99   98  
 100   99  rwlock_t sharetab_lock;         /* lock to protect the cached sharetab */
 101  100  static mutex_t mnttab_lock;     /* prevent concurrent mnttab readers */
 102  101  
 103  102  static mutex_t logging_queue_lock;
 104  103  static cond_t logging_queue_cv;
 105  104  
 106  105  static share_t *find_lofsentry(char *, int *);
 107  106  static int getclientsflavors_old(share_t *, struct cln *, int *);
 108  107  static int getclientsflavors_new(share_t *, struct cln *, int *);
 109  108  static int check_client_old(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
 110  109      gid_t *, uid_t *, gid_t *, uint_t *, gid_t **);
 111  110  static int check_client_new(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
 112  111      gid_t *, uid_t *, gid_t *i, uint_t *, gid_t **);
 113  112  static void mnt(struct svc_req *, SVCXPRT *);
 114  113  static void mnt_pathconf(struct svc_req *);
 115  114  static int mount(struct svc_req *r);
 116  115  static void sh_free(struct sh_list *);
 117  116  static void umount(struct svc_req *);
 118  117  static void umountall(struct svc_req *);
 119  118  static int newopts(char *);
 120  119  static tsol_tpent_t *get_client_template(struct sockaddr *);
 121  120  
 122  121  static int debug;
 123  122  static int verbose;
 124  123  static int rejecting;
 125  124  static int mount_vers_min = MOUNTVERS;
 126  125  static int mount_vers_max = MOUNTVERS3;
 127  126  static int mountd_port = 0;
 128  127  
 129  128  extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
 130  129  
 131  130  thread_t        nfsauth_thread;
 132  131  thread_t        cmd_thread;
 133  132  thread_t        logging_thread;
 134  133  
 135  134  typedef struct logging_data {
 136  135          char                    *ld_host;
 137  136          char                    *ld_path;
 138  137          char                    *ld_rpath;
 139  138          int                     ld_status;
 140  139          char                    *ld_netid;
 141  140          struct netbuf           *ld_nb;
 142  141          struct logging_data     *ld_next;
 143  142  } logging_data;
 144  143  
 145  144  static logging_data *logging_head = NULL;
 146  145  static logging_data *logging_tail = NULL;
 147  146  
 148  147  /*
 149  148   * Our copy of some system variables obtained using sysconf(3c)
 150  149   */
 151  150  static long ngroups_max;        /* _SC_NGROUPS_MAX */
 152  151  static long pw_size;            /* _SC_GETPW_R_SIZE_MAX */
 153  152  
 154  153  /* ARGSUSED */
 155  154  static void *
 156  155  nfsauth_svc(void *arg)
 157  156  {
 158  157          int     doorfd = -1;
 159  158          uint_t  darg;
 160  159  #ifdef DEBUG
 161  160          int     dfd;
 162  161  #endif
 163  162  
 164  163          if ((doorfd = door_create(nfsauth_func, NULL,
 165  164              DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
 166  165                  syslog(LOG_ERR, "Unable to create door: %m\n");
 167  166                  exit(10);
 168  167          }
 169  168  
 170  169  #ifdef DEBUG
 171  170          /*
 172  171           * Create a file system path for the door
 173  172           */
 174  173          if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
 175  174              S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
 176  175                  syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
 177  176                  (void) close(doorfd);
 178  177                  exit(11);
 179  178          }
 180  179  
 181  180          /*
 182  181           * Clean up any stale namespace associations
 183  182           */
 184  183          (void) fdetach(MOUNTD_DOOR);
 185  184  
 186  185          /*
 187  186           * Register in namespace to pass to the kernel to door_ki_open
 188  187           */
 189  188          if (fattach(doorfd, MOUNTD_DOOR) == -1) {
 190  189                  syslog(LOG_ERR, "Unable to fattach door: %m\n");
 191  190                  (void) close(dfd);
 192  191                  (void) close(doorfd);
 193  192                  exit(12);
 194  193          }
 195  194          (void) close(dfd);
 196  195  #endif
 197  196  
 198  197          /*
 199  198           * Must pass the doorfd down to the kernel.
 200  199           */
 201  200          darg = doorfd;
 202  201          (void) _nfssys(MOUNTD_ARGS, &darg);
 203  202  
 204  203          /*
 205  204           * Wait for incoming calls
 206  205           */
 207  206          /*CONSTCOND*/
 208  207          for (;;)
 209  208                  (void) pause();
 210  209  
 211  210          /*NOTREACHED*/
 212  211          syslog(LOG_ERR, gettext("Door server exited"));
 213  212          return (NULL);
 214  213  }
 215  214  
 216  215  /*
 217  216   * NFS command service thread code for setup and handling of the
 218  217   * nfs_cmd requests for character set conversion and other future
 219  218   * events.
 220  219   */
 221  220  
 222  221  static void *
 223  222  cmd_svc(void *arg)
 224  223  {
 225  224          int     doorfd = -1;
 226  225          uint_t  darg;
 227  226  
 228  227          if ((doorfd = door_create(nfscmd_func, NULL,
 229  228              DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
 230  229                  syslog(LOG_ERR, "Unable to create cmd door: %m\n");
 231  230                  exit(10);
 232  231          }
 233  232  
 234  233          /*
 235  234           * Must pass the doorfd down to the kernel.
 236  235           */
 237  236          darg = doorfd;
 238  237          (void) _nfssys(NFSCMD_ARGS, &darg);
 239  238  
 240  239          /*
 241  240           * Wait for incoming calls
 242  241           */
 243  242          /*CONSTCOND*/
 244  243          for (;;)
 245  244                  (void) pause();
 246  245  
 247  246          /*NOTREACHED*/
 248  247          syslog(LOG_ERR, gettext("Cmd door server exited"));
 249  248          return (NULL);
 250  249  }
 251  250  
 252  251  static void
 253  252  free_logging_data(logging_data *lq)
 254  253  {
 255  254          if (lq != NULL) {
 256  255                  free(lq->ld_host);
 257  256                  free(lq->ld_netid);
 258  257  
 259  258                  if (lq->ld_nb != NULL) {
 260  259                          free(lq->ld_nb->buf);
 261  260                          free(lq->ld_nb);
 262  261                  }
 263  262  
 264  263                  free(lq->ld_path);
 265  264                  free(lq->ld_rpath);
 266  265  
 267  266                  free(lq);
 268  267          }
 269  268  }
 270  269  
 271  270  static logging_data *
 272  271  remove_head_of_queue(void)
 273  272  {
 274  273          logging_data    *lq;
 275  274  
 276  275          /*
 277  276           * Pull it off the queue.
 278  277           */
 279  278          lq = logging_head;
 280  279          if (lq) {
 281  280                  logging_head = lq->ld_next;
 282  281  
 283  282                  /*
 284  283                   * Drained it.
 285  284                   */
 286  285                  if (logging_head == NULL) {
 287  286                          logging_tail = NULL;
 288  287                  }
 289  288          }
 290  289  
 291  290          return (lq);
 292  291  }
 293  292  
 294  293  static void
 295  294  do_logging_queue(logging_data *lq)
 296  295  {
 297  296          int             cleared = 0;
 298  297          char            *host;
 299  298  
 300  299          while (lq) {
 301  300                  struct cln cln;
 302  301  
 303  302                  if (lq->ld_host == NULL) {
 304  303                          DTRACE_PROBE(mountd, name_by_lazy);
 305  304                          cln_init_lazy(&cln, lq->ld_netid, lq->ld_nb);
 306  305                          host = cln_gethost(&cln);
 307  306                  } else
 308  307                          host = lq->ld_host;
 309  308  
 310  309                  audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */
 311  310  
 312  311                  /* add entry to mount list */
 313  312                  if (lq->ld_rpath)
 314  313                          mntlist_new(host, lq->ld_rpath);
 315  314  
 316  315                  if (lq->ld_host == NULL)
 317  316                          cln_fini(&cln);
 318  317  
 319  318                  free_logging_data(lq);
 320  319                  cleared++;
 321  320  
 322  321                  (void) mutex_lock(&logging_queue_lock);
 323  322                  lq = remove_head_of_queue();
 324  323                  (void) mutex_unlock(&logging_queue_lock);
 325  324          }
 326  325  
 327  326          DTRACE_PROBE1(mountd, logging_cleared, cleared);
 328  327  }
 329  328  
 330  329  static void *
 331  330  logging_svc(void *arg)
 332  331  {
 333  332          logging_data    *lq;
 334  333  
 335  334          for (;;) {
 336  335                  (void) mutex_lock(&logging_queue_lock);
 337  336                  while (logging_head == NULL) {
 338  337                          (void) cond_wait(&logging_queue_cv,
 339  338                              &logging_queue_lock);
 340  339                  }
 341  340  
 342  341                  lq = remove_head_of_queue();
 343  342                  (void) mutex_unlock(&logging_queue_lock);
 344  343  
 345  344                  do_logging_queue(lq);
 346  345          }
 347  346  
 348  347          /*NOTREACHED*/
 349  348          syslog(LOG_ERR, gettext("Logging server exited"));
 350  349          return (NULL);
 351  350  }
 352  351  
 353  352  static int
 354  353  convert_int(int *val, char *str)
 355  354  {
 356  355          long lval;
 357  356  
 358  357          if (str == NULL || !isdigit(*str))
 359  358                  return (-1);
 360  359  
 361  360          lval = strtol(str, &str, 10);
 362  361          if (*str != '\0' || lval > INT_MAX)
 363  362                  return (-2);
 364  363  
 365  364          *val = (int)lval;
 366  365          return (0);
 367  366  }
 368  367  
 369  368  /*
 370  369   * This function is called for each configured network type to
 371  370   * bind and register our RPC service programs.
 372  371   *
 373  372   * On TCP or UDP, we may want to bind MOUNTPROG on a specific port
 374  373   * (when mountd_port is specified) in which case we'll use the
 375  374   * variant of svc_tp_create() that lets us pass a bind address.
 376  375   */
 377  376  static void
 378  377  md_svc_tp_create(struct netconfig *nconf)
 379  378  {
 380  379          char port_str[8];
 381  380          struct nd_hostserv hs;
 382  381          struct nd_addrlist *al = NULL;
 383  382          SVCXPRT *xprt = NULL;
 384  383          rpcvers_t vers;
 385  384  
 386  385          vers = mount_vers_max;
 387  386  
 388  387          /*
 389  388           * If mountd_port is set and this is an inet transport,
 390  389           * bind this service on the specified port.  The TLI way
 391  390           * to create such a bind address is netdir_getbyname()
 392  391           * with the special "host" HOST_SELF_BIND.  This builds
 393  392           * an all-zeros IP address with the specified port.
 394  393           */
 395  394          if (mountd_port != 0 &&
 396  395              (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
 397  396              strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
 398  397                  int err;
 399  398  
 400  399                  snprintf(port_str, sizeof (port_str), "%u",
 401  400                      (unsigned short)mountd_port);
 402  401  
 403  402                  hs.h_host = HOST_SELF_BIND;
 404  403                  hs.h_serv = port_str;
 405  404                  err = netdir_getbyname((struct netconfig *)nconf, &hs, &al);
 406  405                  if (err == 0 && al != NULL) {
 407  406                          xprt = svc_tp_create_addr(mnt, MOUNTPROG, vers,
 408  407                              nconf, al->n_addrs);
 409  408                          netdir_free(al, ND_ADDRLIST);
 410  409                  }
 411  410                  if (xprt == NULL) {
 412  411                          syslog(LOG_ERR, "mountd: unable to create "
 413  412                              "(MOUNTD,%d) on transport %s (port %d)",
 414  413                              vers, nconf->nc_netid, mountd_port);
 415  414                  }
 416  415                  /* fall-back to default bind */
 417  416          }
 418  417          if (xprt == NULL) {
 419  418                  /*
 420  419                   * Had mountd_port=0, or non-inet transport,
 421  420                   * or the bind to a specific port failed.
 422  421                   * Do a default bind.
 423  422                   */
 424  423                  xprt = svc_tp_create(mnt, MOUNTPROG, vers, nconf);
 425  424          }
 426  425          if (xprt == NULL) {
 427  426                  syslog(LOG_ERR, "mountd: unable to create "
 428  427                      "(MOUNTD,%d) on transport %s",
 429  428                      vers, nconf->nc_netid);
 430  429                  return;
 431  430          }
 432  431  
 433  432          /*
 434  433           * Register additional versions on this transport.
 435  434           */
 436  435          while (--vers >= mount_vers_min) {
 437  436                  if (!svc_reg(xprt, MOUNTPROG, vers, mnt, nconf)) {
 438  437                          (void) syslog(LOG_ERR, "mountd: "
 439  438                              "failed to register vers %d on %s",
 440  439                              vers, nconf->nc_netid);
 441  440                  }
 442  441          }
 443  442  }
 444  443  
 445  444  int
 446  445  main(int argc, char *argv[])
 447  446  {
 448  447          int     pid;
 449  448          int     c;
 450  449          int     rpc_svc_fdunlim = 1;
 451  450          int     rpc_svc_mode = RPC_SVC_MT_AUTO;
 452  451          int     maxrecsz = RPC_MAXDATASIZE;
 453  452          bool_t  exclbind = TRUE;
 454  453          bool_t  can_do_mlp;
 455  454          long    thr_flags = (THR_NEW_LWP|THR_DAEMON);
 456  455          char defval[4];
 457  456          int defvers, ret, bufsz;
 458  457          struct rlimit rl;
 459  458          int listen_backlog = 0;
 460  459          int max_threads = 0;
 461  460          int tmp;
 462  461          struct netconfig *nconf;
 463  462          NCONF_HANDLE *nc;
 464  463  
 465  464          int     pipe_fd = -1;
 466  465  
 467  466          /*
 468  467           * Mountd requires uid 0 for:
 469  468           *      /etc/rmtab updates (we could chown it to daemon)
 470  469           *      /etc/dfs/dfstab reading (it wants to lock out share which
 471  470           *              doesn't do any locking before first truncate;
 472  471           *              NFS share does; should use fcntl locking instead)
 473  472           *      Needed privileges:
 474  473           *              auditing
 475  474           *              nfs syscall
 476  475           *              file dac search (so it can stat all files)
 477  476           *      Optional privileges:
 478  477           *              MLP
  
    | 
      ↓ open down ↓ | 
    444 lines elided | 
    
      ↑ open up ↑ | 
  
 479  478           */
 480  479          can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
 481  480          if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
 482  481              PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
 483  482              PRIV_NET_PRIVADDR,
 484  483              can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
 485  484                  (void) fprintf(stderr,
 486  485                      "%s: must be run with sufficient privileges\n",
 487  486                      argv[0]);
 488  487                  exit(1);
 489      -        }
 490      -
 491      -        /* Mountd cannot run in a non-global zone. */
 492      -        if (getzoneid() != GLOBAL_ZONEID) {
 493      -                (void) fprintf(stderr, "%s: can only run in the global zone\n",
 494      -                    argv[0]);
 495      -                exit(1);
 496  488          }
 497  489  
 498  490          if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
 499  491                  syslog(LOG_ERR, "getrlimit failed");
 500  492          } else {
 501  493                  rl.rlim_cur = rl.rlim_max;
 502  494                  if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
 503  495                          syslog(LOG_ERR, "setrlimit failed");
 504  496          }
 505  497  
 506  498          (void) enable_extended_FILE_stdio(-1, -1);
 507  499  
 508  500          ret = nfs_smf_get_iprop("mountd_max_threads", &max_threads,
 509  501              DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
 510  502          if (ret != SA_OK) {
 511  503                  syslog(LOG_ERR, "Reading of mountd_max_threads from SMF "
 512  504                      "failed, using default value");
 513  505          }
 514  506  
 515  507          ret = nfs_smf_get_iprop("mountd_port", &mountd_port,
 516  508              DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
 517  509          if (ret != SA_OK) {
 518  510                  syslog(LOG_ERR, "Reading of mountd_port from SMF "
 519  511                      "failed, using default value");
 520  512          }
 521  513  
 522  514          while ((c = getopt(argc, argv, "dvrm:p:")) != EOF) {
 523  515                  switch (c) {
 524  516                  case 'd':
 525  517                          debug++;
 526  518                          break;
 527  519                  case 'v':
 528  520                          verbose++;
 529  521                          break;
 530  522                  case 'r':
 531  523                          rejecting = 1;
 532  524                          break;
 533  525                  case 'm':
 534  526                          if (convert_int(&tmp, optarg) != 0 || tmp < 1) {
 535  527                                  (void) fprintf(stderr, "%s: invalid "
 536  528                                      "max_threads option, using defaults\n",
 537  529                                      argv[0]);
 538  530                                  break;
 539  531                          }
 540  532                          max_threads = tmp;
 541  533                          break;
 542  534                  case 'p':
 543  535                          if (convert_int(&tmp, optarg) != 0 || tmp < 1 ||
 544  536                              tmp > UINT16_MAX) {
 545  537                                  (void) fprintf(stderr, "%s: invalid port "
 546  538                                      "number\n", argv[0]);
 547  539                                  break;
 548  540                          }
 549  541                          mountd_port = tmp;
 550  542                          break;
 551  543                  default:
 552  544                          fprintf(stderr, "usage: mountd [-v] [-r]\n");
 553  545                          exit(1);
 554  546                  }
 555  547          }
 556  548  
 557  549          /*
 558  550           * Read in the NFS version values from config file.
 559  551           */
 560  552          bufsz = 4;
 561  553          ret = nfs_smf_get_prop("server_versmin", defval, DEFAULT_INSTANCE,
 562  554              SCF_TYPE_INTEGER, NFSD, &bufsz);
 563  555          if (ret == SA_OK) {
 564  556                  errno = 0;
 565  557                  defvers = strtol(defval, (char **)NULL, 10);
 566  558                  if (errno == 0) {
 567  559                          mount_vers_min = defvers;
 568  560                          /*
 569  561                           * special because NFSv2 is
 570  562                           * supported by mount v1 & v2
 571  563                           */
 572  564                          if (defvers == NFS_VERSION)
 573  565                                  mount_vers_min = MOUNTVERS;
 574  566                  }
 575  567          }
 576  568  
 577  569          bufsz = 4;
 578  570          ret = nfs_smf_get_prop("server_versmax", defval, DEFAULT_INSTANCE,
 579  571              SCF_TYPE_INTEGER, NFSD, &bufsz);
 580  572          if (ret == SA_OK) {
 581  573                  errno = 0;
 582  574                  defvers = strtol(defval, (char **)NULL, 10);
 583  575                  if (errno == 0) {
 584  576                          mount_vers_max = defvers;
 585  577                  }
 586  578          }
 587  579  
 588  580          ret = nfs_smf_get_iprop("mountd_listen_backlog", &listen_backlog,
 589  581              DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
 590  582          if (ret != SA_OK) {
 591  583                  syslog(LOG_ERR, "Reading of mountd_listen_backlog from SMF "
 592  584                      "failed, using default value");
 593  585          }
 594  586  
 595  587          /*
 596  588           * Sanity check versions,
 597  589           * even though we may get versions > MOUNTVERS3, we still need
 598  590           * to start nfsauth service, so continue on regardless of values.
 599  591           */
 600  592          if (mount_vers_max > MOUNTVERS3)
 601  593                  mount_vers_max = MOUNTVERS3;
 602  594          if (mount_vers_min > mount_vers_max) {
 603  595                  fprintf(stderr, "server_versmin > server_versmax\n");
 604  596                  mount_vers_max = mount_vers_min;
 605  597          }
 606  598          (void) setlocale(LC_ALL, "");
 607  599          (void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
 608  600          (void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
 609  601          (void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
 610  602          (void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
 611  603  
 612  604          netgroup_init();
 613  605  
 614  606  #if !defined(TEXT_DOMAIN)
 615  607  #define TEXT_DOMAIN "SYS_TEST"
 616  608  #endif
 617  609          (void) textdomain(TEXT_DOMAIN);
 618  610  
 619  611          /* Don't drop core if the NFS module isn't loaded. */
 620  612          (void) signal(SIGSYS, SIG_IGN);
 621  613  
 622  614          if (!debug)
 623  615                  pipe_fd = daemonize_init();
 624  616  
 625  617          /*
 626  618           * If we coredump it'll be in /core
 627  619           */
 628  620          if (chdir("/") < 0)
 629  621                  fprintf(stderr, "chdir /: %s\n", strerror(errno));
 630  622  
 631  623          if (!debug)
 632  624                  openlog("mountd", LOG_PID, LOG_DAEMON);
 633  625  
 634  626          /*
 635  627           * establish our lock on the lock file and write our pid to it.
 636  628           * exit if some other process holds the lock, or if there's any
 637  629           * error in writing/locking the file.
 638  630           */
 639  631          pid = _enter_daemon_lock(MOUNTD);
 640  632          switch (pid) {
 641  633          case 0:
 642  634                  break;
 643  635          case -1:
 644  636                  fprintf(stderr, "error locking for %s: %s\n", MOUNTD,
 645  637                      strerror(errno));
 646  638                  exit(2);
 647  639          default:
 648  640                  /* daemon was already running */
 649  641                  exit(0);
 650  642          }
 651  643  
 652  644          audit_mountd_setup();   /* BSM */
 653  645  
 654  646          /*
 655  647           * Get required system variables
 656  648           */
 657  649          if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) == -1) {
 658  650                  syslog(LOG_ERR, "Unable to get _SC_NGROUPS_MAX");
 659  651                  exit(1);
 660  652          }
 661  653          if ((pw_size = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
 662  654                  syslog(LOG_ERR, "Unable to get _SC_GETPW_R_SIZE_MAX");
 663  655                  exit(1);
 664  656          }
 665  657  
 666  658          /*
 667  659           * Set number of file descriptors to unlimited
 668  660           */
 669  661          if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) {
 670  662                  syslog(LOG_INFO, "unable to set number of FDs to unlimited");
 671  663          }
 672  664  
 673  665          /*
 674  666           * Tell RPC that we want automatic thread mode.
 675  667           * A new thread will be spawned for each request.
 676  668           */
 677  669          if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
 678  670                  fprintf(stderr, "unable to set automatic MT mode\n");
 679  671                  exit(1);
 680  672          }
 681  673  
 682  674          /*
 683  675           * Enable non-blocking mode and maximum record size checks for
 684  676           * connection oriented transports.
 685  677           */
 686  678          if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
 687  679                  fprintf(stderr, "unable to set RPC max record size\n");
 688  680          }
 689  681  
 690  682          /*
 691  683           * Prevent our non-priv udp and tcp ports bound w/wildcard addr
 692  684           * from being hijacked by a bind to a more specific addr.
 693  685           */
 694  686          if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
 695  687                  fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND\n");
 696  688          }
 697  689  
 698  690          /*
 699  691           * Set the maximum number of outstanding connection
 700  692           * indications (listen backlog) to the value specified.
 701  693           */
 702  694          if (listen_backlog > 0 && !rpc_control(__RPC_SVC_LSTNBKLOG_SET,
 703  695              &listen_backlog)) {
 704  696                  fprintf(stderr, "unable to set listen backlog\n");
 705  697                  exit(1);
 706  698          }
 707  699  
 708  700          /*
 709  701           * If max_threads was specified, then set the
 710  702           * maximum number of threads to the value specified.
 711  703           */
 712  704          if (max_threads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) {
 713  705                  fprintf(stderr, "unable to set max_threads\n");
 714  706                  exit(1);
 715  707          }
 716  708  
 717  709          if (mountd_port < 0 || mountd_port > UINT16_MAX) {
 718  710                  fprintf(stderr, "unable to use specified port\n");
 719  711                  exit(1);
 720  712          }
 721  713  
 722  714          /*
 723  715           * Make sure to unregister any previous versions in case the
 724  716           * user is reconfiguring the server in interesting ways.
 725  717           */
 726  718          svc_unreg(MOUNTPROG, MOUNTVERS);
 727  719          svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
 728  720          svc_unreg(MOUNTPROG, MOUNTVERS3);
 729  721  
 730  722          /*
 731  723           * Create the nfsauth thread with same signal disposition
 732  724           * as the main thread. We need to create a separate thread
 733  725           * since mountd() will be both an RPC server (for remote
 734  726           * traffic) _and_ a doors server (for kernel upcalls).
 735  727           */
 736  728          if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
 737  729                  fprintf(stderr,
 738  730                      gettext("Failed to create NFSAUTH svc thread\n"));
 739  731                  exit(2);
 740  732          }
 741  733  
 742  734          /*
 743  735           * Create the cmd service thread with same signal disposition
 744  736           * as the main thread. We need to create a separate thread
 745  737           * since mountd() will be both an RPC server (for remote
 746  738           * traffic) _and_ a doors server (for kernel upcalls).
 747  739           */
 748  740          if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
 749  741                  syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
 750  742                  exit(2);
 751  743          }
 752  744  
 753  745          /*
 754  746           * Create an additional thread to service the rmtab and
 755  747           * audit_mountd_mount logging for mount requests. Use the same
 756  748           * signal disposition as the main thread. We create
 757  749           * a separate thread to allow the mount request threads to
 758  750           * clear as soon as possible.
 759  751           */
 760  752          if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
 761  753                  syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
 762  754                  exit(2);
 763  755          }
 764  756  
 765  757          /*
 766  758           * Enumerate network transports and create service listeners
 767  759           * as appropriate for each.
 768  760           */
 769  761          if ((nc = setnetconfig()) == NULL) {
 770  762                  syslog(LOG_ERR, "setnetconfig failed: %m");
 771  763                  return (-1);
 772  764          }
 773  765          while ((nconf = getnetconfig(nc)) != NULL) {
 774  766                  /*
 775  767                   * Skip things like tpi_raw, invisible...
 776  768                   */
 777  769                  if ((nconf->nc_flag & NC_VISIBLE) == 0)
 778  770                          continue;
 779  771                  if (nconf->nc_semantics != NC_TPI_CLTS &&
 780  772                      nconf->nc_semantics != NC_TPI_COTS &&
 781  773                      nconf->nc_semantics != NC_TPI_COTS_ORD)
 782  774                          continue;
 783  775  
 784  776                  md_svc_tp_create(nconf);
 785  777          }
 786  778          (void) endnetconfig(nc);
 787  779  
 788  780          /*
 789  781           * Start serving
 790  782           */
 791  783          rmtab_load();
 792  784  
 793  785          daemonize_fini(pipe_fd);
 794  786  
 795  787          /* Get rid of the most dangerous basic privileges. */
 796  788          __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
 797  789              (char *)NULL);
 798  790  
 799  791          svc_run();
 800  792          syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
 801  793          abort();
 802  794  
 803  795          /* NOTREACHED */
 804  796          return (0);
 805  797  }
 806  798  
 807  799  /*
 808  800   * Server procedure switch routine
 809  801   */
 810  802  void
 811  803  mnt(struct svc_req *rqstp, SVCXPRT *transp)
 812  804  {
 813  805          switch (rqstp->rq_proc) {
 814  806          case NULLPROC:
 815  807                  errno = 0;
 816  808                  if (!svc_sendreply(transp, xdr_void, (char *)0))
 817  809                          log_cant_reply(transp);
 818  810                  return;
 819  811  
 820  812          case MOUNTPROC_MNT:
 821  813                  (void) mount(rqstp);
 822  814                  return;
 823  815  
 824  816          case MOUNTPROC_DUMP:
 825  817                  mntlist_send(transp);
 826  818                  return;
 827  819  
 828  820          case MOUNTPROC_UMNT:
 829  821                  umount(rqstp);
 830  822                  return;
 831  823  
 832  824          case MOUNTPROC_UMNTALL:
 833  825                  umountall(rqstp);
 834  826                  return;
 835  827  
 836  828          case MOUNTPROC_EXPORT:
 837  829          case MOUNTPROC_EXPORTALL:
 838  830                  export(rqstp);
 839  831                  return;
 840  832  
 841  833          case MOUNTPROC_PATHCONF:
 842  834                  if (rqstp->rq_vers == MOUNTVERS_POSIX)
 843  835                          mnt_pathconf(rqstp);
 844  836                  else
 845  837                          svcerr_noproc(transp);
 846  838                  return;
 847  839  
 848  840          default:
 849  841                  svcerr_noproc(transp);
 850  842                  return;
 851  843          }
 852  844  }
 853  845  
 854  846  void
 855  847  log_cant_reply_cln(struct cln *cln)
 856  848  {
 857  849          int saverrno;
 858  850          char *host;
 859  851  
 860  852          saverrno = errno;       /* save error code */
 861  853  
 862  854          host = cln_gethost(cln);
 863  855          if (host == NULL)
 864  856                  return;
 865  857  
 866  858          errno = saverrno;
 867  859          if (errno == 0)
 868  860                  syslog(LOG_ERR, "couldn't send reply to %s", host);
 869  861          else
 870  862                  syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
 871  863  }
 872  864  
 873  865  void
 874  866  log_cant_reply(SVCXPRT *transp)
 875  867  {
 876  868          int saverrno;
 877  869          struct cln cln;
 878  870  
 879  871          saverrno = errno;       /* save error code */
 880  872          cln_init(&cln, transp);
 881  873          errno = saverrno;
 882  874  
 883  875          log_cant_reply_cln(&cln);
 884  876  
 885  877          cln_fini(&cln);
 886  878  }
 887  879  
 888  880  /*
 889  881   * Answer pathconf questions for the mount point fs
 890  882   */
 891  883  static void
 892  884  mnt_pathconf(struct svc_req *rqstp)
 893  885  {
 894  886          SVCXPRT *transp;
 895  887          struct pathcnf p;
 896  888          char *path, rpath[MAXPATHLEN];
 897  889          struct stat st;
 898  890  
 899  891          transp = rqstp->rq_xprt;
 900  892          path = NULL;
 901  893          (void) memset((caddr_t)&p, 0, sizeof (p));
 902  894  
 903  895          if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
 904  896                  svcerr_decode(transp);
 905  897                  return;
 906  898          }
 907  899          if (lstat(path, &st) < 0) {
 908  900                  _PC_SET(_PC_ERROR, p.pc_mask);
 909  901                  goto done;
 910  902          }
 911  903          /*
 912  904           * Get a path without symbolic links.
 913  905           */
 914  906          if (realpath(path, rpath) == NULL) {
 915  907                  syslog(LOG_DEBUG,
 916  908                      "mount request: realpath failed on %s: %m",
 917  909                      path);
 918  910                  _PC_SET(_PC_ERROR, p.pc_mask);
 919  911                  goto done;
 920  912          }
 921  913          (void) memset((caddr_t)&p, 0, sizeof (p));
 922  914          /*
 923  915           * can't ask about devices over NFS
 924  916           */
 925  917          _PC_SET(_PC_MAX_CANON, p.pc_mask);
 926  918          _PC_SET(_PC_MAX_INPUT, p.pc_mask);
 927  919          _PC_SET(_PC_PIPE_BUF, p.pc_mask);
 928  920          _PC_SET(_PC_VDISABLE, p.pc_mask);
 929  921  
 930  922          errno = 0;
 931  923          p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
 932  924          if (errno)
 933  925                  _PC_SET(_PC_LINK_MAX, p.pc_mask);
 934  926          p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
 935  927          if (errno)
 936  928                  _PC_SET(_PC_NAME_MAX, p.pc_mask);
 937  929          p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
 938  930          if (errno)
 939  931                  _PC_SET(_PC_PATH_MAX, p.pc_mask);
 940  932          if (pathconf(rpath, _PC_NO_TRUNC) == 1)
 941  933                  _PC_SET(_PC_NO_TRUNC, p.pc_mask);
 942  934          if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
 943  935                  _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
 944  936  
 945  937  done:
 946  938          errno = 0;
 947  939          if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
 948  940                  log_cant_reply(transp);
 949  941          if (path != NULL)
 950  942                  svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
 951  943  }
 952  944  
 953  945  /*
 954  946   * If the rootmount (export) option is specified, the all mount requests for
 955  947   * subdirectories return EACCES.
 956  948   */
 957  949  static int
 958  950  checkrootmount(share_t *sh, char *rpath)
 959  951  {
 960  952          char *val;
 961  953  
 962  954          if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
 963  955                  free(val);
 964  956                  if (strcmp(sh->sh_path, rpath) != 0)
 965  957                          return (0);
 966  958                  else
 967  959                          return (1);
 968  960          } else
 969  961                  return (1);
 970  962  }
 971  963  
 972  964  #define MAX_FLAVORS     128
 973  965  
 974  966  /*
 975  967   * Return only EACCES if client does not have access
 976  968   *  to this directory.
 977  969   * "If the server exports only /a/b, an attempt to
 978  970   *  mount a/b/c will fail with ENOENT if the directory
 979  971   *  does not exist"... However, if the client
 980  972   *  does not have access to /a/b, an attacker can
 981  973   *  determine whether the directory exists.
 982  974   * This routine checks either existence of the file or
 983  975   * existence of the file name entry in the mount table.
 984  976   * If the file exists and there is no file name entry,
 985  977   * the error returned should be EACCES.
 986  978   * If the file does not exist, it must be determined
 987  979   * whether the client has access to a parent
 988  980   * directory.  If the client has access to a parent
 989  981   * directory, the error returned should be ENOENT,
 990  982   * otherwise EACCES.
 991  983   */
 992  984  static int
 993  985  mount_enoent_error(struct cln *cln, char *path, char *rpath, int *flavor_list)
 994  986  {
 995  987          char *checkpath, *dp;
 996  988          share_t *sh = NULL;
 997  989          int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
 998  990          int flavor_count;
 999  991  
1000  992          checkpath = strdup(path);
1001  993          if (checkpath == NULL) {
1002  994                  syslog(LOG_ERR, "mount_enoent: no memory");
1003  995                  return (EACCES);
1004  996          }
1005  997  
1006  998          /* CONSTCOND */
1007  999          while (1) {
1008 1000                  if (sh) {
1009 1001                          sharefree(sh);
1010 1002                          sh = NULL;
1011 1003                  }
1012 1004  
1013 1005                  if ((sh = findentry(rpath)) == NULL &&
1014 1006                      (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1015 1007                          /*
1016 1008                           * There is no file name entry.
1017 1009                           * If the file (with symbolic links resolved) exists,
1018 1010                           * the error returned should be EACCES.
1019 1011                           */
1020 1012                          if (realpath_error == 0)
1021 1013                                  break;
1022 1014                  } else if (checkrootmount(sh, rpath) == 0) {
1023 1015                          /*
1024 1016                           * This is a "nosub" only export, in which case,
1025 1017                           * mounting subdirectories isn't allowed.
1026 1018                           * If the file (with symbolic links resolved) exists,
1027 1019                           * the error returned should be EACCES.
1028 1020                           */
1029 1021                          if (realpath_error == 0)
1030 1022                                  break;
1031 1023                  } else {
1032 1024                          /*
1033 1025                           * Check permissions in mount table.
1034 1026                           */
1035 1027                          if (newopts(sh->sh_opts))
1036 1028                                  flavor_count = getclientsflavors_new(sh, cln,
1037 1029                                      flavor_list);
1038 1030                          else
1039 1031                                  flavor_count = getclientsflavors_old(sh, cln,
1040 1032                                      flavor_list);
1041 1033                          if (flavor_count != 0) {
1042 1034                                  /*
1043 1035                                   * Found entry in table and
1044 1036                                   * client has correct permissions.
1045 1037                                   */
1046 1038                                  reply_error = ENOENT;
1047 1039                                  break;
1048 1040                          }
1049 1041                  }
1050 1042  
1051 1043                  /*
1052 1044                   * Check all parent directories.
1053 1045                   */
1054 1046                  dp = strrchr(checkpath, '/');
1055 1047                  if (dp == NULL)
1056 1048                          break;
1057 1049                  *dp = '\0';
1058 1050                  if (strlen(checkpath) == 0)
1059 1051                          break;
1060 1052                  /*
1061 1053                   * Get the real path (no symbolic links in it)
1062 1054                   */
1063 1055                  if (realpath(checkpath, rpath) == NULL) {
1064 1056                          if (errno != ENOENT)
1065 1057                                  break;
1066 1058                  } else {
1067 1059                          realpath_error = 0;
1068 1060                  }
1069 1061          }
1070 1062  
1071 1063          if (sh)
1072 1064                  sharefree(sh);
1073 1065          free(checkpath);
1074 1066          return (reply_error);
1075 1067  }
1076 1068  
1077 1069  /*
1078 1070   * We need to inform the caller whether or not we were
1079 1071   * able to add a node to the queue. If we are not, then
1080 1072   * it is up to the caller to go ahead and log the data.
1081 1073   */
1082 1074  static int
1083 1075  enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
1084 1076      char *rpath, int status, int error)
1085 1077  {
1086 1078          logging_data    *lq;
1087 1079          struct netbuf   *nb;
1088 1080  
1089 1081          lq = (logging_data *)calloc(1, sizeof (logging_data));
1090 1082          if (lq == NULL)
1091 1083                  goto cleanup;
1092 1084  
1093 1085          /*
1094 1086           * We might not yet have the host...
1095 1087           */
1096 1088          if (host) {
1097 1089                  DTRACE_PROBE1(mountd, log_host, host);
1098 1090                  lq->ld_host = strdup(host);
1099 1091                  if (lq->ld_host == NULL)
1100 1092                          goto cleanup;
1101 1093          } else {
1102 1094                  DTRACE_PROBE(mountd, log_no_host);
1103 1095  
1104 1096                  lq->ld_netid = strdup(transp->xp_netid);
1105 1097                  if (lq->ld_netid == NULL)
1106 1098                          goto cleanup;
1107 1099  
1108 1100                  lq->ld_nb = calloc(1, sizeof (struct netbuf));
1109 1101                  if (lq->ld_nb == NULL)
1110 1102                          goto cleanup;
1111 1103  
1112 1104                  nb = svc_getrpccaller(transp);
1113 1105                  if (nb == NULL) {
1114 1106                          DTRACE_PROBE(mountd, e__nb__enqueue);
1115 1107                          goto cleanup;
1116 1108                  }
1117 1109  
1118 1110                  DTRACE_PROBE(mountd, nb_set_enqueue);
1119 1111  
1120 1112                  lq->ld_nb->maxlen = nb->maxlen;
1121 1113                  lq->ld_nb->len = nb->len;
1122 1114  
1123 1115                  lq->ld_nb->buf = malloc(lq->ld_nb->len);
1124 1116                  if (lq->ld_nb->buf == NULL)
1125 1117                          goto cleanup;
1126 1118  
1127 1119                  bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
1128 1120          }
1129 1121  
1130 1122          lq->ld_path = strdup(path);
1131 1123          if (lq->ld_path == NULL)
1132 1124                  goto cleanup;
1133 1125  
1134 1126          if (!error) {
1135 1127                  lq->ld_rpath = strdup(rpath);
1136 1128                  if (lq->ld_rpath == NULL)
1137 1129                          goto cleanup;
1138 1130          }
1139 1131  
1140 1132          lq->ld_status = status;
1141 1133  
1142 1134          /*
1143 1135           * Add to the tail of the logging queue.
1144 1136           */
1145 1137          (void) mutex_lock(&logging_queue_lock);
1146 1138          if (logging_tail == NULL) {
1147 1139                  logging_tail = logging_head = lq;
1148 1140          } else {
1149 1141                  logging_tail->ld_next = lq;
1150 1142                  logging_tail = lq;
1151 1143          }
1152 1144          (void) cond_signal(&logging_queue_cv);
1153 1145          (void) mutex_unlock(&logging_queue_lock);
1154 1146  
1155 1147          return (TRUE);
1156 1148  
1157 1149  cleanup:
1158 1150  
1159 1151          free_logging_data(lq);
1160 1152  
1161 1153          return (FALSE);
1162 1154  }
1163 1155  
1164 1156  
1165 1157  #define CLN_CLNAMES     (1 << 0)
1166 1158  #define CLN_HOST        (1 << 1)
1167 1159  
1168 1160  static void
1169 1161  cln_init_common(struct cln *cln, SVCXPRT *transp, char *netid,
1170 1162      struct netbuf *nbuf)
1171 1163  {
1172 1164          if ((cln->transp = transp) != NULL) {
1173 1165                  assert(netid == NULL && nbuf == NULL);
1174 1166                  cln->netid = transp->xp_netid;
1175 1167                  cln->nbuf = svc_getrpccaller(transp);
1176 1168          } else {
1177 1169                  cln->netid = netid;
1178 1170                  cln->nbuf = nbuf;
1179 1171          }
1180 1172  
1181 1173          cln->nconf = NULL;
1182 1174          cln->clnames = NULL;
1183 1175          cln->host = NULL;
1184 1176  
1185 1177          cln->flags = 0;
1186 1178  }
1187 1179  
1188 1180  void
1189 1181  cln_init(struct cln *cln, SVCXPRT *transp)
1190 1182  {
1191 1183          cln_init_common(cln, transp, NULL, NULL);
1192 1184  }
1193 1185  
1194 1186  void
1195 1187  cln_init_lazy(struct cln *cln, char *netid, struct netbuf *nbuf)
1196 1188  {
1197 1189          cln_init_common(cln, NULL, netid, nbuf);
1198 1190  }
1199 1191  
1200 1192  void
1201 1193  cln_fini(struct cln *cln)
1202 1194  {
1203 1195          if (cln->nconf != NULL)
1204 1196                  freenetconfigent(cln->nconf);
1205 1197  
1206 1198          if (cln->clnames != NULL)
1207 1199                  netdir_free(cln->clnames, ND_HOSTSERVLIST);
1208 1200  
1209 1201          free(cln->host);
1210 1202  }
1211 1203  
1212 1204  struct netbuf *
1213 1205  cln_getnbuf(struct cln *cln)
1214 1206  {
1215 1207          return (cln->nbuf);
1216 1208  }
1217 1209  
1218 1210  struct nd_hostservlist *
1219 1211  cln_getclientsnames(struct cln *cln)
1220 1212  {
1221 1213          if ((cln->flags & CLN_CLNAMES) == 0) {
1222 1214                  /*
1223 1215                   * nconf is not needed if we do not have nbuf (see
1224 1216                   * cln_gethost() too), so we check for nbuf and in a case it is
1225 1217                   * NULL we do not try to get nconf.
1226 1218                   */
1227 1219                  if (cln->netid != NULL && cln->nbuf != NULL) {
1228 1220                          cln->nconf = getnetconfigent(cln->netid);
1229 1221                          if (cln->nconf == NULL)
1230 1222                                  syslog(LOG_ERR, "%s: getnetconfigent failed",
1231 1223                                      cln->netid);
1232 1224                  }
1233 1225  
1234 1226                  if (cln->nconf != NULL && cln->nbuf != NULL)
1235 1227                          (void) __netdir_getbyaddr_nosrv(cln->nconf,
1236 1228                              &cln->clnames, cln->nbuf);
1237 1229  
1238 1230                  cln->flags |= CLN_CLNAMES;
1239 1231          }
1240 1232  
1241 1233          return (cln->clnames);
1242 1234  }
1243 1235  
1244 1236  /*
1245 1237   * Return B_TRUE if the host is already available at no cost
1246 1238   */
1247 1239  boolean_t
1248 1240  cln_havehost(struct cln *cln)
1249 1241  {
1250 1242          return ((cln->flags & (CLN_CLNAMES | CLN_HOST)) != 0);
1251 1243  }
1252 1244  
1253 1245  char *
1254 1246  cln_gethost(struct cln *cln)
1255 1247  {
1256 1248          if (cln_getclientsnames(cln) != NULL)
1257 1249                  return (cln->clnames->h_hostservs[0].h_host);
1258 1250  
1259 1251          if ((cln->flags & CLN_HOST) == 0) {
1260 1252                  if (cln->nconf == NULL || cln->nbuf == NULL) {
1261 1253                          cln->host = strdup("(anon)");
1262 1254                  } else {
1263 1255                          char host[MAXIPADDRLEN];
1264 1256  
1265 1257                          if (strcmp(cln->nconf->nc_protofmly, NC_INET) == 0) {
1266 1258                                  struct sockaddr_in *sa;
1267 1259  
1268 1260                                  /* LINTED pointer alignment */
1269 1261                                  sa = (struct sockaddr_in *)(cln->nbuf->buf);
1270 1262                                  (void) inet_ntoa_r(sa->sin_addr, host);
1271 1263  
1272 1264                                  cln->host = strdup(host);
1273 1265                          } else if (strcmp(cln->nconf->nc_protofmly,
1274 1266                              NC_INET6) == 0) {
1275 1267                                  struct sockaddr_in6 *sa;
1276 1268  
1277 1269                                  /* LINTED pointer alignment */
1278 1270                                  sa = (struct sockaddr_in6 *)(cln->nbuf->buf);
1279 1271                                  (void) inet_ntop(AF_INET6,
1280 1272                                      sa->sin6_addr.s6_addr,
1281 1273                                      host, INET6_ADDRSTRLEN);
1282 1274  
1283 1275                                  cln->host = strdup(host);
1284 1276                          } else {
1285 1277                                  syslog(LOG_ERR, gettext("Client's address is "
1286 1278                                      "neither IPv4 nor IPv6"));
1287 1279  
1288 1280                                  cln->host = strdup("(anon)");
1289 1281                          }
1290 1282                  }
1291 1283  
1292 1284                  cln->flags |= CLN_HOST;
1293 1285          }
1294 1286  
1295 1287          return (cln->host);
1296 1288  }
1297 1289  
1298 1290  /*
1299 1291   * Check mount requests, add to mounted list if ok
1300 1292   */
1301 1293  static int
1302 1294  mount(struct svc_req *rqstp)
1303 1295  {
1304 1296          SVCXPRT *transp;
1305 1297          int version, vers;
1306 1298          struct fhstatus fhs;
1307 1299          struct mountres3 mountres3;
1308 1300          char fh[FHSIZE3];
1309 1301          int len = FHSIZE3;
1310 1302          char *path, rpath[MAXPATHLEN];
1311 1303          share_t *sh = NULL;
1312 1304          struct cln cln;
1313 1305          char *host = NULL;
1314 1306          int error = 0, lofs_tried = 0, enqueued;
1315 1307          int flavor_list[MAX_FLAVORS];
1316 1308          int flavor_count;
1317 1309          ucred_t *uc = NULL;
1318 1310  
1319 1311          int audit_status;
1320 1312  
1321 1313          transp = rqstp->rq_xprt;
1322 1314          version = rqstp->rq_vers;
1323 1315          path = NULL;
1324 1316  
1325 1317          if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
1326 1318                  svcerr_decode(transp);
1327 1319                  return (EACCES);
1328 1320          }
1329 1321  
1330 1322          cln_init(&cln, transp);
1331 1323  
1332 1324          /*
1333 1325           * Put off getting the name for the client until we
1334 1326           * need it. This is a performance gain. If we are logging,
1335 1327           * then we don't care about performance and might as well
1336 1328           * get the host name now in case we need to spit out an
1337 1329           * error message.
1338 1330           */
1339 1331          if (verbose) {
1340 1332                  DTRACE_PROBE(mountd, name_by_verbose);
1341 1333                  if ((host = cln_gethost(&cln)) == NULL) {
1342 1334                          /*
1343 1335                           * We failed to get a name for the client, even
1344 1336                           * 'anon', probably because we ran out of memory.
1345 1337                           * In this situation it doesn't make sense to
1346 1338                           * allow the mount to succeed.
1347 1339                           */
1348 1340                          error = EACCES;
1349 1341                          goto reply;
1350 1342                  }
1351 1343          }
1352 1344  
1353 1345          /*
1354 1346           * If the version being used is less than the minimum version,
1355 1347           * the filehandle translation should not be provided to the
1356 1348           * client.
1357 1349           */
1358 1350          if (rejecting || version < mount_vers_min) {
1359 1351                  if (verbose)
1360 1352                          syslog(LOG_NOTICE, "Rejected mount: %s for %s",
1361 1353                              host, path);
1362 1354                  error = EACCES;
1363 1355                  goto reply;
1364 1356          }
1365 1357  
1366 1358          /*
1367 1359           * Trusted Extension doesn't support nfsv2. nfsv2 client
1368 1360           * uses MOUNT protocol v1 and v2. To prevent circumventing
1369 1361           * TX label policy via using nfsv2 client, reject a mount
1370 1362           * request with version less than 3 and log an error.
1371 1363           */
1372 1364          if (is_system_labeled()) {
1373 1365                  if (version < 3) {
1374 1366                          if (verbose)
1375 1367                                  syslog(LOG_ERR,
1376 1368                                      "Rejected mount: TX doesn't support NFSv2");
1377 1369                          error = EACCES;
1378 1370                          goto reply;
1379 1371                  }
1380 1372          }
1381 1373  
1382 1374          /*
1383 1375           * Get the real path (no symbolic links in it)
1384 1376           */
1385 1377          if (realpath(path, rpath) == NULL) {
1386 1378                  error = errno;
1387 1379                  if (verbose)
1388 1380                          syslog(LOG_ERR,
1389 1381                              "mount request: realpath: %s: %m", path);
1390 1382                  if (error == ENOENT)
1391 1383                          error = mount_enoent_error(&cln, path, rpath,
1392 1384                              flavor_list);
1393 1385                  goto reply;
1394 1386          }
1395 1387  
1396 1388          if ((sh = findentry(rpath)) == NULL &&
1397 1389              (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1398 1390                  error = EACCES;
1399 1391                  goto reply;
1400 1392          }
1401 1393  
1402 1394          /*
1403 1395           * Check if this is a "nosub" only export, in which case, mounting
1404 1396           * subdirectories isn't allowed. Bug 1184573.
1405 1397           */
1406 1398          if (checkrootmount(sh, rpath) == 0) {
1407 1399                  error = EACCES;
1408 1400                  goto reply;
1409 1401          }
1410 1402  
1411 1403          if (newopts(sh->sh_opts))
1412 1404                  flavor_count = getclientsflavors_new(sh, &cln, flavor_list);
1413 1405          else
1414 1406                  flavor_count = getclientsflavors_old(sh, &cln, flavor_list);
1415 1407  
1416 1408          if (flavor_count == 0) {
1417 1409                  error = EACCES;
1418 1410                  goto reply;
1419 1411          }
1420 1412  
1421 1413          /*
1422 1414           * Check MAC policy here. The server side policy should be
1423 1415           * consistent with client side mount policy, i.e.
1424 1416           * - we disallow an admin_low unlabeled client to mount
1425 1417           * - we disallow mount from a lower labeled client.
1426 1418           */
1427 1419          if (is_system_labeled()) {
1428 1420                  m_label_t *clabel = NULL;
1429 1421                  m_label_t *slabel = NULL;
1430 1422                  m_label_t admin_low;
1431 1423  
1432 1424                  if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
1433 1425                          syslog(LOG_ERR,
1434 1426                              "mount request: Failed to get caller's ucred : %m");
1435 1427                          error = EACCES;
1436 1428                          goto reply;
1437 1429                  }
1438 1430                  if ((clabel = ucred_getlabel(uc)) == NULL) {
1439 1431                          syslog(LOG_ERR,
1440 1432                              "mount request: can't get client label from ucred");
1441 1433                          error = EACCES;
1442 1434                          goto reply;
1443 1435                  }
1444 1436  
1445 1437                  bsllow(&admin_low);
1446 1438                  if (blequal(&admin_low, clabel)) {
1447 1439                          struct sockaddr *ca;
1448 1440                          tsol_tpent_t    *tp;
1449 1441  
1450 1442                          ca = (struct sockaddr *)(void *)svc_getrpccaller(
1451 1443                              rqstp->rq_xprt)->buf;
1452 1444                          if (ca == NULL) {
1453 1445                                  error = EACCES;
1454 1446                                  goto reply;
1455 1447                          }
1456 1448                          /*
1457 1449                           * get trusted network template associated
1458 1450                           * with the client.
1459 1451                           */
1460 1452                          tp = get_client_template(ca);
1461 1453                          if (tp == NULL || tp->host_type != SUN_CIPSO) {
1462 1454                                  if (tp != NULL)
1463 1455                                          tsol_freetpent(tp);
1464 1456                                  error = EACCES;
1465 1457                                  goto reply;
1466 1458                          }
1467 1459                          tsol_freetpent(tp);
1468 1460                  } else {
1469 1461                          if ((slabel = m_label_alloc(MAC_LABEL)) == NULL) {
1470 1462                                  error = EACCES;
1471 1463                                  goto reply;
1472 1464                          }
1473 1465  
1474 1466                          if (getlabel(rpath, slabel) != 0) {
1475 1467                                  m_label_free(slabel);
1476 1468                                  error = EACCES;
1477 1469                                  goto reply;
1478 1470                          }
1479 1471  
1480 1472                          if (!bldominates(clabel, slabel)) {
1481 1473                                  m_label_free(slabel);
1482 1474                                  error = EACCES;
1483 1475                                  goto reply;
1484 1476                          }
1485 1477                          m_label_free(slabel);
1486 1478                  }
1487 1479          }
1488 1480  
1489 1481          /*
1490 1482           * Now get the filehandle.
1491 1483           *
1492 1484           * NFS V2 clients get a 32 byte filehandle.
1493 1485           * NFS V3 clients get a 32 or 64 byte filehandle, depending on
1494 1486           * the embedded FIDs.
1495 1487           */
1496 1488          vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
1497 1489  
1498 1490          /* LINTED pointer alignment */
1499 1491          while (nfs_getfh(rpath, vers, &len, fh) < 0) {
1500 1492                  if (errno == EINVAL &&
1501 1493                      (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
1502 1494                          errno = 0;
1503 1495                          continue;
1504 1496                  }
1505 1497                  error = errno == EINVAL ? EACCES : errno;
1506 1498                  syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
1507 1499                      path);
1508 1500                  break;
1509 1501          }
1510 1502  
1511 1503          if (version == MOUNTVERS3) {
1512 1504                  mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
1513 1505                  mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
1514 1506          } else {
1515 1507                  bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
1516 1508          }
1517 1509  
1518 1510  reply:
1519 1511          if (uc != NULL)
1520 1512                  ucred_free(uc);
1521 1513  
1522 1514          switch (version) {
1523 1515          case MOUNTVERS:
1524 1516          case MOUNTVERS_POSIX:
1525 1517                  if (error == EINVAL)
1526 1518                          fhs.fhs_status = NFSERR_ACCES;
1527 1519                  else if (error == EREMOTE)
1528 1520                          fhs.fhs_status = NFSERR_REMOTE;
1529 1521                  else
1530 1522                          fhs.fhs_status = error;
1531 1523  
1532 1524                  if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
1533 1525                          log_cant_reply_cln(&cln);
1534 1526  
1535 1527                  audit_status = fhs.fhs_status;
1536 1528                  break;
1537 1529  
1538 1530          case MOUNTVERS3:
1539 1531                  if (!error) {
1540 1532                  mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
1541 1533                      flavor_list;
1542 1534                  mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
1543 1535                      flavor_count;
1544 1536  
1545 1537                  } else if (error == ENAMETOOLONG)
1546 1538                          error = MNT3ERR_NAMETOOLONG;
1547 1539  
1548 1540                  mountres3.fhs_status = error;
1549 1541                  if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
1550 1542                          log_cant_reply_cln(&cln);
1551 1543  
1552 1544                  audit_status = mountres3.fhs_status;
1553 1545                  break;
1554 1546          }
1555 1547  
1556 1548          if (cln_havehost(&cln))
1557 1549                  host = cln_gethost(&cln);
1558 1550  
1559 1551          if (verbose)
1560 1552                  syslog(LOG_NOTICE, "MOUNT: %s %s %s",
1561 1553                      (host == NULL) ? "unknown host" : host,
1562 1554                      error ? "denied" : "mounted", path);
1563 1555  
1564 1556          /*
1565 1557           * If we can not create a queue entry, go ahead and do it
1566 1558           * in the context of this thread.
1567 1559           */
1568 1560          enqueued = enqueue_logging_data(host, transp, path, rpath,
1569 1561              audit_status, error);
1570 1562          if (enqueued == FALSE) {
1571 1563                  if (host == NULL) {
1572 1564                          DTRACE_PROBE(mountd, name_by_in_thread);
1573 1565                          host = cln_gethost(&cln);
1574 1566                  }
1575 1567  
1576 1568                  DTRACE_PROBE(mountd, logged_in_thread);
1577 1569                  audit_mountd_mount(host, path, audit_status); /* BSM */
1578 1570                  if (!error)
1579 1571                          mntlist_new(host, rpath); /* add entry to mount list */
1580 1572          }
1581 1573  
1582 1574          if (path != NULL)
1583 1575                  svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
1584 1576  
1585 1577          if (sh)
1586 1578                  sharefree(sh);
1587 1579  
1588 1580          cln_fini(&cln);
1589 1581  
1590 1582          return (error);
1591 1583  }
1592 1584  
1593 1585  /*
1594 1586   * Determine whether two paths are within the same file system.
1595 1587   * Returns nonzero (true) if paths are the same, zero (false) if
1596 1588   * they are different.  If an error occurs, return false.
1597 1589   *
1598 1590   * Use the actual FSID if it's available (via getattrat()); otherwise,
1599 1591   * fall back on st_dev.
1600 1592   *
1601 1593   * With ZFS snapshots, st_dev differs from the regular file system
1602 1594   * versus the snapshot.  But the fsid is the same throughout.  Thus
1603 1595   * the fsid is a better test.
1604 1596   */
1605 1597  static int
1606 1598  same_file_system(const char *path1, const char *path2)
1607 1599  {
1608 1600          uint64_t fsid1, fsid2;
1609 1601          struct stat64 st1, st2;
1610 1602          nvlist_t *nvl1 = NULL;
1611 1603          nvlist_t *nvl2 = NULL;
1612 1604  
1613 1605          if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
1614 1606              (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
1615 1607              (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
1616 1608              (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
1617 1609                  nvlist_free(nvl1);
1618 1610                  nvlist_free(nvl2);
1619 1611                  /*
1620 1612                   * We have found fsid's for both paths.
1621 1613                   */
1622 1614  
1623 1615                  if (fsid1 == fsid2)
1624 1616                          return (B_TRUE);
1625 1617  
1626 1618                  return (B_FALSE);
1627 1619          }
1628 1620  
1629 1621          nvlist_free(nvl1);
1630 1622          nvlist_free(nvl2);
1631 1623  
1632 1624          /*
1633 1625           * We were unable to find fsid's for at least one of the paths.
1634 1626           * fall back on st_dev.
1635 1627           */
1636 1628  
1637 1629          if (stat64(path1, &st1) < 0) {
1638 1630                  syslog(LOG_NOTICE, "%s: %m", path1);
1639 1631                  return (B_FALSE);
1640 1632          }
1641 1633          if (stat64(path2, &st2) < 0) {
1642 1634                  syslog(LOG_NOTICE, "%s: %m", path2);
1643 1635                  return (B_FALSE);
1644 1636          }
1645 1637  
1646 1638          if (st1.st_dev == st2.st_dev)
1647 1639                  return (B_TRUE);
1648 1640  
1649 1641          return (B_FALSE);
1650 1642  }
1651 1643  
1652 1644  share_t *
1653 1645  findentry(char *path)
1654 1646  {
1655 1647          share_t *sh = NULL;
1656 1648          struct sh_list *shp;
1657 1649          char *p1, *p2;
1658 1650  
1659 1651          check_sharetab();
1660 1652  
1661 1653          (void) rw_rdlock(&sharetab_lock);
1662 1654  
1663 1655          for (shp = share_list; shp; shp = shp->shl_next) {
1664 1656                  sh = shp->shl_sh;
1665 1657                  for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
1666 1658                          if (*p1 == '\0')
1667 1659                                  goto done;      /* exact match */
1668 1660  
1669 1661                  /*
1670 1662                   * Now compare the pathnames for three cases:
1671 1663                   *
1672 1664                   * Parent: /export/foo          (no trailing slash on parent)
1673 1665                   * Child:  /export/foo/bar
1674 1666                   *
1675 1667                   * Parent: /export/foo/         (trailing slash on parent)
1676 1668                   * Child:  /export/foo/bar
1677 1669                   *
1678 1670                   * Parent: /export/foo/         (no trailing slash on child)
1679 1671                   * Child:  /export/foo
1680 1672                   */
1681 1673                  if ((*p1 == '\0' && *p2 == '/') ||
1682 1674                      (*p1 == '\0' && *(p1-1) == '/') ||
1683 1675                      (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
1684 1676                          /*
1685 1677                           * We have a subdirectory.  Test whether the
1686 1678                           * subdirectory is in the same file system.
1687 1679                           */
1688 1680                          if (same_file_system(path, sh->sh_path))
1689 1681                                  goto done;
1690 1682                  }
1691 1683          }
1692 1684  done:
1693 1685          sh = shp ? sharedup(sh) : NULL;
1694 1686  
1695 1687          (void) rw_unlock(&sharetab_lock);
1696 1688  
1697 1689          return (sh);
1698 1690  }
1699 1691  
1700 1692  
1701 1693  static int
1702 1694  is_substring(char **mntp, char **path)
1703 1695  {
1704 1696          char *p1 = *mntp, *p2 = *path;
1705 1697  
1706 1698          if (*p1 == '\0' && *p2 == '\0') /* exact match */
1707 1699                  return (1);
1708 1700          else if (*p1 == '\0' && *p2 == '/')
1709 1701                  return (1);
1710 1702          else if (*p1 == '\0' && *(p1-1) == '/') {
1711 1703                  *path = --p2; /* we need the slash in p2 */
1712 1704                  return (1);
1713 1705          } else if (*p2 == '\0') {
1714 1706                  while (*p1 == '/')
1715 1707                          p1++;
1716 1708                  if (*p1 == '\0') /* exact match */
1717 1709                          return (1);
1718 1710          }
1719 1711          return (0);
1720 1712  }
1721 1713  
1722 1714  /*
1723 1715   * find_lofsentry() searches for the real path which this requested LOFS path
1724 1716   * (rpath) shadows. If found, it will return the sharetab entry of
1725 1717   * the real path that corresponds to the LOFS path.
1726 1718   * We first search mnttab to see if the requested path is an automounted
1727 1719   * path. If it is an automounted path, it will trigger the mount by stat()ing
1728 1720   * the requested path. Note that it is important to check that this path is
1729 1721   * actually an automounted path, otherwise we would stat() a path which may
1730 1722   * turn out to be NFS and block indefinitely on a dead server. The automounter
1731 1723   * times-out if the server is dead, so there's no risk of hanging this
1732 1724   * thread waiting for stat().
1733 1725   * After the mount has been triggered (if necessary), we look for a
1734 1726   * mountpoint of type LOFS (by searching /etc/mnttab again) which
1735 1727   * is a substring of the rpath. If found, we construct a new path by
1736 1728   * concatenating the mnt_special and the remaining of rpath, call findentry()
1737 1729   * to make sure the 'real path' is shared.
1738 1730   */
1739 1731  static share_t *
1740 1732  find_lofsentry(char *rpath, int *done_flag)
1741 1733  {
1742 1734          struct stat r_stbuf;
1743 1735          mntlist_t *ml, *mntl, *mntpnt = NULL;
1744 1736          share_t *retcode = NULL;
1745 1737          char tmp_path[MAXPATHLEN];
1746 1738          int mntpnt_len = 0, tmp;
1747 1739          char *p1, *p2;
1748 1740  
1749 1741          if ((*done_flag)++)
1750 1742                  return (retcode);
1751 1743  
1752 1744          /*
1753 1745           * While fsgetmntlist() uses lockf() to
1754 1746           * lock the mnttab before reading it in,
1755 1747           * the lock ignores threads in the same process.
1756 1748           * Read in the mnttab with the protection of a mutex.
1757 1749           */
1758 1750          (void) mutex_lock(&mnttab_lock);
1759 1751          mntl = fsgetmntlist();
1760 1752          (void) mutex_unlock(&mnttab_lock);
1761 1753  
1762 1754          /*
1763 1755           * Obtain the mountpoint for the requested path.
1764 1756           */
1765 1757          for (ml = mntl; ml; ml = ml->mntl_next) {
1766 1758                  for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1767 1759                      *p1 == *p2 && *p1; p1++, p2++)
1768 1760                          ;
1769 1761                  if (is_substring(&p1, &p2) &&
1770 1762                      (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
1771 1763                          mntpnt = ml;
1772 1764                          mntpnt_len = tmp;
1773 1765                  }
1774 1766          }
1775 1767  
1776 1768          /*
1777 1769           * If the path needs to be autoFS mounted, trigger the mount by
1778 1770           * stat()ing it. This is determined by checking whether the
1779 1771           * mountpoint we just found is of type autofs.
1780 1772           */
1781 1773          if (mntpnt != NULL &&
1782 1774              strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
1783 1775                  /*
1784 1776                   * The requested path is a substring of an autoFS filesystem.
1785 1777                   * Trigger the mount.
1786 1778                   */
1787 1779                  if (stat(rpath, &r_stbuf) < 0) {
1788 1780                          if (verbose)
1789 1781                                  syslog(LOG_NOTICE, "%s: %m", rpath);
1790 1782                          goto done;
1791 1783                  }
1792 1784                  if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
1793 1785                          /*
1794 1786                           * The requested path is a directory, stat(2) it
1795 1787                           * again with a trailing '.' to force the autoFS
1796 1788                           * module to trigger the mount of indirect
1797 1789                           * automount entries, such as /net/jurassic/.
1798 1790                           */
1799 1791                          if (strlen(rpath) + 2 > MAXPATHLEN) {
1800 1792                                  if (verbose) {
1801 1793                                          syslog(LOG_NOTICE,
1802 1794                                              "%s/.: exceeds MAXPATHLEN %d",
1803 1795                                              rpath, MAXPATHLEN);
1804 1796                                  }
1805 1797                                  goto done;
1806 1798                          }
1807 1799                          (void) strcpy(tmp_path, rpath);
1808 1800                          (void) strcat(tmp_path, "/.");
1809 1801  
1810 1802                          if (stat(tmp_path, &r_stbuf) < 0) {
1811 1803                                  if (verbose)
1812 1804                                          syslog(LOG_NOTICE, "%s: %m", tmp_path);
1813 1805                                  goto done;
1814 1806                          }
1815 1807                  }
1816 1808  
1817 1809                  /*
1818 1810                   * The mount has been triggered, re-read mnttab to pick up
1819 1811                   * the changes made by autoFS.
1820 1812                   */
1821 1813                  fsfreemntlist(mntl);
1822 1814                  (void) mutex_lock(&mnttab_lock);
1823 1815                  mntl = fsgetmntlist();
1824 1816                  (void) mutex_unlock(&mnttab_lock);
1825 1817          }
1826 1818  
1827 1819          /*
1828 1820           * The autoFS mountpoint has been triggered if necessary,
1829 1821           * now search mnttab again to determine if the requested path
1830 1822           * is an LOFS mount of a shared path.
1831 1823           */
1832 1824          mntpnt_len = 0;
1833 1825          for (ml = mntl; ml; ml = ml->mntl_next) {
1834 1826                  if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
1835 1827                          continue;
1836 1828  
1837 1829                  for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1838 1830                      *p1 == *p2 && *p1; p1++, p2++)
1839 1831                          ;
1840 1832  
1841 1833                  if (is_substring(&p1, &p2) &&
1842 1834                      ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
1843 1835                          mntpnt_len = tmp;
1844 1836  
1845 1837                          if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
1846 1838                              MAXPATHLEN) {
1847 1839                                  if (verbose) {
1848 1840                                          syslog(LOG_NOTICE, "%s%s: exceeds %d",
1849 1841                                              ml->mntl_mnt->mnt_special, p2,
1850 1842                                              MAXPATHLEN);
1851 1843                                  }
1852 1844                                  if (retcode)
1853 1845                                          sharefree(retcode);
1854 1846                                  retcode = NULL;
1855 1847                                  goto done;
1856 1848                          }
1857 1849  
1858 1850                          (void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
1859 1851                          (void) strcat(tmp_path, p2);
1860 1852                          if (retcode)
1861 1853                                  sharefree(retcode);
1862 1854                          retcode = findentry(tmp_path);
1863 1855                  }
1864 1856          }
1865 1857  
1866 1858          if (retcode) {
1867 1859                  assert(strlen(tmp_path) > 0);
1868 1860                  (void) strcpy(rpath, tmp_path);
1869 1861          }
1870 1862  
1871 1863  done:
1872 1864          fsfreemntlist(mntl);
1873 1865          return (retcode);
1874 1866  }
1875 1867  
1876 1868  /*
1877 1869   * Determine whether an access list grants rights to a particular host.
1878 1870   * We match on aliases of the hostname as well as on the canonical name.
1879 1871   * Names in the access list may be either hosts or netgroups;  they're
1880 1872   * not distinguished syntactically.  We check for hosts first because
1881 1873   * it's cheaper, then try netgroups.
1882 1874   *
1883 1875   * Return values:
1884 1876   *  1 - access is granted
1885 1877   *  0 - access is denied
1886 1878   * -1 - an error occured
1887 1879   */
1888 1880  int
1889 1881  in_access_list(struct cln *cln,
1890 1882      char *access_list)  /* N.B. we clobber this "input" parameter */
1891 1883  {
1892 1884          char addr[INET_ADDRSTRLEN];
1893 1885          char buff[256];
1894 1886          int nentries = 0;
1895 1887          char *cstr = access_list;
1896 1888          char *gr = access_list;
1897 1889          int i;
1898 1890          int response;
1899 1891          int ret;
1900 1892          struct netbuf *pnb;
1901 1893          struct nd_hostservlist *clnames = NULL;
1902 1894  
1903 1895          /* If no access list - then it's unrestricted */
1904 1896          if (access_list == NULL || *access_list == '\0')
1905 1897                  return (1);
1906 1898  
1907 1899          if ((pnb = cln_getnbuf(cln)) == NULL)
1908 1900                  return (-1);
1909 1901  
1910 1902          for (;;) {
1911 1903                  if ((cstr = strpbrk(cstr, "[:")) != NULL) {
1912 1904                          if (*cstr == ':') {
1913 1905                                  *cstr = '\0';
1914 1906                          } else {
1915 1907                                  assert(*cstr == '[');
1916 1908                                  cstr = strchr(cstr + 1, ']');
1917 1909                                  if (cstr == NULL)
1918 1910                                          return (-1);
1919 1911                                  cstr++;
1920 1912                                  continue;
1921 1913                          }
1922 1914                  }
1923 1915  
1924 1916                  /*
1925 1917                   * If the list name has a '-' prepended then a match of
1926 1918                   * the following name implies failure instead of success.
1927 1919                   */
1928 1920                  if (*gr == '-') {
1929 1921                          response = 0;
1930 1922                          gr++;
1931 1923                  } else {
1932 1924                          response = 1;
1933 1925                  }
1934 1926  
1935 1927                  /*
1936 1928                   * First check if we have '@' entry, as it doesn't
1937 1929                   * require client hostname.
1938 1930                   */
1939 1931                  if (*gr == '@') {
1940 1932                          gr++;
1941 1933  
1942 1934                          /* Netname support */
1943 1935                          if (!isdigit(*gr) && *gr != '[') {
1944 1936                                  struct netent n, *np;
1945 1937  
1946 1938                                  if ((np = getnetbyname_r(gr, &n, buff,
1947 1939                                      sizeof (buff))) != NULL &&
1948 1940                                      np->n_net != 0) {
1949 1941                                          while ((np->n_net & 0xFF000000u) == 0)
1950 1942                                                  np->n_net <<= 8;
1951 1943                                          np->n_net = htonl(np->n_net);
1952 1944                                          if (inet_ntop(AF_INET, &np->n_net, addr,
1953 1945                                              INET_ADDRSTRLEN) == NULL)
1954 1946                                                  break;
1955 1947                                          ret = inet_matchaddr(pnb->buf, addr);
1956 1948                                          if (ret == -1) {
1957 1949                                                  if (errno == EINVAL) {
1958 1950                                                          syslog(LOG_WARNING,
1959 1951                                                              "invalid access "
1960 1952                                                              "list entry: %s",
1961 1953                                                              addr);
1962 1954                                                  }
1963 1955                                                  return (-1);
1964 1956                                          } else if (ret == 1) {
1965 1957                                                  return (response);
1966 1958                                          }
1967 1959                                  }
1968 1960                          } else {
1969 1961                                  ret = inet_matchaddr(pnb->buf, gr);
1970 1962                                  if (ret == -1) {
1971 1963                                          if (errno == EINVAL) {
1972 1964                                                  syslog(LOG_WARNING,
1973 1965                                                      "invalid access list "
1974 1966                                                      "entry: %s", gr);
1975 1967                                          }
1976 1968                                          return (-1);
1977 1969                                  } else if (ret == 1) {
1978 1970                                          return (response);
1979 1971                                  }
1980 1972                          }
1981 1973  
1982 1974                          goto next;
1983 1975                  }
1984 1976  
1985 1977                  /*
1986 1978                   * No other checks can be performed if client address
1987 1979                   * can't be resolved.
1988 1980                   */
1989 1981                  if ((clnames = cln_getclientsnames(cln)) == NULL)
1990 1982                          goto next;
1991 1983  
1992 1984                  /* Otherwise loop through all client hostname aliases */
1993 1985                  for (i = 0; i < clnames->h_cnt; i++) {
1994 1986                          char *host = clnames->h_hostservs[i].h_host;
1995 1987  
1996 1988                          /*
1997 1989                           * If the list name begins with a dot then
1998 1990                           * do a domain name suffix comparison.
1999 1991                           * A single dot matches any name with no
2000 1992                           * suffix.
2001 1993                           */
2002 1994                          if (*gr == '.') {
2003 1995                                  if (*(gr + 1) == '\0') {  /* single dot */
2004 1996                                          if (strchr(host, '.') == NULL)
2005 1997                                                  return (response);
2006 1998                                  } else {
2007 1999                                          int off = strlen(host) - strlen(gr);
2008 2000                                          if (off > 0 &&
2009 2001                                              strcasecmp(host + off, gr) == 0) {
2010 2002                                                  return (response);
2011 2003                                          }
2012 2004                                  }
2013 2005                          } else {
2014 2006                                  /* Just do a hostname match */
2015 2007                                  if (strcasecmp(gr, host) == 0)
2016 2008                                          return (response);
2017 2009                          }
2018 2010                  }
2019 2011  
2020 2012                  nentries++;
2021 2013  
2022 2014  next:
2023 2015                  if (cstr == NULL)
2024 2016                          break;
2025 2017  
2026 2018                  gr = ++cstr;
2027 2019          }
2028 2020  
2029 2021          if (clnames == NULL)
2030 2022                  return (0);
2031 2023  
2032 2024          return (netgroup_check(clnames, access_list, nentries));
2033 2025  }
2034 2026  
2035 2027  
2036 2028  static char *optlist[] = {
2037 2029  #define OPT_RO          0
2038 2030          SHOPT_RO,
2039 2031  #define OPT_RW          1
2040 2032          SHOPT_RW,
2041 2033  #define OPT_ROOT        2
2042 2034          SHOPT_ROOT,
2043 2035  #define OPT_SECURE      3
2044 2036          SHOPT_SECURE,
2045 2037  #define OPT_ANON        4
2046 2038          SHOPT_ANON,
2047 2039  #define OPT_WINDOW      5
2048 2040          SHOPT_WINDOW,
2049 2041  #define OPT_NOSUID      6
2050 2042          SHOPT_NOSUID,
2051 2043  #define OPT_ACLOK       7
2052 2044          SHOPT_ACLOK,
2053 2045  #define OPT_SEC         8
2054 2046          SHOPT_SEC,
2055 2047  #define OPT_NONE        9
2056 2048          SHOPT_NONE,
2057 2049  #define OPT_UIDMAP      10
2058 2050          SHOPT_UIDMAP,
2059 2051  #define OPT_GIDMAP      11
2060 2052          SHOPT_GIDMAP,
2061 2053          NULL
2062 2054  };
2063 2055  
2064 2056  static int
2065 2057  map_flavor(char *str)
2066 2058  {
2067 2059          seconfig_t sec;
2068 2060  
2069 2061          if (nfs_getseconfig_byname(str, &sec))
2070 2062                  return (-1);
2071 2063  
2072 2064          return (sec.sc_nfsnum);
2073 2065  }
2074 2066  
2075 2067  /*
2076 2068   * If the option string contains a "sec="
2077 2069   * option, then use new option syntax.
2078 2070   */
2079 2071  static int
2080 2072  newopts(char *opts)
2081 2073  {
2082 2074          char *head, *p, *val;
2083 2075  
2084 2076          if (!opts || *opts == '\0')
2085 2077                  return (0);
2086 2078  
2087 2079          head = strdup(opts);
2088 2080          if (head == NULL) {
2089 2081                  syslog(LOG_ERR, "opts: no memory");
2090 2082                  return (0);
2091 2083          }
2092 2084  
2093 2085          p = head;
2094 2086          while (*p) {
2095 2087                  if (getsubopt(&p, optlist, &val) == OPT_SEC) {
2096 2088                          free(head);
2097 2089                          return (1);
2098 2090                  }
2099 2091          }
2100 2092  
2101 2093          free(head);
2102 2094          return (0);
2103 2095  }
2104 2096  
2105 2097  /*
2106 2098   * Given an export and the clients hostname(s)
2107 2099   * determine the security flavors that this
2108 2100   * client is permitted to use.
2109 2101   *
2110 2102   * This routine is called only for "old" syntax, i.e.
2111 2103   * only one security flavor is allowed.  So we need
2112 2104   * to determine two things: the particular flavor,
2113 2105   * and whether the client is allowed to use this
2114 2106   * flavor, i.e. is in the access list.
2115 2107   *
2116 2108   * Note that if there is no access list, then the
2117 2109   * default is that access is granted.
2118 2110   */
2119 2111  static int
2120 2112  getclientsflavors_old(share_t *sh, struct cln *cln, int *flavors)
2121 2113  {
2122 2114          char *opts, *p, *val;
2123 2115          boolean_t ok = B_FALSE;
2124 2116          int defaultaccess = 1;
2125 2117          boolean_t reject = B_FALSE;
2126 2118  
2127 2119          opts = strdup(sh->sh_opts);
2128 2120          if (opts == NULL) {
2129 2121                  syslog(LOG_ERR, "getclientsflavors: no memory");
2130 2122                  return (0);
2131 2123          }
2132 2124  
2133 2125          flavors[0] = AUTH_SYS;
2134 2126          p = opts;
2135 2127  
2136 2128          while (*p) {
2137 2129  
2138 2130                  switch (getsubopt(&p, optlist, &val)) {
2139 2131                  case OPT_SECURE:
2140 2132                          flavors[0] = AUTH_DES;
2141 2133                          break;
2142 2134  
2143 2135                  case OPT_RO:
2144 2136                  case OPT_RW:
2145 2137                          defaultaccess = 0;
2146 2138                          if (in_access_list(cln, val) > 0)
2147 2139                                  ok = B_TRUE;
2148 2140                          break;
2149 2141  
2150 2142                  case OPT_NONE:
2151 2143                          defaultaccess = 0;
2152 2144                          if (in_access_list(cln, val) > 0)
2153 2145                                  reject = B_TRUE;
2154 2146                  }
2155 2147          }
2156 2148  
2157 2149          free(opts);
2158 2150  
2159 2151          /* none takes precedence over everything else */
2160 2152          if (reject)
2161 2153                  ok = B_FALSE;
2162 2154  
2163 2155          return (defaultaccess || ok);
2164 2156  }
2165 2157  
2166 2158  /*
2167 2159   * Given an export and the clients hostname(s)
2168 2160   * determine the security flavors that this
2169 2161   * client is permitted to use.
2170 2162   *
2171 2163   * This is somewhat more complicated than the "old"
2172 2164   * routine because the options may contain multiple
2173 2165   * security flavors (sec=) each with its own access
2174 2166   * lists.  So a client could be granted access based
2175 2167   * on a number of security flavors.  Note that the
2176 2168   * type of access might not always be the same, the
2177 2169   * client may get readonly access with one flavor
2178 2170   * and readwrite with another, however the client
2179 2171   * is not told this detail, it gets only the list
2180 2172   * of flavors, and only if the client is using
2181 2173   * version 3 of the mount protocol.
2182 2174   */
2183 2175  static int
2184 2176  getclientsflavors_new(share_t *sh, struct cln *cln, int *flavors)
2185 2177  {
2186 2178          char *opts, *p, *val;
2187 2179          char *lasts;
2188 2180          char *f;
2189 2181          boolean_t defaultaccess = B_TRUE;       /* default access is rw */
2190 2182          boolean_t access_ok = B_FALSE;
2191 2183          int count, c;
2192 2184          boolean_t reject = B_FALSE;
2193 2185  
2194 2186          opts = strdup(sh->sh_opts);
2195 2187          if (opts == NULL) {
2196 2188                  syslog(LOG_ERR, "getclientsflavors: no memory");
2197 2189                  return (0);
2198 2190          }
2199 2191  
2200 2192          p = opts;
2201 2193          count = c = 0;
2202 2194  
2203 2195          while (*p) {
2204 2196                  switch (getsubopt(&p, optlist, &val)) {
2205 2197                  case OPT_SEC:
2206 2198                          if (reject)
2207 2199                                  access_ok = B_FALSE;
2208 2200  
2209 2201                          /*
2210 2202                           * Before a new sec=xxx option, check if we need
2211 2203                           * to move the c index back to the previous count.
2212 2204                           */
2213 2205                          if (!defaultaccess && !access_ok) {
2214 2206                                  c = count;
2215 2207                          }
2216 2208  
2217 2209                          /* get all the sec=f1[:f2] flavors */
2218 2210                          while ((f = strtok_r(val, ":", &lasts)) != NULL) {
2219 2211                                  flavors[c++] = map_flavor(f);
2220 2212                                  val = NULL;
2221 2213                          }
2222 2214  
2223 2215                          /* for a new sec=xxx option, default is rw access */
2224 2216                          defaultaccess = B_TRUE;
2225 2217                          access_ok = B_FALSE;
2226 2218                          reject = B_FALSE;
2227 2219                          break;
2228 2220  
2229 2221                  case OPT_RO:
2230 2222                  case OPT_RW:
2231 2223                          defaultaccess = B_FALSE;
2232 2224                          if (in_access_list(cln, val) > 0)
2233 2225                                  access_ok = B_TRUE;
2234 2226                          break;
2235 2227  
2236 2228                  case OPT_NONE:
2237 2229                          defaultaccess = B_FALSE;
2238 2230                          if (in_access_list(cln, val) > 0)
2239 2231                                  reject = B_TRUE; /* none overides rw/ro */
2240 2232                          break;
2241 2233                  }
2242 2234          }
2243 2235  
2244 2236          if (reject)
2245 2237                  access_ok = B_FALSE;
2246 2238  
2247 2239          if (!defaultaccess && !access_ok)
2248 2240                  c = count;
2249 2241  
2250 2242          free(opts);
2251 2243  
2252 2244          return (c);
2253 2245  }
2254 2246  
2255 2247  /*
2256 2248   * This is a tricky piece of code that parses the
2257 2249   * share options looking for a match on the auth
2258 2250   * flavor that the client is using. If it finds
2259 2251   * a match, then the client is given ro, rw, or
2260 2252   * no access depending whether it is in the access
2261 2253   * list.  There is a special case for "secure"
2262 2254   * flavor.  Other flavors are values of the new "sec=" option.
2263 2255   */
2264 2256  int
2265 2257  check_client(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2266 2258      gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2267 2259      gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2268 2260  {
2269 2261          if (newopts(sh->sh_opts))
2270 2262                  return (check_client_new(sh, cln, flavor, clnt_uid, clnt_gid,
2271 2263                      clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2272 2264                      srv_gids));
2273 2265          else
2274 2266                  return (check_client_old(sh, cln, flavor, clnt_uid, clnt_gid,
2275 2267                      clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2276 2268                      srv_gids));
2277 2269  }
2278 2270  
2279 2271  extern int _getgroupsbymember(const char *, gid_t[], int, int);
2280 2272  
2281 2273  /*
2282 2274   * Get supplemental groups for uid
2283 2275   */
2284 2276  static int
2285 2277  getusergroups(uid_t uid, uint_t *ngrps, gid_t **grps)
2286 2278  {
2287 2279          struct passwd pwd;
2288 2280          char *pwbuf = alloca(pw_size);
2289 2281          gid_t *tmpgrps = alloca(ngroups_max * sizeof (gid_t));
2290 2282          int tmpngrps;
2291 2283  
2292 2284          if (getpwuid_r(uid, &pwd, pwbuf, pw_size) == NULL)
2293 2285                  return (-1);
2294 2286  
2295 2287          tmpgrps[0] = pwd.pw_gid;
2296 2288  
2297 2289          tmpngrps = _getgroupsbymember(pwd.pw_name, tmpgrps, ngroups_max, 1);
2298 2290          if (tmpngrps <= 0) {
2299 2291                  syslog(LOG_WARNING,
2300 2292                      "getusergroups(): Unable to get groups for user %s",
2301 2293                      pwd.pw_name);
2302 2294  
2303 2295                  return (-1);
2304 2296          }
2305 2297  
2306 2298          *grps = malloc(tmpngrps * sizeof (gid_t));
2307 2299          if (*grps == NULL) {
2308 2300                  syslog(LOG_ERR,
2309 2301                      "getusergroups(): Memory allocation failed: %m");
2310 2302  
2311 2303                  return (-1);
2312 2304          }
2313 2305  
2314 2306          *ngrps = tmpngrps;
2315 2307          (void) memcpy(*grps, tmpgrps, tmpngrps * sizeof (gid_t));
2316 2308  
2317 2309          return (0);
2318 2310  }
2319 2311  
2320 2312  /*
2321 2313   * is_a_number(number)
2322 2314   *
2323 2315   * is the string a number in one of the forms we want to use?
2324 2316   */
2325 2317  
2326 2318  static int
2327 2319  is_a_number(char *number)
2328 2320  {
2329 2321          int ret = 1;
2330 2322          int hex = 0;
2331 2323  
2332 2324          if (strncmp(number, "0x", 2) == 0) {
2333 2325                  number += 2;
2334 2326                  hex = 1;
2335 2327          } else if (*number == '-') {
2336 2328                  number++; /* skip the minus */
2337 2329          }
2338 2330          while (ret == 1 && *number != '\0') {
2339 2331                  if (hex) {
2340 2332                          ret = isxdigit(*number++);
2341 2333                  } else {
2342 2334                          ret = isdigit(*number++);
2343 2335                  }
2344 2336          }
2345 2337          return (ret);
2346 2338  }
2347 2339  
2348 2340  static boolean_t
2349 2341  get_uid(char *value, uid_t *uid)
2350 2342  {
2351 2343          if (!is_a_number(value)) {
2352 2344                  struct passwd *pw;
2353 2345                  /*
2354 2346                   * in this case it would have to be a
2355 2347                   * user name
2356 2348                   */
2357 2349                  pw = getpwnam(value);
2358 2350                  if (pw == NULL)
2359 2351                          return (B_FALSE);
2360 2352                  *uid = pw->pw_uid;
2361 2353                  endpwent();
2362 2354          } else {
2363 2355                  uint64_t intval;
2364 2356                  intval = strtoull(value, NULL, 0);
2365 2357                  if (intval > UID_MAX && intval != -1)
2366 2358                          return (B_FALSE);
2367 2359                  *uid = (uid_t)intval;
2368 2360          }
2369 2361  
2370 2362          return (B_TRUE);
2371 2363  }
2372 2364  
2373 2365  static boolean_t
2374 2366  get_gid(char *value, gid_t *gid)
2375 2367  {
2376 2368          if (!is_a_number(value)) {
2377 2369                  struct group *gr;
2378 2370                  /*
2379 2371                   * in this case it would have to be a
2380 2372                   * group name
2381 2373                   */
2382 2374                  gr = getgrnam(value);
2383 2375                  if (gr == NULL)
2384 2376                          return (B_FALSE);
2385 2377                  *gid = gr->gr_gid;
2386 2378                  endgrent();
2387 2379          } else {
2388 2380                  uint64_t intval;
2389 2381                  intval = strtoull(value, NULL, 0);
2390 2382                  if (intval > UID_MAX && intval != -1)
2391 2383                          return (B_FALSE);
2392 2384                  *gid = (gid_t)intval;
2393 2385          }
2394 2386  
2395 2387          return (B_TRUE);
2396 2388  }
2397 2389  
2398 2390  static int
2399 2391  check_client_old(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2400 2392      gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2401 2393      gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2402 2394  {
2403 2395          char *opts, *p, *val;
2404 2396          int match;      /* Set when a flavor is matched */
2405 2397          int perm = 0;   /* Set when "ro", "rw" or "root" is matched */
2406 2398          int list = 0;   /* Set when "ro", "rw" is found */
2407 2399          int ro_val = 0; /* Set if ro option is 'ro=' */
2408 2400          int rw_val = 0; /* Set if rw option is 'rw=' */
2409 2401  
2410 2402          boolean_t map_deny = B_FALSE;
2411 2403  
2412 2404          opts = strdup(sh->sh_opts);
2413 2405          if (opts == NULL) {
2414 2406                  syslog(LOG_ERR, "check_client: no memory");
2415 2407                  return (0);
2416 2408          }
2417 2409  
2418 2410          /*
2419 2411           * If client provided 16 supplemental groups with AUTH_SYS, lookup
2420 2412           * locally for all of them
2421 2413           */
2422 2414          if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2423 2415                  if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2424 2416                          perm |= NFSAUTH_GROUPS;
2425 2417  
2426 2418          p = opts;
2427 2419          match = AUTH_UNIX;
2428 2420  
2429 2421          while (*p) {
2430 2422                  switch (getsubopt(&p, optlist, &val)) {
2431 2423  
2432 2424                  case OPT_SECURE:
2433 2425                          match = AUTH_DES;
2434 2426  
2435 2427                          if (perm & NFSAUTH_GROUPS) {
2436 2428                                  free(*srv_gids);
2437 2429                                  *srv_ngids = 0;
2438 2430                                  *srv_gids = NULL;
2439 2431                                  perm &= ~NFSAUTH_GROUPS;
2440 2432                          }
2441 2433  
2442 2434                          break;
2443 2435  
2444 2436                  case OPT_RO:
2445 2437                          list++;
2446 2438                          if (val != NULL)
2447 2439                                  ro_val++;
2448 2440                          if (in_access_list(cln, val) > 0)
2449 2441                                  perm |= NFSAUTH_RO;
2450 2442                          break;
2451 2443  
2452 2444                  case OPT_RW:
2453 2445                          list++;
2454 2446                          if (val != NULL)
2455 2447                                  rw_val++;
2456 2448                          if (in_access_list(cln, val) > 0)
2457 2449                                  perm |= NFSAUTH_RW;
2458 2450                          break;
2459 2451  
2460 2452                  case OPT_ROOT:
2461 2453                          /*
2462 2454                           * Check if the client is in
2463 2455                           * the root list. Only valid
2464 2456                           * for AUTH_SYS.
2465 2457                           */
2466 2458                          if (flavor != AUTH_SYS)
2467 2459                                  break;
2468 2460  
2469 2461                          if (val == NULL || *val == '\0')
2470 2462                                  break;
2471 2463  
2472 2464                          if (clnt_uid != 0)
2473 2465                                  break;
2474 2466  
2475 2467                          if (in_access_list(cln, val) > 0) {
2476 2468                                  perm |= NFSAUTH_ROOT;
2477 2469                                  perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2478 2470                                  map_deny = B_FALSE;
2479 2471  
2480 2472                                  if (perm & NFSAUTH_GROUPS) {
2481 2473                                          free(*srv_gids);
2482 2474                                          *srv_ngids = 0;
2483 2475                                          *srv_gids = NULL;
2484 2476                                          perm &= ~NFSAUTH_GROUPS;
2485 2477                                  }
2486 2478                          }
2487 2479                          break;
2488 2480  
2489 2481                  case OPT_NONE:
2490 2482                          /*
2491 2483                           * Check if the client should have no access
2492 2484                           * to this share at all. This option behaves
2493 2485                           * more like "root" than either "rw" or "ro".
2494 2486                           */
2495 2487                          if (in_access_list(cln, val) > 0)
2496 2488                                  perm |= NFSAUTH_DENIED;
2497 2489                          break;
2498 2490  
2499 2491                  case OPT_UIDMAP: {
2500 2492                          char *c;
2501 2493                          char *n;
2502 2494  
2503 2495                          /*
2504 2496                           * The uidmap is supported for AUTH_SYS only.
2505 2497                           */
2506 2498                          if (flavor != AUTH_SYS)
2507 2499                                  break;
2508 2500  
2509 2501                          if (perm & NFSAUTH_UIDMAP || map_deny)
2510 2502                                  break;
2511 2503  
2512 2504                          for (c = val; c != NULL; c = n) {
2513 2505                                  char *s;
2514 2506                                  char *al;
2515 2507                                  uid_t srv;
2516 2508  
2517 2509                                  n = strchr(c, '~');
2518 2510                                  if (n != NULL)
2519 2511                                          *n++ = '\0';
2520 2512  
2521 2513                                  s = strchr(c, ':');
2522 2514                                  if (s != NULL) {
2523 2515                                          *s++ = '\0';
2524 2516                                          al = strchr(s, ':');
2525 2517                                          if (al != NULL)
2526 2518                                                  *al++ = '\0';
2527 2519                                  }
2528 2520  
2529 2521                                  if (s == NULL || al == NULL)
2530 2522                                          continue;
2531 2523  
2532 2524                                  if (*c == '\0') {
2533 2525                                          if (clnt_uid != (uid_t)-1)
2534 2526                                                  continue;
2535 2527                                  } else if (strcmp(c, "*") != 0) {
2536 2528                                          uid_t clnt;
2537 2529  
2538 2530                                          if (!get_uid(c, &clnt))
2539 2531                                                  continue;
2540 2532  
2541 2533                                          if (clnt_uid != clnt)
2542 2534                                                  continue;
2543 2535                                  }
2544 2536  
2545 2537                                  if (*s == '\0')
2546 2538                                          srv = UID_NOBODY;
2547 2539                                  else if (!get_uid(s, &srv))
2548 2540                                          continue;
2549 2541                                  else if (srv == (uid_t)-1) {
2550 2542                                          map_deny = B_TRUE;
2551 2543                                          break;
2552 2544                                  }
2553 2545  
2554 2546                                  if (in_access_list(cln, al) > 0) {
2555 2547                                          *srv_uid = srv;
2556 2548                                          perm |= NFSAUTH_UIDMAP;
2557 2549  
2558 2550                                          if (perm & NFSAUTH_GROUPS) {
2559 2551                                                  free(*srv_gids);
2560 2552                                                  *srv_ngids = 0;
2561 2553                                                  *srv_gids = NULL;
2562 2554                                                  perm &= ~NFSAUTH_GROUPS;
2563 2555                                          }
2564 2556  
2565 2557                                          break;
2566 2558                                  }
2567 2559                          }
2568 2560  
2569 2561                          break;
2570 2562                  }
2571 2563  
2572 2564                  case OPT_GIDMAP: {
2573 2565                          char *c;
2574 2566                          char *n;
2575 2567  
2576 2568                          /*
2577 2569                           * The gidmap is supported for AUTH_SYS only.
2578 2570                           */
2579 2571                          if (flavor != AUTH_SYS)
2580 2572                                  break;
2581 2573  
2582 2574                          if (perm & NFSAUTH_GIDMAP || map_deny)
2583 2575                                  break;
2584 2576  
2585 2577                          for (c = val; c != NULL; c = n) {
2586 2578                                  char *s;
2587 2579                                  char *al;
2588 2580                                  gid_t srv;
2589 2581  
2590 2582                                  n = strchr(c, '~');
2591 2583                                  if (n != NULL)
2592 2584                                          *n++ = '\0';
2593 2585  
2594 2586                                  s = strchr(c, ':');
2595 2587                                  if (s != NULL) {
2596 2588                                          *s++ = '\0';
2597 2589                                          al = strchr(s, ':');
2598 2590                                          if (al != NULL)
2599 2591                                                  *al++ = '\0';
2600 2592                                  }
2601 2593  
2602 2594                                  if (s == NULL || al == NULL)
2603 2595                                          break;
2604 2596  
2605 2597                                  if (*c == '\0') {
2606 2598                                          if (clnt_gid != (gid_t)-1)
2607 2599                                                  continue;
2608 2600                                  } else if (strcmp(c, "*") != 0) {
2609 2601                                          gid_t clnt;
2610 2602  
2611 2603                                          if (!get_gid(c, &clnt))
2612 2604                                                  continue;
2613 2605  
2614 2606                                          if (clnt_gid != clnt)
2615 2607                                                  continue;
2616 2608                                  }
2617 2609  
2618 2610                                  if (*s == '\0')
2619 2611                                          srv = UID_NOBODY;
2620 2612                                  else if (!get_gid(s, &srv))
2621 2613                                          continue;
2622 2614                                  else if (srv == (gid_t)-1) {
2623 2615                                          map_deny = B_TRUE;
2624 2616                                          break;
2625 2617                                  }
2626 2618  
2627 2619                                  if (in_access_list(cln, al) > 0) {
2628 2620                                          *srv_gid = srv;
2629 2621                                          perm |= NFSAUTH_GIDMAP;
2630 2622  
2631 2623                                          if (perm & NFSAUTH_GROUPS) {
2632 2624                                                  free(*srv_gids);
2633 2625                                                  *srv_ngids = 0;
2634 2626                                                  *srv_gids = NULL;
2635 2627                                                  perm &= ~NFSAUTH_GROUPS;
2636 2628                                          }
2637 2629  
2638 2630                                          break;
2639 2631                                  }
2640 2632                          }
2641 2633  
2642 2634                          break;
2643 2635                  }
2644 2636  
2645 2637                  default:
2646 2638                          break;
2647 2639                  }
2648 2640          }
2649 2641  
2650 2642          free(opts);
2651 2643  
2652 2644          if (perm & NFSAUTH_ROOT) {
2653 2645                  *srv_uid = 0;
2654 2646                  *srv_gid = 0;
2655 2647          }
2656 2648  
2657 2649          if (map_deny)
2658 2650                  perm |= NFSAUTH_DENIED;
2659 2651  
2660 2652          if (!(perm & NFSAUTH_UIDMAP))
2661 2653                  *srv_uid = clnt_uid;
2662 2654          if (!(perm & NFSAUTH_GIDMAP))
2663 2655                  *srv_gid = clnt_gid;
2664 2656  
2665 2657          if (flavor != match || perm & NFSAUTH_DENIED)
2666 2658                  return (NFSAUTH_DENIED);
2667 2659  
2668 2660          if (list) {
2669 2661                  /*
2670 2662                   * If the client doesn't match an "ro" or "rw"
2671 2663                   * list then set no access.
2672 2664                   */
2673 2665                  if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
2674 2666                          perm |= NFSAUTH_DENIED;
2675 2667          } else {
2676 2668                  /*
2677 2669                   * The client matched a flavor entry that
2678 2670                   * has no explicit "rw" or "ro" determination.
2679 2671                   * Default it to "rw".
2680 2672                   */
2681 2673                  perm |= NFSAUTH_RW;
2682 2674          }
2683 2675  
2684 2676          /*
2685 2677           * The client may show up in both ro= and rw=
2686 2678           * lists.  If so, then turn off the RO access
2687 2679           * bit leaving RW access.
2688 2680           */
2689 2681          if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2690 2682                  /*
2691 2683                   * Logically cover all permutations of rw=,ro=.
2692 2684                   * In the case where, rw,ro=<host> we would like
2693 2685                   * to remove RW access for the host.  In all other cases
2694 2686                   * RW wins the precedence battle.
2695 2687                   */
2696 2688                  if (!rw_val && ro_val) {
2697 2689                          perm &= ~(NFSAUTH_RW);
2698 2690                  } else {
2699 2691                          perm &= ~(NFSAUTH_RO);
2700 2692                  }
2701 2693          }
2702 2694  
2703 2695          return (perm);
2704 2696  }
2705 2697  
2706 2698  /*
2707 2699   * Check if the client has access by using a flavor different from
2708 2700   * the given "flavor". If "flavor" is not in the flavor list,
2709 2701   * return TRUE to indicate that this "flavor" is a wrong sec.
2710 2702   */
2711 2703  static bool_t
2712 2704  is_wrongsec(share_t *sh, struct cln *cln, int flavor)
2713 2705  {
2714 2706          int flavor_list[MAX_FLAVORS];
2715 2707          int flavor_count, i;
2716 2708  
2717 2709          /* get the flavor list that the client has access with */
2718 2710          flavor_count = getclientsflavors_new(sh, cln, flavor_list);
2719 2711  
2720 2712          if (flavor_count == 0)
2721 2713                  return (FALSE);
2722 2714  
2723 2715          /*
2724 2716           * Check if the given "flavor" is in the flavor_list.
2725 2717           */
2726 2718          for (i = 0; i < flavor_count; i++) {
2727 2719                  if (flavor == flavor_list[i])
2728 2720                          return (FALSE);
2729 2721          }
2730 2722  
2731 2723          /*
2732 2724           * If "flavor" is not in the flavor_list, return TRUE to indicate
2733 2725           * that the client should have access by using a security flavor
2734 2726           * different from this "flavor".
2735 2727           */
2736 2728          return (TRUE);
2737 2729  }
2738 2730  
2739 2731  /*
2740 2732   * Given an export and the client's hostname, we
2741 2733   * check the security options to see whether the
2742 2734   * client is allowed to use the given security flavor.
2743 2735   *
2744 2736   * The strategy is to proceed through the options looking
2745 2737   * for a flavor match, then pay attention to the ro, rw,
2746 2738   * and root options.
2747 2739   *
2748 2740   * Note that an entry may list several flavors in a
2749 2741   * single entry, e.g.
2750 2742   *
2751 2743   *   sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
2752 2744   *
2753 2745   */
2754 2746  
2755 2747  static int
2756 2748  check_client_new(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2757 2749      gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2758 2750      gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2759 2751  {
2760 2752          char *opts, *p, *val;
2761 2753          char *lasts;
2762 2754          char *f;
2763 2755          int match = 0;  /* Set when a flavor is matched */
2764 2756          int perm = 0;   /* Set when "ro", "rw" or "root" is matched */
2765 2757          int list = 0;   /* Set when "ro", "rw" is found */
2766 2758          int ro_val = 0; /* Set if ro option is 'ro=' */
2767 2759          int rw_val = 0; /* Set if rw option is 'rw=' */
2768 2760  
2769 2761          boolean_t map_deny = B_FALSE;
2770 2762  
2771 2763          opts = strdup(sh->sh_opts);
2772 2764          if (opts == NULL) {
2773 2765                  syslog(LOG_ERR, "check_client: no memory");
2774 2766                  return (0);
2775 2767          }
2776 2768  
2777 2769          /*
2778 2770           * If client provided 16 supplemental groups with AUTH_SYS, lookup
2779 2771           * locally for all of them
2780 2772           */
2781 2773          if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2782 2774                  if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2783 2775                          perm |= NFSAUTH_GROUPS;
2784 2776  
2785 2777          p = opts;
2786 2778  
2787 2779          while (*p) {
2788 2780                  switch (getsubopt(&p, optlist, &val)) {
2789 2781  
2790 2782                  case OPT_SEC:
2791 2783                          if (match)
2792 2784                                  goto done;
2793 2785  
2794 2786                          while ((f = strtok_r(val, ":", &lasts))
2795 2787                              != NULL) {
2796 2788                                  if (flavor == map_flavor(f)) {
2797 2789                                          match = 1;
2798 2790                                          break;
2799 2791                                  }
2800 2792                                  val = NULL;
2801 2793                          }
2802 2794                          break;
2803 2795  
2804 2796                  case OPT_RO:
2805 2797                          if (!match)
2806 2798                                  break;
2807 2799  
2808 2800                          list++;
2809 2801                          if (val != NULL)
2810 2802                                  ro_val++;
2811 2803                          if (in_access_list(cln, val) > 0)
2812 2804                                  perm |= NFSAUTH_RO;
2813 2805                          break;
2814 2806  
2815 2807                  case OPT_RW:
2816 2808                          if (!match)
2817 2809                                  break;
2818 2810  
2819 2811                          list++;
2820 2812                          if (val != NULL)
2821 2813                                  rw_val++;
2822 2814                          if (in_access_list(cln, val) > 0)
2823 2815                                  perm |= NFSAUTH_RW;
2824 2816                          break;
2825 2817  
2826 2818                  case OPT_ROOT:
2827 2819                          /*
2828 2820                           * Check if the client is in
2829 2821                           * the root list. Only valid
2830 2822                           * for AUTH_SYS.
2831 2823                           */
2832 2824                          if (flavor != AUTH_SYS)
2833 2825                                  break;
2834 2826  
2835 2827                          if (!match)
2836 2828                                  break;
2837 2829  
2838 2830                          if (val == NULL || *val == '\0')
2839 2831                                  break;
2840 2832  
2841 2833                          if (clnt_uid != 0)
2842 2834                                  break;
2843 2835  
2844 2836                          if (in_access_list(cln, val) > 0) {
2845 2837                                  perm |= NFSAUTH_ROOT;
2846 2838                                  perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2847 2839                                  map_deny = B_FALSE;
2848 2840  
2849 2841                                  if (perm & NFSAUTH_GROUPS) {
2850 2842                                          free(*srv_gids);
2851 2843                                          *srv_gids = NULL;
2852 2844                                          *srv_ngids = 0;
2853 2845                                          perm &= ~NFSAUTH_GROUPS;
2854 2846                                  }
2855 2847                          }
2856 2848                          break;
2857 2849  
2858 2850                  case OPT_NONE:
2859 2851                          /*
2860 2852                           * Check if the client should have no access
2861 2853                           * to this share at all. This option behaves
2862 2854                           * more like "root" than either "rw" or "ro".
2863 2855                           */
2864 2856                          if (in_access_list(cln, val) > 0)
2865 2857                                  perm |= NFSAUTH_DENIED;
2866 2858                          break;
2867 2859  
2868 2860                  case OPT_UIDMAP: {
2869 2861                          char *c;
2870 2862                          char *n;
2871 2863  
2872 2864                          /*
2873 2865                           * The uidmap is supported for AUTH_SYS only.
2874 2866                           */
2875 2867                          if (flavor != AUTH_SYS)
2876 2868                                  break;
2877 2869  
2878 2870                          if (!match || perm & NFSAUTH_UIDMAP || map_deny)
2879 2871                                  break;
2880 2872  
2881 2873                          for (c = val; c != NULL; c = n) {
2882 2874                                  char *s;
2883 2875                                  char *al;
2884 2876                                  uid_t srv;
2885 2877  
2886 2878                                  n = strchr(c, '~');
2887 2879                                  if (n != NULL)
2888 2880                                          *n++ = '\0';
2889 2881  
2890 2882                                  s = strchr(c, ':');
2891 2883                                  if (s != NULL) {
2892 2884                                          *s++ = '\0';
2893 2885                                          al = strchr(s, ':');
2894 2886                                          if (al != NULL)
2895 2887                                                  *al++ = '\0';
2896 2888                                  }
2897 2889  
2898 2890                                  if (s == NULL || al == NULL)
2899 2891                                          continue;
2900 2892  
2901 2893                                  if (*c == '\0') {
2902 2894                                          if (clnt_uid != (uid_t)-1)
2903 2895                                                  continue;
2904 2896                                  } else if (strcmp(c, "*") != 0) {
2905 2897                                          uid_t clnt;
2906 2898  
2907 2899                                          if (!get_uid(c, &clnt))
2908 2900                                                  continue;
2909 2901  
2910 2902                                          if (clnt_uid != clnt)
2911 2903                                                  continue;
2912 2904                                  }
2913 2905  
2914 2906                                  if (*s == '\0')
2915 2907                                          srv = UID_NOBODY;
2916 2908                                  else if (!get_uid(s, &srv))
2917 2909                                          continue;
2918 2910                                  else if (srv == (uid_t)-1) {
2919 2911                                          map_deny = B_TRUE;
2920 2912                                          break;
2921 2913                                  }
2922 2914  
2923 2915                                  if (in_access_list(cln, al) > 0) {
2924 2916                                          *srv_uid = srv;
2925 2917                                          perm |= NFSAUTH_UIDMAP;
2926 2918  
2927 2919                                          if (perm & NFSAUTH_GROUPS) {
2928 2920                                                  free(*srv_gids);
2929 2921                                                  *srv_gids = NULL;
2930 2922                                                  *srv_ngids = 0;
2931 2923                                                  perm &= ~NFSAUTH_GROUPS;
2932 2924                                          }
2933 2925  
2934 2926                                          break;
2935 2927                                  }
2936 2928                          }
2937 2929  
2938 2930                          break;
2939 2931                  }
2940 2932  
2941 2933                  case OPT_GIDMAP: {
2942 2934                          char *c;
2943 2935                          char *n;
2944 2936  
2945 2937                          /*
2946 2938                           * The gidmap is supported for AUTH_SYS only.
2947 2939                           */
2948 2940                          if (flavor != AUTH_SYS)
2949 2941                                  break;
2950 2942  
2951 2943                          if (!match || perm & NFSAUTH_GIDMAP || map_deny)
2952 2944                                  break;
2953 2945  
2954 2946                          for (c = val; c != NULL; c = n) {
2955 2947                                  char *s;
2956 2948                                  char *al;
2957 2949                                  gid_t srv;
2958 2950  
2959 2951                                  n = strchr(c, '~');
2960 2952                                  if (n != NULL)
2961 2953                                          *n++ = '\0';
2962 2954  
2963 2955                                  s = strchr(c, ':');
2964 2956                                  if (s != NULL) {
2965 2957                                          *s++ = '\0';
2966 2958                                          al = strchr(s, ':');
2967 2959                                          if (al != NULL)
2968 2960                                                  *al++ = '\0';
2969 2961                                  }
2970 2962  
2971 2963                                  if (s == NULL || al == NULL)
2972 2964                                          break;
2973 2965  
2974 2966                                  if (*c == '\0') {
2975 2967                                          if (clnt_gid != (gid_t)-1)
2976 2968                                                  continue;
2977 2969                                  } else if (strcmp(c, "*") != 0) {
2978 2970                                          gid_t clnt;
2979 2971  
2980 2972                                          if (!get_gid(c, &clnt))
2981 2973                                                  continue;
2982 2974  
2983 2975                                          if (clnt_gid != clnt)
2984 2976                                                  continue;
2985 2977                                  }
2986 2978  
2987 2979                                  if (*s == '\0')
2988 2980                                          srv = UID_NOBODY;
2989 2981                                  else if (!get_gid(s, &srv))
2990 2982                                          continue;
2991 2983                                  else if (srv == (gid_t)-1) {
2992 2984                                          map_deny = B_TRUE;
2993 2985                                          break;
2994 2986                                  }
2995 2987  
2996 2988                                  if (in_access_list(cln, al) > 0) {
2997 2989                                          *srv_gid = srv;
2998 2990                                          perm |= NFSAUTH_GIDMAP;
2999 2991  
3000 2992                                          if (perm & NFSAUTH_GROUPS) {
3001 2993                                                  free(*srv_gids);
3002 2994                                                  *srv_gids = NULL;
3003 2995                                                  *srv_ngids = 0;
3004 2996                                                  perm &= ~NFSAUTH_GROUPS;
3005 2997                                          }
3006 2998  
3007 2999                                          break;
3008 3000                                  }
3009 3001                          }
3010 3002  
3011 3003                          break;
3012 3004                  }
3013 3005  
3014 3006                  default:
3015 3007                          break;
3016 3008                  }
3017 3009          }
3018 3010  
3019 3011  done:
3020 3012          if (perm & NFSAUTH_ROOT) {
3021 3013                  *srv_uid = 0;
3022 3014                  *srv_gid = 0;
3023 3015          }
3024 3016  
3025 3017          if (map_deny)
3026 3018                  perm |= NFSAUTH_DENIED;
3027 3019  
3028 3020          if (!(perm & NFSAUTH_UIDMAP))
3029 3021                  *srv_uid = clnt_uid;
3030 3022          if (!(perm & NFSAUTH_GIDMAP))
3031 3023                  *srv_gid = clnt_gid;
3032 3024  
3033 3025          /*
3034 3026           * If no match then set the perm accordingly
3035 3027           */
3036 3028          if (!match || perm & NFSAUTH_DENIED) {
3037 3029                  free(opts);
3038 3030                  return (NFSAUTH_DENIED);
3039 3031          }
3040 3032  
3041 3033          if (list) {
3042 3034                  /*
3043 3035                   * If the client doesn't match an "ro" or "rw" list then
3044 3036                   * check if it may have access by using a different flavor.
3045 3037                   * If so, return NFSAUTH_WRONGSEC.
3046 3038                   * If not, return NFSAUTH_DENIED.
3047 3039                   */
3048 3040                  if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
3049 3041                          if (is_wrongsec(sh, cln, flavor))
3050 3042                                  perm |= NFSAUTH_WRONGSEC;
3051 3043                          else
3052 3044                                  perm |= NFSAUTH_DENIED;
3053 3045                  }
3054 3046          } else {
3055 3047                  /*
3056 3048                   * The client matched a flavor entry that
3057 3049                   * has no explicit "rw" or "ro" determination.
3058 3050                   * Make sure it defaults to "rw".
3059 3051                   */
3060 3052                  perm |= NFSAUTH_RW;
3061 3053          }
3062 3054  
3063 3055          /*
3064 3056           * The client may show up in both ro= and rw=
3065 3057           * lists.  If so, then turn off the RO access
3066 3058           * bit leaving RW access.
3067 3059           */
3068 3060          if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
3069 3061                  /*
3070 3062                   * Logically cover all permutations of rw=,ro=.
3071 3063                   * In the case where, rw,ro=<host> we would like
3072 3064                   * to remove RW access for the host.  In all other cases
3073 3065                   * RW wins the precedence battle.
3074 3066                   */
3075 3067                  if (!rw_val && ro_val) {
3076 3068                          perm &= ~(NFSAUTH_RW);
3077 3069                  } else {
3078 3070                          perm &= ~(NFSAUTH_RO);
3079 3071                  }
3080 3072          }
3081 3073  
3082 3074          free(opts);
3083 3075  
3084 3076          return (perm);
3085 3077  }
3086 3078  
3087 3079  void
3088 3080  check_sharetab()
3089 3081  {
3090 3082          FILE *f;
3091 3083          struct stat st;
3092 3084          static timestruc_t last_sharetab_time;
3093 3085          timestruc_t prev_sharetab_time;
3094 3086          share_t *sh;
3095 3087          struct sh_list *shp, *shp_prev;
3096 3088          int res, c = 0;
3097 3089  
3098 3090          /*
3099 3091           *  read in /etc/dfs/sharetab if it has changed
3100 3092           */
3101 3093          if (stat(SHARETAB, &st) != 0) {
3102 3094                  syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3103 3095                  return;
3104 3096          }
3105 3097  
3106 3098          if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
3107 3099              st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
3108 3100                  /*
3109 3101                   * No change.
3110 3102                   */
3111 3103                  return;
3112 3104          }
3113 3105  
3114 3106          /*
3115 3107           * Remember the mod time, then after getting the
3116 3108           * write lock check again.  If another thread
3117 3109           * already did the update, then there's no
3118 3110           * work to do.
3119 3111           */
3120 3112          prev_sharetab_time = last_sharetab_time;
3121 3113  
3122 3114          (void) rw_wrlock(&sharetab_lock);
3123 3115  
3124 3116          if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
3125 3117              prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
3126 3118                  (void) rw_unlock(&sharetab_lock);
3127 3119                  return;
3128 3120          }
3129 3121  
3130 3122          /*
3131 3123           * Note that since the sharetab is now in memory
3132 3124           * and a snapshot is taken, we no longer have to
3133 3125           * lock the file.
3134 3126           */
3135 3127          f = fopen(SHARETAB, "r");
3136 3128          if (f == NULL) {
3137 3129                  syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
3138 3130                  (void) rw_unlock(&sharetab_lock);
3139 3131                  return;
3140 3132          }
3141 3133  
3142 3134          /*
3143 3135           * Once we are sure /etc/dfs/sharetab has been
3144 3136           * modified, flush netgroup cache entries.
3145 3137           */
3146 3138          netgrp_cache_flush();
3147 3139  
3148 3140          sh_free(share_list);                    /* free old list */
3149 3141          share_list = NULL;
3150 3142  
3151 3143          while ((res = getshare(f, &sh)) > 0) {
3152 3144                  c++;
3153 3145                  if (strcmp(sh->sh_fstype, "nfs") != 0)
3154 3146                          continue;
3155 3147  
3156 3148                  shp = malloc(sizeof (*shp));
3157 3149                  if (shp == NULL)
3158 3150                          goto alloc_failed;
3159 3151                  if (share_list == NULL)
3160 3152                          share_list = shp;
3161 3153                  else
3162 3154                          /* LINTED not used before set */
3163 3155                          shp_prev->shl_next = shp;
3164 3156                  shp_prev = shp;
3165 3157                  shp->shl_next = NULL;
3166 3158                  shp->shl_sh = sharedup(sh);
3167 3159                  if (shp->shl_sh == NULL)
3168 3160                          goto alloc_failed;
3169 3161          }
3170 3162  
3171 3163          if (res < 0)
3172 3164                  syslog(LOG_ERR, "%s: invalid at line %d\n",
3173 3165                      SHARETAB, c + 1);
3174 3166  
3175 3167          if (stat(SHARETAB, &st) != 0) {
3176 3168                  syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3177 3169                  (void) fclose(f);
3178 3170                  (void) rw_unlock(&sharetab_lock);
3179 3171                  return;
3180 3172          }
3181 3173  
3182 3174          last_sharetab_time = st.st_mtim;
3183 3175          (void) fclose(f);
3184 3176          (void) rw_unlock(&sharetab_lock);
3185 3177  
3186 3178          return;
3187 3179  
3188 3180  alloc_failed:
3189 3181  
3190 3182          syslog(LOG_ERR, "check_sharetab: no memory");
3191 3183          sh_free(share_list);
3192 3184          share_list = NULL;
3193 3185          (void) fclose(f);
3194 3186          (void) rw_unlock(&sharetab_lock);
3195 3187  }
3196 3188  
3197 3189  static void
3198 3190  sh_free(struct sh_list *shp)
3199 3191  {
3200 3192          struct sh_list *next;
3201 3193  
3202 3194          while (shp) {
3203 3195                  sharefree(shp->shl_sh);
3204 3196                  next = shp->shl_next;
3205 3197                  free(shp);
3206 3198                  shp = next;
3207 3199          }
3208 3200  }
3209 3201  
3210 3202  
3211 3203  /*
3212 3204   * Remove an entry from mounted list
3213 3205   */
3214 3206  static void
3215 3207  umount(struct svc_req *rqstp)
3216 3208  {
3217 3209          char *host, *path, *remove_path;
3218 3210          char rpath[MAXPATHLEN];
3219 3211          SVCXPRT *transp;
3220 3212          struct cln cln;
3221 3213  
3222 3214          transp = rqstp->rq_xprt;
3223 3215          path = NULL;
3224 3216          if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
3225 3217                  svcerr_decode(transp);
3226 3218                  return;
3227 3219          }
3228 3220  
3229 3221          cln_init(&cln, transp);
3230 3222  
3231 3223          errno = 0;
3232 3224          if (!svc_sendreply(transp, xdr_void, (char *)NULL))
3233 3225                  log_cant_reply_cln(&cln);
3234 3226  
3235 3227          host = cln_gethost(&cln);
3236 3228          if (host == NULL) {
3237 3229                  /*
3238 3230                   * Without the hostname we can't do audit or delete
3239 3231                   * this host from the mount entries.
3240 3232                   */
3241 3233                  svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3242 3234                  return;
3243 3235          }
3244 3236  
3245 3237          if (verbose)
3246 3238                  syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
3247 3239  
3248 3240          audit_mountd_umount(host, path);
3249 3241  
3250 3242          remove_path = rpath;    /* assume we will use the cannonical path */
3251 3243          if (realpath(path, rpath) == NULL) {
3252 3244                  if (verbose)
3253 3245                          syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
3254 3246                  remove_path = path;     /* use path provided instead */
3255 3247          }
3256 3248  
3257 3249          mntlist_delete(host, remove_path);      /* remove from mount list */
3258 3250  
3259 3251          cln_fini(&cln);
3260 3252  
3261 3253          svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3262 3254  }
3263 3255  
3264 3256  /*
3265 3257   * Remove all entries for one machine from mounted list
3266 3258   */
3267 3259  static void
3268 3260  umountall(struct svc_req *rqstp)
3269 3261  {
3270 3262          SVCXPRT *transp;
3271 3263          char *host;
3272 3264          struct cln cln;
3273 3265  
3274 3266          transp = rqstp->rq_xprt;
3275 3267          if (!svc_getargs(transp, xdr_void, NULL)) {
3276 3268                  svcerr_decode(transp);
3277 3269                  return;
3278 3270          }
3279 3271          /*
3280 3272           * We assume that this call is asynchronous and made via rpcbind
3281 3273           * callit routine.  Therefore return control immediately. The error
3282 3274           * causes rpcbind to remain silent, as opposed to every machine
3283 3275           * on the net blasting the requester with a response.
3284 3276           */
3285 3277          svcerr_systemerr(transp);
3286 3278  
3287 3279          cln_init(&cln, transp);
3288 3280  
3289 3281          host = cln_gethost(&cln);
3290 3282          if (host == NULL) {
3291 3283                  /* Can't do anything without the name of the client */
3292 3284                  return;
3293 3285          }
3294 3286  
3295 3287          /*
3296 3288           * Remove all hosts entries from mount list
3297 3289           */
3298 3290          mntlist_delete_all(host);
3299 3291  
3300 3292          if (verbose)
3301 3293                  syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
3302 3294  
3303 3295          cln_fini(&cln);
3304 3296  }
3305 3297  
3306 3298  void *
3307 3299  exmalloc(size_t size)
3308 3300  {
3309 3301          void *ret;
3310 3302  
3311 3303          if ((ret = malloc(size)) == NULL) {
3312 3304                  syslog(LOG_ERR, "Out of memory");
3313 3305                  exit(1);
3314 3306          }
3315 3307          return (ret);
3316 3308  }
3317 3309  
3318 3310  static tsol_tpent_t *
3319 3311  get_client_template(struct sockaddr *sock)
3320 3312  {
3321 3313          in_addr_t       v4client;
3322 3314          in6_addr_t      v6client;
3323 3315          char            v4_addr[INET_ADDRSTRLEN];
3324 3316          char            v6_addr[INET6_ADDRSTRLEN];
3325 3317          tsol_rhent_t    *rh;
3326 3318          tsol_tpent_t    *tp;
3327 3319  
3328 3320          switch (sock->sa_family) {
3329 3321          case AF_INET:
3330 3322                  v4client = ((struct sockaddr_in *)(void *)sock)->
3331 3323                      sin_addr.s_addr;
3332 3324                  if (inet_ntop(AF_INET, &v4client, v4_addr, INET_ADDRSTRLEN) ==
3333 3325                      NULL)
3334 3326                          return (NULL);
3335 3327                  rh = tsol_getrhbyaddr(v4_addr, sizeof (v4_addr), AF_INET);
3336 3328                  if (rh == NULL)
3337 3329                          return (NULL);
3338 3330                  tp = tsol_gettpbyname(rh->rh_template);
3339 3331                  tsol_freerhent(rh);
3340 3332                  return (tp);
3341 3333                  break;
3342 3334          case AF_INET6:
3343 3335                  v6client = ((struct sockaddr_in6 *)(void *)sock)->sin6_addr;
3344 3336                  if (inet_ntop(AF_INET6, &v6client, v6_addr, INET6_ADDRSTRLEN) ==
3345 3337                      NULL)
3346 3338                          return (NULL);
3347 3339                  rh = tsol_getrhbyaddr(v6_addr, sizeof (v6_addr), AF_INET6);
3348 3340                  if (rh == NULL)
3349 3341                          return (NULL);
3350 3342                  tp = tsol_gettpbyname(rh->rh_template);
3351 3343                  tsol_freerhent(rh);
3352 3344                  return (tp);
3353 3345                  break;
3354 3346          default:
3355 3347                  return (NULL);
3356 3348          }
3357 3349  }
  
    | 
      ↓ open down ↓ | 
    2852 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX