diff --git a/core/Makefile b/core/Makefile index bd67c89d4..0afb30fdf 100644 --- a/core/Makefile +++ b/core/Makefile @@ -1173,6 +1173,7 @@ $(BUILT_TARGET_FILES_PACKAGE): \ $(INSTALLED_CACHEIMAGE_TARGET) \ $(INSTALLED_VENDORIMAGE_TARGET) \ $(INSTALLED_ANDROID_INFO_TXT_TARGET) \ + $(SELINUX_FC) \ $(built_ota_tools) \ $(APKCERTS_FILE) \ $(HOST_OUT_EXECUTABLES)/fs_config \ @@ -1260,13 +1261,14 @@ ifdef PRODUCT_EXTRA_RECOVERY_KEYS $(hide) echo "extra_recovery_keys=$(PRODUCT_EXTRA_RECOVERY_KEYS)" >> $(zip_root)/META/misc_info.txt endif $(hide) echo "mkbootimg_args=$(BOARD_MKBOOTIMG_ARGS)" >> $(zip_root)/META/misc_info.txt + $(hide) echo "use_set_metadata=1" >> $(zip_root)/META/misc_info.txt $(call generate-userimage-prop-dictionary, $(zip_root)/META/misc_info.txt) @# Zip everything up, preserving symlinks $(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .) @# Run fs_config on all the system, boot ramdisk, and recovery ramdisk files in the zip, and save the output - $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/filesystem_config.txt - $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/boot_filesystem_config.txt - $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/recovery_filesystem_config.txt + $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/filesystem_config.txt + $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/boot_filesystem_config.txt + $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/recovery_filesystem_config.txt $(hide) (cd $(zip_root) && zip -q ../$(notdir $@) META/*filesystem_config.txt) .PHONY: target-files-package diff --git a/tools/fs_config/Android.mk b/tools/fs_config/Android.mk index 5ef32dd34..02deabbf7 100644 --- a/tools/fs_config/Android.mk +++ b/tools/fs_config/Android.mk @@ -17,6 +17,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := fs_config.c LOCAL_MODULE := fs_config +LOCAL_STATIC_LIBRARIES := libselinux LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_HOST_EXECUTABLE) diff --git a/tools/fs_config/fs_config.c b/tools/fs_config/fs_config.c index f6760cc5a..f594c1e93 100644 --- a/tools/fs_config/fs_config.c +++ b/tools/fs_config/fs_config.c @@ -15,10 +15,16 @@ */ #include +#include #include #include #include #include +#include + +#include +#include +#include #include "private/android_filesystem_config.h" @@ -27,21 +33,67 @@ // filename along with its desired uid, gid, and mode (in octal). // The leading slash should be stripped from the input. // +// After the first 4 columns, optional key=value pairs are emitted +// for each file. Currently, the following keys are supported: +// * -S: selabel=[selinux_label] +// * -C: capabilities=[hex capabilities value] +// // Example input: // -// system/etc/dbus.conf -// data/app/ +// system/etc/dbus.conf +// data/app/ // // Output: // -// system/etc/dbus.conf 1002 1002 440 -// data/app 1000 1000 771 +// system/etc/dbus.conf 1002 1002 440 +// data/app 1000 1000 771 +// +// or if, for example, -S is used: +// +// system/etc/dbus.conf 1002 1002 440 selabel=u:object_r:system_file:s0 +// data/app 1000 1000 771 selabel=u:object_r:apk_data_file:s0 // // Note that the output will omit the trailing slash from // directories. +static struct selabel_handle* get_sehnd(const char* context_file) { + struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, context_file } }; + struct selabel_handle* sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1); + + if (!sehnd) { + perror("error running selabel_open"); + exit(EXIT_FAILURE); + } + return sehnd; +} + +static void usage() { + fprintf(stderr, "Usage: fs_config [-S context_file] [-C]\n"); +} + int main(int argc, char** argv) { char buffer[1024]; + const char* context_file = NULL; + struct selabel_handle* sehnd = NULL; + int print_capabilities = 0; + int opt; + while((opt = getopt(argc, argv, "CS:")) != -1) { + switch(opt) { + case 'C': + print_capabilities = 1; + break; + case 'S': + context_file = optarg; + break; + default: + usage(); + exit(EXIT_FAILURE); + } + } + + if (context_file != NULL) { + sehnd = get_sehnd(context_file); + } while (fgets(buffer, 1023, stdin) != NULL) { int is_dir = 0; @@ -64,7 +116,40 @@ int main(int argc, char** argv) { unsigned uid = 0, gid = 0, mode = 0; uint64_t capabilities; fs_config(buffer, is_dir, &uid, &gid, &mode, &capabilities); - printf("%s %d %d %o\n", buffer, uid, gid, mode); + printf("%s %d %d %o", buffer, uid, gid, mode); + + if (sehnd != NULL) { + size_t buffer_strlen = strnlen(buffer, sizeof(buffer)); + if (buffer_strlen >= sizeof(buffer)) { + fprintf(stderr, "non null terminated buffer, aborting\n"); + exit(EXIT_FAILURE); + } + size_t full_name_size = buffer_strlen + 2; + char* full_name = (char*) malloc(full_name_size); + if (full_name == NULL) { + perror("malloc"); + exit(EXIT_FAILURE); + } + + full_name[0] = '/'; + strncpy(full_name + 1, buffer, full_name_size - 1); + full_name[full_name_size - 1] = '\0'; + + char* secontext; + if (selabel_lookup(sehnd, &secontext, full_name, ( mode | (is_dir ? S_IFDIR : S_IFREG)))) { + secontext = strdup("u:object_r:unlabeled:s0"); + } + + printf(" selabel=%s", secontext); + free(full_name); + freecon(secontext); + } + + if (print_capabilities) { + printf(" capabilities=0x%" PRIx64, capabilities); + } + + printf("\n"); } return 0; } diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py index 9ef19264b..2c3b9e756 100644 --- a/tools/releasetools/edify_generator.py +++ b/tools/releasetools/edify_generator.py @@ -217,14 +217,33 @@ class EdifyGenerator(object): else: raise ValueError("don't know how to write \"%s\" partitions" % (p.fs_type,)) - def SetPermissions(self, fn, uid, gid, mode): + def SetPermissions(self, fn, uid, gid, mode, selabel, capabilities): """Set file ownership and permissions.""" - self.script.append('set_perm(%d, %d, 0%o, "%s");' % (uid, gid, mode, fn)) + if not self.info.get("use_set_metadata", False): + self.script.append('set_perm(%d, %d, 0%o, "%s");' % (uid, gid, mode, fn)) + else: + if capabilities is None: capabilities = "0x0" + cmd = 'set_metadata("%s", "uid", %d, "gid", %d, "mode", 0%o, ' \ + '"capabilities", %s' % (fn, uid, gid, mode, capabilities) + if selabel is not None: + cmd += ', "selabel", "%s"' % ( selabel ) + cmd += ');' + self.script.append(cmd) - def SetPermissionsRecursive(self, fn, uid, gid, dmode, fmode): + def SetPermissionsRecursive(self, fn, uid, gid, dmode, fmode, selabel, capabilities): """Recursively set path ownership and permissions.""" - self.script.append('set_perm_recursive(%d, %d, 0%o, 0%o, "%s");' - % (uid, gid, dmode, fmode, fn)) + if not self.info.get("use_set_metadata", False): + self.script.append('set_perm_recursive(%d, %d, 0%o, 0%o, "%s");' + % (uid, gid, dmode, fmode, fn)) + else: + if capabilities is None: capabilities = "0x0" + cmd = 'set_metadata_recursive("%s", "uid", %d, "gid", %d, ' \ + '"dmode", 0%o, "fmode", 0%o, "capabilities", %s' \ + % (fn, uid, gid, dmode, fmode, capabilities) + if selabel is not None: + cmd += ', "selabel", "%s"' % ( selabel ) + cmd += ');' + self.script.append(cmd) def MakeSymlinks(self, symlink_list): """Create symlinks, given a list of (dest, link) pairs.""" diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files index 1e1d04e24..a6b9b6939 100755 --- a/tools/releasetools/ota_from_target_files +++ b/tools/releasetools/ota_from_target_files @@ -117,6 +117,8 @@ class Item: self.uid = None self.gid = None self.mode = None + self.selabel = None + self.capabilities = None self.dir = dir if name: @@ -147,82 +149,88 @@ class Item: @classmethod def GetMetadata(cls, input_zip): - try: - # See if the target_files contains a record of what the uid, - # gid, and mode is supposed to be. - output = input_zip.read("META/filesystem_config.txt") - except KeyError: - # Run the external 'fs_config' program to determine the desired - # uid, gid, and mode for every Item object. Note this uses the - # one in the client now, which might not be the same as the one - # used when this target_files was built. - p = common.Run(["fs_config"], stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - suffix = { False: "", True: "/" } - input = "".join(["%s%s\n" % (i.name, suffix[i.dir]) - for i in cls.ITEMS.itervalues() if i.name]) - output, error = p.communicate(input) - assert not error + # The target_files contains a record of what the uid, + # gid, and mode are supposed to be. + output = input_zip.read("META/filesystem_config.txt") for line in output.split("\n"): if not line: continue - name, uid, gid, mode = line.split() + columns = line.split() + name, uid, gid, mode = columns[:4] + selabel = None + capabilities = None + + # After the first 4 columns, there are a series of key=value + # pairs. Extract out the fields we care about. + for element in columns[4:]: + key, value = element.split("=") + if key == "selabel": + selabel = value + if key == "capabilities": + capabilities = value + i = cls.ITEMS.get(name, None) if i is not None: i.uid = int(uid) i.gid = int(gid) i.mode = int(mode, 8) + i.selabel = selabel + i.capabilities = capabilities if i.dir: i.children.sort(key=lambda i: i.name) # set metadata for the files generated by this script. i = cls.ITEMS.get("system/recovery-from-boot.p", None) - if i: i.uid, i.gid, i.mode = 0, 0, 0644 + if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None i = cls.ITEMS.get("system/etc/install-recovery.sh", None) - if i: i.uid, i.gid, i.mode = 0, 0, 0544 + if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None def CountChildMetadata(self): - """Count up the (uid, gid, mode) tuples for all children and - determine the best strategy for using set_perm_recursive and + """Count up the (uid, gid, mode, selabel, capabilities) tuples for + all children and determine the best strategy for using set_perm_recursive and set_perm to correctly chown/chmod all the files to their desired values. Recursively calls itself for all descendants. - Returns a dict of {(uid, gid, dmode, fmode): count} counting up + Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up all descendants of this node. (dmode or fmode may be None.) Also sets the best_subtree of each directory Item to the (uid, gid, - dmode, fmode) tuple that will match the most descendants of that - Item. + dmode, fmode, selabel, capabilities) tuple that will match the most + descendants of that Item. """ assert self.dir - d = self.descendants = {(self.uid, self.gid, self.mode, None): 1} + d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1} for i in self.children: if i.dir: for k, v in i.CountChildMetadata().iteritems(): d[k] = d.get(k, 0) + v else: - k = (i.uid, i.gid, None, i.mode) + k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities) d[k] = d.get(k, 0) + 1 - # Find the (uid, gid, dmode, fmode) tuple that matches the most - # descendants. + # Find the (uid, gid, dmode, fmode, selabel, capabilities) + # tuple that matches the most descendants. # First, find the (uid, gid) pair that matches the most # descendants. ug = {} - for (uid, gid, _, _), count in d.iteritems(): + for (uid, gid, _, _, _, _), count in d.iteritems(): ug[(uid, gid)] = ug.get((uid, gid), 0) + count ug = MostPopularKey(ug, (0, 0)) - # Now find the dmode and fmode that match the most descendants - # with that (uid, gid), and choose those. + # Now find the dmode, fmode, selabel, and capabilities that match + # the most descendants with that (uid, gid), and choose those. best_dmode = (0, 0755) best_fmode = (0, 0644) + best_selabel = (0, None) + best_capabilities = (0, None) for k, count in d.iteritems(): if k[:2] != ug: continue if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2]) if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3]) - self.best_subtree = ug + (best_dmode[1], best_fmode[1]) + if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4]) + if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5]) + self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1]) return d @@ -234,7 +242,7 @@ class Item: self.CountChildMetadata() def recurse(item, current): - # current is the (uid, gid, dmode, fmode) tuple that the current + # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current # item (and all its children) have already been set to. We only # need to issue set_perm/set_perm_recursive commands if we're # supposed to be something different. @@ -244,17 +252,21 @@ class Item: current = item.best_subtree if item.uid != current[0] or item.gid != current[1] or \ - item.mode != current[2]: - script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode) + item.mode != current[2] or item.selabel != current[4] or \ + item.capabilities != current[5]: + script.SetPermissions("/"+item.name, item.uid, item.gid, + item.mode, item.selabel, item.capabilities) for i in item.children: recurse(i, current) else: if item.uid != current[0] or item.gid != current[1] or \ - item.mode != current[3]: - script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode) + item.mode != current[3] or item.selabel != current[4] or \ + item.capabilities != current[5]: + script.SetPermissions("/"+item.name, item.uid, item.gid, + item.mode, item.selabel, item.capabilities) - recurse(self, (-1, -1, -1, -1)) + recurse(self, (-1, -1, -1, -1, None, None)) def CopySystemFiles(input_zip, output_zip=None, @@ -733,7 +745,7 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): for item in deferred_patch_list: fn, tf, sf, size, _ = item script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p") - script.SetPermissions("/system/build.prop", 0, 0, 0644) + script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None) script.AddToZip(target_zip, output_zip) WriteMetadata(metadata, output_zip)