src/pxar/sequential_decoder.rs: split read and restore of attributes into separate functions

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
Christian Ebner 2019-08-01 13:08:08 +02:00 committed by Dietmar Maurer
parent e60cfbcdd0
commit a92e6c9011
1 changed files with 68 additions and 79 deletions

View File

@ -178,86 +178,71 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
Ok(CaFormatFCaps { data: buffer }) Ok(CaFormatFCaps { data: buffer })
} }
fn restore_attributes( fn read_attributes(&mut self) -> Result<(CaFormatHeader, PxarAttributes), Error> {
&mut self, let mut attr = PxarAttributes::new();
entry: &CaFormatEntry,
fd: Option<RawFd>
) -> Result<CaFormatHeader, Error> {
let mut xattrs = Vec::new();
let mut fcaps = None;
let mut quota_projid = None;
let mut acl_user = Vec::new();
let mut acl_group = Vec::new();
let mut acl_group_obj = None;
let mut acl_default = None;
let mut acl_default_user = Vec::new();
let mut acl_default_group = Vec::new();
let mut head: CaFormatHeader = self.read_item()?; let mut head: CaFormatHeader = self.read_item()?;
let mut size = (head.size - HEADER_SIZE) as usize; let mut size = (head.size - HEADER_SIZE) as usize;
loop { loop {
match head.htype { match head.htype {
CA_FORMAT_XATTR => { CA_FORMAT_XATTR => {
if self.has_features(CA_FORMAT_WITH_XATTRS) { if self.has_features(CA_FORMAT_WITH_XATTRS) {
xattrs.push(self.read_xattr(size)?); attr.xattrs.push(self.read_xattr(size)?);
} else { } else {
self.skip_bytes(size)?; self.skip_bytes(size)?;
} }
}, },
CA_FORMAT_FCAPS => { CA_FORMAT_FCAPS => {
if self.has_features(CA_FORMAT_WITH_FCAPS) { if self.has_features(CA_FORMAT_WITH_FCAPS) {
fcaps = Some(self.read_fcaps(size)?); attr.fcaps = Some(self.read_fcaps(size)?);
} else { } else {
self.skip_bytes(size)?; self.skip_bytes(size)?;
} }
}, },
CA_FORMAT_ACL_USER => { CA_FORMAT_ACL_USER => {
if self.has_features(CA_FORMAT_WITH_ACL) { if self.has_features(CA_FORMAT_WITH_ACL) {
acl_user.push(self.read_item::<CaFormatACLUser>()?); attr.acl_user.push(self.read_item::<CaFormatACLUser>()?);
} else { } else {
self.skip_bytes(size)?; self.skip_bytes(size)?;
} }
}, },
CA_FORMAT_ACL_GROUP => { CA_FORMAT_ACL_GROUP => {
if self.has_features(CA_FORMAT_WITH_ACL) { if self.has_features(CA_FORMAT_WITH_ACL) {
acl_group.push(self.read_item::<CaFormatACLGroup>()?); attr.acl_group.push(self.read_item::<CaFormatACLGroup>()?);
} else { } else {
self.skip_bytes(size)?; self.skip_bytes(size)?;
} }
}, },
CA_FORMAT_ACL_GROUP_OBJ => { CA_FORMAT_ACL_GROUP_OBJ => {
if self.has_features(CA_FORMAT_WITH_ACL) { if self.has_features(CA_FORMAT_WITH_ACL) {
acl_group_obj = Some(self.read_item::<CaFormatACLGroupObj>()?); attr.acl_group_obj = Some(self.read_item::<CaFormatACLGroupObj>()?);
} else { } else {
self.skip_bytes(size)?; self.skip_bytes(size)?;
} }
}, },
CA_FORMAT_ACL_DEFAULT => { CA_FORMAT_ACL_DEFAULT => {
if self.has_features(CA_FORMAT_WITH_ACL) { if self.has_features(CA_FORMAT_WITH_ACL) {
acl_default = Some(self.read_item::<CaFormatACLDefault>()?); attr.acl_default = Some(self.read_item::<CaFormatACLDefault>()?);
} else { } else {
self.skip_bytes(size)?; self.skip_bytes(size)?;
} }
}, },
CA_FORMAT_ACL_DEFAULT_USER => { CA_FORMAT_ACL_DEFAULT_USER => {
if self.has_features(CA_FORMAT_WITH_ACL) { if self.has_features(CA_FORMAT_WITH_ACL) {
acl_default_user.push(self.read_item::<CaFormatACLUser>()?); attr.acl_default_user.push(self.read_item::<CaFormatACLUser>()?);
} else { } else {
self.skip_bytes(size)?; self.skip_bytes(size)?;
} }
}, },
CA_FORMAT_ACL_DEFAULT_GROUP => { CA_FORMAT_ACL_DEFAULT_GROUP => {
if self.has_features(CA_FORMAT_WITH_ACL) { if self.has_features(CA_FORMAT_WITH_ACL) {
acl_default_group.push(self.read_item::<CaFormatACLGroup>()?); attr.acl_default_group.push(self.read_item::<CaFormatACLGroup>()?);
} else { } else {
self.skip_bytes(size)?; self.skip_bytes(size)?;
} }
}, },
CA_FORMAT_QUOTA_PROJID => { CA_FORMAT_QUOTA_PROJID => {
if self.has_features(CA_FORMAT_WITH_QUOTA_PROJID) { if self.has_features(CA_FORMAT_WITH_QUOTA_PROJID) {
quota_projid = Some(self.read_item::<CaFormatQuotaProjID>()?); attr.quota_projid = Some(self.read_item::<CaFormatQuotaProjID>()?);
} else { } else {
self.skip_bytes(size)?; self.skip_bytes(size)?;
} }
@ -267,58 +252,64 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
head = self.read_item()?; head = self.read_item()?;
size = (head.size - HEADER_SIZE) as usize; size = (head.size - HEADER_SIZE) as usize;
} }
// If fd is None, this indicates that we just want to skip over these entries (no restore).
// If on the other hand there is Some(fd), restore the attributes on it.
if let Some(fd) = fd {
self.restore_xattrs_fcaps_fd(fd, &xattrs, &fcaps)?;
Ok((head, attr))
}
fn restore_attributes(
&mut self,
fd: RawFd,
attr: &PxarAttributes,
entry: &CaFormatEntry,
) -> Result<(), Error> {
self.restore_xattrs_fcaps_fd(fd, &attr.xattrs, &attr.fcaps)?;
let mut acl = acl::ACL::init(5)?;
acl.add_entry_full(acl::ACL_USER_OBJ, None, mode_user_to_acl_permissions(entry.mode))?;
acl.add_entry_full(acl::ACL_OTHER, None, mode_other_to_acl_permissions(entry.mode))?;
match &attr.acl_group_obj {
Some(group_obj) => {
acl.add_entry_full(acl::ACL_MASK, None, mode_group_to_acl_permissions(entry.mode))?;
acl.add_entry_full(acl::ACL_GROUP_OBJ, None, group_obj.permissions)?;
},
None => {
acl.add_entry_full(acl::ACL_GROUP_OBJ, None, mode_group_to_acl_permissions(entry.mode))?;
},
}
for user in &attr.acl_user {
acl.add_entry_full(acl::ACL_USER, Some(user.uid), user.permissions)?;
}
for group in &attr.acl_group {
acl.add_entry_full(acl::ACL_GROUP, Some(group.gid), group.permissions)?;
}
let proc_path = Path::new("/proc/self/fd/").join(fd.to_string());
if !acl.is_valid() {
bail!("Error while restoring ACL - ACL invalid");
}
acl.set_file(&proc_path, acl::ACL_TYPE_ACCESS)?;
if let Some(default) = &attr.acl_default {
let mut acl = acl::ACL::init(5)?; let mut acl = acl::ACL::init(5)?;
acl.add_entry_full(acl::ACL_USER_OBJ, None, mode_user_to_acl_permissions(entry.mode))?; acl.add_entry_full(acl::ACL_USER_OBJ, None, default.user_obj_permissions)?;
acl.add_entry_full(acl::ACL_OTHER, None, mode_other_to_acl_permissions(entry.mode))?; acl.add_entry_full(acl::ACL_GROUP_OBJ, None, default.group_obj_permissions)?;
match acl_group_obj { acl.add_entry_full(acl::ACL_OTHER, None, default.other_permissions)?;
Some(group_obj) => { if default.mask_permissions != std::u64::MAX {
acl.add_entry_full(acl::ACL_MASK, None, mode_group_to_acl_permissions(entry.mode))?; acl.add_entry_full(acl::ACL_MASK, None, default.mask_permissions)?;
acl.add_entry_full(acl::ACL_GROUP_OBJ, None, group_obj.permissions)?;
},
None => {
acl.add_entry_full(acl::ACL_GROUP_OBJ, None, mode_group_to_acl_permissions(entry.mode))?;
},
} }
for user in acl_user { for user in &attr.acl_default_user {
acl.add_entry_full(acl::ACL_USER, Some(user.uid), user.permissions)?; acl.add_entry_full(acl::ACL_USER, Some(user.uid), user.permissions)?;
} }
for group in acl_group { for group in &attr.acl_default_group {
acl.add_entry_full(acl::ACL_GROUP, Some(group.gid), group.permissions)?; acl.add_entry_full(acl::ACL_GROUP, Some(group.gid), group.permissions)?;
} }
let proc_path = Path::new("/proc/self/fd/").join(fd.to_string());
if !acl.is_valid() { if !acl.is_valid() {
bail!("Error while restoring ACL - ACL invalid"); bail!("Error while restoring ACL - ACL invalid");
} }
acl.set_file(&proc_path, acl::ACL_TYPE_ACCESS)?; acl.set_file(&proc_path, acl::ACL_TYPE_DEFAULT)?;
if let Some(default) = acl_default {
let mut acl = acl::ACL::init(5)?;
acl.add_entry_full(acl::ACL_USER_OBJ, None, default.user_obj_permissions)?;
acl.add_entry_full(acl::ACL_GROUP_OBJ, None, default.group_obj_permissions)?;
acl.add_entry_full(acl::ACL_OTHER, None, default.other_permissions)?;
if default.mask_permissions != std::u64::MAX {
acl.add_entry_full(acl::ACL_MASK, None, default.mask_permissions)?;
}
for user in acl_default_user {
acl.add_entry_full(acl::ACL_USER, Some(user.uid), user.permissions)?;
}
for group in acl_default_group {
acl.add_entry_full(acl::ACL_GROUP, Some(group.gid), group.permissions)?;
}
if !acl.is_valid() {
bail!("Error while restoring ACL - ACL invalid");
}
acl.set_file(&proc_path, acl::ACL_TYPE_DEFAULT)?;
}
self.restore_quota_projid(fd, &quota_projid)?;
} }
self.restore_quota_projid(fd, &attr.quota_projid)?;
Ok(head) Ok(())
} }
// Restore xattrs and fcaps to the given RawFd. // Restore xattrs and fcaps to the given RawFd.
@ -575,6 +566,8 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
filename: &OsStr filename: &OsStr
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut read_buffer: [u8; 64*1024] = unsafe { std::mem::uninitialized() }; let mut read_buffer: [u8; 64*1024] = unsafe { std::mem::uninitialized() };
let (head, attr) = self.read_attributes()
.map_err(|err| format_err!("Reading of file attributes failed - {}", err))?;
if let Some(fd) = parent_fd { if let Some(fd) = parent_fd {
let flags = OFlag::O_CREAT|OFlag::O_WRONLY|OFlag::O_EXCL; let flags = OFlag::O_CREAT|OFlag::O_WRONLY|OFlag::O_EXCL;
@ -582,11 +575,6 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
let mut file = file_openat(fd, filename, flags, open_mode) let mut file = file_openat(fd, filename, flags, open_mode)
.map_err(|err| format_err!("open file {:?} failed - {}", full_path, err))?; .map_err(|err| format_err!("open file {:?} failed - {}", full_path, err))?;
self.restore_ugid(&entry, file.as_raw_fd())?;
// fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
let head = self.restore_attributes(&entry, Some(file.as_raw_fd()))
.map_err(|err| format_err!("Restoring of file attributes failed - {}", err))?;
if head.htype != CA_FORMAT_PAYLOAD { if head.htype != CA_FORMAT_PAYLOAD {
bail!("got unknown header type for file entry {:016x}", head.htype); bail!("got unknown header type for file entry {:016x}", head.htype);
} }
@ -606,11 +594,12 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
done += n; done += n;
} }
self.restore_ugid(&entry, file.as_raw_fd())?;
// fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
self.restore_attributes(file.as_raw_fd(), &attr, &entry)?;
self.restore_mode(&entry, file.as_raw_fd())?; self.restore_mode(&entry, file.as_raw_fd())?;
self.restore_mtime(&entry, file.as_raw_fd())?; self.restore_mtime(&entry, file.as_raw_fd())?;
} else { } else {
let head = self.restore_attributes(&entry, None)
.map_err(|err| format_err!("Restoring of file attributes failed - {}", err))?;
if head.htype != CA_FORMAT_PAYLOAD { if head.htype != CA_FORMAT_PAYLOAD {
bail!("got unknown header type for file entry {:016x}", head.htype); bail!("got unknown header type for file entry {:016x}", head.htype);
} }
@ -646,12 +635,9 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
(None, None) (None, None)
}; };
if let Some(fd) = fd { let (mut head, attr) = self.read_attributes()
self.restore_ugid(&entry, fd)?; .map_err(|err| format_err!("Reading of directory attributes failed - {}", err))?;
}
// fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
let mut head = self.restore_attributes(&entry, fd)
.map_err(|err| format_err!("Restoring of directory attributes failed - {}", err))?;
while head.htype == CA_FORMAT_FILENAME { while head.htype == CA_FORMAT_FILENAME {
let name = self.read_filename(head.size)?; let name = self.read_filename(head.size)?;
@ -671,6 +657,9 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
// Only restore if we want to restore this part of the archive // Only restore if we want to restore this part of the archive
if let Some(fd) = fd { if let Some(fd) = fd {
self.restore_ugid(&entry, fd)?;
// fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
self.restore_attributes(fd, &attr, &entry)?;
self.restore_mode(&entry, fd)?; self.restore_mode(&entry, fd)?;
self.restore_mtime(&entry, fd)?; self.restore_mtime(&entry, fd)?;
} }