From 79f88bdf8d54a84b7bb727b0c28b2dfcdc3d14d5 Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Fri, 9 Dec 2016 14:52:12 -0600 Subject: Support backup/restore of FBE policies Change-Id: Iba8ef20f57b0fb57bb9406c53148a806441d0b59 --- libtar/Android.mk | 34 ++++++++++------ libtar/append.c | 32 ++++++++++++++++ libtar/block.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ libtar/extract.c | 28 +++++++++++++- libtar/libtar.h | 11 ++++++ 5 files changed, 205 insertions(+), 13 deletions(-) (limited to 'libtar') diff --git a/libtar/Android.mk b/libtar/Android.mk index 838b44175..14c19f73f 100644 --- a/libtar/Android.mk +++ b/libtar/Android.mk @@ -5,16 +5,21 @@ include $(CLEAR_VARS) LOCAL_MODULE := libtar LOCAL_MODULE_TAGS := eng optional -LOCAL_CFLAGS := -LOCAL_SRC_FILES = append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c +LOCAL_SRC_FILES := append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c LOCAL_C_INCLUDES += $(LOCAL_PATH) \ - external/zlib + external/zlib LOCAL_SHARED_LIBRARIES += libz libc ifeq ($(TWHAVE_SELINUX), true) - LOCAL_C_INCLUDES += external/libselinux/include - LOCAL_SHARED_LIBRARIES += libselinux - LOCAL_CFLAGS += -DHAVE_SELINUX + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_SHARED_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX +endif + +ifeq ($(TW_INCLUDE_CRYPTO_FBE), true) + LOCAL_SHARED_LIBRARIES += libe4crypt + LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT + LOCAL_C_INCLUDES += bootable/recovery/crypto/ext4crypt endif include $(BUILD_SHARED_LIBRARY) @@ -24,16 +29,21 @@ include $(CLEAR_VARS) LOCAL_MODULE := libtar_static LOCAL_MODULE_TAGS := eng optional -LOCAL_CFLAGS = -LOCAL_SRC_FILES = append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c +LOCAL_SRC_FILES := append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c LOCAL_C_INCLUDES += $(LOCAL_PATH) \ - external/zlib + external/zlib LOCAL_STATIC_LIBRARIES += libz libc ifeq ($(TWHAVE_SELINUX), true) - LOCAL_C_INCLUDES += external/libselinux/include - LOCAL_STATIC_LIBRARIES += libselinux - LOCAL_CFLAGS += -DHAVE_SELINUX + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_STATIC_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX +endif + +ifeq ($(TW_INCLUDE_CRYPTO_FBE), true) + LOCAL_SHARED_LIBRARIES += libe4crypt + LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT + LOCAL_C_INCLUDES += bootable/recovery/crypto/ext4crypt endif include $(BUILD_STATIC_LIBRARY) diff --git a/libtar/append.c b/libtar/append.c index 4be679ccd..438829753 100644 --- a/libtar/append.c +++ b/libtar/append.c @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef STDC_HEADERS # include @@ -34,6 +35,10 @@ # include "selinux/selinux.h" #endif +#ifdef HAVE_EXT4_CRYPT +# include "ext4crypt_tar.h" +#endif + struct tar_dev { dev_t td_dev; @@ -122,6 +127,33 @@ tar_append_file(TAR *t, const char *realname, const char *savename) } #endif +#ifdef HAVE_EXT4_CRYPT + if (TH_ISDIR(t) && t->options & TAR_STORE_EXT4_POL) + { + if (t->th_buf.e4crypt_policy != NULL) + { + free(t->th_buf.e4crypt_policy); + t->th_buf.e4crypt_policy = NULL; + } + + char e4crypt_policy[EXT4_KEY_DESCRIPTOR_SIZE]; + if (e4crypt_policy_get(realname, e4crypt_policy, EXT4_KEY_DESCRIPTOR_SIZE, 0)) + { + char tar_policy[EXT4_KEY_DESCRIPTOR_SIZE]; + memset(tar_policy, 0, sizeof(tar_policy)); + char policy_hex[EXT4_KEY_DESCRIPTOR_HEX]; + policy_to_hex(e4crypt_policy, policy_hex); + if (lookup_ref_key(e4crypt_policy, &tar_policy)) { + printf("found policy '%s' - '%s' - '%s'\n", realname, tar_policy, policy_hex); + t->th_buf.e4crypt_policy = strdup(tar_policy); + } else { + printf("failed to lookup tar policy for '%s' - '%s'\n", realname, policy_hex); + return -1; + } + } // else no policy found, but this is not an error as not all dirs will have a policy + } +#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 5d3c9d826..2fd61bb76 100644 --- a/libtar/block.c +++ b/libtar/block.c @@ -25,6 +25,10 @@ #define SELINUX_TAG "RHT.security.selinux=" #define SELINUX_TAG_LEN 21 +// Used to identify e4crypt_policy in extended ('x') +#define E4CRYPT_TAG "TWRP.security.e4crypt=" +#define E4CRYPT_TAG_LEN 22 + /* 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 @@ -119,6 +123,11 @@ th_read(TAR *t) if (t->th_buf.selinux_context != NULL) free(t->th_buf.selinux_context); #endif +#ifdef HAVE_EXT4_CRYPT + if (t->th_buf.e4crypt_policy != NULL) { + free(t->th_buf.e4crypt_policy); + } +#endif memset(&(t->th_buf), 0, sizeof(struct tar_header)); @@ -283,6 +292,57 @@ th_read(TAR *t) } #endif +#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); + if(start && start+E4CRYPT_TAG_LEN < buf+len) + { + start += E4CRYPT_TAG_LEN; + char *end = strchr(start, '\n'); + if(end) + { + t->th_buf.e4crypt_policy = strndup(start, end-start); +#ifdef DEBUG + printf(" th_read(): E4Crypt policy detected: %s\n", t->th_buf.e4crypt_policy); +#endif + } + } + } + + i = th_read_internal(t); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + } +#endif + return 0; } @@ -457,6 +517,59 @@ th_write(TAR *t) } #endif +#ifdef HAVE_EXT4_CRYPT + if((t->options & TAR_STORE_EXT4_POL) && t->th_buf.e4crypt_policy != NULL) + { +#ifdef DEBUG + 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; + + 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 "E4CRYPT_TAG"%s\n", (int)sz, t->th_buf.e4crypt_policy); + 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/extract.c b/libtar/extract.c index 6a63ff738..ba29a7771 100644 --- a/libtar/extract.c +++ b/libtar/extract.c @@ -32,6 +32,10 @@ # include "selinux/selinux.h" #endif +#ifdef HAVE_EXT4_CRYPT +# include "ext4crypt_tar.h" +#endif + const unsigned long long progress_size = (unsigned long long)(T_BLOCKSIZE); static int @@ -492,7 +496,7 @@ tar_extract_dir(TAR *t, const char *realname) } else { -#ifdef DEBUG +#if 1 //def DEBUG puts(" *** using existing directory"); #endif return 1; @@ -507,6 +511,28 @@ tar_extract_dir(TAR *t, const char *realname) } } +#ifdef HAVE_EXT4_CRYPT + if(t->th_buf.e4crypt_policy != NULL) + { +#ifdef DEBUG + printf("tar_extract_file(): restoring EXT4 crypt policy %s to dir %s\n", t->th_buf.e4crypt_policy, realname); +#endif + char binary_policy[EXT4_KEY_DESCRIPTOR_SIZE]; + if (!lookup_ref_tar(t->th_buf.e4crypt_policy, &binary_policy)) { + printf("error looking up proper e4crypt policy for '%s' - %s\n", realname, t->th_buf.e4crypt_policy); + return -1; + } + char policy_hex[EXT4_KEY_DESCRIPTOR_HEX]; + policy_to_hex(binary_policy, policy_hex); + printf("restoring policy %s > '%s' to '%s'\n", t->th_buf.e4crypt_policy, policy_hex, realname); + if (!e4crypt_policy_set(realname, binary_policy, EXT4_KEY_DESCRIPTOR_SIZE, 0)) + { + printf("tar_extract_file(): failed to restore EXT4 crypt policy %s to dir '%s' '%s'!!!\n", t->th_buf.e4crypt_policy, realname, policy_hex); + //return -1; // This may not be an error in some cases, so log and ignore + } + } +#endif + return 0; } diff --git a/libtar/libtar.h b/libtar/libtar.h index 4a513754f..ab5a3bede 100644 --- a/libtar/libtar.h +++ b/libtar/libtar.h @@ -19,6 +19,11 @@ #include "libtar_listhash.h" +#ifdef HAVE_EXT4_CRYPT +#define EXT4_KEY_DESCRIPTOR_SIZE 8 +#define EXT4_KEY_DESCRIPTOR_HEX 17 +#endif + #ifdef __cplusplus extern "C" { @@ -38,6 +43,7 @@ extern "C" /* extended metadata for next file - used to store selinux_context */ #define TH_EXT_TYPE 'x' +#define TH_POL_TYPE 'p' /* our version of the tar header structure */ struct tar_header @@ -64,6 +70,9 @@ struct tar_header #ifdef HAVE_SELINUX char *selinux_context; #endif +#ifdef HAVE_EXT4_CRYPT + char *e4crypt_policy; +#endif }; @@ -108,6 +117,7 @@ TAR; #define TAR_IGNORE_CRC 64 /* ignore CRC in file header */ #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 */ /* this is obsolete - it's here for backwards-compatibility only */ #define TAR_IGNORE_MAGIC 0 @@ -204,6 +214,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) /* decode tar header info */ #define th_get_crc(t) oct_to_int((t)->th_buf.chksum, sizeof((t)->th_buf.chksum)) -- cgit v1.2.3