Skip to content

Commit

Permalink
Take into account downgrade privilege when reading
Browse files Browse the repository at this point in the history
Previously downgrade was only allowed when writing to lower labels, but
not when reading from higher labels. This makes both possible, relying
on the same underlying Node privilege.

Credit to @aferr for the explanation and suggestions on how to implement
it, and also link to the relevant Slack discussion, for reference:
https://project-oak.slack.com/messages/CHE9E13C3/p1590712037024800?thread_ts=1590660390.016600

Ref project-oak#630
  • Loading branch information
tiziano88 committed May 29, 2020
1 parent 9f5d57f commit 134c02f
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 24 deletions.
62 changes: 39 additions & 23 deletions oak/server/rust/oak_runtime/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,16 +501,21 @@ impl Runtime {
}

/// Returns the least restrictive (i.e. least confidential, most trusted) label that this Node
/// may downgrade to. This takes into account all the [downgrade privilege](NodeInfo::privilege)
/// that the node possesses.
fn get_node_downgraded_label(&self, node_id: NodeId) -> Label {
// Original (static) Node label.
let node_label = self.get_node_label(node_id);
/// may downgrade `initial_label` to. This takes into account all the [downgrade
/// privilege](NodeInfo::privilege) that the node possesses.
///
/// When reading from a Channel, `initial_label` is the Label of the Channel from which the Node
/// is reading: the thing being downgraded is the data being read into the Node (protected by
/// the Channel Label).
///
/// When writing to a Channel, `initial_label` is the Label of the Node itself: the thing being
/// downgraded is the data inside the Node (protected by the Node Label).
fn get_node_downgraded_label(&self, node_id: NodeId, initial_label: &Label) -> Label {
// Retrieve the set of tags that the node may downgrade.
let node_privilege = self.get_node_privilege(node_id);
Label {
// Remove all the confidentiality tags that the Node may declassify.
confidentiality_tags: node_label
confidentiality_tags: initial_label
.confidentiality_tags
.iter()
.filter(|t| {
Expand All @@ -521,7 +526,7 @@ impl Runtime {
.cloned()
.collect(),
// Add all the integrity tags that the Node may endorse.
integrity_tags: node_label
integrity_tags: initial_label
.integrity_tags
.iter()
.chain(node_privilege.can_endorse_integrity_tags.iter())
Expand Down Expand Up @@ -571,20 +576,23 @@ impl Runtime {
fn validate_can_read_from_label(
&self,
node_id: NodeId,
label: &Label,
source_label: &Label,
) -> Result<(), OakStatus> {
let downgraded_node_label = self.get_node_downgraded_label(node_id);
// We downgrade the source Label, since its data is being read into the current Node.
let downgraded_source_label = self.get_node_downgraded_label(node_id, source_label);
let target_label = self.get_node_label(node_id);
trace!("{:?}: original source label: {:?}?", node_id, source_label);
trace!(
"{:?}: can {:?} read from {:?}?",
"{:?}: downgraded source label: {:?}?",
node_id,
downgraded_node_label,
label
downgraded_source_label
);
if label.flows_to(&downgraded_node_label) {
trace!("{:?}: can read from {:?}", node_id, label);
trace!("{:?}: target label: {:?}?", node_id, target_label);
if downgraded_source_label.flows_to(&target_label) {
trace!("{:?}: can read from {:?}", node_id, source_label);
Ok(())
} else {
debug!("{:?}: cannot read from {:?}", node_id, label);
debug!("{:?}: cannot read from {:?}", node_id, source_label);
Err(OakStatus::ErrPermissionDenied)
}
}
Expand All @@ -602,19 +610,27 @@ impl Runtime {

/// Returns whether the given Node is allowed to write to an entity with the provided [`Label`],
/// taking into account all the [downgrade privilege](NodeInfo::privilege) the Node possesses.
fn validate_can_write_to_label(&self, node_id: NodeId, label: &Label) -> Result<(), OakStatus> {
let downgraded_node_label = self.get_node_downgraded_label(node_id);
fn validate_can_write_to_label(
&self,
node_id: NodeId,
target_label: &Label,
) -> Result<(), OakStatus> {
// We downgrade the current Node Label, since its data is being written into the
// destination.
let source_label = self.get_node_label(node_id);
let downgraded_source_label = self.get_node_downgraded_label(node_id, &source_label);
trace!("{:?}: original source label: {:?}?", node_id, source_label);
trace!(
"{:?}: can {:?} write to {:?}?",
"{:?}: downgraded source label: {:?}?",
node_id,
downgraded_node_label,
label
downgraded_source_label
);
if downgraded_node_label.flows_to(&label) {
trace!("{:?}: can write to {:?}", node_id, label);
trace!("{:?}: target label: {:?}?", node_id, target_label);
if downgraded_source_label.flows_to(&target_label) {
trace!("{:?}: can write to {:?}", node_id, target_label);
Ok(())
} else {
debug!("{:?}: cannot write to {:?}", node_id, label);
debug!("{:?}: cannot write to {:?}", node_id, target_label);
Err(OakStatus::ErrPermissionDenied)
}
}
Expand Down
76 changes: 75 additions & 1 deletion oak/server/rust/oak_runtime/src/runtime/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,11 @@ fn create_channel_less_confidential_label_no_privilege_err() {
);
}

/// Create a test Node that creates a Channel with a more confidential label and succeeds.
/// Create a test Node with no privilege that:
///
/// - creates a Channel with a more confidential label and succeeds
/// - writes to the newly created channel and succeeds
/// - reads from the newly created channel and fails
///
/// Data is always allowed to flow to more confidential labels.
#[test]
Expand All @@ -247,6 +251,76 @@ fn create_channel_more_confidential_label_ok() {
Box::new(move |runtime| {
let result = runtime.channel_create(&more_confidential_label);
assert_eq!(true, result.is_ok());

let (write_handle, read_handle) = result.unwrap();

{
// Writing to a more confidential Channel is allowed.
let message = NodeMessage {
data: vec![14, 12, 88],
handles: vec![],
};
let result = runtime.channel_write(write_handle, message);
assert_eq!(true, result.is_ok());
}

{
// Reading from a more confidential Channel is not allowed.
let result = runtime.channel_read(read_handle);
assert_eq!(false, result.is_ok());
}

Ok(())
}),
);
}

/// Create a test Node with downgrading privilege that:
///
/// - creates a Channel with a more confidential label and succeeds (same as previous test case)
/// - writes to the newly created channel and succeeds (same as previous test case)
/// - reads from the newly created channel and succeeds (different from previous test case, thanks
/// to the newly added privilege)
#[test]
fn create_channel_more_confidential_label_privilege_ok() {
let tag_0 = oak_abi::label::authorization_bearer_token_hmac_tag(&[1, 1, 1]);
let tag_1 = oak_abi::label::authorization_bearer_token_hmac_tag(&[2, 2, 2]);
let initial_label = Label {
confidentiality_tags: vec![tag_0.clone()],
integrity_tags: vec![],
};
let more_confidential_label = Label {
confidentiality_tags: vec![tag_0, tag_1.clone()],
integrity_tags: vec![],
};
run_node_body(
&initial_label,
&NodePrivilege {
can_declassify_confidentiality_tags: hashset! { tag_1 },
can_endorse_integrity_tags: hashset! {},
},
Box::new(move |runtime| {
let result = runtime.channel_create(&more_confidential_label);
assert_eq!(true, result.is_ok());

let (write_handle, read_handle) = result.unwrap();

{
// Writing to a more confidential Channel is allowed.
let message = NodeMessage {
data: vec![14, 12, 88],
handles: vec![],
};
let result = runtime.channel_write(write_handle, message);
assert_eq!(true, result.is_ok());
}

{
// Reading from a more confidential Channel is allowed because of the privilege.
let result = runtime.channel_read(read_handle);
assert_eq!(true, result.is_ok());
}

Ok(())
}),
);
Expand Down

0 comments on commit 134c02f

Please sign in to comment.