squashfs-tools: Allow setting selinux xattrs through file_context

Add a context-file flag that allows passing an selinux security context
file to set security.selinux xattrs rather than reading xattrs from
filesystem's source directory.

Change-Id: Icad4d38a736137d85835a0eab9650c0c99908721
Signed-off-by: Mohamad Ayyash <mkayyash@google.com>
diff --git a/squashfs-tools/Android.mk b/squashfs-tools/Android.mk
index e266e3d..2df06b6 100644
--- a/squashfs-tools/Android.mk
+++ b/squashfs-tools/Android.mk
@@ -65,6 +65,8 @@
 
 LOCAL_LDLIBS := -lpthread -lm -lz
 
+LOCAL_STATIC_LIBRARIES := libselinux
+
 LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/squashfs-tools/android.c b/squashfs-tools/android.c
index 0b1c11a..a59c210 100644
--- a/squashfs-tools/android.c
+++ b/squashfs-tools/android.c
@@ -18,10 +18,66 @@
 ** images generated by build tools (mkbootfs and mkyaffs2image) and
 ** by the device side of adb.
 */
-#include "android.h"
 
-void android_fs_config(char* path, struct stat* stat) {
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+
+#include "android.h"
+#include "private/android_filesystem_config.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+void android_fs_config(const char *path, struct stat *stat) {
     unsigned long capabilities = 0;
-    fs_config(path, S_ISDIR(stat->st_mode), &stat->st_uid, &stat->st_gid, &stat->st_mode, &capabilities);
+    fs_config(path, S_ISDIR(stat->st_mode), &stat->st_uid, &stat->st_gid, &stat->st_mode,
+            &capabilities);
 }
 
+
+struct selabel_handle *get_sehnd(const char *context_file) {
+    struct selinux_opt seopts[] = {
+        {
+            .type = SELABEL_OPT_PATH,
+            .value = context_file
+        }
+    };
+    struct selabel_handle *sehnd =
+        selabel_open(SELABEL_CTX_FILE, seopts, ARRAY_SIZE(seopts));
+
+    if (!sehnd) {
+        perror("Error running selabel_open.");
+        exit(EXIT_FAILURE);
+    }
+    return sehnd;
+}
+
+
+char *set_selabel(const char *path, unsigned int mode, struct selabel_handle *sehnd) {
+    char *secontext;
+    if (sehnd != NULL) {
+        int full_name_size = strlen(path) + 2;
+        char* full_name = (char*) malloc(full_name_size);
+        if (full_name == NULL) {
+            perror("Malloc Failure.");
+            exit(EXIT_FAILURE);
+        }
+
+        full_name[0] = '/';
+        strncpy(full_name + 1, path, full_name_size - 1);
+
+        if (selabel_lookup(sehnd, &secontext, full_name, mode)) {
+            secontext = strdup("u:object_r:unlabeled:s0");
+        }
+
+        free(full_name);
+        return secontext;
+    }
+    perror("Selabel handle is NULL.");
+    exit(EXIT_FAILURE);
+}
diff --git a/squashfs-tools/android.h b/squashfs-tools/android.h
index e505c6a..766cf0a 100644
--- a/squashfs-tools/android.h
+++ b/squashfs-tools/android.h
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
-#include <private/android_filesystem_config.h>
+#ifndef _ANDROID_H_
+#define _ANDROID_H_
 
-void android_fs_config(char* path, struct stat* stat);
+void android_fs_config(const char *path, struct stat *stat);
+struct selabel_handle *get_sehnd(const char *context_file);
+char *set_selabel(const char *path, unsigned int mode, struct selabel_handle *sehnd);
+
+#endif
diff --git a/squashfs-tools/mksquashfs.c b/squashfs-tools/mksquashfs.c
index 4f8d020..11401e2 100644
--- a/squashfs-tools/mksquashfs.c
+++ b/squashfs-tools/mksquashfs.c
@@ -81,6 +81,7 @@
 #ifdef ANDROID
 #include "android.h"
 int android_config = FALSE;
+char *context_file = NULL;
 #endif
 /* ANDROID CHANGES END */
 
