New postinstall domain and rules to run post-install program.

When using the A/B updater, a device specific hook is sometimes needed
to run after the new partitions are updated but before rebooting into
the new image. This hook is referred to throughout the code as the
"postinstall" step.

This patch creates a new execution domain "postinstall" which
update_engine will use to run said hook. Since the hook needs to run
from the new image (namelly, slot "B"), update_engine needs to
temporarly mount this B partition into /postinstall and then run a
program from there.

Since the new program in B runs from the old execution context in A, we
can't rely on the labels set in the xattr in the new filesystem to
enforce the policies baked into the old running image. Instead, when
temporarily mounting the new filesystem in update_engine, we override
all the new file attributes with the new postinstall_file type by
passing "context=u:object_r:postinstall_file:s0" to the mount syscall.
This allows us to set new rules specific to the postinstall environment
that are consistent with the rules in the old system.

Bug: 27177071
TEST=Deployed a payload with a trivial postinstall script to edison-eng.

Change-Id: Ib06fab92afb45edaec3c9c9872304dc9386151b4
diff --git a/domain.te b/domain.te
index 2922da6..c3ed601 100644
--- a/domain.te
+++ b/domain.te
@@ -249,7 +249,7 @@
 # Limit what domains can mount filesystems or change their mount flags.
 # sdcard_type / vfat is exempt as a larger set of domains need
 # this capability, including device-specific domains.
-neverallow { domain -kernel -init -recovery -vold -zygote } { fs_type -sdcard_type }:filesystem { mount remount relabelfrom relabelto };
+neverallow { domain -kernel -init -recovery -vold -zygote -update_engine } { fs_type -sdcard_type }:filesystem { mount remount relabelfrom relabelto };
 
 #
 # Assert that, to the extent possible, we're not loading executable content from
@@ -263,7 +263,7 @@
     userdebug_or_eng(`-su')
     -system_server
     -zygote
-} { file_type -system_file -exec_type }:file execute;
+} { file_type -system_file -exec_type -postinstall_file }:file execute;
 neverallow {
     domain
     -appdomain # for oemfs
diff --git a/file.te b/file.te
index 1998669..a2963a5 100644
--- a/file.te
+++ b/file.te
@@ -109,6 +109,11 @@
 type mnt_media_rw_stub_file, file_type;
 type storage_stub_file, file_type;
 
+# /postinstall: Mount point used by update_engine to run postinstall.
+type postinstall_mnt_dir, file_type;
+# Files inside the /postinstall mountpoint are all labeled as postinstall_file.
+type postinstall_file, file_type, exec_type;
+
 # /data/misc subdirectories
 type adb_keys_file, file_type, data_file_type;
 type audio_data_file, file_type, data_file_type;
@@ -216,6 +221,7 @@
 allow file_type rootfs:filesystem associate;
 allow dev_type tmpfs:filesystem associate;
 allow app_fuse_file app_fusefs:filesystem associate;
+allow postinstall_file self:filesystem associate;
 
 # It's a bug to assign the file_type attribute and fs_type attribute
 # to any type. Do not allow it.
diff --git a/file_contexts b/file_contexts
index 0a75659..d98f25d 100644
--- a/file_contexts
+++ b/file_contexts
@@ -23,6 +23,7 @@
 /acct               u:object_r:cgroup:s0
 /config             u:object_r:rootfs:s0
 /mnt                u:object_r:tmpfs:s0
+/postinstall        u:object_r:postinstall_mnt_dir:s0
 /proc               u:object_r:rootfs:s0
 /root               u:object_r:rootfs:s0
 /sys                u:object_r:sysfs:s0
diff --git a/init.te b/init.te
index 1baeeee..047ea73 100644
--- a/init.te
+++ b/init.te
@@ -88,8 +88,9 @@
 allow init contextmount_type:dir r_dir_perms;
 allow init contextmount_type:notdevfile_class_set r_file_perms;
 
-# restorecon /adb_keys or any other rootfs files to a more specific type.
-allow init rootfs:file relabelfrom;
+# restorecon /adb_keys or any other rootfs files and directories to a more
+# specific type.
+allow init rootfs:{ dir file } relabelfrom;
 
 # mkdir, symlink, write, rm/rmdir, chown/chmod, restorecon/restorecon_recursive from init.rc files.
 # chown/chmod require open+read+setattr required for open()+fchown/fchmod().
diff --git a/postinstall.te b/postinstall.te
new file mode 100644
index 0000000..8afc561
--- /dev/null
+++ b/postinstall.te
@@ -0,0 +1,20 @@
+# Domain where the postinstall program runs during the update.
+# Extend the permissions in this domain to allow this program to access other
+# files needed by the specific device on your device's sepolicy directory.
+type postinstall, domain;
+
+# Allow postinstall to write to its stdout/stderr when redirected via pipes to
+# update_engine.
+allow postinstall update_engine:fd use;
+allow postinstall update_engine:fifo_file rw_file_perms;
+
+# Allow postinstall to read and execute directories and files in the same
+# mounted location.
+allow postinstall postinstall_file:file rx_file_perms;
+allow postinstall postinstall_file:lnk_file r_file_perms;
+allow postinstall postinstall_file:dir r_dir_perms;
+
+# Allow postinstall to execute the shell or other system executables.
+allow postinstall shell_exec:file rx_file_perms;
+allow postinstall system_file:file rx_file_perms;
+allow postinstall toolbox_exec:file rx_file_perms;
diff --git a/update_engine.te b/update_engine.te
index 39b9936..cf614e6 100644
--- a/update_engine.te
+++ b/update_engine.te
@@ -13,6 +13,9 @@
 allow update_engine update_engine_exec:file rx_file_perms;
 wakelock_use(update_engine);
 
+# Ignore these denials.
+dontaudit update_engine kernel:process setsched;
+
 # Allow using persistent storage in /data/misc/update_engine.
 allow update_engine update_engine_data_file:dir { create_dir_perms };
 allow update_engine update_engine_data_file:file { create_file_perms };
@@ -27,6 +30,25 @@
 # Don't allow kernel module loading, just silence the logs.
 dontaudit update_engine kernel:system module_request;
 
+# Allow update_engine to mount on the /postinstall directory and reset the
+# labels on the mounted filesystem to postinstall_file.
+allow update_engine postinstall_mnt_dir:dir mounton;
+allow update_engine postinstall_file:filesystem { mount unmount relabelfrom relabelto };
+allow update_engine labeledfs:filesystem relabelfrom;
+
+# Allow update_engine to read and execute postinstall_file.
+allow update_engine postinstall_file:file rx_file_perms;
+allow update_engine postinstall_file:lnk_file r_file_perms;
+allow update_engine postinstall_file:dir r_dir_perms;
+
+# The postinstall program is run by update_engine and will always be tagged as a
+# postinstall_file regardless of its attributes in the new system.
+domain_auto_trans(update_engine, postinstall_file, postinstall)
+
+# A postinstall program is typically a shell script (with a #!), so we allow
+# to execute those.
+allow update_engine shell_exec:file rx_file_perms;
+
 # Register the service to perform Binder IPC.
 binder_use(update_engine)
 allow update_engine update_engine_service:service_manager { add };