Skip to content

Commit b65db21

Browse files
croissanneallisonkarlitskaya
authored andcommitted
repository: add insecure mode
If a repository is set to insecure, it will files that have fs-verity disabled. Specifically it will continue as normal on `MeasureVerityError:VerityMissing`. A repository will still run with fs-verity enabled if it is able to measure the verity of an image when it is opened, even if the repository was set to insecure. Signed-off-by: Sanne Raymaekers <[email protected]>
1 parent 2dc50d3 commit b65db21

File tree

1 file changed

+79
-15
lines changed

1 file changed

+79
-15
lines changed

crates/composefs/src/repository.rs

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ use sha2::{Digest, Sha256};
2121

2222
use crate::{
2323
fsverity::{
24-
compute_verity, enable_verity, ensure_verity_equal, measure_verity, FsVerityHashValue,
24+
compute_verity, enable_verity, ensure_verity_equal, measure_verity, CompareVerityError,
25+
EnableVerityError, FsVerityHashValue, MeasureVerityError,
2526
},
2627
mount::{composefs_fsmount, mount_at},
2728
splitstream::{DigestMap, SplitStreamReader, SplitStreamWriter},
@@ -57,6 +58,7 @@ fn ensure_dir_and_openat(dirfd: impl AsFd, filename: &str, flags: OFlags) -> Err
5758
pub struct Repository<ObjectID: FsVerityHashValue> {
5859
repository: OwnedFd,
5960
objects: OnceCell<OwnedFd>,
61+
insecure: bool,
6062
_data: std::marker::PhantomData<ObjectID>,
6163
}
6264

@@ -85,6 +87,7 @@ impl<ObjectID: FsVerityHashValue> Repository<ObjectID> {
8587
Ok(Self {
8688
repository,
8789
objects: OnceCell::new(),
90+
insecure: false,
8891
_data: std::marker::PhantomData,
8992
})
9093
}
@@ -127,7 +130,23 @@ impl<ObjectID: FsVerityHashValue> Repository<ObjectID> {
127130
Ok(fd) => {
128131
// measure the existing file to ensure that it's correct
129132
// TODO: try to replace file if it's broken?
130-
ensure_verity_equal(fd, &id)?;
133+
match ensure_verity_equal(&fd, &id) {
134+
Ok(()) => {}
135+
Err(CompareVerityError::Measure(MeasureVerityError::VerityMissing))
136+
if self.insecure =>
137+
{
138+
match enable_verity::<ObjectID>(&fd) {
139+
Ok(()) => {
140+
ensure_verity_equal(&fd, &id)?;
141+
}
142+
Err(other) => Err(other)?,
143+
}
144+
}
145+
Err(CompareVerityError::Measure(
146+
MeasureVerityError::FilesystemNotSupported,
147+
)) if self.insecure => {}
148+
Err(other) => Err(other)?,
149+
}
131150
return Ok(id);
132151
}
133152
Err(Errno::NOENT) => {
@@ -151,8 +170,17 @@ impl<ObjectID: FsVerityHashValue> Repository<ObjectID> {
151170
)?;
152171
drop(file);
153172

154-
enable_verity::<ObjectID>(&ro_fd).context("Enabling verity digest")?;
155-
ensure_verity_equal(&ro_fd, &id).context("Double-checking verity digest")?;
173+
match enable_verity::<ObjectID>(&ro_fd) {
174+
Ok(()) => match ensure_verity_equal(&ro_fd, &id) {
175+
Ok(()) => {}
176+
Err(CompareVerityError::Measure(
177+
MeasureVerityError::VerityMissing | MeasureVerityError::FilesystemNotSupported,
178+
)) if self.insecure => {}
179+
Err(other) => Err(other).context("Double-checking verity digest")?,
180+
},
181+
Err(EnableVerityError::FilesystemNotSupported) if self.insecure => {}
182+
Err(other) => Err(other).context("Enabling verity digest")?,
183+
}
156184

157185
match linkat(
158186
CWD,
@@ -175,10 +203,21 @@ impl<ObjectID: FsVerityHashValue> Repository<ObjectID> {
175203

176204
fn open_with_verity(&self, filename: &str, expected_verity: &ObjectID) -> Result<OwnedFd> {
177205
let fd = self.openat(filename, OFlags::RDONLY)?;
178-
ensure_verity_equal(&fd, expected_verity)?;
206+
match ensure_verity_equal(&fd, expected_verity) {
207+
Ok(()) => {}
208+
Err(CompareVerityError::Measure(
209+
MeasureVerityError::VerityMissing | MeasureVerityError::FilesystemNotSupported,
210+
)) if self.insecure => {}
211+
Err(other) => Err(other)?,
212+
}
179213
Ok(fd)
180214
}
181215

216+
pub fn set_insecure(&mut self, insecure: bool) -> &mut Self {
217+
self.insecure = insecure;
218+
self
219+
}
220+
182221
/// Creates a SplitStreamWriter for writing a split stream.
183222
/// You should write the data to the returned object and then pass it to .store_stream() to
184223
/// store the result.
@@ -220,9 +259,18 @@ impl<ObjectID: FsVerityHashValue> Repository<ObjectID> {
220259

221260
/// Basically the same as has_stream() except that it performs expensive verification
222261
pub fn check_stream(&self, sha256: &Sha256Digest) -> Result<Option<ObjectID>> {
223-
match self.openat(&format!("streams/{}", hex::encode(sha256)), OFlags::RDONLY) {
262+
let stream_path = format!("streams/{}", hex::encode(sha256));
263+
match self.openat(&stream_path, OFlags::RDONLY) {
224264
Ok(stream) => {
225-
let measured_verity: ObjectID = measure_verity(&stream)?;
265+
let path = readlinkat(&self.repository, stream_path, [])?;
266+
let measured_verity = match measure_verity(&stream) {
267+
Ok(found) => found,
268+
Err(
269+
MeasureVerityError::VerityMissing
270+
| MeasureVerityError::FilesystemNotSupported,
271+
) if self.insecure => FsVerityHashValue::from_object_pathname(path.to_bytes())?,
272+
Err(other) => Err(other)?,
273+
};
226274
let mut context = Sha256::new();
227275
let mut split_stream = SplitStreamReader::new(File::from(stream))?;
228276

@@ -383,20 +431,36 @@ impl<ObjectID: FsVerityHashValue> Repository<ObjectID> {
383431
self.write_image(Some(name), &data)
384432
}
385433

386-
fn open_image(&self, name: &str) -> Result<OwnedFd> {
434+
/// Returns the fd of the image and whether or not verity should be
435+
/// enabled when mounting it.
436+
fn open_image(&self, name: &str) -> Result<(OwnedFd, bool)> {
387437
let image = self.openat(&format!("images/{name}"), OFlags::RDONLY)?;
388438

389-
if !name.contains("/") {
390-
// A name with no slashes in it is taken to be a sha256 fs-verity digest
391-
ensure_verity_equal(&image, &ObjectID::from_hex(name)?)?;
439+
if name.contains("/") {
440+
return Ok((image, true));
392441
}
393442

394-
Ok(image)
443+
// A name with no slashes in it is taken to be a sha256 fs-verity digest
444+
match measure_verity::<ObjectID>(&image) {
445+
Ok(found) if found == FsVerityHashValue::from_hex(name)? => Ok((image, true)),
446+
Ok(_) => bail!("fs-verity content mismatch"),
447+
Err(MeasureVerityError::VerityMissing | MeasureVerityError::FilesystemNotSupported)
448+
if self.insecure =>
449+
{
450+
Ok((image, false))
451+
}
452+
Err(other) => Err(other)?,
453+
}
395454
}
396455

397456
pub fn mount(&self, name: &str) -> Result<OwnedFd> {
398-
let image = self.open_image(name)?;
399-
Ok(composefs_fsmount(image, name, self.objects_dir()?, true)?)
457+
let (image, enable_verity) = self.open_image(name)?;
458+
Ok(composefs_fsmount(
459+
image,
460+
name,
461+
self.objects_dir()?,
462+
enable_verity,
463+
)?)
400464
}
401465

402466
pub fn mount_at(&self, name: &str, mountpoint: impl AsRef<Path>) -> Result<()> {
@@ -525,7 +589,7 @@ impl<ObjectID: FsVerityHashValue> Repository<ObjectID> {
525589
}
526590

527591
pub fn objects_for_image(&self, name: &str) -> Result<HashSet<ObjectID>> {
528-
let image = self.open_image(name)?;
592+
let (image, _) = self.open_image(name)?;
529593
let mut data = vec![];
530594
std::fs::File::from(image).read_to_end(&mut data)?;
531595
Ok(crate::erofs::reader::collect_objects(&data)?)

0 commit comments

Comments
 (0)