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 <[email protected]>

---
 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  /  [email protected]


_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to