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_add_32(&lab->tsl_ref, 1); 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_add_32_nv(&lab->tsl_ref, -1) == 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 boolean_t vfs_is_held = B_FALSE; 323 char vpath[MAXPATHLEN]; 324 325 ASSERT(vp); 326 vfsp = vp->v_vfsp; 327 if (vfsp == NULL) 328 return (NULL); 329 330 rvp = vp; 331 332 /* 333 * Traverse lofs mounts and fattach'es to get the real vnode 334 */ 335 if (VOP_REALVP(rvp, &rvp2, NULL) == 0) 336 rvp = rvp2; 337 338 rvfsp = rvp->v_vfsp; 339 340 /* rvp/rvfsp now represent the real vnode/vfs we will be using */ 341 342 /* Go elsewhere to handle all nfs files. */ 343 if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "nfs", 3) == 0) 344 return (getflabel_nfs(rvfsp)); 345 346 /* 347 * Fast path, for objects in a labeled zone: everything except 348 * for lofs/nfs will be just the label of that zone. 349 */ 350 if ((rvfsp->vfs_zone != NULL) && (rvfsp->vfs_zone != global_zone)) { 351 if ((strcmp(vfssw[rvfsp->vfs_fstype].vsw_name, 352 "lofs") != 0)) { 353 zone = rvfsp->vfs_zone; 354 zone_hold(zone); 355 goto zone_out; /* return this label */ 356 } 357 } 358 359 if (vnodetopath(rootdir, rvp, vpath, sizeof (vpath), kcred) != 0) { 360 return (NULL); 361 } 362 363 /* 364 * Sanity check - vpath may be weird for some cases, like devices. 365 */ 366 if (*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 } 599