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  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 #include <sys/types.h>
  26 #include <sys/param.h>
  27 #include <sys/cmn_err.h>
  28 #include <sys/systm.h>
  29 #include <sys/cred.h>
  30 #include <sys/modctl.h>
  31 #include <sys/vfs.h>
  32 #include <sys/vnode.h>
  33 #include <sys/tiuser.h>
  34 #include <sys/kmem.h>
  35 #include <sys/pathname.h>
  36 #include <sys/zone.h>
  37 #include <sys/tsol/label.h>
  38 #include <sys/tsol/tnet.h>
  39 #include <sys/fs/lofs_node.h>
  40 #include <sys/fs/zfs.h>
  41 #include <sys/dsl_prop.h>
  42 #include <inet/ip6.h>
  43 #include <rpc/auth.h>
  44 #include <rpc/clnt.h>
  45 #include <nfs/nfs.h>
  46 #include <nfs/nfs4.h>
  47 #include <nfs/nfs_clnt.h>
  48 
  49 
  50 int sys_labeling = 0;                   /* the default is "off" */
  51 
  52 static kmem_cache_t *tslabel_cache;
  53 ts_label_t *l_admin_low;
  54 ts_label_t *l_admin_high;
  55 
  56 uint32_t default_doi = DEFAULT_DOI;
  57 
  58 /*
  59  * Initialize labels infrastructure.
  60  * This is called during startup() time (before vfs_mntroot) by thread_init().
  61  * It has to be called early so that the is_system_labeled() function returns
  62  * the right value when called by the networking code on a diskless boot.
  63  */
  64 void
  65 label_init(void)
  66 {
  67         bslabel_t label;
  68 
  69         /*
  70          * sys_labeling will default to "off" unless it is overridden
  71          * in /etc/system.
  72          */
  73 
  74         tslabel_cache = kmem_cache_create("tslabel_cache", sizeof (ts_label_t),
  75             0, NULL, NULL, NULL, NULL, NULL, 0);
  76         bsllow(&label);
  77         l_admin_low = labelalloc(&label, default_doi, KM_SLEEP);
  78         bslhigh(&label);
  79         l_admin_high = labelalloc(&label, default_doi, KM_SLEEP);
  80 }
  81 
  82 /*
  83  * Allocate new ts_label_t.
  84  */
  85 ts_label_t *
  86 labelalloc(const bslabel_t *val, uint32_t doi, int flag)
  87 {
  88         ts_label_t *lab = kmem_cache_alloc(tslabel_cache, flag);
  89 
  90         if (lab != NULL) {
  91                 lab->tsl_ref = 1;
  92                 lab->tsl_doi = doi;
  93                 lab->tsl_flags = 0;
  94                 if (val == NULL)
  95                         bzero(&lab->tsl_label, sizeof (bslabel_t));
  96                 else
  97                         bcopy(val, &lab->tsl_label,  sizeof (bslabel_t));
  98         }
  99         return (lab);
 100 }
 101 
 102 /*
 103  * Duplicate an existing ts_label_t to a new one, with only
 104  * the current reference.
 105  */
 106 ts_label_t *
 107 labeldup(const ts_label_t *val, int flag)
 108 {
 109         ts_label_t *lab = kmem_cache_alloc(tslabel_cache, flag);
 110 
 111         if (lab != NULL) {
 112                 bcopy(val, lab, sizeof (ts_label_t));
 113                 lab->tsl_ref = 1;
 114         }
 115         return (lab);
 116 }
 117 
 118 /*
 119  * Put a hold on a label structure.
 120  */
 121 void
 122 label_hold(ts_label_t *lab)
 123 {
 124         atomic_inc_32(&lab->tsl_ref);
 125 }
 126 
 127 /*
 128  * Release previous hold on a label structure.  Free it if refcnt == 0.
 129  */
 130 void
 131 label_rele(ts_label_t *lab)
 132 {
 133         if (atomic_dec_32_nv(&lab->tsl_ref) == 0)
 134                 kmem_cache_free(tslabel_cache, lab);
 135 }
 136 
 137 bslabel_t *
 138 label2bslabel(ts_label_t *lab)
 139 {
 140         return (&lab->tsl_label);
 141 }
 142 
 143 
 144 uint32_t
 145 label2doi(ts_label_t *lab)
 146 {
 147         return (lab->tsl_doi);
 148 }
 149 
 150 /*
 151  * Compare labels. Return 1 if equal, 0 otherwise.
 152  */
 153 boolean_t
 154 label_equal(const ts_label_t *l1, const ts_label_t *l2)
 155 {
 156         return ((l1->tsl_doi == l2->tsl_doi) &&
 157             blequal(&l1->tsl_label, &l2->tsl_label));
 158 }
 159 
 160 /*
 161  * There's no protocol today to obtain the label from the server.
 162  * So we rely on conventions: zones, zone names, and zone paths
 163  * must match across TX servers and their TX clients.  Now use
 164  * the exported name to find the equivalent local zone and its
 165  * label.  Caller is responsible for doing a label_rele of the
 166  * returned ts_label.
 167  */
 168 ts_label_t *
 169 getflabel_cipso(vfs_t *vfsp)
 170 {
 171         zone_t  *reszone;
 172         zone_t  *new_reszone;
 173         char    *nfspath, *respath;
 174         refstr_t        *resource_ref;
 175         boolean_t       treat_abs = B_FALSE;
 176 
 177         if (vfsp->vfs_resource == NULL)
 178                 return (NULL);                  /* error */
 179         resource_ref = vfs_getresource(vfsp);
 180 
 181         nfspath = (char *)refstr_value(resource_ref);
 182         respath = strchr(nfspath, ':');         /* skip server name */
 183         if (respath)
 184                 respath++;                      /* skip over ":" */
 185         if (*respath != '/') {
 186                 /* treat path as absolute but it doesn't have leading '/' */
 187                 treat_abs = B_TRUE;
 188         }
 189 
 190         reszone = zone_find_by_any_path(respath, treat_abs);
 191         if (reszone == global_zone) {
 192                 refstr_rele(resource_ref);
 193                 label_hold(l_admin_low);
 194                 zone_rele(reszone);
 195                 return (l_admin_low);
 196         }
 197 
 198         /*
 199          * Skip over zonepath (not including "root"), e.g. /zone/internal
 200          */
 201         respath += reszone->zone_rootpathlen - 7;
 202         if (treat_abs)
 203                 respath--;                      /* no leading '/' to skip */
 204         if (strncmp(respath, "/root/", 6) == 0) {
 205                 /* Check if we now have something like "/zone/public/" */
 206 
 207                 respath += 5;                   /* skip "/root" first */
 208                 new_reszone = zone_find_by_any_path(respath, B_FALSE);
 209                 if (new_reszone != global_zone) {
 210                         zone_rele(reszone);
 211                         reszone = new_reszone;
 212                 } else {
 213                         zone_rele(new_reszone);
 214                 }
 215         }
 216 
 217         refstr_rele(resource_ref);
 218         label_hold(reszone->zone_slabel);
 219         zone_rele(reszone);
 220 
 221         return (reszone->zone_slabel);
 222 }
 223 
 224 /*
 225  * Get the label if any of a zfs filesystem.  Get the dataset, then
 226  * get its mlslabel property, convert as needed, and return it.  If
 227  * there's no mlslabel or it is the default one, return NULL.
 228  */
 229 static ts_label_t *
 230 getflabel_zfs(vfs_t *vfsp)
 231 {
 232         int             error;
 233         ts_label_t      *tsl = NULL;
 234         refstr_t        *resource_ref;
 235         bslabel_t       ds_sl;
 236         char            ds_hexsl[MAXNAMELEN];
 237         const char      *osname;
 238 
 239         resource_ref = vfs_getresource(vfsp);
 240         osname = refstr_value(resource_ref);
 241 
 242         error = dsl_prop_get(osname, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
 243             1, sizeof (ds_hexsl), &ds_hexsl, NULL);
 244         refstr_rele(resource_ref);
 245 
 246         if ((error) || (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) == 0))
 247                 return (NULL);
 248         if (hexstr_to_label(ds_hexsl, &ds_sl) != 0)
 249                 return (NULL);
 250 
 251         tsl = labelalloc(&ds_sl, default_doi, KM_SLEEP);
 252         return (tsl);
 253 }
 254 
 255 static ts_label_t *
 256 getflabel_nfs(vfs_t *vfsp)
 257 {
 258         bslabel_t       *server_sl;
 259         ts_label_t      *srv_label;
 260         tsol_tpc_t      *tp;
 261         int             addr_type;
 262         void            *ipaddr;
 263         struct servinfo *svp;
 264         struct netbuf   *addr;
 265         struct knetconfig *knconf;
 266         mntinfo_t       *mi;
 267 
 268         mi = VFTOMI(vfsp);
 269         svp = mi->mi_curr_serv;
 270         addr = &svp->sv_addr;
 271         knconf = svp->sv_knconf;
 272 
 273         if (strcmp(knconf->knc_protofmly, NC_INET) == 0) {
 274                 addr_type = IPV4_VERSION;
 275                 /* LINTED: following cast to ipaddr is OK */
 276                 ipaddr = &((struct sockaddr_in *)addr->buf)->sin_addr;
 277         } else if (strcmp(knconf->knc_protofmly, NC_INET6) == 0) {
 278                 addr_type = IPV6_VERSION;
 279                 /* LINTED: following cast to ipaddr is OK */
 280                 ipaddr = &((struct sockaddr_in6 *)addr->buf)->sin6_addr;
 281         } else {
 282                 goto errout;
 283         }
 284 
 285         tp = find_tpc(ipaddr, addr_type, B_FALSE);
 286         if (tp == NULL)
 287                 goto errout;
 288 
 289         if (tp->tpc_tp.host_type == SUN_CIPSO) {
 290                 TPC_RELE(tp);
 291                 return (getflabel_cipso(vfsp));
 292         }
 293 
 294         if (tp->tpc_tp.host_type != UNLABELED)
 295                 goto errout;
 296 
 297         server_sl = &tp->tpc_tp.tp_def_label;
 298         srv_label = labelalloc(server_sl, default_doi, KM_SLEEP);
 299 
 300         TPC_RELE(tp);
 301 
 302         return (srv_label);
 303 
 304 errout:
 305         return (NULL);
 306 }
 307 
 308 /*
 309  * getflabel -
 310  *
 311  * Return pointer to the ts_label associated with the specified file,
 312  * or returns NULL if error occurs.  Caller is responsible for doing
 313  * a label_rele of the ts_label.
 314  */
 315 ts_label_t *
 316 getflabel(vnode_t *vp)
 317 {
 318         vfs_t           *vfsp, *rvfsp;
 319         vnode_t         *rvp, *rvp2;
 320         zone_t          *zone;
 321         ts_label_t      *zl;
 322         int             err;
 323         boolean_t       vfs_is_held = B_FALSE;
 324         char            vpath[MAXPATHLEN];
 325 
 326         ASSERT(vp);
 327         vfsp = vp->v_vfsp;
 328         if (vfsp == NULL)
 329                 return (NULL);
 330 
 331         rvp = vp;
 332 
 333         /*
 334          * Traverse lofs mounts and fattach'es to get the real vnode
 335          */
 336         if (VOP_REALVP(rvp, &rvp2, NULL) == 0)
 337                 rvp = rvp2;
 338 
 339         rvfsp = rvp->v_vfsp;
 340 
 341         /* rvp/rvfsp now represent the real vnode/vfs we will be using */
 342 
 343         /* Go elsewhere to handle all nfs files. */
 344         if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "nfs", 3) == 0)
 345                 return (getflabel_nfs(rvfsp));
 346 
 347         /*
 348          * Fast path, for objects in a labeled zone: everything except
 349          * for lofs/nfs will be just the label of that zone.
 350          */
 351         if ((rvfsp->vfs_zone != NULL) && (rvfsp->vfs_zone != global_zone)) {
 352                 if ((strcmp(vfssw[rvfsp->vfs_fstype].vsw_name,
 353                     "lofs") != 0)) {
 354                         zone = rvfsp->vfs_zone;
 355                         zone_hold(zone);
 356                         goto zone_out;          /* return this label */
 357                 }
 358         }
 359 
 360         /*
 361          * Get the vnode path -- it may be missing or weird for some
 362          * cases, like devices.  In those cases use the label of the
 363          * current zone.
 364          */
 365         err = vnodetopath(rootdir, rvp, vpath, sizeof (vpath), kcred);
 366         if ((err != 0) || (*vpath != '/')) {
 367                 zone = curproc->p_zone;
 368                 zone_hold(zone);
 369                 goto zone_out;
 370         }
 371 
 372         /*
 373          * For zfs filesystem, return the explicit label property if a
 374          * meaningful one exists.
 375          */
 376         if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "zfs", 3) == 0) {
 377                 ts_label_t *tsl;
 378 
 379                 tsl = getflabel_zfs(rvfsp);
 380 
 381                 /* if label found, return it, otherwise continue... */
 382                 if (tsl != NULL)
 383                         return (tsl);
 384         }
 385 
 386         /*
 387          * If a mountpoint exists, hold the vfs while we reference it.
 388          * Otherwise if mountpoint is NULL it should not be held (e.g.,
 389          * a hold/release on spec_vfs would result in an attempted free
 390          * and panic.)
 391          */
 392         if (vfsp->vfs_mntpt != NULL) {
 393                 VFS_HOLD(vfsp);
 394                 vfs_is_held = B_TRUE;
 395         }
 396 
 397         zone = zone_find_by_any_path(vpath, B_FALSE);
 398 
 399         /*
 400          * If the vnode source zone is properly set to a non-global zone, or
 401          * any zone if the mount is R/W, then use the label of that zone.
 402          */
 403         if ((zone != global_zone) || ((vfsp->vfs_flag & VFS_RDONLY) != 0))
 404                 goto zone_out;          /* return this label */
 405 
 406         /*
 407          * Otherwise, if we're not in the global zone, use the label of
 408          * our zone.
 409          */
 410         if ((zone = curproc->p_zone) != global_zone) {
 411                 zone_hold(zone);
 412                 goto zone_out;          /* return this label */
 413         }
 414 
 415         /*
 416          * We're in the global zone and the mount is R/W ... so the file
 417          * may actually be in the global zone -- or in the root of any zone.
 418          * Always build our own path for the file, to be sure it's simplified
 419          * (i.e., no ".", "..", "//", and so on).
 420          */
 421 
 422         zone_rele(zone);
 423         zone = zone_find_by_any_path(vpath, B_FALSE);
 424 
 425 zone_out:
 426         if ((curproc->p_zone == global_zone) && (zone == global_zone)) {
 427                 vfs_t           *nvfs;
 428                 boolean_t       exported = B_FALSE;
 429                 refstr_t        *mntpt_ref;
 430                 char            *mntpt;
 431 
 432                 /*
 433                  * File is in the global zone - check whether it's admin_high.
 434                  * If it's in a filesys that was exported from the global zone,
 435                  * it's admin_low by definition.  Otherwise, if it's in a
 436                  * filesys that's NOT exported to any zone, it's admin_high.
 437                  *
 438                  * And for these files if there wasn't a valid mount resource,
 439                  * the file must be admin_high (not exported, probably a global
 440                  * zone device).
 441                  */
 442                 if (!vfs_is_held)
 443                         goto out_high;
 444 
 445                 mntpt_ref = vfs_getmntpoint(vfsp);
 446                 mntpt = (char *)refstr_value(mntpt_ref);
 447 
 448                 if ((mntpt != NULL) && (*mntpt == '/')) {
 449                         zone_t  *to_zone;
 450 
 451                         to_zone = zone_find_by_any_path(mntpt, B_FALSE);
 452                         zone_rele(to_zone);
 453                         if (to_zone != global_zone) {
 454                                 /* force admin_low */
 455                                 exported = B_TRUE;
 456                         }
 457                 }
 458                 if (mntpt_ref)
 459                         refstr_rele(mntpt_ref);
 460 
 461                 if (!exported) {
 462                         size_t  plen = strlen(vpath);
 463 
 464                         vfs_list_read_lock();
 465                         nvfs = vfsp->vfs_next;
 466                         while (nvfs != vfsp) {
 467                                 const char      *rstr;
 468                                 size_t          rlen = 0;
 469 
 470                                 /*
 471                                  * Skip checking this vfs if it's not lofs
 472                                  * (the only way to export from the global
 473                                  * zone to a zone).
 474                                  */
 475                                 if (strncmp(vfssw[nvfs->vfs_fstype].vsw_name,
 476                                     "lofs", 4) != 0) {
 477                                         nvfs = nvfs->vfs_next;
 478                                         continue;
 479                                 }
 480 
 481                                 rstr = refstr_value(nvfs->vfs_resource);
 482                                 if (rstr != NULL)
 483                                         rlen = strlen(rstr);
 484 
 485                                 /*
 486                                  * Check for a match: does this vfs correspond
 487                                  * to our global zone file path?  I.e., check
 488                                  * if the resource string of this vfs is a
 489                                  * prefix of our path.
 490                                  */
 491                                 if ((rlen > 0) && (rlen <= plen) &&
 492                                     (strncmp(rstr, vpath, rlen) == 0) &&
 493                                     (vpath[rlen] == '/' ||
 494                                     vpath[rlen] == '\0')) {
 495                                         /* force admin_low */
 496                                         exported = B_TRUE;
 497                                         break;
 498                                 }
 499                                 nvfs = nvfs->vfs_next;
 500                         }
 501                         vfs_list_unlock();
 502                 }
 503 
 504                 if (!exported)
 505                         goto out_high;
 506         }
 507 
 508         if (vfs_is_held)
 509                 VFS_RELE(vfsp);
 510 
 511         /*
 512          * Now that we have the "home" zone for the file, return the slabel
 513          * of that zone.
 514          */
 515         zl = zone->zone_slabel;
 516         label_hold(zl);
 517         zone_rele(zone);
 518         return (zl);
 519 
 520 out_high:
 521         if (vfs_is_held)
 522                 VFS_RELE(vfsp);
 523 
 524         label_hold(l_admin_high);
 525         zone_rele(zone);
 526         return (l_admin_high);
 527 }
 528 
 529 static int
 530 cgetlabel(bslabel_t *label_p, vnode_t *vp)
 531 {
 532         ts_label_t      *tsl;
 533         int             error = 0;
 534 
 535         if ((tsl = getflabel(vp)) == NULL)
 536                 return (EIO);
 537 
 538         if (copyout((caddr_t)label2bslabel(tsl), (caddr_t)label_p,
 539             sizeof (*(label_p))) != 0)
 540                 error = EFAULT;
 541 
 542         label_rele(tsl);
 543         return (error);
 544 }
 545 
 546 /*
 547  * fgetlabel(2TSOL) - get file label
 548  * getlabel(2TSOL) - get file label
 549  */
 550 int
 551 getlabel(const char *path, bslabel_t *label_p)
 552 {
 553         struct          vnode   *vp;
 554         char            *spath;
 555         int             error;
 556 
 557         /* Sanity check arguments */
 558         if (path == NULL)
 559                 return (set_errno(EINVAL));
 560 
 561         spath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
 562         if ((error = copyinstr(path, spath, MAXPATHLEN, NULL)) != 0) {
 563                 kmem_free(spath, MAXPATHLEN);
 564                 return (set_errno(error));
 565         }
 566 
 567         if (error = lookupname(spath, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp)) {
 568                 kmem_free(spath, MAXPATHLEN);
 569                 return (set_errno(error));
 570         }
 571         kmem_free(spath, MAXPATHLEN);
 572 
 573         error = cgetlabel(label_p, vp);
 574 
 575         VN_RELE(vp);
 576         if (error != 0)
 577                 return (set_errno(error));
 578         else
 579                 return (0);
 580 }
 581 
 582 int
 583 fgetlabel(int fd, bslabel_t *label_p)
 584 {
 585         file_t          *fp;
 586         int             error;
 587 
 588         if ((fp = getf(fd)) == NULL)
 589                 return (set_errno(EBADF));
 590 
 591         error = cgetlabel(label_p, fp->f_vnode);
 592         releasef(fd);
 593 
 594         if (error != 0)
 595                 return (set_errno(error));
 596         else
 597                 return (0);
 598 }