sysfs, kobject: allow creating kobject belonging to arbitrary users

Normally kobjects and their sysfs representation belong to global root,
however it is not necessarily the case for objects in separate namespaces.
For example, objects in separate network namespace logically belong to the
container's root and not global root.

This change lays groundwork for allowing network namespace objects
ownership to be transferred to container's root user by defining
get_ownership() callback in ktype structure and using it in sysfs code to
retrieve desired uid/gid when creating sysfs objects for given kobject.

Co-Developed-by: Tyler Hicks <tyhicks@canonical.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Dmitry Torokhov 2018-07-20 21:56:48 +00:00 committed by David S. Miller
parent 488dee96bb
commit 5f81880d52
6 changed files with 67 additions and 23 deletions

View File

@ -40,6 +40,8 @@ void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
{ {
struct kernfs_node *parent, *kn; struct kernfs_node *parent, *kn;
kuid_t uid;
kgid_t gid;
BUG_ON(!kobj); BUG_ON(!kobj);
@ -51,9 +53,10 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
if (!parent) if (!parent)
return -ENOENT; return -ENOENT;
kobject_get_ownership(kobj, &uid, &gid);
kn = kernfs_create_dir_ns(parent, kobject_name(kobj), kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
S_IRWXU | S_IRUGO | S_IXUGO, S_IRWXU | S_IRUGO | S_IXUGO, uid, gid,
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
kobj, ns); kobj, ns);
if (IS_ERR(kn)) { if (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST) if (PTR_ERR(kn) == -EEXIST)

View File

@ -245,7 +245,7 @@ static const struct kernfs_ops sysfs_bin_kfops_mmap = {
int sysfs_add_file_mode_ns(struct kernfs_node *parent, int sysfs_add_file_mode_ns(struct kernfs_node *parent,
const struct attribute *attr, bool is_bin, const struct attribute *attr, bool is_bin,
umode_t mode, const void *ns) umode_t mode, kuid_t uid, kgid_t gid, const void *ns)
{ {
struct lock_class_key *key = NULL; struct lock_class_key *key = NULL;
const struct kernfs_ops *ops; const struct kernfs_ops *ops;
@ -302,8 +302,8 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent,
if (!attr->ignore_lockdep) if (!attr->ignore_lockdep)
key = attr->key ?: (struct lock_class_key *)&attr->skey; key = attr->key ?: (struct lock_class_key *)&attr->skey;
#endif #endif
kn = __kernfs_create_file(parent, attr->name,
mode & 0777, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
size, ops, (void *)attr, ns, key); size, ops, (void *)attr, ns, key);
if (IS_ERR(kn)) { if (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST) if (PTR_ERR(kn) == -EEXIST)
@ -313,12 +313,6 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent,
return 0; return 0;
} }
int sysfs_add_file(struct kernfs_node *parent, const struct attribute *attr,
bool is_bin)
{
return sysfs_add_file_mode_ns(parent, attr, is_bin, attr->mode, NULL);
}
/** /**
* sysfs_create_file_ns - create an attribute file for an object with custom ns * sysfs_create_file_ns - create an attribute file for an object with custom ns
* @kobj: object we're creating for * @kobj: object we're creating for
@ -328,9 +322,14 @@ int sysfs_add_file(struct kernfs_node *parent, const struct attribute *attr,
int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr, int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
const void *ns) const void *ns)
{ {
kuid_t uid;
kgid_t gid;
BUG_ON(!kobj || !kobj->sd || !attr); BUG_ON(!kobj || !kobj->sd || !attr);
return sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode, ns); kobject_get_ownership(kobj, &uid, &gid);
return sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode,
uid, gid, ns);
} }
EXPORT_SYMBOL_GPL(sysfs_create_file_ns); EXPORT_SYMBOL_GPL(sysfs_create_file_ns);
@ -359,6 +358,8 @@ int sysfs_add_file_to_group(struct kobject *kobj,
const struct attribute *attr, const char *group) const struct attribute *attr, const char *group)
{ {
struct kernfs_node *parent; struct kernfs_node *parent;
kuid_t uid;
kgid_t gid;
int error; int error;
if (group) { if (group) {
@ -371,7 +372,9 @@ int sysfs_add_file_to_group(struct kobject *kobj,
if (!parent) if (!parent)
return -ENOENT; return -ENOENT;
error = sysfs_add_file(parent, attr, false); kobject_get_ownership(kobj, &uid, &gid);
error = sysfs_add_file_mode_ns(kobj->sd, attr, false,
attr->mode, uid, gid, NULL);
kernfs_put(parent); kernfs_put(parent);
return error; return error;
@ -487,9 +490,14 @@ EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
int sysfs_create_bin_file(struct kobject *kobj, int sysfs_create_bin_file(struct kobject *kobj,
const struct bin_attribute *attr) const struct bin_attribute *attr)
{ {
kuid_t uid;
kgid_t gid;
BUG_ON(!kobj || !kobj->sd || !attr); BUG_ON(!kobj || !kobj->sd || !attr);
return sysfs_add_file(kobj->sd, &attr->attr, true); kobject_get_ownership(kobj, &uid, &gid);
return sysfs_add_file_mode_ns(kobj->sd, &attr->attr, true,
attr->attr.mode, uid, gid, NULL);
} }
EXPORT_SYMBOL_GPL(sysfs_create_bin_file); EXPORT_SYMBOL_GPL(sysfs_create_bin_file);

View File

@ -31,6 +31,7 @@ static void remove_files(struct kernfs_node *parent,
} }
static int create_files(struct kernfs_node *parent, struct kobject *kobj, static int create_files(struct kernfs_node *parent, struct kobject *kobj,
kuid_t uid, kgid_t gid,
const struct attribute_group *grp, int update) const struct attribute_group *grp, int update)
{ {
struct attribute *const *attr; struct attribute *const *attr;
@ -60,7 +61,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj,
mode &= SYSFS_PREALLOC | 0664; mode &= SYSFS_PREALLOC | 0664;
error = sysfs_add_file_mode_ns(parent, *attr, false, error = sysfs_add_file_mode_ns(parent, *attr, false,
mode, NULL); mode, uid, gid, NULL);
if (unlikely(error)) if (unlikely(error))
break; break;
} }
@ -90,7 +91,8 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj,
mode &= SYSFS_PREALLOC | 0664; mode &= SYSFS_PREALLOC | 0664;
error = sysfs_add_file_mode_ns(parent, error = sysfs_add_file_mode_ns(parent,
&(*bin_attr)->attr, true, &(*bin_attr)->attr, true,
mode, NULL); mode,
uid, gid, NULL);
if (error) if (error)
break; break;
} }
@ -106,6 +108,8 @@ static int internal_create_group(struct kobject *kobj, int update,
const struct attribute_group *grp) const struct attribute_group *grp)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
kuid_t uid;
kgid_t gid;
int error; int error;
BUG_ON(!kobj || (!update && !kobj->sd)); BUG_ON(!kobj || (!update && !kobj->sd));
@ -118,9 +122,11 @@ static int internal_create_group(struct kobject *kobj, int update,
kobj->name, grp->name ?: ""); kobj->name, grp->name ?: "");
return -EINVAL; return -EINVAL;
} }
kobject_get_ownership(kobj, &uid, &gid);
if (grp->name) { if (grp->name) {
kn = kernfs_create_dir(kobj->sd, grp->name, kn = kernfs_create_dir_ns(kobj->sd, grp->name,
S_IRWXU | S_IRUGO | S_IXUGO, kobj); S_IRWXU | S_IRUGO | S_IXUGO,
uid, gid, kobj, NULL);
if (IS_ERR(kn)) { if (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST) if (PTR_ERR(kn) == -EEXIST)
sysfs_warn_dup(kobj->sd, grp->name); sysfs_warn_dup(kobj->sd, grp->name);
@ -129,7 +135,7 @@ static int internal_create_group(struct kobject *kobj, int update,
} else } else
kn = kobj->sd; kn = kobj->sd;
kernfs_get(kn); kernfs_get(kn);
error = create_files(kn, kobj, grp, update); error = create_files(kn, kobj, uid, gid, grp, update);
if (error) { if (error) {
if (grp->name) if (grp->name)
kernfs_remove(kn); kernfs_remove(kn);
@ -281,6 +287,8 @@ int sysfs_merge_group(struct kobject *kobj,
const struct attribute_group *grp) const struct attribute_group *grp)
{ {
struct kernfs_node *parent; struct kernfs_node *parent;
kuid_t uid;
kgid_t gid;
int error = 0; int error = 0;
struct attribute *const *attr; struct attribute *const *attr;
int i; int i;
@ -289,8 +297,11 @@ int sysfs_merge_group(struct kobject *kobj,
if (!parent) if (!parent)
return -ENOENT; return -ENOENT;
kobject_get_ownership(kobj, &uid, &gid);
for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr)) for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
error = sysfs_add_file(parent, *attr, false); error = sysfs_add_file_mode_ns(parent, *attr, false,
(*attr)->mode, uid, gid, NULL);
if (error) { if (error) {
while (--i >= 0) while (--i >= 0)
kernfs_remove_by_name(parent, (*--attr)->name); kernfs_remove_by_name(parent, (*--attr)->name);

View File

@ -27,11 +27,10 @@ void sysfs_warn_dup(struct kernfs_node *parent, const char *name);
/* /*
* file.c * file.c
*/ */
int sysfs_add_file(struct kernfs_node *parent,
const struct attribute *attr, bool is_bin);
int sysfs_add_file_mode_ns(struct kernfs_node *parent, int sysfs_add_file_mode_ns(struct kernfs_node *parent,
const struct attribute *attr, bool is_bin, const struct attribute *attr, bool is_bin,
umode_t amode, const void *ns); umode_t amode, kuid_t uid, kgid_t gid,
const void *ns);
/* /*
* symlink.c * symlink.c

View File

@ -26,6 +26,7 @@
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/uidgid.h>
#define UEVENT_HELPER_PATH_LEN 256 #define UEVENT_HELPER_PATH_LEN 256
#define UEVENT_NUM_ENVP 32 /* number of env pointers */ #define UEVENT_NUM_ENVP 32 /* number of env pointers */
@ -114,6 +115,8 @@ extern struct kobject * __must_check kobject_get_unless_zero(
extern void kobject_put(struct kobject *kobj); extern void kobject_put(struct kobject *kobj);
extern const void *kobject_namespace(struct kobject *kobj); extern const void *kobject_namespace(struct kobject *kobj);
extern void kobject_get_ownership(struct kobject *kobj,
kuid_t *uid, kgid_t *gid);
extern char *kobject_get_path(struct kobject *kobj, gfp_t flag); extern char *kobject_get_path(struct kobject *kobj, gfp_t flag);
struct kobj_type { struct kobj_type {
@ -122,6 +125,7 @@ struct kobj_type {
struct attribute **default_attrs; struct attribute **default_attrs;
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj);
void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
}; };
struct kobj_uevent_env { struct kobj_uevent_env {

View File

@ -35,6 +35,25 @@ const void *kobject_namespace(struct kobject *kobj)
return kobj->ktype->namespace(kobj); return kobj->ktype->namespace(kobj);
} }
/**
* kobject_get_ownership - get sysfs ownership data for @kobj
* @kobj: kobject in question
* @uid: kernel user ID for sysfs objects
* @gid: kernel group ID for sysfs objects
*
* Returns initial uid/gid pair that should be used when creating sysfs
* representation of given kobject. Normally used to adjust ownership of
* objects in a container.
*/
void kobject_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid)
{
*uid = GLOBAL_ROOT_UID;
*gid = GLOBAL_ROOT_GID;
if (kobj->ktype->get_ownership)
kobj->ktype->get_ownership(kobj, uid, gid);
}
/* /*
* populate_dir - populate directory with attributes. * populate_dir - populate directory with attributes.
* @kobj: object we're working on. * @kobj: object we're working on.