Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 992aef92 authored by Daniel Rosenberg's avatar Daniel Rosenberg
Browse files

ANDROID: sdcardfs: add support for user permission isolation



This allows you to hide the existence of a package from
a user by adding them to an exclude list. If a user
creates that package's folder and is on the exclude list,
they will not see that package's id.

Signed-off-by: default avatarDaniel Rosenberg <drosen@google.com>
Bug: 34542611
Change-Id: I9eb82e0bf2457d7eb81ee56153b9c7d2f6646323
parent fe232f13
Loading
Loading
Loading
Loading
+19 −15
Original line number Diff line number Diff line
@@ -103,7 +103,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st
		case PERM_ANDROID_OBB:
		case PERM_ANDROID_MEDIA:
			appid = get_appid(newdentry->d_name.name);
			if (appid != 0) {
			if (appid != 0 && !is_excluded(newdentry->d_name.name, parent_info->userid)) {
				info->d_uid = multiuser_get_uid(parent_info->userid, appid);
			}
			set_top(info, &info->vfs_inode);
@@ -116,8 +116,11 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry)
	get_derived_permission_new(parent, dentry, dentry);
}

static int descendant_may_need_fixup(perm_t perm) {
	if (perm == PERM_PRE_ROOT || perm == PERM_ROOT || perm == PERM_ANDROID)
static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit)
{
	if (info->perm == PERM_ROOT)
		return (limit->flags & BY_USERID)?info->userid == limit->userid:1;
	if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID)
		return 1;
	return 0;
}
@@ -129,7 +132,8 @@ static int needs_fixup(perm_t perm) {
	return 0;
}

void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) {
void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit)
{
	struct dentry *child;
	struct sdcardfs_inode_info *info;
	if (!dget(dentry))
@@ -144,10 +148,10 @@ void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len)
		spin_lock(&dentry->d_lock);
		list_for_each_entry(child, &dentry->d_subdirs, d_child) {
			dget(child);
				if (!strncasecmp(child->d_name.name, name, len)) {
					if (child->d_inode) {
			if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) {
				if (d_inode(child)) {
					get_derived_permission(dentry, child);
						fixup_tmp_permissions(child->d_inode);
					fixup_tmp_permissions(d_inode(child));
					dput(child);
					break;
				}
@@ -155,10 +159,10 @@ void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len)
			dput(child);
		}
		spin_unlock(&dentry->d_lock);
	} else 	if (descendant_may_need_fixup(info->perm)) {
	} else 	if (descendant_may_need_fixup(info, limit)) {
		spin_lock(&dentry->d_lock);
		list_for_each_entry(child, &dentry->d_subdirs, d_child) {
				fixup_perms_recursive(child, name, len);
				fixup_perms_recursive(child, limit);
		}
		spin_unlock(&dentry->d_lock);
	}
+273 −24
Original line number Diff line number Diff line
@@ -31,11 +31,13 @@

struct hashtable_entry {
	struct hlist_node hlist;
	struct hlist_node dlist; /* for deletion cleanup */
	const char *key;
	atomic_t value;
};

static DEFINE_HASHTABLE(package_to_appid, 8);
static DEFINE_HASHTABLE(package_to_userid, 8);

static struct kmem_cache *hashtable_entry_cachep;

@@ -69,6 +71,22 @@ appid_t get_appid(const char *app_name)
	return 0;
}

appid_t is_excluded(const char *app_name, userid_t user)
{
	struct hashtable_entry *hash_cur;
	unsigned int hash = str_hash(app_name);

	rcu_read_lock();
	hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
		if (atomic_read(&hash_cur->value) == user && !strcasecmp(app_name, hash_cur->key)) {
			rcu_read_unlock();
			return 1;
		}
	}
	rcu_read_unlock();
	return 0;
}

/* Kernel has already enforced everything we returned through
 * derive_permissions_locked(), so this is used to lock down access
 * even further, such as enforcing that apps hold sdcard_rw. */
@@ -124,7 +142,7 @@ static struct hashtable_entry *alloc_packagelist_entry(const char *key,
	return ret;
}

static int insert_packagelist_entry_locked(const char *key, appid_t value)
static int insert_packagelist_appid_entry_locked(const char *key, appid_t value)
{
	struct hashtable_entry *hash_cur;
	struct hashtable_entry *new_entry;
@@ -143,18 +161,64 @@ static int insert_packagelist_entry_locked(const char *key, appid_t value)
	return 0;
}

