[PATCH v3] kernel: Conditionally support non-root users, groups and capabilities

60 views
Skip to first unread message

Iulia Manda

unread,
Jan 17, 2015, 12:46:44 PM1/17/15
to jo...@joshtriplett.org, opw-k...@googlegroups.com
There are a lot of embedded systems that run most or all of their functionality
in init, running as root:root. For these systems, supporting multiple users is
not necessary.

This patch adds a new symbol, CONFIG_NON_ROOT, that makes support for non-root
users, non-root groups, and capabilities optional.

When this symbol is not defined, UID and GID are zero in any possible case
and processes always have all capabilities.

Also, the following syscalls are compiled out: setuid, setregid, setgid,
setreuid, setresuid, getresuid, setresgid, getresgid, setgroups, getgroups,
setfsuid, setfsgid, capget, capset.

This change saves about 25 KB on a defconfig build.

Bloat-o-meter output:
add/remove: 7/66 grow/shrink: 21/421 up/down: 1701/-27172 (-25471)

Signed-off-by: Iulia Manda <iulia....@gmail.com>
---
include/linux/capability.h | 12 ++++++++++++
include/linux/uidgid.h | 12 ++++++++++++
init/Kconfig | 19 ++++++++++++++++++-
kernel/capability.c | 6 ++++++
kernel/groups.c | 4 ++++
kernel/sys.c | 2 ++
kernel/sys_ni.c | 14 ++++++++++++++
7 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/include/linux/capability.h b/include/linux/capability.h
index aa93e5e..d8791d2 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -211,8 +211,20 @@ extern bool has_ns_capability(struct task_struct *t,
extern bool has_capability_noaudit(struct task_struct *t, int cap);
extern bool has_ns_capability_noaudit(struct task_struct *t,
struct user_namespace *ns, int cap);
+#ifdef CONFIG_NON_ROOT
extern bool capable(int cap);
extern bool ns_capable(struct user_namespace *ns, int cap);
+#else
+static inline bool capable(int cap)
+{
+ return true;
+}
+
+static inline bool ns_capable(struct user_namespace *ns, int cap)
+{
+ return true;
+}
+#endif /* CONFIG_NON_ROOT */
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);

diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h
index 2d1f9b6..22bd1fa 100644
--- a/include/linux/uidgid.h
+++ b/include/linux/uidgid.h
@@ -29,6 +29,7 @@ typedef struct {
#define KUIDT_INIT(value) (kuid_t){ value }
#define KGIDT_INIT(value) (kgid_t){ value }

+#ifdef CONFIG_NON_ROOT
static inline uid_t __kuid_val(kuid_t uid)
{
return uid.val;
@@ -38,6 +39,17 @@ static inline gid_t __kgid_val(kgid_t gid)
{
return gid.val;
}
+#else
+static inline uid_t __kuid_val(kuid_t uid)
+{
+ return 0;
+}
+
+static inline gid_t __kgid_val(kgid_t gid)
+{
+ return 0;
+}
+#endif

#define GLOBAL_ROOT_UID KUIDT_INIT(0)
#define GLOBAL_ROOT_GID KGIDT_INIT(0)
diff --git a/init/Kconfig b/init/Kconfig
index 9afb971..dc5bfd4 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -394,6 +394,7 @@ endchoice

config BSD_PROCESS_ACCT
bool "BSD Process Accounting"
+ select NON_ROOT
help
If you say Y here, a user level program will be able to instruct the
kernel (via a special system call) to write process accounting
@@ -420,6 +421,7 @@ config BSD_PROCESS_ACCT_V3
config TASKSTATS
bool "Export task/process statistics through netlink"
depends on NET
+ select NON_ROOT
default n
help
Export selected statistics for tasks/processes through the
@@ -1140,6 +1142,7 @@ config CHECKPOINT_RESTORE

menuconfig NAMESPACES
bool "Namespaces support" if EXPERT
+ depends on NON_ROOT
default !EXPERT
help
Provides the way to make tasks work with different objects using
@@ -1352,11 +1355,25 @@ menuconfig EXPERT

config UID16
bool "Enable 16-bit UID system calls" if EXPERT
- depends on HAVE_UID16
+ depends on HAVE_UID16 && NON_ROOT
default y
help
This enables the legacy 16-bit UID syscall wrappers.

+config NON_ROOT
+ bool "Multiple users, groups and capabilities support" if EXPERT
+ default y
+ help
+ This option enables support for non-root users, groups and
+ capabilities.
+
+ If you say N here, all processes will run with UID 0, GID 0, and all
+ possible capabilities. Saying N here also compiles out support for
+ system calls related to UIDs, GIDs, and capabilities, such as setuid,
+ setgid, and capset.
+
+ If unsure, say Y here.
+
config SGETMASK_SYSCALL
bool "sgetmask/ssetmask syscalls support" if EXPERT
def_bool PARISC || MN10300 || BLACKFIN || M68K || PPC || MIPS || X86 || SPARC || CRIS || MICROBLAZE || SUPERH
diff --git a/kernel/capability.c b/kernel/capability.c
index 989f5bf..bead84a 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -35,6 +35,7 @@ static int __init file_caps_disable(char *str)
}
__setup("no_file_caps", file_caps_disable);

+#ifdef CONFIG_NON_ROOT
/*
* More recent versions of libcap are available from:
*
@@ -279,6 +280,7 @@ error:
abort_creds(new);
return ret;
}
+#endif

/**
* has_ns_capability - Does a task have a capability in a specific user ns
@@ -360,6 +362,7 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
return has_ns_capability_noaudit(t, &init_user_ns, cap);
}

+#ifdef CONFIG_NON_ROOT
/**
* ns_capable - Determine if the current task has a superior capability in effect
* @ns: The usernamespace we want the capability in
@@ -385,6 +388,7 @@ bool ns_capable(struct user_namespace *ns, int cap)
return false;
}
EXPORT_SYMBOL(ns_capable);
+#endif

/**
* file_ns_capable - Determine if the file's opener had a capability in effect
@@ -411,6 +415,7 @@ bool file_ns_capable(const struct file *file, struct user_namespace *ns,
}
EXPORT_SYMBOL(file_ns_capable);

+#ifdef CONFIG_NON_ROOT
/**
* capable - Determine if the current task has a superior capability in effect
* @cap: The capability to be tested for
@@ -426,6 +431,7 @@ bool capable(int cap)
return ns_capable(&init_user_ns, cap);
}
EXPORT_SYMBOL(capable);
+#endif

/**
* capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
diff --git a/kernel/groups.c b/kernel/groups.c
index 664411f..94f2c89 100644
--- a/kernel/groups.c
+++ b/kernel/groups.c
@@ -190,6 +190,7 @@ int set_current_groups(struct group_info *group_info)

EXPORT_SYMBOL(set_current_groups);

+#ifdef CONFIG_NON_ROOT
SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist)
{
const struct cred *cred = current_cred();
@@ -213,6 +214,7 @@ SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist)
out:
return i;
}
+#endif

bool may_setgroups(void)
{
@@ -227,6 +229,7 @@ bool may_setgroups(void)
* without another task interfering.
*/

+#ifdef CONFIG_NON_ROOT
SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)
{
struct group_info *group_info;
@@ -251,6 +254,7 @@ SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)

