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