summaryrefslogtreecommitdiffstats
path: root/contrib/android/perms.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/android/perms.c')
-rw-r--r--contrib/android/perms.c376
1 files changed, 376 insertions, 0 deletions
diff --git a/contrib/android/perms.c b/contrib/android/perms.c
new file mode 100644
index 0000000..dd05644
--- /dev/null
+++ b/contrib/android/perms.c
@@ -0,0 +1,376 @@
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE //asprintf
+#endif
+#include "config.h"
+#include "perms.h"
+#include "support/nls-enable.h"
+#include <time.h>
+#include <sys/stat.h>
+
+#ifndef XATTR_SELINUX_SUFFIX
+# define XATTR_SELINUX_SUFFIX "selinux"
+#endif
+#ifndef XATTR_CAPS_SUFFIX
+# define XATTR_CAPS_SUFFIX "capability"
+#endif
+
+struct inode_params {
+ ext2_filsys fs;
+ char *path;
+ char *filename;
+ char *src_dir;
+ char *target_out;
+ char *mountpoint;
+ fs_config_f fs_config_func;
+ struct selabel_handle *sehnd;
+ time_t fixed_time;
+ const struct ugid_map* uid_map;
+ const struct ugid_map* gid_map;
+ errcode_t error;
+};
+
+static errcode_t ino_add_xattr(ext2_filsys fs, ext2_ino_t ino, const char *name,
+ const void *value, int value_len)
+{
+ errcode_t retval, close_retval;
+ struct ext2_xattr_handle *xhandle;
+
+ retval = ext2fs_xattrs_open(fs, ino, &xhandle);
+ if (retval) {
+ com_err(__func__, retval, _("while opening inode %u"), ino);
+ return retval;
+ }
+ retval = ext2fs_xattrs_read(xhandle);
+ if (retval) {
+ com_err(__func__, retval,
+ _("while reading xattrs of inode %u"), ino);
+ goto xattrs_close;
+ }
+ retval = ext2fs_xattr_set(xhandle, name, value, value_len);
+ if (retval) {
+ com_err(__func__, retval,
+ _("while setting xattrs of inode %u"), ino);
+ goto xattrs_close;
+ }
+xattrs_close:
+ close_retval = ext2fs_xattrs_close(&xhandle);
+ if (close_retval) {
+ com_err(__func__, close_retval,
+ _("while closing xattrs of inode %u"), ino);
+ return retval ? retval : close_retval;
+ }
+ return retval;
+}
+
+static errcode_t set_selinux_xattr(ext2_filsys fs, ext2_ino_t ino,
+ struct inode_params *params)
+{
+ errcode_t retval;
+ char *secontext = NULL;
+ struct ext2_inode inode;
+
+ if (params->sehnd == NULL)
+ return 0;
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval) {
+ com_err(__func__, retval,
+ _("while reading inode %u"), ino);
+ return retval;
+ }
+
+ retval = selabel_lookup(params->sehnd, &secontext, params->filename,
+ inode.i_mode);
+ if (retval < 0) {
+ int saved_errno = errno;
+ com_err(__func__, errno,
+ _("searching for label \"%s\""), params->filename);
+ return saved_errno;
+ }
+
+ retval = ino_add_xattr(fs, ino, "security." XATTR_SELINUX_SUFFIX,
+ secontext, strlen(secontext) + 1);
+
+ freecon(secontext);
+ return retval;
+}
+
+/*
+ * Returns mapped UID/GID if there is a corresponding entry in |mapping|.
+ * Otherwise |id| as is.
+ */
+static unsigned int resolve_ugid(const struct ugid_map* mapping,
+ unsigned int id)
+{
+ size_t i;
+ for (i = 0; i < mapping->size; ++i) {
+ const struct ugid_map_entry* entry = &mapping->entries[i];
+ if (entry->parent_id <= id &&
+ id < entry->parent_id + entry->length) {
+ return id + entry->child_id - entry->parent_id;
+ }
+ }
+
+ /* No entry is found. */
+ return id;
+}
+
+static errcode_t set_perms_and_caps(ext2_filsys fs, ext2_ino_t ino,
+ struct inode_params *params)
+{
+ errcode_t retval;
+ uint64_t capabilities = 0;
+ struct ext2_inode inode;
+ struct vfs_cap_data cap_data;
+ unsigned int uid = 0, gid = 0, imode = 0;
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval) {
+ com_err(__func__, retval, _("while reading inode %u"), ino);
+ return retval;
+ }
+
+ /* Permissions */
+ if (params->fs_config_func != NULL) {
+ const char *filename = params->filename;
+ if (strcmp(filename, params->mountpoint) == 0) {
+ /* The root of the filesystem needs to be an empty string. */
+ filename = "";
+ }
+ params->fs_config_func(filename, S_ISDIR(inode.i_mode),
+ params->target_out, &uid, &gid, &imode,
+ &capabilities);
+ uid = resolve_ugid(params->uid_map, uid);
+ gid = resolve_ugid(params->gid_map, gid);
+ inode.i_uid = (__u16) uid;
+ inode.i_gid = (__u16) gid;
+ ext2fs_set_i_uid_high(inode, (__u16) (uid >> 16));
+ ext2fs_set_i_gid_high(inode, (__u16) (gid >> 16));
+ inode.i_mode = (inode.i_mode & S_IFMT) | (imode & 0xffff);
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ if (retval) {
+ com_err(__func__, retval,
+ _("while writing inode %u"), ino);
+ return retval;
+ }
+ }
+
+ /* Capabilities */
+ if (!capabilities)
+ return 0;
+ memset(&cap_data, 0, sizeof(cap_data));
+ cap_data.magic_etc = VFS_CAP_REVISION_2 | VFS_CAP_FLAGS_EFFECTIVE;
+ cap_data.data[0].permitted = (uint32_t) (capabilities & 0xffffffff);
+ cap_data.data[1].permitted = (uint32_t) (capabilities >> 32);
+ return ino_add_xattr(fs, ino, "security." XATTR_CAPS_SUFFIX,
+ &cap_data, sizeof(cap_data));
+}
+
+static errcode_t set_timestamp(ext2_filsys fs, ext2_ino_t ino,
+ struct inode_params *params)
+{
+ errcode_t retval;
+ struct ext2_inode inode;
+ struct stat stat;
+ char *src_filename = NULL;
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval) {
+ com_err(__func__, retval,
+ _("while reading inode %u"), ino);
+ return retval;
+ }
+
+ if (params->fixed_time == -1 && params->src_dir) {
+ /* replace mountpoint from filename with src_dir */
+ if (asprintf(&src_filename, "%s/%s", params->src_dir,
+ params->filename + strlen(params->mountpoint)) < 0) {
+ return -ENOMEM;
+ }
+ retval = lstat(src_filename, &stat);
+ if (retval < 0) {
+ com_err(__func__, errno,
+ _("while lstat file %s"), src_filename);
+ goto end;
+ }
+ inode.i_atime = inode.i_ctime = inode.i_mtime = stat.st_mtime;
+ } else {
+ inode.i_atime = inode.i_ctime = inode.i_mtime = params->fixed_time;
+ }
+
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ if (retval) {
+ com_err(__func__, retval,
+ _("while writing inode %u"), ino);
+ goto end;
+ }
+
+end:
+ free(src_filename);
+ return retval;
+}
+
+static int is_dir(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inode inode;
+
+ if (ext2fs_read_inode(fs, ino, &inode))
+ return 0;
+ return S_ISDIR(inode.i_mode);
+}
+
+static errcode_t androidify_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct inode_params *params)
+{
+ errcode_t retval;
+
+ retval = set_timestamp(fs, ino, params);
+ if (retval)
+ return retval;
+
+ retval = set_selinux_xattr(fs, ino, params);
+ if (retval)
+ return retval;
+
+ return set_perms_and_caps(fs, ino, params);
+}
+
+static int walk_dir(ext2_ino_t dir EXT2FS_ATTR((unused)),
+ int flags EXT2FS_ATTR((unused)),
+ struct ext2_dir_entry *de,
+ int offset EXT2FS_ATTR((unused)),
+ int blocksize EXT2FS_ATTR((unused)),
+ char *buf EXT2FS_ATTR((unused)), void *priv_data)
+{
+ __u16 name_len;
+ errcode_t retval;
+ struct inode_params *params = (struct inode_params *)priv_data;
+
+ name_len = de->name_len & 0xff;
+ if (!strncmp(de->name, ".", name_len)
+ || (!strncmp(de->name, "..", name_len)))
+ return 0;
+
+ if (asprintf(&params->filename, "%s/%.*s", params->path, name_len,
+ de->name) < 0) {
+ params->error = ENOMEM;
+ return -ENOMEM;
+ }
+
+ if (!strncmp(de->name, "lost+found", 10)) {
+ retval = set_selinux_xattr(params->fs, de->inode, params);
+ if (retval)
+ goto end;
+ } else {
+ retval = androidify_inode(params->fs, de->inode, params);
+ if (retval)
+ goto end;
+ if (is_dir(params->fs, de->inode)) {
+ char *cur_path = params->path;
+ char *cur_filename = params->filename;
+ params->path = params->filename;
+ retval = ext2fs_dir_iterate2(params->fs, de->inode, 0, NULL,
+ walk_dir, params);
+ if (retval)
+ goto end;
+ params->path = cur_path;
+ params->filename = cur_filename;
+ }
+ }
+
+end:
+ free(params->filename);
+ params->error |= retval;
+ return retval;
+}
+
+errcode_t __android_configure_fs(ext2_filsys fs, char *src_dir,
+ char *target_out,
+ char *mountpoint,
+ fs_config_f fs_config_func,
+ struct selabel_handle *sehnd,
+ time_t fixed_time,
+ const struct ugid_map* uid_map,
+ const struct ugid_map* gid_map)
+{
+ errcode_t retval;
+ struct inode_params params = {
+ .fs = fs,
+ .src_dir = src_dir,
+ .target_out = target_out,
+ .fs_config_func = fs_config_func,
+ .sehnd = sehnd,
+ .fixed_time = fixed_time,
+ .path = mountpoint,
+ .filename = mountpoint,
+ .mountpoint = mountpoint,
+ .uid_map = uid_map,
+ .gid_map = gid_map,
+ .error = 0
+ };
+
+ /* walk_dir will add the "/". Don't add it twice. */
+ if (strlen(mountpoint) == 1 && mountpoint[0] == '/')
+ params.path = "";
+
+ retval = androidify_inode(fs, EXT2_ROOT_INO, &params);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_dir_iterate2(fs, EXT2_ROOT_INO, 0, NULL, walk_dir,
+ &params);
+ if (retval)
+ return retval;
+ return params.error;
+}
+
+errcode_t android_configure_fs(ext2_filsys fs, char *src_dir, char *target_out,
+ char *mountpoint,
+ struct selinux_opt *seopts EXT2FS_ATTR((unused)),
+ unsigned int nopt EXT2FS_ATTR((unused)),
+ char *fs_config_file, time_t fixed_time,
+ const struct ugid_map* uid_map,
+ const struct ugid_map* gid_map)
+{
+ errcode_t retval;
+ fs_config_f fs_config_func = NULL;
+ struct selabel_handle *sehnd = NULL;
+
+ /* Retrieve file contexts */
+#if !defined(__ANDROID__)
+ if (nopt > 0) {
+ sehnd = selabel_open(SELABEL_CTX_FILE, seopts, nopt);
+ if (!sehnd) {
+ int saved_errno = errno;
+ com_err(__func__, errno,
+ _("while opening file contexts \"%s\""),
+ seopts[0].value);
+ return saved_errno;
+ }
+ }
+#else
+ sehnd = selinux_android_file_context_handle();
+ if (!sehnd) {
+ com_err(__func__, EINVAL,
+ _("while opening android file_contexts"));
+ return EINVAL;
+ }
+#endif
+
+ /* Load the FS config */
+ if (fs_config_file) {
+ retval = load_canned_fs_config(fs_config_file);
+ if (retval < 0) {
+ com_err(__func__, retval,
+ _("while loading fs_config \"%s\""),
+ fs_config_file);
+ return retval;
+ }
+ fs_config_func = canned_fs_config;
+ } else if (mountpoint)
+ fs_config_func = fs_config;
+
+ return __android_configure_fs(fs, src_dir, target_out, mountpoint,
+ fs_config_func, sehnd, fixed_time,
+ uid_map, gid_map);
+}