static void fixup_perms(struct super_block *sb, const char *key) {
	if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) {
		fixup_perms_recursive(sb->s_root, key, strlen(key));
static int insert_userid_exclude_entry_locked(const char *key, userid_t value)
{
	struct hashtable_entry *hash_cur;
	struct hashtable_entry *new_entry;
	unsigned int hash = str_hash(key);

	/* Only insert if not already present */
	hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
		if (atomic_read(&hash_cur->value) == value && !strcasecmp(key, hash_cur->key))
			return 0;
	}
	new_entry = alloc_packagelist_entry(key, value);
	if (!new_entry)
		return -ENOMEM;
	hash_add_rcu(package_to_userid, &new_entry->hlist, hash);
	return 0;
}

static void fixup_all_perms_name(const char *key)
{
	struct sdcardfs_sb_info *sbinfo;
	struct limit_search limit = {
		.flags = BY_NAME,
		.name = key,
		.length = strlen(key),
	};
	list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
		if (sbinfo_has_sdcard_magic(sbinfo))
			fixup_perms_recursive(sbinfo->sb->s_root, &limit);
	}
}

static void fixup_all_perms_name_userid(const char *key, userid_t userid)
{
	struct sdcardfs_sb_info *sbinfo;
	struct limit_search limit = {
		.flags = BY_NAME | BY_USERID,
		.name = key,
		.length = strlen(key),
		.userid = userid,
	};
	list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
		if (sbinfo_has_sdcard_magic(sbinfo))
			fixup_perms_recursive(sbinfo->sb->s_root, &limit);
	}
}

static void fixup_all_perms(const char *key)
static void fixup_all_perms_userid(userid_t userid)
{
	struct sdcardfs_sb_info *sbinfo;
	list_for_each_entry(sbinfo, &sdcardfs_super_list, list)
		if (sbinfo)
			fixup_perms(sbinfo->sb, key);
	struct limit_search limit = {
		.flags = BY_USERID,
		.userid = userid,
	};
	list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
		if (sbinfo_has_sdcard_magic(sbinfo))
			fixup_perms_recursive(sbinfo->sb->s_root, &limit);
	}
}

static int insert_packagelist_entry(const char *key, appid_t value)
@@ -162,9 +226,22 @@ static int insert_packagelist_entry(const char *key, appid_t value)
	int err;

	mutex_lock(&sdcardfs_super_list_lock);
	err = insert_packagelist_entry_locked(key, value);
	err = insert_packagelist_appid_entry_locked(key, value);
	if (!err)
		fixup_all_perms(key);
		fixup_all_perms_name(key);
	mutex_unlock(&sdcardfs_super_list_lock);

	return err;
}

static int insert_userid_exclude_entry(const char *key, userid_t value)
{
	int err;

	mutex_lock(&sdcardfs_super_list_lock);
	err = insert_userid_exclude_entry_locked(key, value);
	if (!err)
		fixup_all_perms_name_userid(key, value);
	mutex_unlock(&sdcardfs_super_list_lock);

	return err;
@@ -173,7 +250,7 @@ static int insert_packagelist_entry(const char *key, appid_t value)
static void free_packagelist_entry(struct hashtable_entry *entry)
{
	kfree(entry->key);
	hash_del_rcu(&entry->hlist);
	hash_del_rcu(&entry->dlist);
	kmem_cache_free(hashtable_entry_cachep, entry);
}

@@ -181,22 +258,84 @@ static void remove_packagelist_entry_locked(const char *key)
{
	struct hashtable_entry *hash_cur;
	unsigned int hash = str_hash(key);
	struct hlist_node *h_t;
	HLIST_HEAD(free_list);

	hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
		if (!strcasecmp(key, hash_cur->key)) {
			hash_del_rcu(&hash_cur->hlist);
			hlist_add_head(&hash_cur->dlist, &free_list);
		}
	}
	hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
		if (!strcasecmp(key, hash_cur->key)) {
			hash_del_rcu(&hash_cur->hlist);
			hlist_add_head(&hash_cur->dlist, &free_list);
			break;
		}
	}
	synchronize_rcu();
	hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
		free_packagelist_entry(hash_cur);
}

static void remove_packagelist_entry(const char *key)
{
	mutex_lock(&sdcardfs_super_list_lock);
	remove_packagelist_entry_locked(key);
	fixup_all_perms_name(key);
	mutex_unlock(&sdcardfs_super_list_lock);
	return;
}

