Skip to content

Commit ef955fe

Browse files
drwezCQ Bot
authored andcommitted
[sestarnix] Add permissions checks to fs_node_setsecurity()
When a file node's "security.selinux" xattr is modified, the system validates that the task has "relabelfrom" and "relabelto" rights to the new and old Security Contexts, respectively. The new Security Context should also be checked for the "associate" permission to the file-system; this check is made, but the result ignored, until correct file-system labels are implemented. Bug: 351195217, 355180447 Change-Id: I9f80f3064da711117d639adfb63de9b6880013ef Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/1112132 Commit-Queue: Auto-Submit <[email protected]> Reviewed-by: Nelly Vouzoukidou <[email protected]> Fuchsia-Auto-Submit: Wez <[email protected]>
1 parent b3001b3 commit ef955fe

File tree

6 files changed

+121
-27
lines changed

6 files changed

+121
-27
lines changed

src/starnix/kernel/security/hooks.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,7 @@ mod tests {
12841284
}
12851285

12861286
#[fuchsia::test]
1287-
async fn fs_node_setsecurity_selinux_enforcing_invalid_context_succeeds() {
1287+
async fn fs_node_setsecurity_selinux_enforcing_invalid_context_fails() {
12881288
spawn_kernel_with_selinux_hooks_test_policy_and_run(
12891289
|locked, current_task, _security_server| {
12901290
let node = &testing::create_test_file(locked, current_task).entry.node;
@@ -1300,12 +1300,9 @@ mod tests {
13001300
"!".into(), // Note: Not a valid security context.
13011301
XattrOp::Set,
13021302
)
1303-
.is_ok());
1303+
.is_err());
13041304

1305-
assert_eq!(
1306-
Some(SecurityId::initial(InitialSid::Unlabeled)),
1307-
selinux_hooks::get_cached_sid(node)
1308-
);
1305+
assert_eq!(before_sid, selinux_hooks::get_cached_sid(node));
13091306
},
13101307
)
13111308
}
@@ -1422,10 +1419,12 @@ mod tests {
14221419
#[fuchsia::test]
14231420
async fn fs_node_getsecurity_delegates_to_get_xattr() {
14241421
spawn_kernel_with_selinux_hooks_test_policy_and_run(
1425-
|locked, current_task, _security_server| {
1422+
|locked, current_task, security_server| {
14261423
let node = &testing::create_test_file(locked, current_task).entry.node;
14271424

14281425
// Set an invalid value in `node`'s "security.selinux" attribute.
1426+
// This requires SELinux to be in permissive mode, otherwise the "relabelto" permission check will fail.
1427+
security_server.set_enforcing(false);
14291428
const TEST_VALUE: &str = "Something Random";
14301429
fs_node_setsecurity(
14311430
locked,
@@ -1436,6 +1435,7 @@ mod tests {
14361435
XattrOp::Set,
14371436
)
14381437
.expect("set_xattr(security.selinux) failed");
1438+
security_server.set_enforcing(true);
14391439

14401440
// Reading the security attribute should pass-through to read the value from the file system.
14411441
let result = fs_node_getsecurity(

src/starnix/kernel/security/selinux_hooks/mod.rs

Lines changed: 82 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,6 @@ pub(super) fn fs_node_init_on_create(
273273
// Compute both the SID to store on the in-memory node and the xattr to persist on-disk
274274
// (or None if this circumstance is such that there's no xattr to persist).
275275
let (sid, xattr) = match label.scheme {
276-
FileSystemLabelingScheme::Mountpoint => {
277-
return Ok(None);
278-
}
279276
FileSystemLabelingScheme::FsUse { fs_use_type, .. } => {
280277
let current_task_sid = current_task.read().security_state.attrs.current_sid;
281278
if fs_use_type == FsUseType::Task {
@@ -299,7 +296,7 @@ pub(super) fn fs_node_init_on_create(
299296
(sid, xattr)
300297
}
301298
}
302-
FileSystemLabelingScheme::GenFsCon => {
299+
FileSystemLabelingScheme::Mountpoint | FileSystemLabelingScheme::GenFsCon => {
303300
// The label in this case is decided in the `fs_node_init_with_dentry` hook.
304301
return Ok(None);
305302
}
@@ -639,23 +636,90 @@ pub(super) fn fs_node_setsecurity<L>(
639636
where
640637
L: LockEqualOrBefore<FileOpsCore>,
641638
{
642-
fs_node.ops().set_xattr(
639+
if name != FsStr::new(XATTR_NAME_SELINUX.to_bytes()) {
640+
return fs_node.ops().set_xattr(
641+
&mut locked.cast_locked::<FileOpsCore>(),
642+
fs_node,
643+
current_task,
644+
name,
645+
value,
646+
op,
647+
);
648+
}
649+
650+
// If the "security.selinux" attribute is being modified then the result depends on the
651+
// `FileSystem`'s labeling scheme.
652+
let fs = fs_node.fs();
653+
let fs_label = match &mut *fs.security_state.state.0.lock() {
654+
FileSystemLabelState::Unlabeled { .. } => {
655+
// If the `FileSystem` has not yet been labeled then store the xattr but leave the
656+
// label on the in-memory `fs_node` to be set up when the `FileSystem`'s labeling state
657+
// has been initialized, during load of the initial policy.
658+
return fs_node.ops().set_xattr(
659+
&mut locked.cast_locked::<FileOpsCore>(),
660+
fs_node,
661+
current_task,
662+
name,
663+
value,
664+
op,
665+
);
666+
}
667+
FileSystemLabelState::Labeled { label } => label.clone(),
668+
};
669+
670+
// If the "mountpoint"-labeling is used by this filesystem then setting labels is not supported.
671+
// TODO: - Is re-labeling of "genfscon" nodes allowed?
672+
if fs_label.scheme == FileSystemLabelingScheme::Mountpoint {
673+
return error!(ENOTSUP);
674+
}
675+
676+
// TODO: - Lock the `fs_node` security label here, to ensure consistency.
677+
678+
// Verify that the requested modification is permitted by the loaded policy.
679+
let new_sid = security_server.security_context_to_sid(value.into()).ok();
680+
if security_server.is_enforcing() {
681+
let new_sid = new_sid.ok_or_else(|| errno!(EINVAL))?;
682+
let task_sid = current_task.read().security_state.attrs.current_sid;
683+
let old_sid = fs_node_effective_sid(fs_node);
684+
let file_class = file_class_from_file_mode(fs_node.info().mode)?;
685+
let permission_check = security_server.as_permission_check();
686+
check_permission(
687+
&permission_check,
688+
task_sid,
689+
old_sid,
690+
CommonFilePermission::RelabelFrom.for_class(file_class),
691+
)?;
692+
check_permission(
693+
&permission_check,
694+
task_sid,
695+
new_sid,
696+
CommonFilePermission::RelabelTo.for_class(file_class),
697+
)?;
698+
check_permission(
699+
&permission_check,
700+
new_sid,
701+
fs_label.sid,
702+
FileSystemPermission::Associate,
703+
)?;
704+
}
705+
706+
// Apply the change to the file node.
707+
let result = fs_node.ops().set_xattr(
643708
&mut locked.cast_locked::<FileOpsCore>(),
644709
fs_node,
645710
current_task,
646711
name,
647712
value,
648713
op,
649-
)?;
650-
if name == FsStr::new(XATTR_NAME_SELINUX.to_bytes()) {
651-
// If the new value is a valid Security Context then label the node with the corresponding
652-
// SID, otherwise use the policy's "unlabeled" SID.
653-
let sid = security_server
654-
.security_context_to_sid(value.into())
655-
.unwrap_or(SecurityId::initial(InitialSid::Unlabeled));
656-
set_cached_sid(fs_node, sid)
714+
);
715+
716+
// If the operation succeeded then update the label cached on the file node.
717+
if result.is_ok() {
718+
let effective_new_sid = new_sid.unwrap_or(SecurityId::initial(InitialSid::Unlabeled));
719+
set_cached_sid(fs_node, effective_new_sid);
657720
}
658-
Ok(())
721+
722+
result
659723
}
660724

661725
/// Returns the `SecurityId` that should be used for SELinux access control checks against `fs_node`.
@@ -1127,8 +1191,10 @@ mod tests {
11271191
let dir_entry = &testing::create_test_file(locked, current_task).entry;
11281192
let node = &dir_entry.node;
11291193

1130-
// Store a valid Security Context in the attribute, and ensure any label cached on the `FsNode`
1131-
// is removed, to force the effective-SID query to resolve the label again.
1194+
// Store a valid Security Context in the attribute, then clear the cached label and
1195+
// re-resolve it. The hooks test policy defines that "tmpfs" use "fs_use_xattr"
1196+
// labeling, which should result in the (valid) label being read from the file, and
1197+
// the corresponding SID cached.
11321198
node.ops()
11331199
.set_xattr(
11341200
&mut locked.cast_locked::<FileOpsCore>(),
@@ -1139,8 +1205,6 @@ mod tests {
11391205
XattrOp::Set,
11401206
)
11411207
.expect("setxattr");
1142-
1143-
// Clear the cached SID and use `fs_node_init_with_dentry()` to re-resolve the label.
11441208
clear_cached_sid(node);
11451209
assert_eq!(None, get_cached_sid(node));
11461210
fs_node_init_with_dentry(locked, &security_server, &current_task, dir_entry)

src/starnix/lib/selinux/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ common_permission_enum! {
327327
Link("link"),
328328
/// Permission to open a file.
329329
Open("open"),
330+
/// Permission checked against the existing label when updating a file's security label.
331+
RelabelFrom("relabelfrom"),
332+
/// Permission checked against the new label when updating a file's security label.
333+
RelabelTo("relabelto"),
330334
/// Permission to modify attributes, including uid, gid and extended attributes.
331335
SetAttr("setattr"),
332336
/// Permission to delete a file or remove a hard link.

src/starnix/lib/selinux/testdata/micro_policies/hooks_tests_policy.conf

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# handle_unknown deny
22
class process
33
class file
4+
class filesystem
45
class blk_file
56
class chr_file
67
class lnk_file
@@ -33,9 +34,10 @@ sid kmod
3334
sid policy
3435
sid scmp_packet
3536
sid devnull
36-
common file { create open }
37+
common file { create open relabelfrom relabelto }
3738
class process { fork transition getsched setsched getpgid setpgid sigchld sigkill sigstop signal ptrace getsession setexec setfscreate setrlimit getrlimit setcurrent dyntransition }
3839
class file inherits file { execute_no_trans entrypoint }
40+
class filesystem { associate }
3941
sensitivity s0;
4042
dominance { s0 }
4143
category c0;
@@ -54,6 +56,8 @@ type kernel_t;
5456
type security_t;
5557
type unlabeled_t;
5658
type unlabeled_file_t;
59+
type tmpfs_t;
60+
type tmpfs_file_t;
5761
type test_valid_t;
5862
type test_different_valid_t;
5963
type test_getpgid_no_t;
@@ -79,6 +83,11 @@ type test_setsched_yes_t;
7983
type test_getsid_target_t;
8084
type test_getsid_yes_t;
8185
type test_getsid_no_t;
86+
87+
# When the kernel/init process creates a file in "tmpfs", transition to
88+
# a "tmpfs_file_t" domain.
89+
type_transition kernel_t tmpfs_t:file tmpfs_file_t;
90+
8291
allow exec_no_trans_source_t executable_file_no_trans_t:file { execute_no_trans };
8392
allow exec_transition_denied_target_t executable_file_trans_t:file { entrypoint };
8493
allow exec_transition_source_t exec_transition_target_t:process { transition };
@@ -96,10 +105,28 @@ allow test_setsched_yes_t test_setsched_target_t:process { setsched };
96105
allow test_getsid_yes_t test_getsid_target_t:process { getsession };
97106
allow kernel_t self:process { setexec setfscreate setcurrent };
98107
allow kernel_t test_valid_t:process { dyntransition };
108+
109+
# Allow the kernel/init task to relabel files from the initial "tmpfs_file_t"
110+
# to the valid test types.
111+
allow kernel_t tmpfs_file_t:file { relabelfrom };
112+
allow kernel_t test_valid_t:file { relabelto relabelfrom };
113+
allow kernel_t test_different_valid_t:file { relabelto };
114+
115+
# Allow files in "tmpfs" to be labeled with the "test_*valid_t" types.
116+
allow test_valid_t tmpfs_t:filesystem { associate };
117+
allow test_different_valid_t tmpfs_t:filesystem { associate };
118+
99119
allow test_valid_t self:process { setcurrent };
100120
allow test_valid_t test_different_valid_t:process { dyntransition };
121+
101122
user u roles object_r level s0 range s0 - s0:c0;
102123
sid kernel u:object_r:kernel_t:s0 - s0
103124
sid security u:object_r:security_t:s0
104125
sid unlabeled u:object_r:unlabeled_t:s0
105126
sid file u:object_r:unlabeled_file_t:s0
127+
128+
# Apply fs_use_xattr to the test filesystem, which is "tmpfs".
129+
# This allows some xattr hook tests to clear the label cached in-memory,
130+
# and re-initialize the node to refresh the cached label from the xattr.
131+
# Type transition will cause new files to be labeled as "tmpfs_file_t".
132+
fs_use_xattr tmpfs u:object_r:tmpfs_t:s0 - s0;
Binary file not shown.

src/starnix/tests/selinux/expectations/selinux.json5

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
"*open*",
4747
"*perf_event*",
4848
"*readlink*",
49-
"*relabel*",
5049
"*rename*",
5150
"*rxdir*",
5251
"*sem*",

0 commit comments

Comments
 (0)