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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/param.h> 28 #include <sys/cmn_err.h> 29 #include <sys/systm.h> 30 #include <sys/cred.h> 31 #include <sys/modctl.h> 32 #include <sys/vfs.h> 33 #include <sys/vnode.h> 34 #include <sys/tiuser.h> 35 #include <sys/kmem.h> 36 #include <sys/pathname.h> 37 #include <sys/zone.h> 38 #include <sys/tsol/label.h> 39 #include <sys/tsol/tnet.h> 40 #include <sys/fs/lofs_node.h> 41 #include <inet/ip6.h> 42 #include <rpc/auth.h> 43 #include <rpc/clnt.h> 44 #include <nfs/nfs.h> 45 #include <nfs/nfs4.h> 46 #include <nfs/nfs_clnt.h> 47 #include <sys/dnlc.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 static ts_label_t * 225 getflabel_nfs(vfs_t *vfsp) 226 { 227 bslabel_t *server_sl; 228 ts_label_t *srv_label; 229 tsol_tpc_t *tp; 230 int addr_type; 231 void *ipaddr; 232 struct servinfo *svp; 233 struct netbuf *addr; 234 struct knetconfig *knconf; 235 mntinfo_t *mi; 236 237 mi = VFTOMI(vfsp); 238 svp = mi->mi_curr_serv; 239 addr = &svp->sv_addr; 240 knconf = svp->sv_knconf; 241 242 if (strcmp(knconf->knc_protofmly, NC_INET) == 0) { 243 addr_type = IPV4_VERSION; 244 /* LINTED: following cast to ipaddr is OK */ 245 ipaddr = &((struct sockaddr_in *)addr->buf)->sin_addr; 246 } else if (strcmp(knconf->knc_protofmly, NC_INET6) == 0) { 247 addr_type = IPV6_VERSION; 248 /* LINTED: following cast to ipaddr is OK */ 249 ipaddr = &((struct sockaddr_in6 *)addr->buf)->sin6_addr; 250 } else { 251 goto errout; 252 } 253 254 tp = find_tpc(ipaddr, addr_type, B_FALSE); 255 if (tp == NULL) 256 goto errout; 257 258 if (tp->tpc_tp.host_type == SUN_CIPSO) { 259 TPC_RELE(tp); 260 return (getflabel_cipso(vfsp)); 261 } 262 263 if (tp->tpc_tp.host_type != UNLABELED) 264 goto errout; 265 266 server_sl = &tp->tpc_tp.tp_def_label; 267 srv_label = labelalloc(server_sl, default_doi, KM_SLEEP); 268 269 TPC_RELE(tp); 270 271 return (srv_label); 272 273 errout: 274 return (NULL); 275 } 276 277 /* 278 * getflabel - 279 * 280 * Return pointer to the ts_label associated with the specified file, 281 * or returns NULL if error occurs. Caller is responsible for doing 282 * a label_rele of the ts_label. 283 */ 284 ts_label_t * 285 getflabel(vnode_t *vp) 286 { 287 vfs_t *vfsp, *rvfsp; 288 vnode_t *rvp, *rvp2; 289 zone_t *zone; 290 ts_label_t *zl; 291 boolean_t vfs_is_held = B_FALSE; 292 char vpath[MAXPATHLEN]; 293 294 ASSERT(vp); 295 vfsp = vp->v_vfsp; 296 if (vfsp == NULL) 297 return (NULL); 298 299 rvp = vp; 300 301 /* 302 * Traverse lofs mounts and fattach'es to get the real vnode 303 */ 304 if (VOP_REALVP(rvp, &rvp2, NULL) == 0) 305 rvp = rvp2; 306 307 rvfsp = rvp->v_vfsp; 308 309 /* rvp/rvfsp now represent the real vnode/vfs we will be using */ 310 311 /* Go elsewhere to handle all nfs files. */ 312 if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "nfs", 3) == 0) 313 return (getflabel_nfs(rvfsp)); 314 315 /* 316 * Fast path, for objects in a labeled zone: everything except 317 * for lofs/nfs will be just the label of that zone. 318 */ 319 if ((rvfsp->vfs_zone != NULL) && (rvfsp->vfs_zone != global_zone)) { 320 if ((strcmp(vfssw[rvfsp->vfs_fstype].vsw_name, 321 "lofs") != 0)) { 322 zone = rvfsp->vfs_zone; 323 zone_hold(zone); 324 goto zone_out; /* return this label */ 325 } 326 } 327 328 if (vnodetopath(rootdir, rvp, vpath, sizeof (vpath), kcred) != 0) { 329 return (NULL); 330 } 331 332 /* 333 * Sanity check - vpath may be weird for some cases, like devices. 334 */ 335 if (*vpath != '/') { 336 zone = curproc->p_zone; 337 zone_hold(zone); 338 goto zone_out; 339 } 340 341 /* 342 * If a mountpoint exists, hold the vfs while we reference it. 343 * Otherwise if mountpoint is NULL it should not be held (e.g., 344 * a hold/release on spec_vfs would result in an attempted free 345 * and panic.) 346 */ 347 if (vfsp->vfs_mntpt != NULL) { 348 VFS_HOLD(vfsp); 349 vfs_is_held = B_TRUE; 350 } 351 352 zone = zone_find_by_any_path(vpath, B_FALSE); 353 354 /* 355 * If the vnode source zone is properly set to a non-global zone, or 356 * any zone if the mount is R/W, then use the label of that zone. 357 */ 358 if ((zone != global_zone) || ((vfsp->vfs_flag & VFS_RDONLY) != 0)) 359 goto zone_out; /* return this label */ 360 361 /* 362 * Otherwise, if we're not in the global zone, use the label of 363 * our zone. 364 */ 365 if ((zone = curproc->p_zone) != global_zone) { 366 zone_hold(zone); 367 goto zone_out; /* return this label */ 368 } 369 370 /* 371 * We're in the global zone and the mount is R/W ... so the file 372 * may actually be in the global zone -- or in the root of any zone. 373 * Always build our own path for the file, to be sure it's simplified 374 * (i.e., no ".", "..", "//", and so on). 375 */ 376 377 zone_rele(zone); 378 zone = zone_find_by_any_path(vpath, B_FALSE); 379 380 zone_out: 381 if ((curproc->p_zone == global_zone) && (zone == global_zone)) { 382 vfs_t *nvfs; 383 boolean_t exported = B_FALSE; 384 refstr_t *mntpt_ref; 385 char *mntpt; 386 387 /* 388 * File is in the global zone - check whether it's admin_high. 389 * If it's in a filesys that was exported from the global zone, 390 * it's admin_low by definition. Otherwise, if it's in a 391 * filesys that's NOT exported to any zone, it's admin_high. 392 * 393 * And for these files if there wasn't a valid mount resource, 394 * the file must be admin_high (not exported, probably a global 395 * zone device). 396 */ 397 if (!vfs_is_held) 398 goto out_high; 399 400 mntpt_ref = vfs_getmntpoint(vfsp); 401 mntpt = (char *)refstr_value(mntpt_ref); 402 403 if ((mntpt != NULL) && (*mntpt == '/')) { 404 zone_t *to_zone; 405 406 to_zone = zone_find_by_any_path(mntpt, B_FALSE); 407 zone_rele(to_zone); 408 if (to_zone != global_zone) { 409 /* force admin_low */ 410 exported = B_TRUE; 411 } 412 } 413 if (mntpt_ref) 414 refstr_rele(mntpt_ref); 415 416 if (!exported) { 417 size_t plen = strlen(vpath); 418 419 vfs_list_read_lock(); 420 nvfs = vfsp->vfs_next; 421 while (nvfs != vfsp) { 422 const char *rstr; 423 size_t rlen = 0; 424 425 rstr = refstr_value(nvfs->vfs_resource); 426 if (rstr != NULL) 427 rlen = strlen(rstr); 428 429 /* 430 * Check for a match: does this vfs correspond 431 * to our global zone file path? I.e., check 432 * if the resource string of this vfs is a 433 * prefix of our path. 434 */ 435 if ((rlen > 0) && (rlen <= plen) && 436 (strncmp(rstr, vpath, rlen) == 0) && 437 (vpath[rlen] == '/' || 438 vpath[rlen] == '\0')) { 439 /* force admin_low */ 440 exported = B_TRUE; 441 break; 442 } 443 nvfs = nvfs->vfs_next; 444 } 445 vfs_list_unlock(); 446 } 447 448 if (!exported) 449 goto out_high; 450 } 451 452 if (vfs_is_held) 453 VFS_RELE(vfsp); 454 455 /* 456 * Now that we have the "home" zone for the file, return the slabel 457 * of that zone. 458 */ 459 zl = zone->zone_slabel; 460 label_hold(zl); 461 zone_rele(zone); 462 return (zl); 463 464 out_high: 465 if (vfs_is_held) 466 VFS_RELE(vfsp); 467 468 label_hold(l_admin_high); 469 zone_rele(zone); 470 return (l_admin_high); 471 } 472 473 static int 474 cgetlabel(bslabel_t *label_p, vnode_t *vp) 475 { 476 ts_label_t *tsl; 477 int error = 0; 478 479 if ((tsl = getflabel(vp)) == NULL) 480 return (EIO); 481 482 if (copyout((caddr_t)label2bslabel(tsl), (caddr_t)label_p, 483 sizeof (*(label_p))) != 0) 484 error = EFAULT; 485 486 label_rele(tsl); 487 return (error); 488 } 489 490 /* 491 * fgetlabel(2TSOL) - get file label 492 * getlabel(2TSOL) - get file label 493 */ 494 int 495 getlabel(const char *path, bslabel_t *label_p) 496 { 497 struct vnode *vp; 498 char *spath; 499 int error; 500 501 /* Sanity check arguments */ 502 if (path == NULL) 503 return (set_errno(EINVAL)); 504 505 spath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 506 if ((error = copyinstr(path, spath, MAXPATHLEN, NULL)) != 0) { 507 kmem_free(spath, MAXPATHLEN); 508 return (set_errno(error)); 509 } 510 511 if (error = lookupname(spath, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp)) { 512 kmem_free(spath, MAXPATHLEN); 513 return (set_errno(error)); 514 } 515 kmem_free(spath, MAXPATHLEN); 516 517 error = cgetlabel(label_p, vp); 518 519 VN_RELE(vp); 520 if (error != 0) 521 return (set_errno(error)); 522 else 523 return (0); 524 } 525 526 int 527 fgetlabel(int fd, bslabel_t *label_p) 528 { 529 file_t *fp; 530 int error; 531 532 if ((fp = getf(fd)) == NULL) 533 return (set_errno(EBADF)); 534 535 error = cgetlabel(label_p, fp->f_vnode); 536 releasef(fd); 537 538 if (error != 0) 539 return (set_errno(error)); 540 else 541 return (0); 542 } 543 544 /* 545 * Used by NFSv3 and NFSv4 server to query label of 546 * a pathname component during lookup/access ops. 547 */ 548 ts_label_t * 549 nfs_getflabel(vnode_t *vp) 550 { 551 zone_t *zone; 552 ts_label_t *zone_label; 553 char path[MAXNAMELEN]; 554 vnode_t *pvp, *tvp; 555 556 mutex_enter(&vp->v_lock); 557 /* 558 * mount traverse has been done by caller 559 * before calling this routine. 560 */ 561 ASSERT(!vn_ismntpt(vp)); 562 if (vp->v_path != NULL) { 563 zone = zone_find_by_any_path(vp->v_path, B_FALSE); 564 mutex_exit(&vp->v_lock); 565 } else { 566 /* 567 * v_path not cached. Since we rely on path 568 * of an obj to get its label, we need to get 569 * path corresponding to the parent vnode. 570 */ 571 tvp = vp; 572 do { 573 mutex_exit(&tvp->v_lock); 574 if ((pvp = dnlc_reverse_lookup(tvp, path, 575 sizeof (path))) == NULL) 576 return (NULL); 577 mutex_enter(&pvp->v_lock); 578 tvp = pvp; 579 } while (pvp->v_path == NULL); 580 zone = zone_find_by_any_path(pvp->v_path, B_FALSE); 581 mutex_exit(&pvp->v_lock); 582 } 583 /* 584 * Caller has verified that the file is either 585 * exported or visible. So if the path falls in 586 * global zone, admin_low is returned; otherwise 587 * the zone's label is returned. 588 */ 589 zone_label = zone->zone_slabel; 590 label_hold(zone_label); 591 zone_rele(zone); 592 return (zone_label); 593 } 594