static void remove_userid_all_entry_locked(userid_t userid)
{
	struct hashtable_entry *hash_cur;
	struct hlist_node *h_t;
	HLIST_HEAD(free_list);
	int i;

	hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) {
		if (atomic_read(&hash_cur->value) == userid) {
			hash_del_rcu(&hash_cur->hlist);
			hlist_add_head(&hash_cur->dlist, &free_list);
		}
	}
	synchronize_rcu();
	hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) {
		free_packagelist_entry(hash_cur);
	}
}

static void remove_packagelist_entry(const char *key)
static void remove_userid_all_entry(userid_t userid)
{
	mutex_lock(&sdcardfs_super_list_lock);
	remove_packagelist_entry_locked(key);
	fixup_all_perms(key);
	remove_userid_all_entry_locked(userid);
	fixup_all_perms_userid(userid);
	mutex_unlock(&sdcardfs_super_list_lock);
	return;
}

static void remove_userid_exclude_entry_locked(const char *key, userid_t userid)
{
	struct hashtable_entry *hash_cur;
	unsigned int hash = str_hash(key);

	hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
		if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == userid) {
			hash_del_rcu(&hash_cur->hlist);
			synchronize_rcu();
			free_packagelist_entry(hash_cur);
			break;
		}
	}
}

static void remove_userid_exclude_entry(const char *key, userid_t userid)
{
	mutex_lock(&sdcardfs_super_list_lock);
	remove_userid_exclude_entry_locked(key, userid);
	fixup_all_perms_name_userid(key, userid);
	mutex_unlock(&sdcardfs_super_list_lock);
	return;
}
@@ -210,16 +349,44 @@ static void packagelist_destroy(void)
	mutex_lock(&sdcardfs_super_list_lock);
	hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) {
		hash_del_rcu(&hash_cur->hlist);
		hlist_add_head(&hash_cur->hlist, &free_list);

		hlist_add_head(&hash_cur->dlist, &free_list);
	}
	hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) {
		hash_del_rcu(&hash_cur->hlist);
		hlist_add_head(&hash_cur->dlist, &free_list);
	}
	synchronize_rcu();
	hlist_for_each_entry_safe(hash_cur, h_t, &free_list, hlist)
	hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
		free_packagelist_entry(hash_cur);
	mutex_unlock(&sdcardfs_super_list_lock);
	printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n");
}

#define SDCARDFS_CONFIGFS_ATTR(_pfx, _name)			\
static struct configfs_attribute _pfx##attr_##_name = {	\
	.ca_name	= __stringify(_name),		\
	.ca_mode	= S_IRUGO | S_IWUGO,		\
	.ca_owner	= THIS_MODULE,			\
	.show		= _pfx##_name##_show,		\
	.store		= _pfx##_name##_store,		\
}

#define SDCARDFS_CONFIGFS_ATTR_RO(_pfx, _name)			\
static struct configfs_attribute _pfx##attr_##_name = {	\
	.ca_name	= __stringify(_name),		\
	.ca_mode	= S_IRUGO,			\
	.ca_owner	= THIS_MODULE,			\
	.show		= _pfx##_name##_show,		\
}

#define SDCARDFS_CONFIGFS_ATTR_WO(_pfx, _name)			\
static struct configfs_attribute _pfx##attr_##_name = {	\
	.ca_name	= __stringify(_name),		\
	.ca_mode	= S_IWUGO,			\
	.ca_owner	= THIS_MODULE,			\
	.store		= _pfx##_name##_store,		\
}

struct package_details {
	struct config_item item;
	const char *name;
@@ -253,6 +420,58 @@ static ssize_t package_details_appid_store(struct config_item *item,
	return count;
}

static ssize_t package_details_excluded_userids_show(struct config_item *item,
				      char *page)
{
	struct package_details *package_details = to_package_details(item);
	struct hashtable_entry *hash_cur;
	unsigned int hash = str_hash(package_details->name);
	int count = 0;

	rcu_read_lock();
	hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
		if (!strcasecmp(package_details->name, hash_cur->key))
			count += scnprintf(page + count, PAGE_SIZE - count,
					"%d ", atomic_read(&hash_cur->value));
	}
	rcu_read_unlock();
	if (count)
		count--;
	count += scnprintf(page + count, PAGE_SIZE - count, "\n");
	return count;
}

static ssize_t package_details_excluded_userids_store(struct config_item *item,
				       const char *page, size_t count)
{
	unsigned int tmp;
	int ret;

	ret = kstrtouint(page, 10, &tmp);
	if (ret)
		return ret;

	ret = insert_userid_exclude_entry(to_package_details(item)->name, tmp);

	if (ret)
		return ret;

	return count;
}

