Below is the third iteration of my patch, incorporating further suggestions. One thing fixed was a memory leak if an extended record had an invalid length.
Signed-off-by: Jason Tang <[email protected]> --- archival/Config.in | 8 +++ archival/libunarchive/data_extract_all.c | 4 ++ archival/libunarchive/get_header_tar.c | 74 +++++++++++++++++++++++++++++- 3 files changed, 84 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..0bd9878 100644 --- a/archival/libunarchive/get_header_tar.c +++ b/archival/libunarchive/get_header_tar.c @@ -103,6 +103,65 @@ 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, *keyword, *value; + off_t len; + + /* prevent a malloc of 0 */ + if (sz == 0) { + return; + } + + p = buf = xmalloc(sz); + xread(archive_handle->src_fd, buf, sz); + archive_handle->offset += sz; + + /* Forcibly add a newline at the end of the buffer, to prevent + any buffer overflows. Even though the newline character + will be overwritten later, it is still needed to mark the + end of record lengths, in the call to bb_strtou() below. */ + buf[sz - 1] = '\n'; + + do { + /* 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) || (*keyword != ' ') || + ((p + len) > (buf + sz)) || (len == 0)) { + bb_error_msg("Malformed extended header: length is out of allowed range"); + free(buf); + return; + } + + p += len; + /* overwrite the terminating newline with a null. if + it was not a newline, the extended header is + invalid, so overwrite it anyways. */ + *(p - 1) = '\0'; + + /* handle keywords that are SELinux related */ + if (strncmp(keyword + 1, SELINUX_CONTEXT_KEYWORD "=", sizeof(SELINUX_CONTEXT_KEYWORD "=") - 1) == 0) { + value = keyword + sizeof(SELINUX_CONTEXT_KEYWORD "="); + 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 +209,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 +452,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 +466,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