return retval;
}
+#endif

/*
* Check whether we're fsgid/egid or in the supplemental group..
diff --git a/kernel/sys.c b/kernel/sys.c
index a8c9f5a..bfe532b 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -319,6 +319,7 @@ out_unlock:
* SMP: There are not races, the GIDs are checked only by filesystem
* operations (as far as semantic preservation is concerned).
*/
+#ifdef CONFIG_NON_ROOT
SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)
{
struct user_namespace *ns = current_user_ns();
@@ -809,6 +810,7 @@ change_okay:
commit_creds(new);
return old_fsgid;
}
+#endif /* CONFIG_NON_ROOT */

/**
* sys_getpid - return the thread group id of the current process
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index 5adcb0a..7995ef5 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -159,6 +159,20 @@ cond_syscall(sys_uselib);
cond_syscall(sys_fadvise64);
cond_syscall(sys_fadvise64_64);
cond_syscall(sys_madvise);
+cond_syscall(sys_setuid);
+cond_syscall(sys_setregid);
+cond_syscall(sys_setgid);
+cond_syscall(sys_setreuid);
+cond_syscall(sys_setresuid);
+cond_syscall(sys_getresuid);
+cond_syscall(sys_setresgid);
+cond_syscall(sys_getresgid);
+cond_syscall(sys_setgroups);
+cond_syscall(sys_getgroups);
+cond_syscall(sys_setfsuid);
+cond_syscall(sys_setfsgid);
+cond_syscall(sys_capget);
+cond_syscall(sys_capset);

/* arch-specific weak syscall entries */
cond_syscall(sys_pciconfig_read);
--
1.7.10.4

jo...@joshtriplett.org

unread,
Jan 20, 2015, 10:36:36 AM1/20/15
to Iulia Manda, opw-k...@googlegroups.com
On Sat, Jan 17, 2015 at 07:45:56PM +0200, Iulia Manda wrote:
> There are a lot of embedded systems that run most or all of their functionality
> in init, running as root:root. For these systems, supporting multiple users is
> not necessary.
>
> This patch adds a new symbol, CONFIG_NON_ROOT, that makes support for non-root
> users, non-root groups, and capabilities optional.
>
> When this symbol is not defined, UID and GID are zero in any possible case
> and processes always have all capabilities.
>
> Also, the following syscalls are compiled out: setuid, setregid, setgid,
> setreuid, setresuid, getresuid, setresgid, getresgid, setgroups, getgroups,
> setfsuid, setfsgid, capget, capset.
>
> This change saves about 25 KB on a defconfig build.
>
> Bloat-o-meter output:
> add/remove: 7/66 grow/shrink: 21/421 up/down: 1701/-27172 (-25471)
>
> Signed-off-by: Iulia Manda <iulia....@gmail.com>

