Here is a second iteration of the patch to enhanch tar, incorporating suggestions be Denys. It is baselined against the most recent commit to origin/master.
Signed-off-by: Jason Tang <[email protected]> --- archival/Config.in | 8 +++ archival/libunarchive/data_extract_all.c | 4 + archival/libunarchive/get_header_tar.c | 101 +++++++++++++++++++++++++++++- 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/archival/Config.in b/archival/Config.in index c99896b..deacc28 100644 --- a/archival/Config.in +++ b/archival/Config.in @@ -289,6 +289,14 @@ config FEATURE_TAR_NOPRESERVE_TIME 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. + config UNCOMPRESS bool "uncompress" default n 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 d5b86ff..914f145 100644 --- a/archival/libunarchive/get_header_tar.c +++ b/archival/libunarchive/get_header_tar.c @@ -103,6 +103,92 @@ 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) { + 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++; + } + + /* scan for the length of the record */ + len = bb_strtou(p, &keyword, 10); + /* expect errno to be EINVAL, because the character + following the digits is supposed to be a space */ + if ((errno != EINVAL) || ((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("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) { @@ -150,7 +236,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 */ @@ -393,7 +479,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; @@ -404,6 +493,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] / http://www.jtang.org _______________________________________________ busybox mailing list [email protected] http://lists.busybox.net/mailman/listinfo/busybox
