diff options
Diffstat (limited to '')
-rw-r--r-- | libtar/Android.mk | 6 | ||||
-rw-r--r-- | libtar/append.c | 25 | ||||
-rw-r--r-- | libtar/block.c | 113 | ||||
-rw-r--r-- | libtar/compat.h | 4 | ||||
-rw-r--r-- | libtar/extract.c | 12 | ||||
-rw-r--r-- | libtar/libtar.h | 8 | ||||
-rw-r--r-- | twrpTar.cpp | 20 |
7 files changed, 178 insertions, 10 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) diff --git a/twrpTar.cpp b/twrpTar.cpp index 5a9340a25..9c151b939 100644 --- a/twrpTar.cpp +++ b/twrpTar.cpp @@ -860,10 +860,10 @@ void* twrpTar::extractMulti(void *cookie) { int twrpTar::addFilesToExistingTar(vector <string> files, string fn) { char* charTarFile = (char*) fn.c_str(); - if (tar_open(&t, charTarFile, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU) == -1) + if (tar_open(&t, charTarFile, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1) return -1; removeEOT(charTarFile); - if (tar_open(&t, charTarFile, NULL, O_WRONLY | O_APPEND | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU) == -1) + if (tar_open(&t, charTarFile, NULL, O_WRONLY | O_APPEND | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1) return -1; for (unsigned int i = 0; i < files.size(); ++i) { char* file = (char*) files.at(i).c_str(); @@ -956,7 +956,7 @@ int twrpTar::createTar() { close(pipes[2]); close(pipes[3]); fd = pipes[1]; - if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU) != 0) { + if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { close(fd); LOGERR("tar_fdopen failed\n"); return -1; @@ -1002,7 +1002,7 @@ int twrpTar::createTar() { // Parent close(pigzfd[0]); // close parent input fd = pigzfd[1]; // copy parent output - if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU) != 0) { + if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { close(fd); LOGERR("tar_fdopen failed\n"); return -1; @@ -1042,7 +1042,7 @@ int twrpTar::createTar() { // Parent close(oaesfd[0]); // close parent input fd = oaesfd[1]; // copy parent output - if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU) != 0) { + if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { close(fd); LOGERR("tar_fdopen failed\n"); return -1; @@ -1052,7 +1052,7 @@ int twrpTar::createTar() { } else { // Not compressed or encrypted init_libtar_buffer(0); - if (tar_open(&t, charTarFile, &type, O_WRONLY | O_CREAT | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU) == -1) { + if (tar_open(&t, charTarFile, &type, O_WRONLY | O_CREAT | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1) { LOGERR("tar_open error opening '%s'\n", tarfn.c_str()); return -1; } @@ -1135,7 +1135,7 @@ int twrpTar::openTar() { close(pipes[1]); close(pipes[3]); fd = pipes[2]; - if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU) != 0) { + if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { close(fd); LOGERR("tar_fdopen failed\n"); return -1; @@ -1176,7 +1176,7 @@ int twrpTar::openTar() { // Parent close(oaesfd[1]); // close parent output fd = oaesfd[0]; // copy parent input - if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU) != 0) { + if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { close(fd); LOGERR("tar_fdopen failed\n"); return -1; @@ -1213,13 +1213,13 @@ int twrpTar::openTar() { // Parent close(pigzfd[1]); // close parent output fd = pigzfd[0]; // copy parent input - if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU) != 0) { + if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { close(fd); LOGERR("tar_fdopen failed\n"); return -1; } } - } else if (tar_open(&t, charTarFile, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU) != 0) { + } else if (tar_open(&t, charTarFile, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { LOGERR("Unable to open tar archive '%s'\n", charTarFile); return -1; } |