static ssize_t package_details_clear_userid_store(struct config_item *item,
				       const char *page, size_t count)
{
	unsigned int tmp;
	int ret;

	ret = kstrtouint(page, 10, &tmp);
	if (ret)
		return ret;
	remove_userid_exclude_entry(to_package_details(item)->name, tmp);
	return count;
}

static void package_details_release(struct config_item *item)
{
	struct package_details *package_details = to_package_details(item);
@@ -262,10 +481,14 @@ static void package_details_release(struct config_item *item)
	kfree(package_details);
}

CONFIGFS_ATTR(package_details_, appid);
SDCARDFS_CONFIGFS_ATTR(package_details_, appid);
SDCARDFS_CONFIGFS_ATTR(package_details_, excluded_userids);
SDCARDFS_CONFIGFS_ATTR_WO(package_details_, clear_userid);

static struct configfs_attribute *package_details_attrs[] = {
	&package_details_attr_appid,
	&package_details_attr_excluded_userids,
	&package_details_attr_clear_userid,
	NULL,
};

@@ -300,16 +523,26 @@ static struct config_item *packages_make_item(struct config_group *group, const

static ssize_t packages_list_show(struct config_item *item, char *page)
{
	struct hashtable_entry *hash_cur;
	struct hashtable_entry *hash_cur_app;
	struct hashtable_entry *hash_cur_user;
	int i;
	int count = 0, written = 0;
	const char errormsg[] = "<truncated>\n";
	unsigned int hash;

	rcu_read_lock();
	hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) {
	hash_for_each_rcu(package_to_appid, i, hash_cur_app, hlist) {
		written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n",
					(const char *)hash_cur->key, atomic_read(&hash_cur->value));
		if (count + written == PAGE_SIZE - sizeof(errormsg)) {
					hash_cur_app->key, atomic_read(&hash_cur_app->value));
		hash = str_hash(hash_cur_app->key);
		hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) {
			if (!strcasecmp(hash_cur_app->key, hash_cur_user->key)) {
				written += scnprintf(page + count + written - 1,
					PAGE_SIZE - sizeof(errormsg) - count - written + 1,
					" %d\n",	atomic_read(&hash_cur_user->value)) - 1;
			}
		}
		if (count + written == PAGE_SIZE - sizeof(errormsg) - 1) {
			count += scnprintf(page + count, PAGE_SIZE - count, errormsg);
			break;
		}
@@ -320,6 +553,19 @@ static ssize_t packages_list_show(struct config_item *item, char *page)
	return count;
}

static ssize_t packages_remove_userid_store(struct config_item *item,
				       const char *page, size_t count)
{
	unsigned int tmp;
	int ret;

	ret = kstrtouint(page, 10, &tmp);
	if (ret)
		return ret;
	remove_userid_all_entry(tmp);
	return count;
}

static struct configfs_attribute packages_attr_packages_gid_list = {
	.ca_name	= "packages_gid.list",
	.ca_mode	= S_IRUGO,
@@ -327,8 +573,11 @@ static struct configfs_attribute packages_attr_packages_gid_list = {
	.show		= packages_list_show,
};

SDCARDFS_CONFIGFS_ATTR_WO(packages_, remove_userid);

static struct configfs_attribute *packages_attrs[] = {
	&packages_attr_packages_gid_list,
	&packages_attr_remove_userid,
	NULL,
};

+16 −1
Original line number Diff line number Diff line
@@ -335,6 +335,11 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \
SDCARDFS_DENT_FUNC(lower_path)
SDCARDFS_DENT_FUNC(orig_path)

static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo)
{
  return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
}

/* grab a refererence if we aren't linking to ourself */
static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top)
{
@@ -442,18 +447,28 @@ extern struct list_head sdcardfs_super_list;

/* for packagelist.c */
extern appid_t get_appid(const char *app_name);
extern appid_t is_excluded(const char *app_name, userid_t userid);
extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
extern int open_flags_to_access_mode(int open_flags);
extern int packagelist_init(void);
extern void packagelist_exit(void);

/* for derived_perm.c */
#define BY_NAME		(1 << 0)
#define BY_USERID	(1 << 1)
struct limit_search {
	unsigned int flags;
	const char *name;
	size_t length;
	userid_t userid;
};

extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
			uid_t uid, bool under_android, struct inode *top);
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry);
extern void fixup_top_recursive(struct dentry *parent);
extern void fixup_perms_recursive(struct dentry *dentry, const char *name, size_t len);
extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);

extern void update_derived_permission_lock(struct dentry *dentry);
extern int need_graft_path(struct dentry *dentry);