summaryrefslogtreecommitdiffstats
path: root/libtar
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libtar/Android.mk6
-rw-r--r--libtar/append.c25
-rw-r--r--libtar/block.c113
-rw-r--r--libtar/compat.h4
-rw-r--r--libtar/extract.c12
-rw-r--r--libtar/libtar.h8
6 files changed, 168 insertions, 0 deletions
diff --git a/libtar/Android.mk b/libtar/Android.mk
index b73a41f3f..7e0fafb51 100644
--- a/libtar/Android.mk
+++ b/libtar/Android.mk
@@ -11,5 +11,11 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH) \
external/zlib
LOCAL_SHARED_LIBRARIES += libz libc
+ifneq ($(wildcard external/libselinux/Android.mk),)
+ LOCAL_C_INCLUDES += external/libselinux/include
+ LOCAL_SHARED_LIBRARIES += libselinux
+ LOCAL_CFLAGS += -DHAVE_SELINUX
+endif
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/libtar/append.c b/libtar/append.c
index 05024b926..3a8bfc690 100644
--- a/libtar/append.c
+++ b/libtar/append.c
@@ -90,6 +90,31 @@ tar_append_file(TAR *t, char *realname, char *savename)
#endif
th_set_path(t, (savename ? savename : realname));
+#ifdef HAVE_SELINUX
+ /* get selinux context */
+ if(t->options & TAR_STORE_SELINUX)
+ {
+ if(t->th_buf.selinux_context != NULL)
+ {
+ free(t->th_buf.selinux_context);
+ t->th_buf.selinux_context = NULL;
+ }
+
+ security_context_t selinux_context = NULL;
+ if(getfilecon(realname, &selinux_context) >= 0)
+ {
+ t->th_buf.selinux_context = strdup(selinux_context);
+ freecon(selinux_context);
+ }
+ else
+ {
+#ifdef DEBUG
+ perror("Failed to get selinux context");
+#endif
+ }
+ }
+#endif
+
/* check if it's a hardlink */
#ifdef DEBUG
puts(" tar_append_file(): checking inode cache for hardlink...");
diff --git a/libtar/block.c b/libtar/block.c
index 89e5e3d70..1cfc0e418 100644
--- a/libtar/block.c
+++ b/libtar/block.c
@@ -21,6 +21,10 @@
#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
+// Used to identify selinux_context in extended ('x')
+// metadata. From RedHat implementation.
+#define SELINUX_TAG "RHT.security.selinux="
+#define SELINUX_TAG_LEN 21
/* read a header block */
int
@@ -101,6 +105,11 @@ th_read(TAR *t)
free(t->th_buf.gnu_longname);
if (t->th_buf.gnu_longlink != NULL)
free(t->th_buf.gnu_longlink);
+#ifdef HAVE_SELINUX
+ if (t->th_buf.selinux_context != NULL)
+ free(t->th_buf.selinux_context);
+#endif
+
memset(&(t->th_buf), 0, sizeof(struct tar_header));
i = th_read_internal(t);
@@ -203,6 +212,57 @@ th_read(TAR *t)
}
}
+#ifdef HAVE_SELINUX
+ if(TH_ISEXTHEADER(t))
+ {
+ sz = th_get_size(t);
+
+ if(sz >= T_BLOCKSIZE) // Not supported
+ {
+#ifdef DEBUG
+ printf(" th_read(): Extended header is too long!\n");
+#endif
+ }
+ else
+ {
+ char buf[T_BLOCKSIZE];
+ i = tar_block_read(t, buf);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ // To be sure
+ buf[T_BLOCKSIZE-1] = 0;
+
+ int len = strlen(buf);
+ char *start = strstr(buf, SELINUX_TAG);
+ if(start && start+SELINUX_TAG_LEN < buf+len)
+ {
+ start += SELINUX_TAG_LEN;
+ char *end = strchr(start, '\n');
+ if(end)
+ {
+ t->th_buf.selinux_context = strndup(start, end-start);
+#ifdef DEBUG
+ printf(" th_read(): SELinux context xattr detected: %s\n", t->th_buf.selinux_context);
+#endif
+ }
+ }
+ }
+
+ i = th_read_internal(t);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+ }
+#endif
+
#if 0
/*
** work-around for old archive files with broken typeflag fields
@@ -359,6 +419,59 @@ th_write(TAR *t)
th_set_size(t, sz2);
}
+#ifdef HAVE_SELINUX
+ if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
+ {
+#ifdef DEBUG
+ printf("th_write(): using selinux_context (\"%s\")\n",
+ t->th_buf.selinux_context);
+#endif
+ /* save old size and type */
+ type2 = t->th_buf.typeflag;
+ sz2 = th_get_size(t);
+
+ /* write out initial header block with fake size and type */
+ t->th_buf.typeflag = TH_EXT_TYPE;
+
+ /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
+ // size newline
+ sz = SELINUX_TAG_LEN + strlen(t->th_buf.selinux_context) + 3 + 1;
+
+ if(sz >= 100) // another ascci digit for size
+ ++sz;
+
+ if(sz >= T_BLOCKSIZE) // impossible
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ th_set_size(t, sz);
+ th_finish(t);
+ i = tar_block_write(t, &(t->th_buf));
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(buf, 0, T_BLOCKSIZE);
+ snprintf(buf, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", sz, t->th_buf.selinux_context);
+ i = tar_block_write(t, &buf);
+ if (i != T_BLOCKSIZE)
+ {
+ if (i != -1)
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* reset type and size to original values */
+ t->th_buf.typeflag = type2;
+ th_set_size(t, sz2);
+ }
+#endif
+
th_finish(t);
#ifdef DEBUG
diff --git a/libtar/compat.h b/libtar/compat.h
index 70ac2f435..d0862943b 100644
--- a/libtar/compat.h
+++ b/libtar/compat.h
@@ -12,6 +12,10 @@
# include <libgen.h>
#endif
+#ifdef HAVE_SELINUX
+#include "selinux/selinux.h"
+#endif
+
#if defined(NEED_BASENAME) && !defined(HAVE_BASENAME)
diff --git a/libtar/extract.c b/libtar/extract.c
index 613e29fff..d19ba859d 100644
--- a/libtar/extract.c
+++ b/libtar/extract.c
@@ -154,6 +154,18 @@ tar_extract_file(TAR *t, char *realname, char *prefix)
printf("FAILED SETTING PERMS: %d\n", i);
return i;
}
+
+#ifdef HAVE_SELINUX
+ if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
+ {
+#ifdef DEBUG
+ printf(" Restoring SELinux context %s to file %s\n", t->th_buf.selinux_context, realname);
+#endif
+ if(setfilecon(realname, t->th_buf.selinux_context) < 0)
+ fprintf(stderr, "Failed to restore SELinux context %s!\n", strerror(errno));
+ }
+#endif
+
/*
pathname_len = strlen(th_get_pathname(t)) + 1;
realname_len = strlen(realname) + 1;
diff --git a/libtar/libtar.h b/libtar/libtar.h
index e7a355a85..91523d043 100644
--- a/libtar/libtar.h
+++ b/libtar/libtar.h
@@ -35,6 +35,9 @@ extern "C"
#define GNU_LONGNAME_TYPE 'L'
#define GNU_LONGLINK_TYPE 'K'
+/* extended metadata for next file - used to store selinux_context */
+#define TH_EXT_TYPE 'x'
+
/* our version of the tar header structure */
struct tar_header
{
@@ -57,6 +60,9 @@ struct tar_header
char padding[12];
char *gnu_longname;
char *gnu_longlink;
+#ifdef HAVE_SELINUX
+ char *selinux_context;
+#endif
};
@@ -96,6 +102,7 @@ TAR;
#define TAR_CHECK_MAGIC 16 /* check magic in file header */
#define TAR_CHECK_VERSION 32 /* check version in file header */
#define TAR_IGNORE_CRC 64 /* ignore CRC in file header */
+#define TAR_STORE_SELINUX 128 /* store selinux context */
/* this is obsolete - it's here for backwards-compatibility only */
#define TAR_IGNORE_MAGIC 0
@@ -177,6 +184,7 @@ int th_write(TAR *t);
|| S_ISFIFO((mode_t)oct_to_int((t)->th_buf.mode)))
#define TH_ISLONGNAME(t) ((t)->th_buf.typeflag == GNU_LONGNAME_TYPE)
#define TH_ISLONGLINK(t) ((t)->th_buf.typeflag == GNU_LONGLINK_TYPE)
+#define TH_ISEXTHEADER(t) ((t)->th_buf.typeflag == TH_EXT_TYPE)
/* decode tar header info */
#define th_get_crc(t) oct_to_int((t)->th_buf.chksum)