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