@@ -5542,6 +5543,18 @@
 		else if(strcmp(argv[i], "-xattrs") == 0)
 			no_xattrs = FALSE;
 
+/* ANDROID CHANGES START*/
+#ifdef ANDROID
+		else if(strcmp(argv[i], "-context-file") == 0) {
+			if(++i == argc) {
+				ERROR("%s: -context-file: missing file name\n",
+					argv[0]);
+				exit(1);
+			}
+			context_file = argv[i];
+		}
+#endif
+/* ANDROID CHANGES END */
 		else if(strcmp(argv[i], "-nopad") == 0)
 			nopad = TRUE;
 
@@ -5596,6 +5609,13 @@
 				NOXOPT_STR "\n");
 			ERROR("-xattrs\t\t\tstore extended attributes" XOPT_STR
 				"\n");
+/* ANDROID CHANGES START*/
+#ifdef ANDROID
+			ERROR("-context-file <file>\tApply selinux security "
+				"xattrs from context-file instead\n\t\t\t"
+				"of reading xattrs from file system\n");
+#endif
+/* ANDROID CHANGES END */
 			ERROR("-noI\t\t\tdo not compress inode table\n");
 			ERROR("-noD\t\t\tdo not compress data blocks\n");
 			ERROR("-noF\t\t\tdo not compress fragment blocks\n");
diff --git a/squashfs-tools/xattr.c b/squashfs-tools/xattr.c
index b46550c..bef7115 100644
--- a/squashfs-tools/xattr.c
+++ b/squashfs-tools/xattr.c
@@ -43,6 +43,13 @@
 #include "error.h"
 #include "progressbar.h"
 
+/* ANDROID CHANGES START*/
+#ifdef ANDROID
+#include "android.h"
+static struct selabel_handle *sehnd = NULL;
+#endif
+/* ANDROID CHANGES END */
+
 /* compressed xattr table */
 static char *xattr_table = NULL;
 static unsigned int xattr_size = 0;
@@ -76,6 +83,9 @@
 extern long long bytes;
 extern int fd;
 extern unsigned int xattr_bytes, total_xattr_bytes;
+/* ANDROID CHANGES START*/
+extern char *context_file;
+/* ANDROID CHANGES END */
 
 /* helper functions from mksquashfs.c */
 extern unsigned short get_checksum(char *, int, unsigned short);
@@ -110,7 +120,28 @@
 	return prefix_table[i].type;
 }
 
-	
+
+/* ANDROID CHANGES START*/
+#ifdef ANDROID
+static int read_xattrs_from_context_file(char *filename, int mode,
+	struct selabel_handle *sehnd, struct xattr_list **xattrs)
+{
+	char *attr_val;
+	struct xattr_list *x = malloc(sizeof(*x));
+	if(x == NULL)
+		MEM_ERROR();
+
+	x->type = get_prefix(x, "security.selinux");
+	attr_val = set_selabel(filename, mode, sehnd);
+	x->value = (void *)attr_val;
+	x->vsize = strlen(attr_val);
+	*xattrs = x;
+	return 1;
+}
+#endif
+/* ANDROID CHANGES END */
+
+
 static int read_xattrs_from_system(char *filename, struct xattr_list **xattrs)
 {
 	ssize_t size, vsize;
@@ -614,7 +645,21 @@
 	if(no_xattrs || IS_PSEUDO(inode) || inode->root_entry)
 		return SQUASHFS_INVALID_XATTR;
 
+/* ANDROID CHANGES START*/
+#ifdef ANDROID
+	if (context_file) {
+		if (sehnd == NULL)
+			sehnd = get_sehnd(context_file);
+		xattrs = read_xattrs_from_context_file(filename, inode->buf.st_mode,
+				sehnd, &xattr_list);
+	} else {
+		xattrs = read_xattrs_from_system(filename, &xattr_list);
+	}
+#else
 	xattrs = read_xattrs_from_system(filename, &xattr_list);
+#endif
+/* ANDROID CHANGES END */
+
 	if(xattrs == 0)
 		return SQUASHFS_INVALID_XATTR;