Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

security: add trace event for cap_capable

In cases where we want a stable way to observe/trace
cap_capable (e.g. protection from inlining and API updates)
add a tracepoint that passes:
- The credentials used
- The user namespace of the resource being accessed
- The user namespace in which the credential provides the
capability to access the targeted resource
- The capability to check for
- The return value of the check

Signed-off-by: Jordan Rome <linux@jordanrome.com>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Reviewed-by: Paul Moore <paul@paul-moore.com>
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Link: https://lore.kernel.org/r/20241204155911.1817092-1-linux@jordanrome.com
Signed-off-by: Serge Hallyn <sergeh@kernel.org>

authored by

Jordan Rome and committed by
Serge Hallyn
d48da4d5 3f4f1f8a

+99 -13
+1
MAINTAINERS
··· 5147 5147 L: linux-security-module@vger.kernel.org 5148 5148 S: Supported 5149 5149 F: include/linux/capability.h 5150 + F: include/trace/events/capability.h 5150 5151 F: include/uapi/linux/capability.h 5151 5152 F: kernel/capability.c 5152 5153 F: security/commoncap.c
+57
include/trace/events/capability.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #undef TRACE_SYSTEM 3 + #define TRACE_SYSTEM capability 4 + 5 + #if !defined(_TRACE_CAPABILITY_H) || defined(TRACE_HEADER_MULTI_READ) 6 + #define _TRACE_CAPABILITY_H 7 + 8 + #include <linux/cred.h> 9 + #include <linux/tracepoint.h> 10 + #include <linux/user_namespace.h> 11 + 12 + /** 13 + * cap_capable - called after it's determined if a task has a particular 14 + * effective capability 15 + * 16 + * @cred: The credentials used 17 + * @target_ns: The user namespace of the resource being accessed 18 + * @capable_ns: The user namespace in which the credential provides the 19 + * capability to access the targeted resource. 20 + * This will be NULL if ret is not 0. 21 + * @cap: The capability to check for 22 + * @ret: The return value of the check: 0 if it does, -ve if it does not 23 + * 24 + * Allows to trace calls to cap_capable in commoncap.c 25 + */ 26 + TRACE_EVENT(cap_capable, 27 + 28 + TP_PROTO(const struct cred *cred, struct user_namespace *target_ns, 29 + const struct user_namespace *capable_ns, int cap, int ret), 30 + 31 + TP_ARGS(cred, target_ns, capable_ns, cap, ret), 32 + 33 + TP_STRUCT__entry( 34 + __field(const struct cred *, cred) 35 + __field(struct user_namespace *, target_ns) 36 + __field(const struct user_namespace *, capable_ns) 37 + __field(int, cap) 38 + __field(int, ret) 39 + ), 40 + 41 + TP_fast_assign( 42 + __entry->cred = cred; 43 + __entry->target_ns = target_ns; 44 + __entry->capable_ns = ret == 0 ? capable_ns : NULL; 45 + __entry->cap = cap; 46 + __entry->ret = ret; 47 + ), 48 + 49 + TP_printk("cred %p, target_ns %p, capable_ns %p, cap %d, ret %d", 50 + __entry->cred, __entry->target_ns, __entry->capable_ns, __entry->cap, 51 + __entry->ret) 52 + ); 53 + 54 + #endif /* _TRACE_CAPABILITY_H */ 55 + 56 + /* This part must be outside protection */ 57 + #include <trace/define_trace.h>
+41 -13
security/commoncap.c
··· 27 27 #include <linux/mnt_idmapping.h> 28 28 #include <uapi/linux/lsm.h> 29 29 30 + #define CREATE_TRACE_POINTS 31 + #include <trace/events/capability.h> 32 + 30 33 /* 31 34 * If a non-root user executes a setuid-root binary in 32 35 * !secure(SECURE_NOROOT) mode, then we raise capabilities. ··· 53 50 } 54 51 55 52 /** 56 - * cap_capable - Determine whether a task has a particular effective capability 53 + * cap_capable_helper - Determine whether a task has a particular effective 54 + * capability. 57 55 * @cred: The credentials to use 58 - * @targ_ns: The user namespace in which we need the capability 56 + * @target_ns: The user namespace of the resource being accessed 57 + * @cred_ns: The user namespace of the credentials 59 58 * @cap: The capability to check for 60 - * @opts: Bitmask of options defined in include/linux/security.h 61 59 * 62 60 * Determine whether the nominated task has the specified capability amongst 63 61 * its effective set, returning 0 if it does, -ve if it does not. 64 62 * 65 - * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable() 66 - * and has_capability() functions. That is, it has the reverse semantics: 67 - * cap_has_capability() returns 0 when a task has a capability, but the 68 - * kernel's capable() and has_capability() returns 1 for this case. 63 + * See cap_capable for more details. 69 64 */ 70 - int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, 71 - int cap, unsigned int opts) 65 + static inline int cap_capable_helper(const struct cred *cred, 66 + struct user_namespace *target_ns, 67 + const struct user_namespace *cred_ns, 68 + int cap) 72 69 { 73 - struct user_namespace *ns = targ_ns; 70 + struct user_namespace *ns = target_ns; 74 71 75 72 /* See if cred has the capability in the target user namespace 76 73 * by examining the target user namespace and all of the target ··· 78 75 */ 79 76 for (;;) { 80 77 /* Do we have the necessary capabilities? */ 81 - if (ns == cred->user_ns) 78 + if (likely(ns == cred_ns)) 82 79 return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; 83 80 84 81 /* 85 82 * If we're already at a lower level than we're looking for, 86 83 * we're done searching. 87 84 */ 88 - if (ns->level <= cred->user_ns->level) 85 + if (ns->level <= cred_ns->level) 89 86 return -EPERM; 90 87 91 88 /* 92 89 * The owner of the user namespace in the parent of the 93 90 * user namespace has all caps. 94 91 */ 95 - if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid)) 92 + if ((ns->parent == cred_ns) && uid_eq(ns->owner, cred->euid)) 96 93 return 0; 97 94 98 95 /* ··· 103 100 } 104 101 105 102 /* We never get here */ 103 + } 104 + 105 + /** 106 + * cap_capable - Determine whether a task has a particular effective capability 107 + * @cred: The credentials to use 108 + * @target_ns: The user namespace of the resource being accessed 109 + * @cap: The capability to check for 110 + * @opts: Bitmask of options defined in include/linux/security.h (unused) 111 + * 112 + * Determine whether the nominated task has the specified capability amongst 113 + * its effective set, returning 0 if it does, -ve if it does not. 114 + * 115 + * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable() 116 + * and has_capability() functions. That is, it has the reverse semantics: 117 + * cap_has_capability() returns 0 when a task has a capability, but the 118 + * kernel's capable() and has_capability() returns 1 for this case. 119 + */ 120 + int cap_capable(const struct cred *cred, struct user_namespace *target_ns, 121 + int cap, unsigned int opts) 122 + { 123 + const struct user_namespace *cred_ns = cred->user_ns; 124 + int ret = cap_capable_helper(cred, target_ns, cred_ns, cap); 125 + 126 + trace_cap_capable(cred, target_ns, cred_ns, cap, ret); 127 + return ret; 106 128 } 107 129 108 130 /**