Reviewed-by: Josh Triplett <jo...@joshtriplett.org>

Please go ahead and send this upstream.

> include/linux/capability.h | 12 ++++++++++++
> include/linux/uidgid.h | 12 ++++++++++++
> init/Kconfig | 19 ++++++++++++++++++-
> kernel/capability.c | 6 ++++++
> kernel/groups.c | 4 ++++
> kernel/sys.c | 2 ++
> kernel/sys_ni.c | 14 ++++++++++++++
> 7 files changed, 68 insertions(+), 1 deletion(-)

An impressively small diffstat for such a large effect.
> --
> You received this message because you are subscribed to the Google Groups "opw-kernel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to opw-kernel+...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Iulia Manda

unread,
Jan 22, 2015, 9:26:43 AM1/22/15
to jo...@joshtriplett.org, opw-k...@googlegroups.com
This patch comes as a response to the discussions that took place after
I sent this patch upstream. It compiles out groups.c and also tries to
regroup the code in order to have all the functions in kernel/capability.c
that are to be conditionally compiled under a singe ifdef.

Note that struct init_groups and groups_alloc need to be defined somewhere
else (in another source file). If we make them static inline stubs in header,
other files which define these structs as extern and don't include linux/cred.h
will not be able to see their declaration and will fail with an undefined
reference compile error.

Also, in security/keys.c, key_task_permission is safe because groups_search
does not get to be called. may_setgroups function is only called in uid16.c,
which does not get compiled if CONFIG_NON_ROOT is not set. For the rest of
the functions defined in groups.c, all the places where they are called
select NON_ROOT (hope I did not miss anything).

I think we can do one more thing here - compiling out capability.c entirely.
Waiting for feedback if we should also try to do this in this patch.

The body of the is the following:

There are a lot of embedded systems that run most or all of their functionality
in init, running as root:root. For these systems, supporting multiple users is
not necessary.

This patch adds a new symbol, CONFIG_NON_ROOT, that makes support for non-root
users, non-root groups, and capabilities optional.

When this symbol is not defined, UID and GID are zero in any possible case
and processes always have all capabilities.

The following syscalls are compiled out: setuid, setregid, setgid,
setreuid, setresuid, getresuid, setresgid, getresgid, setgroups, getgroups,
setfsuid, setfsgid, capget, capset.

Also, groups.c is compiled out completely.

This change saves about 26 KB on a defconfig build.

Bloat-o-meter output:
add/remove: 7/86 grow/shrink: 27/428 up/down: 1733/-28111 (-26378)

Signed-off-by: Iulia Manda <iulia....@gmail.com>
---
arch/s390/Kconfig | 1 +
drivers/staging/lustre/lustre/Kconfig | 1 +
fs/nfsd/Kconfig | 1 +
include/linux/capability.h | 12 +++++
include/linux/cred.h | 21 ++++++--
include/linux/uidgid.h | 12 +++++
init/Kconfig | 19 +++++++-
kernel/Makefile | 4 +-
kernel/capability.c | 86 +++++++++++++++++----------------
kernel/cred.c | 8 +++
kernel/sys.c | 2 +
kernel/sys_ni.c | 14 ++++++
12 files changed, 134 insertions(+), 47 deletions(-)

diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 68b68d7..ee7e45d 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -324,6 +324,7 @@ config COMPAT
select COMPAT_BINFMT_ELF if BINFMT_ELF
select ARCH_WANT_OLD_COMPAT_IPC
select COMPAT_OLD_SIGACTION
+ select NON_ROOT
help
Select this option if you want to enable your system kernel to
handle system-calls from ELF binaries for 31 bit ESA. This option
diff --git a/drivers/staging/lustre/lustre/Kconfig b/drivers/staging/lustre/lustre/Kconfig
index 6725467..655f413 100644
--- a/drivers/staging/lustre/lustre/Kconfig
+++ b/drivers/staging/lustre/lustre/Kconfig
@@ -10,6 +10,7 @@ config LUSTRE_FS
select CRYPTO_SHA1
select CRYPTO_SHA256
select CRYPTO_SHA512
+ select NON_ROOT
help
This option enables Lustre file system client support. Choose Y
here if you want to access a Lustre file system cluster. To compile
diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
index 7339515..c8a3b60 100644
--- a/fs/nfsd/Kconfig
+++ b/fs/nfsd/Kconfig
@@ -6,6 +6,7 @@ config NFSD
select SUNRPC
select EXPORTFS
select NFS_ACL_SUPPORT if NFSD_V2_ACL
+ select NON_ROOT
help
Choose Y here if you want to allow other computers to access
files residing on this system using Sun's Network File System
diff --git a/include/linux/capability.h b/include/linux/capability.h
index aa93e5e..d8791d2 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -211,8 +211,20 @@ extern bool has_ns_capability(struct task_struct *t,
extern bool has_capability_noaudit(struct task_struct *t, int cap);
extern bool has_ns_capability_noaudit(struct task_struct *t,
struct user_namespace *ns, int cap);
+#ifdef CONFIG_NON_ROOT
extern bool capable(int cap);
extern bool ns_capable(struct user_namespace *ns, int cap);
+#else
+static inline bool capable(int cap)
+{
+ return true;
+}
+
+static inline bool ns_capable(struct user_namespace *ns, int cap)
+{
+ return true;
+}
+#endif /* CONFIG_NON_ROOT */
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);

diff --git a/include/linux/cred.h b/include/linux/cred.h
index 2fb2ca2..20b1234 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -64,7 +64,25 @@ do { \

extern struct group_info *groups_alloc(int);
extern struct group_info init_groups;
+#ifdef CONFIG_NON_ROOT
extern void groups_free(struct group_info *);
+
+extern int in_group_p(kgid_t);
+extern int in_egroup_p(kgid_t);
+#else
+static inline void groups_free(struct group_info *group_info)
+{
+}
+
+static inline int in_group_p(kgid_t grp)
+{
+ return 0;
+}
+static inline int in_egroup_p(kgid_t grp)
+{
+ return 0;
+}
+#endif
extern int set_current_groups(struct group_info *);
extern void set_groups(struct cred *, struct group_info *);
extern int groups_search(const struct group_info *, kgid_t);
@@ -74,9 +92,6 @@ extern bool may_setgroups(void);
#define GROUP_AT(gi, i) \
((gi)->blocks[(i) / NGROUPS_PER_BLOCK][(i) % NGROUPS_PER_BLOCK])

-extern int in_group_p(kgid_t);
-extern int in_egroup_p(kgid_t);
-
/*
* The security context of a task
*
diff --git a/kernel/Makefile b/kernel/Makefile
index a59481a..d5ca6b8 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -9,7 +9,9 @@ obj-y = fork.o exec_domain.o panic.o \
extable.o params.o \
kthread.o sys_ni.o nsproxy.o \
notifier.o ksysfs.o cred.o reboot.o \
- async.o range.o groups.o smpboot.o
+ async.o range.o smpboot.o
+
+obj-$(CONFIG_NON_ROOT) += groups.o

ifdef CONFIG_FUNCTION_TRACER
# Do not trace debug files and internal ftrace files
diff --git a/kernel/capability.c b/kernel/capability.c
index 989f5bf..17edd5b 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -35,6 +35,7 @@ static int __init file_caps_disable(char *str)
}
__setup("no_file_caps", file_caps_disable);

+#ifdef CONFIG_NON_ROOT
/*
* More recent versions of libcap are available from:
*
@@ -281,6 +282,49 @@ error:
}

/**
+ * ns_capable - Determine if the current task has a superior capability in effect
+ * @ns: The usernamespace we want the capability in
+ * @cap: The capability to be tested for
+ *
+ * Return true if the current task has the given superior capability currently
+ * available for use, false if not.
+ *
+ * This sets PF_SUPERPRIV on the task if the capability is available on the
+ * assumption that it's about to be used.
+ */
+bool ns_capable(struct user_namespace *ns, int cap)
+{
+ if (unlikely(!cap_valid(cap))) {
+ pr_crit("capable() called with invalid cap=%u\n", cap);
+ BUG();
+ }
+
+ if (security_capable(current_cred(), ns, cap) == 0) {
+ current->flags |= PF_SUPERPRIV;
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL(ns_capable);
+
+/**
+ * capable - Determine if the current task has a superior capability in effect
+ * @cap: The capability to be tested for
+ *
+ * Return true if the current task has the given superior capability currently
+ * available for use, false if not.
+ *
+ * This sets PF_SUPERPRIV on the task if the capability is available on the
+ * assumption that it's about to be used.
+ */
+bool capable(int cap)
+{
+ return ns_capable(&init_user_ns, cap);
+}
+EXPORT_SYMBOL(capable);
+#endif
+
+/**
* has_ns_capability - Does a task have a capability in a specific user ns
* @t: The task in question
* @ns: target user namespace
@@ -361,32 +405,6 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
}

/**
- * ns_capable - Determine if the current task has a superior capability in effect
- * @ns: The usernamespace we want the capability in
- * @cap: The capability to be tested for
- *
- * Return true if the current task has the given superior capability currently
- * available for use, false if not.
- *
- * This sets PF_SUPERPRIV on the task if the capability is available on the
- * assumption that it's about to be used.
- */
-bool ns_capable(struct user_namespace *ns, int cap)
-{
- if (unlikely(!cap_valid(cap))) {
- pr_crit("capable() called with invalid cap=%u\n", cap);
- BUG();
- }
-
- if (security_capable(current_cred(), ns, cap) == 0) {
- current->flags |= PF_SUPERPRIV;
- return true;
- }
- return false;
-}
-EXPORT_SYMBOL(ns_capable);
-
-/**
* file_ns_capable - Determine if the file's opener had a capability in effect
* @file: The file we want to check
* @ns: The usernamespace we want the capability in
@@ -412,22 +430,6 @@ bool file_ns_capable(const struct file *file, struct user_namespace *ns,
EXPORT_SYMBOL(file_ns_capable);

/**
- * capable - Determine if the current task has a superior capability in effect
- * @cap: The capability to be tested for
- *
- * Return true if the current task has the given superior capability currently
- * available for use, false if not.
- *
- * This sets PF_SUPERPRIV on the task if the capability is available on the
- * assumption that it's about to be used.
- */
-bool capable(int cap)
-{
- return ns_capable(&init_user_ns, cap);
-}
-EXPORT_SYMBOL(capable);
-
-/**
* capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
* @inode: The inode in question
* @cap: The capability in question
diff --git a/kernel/cred.c b/kernel/cred.c
index e0573a4..e30c579 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -29,6 +29,14 @@

static struct kmem_cache *cred_jar;

+#if !defined(CONFIG_NON_ROOT)
+struct group_info init_groups;
+struct group_info *groups_alloc(int gidsetsize)
+{
+ return NULL;
+}
+#endif
+
/*
* The initial credentials for the initial task
*/

Josh Triplett

unread,
Jan 22, 2015, 12:25:21 PM1/22/15
to Iulia Manda, opw-k...@googlegroups.com
On Thu, Jan 22, 2015 at 04:26:23PM +0200, Iulia Manda wrote:
> This patch comes as a response to the discussions that took place after
> I sent this patch upstream. It compiles out groups.c and also tries to
> regroup the code in order to have all the functions in kernel/capability.c
> that are to be conditionally compiled under a singe ifdef.

Nice! Comments below.

> Note that struct init_groups and groups_alloc need to be defined somewhere
> else (in another source file). If we make them static inline stubs in header,
> other files which define these structs as extern and don't include linux/cred.h
> will not be able to see their declaration and will fail with an undefined
> reference compile error.

Ideally, init_groups could go away, and its one reference as the
initializer of that field (wrapping the initializer in a macro), and
that field entirely, but I'm not sure how much additional trouble it'd
be to eliminate that field and all references to it. I'd suggest
leaving that one alone for now.

> Also, in security/keys.c, key_task_permission is safe because groups_search
> does not get to be called. may_setgroups function is only called in uid16.c,
> which does not get compiled if CONFIG_NON_ROOT is not set. For the rest of
> the functions defined in groups.c, all the places where they are called
> select NON_ROOT (hope I did not miss anything).

Looks like you even caught the in-kernel NFS server. Nicely done!

> I think we can do one more thing here - compiling out capability.c entirely.
> Waiting for feedback if we should also try to do this in this patch.

I'd suggest leaving that one for now, unless it turns out to be trivial.
Let's see how this much goes over.

> The body of the is the following:
>
> There are a lot of embedded systems that run most or all of their functionality
> in init, running as root:root. For these systems, supporting multiple users is
> not necessary.
>
> This patch adds a new symbol, CONFIG_NON_ROOT, that makes support for non-root
> users, non-root groups, and capabilities optional.
>
> When this symbol is not defined, UID and GID are zero in any possible case
> and processes always have all capabilities.
>
> The following syscalls are compiled out: setuid, setregid, setgid,
> setreuid, setresuid, getresuid, setresgid, getresgid, setgroups, getgroups,
> setfsuid, setfsgid, capget, capset.
>
> Also, groups.c is compiled out completely.

Very impressive!

> This change saves about 26 KB on a defconfig build.

Wow, it just keeps getting better.

> Bloat-o-meter output:
> add/remove: 7/86 grow/shrink: 27/428 up/down: 1733/-28111 (-26378)
>
> Signed-off-by: Iulia Manda <iulia....@gmail.com>

A few more comments below.

> arch/s390/Kconfig | 1 +
> drivers/staging/lustre/lustre/Kconfig | 1 +
> fs/nfsd/Kconfig | 1 +
> include/linux/capability.h | 12 +++++
> include/linux/cred.h | 21 ++++++--
> include/linux/uidgid.h | 12 +++++
> init/Kconfig | 19 +++++++-
> kernel/Makefile | 4 +-
> kernel/capability.c | 86 +++++++++++++++++----------------
> kernel/cred.c | 8 +++
> kernel/sys.c | 2 +
> kernel/sys_ni.c | 14 ++++++
> 12 files changed, 134 insertions(+), 47 deletions(-)
>
> diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
> index 68b68d7..ee7e45d 100644
> --- a/arch/s390/Kconfig
> +++ b/arch/s390/Kconfig
> @@ -324,6 +324,7 @@ config COMPAT
> select COMPAT_BINFMT_ELF if BINFMT_ELF
> select ARCH_WANT_OLD_COMPAT_IPC
> select COMPAT_OLD_SIGACTION
> + select NON_ROOT

There's an indentation problem here and on all the other "select
NON_ROOT" lines: this should use a tab to match the surrounding
indentation, rather than spaces.
I think these stubs should return 1 (true), actually. You always have
any given group permission, rather than never having permission.
Rather than moving these symbols around, would it make sense to compile
out has_ns_capability and similar as well? That way you eliminate a set
of adjacent functions...
> +struct group_info init_group;

jo...@joshtriplett.org

unread,
Jan 22, 2015, 12:51:37 PM1/22/15
to Iulia Manda, opw-k...@googlegroups.com
On Thu, Jan 22, 2015 at 09:25:17AM -0800, Josh Triplett wrote:
> On Thu, Jan 22, 2015 at 04:26:23PM +0200, Iulia Manda wrote:
> > This patch comes as a response to the discussions that took place after
> > I sent this patch upstream. It compiles out groups.c and also tries to
> > regroup the code in order to have all the functions in kernel/capability.c
> > that are to be conditionally compiled under a singe ifdef.
>
> Nice! Comments below.

A couple of items I missed in my first mail:

> > --- a/kernel/cred.c
> > +++ b/kernel/cred.c
> > @@ -29,6 +29,14 @@
> >
> > static struct kmem_cache *cred_jar;
> >
> > +#if !defined(CONFIG_NON_ROOT)
> > +struct group_info init_group;

init_groups, not init_group

Also, I think you still want the initializer for this. So, perhaps it
would make sense to unconditionally move the definition and initializer
here, not just for CONFIG_NON_ROOT.

> > +struct group_info *groups_alloc(int gidsetsize)
> > +{
> > + return NULL;
> > +}

I understand why init_group can't go in a header; however, it seems like
this stub could go in a header file. What goes wrong if you put it in
cred.h?

- Josh Triplett

Iulia Manda

unread,
Jan 25, 2015, 2:30:21 PM1/25/15
to jo...@joshtriplett.org, opw-k...@googlegroups.com
Compiling out has_ns_capability and similar adds 4 more stubs in the header.
So if you think this is adding too much complexity to this patch, I can return
to the previous version where we were only compiling out capable and
ns_capable.

Also, if the groups' allocation does not succeed, -ENOMEM is returned
(see, for example net/sunrpc/svcauth_unix.c, in function unix_gid_parse).
Ending up with an "Out of memory" error code does not sound like something we
would like to do. This is why, in every place where this function is called
needs to select CONFIG_NON_ROOT. Please correct me if I am wrong with this.

The decrease in the difference of size that you may observe is because in the
previous patch CONFIG_SUNRPC was enabled (my bad, it is actually not enabled
by default in defconfig); groups_alloc was stubbed out, so the difference
comes from constant folding that in the current version does not happen for
the functions in sunrpc that were using groups_alloc.

The body of the patch below:

There are a lot of embedded systems that run most or all of their functionality
in init, running as root:root. For these systems, supporting multiple users is
not necessary.

This patch adds a new symbol, CONFIG_NON_ROOT, that makes support for non-root
users, non-root groups, and capabilities optional.

When this symbol is not defined, UID and GID are zero in any possible case
and processes always have all capabilities.

The following syscalls are compiled out: setuid, setregid, setgid,
setreuid, setresuid, getresuid, setresgid, getresgid, setgroups, getgroups,
setfsuid, setfsgid, capget, capset.

Also, groups.c is compiled out completely.

This change saves about 25 KB on a defconfig build.

The kernel was booted in Qemu. All the common functionalities work. Adding
users/groups is not possible, failing with -ENOSYS.

Bloat-o-meter output:
add/remove: 7/87 grow/shrink: 19/397 up/down: 1675/-26325 (-24650)

Signed-off-by: Iulia Manda <iulia....@gmail.com>
---
Changes since v3:
- refactor code and solve coding style issues;
- give any group persmissions;
- if groups_alloc is called, enable NON_ROOT;
- move init_groups definition and initializer in cred.c

arch/s390/Kconfig | 1 +
drivers/staging/lustre/lustre/Kconfig | 1 +
fs/nfsd/Kconfig | 1 +
include/linux/capability.h | 29 +++++++++++++++++++++++++++
include/linux/cred.h | 23 ++++++++++++++++++----
include/linux/uidgid.h | 12 +++++++++++
init/Kconfig | 19 +++++++++++++++++-
kernel/Makefile | 4 +++-
kernel/capability.c | 35 ++++++++++++++++++---------------
kernel/cred.c | 3 +++
kernel/groups.c | 3 ---
kernel/sys.c | 2 ++
kernel/sys_ni.c | 14 +++++++++++++
net/sunrpc/Kconfig | 2 ++
14 files changed, 124 insertions(+), 25 deletions(-)

diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 68b68d7..b2d2116 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -324,6 +324,7 @@ config COMPAT
select COMPAT_BINFMT_ELF if BINFMT_ELF
select ARCH_WANT_OLD_COMPAT_IPC
select COMPAT_OLD_SIGACTION
+ select NON_ROOT
help
Select this option if you want to enable your system kernel to
handle system-calls from ELF binaries for 31 bit ESA. This option
diff --git a/drivers/staging/lustre/lustre/Kconfig b/drivers/staging/lustre/lustre/Kconfig
index 6725467..b975f62 100644
--- a/drivers/staging/lustre/lustre/Kconfig
+++ b/drivers/staging/lustre/lustre/Kconfig
@@ -10,6 +10,7 @@ config LUSTRE_FS
select CRYPTO_SHA1
select CRYPTO_SHA256
select CRYPTO_SHA512
+ select NON_ROOT
help
This option enables Lustre file system client support. Choose Y
here if you want to access a Lustre file system cluster. To compile
diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
index 7339515..1a8d6d9 100644
--- a/fs/nfsd/Kconfig
+++ b/fs/nfsd/Kconfig
@@ -6,6 +6,7 @@ config NFSD
select SUNRPC
select EXPORTFS
select NFS_ACL_SUPPORT if NFSD_V2_ACL
+ select NON_ROOT
help
Choose Y here if you want to allow other computers to access
files residing on this system using Sun's Network File System
diff --git a/include/linux/capability.h b/include/linux/capability.h
index aa93e5e..601c5de 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -205,6 +205,7 @@ static inline kernel_cap_t cap_raise_nfsd_set(const kernel_cap_t a,
cap_intersect(permitted, __cap_nfsd_set));
}

+#ifdef CONFIG_NON_ROOT
extern bool has_capability(struct task_struct *t, int cap);
extern bool has_ns_capability(struct task_struct *t,
struct user_namespace *ns, int cap);
@@ -213,6 +214,34 @@ extern bool has_ns_capability_noaudit(struct task_struct *t,
struct user_namespace *ns, int cap);
extern bool capable(int cap);
extern bool ns_capable(struct user_namespace *ns, int cap);
+#else
+static inline bool has_capability(struct task_struct *t, int cap)
+{
+ return true;
+}
+static inline bool has_ns_capability(struct task_struct *t,
+ struct user_namespace *ns, int cap)
+{
+ return true;
+}
+static inline bool has_capability_noaudit(struct task_struct *t, int cap)
+{
+ return true;
+}
+static inline bool has_ns_capability_noaudit(struct task_struct *t,
+ struct user_namespace *ns, int cap)
+{
+ return true;
+}
+static inline bool capable(int cap)
+{
+ return true;
+}
+static inline bool ns_capable(struct user_namespace *ns, int cap)
+{
+ return true;
+}
+#endif /* CONFIG_NON_ROOT */
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);

diff --git a/include/linux/cred.h b/include/linux/cred.h
index 2fb2ca2..08ea5c6 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -62,9 +62,27 @@ do { \
groups_free(group_info); \
} while (0)

-extern struct group_info *groups_alloc(int);
extern struct group_info init_groups;
+#ifdef CONFIG_NON_ROOT
+extern struct group_info *groups_alloc(int);
extern void groups_free(struct group_info *);
+
+extern int in_group_p(kgid_t);
+extern int in_egroup_p(kgid_t);
+#else
+static inline void groups_free(struct group_info *group_info)
+{
+}
+
+static inline int in_group_p(kgid_t grp)
+{
+ return 1;
+}
+static inline int in_egroup_p(kgid_t grp)
+{
+ return 1;
+}
index 989f5bf..2638412 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -35,6 +35,7 @@ static int __init file_caps_disable(char *str)
}
__setup("no_file_caps", file_caps_disable);

