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.
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.
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]
22 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <sys/types.h>
26 #include <sys/param.h>
27 #include <sys/cmn_err.h>
28 #include <sys/systm.h>
30 #include <sys/modctl.h>
32 #include <sys/vnode.h>
33 #include <sys/tiuser.h>
35 #include <sys/pathname.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>
47 #include <nfs/nfs_clnt.h>
50 int sys_labeling
= 0; /* the default is "off" */
52 static kmem_cache_t
*tslabel_cache
;
53 ts_label_t
*l_admin_low
;
54 ts_label_t
*l_admin_high
;
56 uint32_t default_doi
= DEFAULT_DOI
;
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.
70 * sys_labeling will default to "off" unless it is overridden
74 tslabel_cache
= kmem_cache_create("tslabel_cache", sizeof (ts_label_t
),
75 0, NULL
, NULL
, NULL
, NULL
, NULL
, 0);
77 l_admin_low
= labelalloc(&label
, default_doi
, KM_SLEEP
);
79 l_admin_high
= labelalloc(&label
, default_doi
, KM_SLEEP
);
83 * Allocate new ts_label_t.
86 labelalloc(const bslabel_t
*val
, uint32_t doi
, int flag
)
88 ts_label_t
*lab
= kmem_cache_alloc(tslabel_cache
, flag
);
95 bzero(&lab
->tsl_label
, sizeof (bslabel_t
));
97 bcopy(val
, &lab
->tsl_label
, sizeof (bslabel_t
));
103 * Duplicate an existing ts_label_t to a new one, with only
104 * the current reference.
107 labeldup(const ts_label_t
*val
, int flag
)
109 ts_label_t
*lab
= kmem_cache_alloc(tslabel_cache
, flag
);
112 bcopy(val
, lab
, sizeof (ts_label_t
));
119 * Put a hold on a label structure.
122 label_hold(ts_label_t
*lab
)
124 atomic_inc_32(&lab
->tsl_ref
);
128 * Release previous hold on a label structure. Free it if refcnt == 0.
131 label_rele(ts_label_t
*lab
)
133 if (atomic_dec_32_nv(&lab
->tsl_ref
) == 0)
134 kmem_cache_free(tslabel_cache
, lab
);
138 label2bslabel(ts_label_t
*lab
)
140 return (&lab
->tsl_label
);
145 label2doi(ts_label_t
*lab
)
147 return (lab
->tsl_doi
);
151 * Compare labels. Return 1 if equal, 0 otherwise.
154 label_equal(const ts_label_t
*l1
, const ts_label_t
*l2
)
156 return ((l1
->tsl_doi
== l2
->tsl_doi
) &&
157 blequal(&l1
->tsl_label
, &l2
->tsl_label
));
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
169 getflabel_cipso(vfs_t
*vfsp
)
173 char *nfspath
, *respath
;
174 refstr_t
*resource_ref
;
175 boolean_t treat_abs
= B_FALSE
;
177 if (vfsp
->vfs_resource
== NULL
)
178 return (NULL
); /* error */
179 resource_ref
= vfs_getresource(vfsp
);
181 nfspath
= (char *)refstr_value(resource_ref
);
182 respath
= strchr(nfspath
, ':'); /* skip server name */
184 respath
++; /* skip over ":" */
185 if (*respath
!= '/') {
186 /* treat path as absolute but it doesn't have leading '/' */
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
);
195 return (l_admin_low
);
199 * Skip over zonepath (not including "root"), e.g. /zone/internal
201 respath
+= reszone
->zone_rootpathlen
- 7;
203 respath
--; /* no leading '/' to skip */
204 if (strncmp(respath
, "/root/", 6) == 0) {
205 /* Check if we now have something like "/zone/public/" */
207 respath
+= 5; /* skip "/root" first */
208 new_reszone
= zone_find_by_any_path(respath
, B_FALSE
);
209 if (new_reszone
!= global_zone
) {
211 reszone
= new_reszone
;
213 zone_rele(new_reszone
);
217 refstr_rele(resource_ref
);
218 label_hold(reszone
->zone_slabel
);
221 return (reszone
->zone_slabel
);
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.
230 getflabel_zfs(vfs_t
*vfsp
)
233 ts_label_t
*tsl
= NULL
;
234 refstr_t
*resource_ref
;
236 char ds_hexsl
[MAXNAMELEN
];
239 resource_ref
= vfs_getresource(vfsp
);
240 osname
= refstr_value(resource_ref
);
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
);
246 if ((error
) || (strcasecmp(ds_hexsl
, ZFS_MLSLABEL_DEFAULT
) == 0))
248 if (hexstr_to_label(ds_hexsl
, &ds_sl
) != 0)
251 tsl
= labelalloc(&ds_sl
, default_doi
, KM_SLEEP
);
256 getflabel_nfs(vfs_t
*vfsp
)
258 bslabel_t
*server_sl
;
259 ts_label_t
*srv_label
;
263 struct servinfo
*svp
;
265 struct knetconfig
*knconf
;
269 svp
= mi
->mi_curr_serv
;
270 addr
= &svp
->sv_addr
;
271 knconf
= svp
->sv_knconf
;
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
;
285 tp
= find_tpc(ipaddr
, addr_type
, B_FALSE
);
289 if (tp
->tpc_tp
.host_type
== SUN_CIPSO
) {
291 return (getflabel_cipso(vfsp
));
294 if (tp
->tpc_tp
.host_type
!= UNLABELED
)
297 server_sl
= &tp
->tpc_tp
.tp_def_label
;
298 srv_label
= labelalloc(server_sl
, default_doi
, KM_SLEEP
);
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.
316 getflabel(vnode_t
*vp
)
323 boolean_t vfs_is_held
= B_FALSE
;
324 char vpath
[MAXPATHLEN
];
334 * Traverse lofs mounts and fattach'es to get the real vnode
336 if (VOP_REALVP(rvp
, &rvp2
, NULL
) == 0)
341 /* rvp/rvfsp now represent the real vnode/vfs we will be using */
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
));
348 * Fast path, for objects in a labeled zone: everything except
349 * for lofs/nfs will be just the label of that zone.
351 if ((rvfsp
->vfs_zone
!= NULL
) && (rvfsp
->vfs_zone
!= global_zone
)) {
352 if ((strcmp(vfssw
[rvfsp
->vfs_fstype
].vsw_name
,
354 zone
= rvfsp
->vfs_zone
;
356 goto zone_out
; /* return this label */
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
365 err
= vnodetopath(rootdir
, rvp
, vpath
, sizeof (vpath
), kcred
);
366 if ((err
!= 0) || (*vpath
!= '/')) {
367 zone
= curproc
->p_zone
;
373 * For zfs filesystem, return the explicit label property if a
374 * meaningful one exists.
376 if (strncmp(vfssw
[rvfsp
->vfs_fstype
].vsw_name
, "zfs", 3) == 0) {
379 tsl
= getflabel_zfs(rvfsp
);
381 /* if label found, return it, otherwise continue... */
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
392 if (vfsp
->vfs_mntpt
!= NULL
) {
394 vfs_is_held
= B_TRUE
;
397 zone
= zone_find_by_any_path(vpath
, B_FALSE
);
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.
403 if ((zone
!= global_zone
) || ((vfsp
->vfs_flag
& VFS_RDONLY
) != 0))
404 goto zone_out
; /* return this label */
407 * Otherwise, if we're not in the global zone, use the label of
410 if ((zone
= curproc
->p_zone
) != global_zone
) {
412 goto zone_out
; /* return this label */
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).
423 zone
= zone_find_by_any_path(vpath
, B_FALSE
);
426 if ((curproc
->p_zone
== global_zone
) && (zone
== global_zone
)) {
428 boolean_t exported
= B_FALSE
;
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.
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
445 mntpt_ref
= vfs_getmntpoint(vfsp
);
446 mntpt
= (char *)refstr_value(mntpt_ref
);
448 if ((mntpt
!= NULL
) && (*mntpt
== '/')) {
451 to_zone
= zone_find_by_any_path(mntpt
, B_FALSE
);
453 if (to_zone
!= global_zone
) {
454 /* force admin_low */
459 refstr_rele(mntpt_ref
);
462 size_t plen
= strlen(vpath
);
464 vfs_list_read_lock();
465 nvfs
= vfsp
->vfs_next
;
466 while (nvfs
!= vfsp
) {
471 * Skip checking this vfs if it's not lofs
472 * (the only way to export from the global
475 if (strncmp(vfssw
[nvfs
->vfs_fstype
].vsw_name
,
477 nvfs
= nvfs
->vfs_next
;
481 rstr
= refstr_value(nvfs
->vfs_resource
);
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.
491 if ((rlen
> 0) && (rlen
<= plen
) &&
492 (strncmp(rstr
, vpath
, rlen
) == 0) &&
493 (vpath
[rlen
] == '/' ||
494 vpath
[rlen
] == '\0')) {
495 /* force admin_low */
499 nvfs
= nvfs
->vfs_next
;
512 * Now that we have the "home" zone for the file, return the slabel
515 zl
= zone
->zone_slabel
;
524 label_hold(l_admin_high
);
526 return (l_admin_high
);
530 cgetlabel(bslabel_t
*label_p
, vnode_t
*vp
)
535 if ((tsl
= getflabel(vp
)) == NULL
)
538 if (copyout((caddr_t
)label2bslabel(tsl
), (caddr_t
)label_p
,
539 sizeof (*(label_p
))) != 0)
547 * fgetlabel(2TSOL) - get file label
548 * getlabel(2TSOL) - get file label
551 getlabel(const char *path
, bslabel_t
*label_p
)
557 /* Sanity check arguments */
559 return (set_errno(EINVAL
));
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
));
567 if (error
= lookupname(spath
, UIO_SYSSPACE
, FOLLOW
, NULLVPP
, &vp
)) {
568 kmem_free(spath
, MAXPATHLEN
);
569 return (set_errno(error
));
571 kmem_free(spath
, MAXPATHLEN
);
573 error
= cgetlabel(label_p
, vp
);
577 return (set_errno(error
));
583 fgetlabel(int fd
, bslabel_t
*label_p
)
588 if ((fp
= getf(fd
)) == NULL
)
589 return (set_errno(EBADF
));
591 error
= cgetlabel(label_p
, fp
->f_vnode
);
595 return (set_errno(error
));