Skip to content

Commit

Permalink
Make TarArchive preserve permissions and SELinux context
Browse files Browse the repository at this point in the history
Make the TarArchive class preserve permissions and SELinux context on
all files and directories it collects. This requires us to recursively
add the parent directories of any files copied in by full path (or the
directory would only be implicitly created and so receive default
ownership and permissions and no SELinux context).

To make this work, don't call tarfile.add() to recursively add
directories - the doCopyFileOrDir machinery in the Plugin class already
handles directory recursion properly. Instead when a directory is passed
to add_file(), just create a TarInfo record of type tarfile.DIRTYPE and
propagate the permissions from the host file system.

The SELinux contexts have the unfortunate side-effect of spewing errors
if unpacked without --no-selinux as some contexts cannot be placed on
some file systems.

It may be more useful to eventually store the contexts in the
in a file rather than directly in the tarball.

Before:

drwxr-xr-x.  9 root root  640 Dec  7 00:57 proc
lrwxrwxrwx.  1 root root   30 Dec  7 00:57 ps -> sos_commands/process/ps_auxwww
drwxr-xr-x.  2 root root   60 Dec  7 00:57 root
drwxr-xr-x.  2 root root   80 Dec  7 00:57 sbin

After:

dr-xr-xr-x.  9 root root  640 Dec  5 19:07 proc
lrwxrwxrwx.  1 root root   30 Dec  7 00:58 ps -> sos_commands/process/ps_auxwww
dr-xr-x---.  2 root root   60 Dec  7 00:48 root
dr-xr-xr-x.  2 root root   80 Dec  7 00:57 sbin

More fixes issue #76
  • Loading branch information
bmr-cymru committed Dec 7, 2012
1 parent f30f0cf commit 179d9bb
Showing 1 changed file with 53 additions and 18 deletions.
71 changes: 53 additions & 18 deletions sos/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import hashlib
import logging
import fnmatch
import selinux

from contextlib import closing
try:
Expand Down Expand Up @@ -201,39 +202,74 @@ def compress(self, method):

self.close()


class TarFileArchive(Archive):

def __init__(self, name):
self._name = name
self._suffix = "tar"
self.tarfile = tarfile.open(self.name(), mode="w")

self.tarfile = tarfile.open(self.name(),
mode="w", format=tarfile.PAX_FORMAT)

# this can be used to set permissions if using the
# tarfile.add() interface to add directory trees.
def copy_permissions_filter(self, tar_info):
orig_path = tar_info.name[len(os.path.split(self._name)[-1]):]
fstat = os.stat(orig_path)
context = self.get_selinux_context(orig_path)
if(context):
tar_info.pax_headers['RHT.security.selinux'] = context
self.set_tar_info_from_stat(tar_info,fstat)
print "filtering orig_path=%s context=%s" % (orig_path, context)
return tar_info

def get_selinux_context(self, path):
try:
(rc, c) = selinux.getfilecon(path)
return c
except:
return None

def set_tar_info_from_stat(self, tar_info, fstat):
tar_info.mtime = "%.9f" % fstat.st_mtime
tar_info.pax_headers['atime'] = "%.9f" % fstat.st_atime
tar_info.pax_headers['ctime'] = "%.9f" % fstat.st_ctime
tar_info.mode = fstat.st_mode
tar_info.uid = fstat.st_uid
tar_info.gid = fstat.st_gid

def name(self):
return "%s.%s" % (self._name, self._suffix)

def add_parent(self, path):
if (path == '/'):
return
path = os.path.split(path)[0]
self.add_file(path)

def add_file(self, src, dest=None):
if dest:
dest = self.prepend(dest)
else:
dest = self.prepend(src)

tar_info = tarfile.TarInfo(name=dest)

if os.path.isdir(src):
self.tarfile.add(src, dest)
tar_info.type = tarfile.DIRTYPE
fileobj = None
else:
fp = open(src, 'rb')
content = fp.read()
fp.close()
fstat = os.stat(src)
tar_info = tarfile.TarInfo(name=dest)
tar_info.size = len(content)
tar_info.mtime = fstat.st_mtime
tar_info.pax_headers['atime'] = fstat.st_atime
tar_info.mode = fstat.st_mode
tar_info.uid = fstat.st_uid
tar_info.gid = fstat.st_gid

self.tarfile.addfile(tar_info, StringIO(content))
fileobj = StringIO(content)
fstat = os.stat(src)
context = self.get_selinux_context(src)
if context:
tar_info.pax_headers['RHT.security.selinux'] = context
self.set_tar_info_from_stat(tar_info,fstat)
self.add_parent(src)
self.tarfile.addfile(tar_info, fileobj)

def add_string(self, content, dest):
fstat = None
Expand All @@ -243,11 +279,10 @@ def add_string(self, content, dest):
tar_info = tarfile.TarInfo(name=dest)
tar_info.size = len(content)
if fstat:
tar_info.mtime = fstat.st_mtime
tar_info.pax_headers['atime'] = fstat.st_atime
tar_info.mode = fstat.st_mode
tar_info.uid = fstat.st_uid
tar_info.gid = fstat.st_gid
context = self.get_selinux_context(dest)
if context:
tar_info.pax_headers['RHT.security.selinux'] = context
self.set_tar_info_from_stat(tar_info, fstat)
else:
tar_info.mtime = time.time()
self.tarfile.addfile(tar_info, StringIO(content))
Expand Down

1 comment on commit 179d9bb

@bmr-cymru
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this has a few consequences that I am not entirely happy with:

  • Breaks when the collection/analysis host's policies differ (failure to set context on unpacked files)
  • Problems with virtual file system contexts (/proc and /sys) since these cannot be set on "normal" file system inodes
  • Forces us to use PAX tar format which not supported by all tar implementations (this is important for our diagnostics folks who typically work in Java).

Overall I'm not sure I could release sos with the current form of this change. I'm more inclined to move toward a model of including files of the form:

..

Where could be "selinux", "acl", "extattr", "xattr", "fscap" etc. - this would allow us to support many more types of additional information with no change to the tar format we use and also side-steps all of the complications with mismatching policies.

I'm not aware of a case where we would really need the context labels on files themselves (unless there are tools we want to use that expect to read it directly). If this were the case then we could probably provide a script to re-apply contexts from these files where possible assuming that a properly matched system is in use.

Please sign in to comment.