+#ifdef CONFIG_NON_ROOT
/*
* More recent versions of libcap are available from:
*
@@ -386,6 +387,24 @@ bool ns_capable(struct user_namespace *ns, int cap)
}
EXPORT_SYMBOL(ns_capable);

+
+/**
+ * capable - Determine if the current task has a superior capability in effect
+ * @cap: The capability to be tested for
+ *
+ * Return true if the current task has the given superior capability currently
+ * available for use, false if not.
+ *
+ * This sets PF_SUPERPRIV on the task if the capability is available on the
+ * assumption that it's about to be used.
+ */
+bool capable(int cap)
+{
+ return ns_capable(&init_user_ns, cap);
+}
+EXPORT_SYMBOL(capable);
+#endif /* CONFIG_NON_ROOT */
+
/**
* file_ns_capable - Determine if the file's opener had a capability in effect
* @file: The file we want to check
@@ -412,22 +431,6 @@ bool file_ns_capable(const struct file *file, struct user_namespace *ns,
EXPORT_SYMBOL(file_ns_capable);

/**
- * capable - Determine if the current task has a superior capability in effect
- * @cap: The capability to be tested for
- *
- * Return true if the current task has the given superior capability currently
- * available for use, false if not.
- *
- * This sets PF_SUPERPRIV on the task if the capability is available on the
- * assumption that it's about to be used.
- */
-bool capable(int cap)
-{
- return ns_capable(&init_user_ns, cap);
-}
-EXPORT_SYMBOL(capable);
-
-/**
* capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
* @inode: The inode in question
* @cap: The capability in question
diff --git a/kernel/cred.c b/kernel/cred.c
index e0573a4..ec1c076 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -29,6 +29,9 @@

static struct kmem_cache *cred_jar;

+/* init to 2 - one for init_task, one to ensure it is never freed */
+struct group_info init_groups = { .usage = ATOMIC_INIT(2) };
+
/*
* The initial credentials for the initial task
*/
diff --git a/kernel/groups.c b/kernel/groups.c
index 664411f..74d431d 100644
--- a/kernel/groups.c
+++ b/kernel/groups.c
@@ -9,9 +9,6 @@
#include <linux/user_namespace.h>
#include <asm/uaccess.h>

