1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  * Copyright 2018 Nexenta Systems, Inc.
  28  * Copyright 2019 Joyent, Inc.
  29  */
  30 
  31 #include <sys/types.h>
  32 #include <sys/types32.h>
  33 #include <sys/param.h>
  34 #include <sys/systm.h>
  35 #include <rpc/types.h>
  36 #include <sys/vfs.h>
  37 #include <sys/siginfo.h>
  38 #include <sys/proc.h>             /* for exit() declaration */
  39 #include <sys/kmem.h>
  40 #include <sys/pathname.h>
  41 #include <sys/debug.h>
  42 #include <sys/vtrace.h>
  43 #include <sys/cmn_err.h>
  44 #include <sys/atomic.h>
  45 #include <sys/policy.h>
  46 
  47 #include <sharefs/sharefs.h>
  48 
  49 /*
  50  * A macro to avoid cut-and-paste errors on getting a string field
  51  * from user-land.
  52  */
  53 #define SHARETAB_COPYIN(field)                                          \
  54         if (copyinstr(STRUCT_FGETP(u_sh, sh_##field),                   \
  55             buf,                                                        \
  56             bufsz + 1,  /* Add one for extra NUL */                     \
  57             &len)) {                                                        \
  58                 error = EFAULT;                                         \
  59                 goto cleanup;                                           \
  60         }                                                               \
  61         /* Need to remove 1 because copyinstr() counts the NUL */       \
  62         len--;                                                          \
  63         sh->sh_##field = kmem_alloc(len + 1, KM_SLEEP);                      \
  64         bcopy(buf, sh->sh_##field, len);                             \
  65         sh->sh_##field[len] = '\0';                                  \
  66         shl.shl_##field = (int)len;                                     \
  67         sh->sh_size += shl.shl_##field;      /* Debug counting */
  68 
  69 #define SHARETAB_DELETE_FIELD(field)                                    \
  70         if (sh->sh_##field != NULL) {                                        \
  71                 kmem_free(sh->sh_##field,                            \
  72                     shl ? shl->shl_##field + 1 :                     \
  73                     strlen(sh->sh_##field) + 1);                     \
  74         }
  75 
  76 static zone_key_t sharetab_zone_key;
  77 
  78 /*
  79  * Take care of cleaning up a share.
  80  * If passed in a length array, use it to determine how much
  81  * space to clean up. Else, figure that out.
  82  */
  83 static void
  84 sharefree(share_t *sh, sharefs_lens_t *shl)
  85 {
  86         if (sh == NULL)
  87                 return;
  88 
  89         SHARETAB_DELETE_FIELD(path);
  90         SHARETAB_DELETE_FIELD(res);
  91         SHARETAB_DELETE_FIELD(fstype);
  92         SHARETAB_DELETE_FIELD(opts);
  93         SHARETAB_DELETE_FIELD(descr);
  94 
  95         kmem_free(sh, sizeof (*sh));
  96 }
  97 
  98 /*
  99  * If there is no error, then this function is responsible for
 100  * cleaning up the memory associated with the share argument.
 101  */
 102 static int
 103 sharefs_remove(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl)
 104 {
 105         int             iHash;
 106         sharetab_t      *sht;
 107         share_t         *s, *p;
 108         int             iPath;
 109 
 110         if (!sh)
 111                 return (ENOENT);
 112 
 113         rw_enter(&sg->sharetab_lock, RW_WRITER);
 114         for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) {
 115                 if (strcmp(sh->sh_fstype, sht->s_fstype) == 0)
 116                         break;
 117         }
 118 
 119         /*
 120          * There does not exist a fstype in memory which
 121          * matches the share passed in.
 122          */
 123         if (sht == NULL) {
 124                 rw_exit(&sg->sharetab_lock);
 125                 return (ENOENT);
 126         }
 127 
 128         iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path);
 129         iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
 130 
 131         /*
 132          * Now walk down the hash table and find the entry to free!
 133          */
 134         for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
 135             s != NULL; s = s->sh_next) {
 136                 /*
 137                  * We need exact matches.
 138                  */
 139                 if (strcmp(sh->sh_path, s->sh_path) == 0 &&
 140                     strlen(s->sh_path) == iPath) {
 141                         if (p != NULL)
 142                                 p->sh_next = s->sh_next;
 143                         else
 144                                 sht->s_buckets[iHash].ssh_sh = s->sh_next;
 145 
 146                         ASSERT(sht->s_buckets[iHash].ssh_count != 0);
 147                         atomic_dec_32(&sht->s_buckets[iHash].ssh_count);
 148                         atomic_dec_32(&sht->s_count);
 149                         atomic_dec_32(&sg->sharetab_count);
 150 
 151                         ASSERT(sg->sharetab_size >= s->sh_size);
 152                         sg->sharetab_size -= s->sh_size;
 153 
 154                         gethrestime(&sg->sharetab_mtime);
 155                         atomic_inc_32(&sg->sharetab_generation);
 156 
 157                         break;
 158                 }
 159 
 160                 p = s;
 161         }
 162 
 163         rw_exit(&sg->sharetab_lock);
 164 
 165         if (s == NULL)
 166                 return (ENOENT);
 167 
 168         s->sh_next = NULL;
 169         sharefree(s, NULL);
 170 
 171         /* We need to free the share for the caller */
 172         sharefree(sh, shl);
 173 
 174         return (0);
 175 }
 176 
 177 /*
 178  * The caller must have allocated memory for us to use.
 179  */
 180 static int
 181 sharefs_add(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl)
 182 {
 183         int             iHash;
 184         sharetab_t      *sht;
 185         share_t         *s, *p;
 186         int             iPath;
 187         int             n;
 188 
 189         if (sh == NULL)
 190                 return (ENOENT);
 191 
 192         /* We need to find the hash buckets for the fstype */
 193         rw_enter(&sg->sharetab_lock, RW_WRITER);
 194         for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) {
 195                 if (strcmp(sh->sh_fstype, sht->s_fstype) == 0)
 196                         break;
 197         }
 198 
 199         /* Did not exist, so allocate one and add it to the sharetab */
 200         if (sht == NULL) {
 201                 sht = kmem_zalloc(sizeof (*sht), KM_SLEEP);
 202                 n = strlen(sh->sh_fstype);
 203                 sht->s_fstype = kmem_zalloc(n + 1, KM_SLEEP);
 204                 (void) strncpy(sht->s_fstype, sh->sh_fstype, n);
 205 
 206                 sht->s_next = sg->sharefs_sharetab;
 207                 sg->sharefs_sharetab = sht;
 208         }
 209 
 210         /* Now we need to find where we have to add the entry */
 211         iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path);
 212         iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
 213 
 214         if (shl) {
 215                 sh->sh_size = shl->shl_path + shl->shl_res +
 216                     shl->shl_fstype + shl->shl_opts + shl->shl_descr;
 217         } else {
 218                 sh->sh_size = strlen(sh->sh_path) +
 219                     strlen(sh->sh_res) + strlen(sh->sh_fstype) +
 220                     strlen(sh->sh_opts) + strlen(sh->sh_descr);
 221         }
 222 
 223         /* We need to account for field seperators and the EOL */
 224         sh->sh_size += 5;
 225 
 226         /* Now walk down the hash table and add the new entry */
 227         for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
 228             s != NULL; s = s->sh_next) {
 229                 /*
 230                  * We need exact matches.
 231                  *
 232                  * We found a matching path. Either we have a
 233                  * duplicate path in a share command or we are
 234                  * being asked to replace an existing entry.
 235                  */
 236                 if (strcmp(sh->sh_path, s->sh_path) == 0 &&
 237                     strlen(s->sh_path) == iPath) {
 238                         if (p != NULL)
 239                                 p->sh_next = sh;
 240                         else
 241                                 sht->s_buckets[iHash].ssh_sh = sh;
 242 
 243                         sh->sh_next = s->sh_next;
 244 
 245                         ASSERT(sg->sharetab_size >= s->sh_size);
 246                         sg->sharetab_size -= s->sh_size;
 247                         sg->sharetab_size += sh->sh_size;
 248 
 249                         /* Get rid of the old node */
 250                         sharefree(s, NULL);
 251 
 252                         gethrestime(&sg->sharetab_mtime);
 253                         atomic_inc_32(&sg->sharetab_generation);
 254 
 255                         ASSERT(sht->s_buckets[iHash].ssh_count != 0);
 256                         rw_exit(&sg->sharetab_lock);
 257 
 258                         return (0);
 259                 }
 260 
 261                 p = s;
 262         }
 263 
 264         /*
 265          * Okay, we have gone through the entire hash chain and not
 266          * found a match. We just need to add this node.
 267          */
 268         sh->sh_next = sht->s_buckets[iHash].ssh_sh;
 269         sht->s_buckets[iHash].ssh_sh = sh;
 270         atomic_inc_32(&sht->s_buckets[iHash].ssh_count);
 271         atomic_inc_32(&sht->s_count);
 272         atomic_inc_32(&sg->sharetab_count);
 273         sg->sharetab_size += sh->sh_size;
 274 
 275         gethrestime(&sg->sharetab_mtime);
 276         atomic_inc_32(&sg->sharetab_generation);
 277 
 278         rw_exit(&sg->sharetab_lock);
 279 
 280         return (0);
 281 }
 282 
 283 /* ARGSUSED */
 284 static void *
 285 sharetab_zone_init(zoneid_t zoneid)
 286 {
 287         sharetab_globals_t *sg;
 288 
 289         sg = kmem_zalloc(sizeof (*sg), KM_SLEEP);
 290 
 291         rw_init(&sg->sharetab_lock, NULL, RW_DEFAULT, NULL);
 292         rw_init(&sg->sharefs_lock, NULL, RW_DEFAULT, NULL);
 293 
 294         sg->sharetab_size = 0;
 295         sg->sharetab_count = 0;
 296         sg->sharetab_generation = 1;
 297 
 298         gethrestime(&sg->sharetab_mtime);
 299         gethrestime(&sg->sharetab_snap_time);
 300 
 301         return (sg);
 302 }
 303 
 304 /* ARGSUSED */
 305 static void
 306 sharetab_zone_fini(zoneid_t zoneid, void *data)
 307 {
 308         sharetab_globals_t *sg = data;
 309 
 310         rw_destroy(&sg->sharefs_lock);
 311         rw_destroy(&sg->sharetab_lock);
 312 
 313         /* ALL of the allocated things must be cleaned before we free sg. */
 314         while (sg->sharefs_sharetab != NULL) {
 315                 int i;
 316                 sharetab_t *freeing = sg->sharefs_sharetab;
 317 
 318                 sg->sharefs_sharetab = freeing->s_next;
 319                 kmem_free(freeing->s_fstype, strlen(freeing->s_fstype) + 1);
 320                 for (i = 0; i < PKP_HASH_SIZE; i++) {
 321                         sharefs_hash_head_t *bucket;
 322 
 323                         bucket = &(freeing->s_buckets[i]);
 324                         while (bucket->ssh_sh != NULL) {
 325                                 share_t *share = bucket->ssh_sh;
 326 
 327                                 bucket->ssh_sh = share->sh_next;
 328                                 sharefree(share, NULL);
 329                         }
 330                 }
 331                 kmem_free(freeing, sizeof (*freeing));
 332         }
 333 
 334         kmem_free(sg, sizeof (*sg));
 335 }
 336 
 337 void
 338 sharefs_sharetab_init(void)
 339 {
 340         zone_key_create(&sharetab_zone_key, sharetab_zone_init,
 341             NULL, sharetab_zone_fini);
 342 }
 343 
 344 sharetab_globals_t *
 345 sharetab_get_globals(zone_t *zone)
 346 {
 347         return (zone_getspecific(sharetab_zone_key, zone));
 348 }
 349 
 350 int
 351 sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
 352 {
 353         int             error = 0;
 354         size_t          len;
 355         size_t          bufsz;
 356         share_t         *sh;
 357         sharefs_lens_t  shl;
 358         model_t         model;
 359         char            *buf = NULL;
 360         sharetab_globals_t *sg = sharetab_get_globals(curzone);
 361 
 362         STRUCT_DECL(share, u_sh);
 363 
 364         bufsz = iMaxLen;
 365 
 366         /*
 367          * Before we do anything, lets make sure we have
 368          * a sharetab in memory if we need one.
 369          */
 370         rw_enter(&sg->sharetab_lock, RW_READER);
 371         switch (opcode) {
 372         case SHAREFS_REMOVE:
 373         case SHAREFS_REPLACE:
 374                 if (!sg->sharefs_sharetab) {
 375                         rw_exit(&sg->sharetab_lock);
 376                         return (set_errno(ENOENT));
 377                 }
 378                 break;
 379         case SHAREFS_ADD:
 380         default:
 381                 break;
 382         }
 383         rw_exit(&sg->sharetab_lock);
 384 
 385         model = get_udatamodel();
 386 
 387         /*
 388          * Initialize the data pointers.
 389          */
 390         STRUCT_INIT(u_sh, model);
 391         if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh)))
 392                 return (set_errno(EFAULT));
 393 
 394         /* Get the share */
 395         sh = kmem_zalloc(sizeof (share_t), KM_SLEEP);
 396 
 397         /* Get some storage for copying in the strings */
 398         buf = kmem_zalloc(bufsz + 1, KM_SLEEP);
 399         bzero(&shl, sizeof (sharefs_lens_t));
 400 
 401         /* Only grab these two until we know what we want */
 402         SHARETAB_COPYIN(path);
 403         SHARETAB_COPYIN(fstype);
 404 
 405         switch (opcode) {
 406         case SHAREFS_ADD:
 407         case SHAREFS_REPLACE:
 408                 SHARETAB_COPYIN(res);
 409                 SHARETAB_COPYIN(opts);
 410                 SHARETAB_COPYIN(descr);
 411                 error = sharefs_add(sg, sh, &shl);
 412                 break;
 413         case SHAREFS_REMOVE:
 414                 error = sharefs_remove(sg, sh, &shl);
 415                 break;
 416         default:
 417                 error = EINVAL;
 418                 break;
 419         }
 420 
 421 cleanup:
 422         /*
 423          * If there is no error, then we have stashed the structure
 424          * away in the sharetab hash table or have deleted it.
 425          *
 426          * Either way, the only reason to blow away the data is if
 427          * there was an error.
 428          */
 429         if (error != 0)
 430                 sharefree(sh, &shl);
 431 
 432         if (buf != NULL)
 433                 kmem_free(buf, bufsz + 1);
 434 
 435         return (error != 0 ? set_errno(error) : 0);
 436 }
 437 
 438 int
 439 sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
 440 {
 441         /*
 442          * If we're in the global zone PRIV_SYS_CONFIG gives us the
 443          * priviledges needed to act on sharetab. However if we're in
 444          * a non-global zone PRIV_SYS_CONFIG is not allowed. To work
 445          * around this issue PRIV_SYS_NFS is used in this case.
 446          *
 447          * TODO: This basically overloads the definition/use of
 448          * PRIV_SYS_NFS to work around the limitation of PRIV_SYS_CONFIG
 449          * in a zone. Solaris 11 solved this by implementing a PRIV_SYS_SHARE
 450          * we should do the same and replace the use of PRIV_SYS_NFS here and
 451          * in zfs_secpolicy_share.
 452          */
 453         if (INGLOBALZONE(curproc)) {
 454                 if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
 455                         return (set_errno(EPERM));
 456         } else {
 457                 /* behave like zfs_secpolicy_share() */
 458                 if (secpolicy_nfs(CRED()) != 0)
 459                         return (set_errno(EPERM));
 460 
 461         }
 462         return (sharefs_impl(opcode, sh_in, iMaxLen));
 463 }