Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7118774e authored by Ethan Yonker's avatar Ethan Yonker Committed by Dees Troy
Browse files

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
parent a083dc68
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -22,6 +22,10 @@
#include <sys/types.h>
#include <stdbool.h>

#include <sys/capability.h>
#include <sys/xattr.h>
#include <linux/xattr.h>

#ifdef STDC_HEADERS
# include <stdlib.h>
# include <string.h>
@@ -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...");
+115 −109
Original line number Diff line number Diff line
@@ -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,6 +301,22 @@ th_read(TAR *t)
#endif
				}
			}
#endif // HAVE_SELINUX
#ifdef HAVE_EXT4_CRYPT
			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
				}
			}
#endif // HAVE_EXT4_CRYPT
		}

		i = th_read_internal(t);
@@ -290,23 +327,34 @@ th_read(TAR *t)
			return -1;
		}
	}
#endif

#ifdef HAVE_EXT4_CRYPT
	if(TH_ISPOLHEADER(t))
	return 0;
}

/* write an extended block */
static int
th_write_extended(TAR *t, char* buf, uint64_t sz)
{
		sz = th_get_size(t);
	char type2;
	uint64_t sz2;
	int i;

		if(sz >= T_BLOCKSIZE) // Not supported
	/* 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
	{
#ifdef DEBUG
			printf("    th_read(): Policy header is too long!\n");
#endif
		errno = EINVAL;
		return -1;
	}
		else
		{
			char buf[T_BLOCKSIZE];
			i = tar_block_read(t, buf);

	th_set_size(t, sz);
	th_finish(t);
	i = tar_block_write(t, &(t->th_buf));
	if (i != T_BLOCKSIZE)
	{
		if (i != -1)
@@ -314,46 +362,28 @@ th_read(TAR *t)
		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);
	i = tar_block_write(t, buf);
	if (i != T_BLOCKSIZE)
	{
		if (i != -1)
			errno = EINVAL;
		return -1;
	}
	}
#endif

	/* 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
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;
			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)
	{
			if (i != -1)
				errno = EINVAL;
#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 (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);

+14 −0
Original line number Diff line number Diff line
@@ -20,6 +20,10 @@
#include <errno.h>
#include <utime.h>

#include <sys/capability.h>
#include <sys/xattr.h>
#include <linux/xattr.h>

#ifdef STDC_HEADERS
# include <stdlib.h>
#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;
+9 −2
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@

#include <sys/types.h>
#include <sys/stat.h>
#include <linux/capability.h>
#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 **********************************************************/

+9 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <stdio.h>
#include <sys/param.h>
#include <errno.h>
#include <linux/capability.h>

#ifdef STDC_HEADERS
# include <string.h>
@@ -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);
}
Loading