[PATCH] write SELinux contexts when extracting a tar file

Denys Vlasenko vda.linux at googlemail.com
Mon Mar 15 16:59:47 UTC 2010


On Sat, Mar 13, 2010 at 1:20 AM, J. Tang <tang at jtang.org> wrote:
> 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>


> +#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;
> +       }

Why do you think such header is malformed?

> +
> +       /* 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);

(1) You just overwrote your "\n".
(2) Look at docs/tar_pax.txt example. There, sz == 52
and by reading only 52 bytes here, you fall out of sync with
tar's 512-byte blocks.

> +       do {
> +               /* skip leading whitespace */
> +               while (*p == ' ' || *p == '\t') {
> +                       p++;
> +               }

Maybe use p = skip_whitespace(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;
> +               }

Use bb_strtou, it makes error checking much simpler.
You will be able to drop isdigit check too.

> +               while (*p != '=' && *p != '\n') {
> +                       p++;
> +               }
> +               if (*p != '=') {
> +                       bb_error_msg("Malformed extended header: missing equal sign");
> +                       return;
> +               }

Can you use strchrnul instead of this block?

> +               while (*p != '\n') {
> +                       p++;
> +               }
> +               *p = '\0';

strchr

> +               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);

Drop "warning:" and use "'%s'", not "%s".
Imagine how message would look if value == ""...

-- 
vda


More information about the busybox mailing list