From 7118774ec94bb1a2e8ef7a5f8cbcb6b781ccfdd8 Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Fri, 13 Jan 2017 13:30:10 -0600 Subject: libtar: backup and restore posix capabilities This patch also allows libtar to combine data from multiple extended tar headers into a single header. Change-Id: I82d13e89a3622ea665b60062b1904ddbedfa41b3 --- libtar/append.c | 22 ++++++ libtar/block.c | 224 ++++++++++++++++++++++++++++--------------------------- libtar/extract.c | 14 ++++ libtar/libtar.h | 11 ++- libtar/util.c | 9 +++ twrpTar.cpp | 4 +- 6 files changed, 171 insertions(+), 113 deletions(-) diff --git a/libtar/append.c b/libtar/append.c index 438829753..7c679f66d 100644 --- a/libtar/append.c +++ b/libtar/append.c @@ -22,6 +22,10 @@ #include #include +#include +#include +#include + #ifdef STDC_HEADERS # include # include @@ -154,6 +158,24 @@ tar_append_file(TAR *t, const char *realname, const char *savename) } #endif + /* get posix file capabilities */ + if (TH_ISREG(t) && t->options & TAR_STORE_POSIX_CAP) + { + if (t->th_buf.has_cap_data) + { + memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data)); + t->th_buf.has_cap_data = 0; + } + + if (getxattr(realname, XATTR_NAME_CAPS, &t->th_buf.cap_data, sizeof(struct vfs_cap_data)) >= 0) + { + t->th_buf.has_cap_data = 1; +#if 1 //def DEBUG + print_caps(&t->th_buf.cap_data); +#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 2fd61bb76..a1178579b 100644 --- a/libtar/block.c +++ b/libtar/block.c @@ -29,6 +29,10 @@ #define E4CRYPT_TAG "TWRP.security.e4crypt=" #define E4CRYPT_TAG_LEN 22 +// Used to identify Posix capabilities in extended ('x') +#define CAPABILITIES_TAG "SCHILY.xattr.security.capability=" +#define CAPABILITIES_TAG_LEN 33 + /* read a header block */ /* FIXME: the return value of this function should match the return value of tar_block_read(), which is a macro which references a prototype @@ -128,6 +132,11 @@ th_read(TAR *t) free(t->th_buf.e4crypt_policy); } #endif + if (t->th_buf.has_cap_data) + { + memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data)); + t->th_buf.has_cap_data = 0; + } memset(&(t->th_buf), 0, sizeof(struct tar_header)); @@ -241,8 +250,8 @@ th_read(TAR *t) } } -#ifdef HAVE_SELINUX - if(TH_ISEXTHEADER(t)) + // Extended headers (selinux contexts, posix file capabilities, ext4 encryption policies) + while(TH_ISEXTHEADER(t) || TH_ISPOLHEADER(t)) { sz = th_get_size(t); @@ -267,7 +276,19 @@ th_read(TAR *t) buf[T_BLOCKSIZE-1] = 0; int len = strlen(buf); - char *start = strstr(buf, SELINUX_TAG); + // posix capabilities + char *start = strstr(buf, CAPABILITIES_TAG); + if(start && start+CAPABILITIES_TAG_LEN < buf+len) + { + start += CAPABILITIES_TAG_LEN; + memcpy(&t->th_buf.cap_data, start, sizeof(struct vfs_cap_data)); + t->th_buf.has_cap_data = 1; +#ifdef DEBUG + printf(" th_read(): Posix capabilities detected\n"); +#endif + } // end posix capabilities +#ifdef HAVE_SELINUX // selinux contexts + start = strstr(buf, SELINUX_TAG); if(start && start+SELINUX_TAG_LEN < buf+len) { start += SELINUX_TAG_LEN; @@ -280,45 +301,9 @@ th_read(TAR *t) #endif } } - } - - i = th_read_internal(t); - if (i != T_BLOCKSIZE) - { - if (i != -1) - errno = EINVAL; - return -1; - } - } -#endif - +#endif // HAVE_SELINUX #ifdef HAVE_EXT4_CRYPT - if(TH_ISPOLHEADER(t)) - { - sz = th_get_size(t); - - if(sz >= T_BLOCKSIZE) // Not supported - { -#ifdef DEBUG - printf(" th_read(): Policy 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, E4CRYPT_TAG); + start = strstr(buf, E4CRYPT_TAG); if(start && start+E4CRYPT_TAG_LEN < buf+len) { start += E4CRYPT_TAG_LEN; @@ -331,6 +316,7 @@ th_read(TAR *t) #endif } } +#endif // HAVE_EXT4_CRYPT } i = th_read_internal(t); @@ -341,11 +327,55 @@ th_read(TAR *t) return -1; } } -#endif return 0; } +/* write an extended block */ +static int +th_write_extended(TAR *t, char* buf, uint64_t sz) +{ + char type2; + uint64_t sz2; + int i; + + /* 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; + + 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; + } + + 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); + memset(buf, 0, T_BLOCKSIZE); + return 0; +} /* write a header block */ int @@ -353,7 +383,7 @@ th_write(TAR *t) { int i, j; char type2; - uint64_t sz, sz2; + uint64_t sz, sz2, total_sz = 0; char *ptr; char buf[T_BLOCKSIZE]; @@ -464,6 +494,8 @@ th_write(TAR *t) th_set_size(t, sz2); } + memset(buf, 0, T_BLOCKSIZE); + ptr = buf; #ifdef HAVE_SELINUX if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL) { @@ -471,13 +503,6 @@ th_write(TAR *t) 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; @@ -485,35 +510,9 @@ th_write(TAR *t) 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", (int)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); + total_sz += sz; + snprintf(ptr, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", (int)sz, t->th_buf.selinux_context); + ptr += sz; } #endif @@ -524,13 +523,6 @@ th_write(TAR *t) printf("th_write(): using e4crypt_policy %s\n", t->th_buf.e4crypt_policy); #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_POL_TYPE; - /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */ // size newline sz = E4CRYPT_TAG_LEN + EXT4_KEY_DESCRIPTOR_HEX + 3 + 1; @@ -538,37 +530,51 @@ th_write(TAR *t) if(sz >= 100) // another ascci digit for size ++sz; - if(sz >= T_BLOCKSIZE) // impossible + if (total_sz + sz >= T_BLOCKSIZE) { - errno = EINVAL; - return -1; + if (th_write_extended(t, &buf, total_sz)) + return -1; + ptr = buf; + total_sz = sz; } + else + total_sz += sz; - 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; - } + snprintf(ptr, T_BLOCKSIZE, "%d "E4CRYPT_TAG"%s", (int)sz, t->th_buf.e4crypt_policy); + char *nlptr = ptr + sz - 1; + *nlptr = '\n'; + ptr += sz; + } +#endif - memset(buf, 0, T_BLOCKSIZE); - snprintf(buf, T_BLOCKSIZE, "%d "E4CRYPT_TAG"%s\n", (int)sz, t->th_buf.e4crypt_policy); - i = tar_block_write(t, &buf); - if (i != T_BLOCKSIZE) + if((t->options & TAR_STORE_POSIX_CAP) && t->th_buf.has_cap_data) + { +#ifdef DEBUG + printf("th_write(): has a posix capability\n"); +#endif + sz = CAPABILITIES_TAG_LEN + sizeof(struct vfs_cap_data) + 3 + 1; + + if(sz >= 100) // another ascci digit for size + ++sz; + + if (total_sz + sz >= T_BLOCKSIZE) { - if (i != -1) - errno = EINVAL; - return -1; + if (th_write_extended(t, &buf, total_sz)) + return -1; + ptr = buf; + total_sz = sz; } + else + total_sz += sz; - /* reset type and size to original values */ - t->th_buf.typeflag = type2; - th_set_size(t, sz2); + snprintf(ptr, T_BLOCKSIZE, "%d "CAPABILITIES_TAG, (int)sz); + memcpy(ptr + CAPABILITIES_TAG_LEN + 3, &t->th_buf.cap_data, sizeof(struct vfs_cap_data)); + char *nlptr = ptr + sz - 1; + *nlptr = '\n'; + ptr += sz; } -#endif + if (total_sz > 0 && th_write_extended(t, &buf, total_sz)) // write any outstanding tar extended header + return -1; th_finish(t); diff --git a/libtar/extract.c b/libtar/extract.c index ba29a7771..9e24e8ee7 100644 --- a/libtar/extract.c +++ b/libtar/extract.c @@ -20,6 +20,10 @@ #include #include +#include +#include +#include + #ifdef STDC_HEADERS # include #endif @@ -166,6 +170,16 @@ tar_extract_file(TAR *t, const char *realname, const char *prefix, const int *pr } #endif + if((t->options & TAR_STORE_POSIX_CAP) && t->th_buf.has_cap_data) + { +#if 1 //def DEBUG + printf("tar_extract_file(): restoring posix capabilities to file %s\n", realname); + print_caps(&t->th_buf.cap_data); +#endif + if (setxattr(realname, XATTR_NAME_CAPS, &t->th_buf.cap_data, sizeof(struct vfs_cap_data), 0) < 0) + fprintf(stderr, "tar_extract_file(): failed to restore posix capabilities to file %s !!!\n", realname); + } + #ifdef LIBTAR_FILE_HASH pn = th_get_pathname(t); pathname_len = strlen(pn) + 1; diff --git a/libtar/libtar.h b/libtar/libtar.h index ab5a3bede..8c4202817 100644 --- a/libtar/libtar.h +++ b/libtar/libtar.h @@ -15,6 +15,7 @@ #include #include +#include #include "tar.h" #include "libtar_listhash.h" @@ -43,7 +44,7 @@ extern "C" /* extended metadata for next file - used to store selinux_context */ #define TH_EXT_TYPE 'x' -#define TH_POL_TYPE 'p' +#define TH_POL_TYPE_DO_NOT_USE 'p' /* our version of the tar header structure */ struct tar_header @@ -73,6 +74,8 @@ struct tar_header #ifdef HAVE_EXT4_CRYPT char *e4crypt_policy; #endif + int has_cap_data; + struct vfs_cap_data cap_data; }; @@ -118,6 +121,7 @@ TAR; #define TAR_STORE_SELINUX 128 /* store selinux context */ #define TAR_USE_NUMERIC_ID 256 /* favor numeric owner over names */ #define TAR_STORE_EXT4_POL 512 /* store ext4 crypto policy */ +#define TAR_STORE_POSIX_CAP 1024 /* store posix file capabilities */ /* this is obsolete - it's here for backwards-compatibility only */ #define TAR_IGNORE_MAGIC 0 @@ -214,7 +218,7 @@ int th_write(TAR *t); #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) -#define TH_ISPOLHEADER(t) ((t)->th_buf.typeflag == TH_POL_TYPE) +#define TH_ISPOLHEADER(t) ((t)->th_buf.typeflag == TH_POL_TYPE_DO_NOT_USE) /* decode tar header info */ #define th_get_crc(t) oct_to_int((t)->th_buf.chksum, sizeof((t)->th_buf.chksum)) @@ -323,6 +327,9 @@ void int_to_oct(int64_t num, char *oct, size_t octlen); /* integer to string-octal conversion, or binary as necessary */ void int_to_oct_ex(int64_t num, char *oct, size_t octlen); +/* prints posix file capabilities */ +void print_caps(struct vfs_cap_data *cap_data); + /***** wrapper.c **********************************************************/ diff --git a/libtar/util.c b/libtar/util.c index f472f38b5..7fb3f51f0 100644 --- a/libtar/util.c +++ b/libtar/util.c @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef STDC_HEADERS # include @@ -210,3 +211,11 @@ int_to_oct_ex(int64_t num, char *oct, size_t octlen) } int_to_oct(num, oct, octlen); } + +void print_caps(struct vfs_cap_data *cap_data) { + printf(" magic_etc=%u \n", cap_data->magic_etc); + printf(" data[0].permitted=%u \n", cap_data->data[0].permitted); + printf(" data[0].inheritable=%u \n", cap_data->data[0].inheritable); + printf(" data[1].permitted=%u \n", cap_data->data[1].permitted); + printf(" data[1].inheritable=%u \n", cap_data->data[1].inheritable); +} diff --git a/twrpTar.cpp b/twrpTar.cpp index 4f9b63303..27c7c7b1b 100644 --- a/twrpTar.cpp +++ b/twrpTar.cpp @@ -55,9 +55,9 @@ extern "C" { #ifdef TW_INCLUDE_FBE #include "crypto/ext4crypt/ext4crypt_tar.h" -#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_EXT4_POL +#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP |TAR_STORE_EXT4_POL #else -#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX +#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP #endif using namespace std; -- cgit v1.2.3