[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[PATCH] libdpkg: Implement support for PAX Extended Header



From: Stefan Berger <stefanb@us.ibm.com>

Implement support for the pax extended header found in tar files. From all
the collected pax header entries we set the security.ima extended attribute
associate with the extracted file.
---
 dpkg-deb/extract.c |   5 +++
 lib/dpkg/tarfn.c   | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/dpkg/tarfn.h   |  13 +++++++
 src/archives.c     |  20 ++++++++++
 4 files changed, 146 insertions(+)

diff --git a/dpkg-deb/extract.c b/dpkg-deb/extract.c
index 6c63498..0646341 100644
--- a/dpkg-deb/extract.c
+++ b/dpkg-deb/extract.c
@@ -329,6 +329,11 @@ extracthalf(const char *debar, const char *dir,
       else
         internerr("unknown or missing tar action '%d'", taroption);
 
+      if (taroption & DPKG_TAR_EXTRACT) {
+        command_add_arg(&cmd, "--xattrs");
+        command_add_arg(&cmd, "--xattrs-include=security.ima");
+      }
+
       if (taroption & DPKG_TAR_PERMS)
         command_add_arg(&cmd, "-p");
       if (taroption & DPKG_TAR_NOMTIME)
diff --git a/lib/dpkg/tarfn.c b/lib/dpkg/tarfn.c
index 6082053..5ab7680 100644
--- a/lib/dpkg/tarfn.c
+++ b/lib/dpkg/tarfn.c
@@ -36,6 +36,7 @@
 #include <dpkg/macros.h>
 #include <dpkg/dpkg.h>
 #include <dpkg/tarfn.h>
+#include <dpkg/i18n.h>
 
 #define TAR_MAGIC_USTAR "ustar\0" "00"
 #define TAR_MAGIC_GNU   "ustar "  " \0"
@@ -244,6 +245,102 @@ tar_gnu_long(void *ctx, const struct tar_operations *ops, struct tar_entry *te,
 }
 
 static void
+pax_data_destroy(struct pax_data *pd)
+{
+	size_t i;
+
+	for (i = 0; i < pd->num_entries; i++) {
+		free(pd->entries[i].key);
+		free(pd->entries[i].value);
+	}
+	free(pd->entries);
+	pd->entries = NULL;
+	pd->num_entries = 0;
+}
+
+static void
+pax_data_add(struct pax_data *pd, const char *key,
+             const unsigned char *value, size_t value_len)
+{
+	size_t idx = pd->num_entries++;
+
+	pd->entries = m_realloc(pd->entries, (idx + 1) * sizeof(struct pax_entry));
+	pd->entries[idx].key = m_strdup(key);
+	pd->entries[idx].value = m_malloc(value_len);
+	memcpy(pd->entries[idx].value, value, value_len);
+	pd->entries[idx].value_len = value_len;
+}
+
+static int
+tar_pax_header(void *ctx, const struct tar_operations *ops,
+               struct tar_entry *te, struct pax_data *pd)
+{
+	int status, n;
+	unsigned int len;
+	char *key_start, *key_end, *buf, *current, space;
+	unsigned char *value;
+	size_t value_len, buf_len;
+
+	pax_data_destroy(pd);
+
+	/* read whole blocks: round up to the next block size */
+	buf_len = (te->size + TARBLKSZ - 1) & ~(TARBLKSZ - 1);
+	buf = m_malloc(buf_len + 1);
+
+	/* avoid parsing beyond the buffer */
+	buf[buf_len] = 0;
+
+	status = ops->read(ctx, buf, buf_len);
+	if (status == (int)buf_len)
+		status = 0;
+	else
+		return -1;
+
+	current = buf;
+
+	while (current - buf < te->size) {
+		/* parse 'untrusted' len */
+		n = sscanf(current, "%u%c", &len, &space);
+		if (n != 2 || space != ' ' || len == 0)
+			break;
+
+		key_start = index(current, ' ');
+		if (!key_start)
+			break;
+
+		key_start++;
+		key_end = index(key_start, '=');
+		if (!key_end)
+			break;
+
+		*key_end = '\0';
+
+		/* check 'untrusted' len: beyond buffer ? */
+		current += len;
+		if (current > buf + te->size || current[-1] != '\n')
+			break;
+
+		/* value starts after the '='; value can be binary data */
+		value = (unsigned char *)key_end + 1;
+		value_len = current - 1 - (char *)value;
+		/* overwrite terminating '\n' */
+		value[value_len] = 0;
+
+		pax_data_add(pd, key_start, value, value_len);
+	}
+
+	free(buf);
+
+	if (current - buf != te->size)
+		status = -1;
+
+	if (status)
+		ohshite(_("Malformed PAX header"));
+
+	return status;
+}
+
+static void
 tar_entry_copy(struct tar_entry *dst, struct tar_entry *src)
 {
 	memcpy(dst, src, sizeof(struct tar_entry));
@@ -264,6 +361,7 @@ tar_entry_destroy(struct tar_entry *te)
 	free(te->linkname);
 	free(te->stat.uname);
 	free(te->stat.gname);
+	te->pd = NULL;
 }
 
 struct tar_symlink_entry {
@@ -303,6 +401,9 @@ tar_extractor(void *ctx, const struct tar_operations *ops)
 
 	char *next_long_name, *next_long_link;
 	struct tar_symlink_entry *symlink_head, *symlink_tail, *symlink_node;
+	struct pax_data pd;
+
+	memset(&pd, 0, sizeof(pd));
 
 	next_long_name = NULL;
 	next_long_link = NULL;
@@ -355,7 +456,9 @@ tar_extractor(void *ctx, const struct tar_operations *ops)
 		case TAR_FILETYPE_FILE:
 			/* Compatibility with pre-ANSI ustar. */
 			if (h.name[name_len - 1] != '/') {
+				h.pd = &pd;
 				status = ops->extract_file(ctx, &h);
+				pax_data_destroy(&pd);
 				break;
 			}
 			/* Else, fall through. */
@@ -391,6 +494,9 @@ tar_extractor(void *ctx, const struct tar_operations *ops)
 		case TAR_FILETYPE_GNU_LONGNAME:
 			status = tar_gnu_long(ctx, ops, &h, &next_long_name);
 			break;
+		case TAR_FILETYPE_PAX_EXTENDED_HEADER:
+			status = tar_pax_header(ctx, ops, &h, &pd);
+			break;
 		default:
 			/* Indicates broken tarfile: “Bad header field”. */
 			errno = 0;
@@ -402,6 +508,8 @@ tar_extractor(void *ctx, const struct tar_operations *ops)
 			break;
 	}
 
+	pax_data_destroy(&pd);
+
 	while (symlink_head) {
 		symlink_node = symlink_head->next;
 		if (status == 0)
diff --git a/lib/dpkg/tarfn.h b/lib/dpkg/tarfn.h
index ce69424..0cf301b 100644
--- a/lib/dpkg/tarfn.h
+++ b/lib/dpkg/tarfn.h
@@ -53,6 +53,18 @@ enum tar_filetype {
 	TAR_FILETYPE_FIFO = '6',
 	TAR_FILETYPE_GNU_LONGLINK = 'K',
 	TAR_FILETYPE_GNU_LONGNAME = 'L',
+	TAR_FILETYPE_PAX_EXTENDED_HEADER = 'x',
+};
+
+struct pax_entry {
+	char *key;
+	unsigned char *value;
+	size_t value_len;
+};
+
+struct pax_data {
+	size_t num_entries;
+	struct pax_entry *entries;
 };
 
 struct tar_entry {
@@ -72,6 +84,7 @@ struct tar_entry {
 	dev_t dev;
 
 	struct file_stat stat;
+	struct pax_data *pd;
 };
 
 typedef int tar_read_func(void *ctx, char *buffer, int length);
diff --git a/src/archives.c b/src/archives.c
index ae7fa30..6078d11 100644
--- a/src/archives.c
+++ b/src/archives.c
@@ -28,6 +28,7 @@
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/stat.h>
+#include <sys/xattr.h>
 
 #include <assert.h>
 #include <errno.h>
@@ -523,6 +524,24 @@ tarobject_set_se_context(const char *matchpath, const char *path, mode_t mode)
 }
 
 static void
+tarobject_set_pax_data(const char *path, struct pax_data *pd)
+{
+  size_t i;
+  const char *key;
+
+  if (!pd)
+    return;
+
+  for (i = 0; i < pd->num_entries; i++) {
+    key = pd->entries[i].key;
+    if (!strcmp(key, "SCHILY.xattr.security.ima")) {
+      lsetxattr(path, &key[13],
+                pd->entries[i].value, pd->entries[i].value_len, 0);
+    }
+  }
+}
+
+static void
 tarobject_matches(struct tarcontext *tc,
                   const char *fn_old, struct stat *stab, char *oldhash,
                   const char *fn_new, struct tar_entry *te,
@@ -993,6 +1012,7 @@ tarobject(void *ctx, struct tar_entry *ti)
   tarobject_set_perms(ti, fnamenewvb.buf, &nodestat);
   tarobject_set_mtime(ti, fnamenewvb.buf);
   tarobject_set_se_context(fnamevb.buf, fnamenewvb.buf, nodestat.mode);
+  tarobject_set_pax_data(fnamenewvb.buf, ti->pd);
 
   /*
    * CLEANUP: Now we have extracted the new object in .dpkg-new (or,
-- 
1.9.1


Reply to: