[PATCH] write SELinux contexts when extracting a tar file

J. Tang tang at jtang.org
Sat Mar 13 00:20:55 UTC 2010


While doing some experiments with the current version of busybox out  
of the git repo, I discovered that tar does ignores SELinux file  
contexts while extracting a tarball.  The following patch:

1. Adds a config option, FEATURE_TAR_SELINUX, to enable this feature,  
and
2. Modifies parsing of tar headers to respect SELinux contexts stored  
in the tarball.

Technically, there is no standard yet for recording SELinux  
contexts.  The most prevalent method is by a Red Hat vendor-specific  
tag, "RHT.security.selinux".  The proposed patch is flexible in that  
it can easily support future standards.

Note that this patch only deals with tarball extraction.  It does not  
modify tarball creation.

Signed-off-by: Jason Tang <tang at jtang.org>

---
  archival/Config.in                       |    8 ++
  archival/libunarchive/data_extract_all.c |    4 +
  archival/libunarchive/get_header_tar.c   |  107 ++++++++++++++++++++ 
+++++++++-
  3 files changed, 117 insertions(+), 2 deletions(-)

diff --git a/archival/Config.in b/archival/Config.in
index cf771f9..10e150e 100644
--- a/archival/Config.in
+++ b/archival/Config.in
@@ -283,6 +283,14 @@ config FEATURE_TAR_NOPRESERVE_TIME
  	help
  	  With this option busybox supports GNU tar -m (do not preserve  
time) option.

+config FEATURE_TAR_SELINUX
+	bool "Support for extracting SELinux labels"
+	default n
+	depends on TAR && SELINUX
+	help
+	  With this option busybox supports restoring SELinux labels
+	  when extracting files from tar archives.
+
  endif #tar

  config UNCOMPRESS
diff --git a/archival/libunarchive/data_extract_all.c b/archival/ 
libunarchive/data_extract_all.c
index 58b0533..57c5ec4 100644
--- a/archival/libunarchive/data_extract_all.c
+++ b/archival/libunarchive/data_extract_all.c
@@ -158,4 +158,8 @@ void FAST_FUNC data_extract_all(archive_handle_t  
*archive_handle)
  			utimes(file_header->name, t);
  		}
  	}
+#if ENABLE_FEATURE_TAR_SELINUX
+	/* always reset the context after creating an entry */
+	(void) setfscreatecon(NULL);
+#endif
  }
diff --git a/archival/libunarchive/get_header_tar.c b/archival/ 
libunarchive/get_header_tar.c
index 982404d..a8d0e87 100644
--- a/archival/libunarchive/get_header_tar.c
+++ b/archival/libunarchive/get_header_tar.c
@@ -99,6 +99,98 @@ static unsigned long long getOctal(char *str, int  
len)
  }
  #define GET_OCTAL(a) getOctal((a), sizeof(a))

+#if ENABLE_FEATURE_TAR_SELINUX
+/* Scan a PAX extended header for SELinux contexts, via
+ * "RHT.security.selinux" keyword.  This is the same vendor specific
+ * keyword used by Red Hat's patched version of tar.
+ */
+#define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux"
+
+static void parse_extended_header(archive_handle_t *archive_handle,  
off_t sz)
+{
+	char *buf, *p, *next_p, *keyword, *value;
+	off_t len;
+
+	/* prevent a malloc of 0 */
+	if (sz == 0) {
+		bb_error_msg("Malformed extended header: length is 0");
+		return;
+	}
+
+	/* forcibly add a newline at the end of the buffer, to prevent
+	   any buffer overflows */
+	p = buf = xmalloc(sz);
+	buf[sz - 1] = '\n';
+
+	xread(archive_handle->src_fd, buf, sz);
+	archive_handle->offset += sz;
+
+	do {
+		/* skip leading whitespace */
+		while (*p == ' ' || *p == '\t') {
+			p++;
+		}
+
+		if (!isdigit(*p)) {
+			bb_error_msg("Malformed extended header: missing length");
+			return;
+		}
+
+		/* scan for the length of the record */
+
+		errno = 0;
+		len = strtoul(p, &keyword, 10);
+		if ((errno == ERANGE) || ((p + len) > (buf + sz))) {
+			bb_error_msg("Malformed extended header: length is out of allowed  
range");
+			return;
+		}
+
+		next_p = p + len;
+
+		p = keyword;
+
+		/* scan for the start of the keyword and the value;
+		   they are '=' separated */
+
+		while (*p == ' ' || *p == '\t') {
+			p++;
+		}
+		keyword = p;
+
+		while (*p != '=' && *p != '\n') {
+			p++;
+		}
+		if (*p != '=') {
+			bb_error_msg("Malformed extended header: missing equal sign");
+			return;
+		}
+
+		*p = '\0';
+
+		p++;
+		value = p;
+		while (*p != '\n') {
+			p++;
+		}
+		*p = '\0';
+
+		p = next_p;
+
+		/* handle fields that are SELinux related */
+		if (strcmp(keyword, SELINUX_CONTEXT_KEYWORD) == 0) {
+			/* free old context, in case context is given
+			   multiple times */
+			if (setfscreatecon(value) < 0) {
+				bb_perror_msg("warning: cannot set label to %s", value);
+			}
+		}
+
+	} while (p < buf + sz);
+
+	free(buf);
+}
+#endif
+
  void BUG_tar_header_size(void);
  char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
  {
@@ -146,7 +238,7 @@ char FAST_FUNC get_header_tar(archive_handle_t  
*archive_handle)
  	if (sizeof(tar) != 512)
  		BUG_tar_header_size();

-#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS || ENABLE_FEATURE_TAR_SELINUX
   again:
  #endif
  	/* Align header */
@@ -389,7 +481,10 @@ char FAST_FUNC get_header_tar(archive_handle_t  
*archive_handle)
  	case 'V':	/* Volume header */
  #endif
  	case 'g':	/* pax global header */
-	case 'x': {	/* pax extended header */
+#if !ENABLE_FEATURE_TAR_SELINUX
+	case 'x':	/* pax extended header, which is skipped */
+#endif
+	{
  		off_t sz;
  		bb_error_msg("warning: skipping header '%c'", tar.typeflag);
  		sz = (file_header->size + 511) & ~(off_t)511;
@@ -400,6 +495,14 @@ char FAST_FUNC get_header_tar(archive_handle_t  
*archive_handle)
  		/* return get_header_tar(archive_handle); */
  		goto again_after_align;
  	}
+#if ENABLE_FEATURE_TAR_SELINUX
+	case 'x': {	/* pax extended header */
+		/* read the rest of the extended header, and then
+		   parse its contents */
+		parse_extended_header(archive_handle, file_header->size);
+		goto again;
+#endif
+	}
  	default:
  		bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag);
  	}
-- 
1.6.6.1


-- 
Jason Tang  /  tang at jtang.org




More information about the busybox mailing list