-/* init to 2 - one for init_task, one to ensure it is never freed */
-struct group_info init_groups = { .usage = ATOMIC_INIT(2) };
-
struct group_info *groups_alloc(int gidsetsize)
{
struct group_info *group_info;
diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig
index fb78117..2b2c471 100644
--- a/net/sunrpc/Kconfig
+++ b/net/sunrpc/Kconfig
@@ -1,9 +1,11 @@
config SUNRPC
tristate
+ select NON_ROOT

config SUNRPC_GSS
tristate
select OID_REGISTRY
+ select NON_ROOT

config SUNRPC_BACKCHANNEL
bool
--
1.7.10.4

Josh Triplett

unread,
Jan 29, 2015, 12:01:46 PM1/29/15
to Iulia Manda, opw-k...@googlegroups.com
On Sun, Jan 25, 2015 at 09:30:00PM +0200, Iulia Manda wrote:
> Compiling out has_ns_capability and similar adds 4 more stubs in the header.
> So if you think this is adding too much complexity to this patch, I can return
> to the previous version where we were only compiling out capable and
> ns_capable.

I think adding the extra stubs makes sense, and it simplifies the
changes to capability.c

> Also, if the groups' allocation does not succeed, -ENOMEM is returned
> (see, for example net/sunrpc/svcauth_unix.c, in function unix_gid_parse).
> Ending up with an "Out of memory" error code does not sound like something we
> would like to do. This is why, in every place where this function is called
> needs to select CONFIG_NON_ROOT. Please correct me if I am wrong with this.

That seems sensible; let's keep the patch conservative for now,
selecting NON_ROOT in more circumstances. We can always try to
make CONFIG_NON_ROOT=n work with things using groups_alloc later.

> The decrease in the difference of size that you may observe is because in the
> previous patch CONFIG_SUNRPC was enabled (my bad, it is actually not enabled
> by default in defconfig); groups_alloc was stubbed out, so the difference
> comes from constant folding that in the current version does not happen for
> the functions in sunrpc that were using groups_alloc.

Fair enough.

> The body of the patch below:
>
> There are a lot of embedded systems that run most or all of their functionality
> in init, running as root:root. For these systems, supporting multiple users is
> not necessary.
>
> This patch adds a new symbol, CONFIG_NON_ROOT, that makes support for non-root
> users, non-root groups, and capabilities optional.
>
> When this symbol is not defined, UID and GID are zero in any possible case
> and processes always have all capabilities.
>
> The following syscalls are compiled out: setuid, setregid, setgid,
> setreuid, setresuid, getresuid, setresgid, getresgid, setgroups, getgroups,
> setfsuid, setfsgid, capget, capset.
>
> Also, groups.c is compiled out completely.
>
> This change saves about 25 KB on a defconfig build.
>
> The kernel was booted in Qemu. All the common functionalities work. Adding
> users/groups is not possible, failing with -ENOSYS.
>
> Bloat-o-meter output:
> add/remove: 7/87 grow/shrink: 19/397 up/down: 1675/-26325 (-24650)
>
> Signed-off-by: Iulia Manda <iulia....@gmail.com>
> ---
> Changes since v3:

For the public version, this should say "Changes since v1", and the
patch should be v2.

> - refactor code and solve coding style issues;
> - give any group persmissions;

"permissions"

> - if groups_alloc is called, enable NON_ROOT;
> - move init_groups definition and initializer in cred.c

This looks good to me. Go ahead and send it to the upstream thread.
Reply all
Reply to author
Forward
0 new messages