On Mon, May 02, 2022 at 06:00:30PM +0200, Christian Göttsche wrote: > Add the interface `capable_or()` as an alternative to or multiple How about 'capable_contains_oneof(x, y)' or 'capable_of_one(a, b)'? > `capable()` calls, like `capable_or(CAP_SYS_NICE, CAP_SYS_ADMIN)` > instead of `capable(CAP_SYS_NICE) || capable(CAP_SYS_ADMIN)`. > `capable_or()` will in particular generate exactly one audit message, > either for the left most capability in effect or, if the task has none, > the first one. > This is especially helpful with regard to SELinux, where each audit > message about a not allowed capability will create an avc denial. > Using this function with the least invasive capability as left most > argument (e.g. CAP_SYS_NICE before CAP_SYS_ADMIN) enables policy writers > to only allow the least invasive one and SELinux domains pass this check > with only capability:sys_nice or capability:sys_admin allowed without > any avc denial message. > > Signed-off-by: Christian Göttsche <cgzones@xxxxxxxxxxxxxx> > > --- > v2: > avoid varargs and fix to two capabilities; capable_or3() can be added > later if needed > --- > include/linux/capability.h | 5 +++++ > kernel/capability.c | 29 +++++++++++++++++++++++++++++ > 2 files changed, 34 insertions(+) > > diff --git a/include/linux/capability.h b/include/linux/capability.h > index 65efb74c3585..a16d1edea9b3 100644 > --- a/include/linux/capability.h > +++ b/include/linux/capability.h > @@ -207,6 +207,7 @@ 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); > +extern bool capable_or(int cap1, int cap2); > extern bool capable(int cap); > extern bool ns_capable(struct user_namespace *ns, int cap); > extern bool ns_capable_noaudit(struct user_namespace *ns, int cap); > @@ -230,6 +231,10 @@ static inline bool has_ns_capability_noaudit(struct task_struct *t, > { > return true; > } > +static inline bool capable_or(int cap1, int cap2) > +{ > + return true; > +} > static inline bool capable(int cap) > { > return true; > diff --git a/kernel/capability.c b/kernel/capability.c > index 765194f5d678..cd8f3efe6d08 100644 > --- a/kernel/capability.c > +++ b/kernel/capability.c > @@ -435,6 +435,35 @@ bool ns_capable_setid(struct user_namespace *ns, int cap) > } > EXPORT_SYMBOL(ns_capable_setid); > > +/** > + * capable_or - Determine if the current task has one of two superior capabilities in effect > + * @cap1: The capabilities to be tested for first > + * @cap2: The capabilities to be tested for secondly > + * > + * Return true if the current task has at one of the two given superior s/has at one/has at least one/ ? > + * capabilities currently available for use, false if not. > + * > + * In contrast to or'ing capable() this call will create exactly one audit > + * message, either for @cap1, if it is granted or both are not permitted, > + * or @cap2, if it is granted while the other one is not. > + * > + * The capabilities should be ordered from least to most invasive, i.e. CAP_SYS_ADMIN last. > + * > + * This sets PF_SUPERPRIV on the task if the capability is available on the > + * assumption that it's about to be used. > + */ > +bool capable_or(int cap1, int cap2) > +{ > + if (ns_capable_noaudit(&init_user_ns, cap1)) > + return ns_capable(&init_user_ns, cap1); > + > + if (ns_capable_noaudit(&init_user_ns, cap2)) > + return ns_capable(&init_user_ns, cap2); > + > + return ns_capable(&init_user_ns, cap1); Hm, too bad about the repetition of work, but I guess it has to be this way right now. > +} > +EXPORT_SYMBOL(capable_or); > + > /** > * capable - Determine if the current task has a superior capability in effect > * @cap: The capability to be tested for > -- > 2.36.0