* Re: [PATCH v5 0/5] Audit: Records for multiple security contexts
From: Casey Schaufler @ 2025-08-16 17:27 UTC (permalink / raw)
To: paul, eparis, linux-security-module, audit
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, linux-kernel, selinux
In-Reply-To: <20250816164140.6045-1-casey@schaufler-ca.com>
Opps. script error. Please disregard.
On 8/16/2025 9:41 AM, Casey Schaufler wrote:
> The Linux audit system includes LSM based security "context" information
> in its events. Historically, only one LSM that uses security contexts can
> be active on a system. One of the few obsticles to allowing multiple LSM
> support is the inability to report more than one security context in an
> audit event. This patchset provides a mechanism to provide supplimental
> records containing more than one security context for subjects and
> objects.
>
> The mechanism for reporting multiple security contexts inspired
> considerable discussion. It would have been possible to add multiple
> contexts to existing records using sophisticated formatting. This would
> have significant backward compatibility issues, and require additional
> parsing in user space code. Adding new records for an event that contain
> the contexts is more in keeping with the way audit events have been
> constructed in the past.
>
> Only audit events associated with system calls have required multiple
> records prior to this. Mechanism has been added allowing any event
> to be composed of multiple records. This should make it easier to
> add information to existing audit events without breaking backward
> compatability.
>
> v5:
> Comment on the LSM_ID_UNDEF behavior in security_secid_to_secctx().
> Change some names to better reflect their purpose.
> Move alignment changes into a separate patch.
> v4:
> Use LSM_ID_UNDEF when checking for valid LSM IDs in
> security_lsmprop_to_secctx().
> Fix the object record to include only those for LSMs that use them.
> Squash the two patches dealing with subject contexts.
> Base the patches on Paul Moore's LSM initialization patchset.
> https://lore.kernel.org/all/20250409185019.238841-31-paul@paul-moore.com/
> v3:
> Rework how security modules identify that they provide security
> contexts to the audit system. Maintain a list within the audit
> system of the security modules that provide security contexts.
> Revert the separate counts of subject and object contexts.
> v2:
> Maintain separate counts for LSMs using subject contexts and object
> contexts. AppArmor uses the former but not the latter.
> Correct error handling in object record creation.
>
> https://github.com/cschaufler/lsm-stacking#audit-6.16-rc4-v5
>
> Casey Schaufler (5):
> Audit: Create audit_stamp structure
> LSM: security_lsmblob_to_secctx module selection
> Audit: Add record for multiple task security contexts
> Audit: Fix indentation in audit_log_exit
> Audit: Add record for multiple object contexts
>
> include/linux/audit.h | 23 +++
> include/linux/security.h | 6 +-
> include/uapi/linux/audit.h | 2 +
> kernel/audit.c | 274 ++++++++++++++++++++++++++++++-----
> kernel/audit.h | 13 +-
> kernel/auditsc.c | 65 +++------
> net/netlabel/netlabel_user.c | 8 +-
> security/apparmor/lsm.c | 3 +
> security/lsm.h | 4 -
> security/lsm_init.c | 5 -
> security/security.c | 21 ++-
> security/selinux/hooks.c | 5 +
> security/smack/smack_lsm.c | 5 +
> 13 files changed, 325 insertions(+), 109 deletions(-)
>
^ permalink raw reply
* [PATCH v6 4/4] Audit: Add record for multiple object contexts
From: Casey Schaufler @ 2025-08-16 17:28 UTC (permalink / raw)
To: casey, paul, eparis, linux-security-module, audit
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, linux-kernel, selinux
In-Reply-To: <20250816172859.6437-1-casey@schaufler-ca.com>
Create a new audit record AUDIT_MAC_OBJ_CONTEXTS.
An example of the MAC_OBJ_CONTEXTS record is:
type=MAC_OBJ_CONTEXTS
msg=audit(1601152467.009:1050):
obj_selinux=unconfined_u:object_r:user_home_t:s0
When an audit event includes a AUDIT_MAC_OBJ_CONTEXTS record
the "obj=" field in other records in the event will be "obj=?".
An AUDIT_MAC_OBJ_CONTEXTS record is supplied when the system has
multiple security modules that may make access decisions based
on an object security context.
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
include/linux/audit.h | 7 +++++
include/uapi/linux/audit.h | 1 +
kernel/audit.c | 58 +++++++++++++++++++++++++++++++++++++-
kernel/auditsc.c | 38 +++++--------------------
security/selinux/hooks.c | 4 ++-
security/smack/smack_lsm.c | 4 ++-
6 files changed, 78 insertions(+), 34 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 38e5edffe371..150d34716f85 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -151,6 +151,7 @@ extern unsigned compat_signal_class[];
/* bit values for audit_cfg_lsm */
#define AUDIT_CFG_LSM_SECCTX_SUBJECT BIT(0)
+#define AUDIT_CFG_LSM_SECCTX_OBJECT BIT(1)
struct filename;
@@ -191,6 +192,7 @@ extern void audit_log_path_denied(int type,
extern void audit_log_lost(const char *message);
extern int audit_log_subj_ctx(struct audit_buffer *ab, struct lsm_prop *prop);
+extern int audit_log_obj_ctx(struct audit_buffer *ab, struct lsm_prop *prop);
extern int audit_log_task_context(struct audit_buffer *ab);
extern void audit_log_task_info(struct audit_buffer *ab);
@@ -258,6 +260,11 @@ static inline int audit_log_subj_ctx(struct audit_buffer *ab,
{
return 0;
}
+static inline int audit_log_obj_ctx(struct audit_buffer *ab,
+ struct lsm_prop *prop)
+{
+ return 0;
+}
static inline int audit_log_task_context(struct audit_buffer *ab)
{
return 0;
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 8cad2f307719..14a1c1fe013a 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -149,6 +149,7 @@
#define AUDIT_LANDLOCK_ACCESS 1423 /* Landlock denial */
#define AUDIT_LANDLOCK_DOMAIN 1424 /* Landlock domain status */
#define AUDIT_MAC_TASK_CONTEXTS 1425 /* Multiple LSM task contexts */
+#define AUDIT_MAC_OBJ_CONTEXTS 1426 /* Multiple LSM objext contexts */
#define AUDIT_FIRST_KERN_ANOM_MSG 1700
#define AUDIT_LAST_KERN_ANOM_MSG 1799
diff --git a/kernel/audit.c b/kernel/audit.c
index c924b30f2524..bd7474fd8d2c 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -85,7 +85,9 @@ static unsigned int audit_net_id;
/* Number of modules that provide a security context.
List of lsms that provide a security context */
static u32 audit_subj_secctx_cnt;
+static u32 audit_obj_secctx_cnt;
static const struct lsm_id *audit_subj_lsms[MAX_LSM_COUNT];
+static const struct lsm_id *audit_obj_lsms[MAX_LSM_COUNT];
/**
* struct audit_net - audit private network namespace data
@@ -305,6 +307,12 @@ void audit_cfg_lsm(const struct lsm_id *lsmid, int flags)
return;
audit_subj_lsms[audit_subj_secctx_cnt++] = lsmid;
}
+ if (flags & AUDIT_CFG_LSM_SECCTX_OBJECT) {
+ for (i = 0 ; i < audit_obj_secctx_cnt; i++)
+ if (audit_obj_lsms[i] == lsmid)
+ return;
+ audit_obj_lsms[audit_obj_secctx_cnt++] = lsmid;
+ }
}
/**
@@ -1142,7 +1150,6 @@ static int is_audit_feature_set(int i)
return af.features & AUDIT_FEATURE_TO_MASK(i);
}
-
static int audit_get_feature(struct sk_buff *skb)
{
u32 seq;
@@ -2337,6 +2344,55 @@ int audit_log_task_context(struct audit_buffer *ab)
}
EXPORT_SYMBOL(audit_log_task_context);
+int audit_log_obj_ctx(struct audit_buffer *ab, struct lsm_prop *prop)
+{
+ int i;
+ int rc;
+ int error = 0;
+ char *space = "";
+ struct lsm_context ctx;
+
+ if (audit_obj_secctx_cnt < 2) {
+ error = security_lsmprop_to_secctx(prop, &ctx, LSM_ID_UNDEF);
+ if (error < 0) {
+ if (error != -EINVAL)
+ goto error_path;
+ return error;
+ }
+ audit_log_format(ab, " obj=%s", ctx.context);
+ security_release_secctx(&ctx);
+ return 0;
+ }
+ audit_log_format(ab, " obj=?");
+ error = audit_buffer_aux_new(ab, AUDIT_MAC_OBJ_CONTEXTS);
+ if (error)
+ goto error_path;
+
+ for (i = 0; i < audit_obj_secctx_cnt; i++) {
+ rc = security_lsmprop_to_secctx(prop, &ctx,
+ audit_obj_lsms[i]->id);
+ if (rc < 0) {
+ audit_log_format(ab, "%sobj_%s=?", space,
+ audit_obj_lsms[i]->name);
+ if (rc != -EINVAL)
+ audit_panic("error in audit_log_obj_ctx");
+ error = rc;
+ } else {
+ audit_log_format(ab, "%sobj_%s=%s", space,
+ audit_obj_lsms[i]->name, ctx.context);
+ security_release_secctx(&ctx);
+ }
+ space = " ";
+ }
+
+ audit_buffer_aux_end(ab);
+ return error;
+
+error_path:
+ audit_panic("error in audit_log_obj_ctx");
+ return error;
+}
+
void audit_log_d_path_exe(struct audit_buffer *ab,
struct mm_struct *mm)
{
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 03f33da8d02e..006273c323dd 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1098,7 +1098,6 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid,
char *comm)
{
struct audit_buffer *ab;
- struct lsm_context ctx;
int rc = 0;
ab = audit_log_start(context, GFP_KERNEL, AUDIT_OBJ_PID);
@@ -1108,15 +1107,9 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid,
audit_log_format(ab, "opid=%d oauid=%d ouid=%d oses=%d", pid,
from_kuid(&init_user_ns, auid),
from_kuid(&init_user_ns, uid), sessionid);
- if (lsmprop_is_set(prop)) {
- if (security_lsmprop_to_secctx(prop, &ctx, LSM_ID_UNDEF) < 0) {
- audit_log_format(ab, " obj=(none)");
- rc = 1;
- } else {
- audit_log_format(ab, " obj=%s", ctx.context);
- security_release_secctx(&ctx);
- }
- }
+ if (lsmprop_is_set(prop) && audit_log_obj_ctx(ab, prop))
+ rc = 1;
+
audit_log_format(ab, " ocomm=");
audit_log_untrustedstring(ab, comm);
audit_log_end(ab);
@@ -1392,16 +1385,8 @@ static void show_special(struct audit_context *context, int *call_panic)
from_kgid(&init_user_ns, context->ipc.gid),
context->ipc.mode);
if (lsmprop_is_set(&context->ipc.oprop)) {
- struct lsm_context lsmctx;
-
- if (security_lsmprop_to_secctx(&context->ipc.oprop,
- &lsmctx,
- LSM_ID_UNDEF) < 0) {
+ if (audit_log_obj_ctx(ab, &context->ipc.oprop))
*call_panic = 1;
- } else {
- audit_log_format(ab, " obj=%s", lsmctx.context);
- security_release_secctx(&lsmctx);
- }
}
if (context->ipc.has_perm) {
audit_log_end(ab);
@@ -1558,18 +1543,9 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n,
from_kgid(&init_user_ns, n->gid),
MAJOR(n->rdev),
MINOR(n->rdev));
- if (lsmprop_is_set(&n->oprop)) {
- struct lsm_context ctx;
-
- if (security_lsmprop_to_secctx(&n->oprop, &ctx,
- LSM_ID_UNDEF) < 0) {
- if (call_panic)
- *call_panic = 2;
- } else {
- audit_log_format(ab, " obj=%s", ctx.context);
- security_release_secctx(&ctx);
- }
- }
+ if (lsmprop_is_set(&n->oprop) &&
+ audit_log_obj_ctx(ab, &n->oprop))
+ *call_panic = 2;
/* log the audit_names record type */
switch (n->type) {
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 975b84b466b4..3999f58a1842 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7619,7 +7619,9 @@ static __init int selinux_init(void)
cred_init_security();
/* Inform the audit system that secctx is used */
- audit_cfg_lsm(&selinux_lsmid, AUDIT_CFG_LSM_SECCTX_SUBJECT);
+ audit_cfg_lsm(&selinux_lsmid,
+ AUDIT_CFG_LSM_SECCTX_SUBJECT |
+ AUDIT_CFG_LSM_SECCTX_OBJECT);
default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
if (!default_noexec)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index eaff9b8901a7..fdf2f193a291 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -5268,7 +5268,9 @@ static __init int smack_init(void)
init_smack_known_list();
/* Inform the audit system that secctx is used */
- audit_cfg_lsm(&smack_lsmid, AUDIT_CFG_LSM_SECCTX_SUBJECT);
+ audit_cfg_lsm(&smack_lsmid,
+ AUDIT_CFG_LSM_SECCTX_SUBJECT |
+ AUDIT_CFG_LSM_SECCTX_OBJECT);
return 0;
}
--
2.50.1
^ permalink raw reply related
* [PATCH v6 0/4] Audit: Records for multiple security contexts
From: Casey Schaufler @ 2025-08-16 17:28 UTC (permalink / raw)
To: casey, paul, eparis, linux-security-module, audit
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, linux-kernel, selinux
In-Reply-To: <20250816172859.6437-1-casey.ref@schaufler-ca.com>
The Linux audit system includes LSM based security "context" information
in its events. Historically, only one LSM that uses security contexts can
be active on a system. One of the few obsticles to allowing multiple LSM
support is the inability to report more than one security context in an
audit event. This patchset provides a mechanism to provide supplimental
records containing more than one security context for subjects and
objects.
The mechanism for reporting multiple security contexts inspired
considerable discussion. It would have been possible to add multiple
contexts to existing records using sophisticated formatting. This would
have significant backward compatibility issues, and require additional
parsing in user space code. Adding new records for an event that contain
the contexts is more in keeping with the way audit events have been
constructed in the past.
Only audit events associated with system calls have required multiple
records prior to this. Mechanism has been added allowing any event
to be composed of multiple records. This should make it easier to
add information to existing audit events without breaking backward
compatability.
v6:
Rebase on Linus' tree.
Drop the alignment change patch.
Finish changes from review.
v5:
Comment on the LSM_ID_UNDEF behavior in security_secid_to_secctx().
Change some names to better reflect their purpose.
Move alignment changes into a separate patch.
v4:
Use LSM_ID_UNDEF when checking for valid LSM IDs in
security_lsmprop_to_secctx().
Fix the object record to include only those for LSMs that use them.
Squash the two patches dealing with subject contexts.
Base the patches on Paul Moore's LSM initialization patchset.
https://lore.kernel.org/all/20250409185019.238841-31-paul@paul-moore.com/
v3:
Rework how security modules identify that they provide security
contexts to the audit system. Maintain a list within the audit
system of the security modules that provide security contexts.
Revert the separate counts of subject and object contexts.
v2:
Maintain separate counts for LSMs using subject contexts and object
contexts. AppArmor uses the former but not the latter.
Correct error handling in object record creation.
https://github.com/cschaufler/lsm-stacking#audit-6.17-rc1
Casey Schaufler (4):
Audit: Create audit_stamp structure
LSM: security_lsmblob_to_secctx module selection
Audit: Add record for multiple task security contexts
Audit: Add record for multiple object contexts
include/linux/audit.h | 23 +++
include/linux/security.h | 6 +-
include/uapi/linux/audit.h | 2 +
kernel/audit.c | 275 ++++++++++++++++++++++++++++++-----
kernel/audit.h | 13 +-
kernel/auditsc.c | 58 ++------
net/netlabel/netlabel_user.c | 8 +-
security/apparmor/lsm.c | 3 +
security/security.c | 18 ++-
security/selinux/hooks.c | 5 +
security/smack/smack_lsm.c | 5 +
11 files changed, 321 insertions(+), 95 deletions(-)
--
2.50.1
^ permalink raw reply
* [PATCH v6 1/4] Audit: Create audit_stamp structure
From: Casey Schaufler @ 2025-08-16 17:28 UTC (permalink / raw)
To: casey, paul, eparis, linux-security-module, audit
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, linux-kernel, selinux
In-Reply-To: <20250816172859.6437-1-casey@schaufler-ca.com>
Replace the timestamp and serial number pair used in audit records
with a structure containing the two elements.
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
kernel/audit.c | 17 +++++++++--------
kernel/audit.h | 13 +++++++++----
kernel/auditsc.c | 22 +++++++++-------------
3 files changed, 27 insertions(+), 25 deletions(-)
diff --git a/kernel/audit.c b/kernel/audit.c
index 61b5744d0bb6..547967cb4266 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1833,11 +1833,11 @@ unsigned int audit_serial(void)
}
static inline void audit_get_stamp(struct audit_context *ctx,
- struct timespec64 *t, unsigned int *serial)
+ struct audit_stamp *stamp)
{
- if (!ctx || !auditsc_get_stamp(ctx, t, serial)) {
- ktime_get_coarse_real_ts64(t);
- *serial = audit_serial();
+ if (!ctx || !auditsc_get_stamp(ctx, stamp)) {
+ ktime_get_coarse_real_ts64(&stamp->ctime);
+ stamp->serial = audit_serial();
}
}
@@ -1860,8 +1860,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
int type)
{
struct audit_buffer *ab;
- struct timespec64 t;
- unsigned int serial;
+ struct audit_stamp stamp;
if (audit_initialized != AUDIT_INITIALIZED)
return NULL;
@@ -1916,12 +1915,14 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
return NULL;
}
- audit_get_stamp(ab->ctx, &t, &serial);
+ audit_get_stamp(ab->ctx, &stamp);
/* cancel dummy context to enable supporting records */
if (ctx)
ctx->dummy = 0;
audit_log_format(ab, "audit(%llu.%03lu:%u): ",
- (unsigned long long)t.tv_sec, t.tv_nsec/1000000, serial);
+ (unsigned long long)stamp.ctime.tv_sec,
+ stamp.ctime.tv_nsec/1000000,
+ stamp.serial);
return ab;
}
diff --git a/kernel/audit.h b/kernel/audit.h
index 2a24d01c5fb0..0f05933a173b 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -99,6 +99,12 @@ struct audit_proctitle {
char *value; /* the cmdline field */
};
+/* A timestamp/serial pair to identify an event */
+struct audit_stamp {
+ struct timespec64 ctime; /* time of syscall entry */
+ unsigned int serial; /* serial number for record */
+};
+
/* The per-task audit context. */
struct audit_context {
int dummy; /* must be the first element */
@@ -108,10 +114,9 @@ struct audit_context {
AUDIT_CTX_URING, /* in use by io_uring */
} context;
enum audit_state state, current_state;
- unsigned int serial; /* serial number for record */
+ struct audit_stamp stamp; /* event identifier */
int major; /* syscall number */
int uring_op; /* uring operation */
- struct timespec64 ctime; /* time of syscall entry */
unsigned long argv[4]; /* syscall arguments */
long return_code;/* syscall return code */
u64 prio;
@@ -263,7 +268,7 @@ extern void audit_put_tty(struct tty_struct *tty);
extern unsigned int audit_serial(void);
#ifdef CONFIG_AUDITSYSCALL
extern int auditsc_get_stamp(struct audit_context *ctx,
- struct timespec64 *t, unsigned int *serial);
+ struct audit_stamp *stamp);
extern void audit_put_watch(struct audit_watch *watch);
extern void audit_get_watch(struct audit_watch *watch);
@@ -304,7 +309,7 @@ extern void audit_filter_inodes(struct task_struct *tsk,
struct audit_context *ctx);
extern struct list_head *audit_killed_trees(void);
#else /* CONFIG_AUDITSYSCALL */
-#define auditsc_get_stamp(c, t, s) 0
+#define auditsc_get_stamp(c, s) 0
#define audit_put_watch(w) do { } while (0)
#define audit_get_watch(w) do { } while (0)
#define audit_to_watch(k, p, l, o) (-EINVAL)
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index eb98cd6fe91f..aa6add4b9e30 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -994,10 +994,10 @@ static void audit_reset_context(struct audit_context *ctx)
*/
ctx->current_state = ctx->state;
- ctx->serial = 0;
+ ctx->stamp.serial = 0;
+ ctx->stamp.ctime = (struct timespec64){ .tv_sec = 0, .tv_nsec = 0 };
ctx->major = 0;
ctx->uring_op = 0;
- ctx->ctime = (struct timespec64){ .tv_sec = 0, .tv_nsec = 0 };
memset(ctx->argv, 0, sizeof(ctx->argv));
ctx->return_code = 0;
ctx->prio = (ctx->state == AUDIT_STATE_RECORD ? ~0ULL : 0);
@@ -1917,7 +1917,7 @@ void __audit_uring_entry(u8 op)
ctx->context = AUDIT_CTX_URING;
ctx->current_state = ctx->state;
- ktime_get_coarse_real_ts64(&ctx->ctime);
+ ktime_get_coarse_real_ts64(&ctx->stamp.ctime);
}
/**
@@ -2039,7 +2039,7 @@ void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2,
context->argv[3] = a4;
context->context = AUDIT_CTX_SYSCALL;
context->current_state = state;
- ktime_get_coarse_real_ts64(&context->ctime);
+ ktime_get_coarse_real_ts64(&context->stamp.ctime);
}
/**
@@ -2508,21 +2508,17 @@ EXPORT_SYMBOL_GPL(__audit_inode_child);
/**
* auditsc_get_stamp - get local copies of audit_context values
* @ctx: audit_context for the task
- * @t: timespec64 to store time recorded in the audit_context
- * @serial: serial value that is recorded in the audit_context
+ * @stamp: timestamp to record
*
* Also sets the context as auditable.
*/
-int auditsc_get_stamp(struct audit_context *ctx,
- struct timespec64 *t, unsigned int *serial)
+int auditsc_get_stamp(struct audit_context *ctx, struct audit_stamp *stamp)
{
if (ctx->context == AUDIT_CTX_UNUSED)
return 0;
- if (!ctx->serial)
- ctx->serial = audit_serial();
- t->tv_sec = ctx->ctime.tv_sec;
- t->tv_nsec = ctx->ctime.tv_nsec;
- *serial = ctx->serial;
+ if (!ctx->stamp.serial)
+ ctx->stamp.serial = audit_serial();
+ *stamp = ctx->stamp;
if (!ctx->prio) {
ctx->prio = 1;
ctx->current_state = AUDIT_STATE_RECORD;
--
2.50.1
^ permalink raw reply related
* [PATCH v6 2/4] LSM: security_lsmblob_to_secctx module selection
From: Casey Schaufler @ 2025-08-16 17:28 UTC (permalink / raw)
To: casey, paul, eparis, linux-security-module, audit
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, linux-kernel, selinux
In-Reply-To: <20250816172859.6437-1-casey@schaufler-ca.com>
Add a parameter lsmid to security_lsmblob_to_secctx() to identify which
of the security modules that may be active should provide the security
context. If the value of lsmid is LSM_ID_UNDEF the first LSM providing
a hook is used. security_secid_to_secctx() is unchanged, and will
always report the first LSM providing a hook.
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
include/linux/security.h | 6 ++++--
kernel/audit.c | 4 ++--
kernel/auditsc.c | 8 +++++---
net/netlabel/netlabel_user.c | 3 ++-
security/security.c | 18 ++++++++++++++++--
5 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/include/linux/security.h b/include/linux/security.h
index 521bcb5b9717..6d1ed6e7387b 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -567,7 +567,8 @@ int security_getprocattr(struct task_struct *p, int lsmid, const char *name,
int security_setprocattr(int lsmid, const char *name, void *value, size_t size);
int security_ismaclabel(const char *name);
int security_secid_to_secctx(u32 secid, struct lsm_context *cp);
-int security_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp);
+int security_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp,
+ int lsmid);
int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
void security_release_secctx(struct lsm_context *cp);
void security_inode_invalidate_secctx(struct inode *inode);
@@ -1551,7 +1552,8 @@ static inline int security_secid_to_secctx(u32 secid, struct lsm_context *cp)
}
static inline int security_lsmprop_to_secctx(struct lsm_prop *prop,
- struct lsm_context *cp)
+ struct lsm_context *cp,
+ int lsmid)
{
return -EOPNOTSUPP;
}
diff --git a/kernel/audit.c b/kernel/audit.c
index 547967cb4266..226c8ae00d04 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1473,7 +1473,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
case AUDIT_SIGNAL_INFO:
if (lsmprop_is_set(&audit_sig_lsm)) {
err = security_lsmprop_to_secctx(&audit_sig_lsm,
- &lsmctx);
+ &lsmctx, LSM_ID_UNDEF);
if (err < 0)
return err;
}
@@ -2188,7 +2188,7 @@ int audit_log_task_context(struct audit_buffer *ab)
if (!lsmprop_is_set(&prop))
return 0;
- error = security_lsmprop_to_secctx(&prop, &ctx);
+ error = security_lsmprop_to_secctx(&prop, &ctx, LSM_ID_UNDEF);
if (error < 0) {
if (error != -EINVAL)
goto error_path;
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index aa6add4b9e30..03f33da8d02e 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1109,7 +1109,7 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid,
from_kuid(&init_user_ns, auid),
from_kuid(&init_user_ns, uid), sessionid);
if (lsmprop_is_set(prop)) {
- if (security_lsmprop_to_secctx(prop, &ctx) < 0) {
+ if (security_lsmprop_to_secctx(prop, &ctx, LSM_ID_UNDEF) < 0) {
audit_log_format(ab, " obj=(none)");
rc = 1;
} else {
@@ -1395,7 +1395,8 @@ static void show_special(struct audit_context *context, int *call_panic)
struct lsm_context lsmctx;
if (security_lsmprop_to_secctx(&context->ipc.oprop,
- &lsmctx) < 0) {
+ &lsmctx,
+ LSM_ID_UNDEF) < 0) {
*call_panic = 1;
} else {
audit_log_format(ab, " obj=%s", lsmctx.context);
@@ -1560,7 +1561,8 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n,
if (lsmprop_is_set(&n->oprop)) {
struct lsm_context ctx;
- if (security_lsmprop_to_secctx(&n->oprop, &ctx) < 0) {
+ if (security_lsmprop_to_secctx(&n->oprop, &ctx,
+ LSM_ID_UNDEF) < 0) {
if (call_panic)
*call_panic = 2;
} else {
diff --git a/net/netlabel/netlabel_user.c b/net/netlabel/netlabel_user.c
index 0d04d23aafe7..6d6545297ee3 100644
--- a/net/netlabel/netlabel_user.c
+++ b/net/netlabel/netlabel_user.c
@@ -98,7 +98,8 @@ struct audit_buffer *netlbl_audit_start_common(int type,
audit_info->sessionid);
if (lsmprop_is_set(&audit_info->prop) &&
- security_lsmprop_to_secctx(&audit_info->prop, &ctx) > 0) {
+ security_lsmprop_to_secctx(&audit_info->prop, &ctx,
+ LSM_ID_UNDEF) > 0) {
audit_log_format(audit_buf, " subj=%s", ctx.context);
security_release_secctx(&ctx);
}
diff --git a/security/security.c b/security/security.c
index ad163f06bf7a..dd588f548a2b 100644
--- a/security/security.c
+++ b/security/security.c
@@ -4342,17 +4342,31 @@ EXPORT_SYMBOL(security_secid_to_secctx);
* security_lsmprop_to_secctx() - Convert a lsm_prop to a secctx
* @prop: lsm specific information
* @cp: the LSM context
+ * @lsmid: which security module to report
*
* Convert a @prop entry to security context. If @cp is NULL the
* length of the result will be returned. This does mean that the
* length could change between calls to check the length and the
* next call which actually allocates and returns the @cp.
*
+ * @lsmid identifies which LSM should supply the context.
+ * A value of LSM_ID_UNDEF indicates that the first LSM suppling
+ * the hook should be used. This is used in cases where the
+ * ID of the supplying LSM is unambiguous.
+ *
* Return: Return length of data on success, error on failure.
*/
-int security_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp)
+int security_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp,
+ int lsmid)
{
- return call_int_hook(lsmprop_to_secctx, prop, cp);
+ struct lsm_static_call *scall;
+
+ lsm_for_each_hook(scall, lsmprop_to_secctx) {
+ if (lsmid != LSM_ID_UNDEF && lsmid != scall->hl->lsmid->id)
+ continue;
+ return scall->hl->hook.lsmprop_to_secctx(prop, cp);
+ }
+ return LSM_RET_DEFAULT(lsmprop_to_secctx);
}
EXPORT_SYMBOL(security_lsmprop_to_secctx);
--
2.50.1
^ permalink raw reply related
* [PATCH v6 3/4] Audit: Add record for multiple task security contexts
From: Casey Schaufler @ 2025-08-16 17:28 UTC (permalink / raw)
To: casey, paul, eparis, linux-security-module, audit
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, linux-kernel, selinux
In-Reply-To: <20250816172859.6437-1-casey@schaufler-ca.com>
Replace the single skb pointer in an audit_buffer with a list of
skb pointers. Add the audit_stamp information to the audit_buffer as
there's no guarantee that there will be an audit_context containing
the stamp associated with the event. At audit_log_end() time create
auxiliary records as have been added to the list. Functions are
created to manage the skb list in the audit_buffer.
Create a new audit record AUDIT_MAC_TASK_CONTEXTS.
An example of the MAC_TASK_CONTEXTS record is:
type=MAC_TASK_CONTEXTS
msg=audit(1600880931.832:113)
subj_apparmor=unconfined
subj_smack=_
When an audit event includes a AUDIT_MAC_TASK_CONTEXTS record the
"subj=" field in other records in the event will be "subj=?".
An AUDIT_MAC_TASK_CONTEXTS record is supplied when the system has
multiple security modules that may make access decisions based on a
subject security context.
Refactor audit_log_task_context(), creating a new audit_log_subj_ctx().
This is used in netlabel auditing to provide multiple subject security
contexts as necessary.
Suggested-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
include/linux/audit.h | 16 +++
include/uapi/linux/audit.h | 1 +
kernel/audit.c | 208 +++++++++++++++++++++++++++++------
net/netlabel/netlabel_user.c | 9 +-
security/apparmor/lsm.c | 3 +
security/selinux/hooks.c | 3 +
security/smack/smack_lsm.c | 3 +
7 files changed, 202 insertions(+), 41 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index a394614ccd0b..38e5edffe371 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -37,6 +37,8 @@ struct audit_watch;
struct audit_tree;
struct sk_buff;
struct kern_ipc_perm;
+struct lsm_id;
+struct lsm_prop;
struct audit_krule {
u32 pflags;
@@ -147,6 +149,9 @@ extern unsigned compat_signal_class[];
#define AUDIT_TTY_ENABLE BIT(0)
#define AUDIT_TTY_LOG_PASSWD BIT(1)
+/* bit values for audit_cfg_lsm */
+#define AUDIT_CFG_LSM_SECCTX_SUBJECT BIT(0)
+
struct filename;
#define AUDIT_OFF 0
@@ -185,6 +190,7 @@ extern void audit_log_path_denied(int type,
const char *operation);
extern void audit_log_lost(const char *message);
+extern int audit_log_subj_ctx(struct audit_buffer *ab, struct lsm_prop *prop);
extern int audit_log_task_context(struct audit_buffer *ab);
extern void audit_log_task_info(struct audit_buffer *ab);
@@ -210,6 +216,8 @@ extern u32 audit_enabled;
extern int audit_signal_info(int sig, struct task_struct *t);
+extern void audit_cfg_lsm(const struct lsm_id *lsmid, int flags);
+
#else /* CONFIG_AUDIT */
static inline __printf(4, 5)
void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type,
@@ -245,6 +253,11 @@ static inline void audit_log_key(struct audit_buffer *ab, char *key)
{ }
static inline void audit_log_path_denied(int type, const char *operation)
{ }
+static inline int audit_log_subj_ctx(struct audit_buffer *ab,
+ struct lsm_prop *prop)
+{
+ return 0;
+}
static inline int audit_log_task_context(struct audit_buffer *ab)
{
return 0;
@@ -269,6 +282,9 @@ static inline int audit_signal_info(int sig, struct task_struct *t)
return 0;
}
+static inline void audit_cfg_lsm(const struct lsm_id *lsmid, int flags)
+{ }
+
#endif /* CONFIG_AUDIT */
#ifdef CONFIG_AUDIT_COMPAT_GENERIC
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 9a4ecc9f6dc5..8cad2f307719 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -148,6 +148,7 @@
#define AUDIT_IPE_POLICY_LOAD 1422 /* IPE policy load */
#define AUDIT_LANDLOCK_ACCESS 1423 /* Landlock denial */
#define AUDIT_LANDLOCK_DOMAIN 1424 /* Landlock domain status */
+#define AUDIT_MAC_TASK_CONTEXTS 1425 /* Multiple LSM task contexts */
#define AUDIT_FIRST_KERN_ANOM_MSG 1700
#define AUDIT_LAST_KERN_ANOM_MSG 1799
diff --git a/kernel/audit.c b/kernel/audit.c
index 226c8ae00d04..c924b30f2524 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -54,6 +54,7 @@
#include <net/netlink.h>
#include <linux/skbuff.h>
#include <linux/security.h>
+#include <linux/lsm_hooks.h>
#include <linux/freezer.h>
#include <linux/pid_namespace.h>
#include <net/netns/generic.h>
@@ -81,6 +82,11 @@ static u32 audit_failure = AUDIT_FAIL_PRINTK;
/* private audit network namespace index */
static unsigned int audit_net_id;
+/* Number of modules that provide a security context.
+ List of lsms that provide a security context */
+static u32 audit_subj_secctx_cnt;
+static const struct lsm_id *audit_subj_lsms[MAX_LSM_COUNT];
+
/**
* struct audit_net - audit private network namespace data
* @sk: communication socket
@@ -195,8 +201,10 @@ static struct audit_ctl_mutex {
* to place it on a transmit queue. Multiple audit_buffers can be in
* use simultaneously. */
struct audit_buffer {
- struct sk_buff *skb; /* formatted skb ready to send */
+ struct sk_buff *skb; /* the skb for audit_log functions */
+ struct sk_buff_head skb_list; /* formatted skbs, ready to send */
struct audit_context *ctx; /* NULL or associated context */
+ struct audit_stamp stamp; /* audit stamp for these records */
gfp_t gfp_mask;
};
@@ -278,6 +286,27 @@ static pid_t auditd_pid_vnr(void)
return pid;
}
+/**
+ * audit_cfg_lsm - Identify a security module as providing a secctx.
+ * @lsmid: LSM identity
+ * @flags: which contexts are provided
+ *
+ * Description:
+ * Increments the count of the security modules providing a secctx.
+ * If the LSM id is already in the list leave it alone.
+ */
+void audit_cfg_lsm(const struct lsm_id *lsmid, int flags)
+{
+ int i;
+
+ if (flags & AUDIT_CFG_LSM_SECCTX_SUBJECT) {
+ for (i = 0 ; i < audit_subj_secctx_cnt; i++)
+ if (audit_subj_lsms[i] == lsmid)
+ return;
+ audit_subj_lsms[audit_subj_secctx_cnt++] = lsmid;
+ }
+}
+
/**
* audit_get_sk - Return the audit socket for the given network namespace
* @net: the destination network namespace
@@ -1776,10 +1805,13 @@ __setup("audit_backlog_limit=", audit_backlog_limit_set);
static void audit_buffer_free(struct audit_buffer *ab)
{
+ struct sk_buff *skb;
+
if (!ab)
return;
- kfree_skb(ab->skb);
+ while ((skb = skb_dequeue(&ab->skb_list)))
+ kfree_skb(skb);
kmem_cache_free(audit_buffer_cache, ab);
}
@@ -1795,6 +1827,10 @@ static struct audit_buffer *audit_buffer_alloc(struct audit_context *ctx,
ab->skb = nlmsg_new(AUDIT_BUFSIZ, gfp_mask);
if (!ab->skb)
goto err;
+
+ skb_queue_head_init(&ab->skb_list);
+ skb_queue_tail(&ab->skb_list, ab->skb);
+
if (!nlmsg_put(ab->skb, 0, 0, type, 0, 0))
goto err;
@@ -1860,7 +1896,6 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
int type)
{
struct audit_buffer *ab;
- struct audit_stamp stamp;
if (audit_initialized != AUDIT_INITIALIZED)
return NULL;
@@ -1915,14 +1950,14 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
return NULL;
}
- audit_get_stamp(ab->ctx, &stamp);
+ audit_get_stamp(ab->ctx, &ab->stamp);
/* cancel dummy context to enable supporting records */
if (ctx)
ctx->dummy = 0;
audit_log_format(ab, "audit(%llu.%03lu:%u): ",
- (unsigned long long)stamp.ctime.tv_sec,
- stamp.ctime.tv_nsec/1000000,
- stamp.serial);
+ (unsigned long long)ab->stamp.ctime.tv_sec,
+ ab->stamp.ctime.tv_nsec/1000000,
+ ab->stamp.serial);
return ab;
}
@@ -2178,31 +2213,128 @@ void audit_log_key(struct audit_buffer *ab, char *key)
audit_log_format(ab, "(null)");
}
-int audit_log_task_context(struct audit_buffer *ab)
+/**
+ * audit_buffer_aux_new - Add an aux record buffer to the skb list
+ * @ab: audit_buffer
+ * @type: message type
+ *
+ * Aux records are allocated and added to the skb list of
+ * the "main" record. The ab->skb is reset to point to the
+ * aux record on its creation. When the aux record in complete
+ * ab->skb has to be reset to point to the "main" record.
+ * This allows the audit_log_ functions to be ignorant of
+ * which kind of record it is logging to. It also avoids adding
+ * special data for aux records.
+ *
+ * On success ab->skb will point to the new aux record.
+ * Returns 0 on success, -ENOMEM should allocation fail.
+ */
+static int audit_buffer_aux_new(struct audit_buffer *ab, int type)
+{
+ WARN_ON(ab->skb != skb_peek(&ab->skb_list));
+
+ ab->skb = nlmsg_new(AUDIT_BUFSIZ, ab->gfp_mask);
+ if (!ab->skb)
+ goto err;
+ if (!nlmsg_put(ab->skb, 0, 0, type, 0, 0))
+ goto err;
+ skb_queue_tail(&ab->skb_list, ab->skb);
+
+ audit_log_format(ab, "audit(%llu.%03lu:%u): ",
+ (unsigned long long)ab->stamp.ctime.tv_sec,
+ ab->stamp.ctime.tv_nsec/1000000,
+ ab->stamp.serial);
+
+ return 0;
+
+err:
+ kfree_skb(ab->skb);
+ ab->skb = skb_peek(&ab->skb_list);
+ return -ENOMEM;
+}
+
+/**
+ * audit_buffer_aux_end - Switch back to the "main" record from an aux record
+ * @ab: audit_buffer
+ *
+ * Restores the "main" audit record to ab->skb.
+ */
+static void audit_buffer_aux_end(struct audit_buffer *ab)
+{
+ ab->skb = skb_peek(&ab->skb_list);
+}
+
+/**
+ * audit_log_subj_ctx - Add LSM subject information
+ * @ab: audit_buffer
+ * @prop: LSM subject properties.
+ *
+ * Add a subj= field and, if necessary, a AUDIT_MAC_TASK_CONTEXTS record.
+ */
+int audit_log_subj_ctx(struct audit_buffer *ab, struct lsm_prop *prop)
{
- struct lsm_prop prop;
struct lsm_context ctx;
+ char *space = "";
int error;
+ int i;
- security_current_getlsmprop_subj(&prop);
- if (!lsmprop_is_set(&prop))
+ security_current_getlsmprop_subj(prop);
+ if (!lsmprop_is_set(prop))
return 0;
- error = security_lsmprop_to_secctx(&prop, &ctx, LSM_ID_UNDEF);
- if (error < 0) {
- if (error != -EINVAL)
- goto error_path;
+ if (audit_subj_secctx_cnt < 2) {
+ error = security_lsmprop_to_secctx(prop, &ctx, LSM_ID_UNDEF);
+ if (error < 0) {
+ if (error != -EINVAL)
+ goto error_path;
+ return 0;
+ }
+ audit_log_format(ab, " subj=%s", ctx.context);
+ security_release_secctx(&ctx);
return 0;
}
-
- audit_log_format(ab, " subj=%s", ctx.context);
- security_release_secctx(&ctx);
+ /* Multiple LSMs provide contexts. Include an aux record. */
+ audit_log_format(ab, " subj=?");
+ error = audit_buffer_aux_new(ab, AUDIT_MAC_TASK_CONTEXTS);
+ if (error)
+ goto error_path;
+
+ for (i = 0; i < audit_subj_secctx_cnt; i++) {
+ error = security_lsmprop_to_secctx(prop, &ctx,
+ audit_subj_lsms[i]->id);
+ if (error < 0) {
+ /*
+ * Don't print anything. An LSM like BPF could
+ * claim to support contexts, but only do so under
+ * certain conditions.
+ */
+ if (error == -EOPNOTSUPP)
+ continue;
+ if (error != -EINVAL)
+ audit_panic("error in audit_log_subj_ctx");
+ } else {
+ audit_log_format(ab, "%ssubj_%s=%s", space,
+ audit_subj_lsms[i]->name, ctx.context);
+ space = " ";
+ security_release_secctx(&ctx);
+ }
+ }
+ audit_buffer_aux_end(ab);
return 0;
error_path:
- audit_panic("error in audit_log_task_context");
+ audit_panic("error in audit_log_subj_ctx");
return error;
}
+EXPORT_SYMBOL(audit_log_subj_ctx);
+
+int audit_log_task_context(struct audit_buffer *ab)
+{
+ struct lsm_prop prop;
+
+ security_current_getlsmprop_subj(&prop);
+ return audit_log_subj_ctx(ab, &prop);
+}
EXPORT_SYMBOL(audit_log_task_context);
void audit_log_d_path_exe(struct audit_buffer *ab,
@@ -2411,6 +2543,26 @@ int audit_signal_info(int sig, struct task_struct *t)
return audit_signal_info_syscall(t);
}
+/**
+ * __audit_log_end - enqueue one audit record
+ * @skb: the buffer to send
+ */
+static void __audit_log_end(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh;
+
+ if (audit_rate_check()) {
+ /* setup the netlink header, see the comments in
+ * kauditd_send_multicast_skb() for length quirks */
+ nlh = nlmsg_hdr(skb);
+ nlh->nlmsg_len = skb->len - NLMSG_HDRLEN;
+
+ /* queue the netlink packet */
+ skb_queue_tail(&audit_queue, skb);
+ } else
+ audit_log_lost("rate limit exceeded");
+}
+
/**
* audit_log_end - end one audit record
* @ab: the audit_buffer
@@ -2423,25 +2575,15 @@ int audit_signal_info(int sig, struct task_struct *t)
void audit_log_end(struct audit_buffer *ab)
{
struct sk_buff *skb;
- struct nlmsghdr *nlh;
if (!ab)
return;
- if (audit_rate_check()) {
- skb = ab->skb;
- ab->skb = NULL;
+ while ((skb = skb_dequeue(&ab->skb_list)))
+ __audit_log_end(skb);
- /* setup the netlink header, see the comments in
- * kauditd_send_multicast_skb() for length quirks */
- nlh = nlmsg_hdr(skb);
- nlh->nlmsg_len = skb->len - NLMSG_HDRLEN;
-
- /* queue the netlink packet and poke the kauditd thread */
- skb_queue_tail(&audit_queue, skb);
- wake_up_interruptible(&kauditd_wait);
- } else
- audit_log_lost("rate limit exceeded");
+ /* poke the kauditd thread */
+ wake_up_interruptible(&kauditd_wait);
audit_buffer_free(ab);
}
diff --git a/net/netlabel/netlabel_user.c b/net/netlabel/netlabel_user.c
index 6d6545297ee3..0da652844dd6 100644
--- a/net/netlabel/netlabel_user.c
+++ b/net/netlabel/netlabel_user.c
@@ -84,7 +84,6 @@ struct audit_buffer *netlbl_audit_start_common(int type,
struct netlbl_audit *audit_info)
{
struct audit_buffer *audit_buf;
- struct lsm_context ctx;
if (audit_enabled == AUDIT_OFF)
return NULL;
@@ -96,13 +95,7 @@ struct audit_buffer *netlbl_audit_start_common(int type,
audit_log_format(audit_buf, "netlabel: auid=%u ses=%u",
from_kuid(&init_user_ns, audit_info->loginuid),
audit_info->sessionid);
-
- if (lsmprop_is_set(&audit_info->prop) &&
- security_lsmprop_to_secctx(&audit_info->prop, &ctx,
- LSM_ID_UNDEF) > 0) {
- audit_log_format(audit_buf, " subj=%s", ctx.context);
- security_release_secctx(&ctx);
- }
+ audit_log_subj_ctx(audit_buf, &audit_info->prop);
return audit_buf;
}
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 8e1cc229b41b..220d1684b8d4 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -2530,6 +2530,9 @@ static int __init apparmor_init(void)
security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
&apparmor_lsmid);
+ /* Inform the audit system that secctx is used */
+ audit_cfg_lsm(&apparmor_lsmid, AUDIT_CFG_LSM_SECCTX_SUBJECT);
+
/* Report that AppArmor successfully initialized */
apparmor_initialized = 1;
if (aa_g_profile_mode == APPARMOR_COMPLAIN)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index c95a5874bf7d..975b84b466b4 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7618,6 +7618,9 @@ static __init int selinux_init(void)
/* Set the security state for the initial task. */
cred_init_security();
+ /* Inform the audit system that secctx is used */
+ audit_cfg_lsm(&selinux_lsmid, AUDIT_CFG_LSM_SECCTX_SUBJECT);
+
default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
if (!default_noexec)
pr_notice("SELinux: virtual memory is executable by default\n");
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index fc340a6f0dde..eaff9b8901a7 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -5267,6 +5267,9 @@ static __init int smack_init(void)
/* initialize the smack_known_list */
init_smack_known_list();
+ /* Inform the audit system that secctx is used */
+ audit_cfg_lsm(&smack_lsmid, AUDIT_CFG_LSM_SECCTX_SUBJECT);
+
return 0;
}
--
2.50.1
^ permalink raw reply related
* Re: [PATCH v3 11/12] bpftool: Add support for signing BPF programs
From: KP Singh @ 2025-08-17 2:16 UTC (permalink / raw)
To: Blaise Boscaccy
Cc: bpf, linux-security-module, paul, kys, ast, daniel, andrii
In-Reply-To: <87bjohonio.fsf@microsoft.com>
On Thu, Aug 14, 2025 at 6:51 PM Blaise Boscaccy
<bboscaccy@linux.microsoft.com> wrote:
>
> KP Singh <kpsingh@kernel.org> writes:
>
> > Two modes of operation being added:
> >
> > Add two modes of operation:
> >
> > * For prog load, allow signing a program immediately before loading. This
> > is essential for command-line testing and administration.
> >
> > bpftool prog load -S -k <private_key> -i <identity_cert> fentry_test.bpf.o
> >
> > * For gen skeleton, embed a pre-generated signature into the C skeleton
> > file. This supports the use of signed programs in compiled applications.
> >
> > bpftool gen skeleton -S -k <private_key> -i <identity_cert> fentry_test.bpf.o
> >
> > Generation of the loader program and its metadata map is implemented in
> > libbpf (bpf_obj__gen_loader). bpftool generates a skeleton that loads
> > the program and automates the required steps: freezing the map, creating
> > an exclusive map, loading, and running. Users can use standard libbpf
> > APIs directly or integrate loader program generation into their own
> > toolchains.
> >
> > Signed-off-by: KP Singh <kpsingh@kernel.org>
> > ---
> > .../bpf/bpftool/Documentation/bpftool-gen.rst | 16 +-
> > .../bpftool/Documentation/bpftool-prog.rst | 18 +-
> > tools/bpf/bpftool/Makefile | 6 +-
> > tools/bpf/bpftool/cgroup.c | 4 +
> > tools/bpf/bpftool/gen.c | 60 ++++-
> > tools/bpf/bpftool/main.c | 26 ++-
> > tools/bpf/bpftool/main.h | 11 +
> > tools/bpf/bpftool/prog.c | 27 ++-
> > tools/bpf/bpftool/sign.c | 212 ++++++++++++++++++
> > 9 files changed, 367 insertions(+), 13 deletions(-)
> > create mode 100644 tools/bpf/bpftool/sign.c
> >
> > diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> > index ca860fd97d8d..cef469d758ed 100644
> > --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> > +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> > @@ -16,7 +16,8 @@ SYNOPSIS
> >
> > **bpftool** [*OPTIONS*] **gen** *COMMAND*
> >
> > -*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } }
> > +*OPTIONS* := { |COMMON_OPTIONS| [ { **-L** | **--use-loader** } ]
> > +[ { { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> } ] }}
> >
> > *COMMAND* := { **object** | **skeleton** | **help** }
> >
> > @@ -186,6 +187,19 @@ OPTIONS
> > skeleton). A light skeleton contains a loader eBPF program. It does not use
> > the majority of the libbpf infrastructure, and does not need libelf.
> >
> > +-S, --sign
> > + For skeletons, generate a signed skeleton. This option must be used with
> > + **-k** and **-i**. Using this flag implicitly enables **--use-loader**.
> > + See the "Signed Skeletons" section in the description of the
> > + **gen skeleton** command for more details.
> > +
> > +-k <private_key.pem>
> > + Path to the private key file in PEM format, required for signing.
> > +
> > +-i <certificate.x509>
> > + Path to the X.509 certificate file in PEM or DER format, required for
> > + signing.
> > +
> > EXAMPLES
> > ========
> > **$ cat example1.bpf.c**
> > diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
> > index f69fd92df8d8..55b812761df2 100644
> > --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
> > +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
> > @@ -16,9 +16,9 @@ SYNOPSIS
> >
> > **bpftool** [*OPTIONS*] **prog** *COMMAND*
> >
> > -*OPTIONS* := { |COMMON_OPTIONS| |
> > -{ **-f** | **--bpffs** } | { **-m** | **--mapcompat** } | { **-n** | **--nomount** } |
> > -{ **-L** | **--use-loader** } }
> > +*OPTIONS* := { |COMMON_OPTIONS| [ { **-f** | **--bpffs** } ] [ { **-m** | **--mapcompat** } ]
> > +[ { **-n** | **--nomount** } ] [ { **-L** | **--use-loader** } ]
> > +[ { { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> } ] }
> >
> > *COMMANDS* :=
> > { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** |
> > @@ -248,6 +248,18 @@ OPTIONS
> > creating the maps, and loading the programs (see **bpftool prog tracelog**
> > as a way to dump those messages).
> >
> > +-S, --sign
> > + Enable signing of the BPF program before loading. This option must be
> > + used with **-k** and **-i**. Using this flag implicitly enables
> > + **--use-loader**.
> > +
> > +-k <private_key.pem>
> > + Path to the private key file in PEM format, required when signing.
> > +
> > +-i <certificate.x509>
> > + Path to the X.509 certificate file in PEM or DER format, required when
> > + signing.
> > +
> > EXAMPLES
> > ========
> > **# bpftool prog show**
> > diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
> > index 9e9a5f006cd2..586d1b2595d1 100644
> > --- a/tools/bpf/bpftool/Makefile
> > +++ b/tools/bpf/bpftool/Makefile
> > @@ -130,8 +130,8 @@ include $(FEATURES_DUMP)
> > endif
> > endif
> >
> > -LIBS = $(LIBBPF) -lelf -lz
> > -LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz
> > +LIBS = $(LIBBPF) -lelf -lz -lcrypto
> > +LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz -lcrypto
> >
> > ifeq ($(feature-libelf-zstd),1)
> > LIBS += -lzstd
> > @@ -194,7 +194,7 @@ endif
> >
> > BPFTOOL_BOOTSTRAP := $(BOOTSTRAP_OUTPUT)bpftool
> >
> > -BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o)
> > +BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o sign.o)
> > $(BOOTSTRAP_OBJS): $(LIBBPF_BOOTSTRAP)
> >
> > OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
> > diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c
> > index 944ebe21a216..ec356deb27c9 100644
> > --- a/tools/bpf/bpftool/cgroup.c
> > +++ b/tools/bpf/bpftool/cgroup.c
> > @@ -2,6 +2,10 @@
> > // Copyright (C) 2017 Facebook
> > // Author: Roman Gushchin <guro@fb.com>
> >
> > +#undef GCC_VERSION
> > +#ifndef _GNU_SOURCE
> > +#define _GNU_SOURCE
> > +#endif
> > #define _XOPEN_SOURCE 500
> > #include <errno.h>
> > #include <fcntl.h>
> > diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> > index 67a60114368f..427468c9e9c2 100644
> > --- a/tools/bpf/bpftool/gen.c
> > +++ b/tools/bpf/bpftool/gen.c
> > @@ -688,10 +688,17 @@ static void codegen_destroy(struct bpf_object *obj, const char *obj_name)
> > static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *header_guard)
> > {
> > DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
> > + struct bpf_load_and_run_opts sopts = {};
> > + char sig_buf[MAX_SIG_SIZE];
> > + __u8 prog_sha[SHA256_DIGEST_LENGTH];
> > struct bpf_map *map;
> > +
> > char ident[256];
> > int err = 0;
> >
> > + if (sign_progs)
> > + opts.gen_hash = true;
> > +
> > err = bpf_object__gen_loader(obj, &opts);
> > if (err)
> > return err;
> > @@ -701,6 +708,7 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
> > p_err("failed to load object file");
> > goto out;
> > }
> > +
> > /* If there was no error during load then gen_loader_opts
> > * are populated with the loader program.
> > */
> > @@ -780,8 +788,51 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
> > print_hex(opts.insns, opts.insns_sz);
> > codegen("\
> > \n\
> > - \"; \n\
> > - \n\
> > + \";\n");
> > +
> > + if (sign_progs) {
> > + sopts.insns = opts.insns;
> > + sopts.insns_sz = opts.insns_sz;
> > + sopts.excl_prog_hash = prog_sha;
> > + sopts.excl_prog_hash_sz = sizeof(prog_sha);
> > + sopts.signature = sig_buf;
> > + sopts.signature_sz = MAX_SIG_SIZE;
> > + sopts.keyring_id = KEY_SPEC_SESSION_KEYRING;
> > +
>
> This still has the session keyring hardcoded.
We can do this for now:
diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
index 427468c9e9c2..694e61f1909e 100644
--- a/tools/bpf/bpftool/gen.c
+++ b/tools/bpf/bpftool/gen.c
@@ -797,7 +797,6 @@ static int gen_trace(struct bpf_object *obj, const
char *obj_name, const char *h
sopts.excl_prog_hash_sz = sizeof(prog_sha);
sopts.signature = sig_buf;
sopts.signature_sz = MAX_SIG_SIZE;
- sopts.keyring_id = KEY_SPEC_SESSION_KEYRING;
err = bpftool_prog_sign(&sopts);
if (err < 0)
@@ -827,7 +826,7 @@ static int gen_trace(struct bpf_object *obj, const
char *obj_name, const char *h
opts.signature_sz = sizeof(opts_sig) - 1;
\n\
opts.excl_prog_hash = (void *)opts_excl_hash;
\n\
opts.excl_prog_hash_sz =
sizeof(opts_excl_hash) - 1; \n\
- opts.keyring_id = KEY_SPEC_SESSION_KEYRING;
\n\
+ opts.keyring_id = skel->keyring_id;
\n\
");
}
@@ -1406,6 +1405,13 @@ static int do_skeleton(int argc, char **argv)
printf("\t} links;\n");
}
+ if (sign_progs) {
+ codegen("\
+ \n\
+ __s32 keyring_id; \n\
+ ");
+ }
+
if (btf) {
err = codegen_datasecs(obj, obj_name);
if (err)
diff --git a/tools/testing/selftests/bpf/prog_tests/atomics.c
b/tools/testing/selftests/bpf/prog_tests/atomics.c
index 13e101f370a1..92b5f378bfb8 100644
--- a/tools/testing/selftests/bpf/prog_tests/atomics.c
+++ b/tools/testing/selftests/bpf/prog_tests/atomics.c
@@ -165,11 +165,17 @@ static void test_xchg(struct atomics_lskel *skel)
void test_atomics(void)
{
struct atomics_lskel *skel;
+ int err;
- skel = atomics_lskel__open_and_load();
- if (!ASSERT_OK_PTR(skel, "atomics skeleton load"))
+ skel = atomics_lskel__open();
+ if (!ASSERT_OK_PTR(skel, "atomics skeleton open"))
return;
+ skel->keyring_id = KEY_SPEC_SESSION_KEYRING;
+ err = atomics_lskel__load(skel);
+ if (!ASSERT_OK(err, "atomics skeleton load"))
+ goto cleanup;
+
if (skel->data->skip_tests) {
printf("%s:SKIP:no ENABLE_ATOMICS_TESTS (missing Clang
BPF atomics support)",
__func__);
- KP
>
> > + err = bpftool_prog_sign(&sopts);
> > + if (err < 0)
> > + return err;
> > +
> > + codegen("\
> > + \n\
> > + static const char opts_sig[] __attribute__((__aligned__(8))) = \"\\\n\
> > + ");
> > + print_hex((const void *)sig_buf, sopts.signature_sz);
> > + codegen("\
> > + \n\
> > + \";\n");
> > +
> > + codegen("\
> > + \n\
> > + static const char opts_excl_hash[] __attribute__((__aligned__(8))) = \"\\\n\
> > + ");
> > + print_hex((const void *)prog_sha, sizeof(prog_sha));
> > + codegen("\
> > + \n\
> > + \";\n");
> > +
> > + codegen("\
> > + \n\
> > + opts.signature = (void *)opts_sig; \n\
> > + opts.signature_sz = sizeof(opts_sig) - 1; \n\
> > + opts.excl_prog_hash = (void *)opts_excl_hash; \n\
> > + opts.excl_prog_hash_sz = sizeof(opts_excl_hash) - 1; \n\
> > + opts.keyring_id = KEY_SPEC_SESSION_KEYRING; \n\
> > + ");
>
> And here.
>
> > + }
> > +
> > + codegen("\
> > + \n\
> > opts.ctx = (struct bpf_loader_ctx *)skel; \n\
> > opts.data_sz = sizeof(opts_data) - 1; \n\
> > opts.data = (void *)opts_data; \n\
> > @@ -1240,7 +1291,7 @@ static int do_skeleton(int argc, char **argv)
> > err = -errno;
> > libbpf_strerror(err, err_buf, sizeof(err_buf));
> > p_err("failed to open BPF object file: %s", err_buf);
> > - goto out;
> > + goto out_obj;
> > }
> >
> > bpf_object__for_each_map(map, obj) {
> > @@ -1552,6 +1603,7 @@ static int do_skeleton(int argc, char **argv)
> > err = 0;
> > out:
> > bpf_object__close(obj);
> > +out_obj:
> > if (obj_data)
> > munmap(obj_data, mmap_sz);
> > close(fd);
> > @@ -1930,7 +1982,7 @@ static int do_help(int argc, char **argv)
> > " %1$s %2$s help\n"
> > "\n"
> > " " HELP_SPEC_OPTIONS " |\n"
> > - " {-L|--use-loader} }\n"
> > + " {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ]}\n"
> > "",
> > bin_name, "gen");
> >
> > diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
> > index 0f1183b2ed0a..c78eb80b9c94 100644
> > --- a/tools/bpf/bpftool/main.c
> > +++ b/tools/bpf/bpftool/main.c
> > @@ -33,6 +33,9 @@ bool relaxed_maps;
> > bool use_loader;
> > struct btf *base_btf;
> > struct hashmap *refs_table;
> > +bool sign_progs;
> > +const char *private_key_path;
> > +const char *cert_path;
> >
> > static void __noreturn clean_and_exit(int i)
> > {
> > @@ -448,6 +451,7 @@ int main(int argc, char **argv)
> > { "nomount", no_argument, NULL, 'n' },
> > { "debug", no_argument, NULL, 'd' },
> > { "use-loader", no_argument, NULL, 'L' },
> > + { "sign", no_argument, NULL, 'S' },
> > { "base-btf", required_argument, NULL, 'B' },
> > { 0 }
> > };
> > @@ -474,7 +478,7 @@ int main(int argc, char **argv)
> > bin_name = "bpftool";
> >
> > opterr = 0;
> > - while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l",
> > + while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
> > options, NULL)) >= 0) {
> > switch (opt) {
> > case 'V':
> > @@ -520,6 +524,16 @@ int main(int argc, char **argv)
> > case 'L':
> > use_loader = true;
> > break;
> > + case 'S':
> > + sign_progs = true;
> > + use_loader = true;
> > + break;
> > + case 'k':
> > + private_key_path = optarg;
> > + break;
> > + case 'i':
> > + cert_path = optarg;
> > + break;
> > default:
> > p_err("unrecognized option '%s'", argv[optind - 1]);
> > if (json_output)
> > @@ -534,6 +548,16 @@ int main(int argc, char **argv)
> > if (argc < 0)
> > usage();
> >
> > + if (sign_progs && (private_key_path == NULL || cert_path == NULL)) {
> > + p_err("-i <identity_x509_cert> and -k <private> key must be supplied with -S for signing");
> > + return -EINVAL;
> > + }
> > +
> > + if (!sign_progs && (private_key_path != NULL || cert_path != NULL)) {
> > + p_err("-i <identity_x509_cert> and -k <private> also need --sign to be used for sign programs");
> > + return -EINVAL;
> > + }
> > +
> > if (version_requested)
> > ret = do_version(argc, argv);
> > else
> > diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
> > index a2bb0714b3d6..f7f5b39b66c8 100644
> > --- a/tools/bpf/bpftool/main.h
> > +++ b/tools/bpf/bpftool/main.h
> > @@ -6,9 +6,14 @@
> >
> > /* BFD and kernel.h both define GCC_VERSION, differently */
> > #undef GCC_VERSION
> > +#ifndef _GNU_SOURCE
> > +#define _GNU_SOURCE
> > +#endif
> > #include <stdbool.h>
> > #include <stdio.h>
> > +#include <errno.h>
> > #include <stdlib.h>
> > +#include <bpf/skel_internal.h>
> > #include <linux/bpf.h>
> > #include <linux/compiler.h>
> > #include <linux/kernel.h>
> > @@ -52,6 +57,7 @@ static inline void *u64_to_ptr(__u64 ptr)
> > })
> >
> > #define ERR_MAX_LEN 1024
> > +#define MAX_SIG_SIZE 4096
> >
> > #define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
> >
> > @@ -85,6 +91,9 @@ extern bool relaxed_maps;
> > extern bool use_loader;
> > extern struct btf *base_btf;
> > extern struct hashmap *refs_table;
> > +extern bool sign_progs;
> > +extern const char *private_key_path;
> > +extern const char *cert_path;
> >
> > void __printf(1, 2) p_err(const char *fmt, ...);
> > void __printf(1, 2) p_info(const char *fmt, ...);
> > @@ -275,4 +284,6 @@ int pathname_concat(char *buf, int buf_sz, const char *path,
> > /* print netfilter bpf_link info */
> > void netfilter_dump_plain(const struct bpf_link_info *info);
> > void netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr);
> > +int bpftool_prog_sign(struct bpf_load_and_run_opts *opts);
> > +__u32 register_session_key(const char *key_der_path);
> > #endif
> > diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
> > index 9722d841abc0..82b8da084504 100644
> > --- a/tools/bpf/bpftool/prog.c
> > +++ b/tools/bpf/bpftool/prog.c
> > @@ -23,6 +23,7 @@
> > #include <linux/err.h>
> > #include <linux/perf_event.h>
> > #include <linux/sizes.h>
> > +#include <linux/keyctl.h>
> >
> > #include <bpf/bpf.h>
> > #include <bpf/btf.h>
> > @@ -1930,6 +1931,8 @@ static int try_loader(struct gen_loader_opts *gen)
> > {
> > struct bpf_load_and_run_opts opts = {};
> > struct bpf_loader_ctx *ctx;
> > + char sig_buf[MAX_SIG_SIZE];
> > + __u8 prog_sha[SHA256_DIGEST_LENGTH];
> > int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
> > sizeof(struct bpf_prog_desc));
> > int log_buf_sz = (1u << 24) - 1;
> > @@ -1953,6 +1956,24 @@ static int try_loader(struct gen_loader_opts *gen)
> > opts.insns = gen->insns;
> > opts.insns_sz = gen->insns_sz;
> > fds_before = count_open_fds();
> > +
> > + if (sign_progs) {
> > + opts.excl_prog_hash = prog_sha;
> > + opts.excl_prog_hash_sz = sizeof(prog_sha);
> > + opts.signature = sig_buf;
> > + opts.signature_sz = MAX_SIG_SIZE;
> > + opts.keyring_id = KEY_SPEC_SESSION_KEYRING;
> > +
>
> And here as well.
The "load -S" command loads and signs the program in one go, so this
is purely for debugging and not how one would use signing. Session key
is fine here. What we really want is flexibility when using skeletons.
- KP
>
> > + err = bpftool_prog_sign(&opts);
> > + if (err < 0)
> > + return err;
> > +
> > + err = register_session_key(cert_path);
> > + if (err < 0) {
> > + p_err("failed to add session key");
> > + goto out;
> > + }
> > + }
> > err = bpf_load_and_run(&opts);
> > fd_delta = count_open_fds() - fds_before;
> > if (err < 0 || verifier_logs) {
> > @@ -1961,6 +1982,7 @@ static int try_loader(struct gen_loader_opts *gen)
> > fprintf(stderr, "loader prog leaked %d FDs\n",
> > fd_delta);
> > }
> > +out:
> > free(log_buf);
> > return err;
> > }
> > @@ -1988,6 +2010,9 @@ static int do_loader(int argc, char **argv)
> > goto err_close_obj;
> > }
> >
> > + if (sign_progs)
> > + gen.gen_hash = true;
> > +
> > err = bpf_object__gen_loader(obj, &gen);
> > if (err)
> > goto err_close_obj;
> > @@ -2562,7 +2587,7 @@ static int do_help(int argc, char **argv)
> > " METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
> > " " HELP_SPEC_OPTIONS " |\n"
> > " {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
> > - " {-L|--use-loader} }\n"
> > + " {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ] \n"
> > "",
> > bin_name, argv[-2]);
> >
> > diff --git a/tools/bpf/bpftool/sign.c b/tools/bpf/bpftool/sign.c
> > new file mode 100644
> > index 000000000000..b29d825bb1d4
> > --- /dev/null
> > +++ b/tools/bpf/bpftool/sign.c
> > @@ -0,0 +1,212 @@
> > +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +/*
> > + * Copyright (C) 2025 Google LLC.
> > + */
> > +
> > +#ifndef _GNU_SOURCE
> > +#define _GNU_SOURCE
> > +#endif
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <stdint.h>
> > +#include <stdbool.h>
> > +#include <string.h>
> > +#include <string.h>
> > +#include <getopt.h>
> > +#include <err.h>
> > +#include <openssl/opensslv.h>
> > +#include <openssl/bio.h>
> > +#include <openssl/evp.h>
> > +#include <openssl/pem.h>
> > +#include <openssl/err.h>
> > +#include <openssl/cms.h>
> > +#include <linux/keyctl.h>
> > +#include <errno.h>
> > +
> > +#include <bpf/skel_internal.h>
> > +
> > +#include "main.h"
> > +
> > +#define OPEN_SSL_ERR_BUF_LEN 256
> > +
> > +static void display_openssl_errors(int l)
> > +{
> > + char buf[OPEN_SSL_ERR_BUF_LEN];
> > + const char *file;
> > + const char *data;
> > + unsigned long e;
> > + int flags;
> > + int line;
> > +
> > + while ((e = ERR_get_error_all(&file, &line, NULL, &data, &flags))) {
> > + ERR_error_string_n(e, buf, sizeof(buf));
> > + if (data && (flags & ERR_TXT_STRING)) {
> > + p_err("OpenSSL %s: %s:%d: %s", buf, file, line, data);
> > + } else {
> > + p_err("OpenSSL %s: %s:%d", buf, file, line);
> > + }
> > + }
> > +}
> > +
> > +#define DISPLAY_OSSL_ERR(cond) \
> > + do { \
> > + bool __cond = (cond); \
> > + if (__cond && ERR_peek_error()) \
> > + display_openssl_errors(__LINE__);\
> > + } while (0)
> > +
> > +static EVP_PKEY *read_private_key(const char *pkey_path)
> > +{
> > + EVP_PKEY *private_key = NULL;
> > + BIO *b;
> > +
> > + b = BIO_new_file(pkey_path, "rb");
> > + private_key = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL);
> > + BIO_free(b);
> > + DISPLAY_OSSL_ERR(!private_key);
> > + return private_key;
> > +}
> > +
> > +static X509 *read_x509(const char *x509_name)
> > +{
> > + unsigned char buf[2];
> > + X509 *x509 = NULL;
> > + BIO *b;
> > + int n;
> > +
> > + b = BIO_new_file(x509_name, "rb");
> > + if (!b)
> > + goto cleanup;
> > +
> > + /* Look at the first two bytes of the file to determine the encoding */
> > + n = BIO_read(b, buf, 2);
> > + if (n != 2)
> > + goto cleanup;
> > +
> > + if (BIO_reset(b) != 0)
> > + goto cleanup;
> > +
> > + if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
> > + /* Assume raw DER encoded X.509 */
> > + x509 = d2i_X509_bio(b, NULL);
> > + else
> > + /* Assume PEM encoded X.509 */
> > + x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
> > +
> > +cleanup:
> > + BIO_free(b);
> > + DISPLAY_OSSL_ERR(!x509);
> > + return x509;
> > +}
> > +
> > +__u32 register_session_key(const char *key_der_path)
> > +{
> > + unsigned char *der_buf = NULL;
> > + X509 *x509 = NULL;
> > + int key_id = -1;
> > + int der_len;
> > +
> > + if (!key_der_path)
> > + return key_id;
> > + x509 = read_x509(key_der_path);
> > + if (!x509)
> > + goto cleanup;
> > + der_len = i2d_X509(x509, &der_buf);
> > + if (der_len < 0)
> > + goto cleanup;
> > + key_id = syscall(__NR_add_key, "asymmetric", key_der_path, der_buf,
> > + (size_t)der_len, KEY_SPEC_SESSION_KEYRING);
> > +cleanup:
> > + X509_free(x509);
> > + OPENSSL_free(der_buf);
> > + DISPLAY_OSSL_ERR(key_id == -1);
> > + return key_id;
> > +}
> > +
> > +int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
> > +{
> > + BIO *bd_in = NULL, *bd_out = NULL;
> > + EVP_PKEY *private_key = NULL;
> > + CMS_ContentInfo *cms = NULL;
> > + long actual_sig_len = 0;
> > + X509 *x509 = NULL;
> > + int err = 0;
> > +
> > + bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
> > + if (!bd_in) {
> > + err = -ENOMEM;
> > + goto cleanup;
> > + }
> > +
> > + private_key = read_private_key(private_key_path);
> > + if (!private_key) {
> > + err = -EINVAL;
> > + goto cleanup;
> > + }
> > +
> > + x509 = read_x509(cert_path);
> > + if (!x509) {
> > + err = -EINVAL;
> > + goto cleanup;
> > + }
> > +
> > + cms = CMS_sign(NULL, NULL, NULL, NULL,
> > + CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED |
> > + CMS_STREAM);
> > + if (!cms) {
> > + err = -EINVAL;
> > + goto cleanup;
> > + }
> > +
> > + if (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(),
> > + CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
> > + CMS_USE_KEYID | CMS_NOATTR)) {
> > + err = -EINVAL;
> > + goto cleanup;
> > + }
> > +
> > + if (CMS_final(cms, bd_in, NULL, CMS_NOCERTS | CMS_BINARY) != 1) {
> > + err = -EIO;
> > + goto cleanup;
> > + }
> > +
> > + EVP_Digest(opts->insns, opts->insns_sz, opts->excl_prog_hash,
> > + &opts->excl_prog_hash_sz, EVP_sha256(), NULL);
> > +
> > + bd_out = BIO_new(BIO_s_mem());
> > + if (!bd_out) {
> > + err = -ENOMEM;
> > + goto cleanup;
> > + }
> > +
> > + if (!i2d_CMS_bio_stream(bd_out, cms, NULL, 0)) {
> > + err = -EIO;
> > + goto cleanup;
> > + }
> > +
> > + actual_sig_len = BIO_get_mem_data(bd_out, NULL);
> > + if (actual_sig_len <= 0) {
> > + err = -EIO;
> > + goto cleanup;
> > + }
> > +
> > + if ((size_t)actual_sig_len > opts->signature_sz) {
> > + err = -ENOSPC;
> > + goto cleanup;
> > + }
> > +
> > + if (BIO_read(bd_out, opts->signature, actual_sig_len) != actual_sig_len) {
> > + err = -EIO;
> > + goto cleanup;
> > + }
> > +
> > + opts->signature_sz = actual_sig_len;
> > +cleanup:
> > + BIO_free(bd_out);
> > + CMS_ContentInfo_free(cms);
> > + X509_free(x509);
> > + EVP_PKEY_free(private_key);
> > + BIO_free(bd_in);
> > + DISPLAY_OSSL_ERR(err < 0);
> > + return err;
> > +}
> > --
> > 2.43.0
^ permalink raw reply related
* Re: [PATCH] rust: cred: update AlwaysRefCounted import to sync::aref
From: Miguel Ojeda @ 2025-08-17 17:01 UTC (permalink / raw)
To: Shankari Anand, Paul Moore, Serge Hallyn, linux-security-module
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, rust-for-linux, linux-kernel
In-Reply-To: <20250816114409.10107-1-shankari.ak0208@gmail.com>
On Sat, Aug 16, 2025 at 1:44 PM Shankari Anand
<shankari.ak0208@gmail.com> wrote:
>
> Update the import of `AlwaysRefCounted` in `cred.rs` to use `sync::aref`
> instead of `types`.
>
> This is part of the ongoing effort to move `ARef` and
> `AlwaysRefCounted` to the `sync` module for better modularity.
>
> Suggested-by: Benno Lossin <lossin@kernel.org>
> Link: https://github.com/Rust-for-Linux/linux/issues/1173
> Signed-off-by: Shankari Anand <shankari.ak0208@gmail.com>
> ---
> It part of a subsystem-wise split series, as suggested in:
> https://lore.kernel.org/rust-for-linux/CANiq72=NSRMV_6UxXVgkebmWmbgN4i=sfRszr-G+x3W5A4DYOg@mail.gmail.com/T/#u
> This split series is intended to ease review and subsystem-level maintenance.
>
> The original moving patch is here: (commit 07dad44aa9a93)
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=07dad44aa9a93b16af19e8609a10b241c352b440
>
> Gradually the re-export from types.rs will be eliminated in the
> future cycle.
Cc'ing Paul, Serge and -security, just in case.
Thanks!
Cheers,
Miguel
^ permalink raw reply
* Re: [PATCH] rust: cred: update AlwaysRefCounted import to sync::aref
From: Serge E. Hallyn @ 2025-08-18 3:26 UTC (permalink / raw)
To: Shankari Anand
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, rust-for-linux, linux-kernel,
Paul Moore, linux-security-module
In-Reply-To: <20250816114409.10107-1-shankari.ak0208@gmail.com>
On Sat, Aug 16, 2025 at 05:14:09PM +0530, Shankari Anand wrote:
> Update the import of `AlwaysRefCounted` in `cred.rs` to use `sync::aref`
> instead of `types`.
Thank you for forwarding, Miguel.
As far as I can see from the included links, this looks good.
> This is part of the ongoing effort to move `ARef` and
> `AlwaysRefCounted` to the `sync` module for better modularity.
>
> Suggested-by: Benno Lossin <lossin@kernel.org>
> Link: https://github.com/Rust-for-Linux/linux/issues/1173
> Signed-off-by: Shankari Anand <shankari.ak0208@gmail.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
> ---
> It part of a subsystem-wise split series, as suggested in:
> https://lore.kernel.org/rust-for-linux/CANiq72=NSRMV_6UxXVgkebmWmbgN4i=sfRszr-G+x3W5A4DYOg@mail.gmail.com/T/#u
> This split series is intended to ease review and subsystem-level maintenance.
>
> The original moving patch is here: (commit 07dad44aa9a93)
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=07dad44aa9a93b16af19e8609a10b241c352b440
>
> Gradually the re-export from types.rs will be eliminated in the
> future cycle.
> ---
> rust/kernel/cred.rs | 6 +-----
> 1 file changed, 1 insertion(+), 5 deletions(-)
>
> diff --git a/rust/kernel/cred.rs b/rust/kernel/cred.rs
> index 2599f01e8b28..4a2229542fb7 100644
> --- a/rust/kernel/cred.rs
> +++ b/rust/kernel/cred.rs
> @@ -8,11 +8,7 @@
> //!
> //! Reference: <https://www.kernel.org/doc/html/latest/security/credentials.html>
>
> -use crate::{
> - bindings,
> - task::Kuid,
> - types::{AlwaysRefCounted, Opaque},
> -};
> +use crate::{bindings, sync::aref::AlwaysRefCounted, task::Kuid, types::Opaque};
>
> /// Wraps the kernel's `struct cred`.
> ///
>
> base-commit: 8f5ae30d69d7543eee0d70083daf4de8fe15d585
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH v2] security: min_addr: use umax() to improve code
From: Paul Moore @ 2025-08-18 19:41 UTC (permalink / raw)
To: Qianfeng Rong, James Morris, Serge E. Hallyn,
linux-security-module, linux-kernel
Cc: Qianfeng Rong
In-Reply-To: <20250815035604.502874-1-rongqianfeng@vivo.com>
On Aug 14, 2025 Qianfeng Rong <rongqianfeng@vivo.com> wrote:
>
> Use umax() to reduce the code in update_mmap_min_addr() and improve its
> readability.
>
> Signed-off-by: Qianfeng Rong <rongqianfeng@vivo.com>
> ---
> v2: Change max() to umax() and include <linux/minmax.h> explicitly.
> ---
> security/min_addr.c | 6 ++----
> 1 file changed, 2 insertions(+), 4 deletions(-)
Merged into lsm/dev, thanks.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH v3 11/12] bpftool: Add support for signing BPF programs
From: Blaise Boscaccy @ 2025-08-18 20:37 UTC (permalink / raw)
To: KP Singh; +Cc: bpf, linux-security-module, paul, kys, ast, daniel, andrii
In-Reply-To: <CACYkzJ7kTbGEKtPt+HRM53bfgMFeF0P6kZnH6hjxGUy5L-kw6Q@mail.gmail.com>
KP Singh <kpsingh@kernel.org> writes:
> On Thu, Aug 14, 2025 at 6:51 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
>>
>> KP Singh <kpsingh@kernel.org> writes:
>>
>> > Two modes of operation being added:
>> >
>> > Add two modes of operation:
>> >
>> > * For prog load, allow signing a program immediately before loading. This
>> > is essential for command-line testing and administration.
>> >
>> > bpftool prog load -S -k <private_key> -i <identity_cert> fentry_test.bpf.o
>> >
>> > * For gen skeleton, embed a pre-generated signature into the C skeleton
>> > file. This supports the use of signed programs in compiled applications.
>> >
>> > bpftool gen skeleton -S -k <private_key> -i <identity_cert> fentry_test.bpf.o
>> >
>> > Generation of the loader program and its metadata map is implemented in
>> > libbpf (bpf_obj__gen_loader). bpftool generates a skeleton that loads
>> > the program and automates the required steps: freezing the map, creating
>> > an exclusive map, loading, and running. Users can use standard libbpf
>> > APIs directly or integrate loader program generation into their own
>> > toolchains.
>> >
>> > Signed-off-by: KP Singh <kpsingh@kernel.org>
>> > ---
>> > .../bpf/bpftool/Documentation/bpftool-gen.rst | 16 +-
>> > .../bpftool/Documentation/bpftool-prog.rst | 18 +-
>> > tools/bpf/bpftool/Makefile | 6 +-
>> > tools/bpf/bpftool/cgroup.c | 4 +
>> > tools/bpf/bpftool/gen.c | 60 ++++-
>> > tools/bpf/bpftool/main.c | 26 ++-
>> > tools/bpf/bpftool/main.h | 11 +
>> > tools/bpf/bpftool/prog.c | 27 ++-
>> > tools/bpf/bpftool/sign.c | 212 ++++++++++++++++++
>> > 9 files changed, 367 insertions(+), 13 deletions(-)
>> > create mode 100644 tools/bpf/bpftool/sign.c
>> >
>> > diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
>> > index ca860fd97d8d..cef469d758ed 100644
>> > --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
>> > +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
>> > @@ -16,7 +16,8 @@ SYNOPSIS
>> >
>> > **bpftool** [*OPTIONS*] **gen** *COMMAND*
>> >
>> > -*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } }
>> > +*OPTIONS* := { |COMMON_OPTIONS| [ { **-L** | **--use-loader** } ]
>> > +[ { { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> } ] }}
>> >
>> > *COMMAND* := { **object** | **skeleton** | **help** }
>> >
>> > @@ -186,6 +187,19 @@ OPTIONS
>> > skeleton). A light skeleton contains a loader eBPF program. It does not use
>> > the majority of the libbpf infrastructure, and does not need libelf.
>> >
>> > +-S, --sign
>> > + For skeletons, generate a signed skeleton. This option must be used with
>> > + **-k** and **-i**. Using this flag implicitly enables **--use-loader**.
>> > + See the "Signed Skeletons" section in the description of the
>> > + **gen skeleton** command for more details.
>> > +
>> > +-k <private_key.pem>
>> > + Path to the private key file in PEM format, required for signing.
>> > +
>> > +-i <certificate.x509>
>> > + Path to the X.509 certificate file in PEM or DER format, required for
>> > + signing.
>> > +
>> > EXAMPLES
>> > ========
>> > **$ cat example1.bpf.c**
>> > diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
>> > index f69fd92df8d8..55b812761df2 100644
>> > --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
>> > +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
>> > @@ -16,9 +16,9 @@ SYNOPSIS
>> >
>> > **bpftool** [*OPTIONS*] **prog** *COMMAND*
>> >
>> > -*OPTIONS* := { |COMMON_OPTIONS| |
>> > -{ **-f** | **--bpffs** } | { **-m** | **--mapcompat** } | { **-n** | **--nomount** } |
>> > -{ **-L** | **--use-loader** } }
>> > +*OPTIONS* := { |COMMON_OPTIONS| [ { **-f** | **--bpffs** } ] [ { **-m** | **--mapcompat** } ]
>> > +[ { **-n** | **--nomount** } ] [ { **-L** | **--use-loader** } ]
>> > +[ { { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> } ] }
>> >
>> > *COMMANDS* :=
>> > { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** |
>> > @@ -248,6 +248,18 @@ OPTIONS
>> > creating the maps, and loading the programs (see **bpftool prog tracelog**
>> > as a way to dump those messages).
>> >
>> > +-S, --sign
>> > + Enable signing of the BPF program before loading. This option must be
>> > + used with **-k** and **-i**. Using this flag implicitly enables
>> > + **--use-loader**.
>> > +
>> > +-k <private_key.pem>
>> > + Path to the private key file in PEM format, required when signing.
>> > +
>> > +-i <certificate.x509>
>> > + Path to the X.509 certificate file in PEM or DER format, required when
>> > + signing.
>> > +
>> > EXAMPLES
>> > ========
>> > **# bpftool prog show**
>> > diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
>> > index 9e9a5f006cd2..586d1b2595d1 100644
>> > --- a/tools/bpf/bpftool/Makefile
>> > +++ b/tools/bpf/bpftool/Makefile
>> > @@ -130,8 +130,8 @@ include $(FEATURES_DUMP)
>> > endif
>> > endif
>> >
>> > -LIBS = $(LIBBPF) -lelf -lz
>> > -LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz
>> > +LIBS = $(LIBBPF) -lelf -lz -lcrypto
>> > +LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz -lcrypto
>> >
>> > ifeq ($(feature-libelf-zstd),1)
>> > LIBS += -lzstd
>> > @@ -194,7 +194,7 @@ endif
>> >
>> > BPFTOOL_BOOTSTRAP := $(BOOTSTRAP_OUTPUT)bpftool
>> >
>> > -BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o)
>> > +BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o sign.o)
>> > $(BOOTSTRAP_OBJS): $(LIBBPF_BOOTSTRAP)
>> >
>> > OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
>> > diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c
>> > index 944ebe21a216..ec356deb27c9 100644
>> > --- a/tools/bpf/bpftool/cgroup.c
>> > +++ b/tools/bpf/bpftool/cgroup.c
>> > @@ -2,6 +2,10 @@
>> > // Copyright (C) 2017 Facebook
>> > // Author: Roman Gushchin <guro@fb.com>
>> >
>> > +#undef GCC_VERSION
>> > +#ifndef _GNU_SOURCE
>> > +#define _GNU_SOURCE
>> > +#endif
>> > #define _XOPEN_SOURCE 500
>> > #include <errno.h>
>> > #include <fcntl.h>
>> > diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
>> > index 67a60114368f..427468c9e9c2 100644
>> > --- a/tools/bpf/bpftool/gen.c
>> > +++ b/tools/bpf/bpftool/gen.c
>> > @@ -688,10 +688,17 @@ static void codegen_destroy(struct bpf_object *obj, const char *obj_name)
>> > static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *header_guard)
>> > {
>> > DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
>> > + struct bpf_load_and_run_opts sopts = {};
>> > + char sig_buf[MAX_SIG_SIZE];
>> > + __u8 prog_sha[SHA256_DIGEST_LENGTH];
>> > struct bpf_map *map;
>> > +
>> > char ident[256];
>> > int err = 0;
>> >
>> > + if (sign_progs)
>> > + opts.gen_hash = true;
>> > +
>> > err = bpf_object__gen_loader(obj, &opts);
>> > if (err)
>> > return err;
>> > @@ -701,6 +708,7 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
>> > p_err("failed to load object file");
>> > goto out;
>> > }
>> > +
>> > /* If there was no error during load then gen_loader_opts
>> > * are populated with the loader program.
>> > */
>> > @@ -780,8 +788,51 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
>> > print_hex(opts.insns, opts.insns_sz);
>> > codegen("\
>> > \n\
>> > - \"; \n\
>> > - \n\
>> > + \";\n");
>> > +
>> > + if (sign_progs) {
>> > + sopts.insns = opts.insns;
>> > + sopts.insns_sz = opts.insns_sz;
>> > + sopts.excl_prog_hash = prog_sha;
>> > + sopts.excl_prog_hash_sz = sizeof(prog_sha);
>> > + sopts.signature = sig_buf;
>> > + sopts.signature_sz = MAX_SIG_SIZE;
>> > + sopts.keyring_id = KEY_SPEC_SESSION_KEYRING;
>> > +
>>
>> This still has the session keyring hardcoded.
>
> We can do this for now:
>
> diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> index 427468c9e9c2..694e61f1909e 100644
> --- a/tools/bpf/bpftool/gen.c
> +++ b/tools/bpf/bpftool/gen.c
> @@ -797,7 +797,6 @@ static int gen_trace(struct bpf_object *obj, const
> char *obj_name, const char *h
> sopts.excl_prog_hash_sz = sizeof(prog_sha);
> sopts.signature = sig_buf;
> sopts.signature_sz = MAX_SIG_SIZE;
> - sopts.keyring_id = KEY_SPEC_SESSION_KEYRING;
>
> err = bpftool_prog_sign(&sopts);
> if (err < 0)
> @@ -827,7 +826,7 @@ static int gen_trace(struct bpf_object *obj, const
> char *obj_name, const char *h
> opts.signature_sz = sizeof(opts_sig) - 1;
> \n\
> opts.excl_prog_hash = (void *)opts_excl_hash;
> \n\
> opts.excl_prog_hash_sz =
> sizeof(opts_excl_hash) - 1; \n\
> - opts.keyring_id = KEY_SPEC_SESSION_KEYRING;
> \n\
> + opts.keyring_id = skel->keyring_id;
> \n\
> ");
> }
>
> @@ -1406,6 +1405,13 @@ static int do_skeleton(int argc, char **argv)
> printf("\t} links;\n");
> }
>
> + if (sign_progs) {
> + codegen("\
> + \n\
> + __s32 keyring_id; \n\
> + ");
> + }
> +
> if (btf) {
> err = codegen_datasecs(obj, obj_name);
> if (err)
> diff --git a/tools/testing/selftests/bpf/prog_tests/atomics.c
> b/tools/testing/selftests/bpf/prog_tests/atomics.c
> index 13e101f370a1..92b5f378bfb8 100644
> --- a/tools/testing/selftests/bpf/prog_tests/atomics.c
> +++ b/tools/testing/selftests/bpf/prog_tests/atomics.c
> @@ -165,11 +165,17 @@ static void test_xchg(struct atomics_lskel *skel)
> void test_atomics(void)
> {
> struct atomics_lskel *skel;
> + int err;
>
> - skel = atomics_lskel__open_and_load();
> - if (!ASSERT_OK_PTR(skel, "atomics skeleton load"))
> + skel = atomics_lskel__open();
> + if (!ASSERT_OK_PTR(skel, "atomics skeleton open"))
> return;
>
> + skel->keyring_id = KEY_SPEC_SESSION_KEYRING;
> + err = atomics_lskel__load(skel);
> + if (!ASSERT_OK(err, "atomics skeleton load"))
> + goto cleanup;
> +
> if (skel->data->skip_tests) {
> printf("%s:SKIP:no ENABLE_ATOMICS_TESTS (missing Clang
> BPF atomics support)",
> __func__);
> - KP
>
That should work.
>>
>> > + err = bpftool_prog_sign(&sopts);
>> > + if (err < 0)
>> > + return err;
>> > +
>> > + codegen("\
>> > + \n\
>> > + static const char opts_sig[] __attribute__((__aligned__(8))) = \"\\\n\
>> > + ");
>> > + print_hex((const void *)sig_buf, sopts.signature_sz);
>> > + codegen("\
>> > + \n\
>> > + \";\n");
>> > +
>> > + codegen("\
>> > + \n\
>> > + static const char opts_excl_hash[] __attribute__((__aligned__(8))) = \"\\\n\
>> > + ");
>> > + print_hex((const void *)prog_sha, sizeof(prog_sha));
>> > + codegen("\
>> > + \n\
>> > + \";\n");
>> > +
>> > + codegen("\
>> > + \n\
>> > + opts.signature = (void *)opts_sig; \n\
>> > + opts.signature_sz = sizeof(opts_sig) - 1; \n\
>> > + opts.excl_prog_hash = (void *)opts_excl_hash; \n\
>> > + opts.excl_prog_hash_sz = sizeof(opts_excl_hash) - 1; \n\
>> > + opts.keyring_id = KEY_SPEC_SESSION_KEYRING; \n\
>> > + ");
>>
>> And here.
>>
>> > + }
>> > +
>> > + codegen("\
>> > + \n\
>> > opts.ctx = (struct bpf_loader_ctx *)skel; \n\
>> > opts.data_sz = sizeof(opts_data) - 1; \n\
>> > opts.data = (void *)opts_data; \n\
>> > @@ -1240,7 +1291,7 @@ static int do_skeleton(int argc, char **argv)
>> > err = -errno;
>> > libbpf_strerror(err, err_buf, sizeof(err_buf));
>> > p_err("failed to open BPF object file: %s", err_buf);
>> > - goto out;
>> > + goto out_obj;
>> > }
>> >
>> > bpf_object__for_each_map(map, obj) {
>> > @@ -1552,6 +1603,7 @@ static int do_skeleton(int argc, char **argv)
>> > err = 0;
>> > out:
>> > bpf_object__close(obj);
>> > +out_obj:
>> > if (obj_data)
>> > munmap(obj_data, mmap_sz);
>> > close(fd);
>> > @@ -1930,7 +1982,7 @@ static int do_help(int argc, char **argv)
>> > " %1$s %2$s help\n"
>> > "\n"
>> > " " HELP_SPEC_OPTIONS " |\n"
>> > - " {-L|--use-loader} }\n"
>> > + " {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ]}\n"
>> > "",
>> > bin_name, "gen");
>> >
>> > diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
>> > index 0f1183b2ed0a..c78eb80b9c94 100644
>> > --- a/tools/bpf/bpftool/main.c
>> > +++ b/tools/bpf/bpftool/main.c
>> > @@ -33,6 +33,9 @@ bool relaxed_maps;
>> > bool use_loader;
>> > struct btf *base_btf;
>> > struct hashmap *refs_table;
>> > +bool sign_progs;
>> > +const char *private_key_path;
>> > +const char *cert_path;
>> >
>> > static void __noreturn clean_and_exit(int i)
>> > {
>> > @@ -448,6 +451,7 @@ int main(int argc, char **argv)
>> > { "nomount", no_argument, NULL, 'n' },
>> > { "debug", no_argument, NULL, 'd' },
>> > { "use-loader", no_argument, NULL, 'L' },
>> > + { "sign", no_argument, NULL, 'S' },
>> > { "base-btf", required_argument, NULL, 'B' },
>> > { 0 }
>> > };
>> > @@ -474,7 +478,7 @@ int main(int argc, char **argv)
>> > bin_name = "bpftool";
>> >
>> > opterr = 0;
>> > - while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l",
>> > + while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
>> > options, NULL)) >= 0) {
>> > switch (opt) {
>> > case 'V':
>> > @@ -520,6 +524,16 @@ int main(int argc, char **argv)
>> > case 'L':
>> > use_loader = true;
>> > break;
>> > + case 'S':
>> > + sign_progs = true;
>> > + use_loader = true;
>> > + break;
>> > + case 'k':
>> > + private_key_path = optarg;
>> > + break;
>> > + case 'i':
>> > + cert_path = optarg;
>> > + break;
>> > default:
>> > p_err("unrecognized option '%s'", argv[optind - 1]);
>> > if (json_output)
>> > @@ -534,6 +548,16 @@ int main(int argc, char **argv)
>> > if (argc < 0)
>> > usage();
>> >
>> > + if (sign_progs && (private_key_path == NULL || cert_path == NULL)) {
>> > + p_err("-i <identity_x509_cert> and -k <private> key must be supplied with -S for signing");
>> > + return -EINVAL;
>> > + }
>> > +
>> > + if (!sign_progs && (private_key_path != NULL || cert_path != NULL)) {
>> > + p_err("-i <identity_x509_cert> and -k <private> also need --sign to be used for sign programs");
>> > + return -EINVAL;
>> > + }
>> > +
>> > if (version_requested)
>> > ret = do_version(argc, argv);
>> > else
>> > diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
>> > index a2bb0714b3d6..f7f5b39b66c8 100644
>> > --- a/tools/bpf/bpftool/main.h
>> > +++ b/tools/bpf/bpftool/main.h
>> > @@ -6,9 +6,14 @@
>> >
>> > /* BFD and kernel.h both define GCC_VERSION, differently */
>> > #undef GCC_VERSION
>> > +#ifndef _GNU_SOURCE
>> > +#define _GNU_SOURCE
>> > +#endif
>> > #include <stdbool.h>
>> > #include <stdio.h>
>> > +#include <errno.h>
>> > #include <stdlib.h>
>> > +#include <bpf/skel_internal.h>
>> > #include <linux/bpf.h>
>> > #include <linux/compiler.h>
>> > #include <linux/kernel.h>
>> > @@ -52,6 +57,7 @@ static inline void *u64_to_ptr(__u64 ptr)
>> > })
>> >
>> > #define ERR_MAX_LEN 1024
>> > +#define MAX_SIG_SIZE 4096
>> >
>> > #define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
>> >
>> > @@ -85,6 +91,9 @@ extern bool relaxed_maps;
>> > extern bool use_loader;
>> > extern struct btf *base_btf;
>> > extern struct hashmap *refs_table;
>> > +extern bool sign_progs;
>> > +extern const char *private_key_path;
>> > +extern const char *cert_path;
>> >
>> > void __printf(1, 2) p_err(const char *fmt, ...);
>> > void __printf(1, 2) p_info(const char *fmt, ...);
>> > @@ -275,4 +284,6 @@ int pathname_concat(char *buf, int buf_sz, const char *path,
>> > /* print netfilter bpf_link info */
>> > void netfilter_dump_plain(const struct bpf_link_info *info);
>> > void netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr);
>> > +int bpftool_prog_sign(struct bpf_load_and_run_opts *opts);
>> > +__u32 register_session_key(const char *key_der_path);
>> > #endif
>> > diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
>> > index 9722d841abc0..82b8da084504 100644
>> > --- a/tools/bpf/bpftool/prog.c
>> > +++ b/tools/bpf/bpftool/prog.c
>> > @@ -23,6 +23,7 @@
>> > #include <linux/err.h>
>> > #include <linux/perf_event.h>
>> > #include <linux/sizes.h>
>> > +#include <linux/keyctl.h>
>> >
>> > #include <bpf/bpf.h>
>> > #include <bpf/btf.h>
>> > @@ -1930,6 +1931,8 @@ static int try_loader(struct gen_loader_opts *gen)
>> > {
>> > struct bpf_load_and_run_opts opts = {};
>> > struct bpf_loader_ctx *ctx;
>> > + char sig_buf[MAX_SIG_SIZE];
>> > + __u8 prog_sha[SHA256_DIGEST_LENGTH];
>> > int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
>> > sizeof(struct bpf_prog_desc));
>> > int log_buf_sz = (1u << 24) - 1;
>> > @@ -1953,6 +1956,24 @@ static int try_loader(struct gen_loader_opts *gen)
>> > opts.insns = gen->insns;
>> > opts.insns_sz = gen->insns_sz;
>> > fds_before = count_open_fds();
>> > +
>> > + if (sign_progs) {
>> > + opts.excl_prog_hash = prog_sha;
>> > + opts.excl_prog_hash_sz = sizeof(prog_sha);
>> > + opts.signature = sig_buf;
>> > + opts.signature_sz = MAX_SIG_SIZE;
>> > + opts.keyring_id = KEY_SPEC_SESSION_KEYRING;
>> > +
>>
>> And here as well.
>
> The "load -S" command loads and signs the program in one go, so this
> is purely for debugging and not how one would use signing. Session key
> is fine here. What we really want is flexibility when using skeletons.
>
> - KP
>
>
>
>>
>> > + err = bpftool_prog_sign(&opts);
>> > + if (err < 0)
>> > + return err;
>> > +
>> > + err = register_session_key(cert_path);
>> > + if (err < 0) {
>> > + p_err("failed to add session key");
>> > + goto out;
>> > + }
>> > + }
>> > err = bpf_load_and_run(&opts);
>> > fd_delta = count_open_fds() - fds_before;
>> > if (err < 0 || verifier_logs) {
>> > @@ -1961,6 +1982,7 @@ static int try_loader(struct gen_loader_opts *gen)
>> > fprintf(stderr, "loader prog leaked %d FDs\n",
>> > fd_delta);
>> > }
>> > +out:
>> > free(log_buf);
>> > return err;
>> > }
>> > @@ -1988,6 +2010,9 @@ static int do_loader(int argc, char **argv)
>> > goto err_close_obj;
>> > }
>> >
>> > + if (sign_progs)
>> > + gen.gen_hash = true;
>> > +
>> > err = bpf_object__gen_loader(obj, &gen);
>> > if (err)
>> > goto err_close_obj;
>> > @@ -2562,7 +2587,7 @@ static int do_help(int argc, char **argv)
>> > " METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
>> > " " HELP_SPEC_OPTIONS " |\n"
>> > " {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
>> > - " {-L|--use-loader} }\n"
>> > + " {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ] \n"
>> > "",
>> > bin_name, argv[-2]);
>> >
>> > diff --git a/tools/bpf/bpftool/sign.c b/tools/bpf/bpftool/sign.c
>> > new file mode 100644
>> > index 000000000000..b29d825bb1d4
>> > --- /dev/null
>> > +++ b/tools/bpf/bpftool/sign.c
>> > @@ -0,0 +1,212 @@
>> > +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> > +/*
>> > + * Copyright (C) 2025 Google LLC.
>> > + */
>> > +
>> > +#ifndef _GNU_SOURCE
>> > +#define _GNU_SOURCE
>> > +#endif
>> > +#include <stdio.h>
>> > +#include <stdlib.h>
>> > +#include <stdint.h>
>> > +#include <stdbool.h>
>> > +#include <string.h>
>> > +#include <string.h>
>> > +#include <getopt.h>
>> > +#include <err.h>
>> > +#include <openssl/opensslv.h>
>> > +#include <openssl/bio.h>
>> > +#include <openssl/evp.h>
>> > +#include <openssl/pem.h>
>> > +#include <openssl/err.h>
>> > +#include <openssl/cms.h>
>> > +#include <linux/keyctl.h>
>> > +#include <errno.h>
>> > +
>> > +#include <bpf/skel_internal.h>
>> > +
>> > +#include "main.h"
>> > +
>> > +#define OPEN_SSL_ERR_BUF_LEN 256
>> > +
>> > +static void display_openssl_errors(int l)
>> > +{
>> > + char buf[OPEN_SSL_ERR_BUF_LEN];
>> > + const char *file;
>> > + const char *data;
>> > + unsigned long e;
>> > + int flags;
>> > + int line;
>> > +
>> > + while ((e = ERR_get_error_all(&file, &line, NULL, &data, &flags))) {
>> > + ERR_error_string_n(e, buf, sizeof(buf));
>> > + if (data && (flags & ERR_TXT_STRING)) {
>> > + p_err("OpenSSL %s: %s:%d: %s", buf, file, line, data);
>> > + } else {
>> > + p_err("OpenSSL %s: %s:%d", buf, file, line);
>> > + }
>> > + }
>> > +}
>> > +
>> > +#define DISPLAY_OSSL_ERR(cond) \
>> > + do { \
>> > + bool __cond = (cond); \
>> > + if (__cond && ERR_peek_error()) \
>> > + display_openssl_errors(__LINE__);\
>> > + } while (0)
>> > +
>> > +static EVP_PKEY *read_private_key(const char *pkey_path)
>> > +{
>> > + EVP_PKEY *private_key = NULL;
>> > + BIO *b;
>> > +
>> > + b = BIO_new_file(pkey_path, "rb");
>> > + private_key = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL);
>> > + BIO_free(b);
>> > + DISPLAY_OSSL_ERR(!private_key);
>> > + return private_key;
>> > +}
>> > +
>> > +static X509 *read_x509(const char *x509_name)
>> > +{
>> > + unsigned char buf[2];
>> > + X509 *x509 = NULL;
>> > + BIO *b;
>> > + int n;
>> > +
>> > + b = BIO_new_file(x509_name, "rb");
>> > + if (!b)
>> > + goto cleanup;
>> > +
>> > + /* Look at the first two bytes of the file to determine the encoding */
>> > + n = BIO_read(b, buf, 2);
>> > + if (n != 2)
>> > + goto cleanup;
>> > +
>> > + if (BIO_reset(b) != 0)
>> > + goto cleanup;
>> > +
>> > + if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
>> > + /* Assume raw DER encoded X.509 */
>> > + x509 = d2i_X509_bio(b, NULL);
>> > + else
>> > + /* Assume PEM encoded X.509 */
>> > + x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
>> > +
>> > +cleanup:
>> > + BIO_free(b);
>> > + DISPLAY_OSSL_ERR(!x509);
>> > + return x509;
>> > +}
>> > +
>> > +__u32 register_session_key(const char *key_der_path)
>> > +{
>> > + unsigned char *der_buf = NULL;
>> > + X509 *x509 = NULL;
>> > + int key_id = -1;
>> > + int der_len;
>> > +
>> > + if (!key_der_path)
>> > + return key_id;
>> > + x509 = read_x509(key_der_path);
>> > + if (!x509)
>> > + goto cleanup;
>> > + der_len = i2d_X509(x509, &der_buf);
>> > + if (der_len < 0)
>> > + goto cleanup;
>> > + key_id = syscall(__NR_add_key, "asymmetric", key_der_path, der_buf,
>> > + (size_t)der_len, KEY_SPEC_SESSION_KEYRING);
>> > +cleanup:
>> > + X509_free(x509);
>> > + OPENSSL_free(der_buf);
>> > + DISPLAY_OSSL_ERR(key_id == -1);
>> > + return key_id;
>> > +}
>> > +
>> > +int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
>> > +{
>> > + BIO *bd_in = NULL, *bd_out = NULL;
>> > + EVP_PKEY *private_key = NULL;
>> > + CMS_ContentInfo *cms = NULL;
>> > + long actual_sig_len = 0;
>> > + X509 *x509 = NULL;
>> > + int err = 0;
>> > +
>> > + bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
>> > + if (!bd_in) {
>> > + err = -ENOMEM;
>> > + goto cleanup;
>> > + }
>> > +
>> > + private_key = read_private_key(private_key_path);
>> > + if (!private_key) {
>> > + err = -EINVAL;
>> > + goto cleanup;
>> > + }
>> > +
>> > + x509 = read_x509(cert_path);
>> > + if (!x509) {
>> > + err = -EINVAL;
>> > + goto cleanup;
>> > + }
>> > +
>> > + cms = CMS_sign(NULL, NULL, NULL, NULL,
>> > + CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED |
>> > + CMS_STREAM);
>> > + if (!cms) {
>> > + err = -EINVAL;
>> > + goto cleanup;
>> > + }
>> > +
>> > + if (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(),
>> > + CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
>> > + CMS_USE_KEYID | CMS_NOATTR)) {
>> > + err = -EINVAL;
>> > + goto cleanup;
>> > + }
>> > +
>> > + if (CMS_final(cms, bd_in, NULL, CMS_NOCERTS | CMS_BINARY) != 1) {
>> > + err = -EIO;
>> > + goto cleanup;
>> > + }
>> > +
>> > + EVP_Digest(opts->insns, opts->insns_sz, opts->excl_prog_hash,
>> > + &opts->excl_prog_hash_sz, EVP_sha256(), NULL);
>> > +
>> > + bd_out = BIO_new(BIO_s_mem());
>> > + if (!bd_out) {
>> > + err = -ENOMEM;
>> > + goto cleanup;
>> > + }
>> > +
>> > + if (!i2d_CMS_bio_stream(bd_out, cms, NULL, 0)) {
>> > + err = -EIO;
>> > + goto cleanup;
>> > + }
>> > +
>> > + actual_sig_len = BIO_get_mem_data(bd_out, NULL);
>> > + if (actual_sig_len <= 0) {
>> > + err = -EIO;
>> > + goto cleanup;
>> > + }
>> > +
>> > + if ((size_t)actual_sig_len > opts->signature_sz) {
>> > + err = -ENOSPC;
>> > + goto cleanup;
>> > + }
>> > +
>> > + if (BIO_read(bd_out, opts->signature, actual_sig_len) != actual_sig_len) {
>> > + err = -EIO;
>> > + goto cleanup;
>> > + }
>> > +
>> > + opts->signature_sz = actual_sig_len;
>> > +cleanup:
>> > + BIO_free(bd_out);
>> > + CMS_ContentInfo_free(cms);
>> > + X509_free(x509);
>> > + EVP_PKEY_free(private_key);
>> > + BIO_free(bd_in);
>> > + DISPLAY_OSSL_ERR(err < 0);
>> > + return err;
>> > +}
>> > --
>> > 2.43.0
^ permalink raw reply
* Re: [PATCH] rust: cred: update AlwaysRefCounted import to sync::aref
From: Paul Moore @ 2025-08-18 21:22 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: Shankari Anand, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, rust-for-linux, linux-kernel,
linux-security-module
In-Reply-To: <aKKdULYbLFRMS9qe@mail.hallyn.com>
On Sun, Aug 17, 2025 at 11:26 PM Serge E. Hallyn <serge@hallyn.com> wrote:
> On Sat, Aug 16, 2025 at 05:14:09PM +0530, Shankari Anand wrote:
> > Update the import of `AlwaysRefCounted` in `cred.rs` to use `sync::aref`
> > instead of `types`.
>
> Thank you for forwarding, Miguel.
>
> As far as I can see from the included links, this looks good.
>
> > This is part of the ongoing effort to move `ARef` and
> > `AlwaysRefCounted` to the `sync` module for better modularity.
> >
> > Suggested-by: Benno Lossin <lossin@kernel.org>
> > Link: https://github.com/Rust-for-Linux/linux/issues/1173
> > Signed-off-by: Shankari Anand <shankari.ak0208@gmail.com>
>
> Acked-by: Serge Hallyn <serge@hallyn.com>
As mentioned previously, I'm still not well versed in Rust so as long
as Serge is happy with it, I'm good with it too :)
I'm guessing it probably makes sense to include rust/kernel/cred.rs in
the creds MAINTAINERS section just as we did (or will do) with the LSM
Rust shim?
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH] rust: cred: update AlwaysRefCounted import to sync::aref
From: Alice Ryhl @ 2025-08-18 22:05 UTC (permalink / raw)
To: Paul Moore
Cc: Serge E. Hallyn, Shankari Anand, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich, rust-for-linux,
linux-kernel, linux-security-module
In-Reply-To: <CAHC9VhRwpYx0jVybcAnGdm4AGDno-GwyCzZCS7U+56Fwu2tuCg@mail.gmail.com>
On Mon, Aug 18, 2025 at 11:22 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Sun, Aug 17, 2025 at 11:26 PM Serge E. Hallyn <serge@hallyn.com> wrote:
> > On Sat, Aug 16, 2025 at 05:14:09PM +0530, Shankari Anand wrote:
> > > Update the import of `AlwaysRefCounted` in `cred.rs` to use `sync::aref`
> > > instead of `types`.
> >
> > Thank you for forwarding, Miguel.
> >
> > As far as I can see from the included links, this looks good.
> >
> > > This is part of the ongoing effort to move `ARef` and
> > > `AlwaysRefCounted` to the `sync` module for better modularity.
> > >
> > > Suggested-by: Benno Lossin <lossin@kernel.org>
> > > Link: https://github.com/Rust-for-Linux/linux/issues/1173
> > > Signed-off-by: Shankari Anand <shankari.ak0208@gmail.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > Acked-by: Serge Hallyn <serge@hallyn.com>
>
> As mentioned previously, I'm still not well versed in Rust so as long
> as Serge is happy with it, I'm good with it too :)
>
> I'm guessing it probably makes sense to include rust/kernel/cred.rs in
> the creds MAINTAINERS section just as we did (or will do) with the LSM
> Rust shim?
That would make sense to me.
My understanding is that this patch, unlike the pin-init one, is
intended to go through the LSM / CRED tree rather than taking
everything through a shared tree with Acked-bys.
Alice
^ permalink raw reply
* Re: [PATCH v3 bpf-next 1/4] kernfs: remove iattr_mutex
From: Christian Brauner @ 2025-08-19 10:05 UTC (permalink / raw)
To: Jan Kiszka
Cc: André Draszik, Song Liu, bpf, linux-fsdevel, linux-kernel,
linux-security-module, kernel-team, andrii, eddyz87, ast, daniel,
martin.lau, viro, jack, kpsingh, mattbobrowski, amir73il, gregkh,
tj, daan.j.demeyer, Will McVicker, Peter Griffin, Tudor Ambarus,
kernel-team
In-Reply-To: <8f53c544-fd4a-4526-957f-9264a36aead6@siemens.com>
> ...but it looks like v3 was merged as-is in the end, without this fixup.
> Is there some separate patch in the pipeline, or was this forgotten?
This is a result of the trees diverging which we discussed earlier.
I sent a fix.
^ permalink raw reply
* LSM namespacing API
From: Paul Moore @ 2025-08-19 14:56 UTC (permalink / raw)
To: linux-security-module, selinux; +Cc: John Johansen, Stephen Smalley
Hello all,
As most of you are likely aware, Stephen Smalley has been working on
adding namespace support to SELinux, and the work has now progressed
to the point where a serious discussion on the API is warranted. For
those of you are unfamiliar with the details or Stephen's patchset, or
simply need a refresher, he has some excellent documentation in his
work-in-progress repo:
* https://github.com/stephensmalley/selinuxns
Stephen also gave a (pre-recorded) presentation at LSS-NA this year
about SELinux namespacing, you can watch the presentation here:
* https://www.youtube.com/watch?v=AwzGCOwxLoM
In the past you've heard me state, rather firmly at times, that I
believe namespacing at the LSM framework layer to be a mistake,
although if there is something that can be done to help facilitate the
namespacing of individual LSMs at the framework layer, I would be
supportive of that. I think that a single LSM namespace API, similar
to our recently added LSM syscalls, may be such a thing, so I'd like
us to have a discussion to see if we all agree on that, and if so,
what such an API might look like.
At LSS-NA this year, John Johansen and I had a brief discussion where
he suggested a single LSM wide clone*(2) flag that individual LSM's
could opt into via callbacks. John is directly CC'd on this mail, so
I'll let him expand on this idea.
While I agree with John that a fs based API is problematic (see all of
our discussions around the LSM syscalls), I'm concerned that a single
clone*(2) flag will significantly limit our flexibility around how
individual LSMs are namespaced, something I don't want to see happen.
This makes me wonder about the potential for expanding
lsm_set_self_attr(2) to support a new LSM attribute that would support
a namespace "unshare" operation, e.g. LSM_ATTR_UNSHARE. This would
provide a single LSM framework API for an unshare operation while also
providing a mechanism to pass LSM specific via the lsm_ctx struct if
needed. Just as we do with the other LSM_ATTR_* flags today,
individual LSMs can opt-in to the API fairly easily by providing a
setselfattr() LSM callback.
Thoughts?
--
paul-moore.com
^ permalink raw reply
* Re: LSM namespacing API
From: Casey Schaufler @ 2025-08-19 17:11 UTC (permalink / raw)
To: Paul Moore, linux-security-module, selinux
Cc: John Johansen, Stephen Smalley, Casey Schaufler
In-Reply-To: <CAHC9VhRGMmhxbajwQNfGFy+ZFF1uN=UEBjqQZQ4UBy7yds3eVQ@mail.gmail.com>
On 8/19/2025 7:56 AM, Paul Moore wrote:
> Hello all,
>
> As most of you are likely aware, Stephen Smalley has been working on
> adding namespace support to SELinux, and the work has now progressed
> to the point where a serious discussion on the API is warranted. For
> those of you are unfamiliar with the details or Stephen's patchset, or
> simply need a refresher, he has some excellent documentation in his
> work-in-progress repo:
>
> * https://github.com/stephensmalley/selinuxns
>
> Stephen also gave a (pre-recorded) presentation at LSS-NA this year
> about SELinux namespacing, you can watch the presentation here:
>
> * https://www.youtube.com/watch?v=AwzGCOwxLoM
>
> In the past you've heard me state, rather firmly at times, that I
> believe namespacing at the LSM framework layer to be a mistake,
> although if there is something that can be done to help facilitate the
> namespacing of individual LSMs at the framework layer, I would be
> supportive of that. I think that a single LSM namespace API, similar
> to our recently added LSM syscalls, may be such a thing, so I'd like
> us to have a discussion to see if we all agree on that, and if so,
> what such an API might look like.
>
> At LSS-NA this year, John Johansen and I had a brief discussion where
> he suggested a single LSM wide clone*(2) flag that individual LSM's
> could opt into via callbacks. John is directly CC'd on this mail, so
> I'll let him expand on this idea.
>
> While I agree with John that a fs based API is problematic (see all of
> our discussions around the LSM syscalls), I'm concerned that a single
> clone*(2) flag will significantly limit our flexibility around how
> individual LSMs are namespaced, something I don't want to see happen.
> This makes me wonder about the potential for expanding
> lsm_set_self_attr(2) to support a new LSM attribute that would support
> a namespace "unshare" operation, e.g. LSM_ATTR_UNSHARE. This would
> provide a single LSM framework API for an unshare operation while also
> providing a mechanism to pass LSM specific via the lsm_ctx struct if
> needed. Just as we do with the other LSM_ATTR_* flags today,
> individual LSMs can opt-in to the API fairly easily by providing a
> setselfattr() LSM callback.
>
> Thoughts?
The advantage of a clone flag is that the operation is atomic with
the other namespace flag based behaviors. Having a two step process
clone(); lsm_set_self_attr(); - or -
lsm_set_self_attr(); clone();
is going to lead to cases where neither order really works correctly.
On the other hand, it's better to have a mechanism with a few drawbacks
than nothing at all. I think it could be workable.
^ permalink raw reply
* Re: LSM namespacing API
From: Stephen Smalley @ 2025-08-19 17:47 UTC (permalink / raw)
To: Paul Moore; +Cc: linux-security-module, selinux, John Johansen
In-Reply-To: <CAHC9VhRGMmhxbajwQNfGFy+ZFF1uN=UEBjqQZQ4UBy7yds3eVQ@mail.gmail.com>
On Tue, Aug 19, 2025 at 10:56 AM Paul Moore <paul@paul-moore.com> wrote:
>
> Hello all,
>
> As most of you are likely aware, Stephen Smalley has been working on
> adding namespace support to SELinux, and the work has now progressed
> to the point where a serious discussion on the API is warranted. For
> those of you are unfamiliar with the details or Stephen's patchset, or
> simply need a refresher, he has some excellent documentation in his
> work-in-progress repo:
>
> * https://github.com/stephensmalley/selinuxns
>
> Stephen also gave a (pre-recorded) presentation at LSS-NA this year
> about SELinux namespacing, you can watch the presentation here:
>
> * https://www.youtube.com/watch?v=AwzGCOwxLoM
>
> In the past you've heard me state, rather firmly at times, that I
> believe namespacing at the LSM framework layer to be a mistake,
> although if there is something that can be done to help facilitate the
> namespacing of individual LSMs at the framework layer, I would be
> supportive of that. I think that a single LSM namespace API, similar
> to our recently added LSM syscalls, may be such a thing, so I'd like
> us to have a discussion to see if we all agree on that, and if so,
> what such an API might look like.
>
> At LSS-NA this year, John Johansen and I had a brief discussion where
> he suggested a single LSM wide clone*(2) flag that individual LSM's
> could opt into via callbacks. John is directly CC'd on this mail, so
> I'll let him expand on this idea.
>
> While I agree with John that a fs based API is problematic (see all of
> our discussions around the LSM syscalls), I'm concerned that a single
> clone*(2) flag will significantly limit our flexibility around how
> individual LSMs are namespaced, something I don't want to see happen.
> This makes me wonder about the potential for expanding
> lsm_set_self_attr(2) to support a new LSM attribute that would support
> a namespace "unshare" operation, e.g. LSM_ATTR_UNSHARE. This would
> provide a single LSM framework API for an unshare operation while also
> providing a mechanism to pass LSM specific via the lsm_ctx struct if
> needed. Just as we do with the other LSM_ATTR_* flags today,
> individual LSMs can opt-in to the API fairly easily by providing a
> setselfattr() LSM callback.
>
> Thoughts?
I think we want to be able to unshare a specific security module
namespace without unsharing the others, i.e. just SELinux or just
AppArmor.
Not sure if your suggestion above supports that already but wanted to note it.
Regardless, I have no objections to any system call or flag that can
be used to unshare the SELinux namespace and it should be trivial to
wire it up to the existing underlying function.
Serge pointed out that we also will need an API to attach to an
existing SELinux namespace, which I captured here:
https://github.com/stephensmalley/selinuxns/issues/19
This is handled for other Linux namespaces by opening a pseudo file
under /proc/pid/ns and invoking setns(2), so not sure how we want to
do it.
^ permalink raw reply
* Re: LSM namespacing API
From: Paul Moore @ 2025-08-19 18:40 UTC (permalink / raw)
To: Casey Schaufler
Cc: linux-security-module, selinux, John Johansen, Stephen Smalley
In-Reply-To: <2e303958-ca60-4458-ac6d-6b83f331f660@schaufler-ca.com>
On Tue, Aug 19, 2025 at 1:11 PM Casey Schaufler <casey@schaufler-ca.com> wrote:
>
> The advantage of a clone flag is that the operation is atomic with
> the other namespace flag based behaviors. Having a two step process
>
> clone(); lsm_set_self_attr(); - or -
> lsm_set_self_attr(); clone();
>
> is going to lead to cases where neither order really works correctly.
I was envisioning something that works similarly to LSM_ATTR_EXEC
where the unshare isn't immediate, but rather happens at a future
event. With LSM_ATTR_EXEC it happens at the next exec*(), with
LSM_ATTR_UNSHARE I imagine it would happen at the next clone*().
--
paul-moore.com
^ permalink raw reply
* Re: LSM namespacing API
From: Paul Moore @ 2025-08-19 18:51 UTC (permalink / raw)
To: Stephen Smalley; +Cc: linux-security-module, selinux, John Johansen
In-Reply-To: <CAEjxPJ5EvR+2fboLu_nBGZu+ZVUpX4KM6xdPUqDErCmw=iA37g@mail.gmail.com>
On Tue, Aug 19, 2025 at 1:47 PM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> I think we want to be able to unshare a specific security module
> namespace without unsharing the others, i.e. just SELinux or just
> AppArmor.
> Not sure if your suggestion above supports that already but wanted to note it.
The lsm_set_self_attr(2) approach allows for LSM specific unshare
operations. Take the existing LSM_ATTR_EXEC attribute as an example,
two LSMs have implemented support (AppArmor and SELinux), and
userspace can independently set the attribute as desired for each LSM.
> Serge pointed out that we also will need an API to attach to an
> existing SELinux namespace, which I captured here:
> https://github.com/stephensmalley/selinuxns/issues/19
> This is handled for other Linux namespaces by opening a pseudo file
> under /proc/pid/ns and invoking setns(2), so not sure how we want to
> do it.
One option would be to have a the LSM framework return a LSM namespace
"handle" for a given LSM using lsm_get_self_attr(2) and then do a
setns(2)-esque operation using lsm_set_self_attr(2) with that
"handle". We would need to figure out what would constitute a
"handle" but let's just mark that as TBD for now with this approach (I
think better options are available).
Since we have an existing LSM namespace combination, with processes
running inside of it, it might be sufficient to simply support moving
into an existing LSM namespace set with setns(2) using only a pidfd
and a new CLONE_LSMNS flag (or similar, upstream might want this as
CLONE_NEWLSM). This would simply set the LSM namespace set for the
setns(2) caller to match that of the target pidfd. We still wouldn't
want to support CLONE_LSMNS/CLONE_NEWLSM for clone*().
Any other ideas?
--
paul-moore.com
^ permalink raw reply
* Re: LSM namespacing API
From: Paul Moore @ 2025-08-19 18:52 UTC (permalink / raw)
To: Stephen Smalley; +Cc: linux-security-module, selinux, John Johansen
In-Reply-To: <CAHC9VhSubXA4tAUoz7T==UvfrM_DXS6nF5s0tJZ1HrrVizMgZA@mail.gmail.com>
On Tue, Aug 19, 2025 at 2:51 PM Paul Moore <paul@paul-moore.com> wrote:
> On Tue, Aug 19, 2025 at 1:47 PM Stephen Smalley
> <stephen.smalley.work@gmail.com> wrote:
> >
> > I think we want to be able to unshare a specific security module
> > namespace without unsharing the others, i.e. just SELinux or just
> > AppArmor.
> > Not sure if your suggestion above supports that already but wanted to note it.
>
> The lsm_set_self_attr(2) approach allows for LSM specific unshare
> operations. Take the existing LSM_ATTR_EXEC attribute as an example,
> two LSMs have implemented support (AppArmor and SELinux), and
> userspace can independently set the attribute as desired for each LSM.
I should add, for those that didn't follow the lsm_set_self_attr(2)
development, if you want to set the same attribute on multiple LSMs,
you must make multiple calls to lsm_set_self_attr(2) (think of error
handling/conditions).
--
paul-moore.com
^ permalink raw reply
* Re: LSM namespacing API
From: Stephen Smalley @ 2025-08-19 18:58 UTC (permalink / raw)
To: Paul Moore; +Cc: Casey Schaufler, linux-security-module, selinux, John Johansen
In-Reply-To: <CAHC9VhQ5Vs+_DYAcN_Z4M9PVqW=PjaHcr4sVXADU5yqp1zFHVQ@mail.gmail.com>
On Tue, Aug 19, 2025 at 2:41 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Tue, Aug 19, 2025 at 1:11 PM Casey Schaufler <casey@schaufler-ca.com> wrote:
> >
> > The advantage of a clone flag is that the operation is atomic with
> > the other namespace flag based behaviors. Having a two step process
> >
> > clone(); lsm_set_self_attr(); - or -
> > lsm_set_self_attr(); clone();
> >
> > is going to lead to cases where neither order really works correctly.
>
> I was envisioning something that works similarly to LSM_ATTR_EXEC
> where the unshare isn't immediate, but rather happens at a future
> event. With LSM_ATTR_EXEC it happens at the next exec*(), with
> LSM_ATTR_UNSHARE I imagine it would happen at the next clone*().
I've only implemented support for an immediate unsharing of the
SELinux namespace, not any kind of deferred unsharing until the next
exec or clone.
Not saying that would be impossible, but since I was following the
example of clone(2) and unshare(2) I didn't do it.
May be some complications in doing so, but I haven't looked at it yet.
^ permalink raw reply
* Re: [PATCH v3 08/12] bpf: Implement signature verification for BPF programs
From: Paul Moore @ 2025-08-19 19:19 UTC (permalink / raw)
To: KP Singh; +Cc: bpf, linux-security-module, bboscaccy, kys, ast, daniel, andrii
In-Reply-To: <CAHC9VhT2Q4QOKq+mY9qWHz8pYg6GzUuhntg1Vd-cpGcQ7x6TLg@mail.gmail.com>
On Wed, Aug 13, 2025 at 6:17 PM Paul Moore <paul@paul-moore.com> wrote:
> On Wed, Aug 13, 2025 at 5:37 PM KP Singh <kpsingh@kernel.org> wrote:
> > On Wed, Aug 13, 2025 at 11:02 PM Paul Moore <paul@paul-moore.com> wrote:
> > >
> > > It's nice to see a v3 revision, but it would be good to see some
> > > comments on Blaise's reply to your v2 revision. From what I can see
> > > it should enable the different use cases and requirements that have
> > > been posted.
> >
> > I will defer to Alexei and others here (mostly due to time crunch). It
> > would however be useful to explain the use-cases in which signed maps
> > are useful (beyond being a different approach than the current
> > delegated verification).
I wanted to bring this up again as it has been another week with no
comment from the BPF side of the house regarding Blaise's additions.
As a reminder, Blaise's patch can be found here:
https://lore.kernel.org/linux-security-module/87sei58vy3.fsf@microsoft.com
> The use cases and requirements have been described quite a bit in
> previous threads already, with both you and Alexei participating in
> those discussions. If you really can't find the threads on lore let
> me know and I'll be happy to send you links to all of the various
> threads from the past several months.
>
> However, if I had to point to a single email that I felt best
> summarized my requirements, I think it might be this:
>
> <<< QUOTE >>>
> The loader (+ implicit loader verification of maps w/original program)
> signature verification scheme has been requested by Alexei/KP, and
> that's fine, the code is trivial and if the user/admin is satisfied
> with that as a solution, great. However, the loader + map signature
> verification scheme has some advantages and helps satisfy some
> requirements that are not satisfied by only verifying the loader and
> relying on the loader to verify the original program stored in the
> maps. One obvious advantage is that the lskel loader is much simpler
> in this case as it doesn't need to worry about verification of the
> program maps as that has already been done in bpf_check_signature().
> I'm sure there are probably some other obvious reasons, but beyond the
> one mentioned above, the other advantages that I'm interested in are a
> little less obvious, or at least I haven't seen them brought up yet.
> As I mentioned in an earlier thread, it's important to have the LSM
> hook that handles authorization of a BPF program load *after* the BPF
> program's signature has been verified. This is not simply because the
> LSM implementation might want to enforce and access control on a BPF
> program load due to the signature state (signature verified vs no
> signature), but also because the LSM might want to measure system
> state and/or provide a record of the operation. If we only verify the
> lskel loader, at the point in time that the security_bpf_prog_load()
> hook is called, we haven't properly verified both the loader and the
> original BPF program stored in the map, that doesn't happen until much
> later when the lskel loader executes. Yes, I understand that may
> sound very pedantic and fussy, but there are users who care very much
> about those details, and if they see an event in the logs that
> indicates that the BPF program signature has been verified as "good",
> they need that log event to be fully, 100% true, and not have an
> asterix of "only the lskel loader has been verified, the original BPF
> program will potentially be verified later without any additional
> events being logged to indicate the verification".
> <<< /QUOTE >>>
>
> The above was taken from this on-list email:
> https://lore.kernel.org/linux-security-module/CAHC9VhQT=ymqssa9ymXtvssHTdVH_64T8Mpb0Mh8oxRD0Guo_Q@mail.gmail.com/
--
paul-moore.com
^ permalink raw reply
* [PATCH] MAINTAINERS: add the associated Rust helper to the CREDENTIALS section
From: Paul Moore @ 2025-08-19 19:58 UTC (permalink / raw)
To: linux-security-module, rust-for-linux
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d61f7246e5bf..0ee0098f2df8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6484,6 +6484,7 @@ S: Supported
T: git https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/lsm.git
F: include/linux/cred.h
F: kernel/cred.c
+F: rust/kernel/cred.rs
F: Documentation/security/credentials.rst
INTEL CRPS COMMON REDUNDANT PSU DRIVER
--
2.50.1
^ permalink raw reply related
* Re: [PATCH] MAINTAINERS: add the associated Rust helper to the CREDENTIALS section
From: Miguel Ojeda @ 2025-08-19 20:03 UTC (permalink / raw)
To: Paul Moore, Christian Brauner; +Cc: linux-security-module, rust-for-linux
In-Reply-To: <20250819195841.238246-2-paul@paul-moore.com>
On Tue, Aug 19, 2025 at 9:59 PM Paul Moore <paul@paul-moore.com> wrote:
>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
Sounds great, thanks for taking ownership of the file!
Cc'ing Christian, since he originally took the first patches to the
file. (I think some people may be filtering based on whether they are
Cc'd or not, so it may be good to Cc others too)
Acked-by: Miguel Ojeda <ojeda@kernel.org>
It may be a good idea to give some context in the commit message too.
Cheers,
Miguel
^ permalink raw reply
* Re: [PATCH] rust: cred: update AlwaysRefCounted import to sync::aref
From: Paul Moore @ 2025-08-19 20:06 UTC (permalink / raw)
To: Alice Ryhl
Cc: Serge E. Hallyn, Shankari Anand, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich, rust-for-linux,
linux-kernel, linux-security-module
In-Reply-To: <CAH5fLgin9OhTmf52i2hQKztYLcHTxE+n1gMPXDFN83atE+u_oA@mail.gmail.com>
On Mon, Aug 18, 2025 at 6:06 PM Alice Ryhl <aliceryhl@google.com> wrote:
> On Mon, Aug 18, 2025 at 11:22 PM Paul Moore <paul@paul-moore.com> wrote:
> >
> > On Sun, Aug 17, 2025 at 11:26 PM Serge E. Hallyn <serge@hallyn.com> wrote:
> > > On Sat, Aug 16, 2025 at 05:14:09PM +0530, Shankari Anand wrote:
> > > > Update the import of `AlwaysRefCounted` in `cred.rs` to use `sync::aref`
> > > > instead of `types`.
> > >
> > > Thank you for forwarding, Miguel.
> > >
> > > As far as I can see from the included links, this looks good.
> > >
> > > > This is part of the ongoing effort to move `ARef` and
> > > > `AlwaysRefCounted` to the `sync` module for better modularity.
> > > >
> > > > Suggested-by: Benno Lossin <lossin@kernel.org>
> > > > Link: https://github.com/Rust-for-Linux/linux/issues/1173
> > > > Signed-off-by: Shankari Anand <shankari.ak0208@gmail.com>
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>
> > > Acked-by: Serge Hallyn <serge@hallyn.com>
> >
> > As mentioned previously, I'm still not well versed in Rust so as long
> > as Serge is happy with it, I'm good with it too :)
> >
> > I'm guessing it probably makes sense to include rust/kernel/cred.rs in
> > the creds MAINTAINERS section just as we did (or will do) with the LSM
> > Rust shim?
>
> That would make sense to me.
Okay, patch sent.
https://lore.kernel.org/linux-security-module/20250819195841.238246-2-paul@paul-moore.com
> My understanding is that this patch, unlike the pin-init one, is
> intended to go through the LSM / CRED tree rather than taking
> everything through a shared tree with Acked-bys.
In that case, I've gone ahead and merged Shankari's patch into lsm/dev
and will plan to send it up to Linus during the next merge window. If
something changes and the Rust folks want to send this up via a Rust
tree just let me know and I'll drop the patch.
--
paul-moore.com
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox