Skip to content
API Documentation
BastionGuard™
rootguard.bpf.c File Reference

Go to the source code of this file.

Classes

struct  rg_task_state
struct  rg_exec_context
struct  rg_pending_path

Macros

#define EPERM   1
#define ATTR_MODE   (1U << 0)
#define ATTR_UID   (1U << 1)
#define ATTR_GID   (1U << 2)
#define S_ISUID   0004000
#define S_ISGID   0002000
#define S_IWGRP   0000020
#define S_IWOTH   0000002
#define S_IRUSR   0000400
#define S_IWUSR   0000200
#define RG_METADATA_DECISION_SAMPLE   (1U << 0)
#define RG_METADATA_DECISION_BURST   (1U << 1)

Enumerations

enum  rg_pending_operation { RG_PENDING_NONE = 0 , RG_PENDING_CHMOD = 1 , RG_PENDING_CHOWN = 2 }

Functions

char LICENSE[] SEC ("license")
int bpf_path_d_path (const struct path *path, char *buf, size_t buf__sz) __weak __ksym
struct { ... }  SEC (".maps")
 __uint (type, BPF_MAP_TYPE_RINGBUF)
 __uint (max_entries, 1<< 22)
 __uint (type, BPF_MAP_TYPE_HASH)
 __uint (max_entries, 4096)
 __type (key, __u32)
 __type (value, struct rg_auth_token)
 __uint (type, BPF_MAP_TYPE_ARRAY)
 __uint (max_entries, 1)
 __type (value, struct rg_kernel_config)
 __uint (max_entries, 2048)
 __type (key, struct rg_file_key)
 __type (value, struct rg_rule_value)
 __uint (max_entries, 8192)
 __type (value, struct rg_protected_value)
 __uint (max_entries, 32768)
 __type (key, struct rg_metadata_domain_key)
 __type (value, struct rg_metadata_domain_state)
 __uint (type, BPF_MAP_TYPE_LRU_HASH)
 __uint (max_entries, 65536)
 __type (value, struct rg_exec_context)
 __uint (max_entries, 16384)
 __type (key, __u64)
 __type (value, struct rg_pending_path)
 __type (value, struct rg_task_state)
static __always_inline __u32 cred_uid (const struct cred *cred)
static __always_inline __u32 cred_euid (const struct cred *cred)
static __always_inline const struct cred * current_cred_ptr (void)
static __always_inline __u32 current_euid_value (void)
static __always_inline void current_ids (__u32 *pid, __u32 *tgid, __u32 *ppid)
static __always_inline const struct rg_kernel_config * kernel_config (void)
static __always_inline bool current_executable_key (struct rg_file_key *key)
static __always_inline bool parent_executable_key (struct rg_file_key *key)
static __always_inline __u32 observe_unprotected_metadata (bool count_burst_candidate, bool request_sample, __u32 *burst_count)
static __always_inline int lsm_result_from_verdict (__u32 verdict)
static __always_inline bool file_key_from_inode (const struct inode *inode, struct rg_file_key *key)
static __always_inline bool file_key_from_file (const struct file *file, struct rg_file_key *key)
static __always_inline bool file_key_from_dentry (const struct dentry *dentry, struct rg_file_key *key)
static __always_inline const char * dentry_name_ptr (const struct dentry *dentry)
static __always_inline bool file_key_equal (const struct rg_file_key *left, const struct rg_file_key *right)
static __always_inline void copy_map_string (char *destination, const char *source, __u32 size)
static __always_inline void capture_exec_context (struct linux_binprm *bprm, const struct rg_file_key *key)
static __always_inline bool runtime_path_is_run (const char *path)
static __always_inline bool pending_path_is_under_run (const struct rg_file_key *file_key)
static __always_inline void discard_pending_path (void)
static __always_inline void record_pending_path (const struct path *path, __u32 operation)
static __always_inline bool consume_pending_path (const struct rg_file_key *file_key, char *destination, __u32 *path_resolution)
static __always_inline void enrich_event_actor (struct rg_event *event)
static __always_inline __u64 task_key (const struct task_struct *task)
static __always_inline void observe_task (struct task_struct *task, __u32 uid, __u32 euid)
static __always_inline void observe_current_task (const struct cred *cred)
static __always_inline void mark_current_authorized_root (void)
static __always_inline bool consume_token_for (__u32 tgid, __u32 ppid, __u32 target_euid)
static __always_inline __u32 decide_escalation (const struct rg_file_key *file_key, bool have_file_key, bool authorized, __u32 *reason_flags, __u32 *rule_id)
static __always_inline __u32 metadata_verdict (bool protected_change, bool protected_audit_only, bool global_change)
static __always_inline void emit_event (__u32 type, __u32 verdict, __u32 reason_flags, __u32 rule_id, __u32 old_euid, __u32 new_euid, __s32 flags, __u32 auxiliary, const struct rg_file_key *file_key, const char *kernel_filename, const char *kernel_xattr, __u32 old_mode, __u32 new_mode, __u32 old_uid, __u32 new_uid, __u32 old_gid, __u32 new_gid)
static __always_inline int xattr_kind (const char *name)
static __always_inline void inode_metadata (const struct inode *inode, __u32 *mode, __u32 *uid, __u32 *gid)
int BPF_PROG (rg_task_alloc, struct task_struct *task, unsigned long clone_flags, int ret)
int BPF_PROG (rg_task_fix_setuid, struct cred *new, const struct cred *old, int flags, int ret)
int BPF_PROG (rg_bprm_check_security, struct linux_binprm *bprm, int ret)
static __always_inline int guard_current_root_provenance (__u32 auxiliary, __s32 operation_flags, const struct rg_file_key *file_key)
int BPF_PROG (rg_capable, const struct cred *cred, struct user_namespace *ns, int cap, unsigned int opts, int ret)
int BPF_PROG (rg_inode_permission, struct inode *inode, int mask, int ret)
int BPF_PROG (rg_file_open, struct file *file, int ret)
int BPF_PROG (rg_path_chmod, const struct path *path, umode_t mode, int ret)
int BPF_PROG2 (rg_path_chown, const struct path *, path, kuid_t, uid, kgid_t, gid, int, ret)
int BPF_PROG (rg_inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr, int ret)
static __always_inline int handle_xattr_change (struct dentry *dentry, const char *name, int operation_flags)
int BPF_PROG (rg_inode_setxattr, struct mnt_idmap *idmap, struct dentry *dentry, const char *name, const void *value, size_t size, int flags, int ret)
int BPF_PROG (rg_inode_removexattr, struct mnt_idmap *idmap, struct dentry *dentry, const char *name, int ret)
int BPF_PROG (rg_inode_set_acl, struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name, struct posix_acl *kacl, int ret)
int BPF_PROG (rg_inode_remove_acl, struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name, int ret)
int BPF_PROG (rg_inode_unlink, struct inode *dir, struct dentry *dentry, int ret)
int BPF_PROG (rg_inode_rename, struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, int ret)
int rg_sched_process_exit (struct trace_event_raw_sched_process_template *ctx)
int BPF_PROG (rg_inode_link, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, int ret)

Variables

struct rg_task_state SEC

Macro Definition Documentation

◆ ATTR_GID

#define ATTR_GID   (1U << 2)

Definition at line 14 of file rootguard.bpf.c.

Referenced by BPF_PROG().

◆ ATTR_MODE

#define ATTR_MODE   (1U << 0)

Definition at line 12 of file rootguard.bpf.c.

Referenced by BPF_PROG().

◆ ATTR_UID

#define ATTR_UID   (1U << 1)

Definition at line 13 of file rootguard.bpf.c.

Referenced by BPF_PROG().

◆ EPERM

◆ RG_METADATA_DECISION_BURST

#define RG_METADATA_DECISION_BURST   (1U << 1)

Definition at line 149 of file rootguard.bpf.c.

Referenced by BPF_PROG(), and observe_unprotected_metadata().

◆ RG_METADATA_DECISION_SAMPLE

#define RG_METADATA_DECISION_SAMPLE   (1U << 0)

Definition at line 148 of file rootguard.bpf.c.

Referenced by BPF_PROG(), handle_xattr_change(), and observe_unprotected_metadata().

◆ S_IRUSR

#define S_IRUSR   0000400

◆ S_ISGID

#define S_ISGID   0002000

Definition at line 18 of file rootguard.bpf.c.

Referenced by BPF_PROG().

◆ S_ISUID

#define S_ISUID   0004000

Definition at line 17 of file rootguard.bpf.c.

Referenced by BPF_PROG(), and running_confined().

◆ S_IWGRP

◆ S_IWOTH

◆ S_IWUSR

#define S_IWUSR   0000200

Enumeration Type Documentation

◆ rg_pending_operation

Enumerator
RG_PENDING_NONE 
RG_PENDING_CHMOD 
RG_PENDING_CHOWN 

Definition at line 92 of file rootguard.bpf.c.

92 {
96};
@ RG_PENDING_NONE
@ RG_PENDING_CHOWN
@ RG_PENDING_CHMOD

Function Documentation

◆ __type() [1/12]

__type ( key ,
__u32  )

◆ __type() [2/12]

__type ( key ,
__u64  )

◆ __type() [3/12]

__type ( key ,
struct rg_file_key  )

◆ __type() [4/12]

@104065326213267065077204246042154202176115111033::__type ( key ,
struct rg_metadata_domain_key  )

◆ __type() [5/12]

@224373156225346300110351153213010171132137273270::__type ( value ,
struct rg_auth_token  )

◆ __type() [6/12]

@203143041140227040067334045072204174231203054350::__type ( value ,
struct rg_exec_context  )

◆ __type() [7/12]

@352161370213374271236245174007307224210154071310::__type ( value ,
struct rg_kernel_config  )

◆ __type() [8/12]

@104065326213267065077204246042154202176115111033::__type ( value ,
struct rg_metadata_domain_state  )

◆ __type() [9/12]

@130147134071022107251232371157261274371323016044::__type ( value ,
struct rg_pending_path  )

◆ __type() [10/12]

@245134012353276161305305227332024146131121156167::__type ( value ,
struct rg_protected_value  )

◆ __type() [11/12]

__type ( value ,
struct rg_rule_value  )

◆ __type() [12/12]

@071173234047075171020256176074174110064126225056::__type ( value ,
struct rg_task_state  )

◆ __uint() [1/12]

@352161370213374271236245174007307224210154071310::__uint ( max_entries ,
1  )

◆ __uint() [2/12]

@130147134071022107251232371157261274371323016044::__uint ( max_entries ,
16384  )

◆ __uint() [3/12]

@046352274040072010274252261242260131256021005134::__uint ( max_entries ,
1<< 22 )

◆ __uint() [4/12]

__uint ( max_entries ,
2048  )

◆ __uint() [5/12]

@104065326213267065077204246042154202176115111033::__uint ( max_entries ,
32768  )

◆ __uint() [6/12]

@224373156225346300110351153213010171132137273270::__uint ( max_entries ,
4096  )

◆ __uint() [7/12]

__uint ( max_entries ,
65536  )

◆ __uint() [8/12]

@072270030377323145125163337314057176140314073041::__uint ( max_entries ,
8192  )

◆ __uint() [9/12]

@352161370213374271236245174007307224210154071310::__uint ( type ,
BPF_MAP_TYPE_ARRAY  )

◆ __uint() [10/12]

__uint ( type ,
BPF_MAP_TYPE_HASH  )

◆ __uint() [11/12]

@072270030377323145125163337314057176140314073041::__uint ( type ,
BPF_MAP_TYPE_LRU_HASH  )

◆ __uint() [12/12]

@314017302272111134376103221067163065253031234061::__uint ( type ,
BPF_MAP_TYPE_RINGBUF  )

Referenced by bpf_path_d_path().

◆ bpf_path_d_path()

int bpf_path_d_path ( const struct path * path,
char * buf,
size_t buf__sz )
extern

References __uint(), and bpf_path_d_path().

Referenced by bpf_path_d_path(), and record_pending_path().

◆ BPF_PROG() [1/15]

int BPF_PROG ( rg_bprm_check_security ,
struct linux_binprm * bprm,
int ret )

Definition at line 937 of file rootguard.bpf.c.

938{
939 struct task_struct *task;
940 const struct cred *old_cred;
941 const struct cred *new_cred;
942 const struct file *file;
943 const char *filename;
944 struct rg_file_key file_key = {};
945 __u32 old_euid, new_euid, pid, tgid, ppid;
946 __u32 reason_flags, rule_id, verdict;
947 bool authorized, have_file_key;
948
949 /* Preserve an earlier LSM denial with a verifier-safe errno. */
950 if (ret)
951 return -EPERM;
952
953 task = (struct task_struct *)bpf_get_current_task_btf();
954 old_cred = BPF_CORE_READ(task, cred);
955 observe_current_task(old_cred);
956 new_cred = BPF_CORE_READ(bprm, cred);
957 old_euid = cred_euid(old_cred);
958 new_euid = cred_euid(new_cred);
959
960 /*
961 * Capture every exec, not only privilege transitions. Short-lived tools
962 * such as chmod/chown may exit before userspace can read /proc/<pid>/exe.
963 */
964 file = BPF_CORE_READ(bprm, file);
965 have_file_key = file_key_from_file(file, &file_key);
966 capture_exec_context(bprm, have_file_key ? &file_key : 0);
967
968 if (new_euid != 0) {
969 observe_task(task, cred_uid(new_cred), new_euid);
970 return 0;
971 }
972 if (old_euid == 0)
973 return 0;
974
975 current_ids(&pid, &tgid, &ppid);
976 authorized = consume_token_for(tgid, ppid, new_euid);
977 verdict = decide_escalation(&file_key, have_file_key, authorized,
978 &reason_flags, &rule_id);
979 filename = BPF_CORE_READ(bprm, filename);
980
981 if (verdict == RG_VERDICT_AUTHORIZED || verdict == RG_VERDICT_TRUSTED)
983
984 emit_event(RG_EVENT_EXEC_PRIV, verdict, reason_flags, rule_id,
985 old_euid, new_euid, 0, 0,
986 have_file_key ? &file_key : 0, filename, 0,
987 0, 0, 0, 0, 0, 0);
988
989 return lsm_result_from_verdict(verdict);
990}
static __always_inline bool file_key_from_file(const struct file *file, struct rg_file_key *key)
static __always_inline void emit_event(__u32 type, __u32 verdict, __u32 reason_flags, __u32 rule_id, __u32 old_euid, __u32 new_euid, __s32 flags, __u32 auxiliary, const struct rg_file_key *file_key, const char *kernel_filename, const char *kernel_xattr, __u32 old_mode, __u32 new_mode, __u32 old_uid, __u32 new_uid, __u32 old_gid, __u32 new_gid)
static __always_inline __u32 cred_uid(const struct cred *cred)
static __always_inline __u32 decide_escalation(const struct rg_file_key *file_key, bool have_file_key, bool authorized, __u32 *reason_flags, __u32 *rule_id)
static __always_inline void mark_current_authorized_root(void)
static __always_inline int lsm_result_from_verdict(__u32 verdict)
static __always_inline void observe_task(struct task_struct *task, __u32 uid, __u32 euid)
static __always_inline void observe_current_task(const struct cred *cred)
static __always_inline void current_ids(__u32 *pid, __u32 *tgid, __u32 *ppid)
static __always_inline bool consume_token_for(__u32 tgid, __u32 ppid, __u32 target_euid)
static __always_inline void capture_exec_context(struct linux_binprm *bprm, const struct rg_file_key *key)
#define EPERM
static __always_inline __u32 cred_euid(const struct cred *cred)
@ RG_EVENT_EXEC_PRIV
@ RG_VERDICT_TRUSTED
@ RG_VERDICT_AUTHORIZED

References BPF_PROG(), capture_exec_context(), consume_token_for(), cred_euid(), cred_uid(), current_ids(), decide_escalation(), emit_event(), EPERM, file_key_from_file(), lsm_result_from_verdict(), mark_current_authorized_root(), observe_current_task(), observe_task(), RG_EVENT_EXEC_PRIV, RG_VERDICT_AUTHORIZED, and RG_VERDICT_TRUSTED.

◆ BPF_PROG() [2/15]

int BPF_PROG ( rg_capable ,
const struct cred * cred,
struct user_namespace * ns,
int cap,
unsigned int opts,
int ret )

Definition at line 1064 of file rootguard.bpf.c.

1066{
1067 const struct cred *current_cred;
1068
1069 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1070 if (ret)
1071 return -EPERM;
1072
1073 current_cred = current_cred_ptr();
1074 if (cred != current_cred)
1075 return 0;
1076
1077 return guard_current_root_provenance((__u32)cap, (__s32)opts, 0);
1078}
static __always_inline const struct cred * current_cred_ptr(void)
static __always_inline int guard_current_root_provenance(__u32 auxiliary, __s32 operation_flags, const struct rg_file_key *file_key)

References BPF_PROG(), current_cred_ptr(), EPERM, and guard_current_root_provenance().

◆ BPF_PROG() [3/15]

int BPF_PROG ( rg_file_open ,
struct file * file,
int ret )

Definition at line 1102 of file rootguard.bpf.c.

1103{
1104 struct rg_file_key key = {};
1105 bool have_key;
1106
1107 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1108 if (ret)
1109 return -EPERM;
1110
1111 have_key = file_key_from_file(file, &key);
1113 have_key ? &key : 0);
1114}

References BPF_PROG(), EPERM, file_key_from_file(), and guard_current_root_provenance().

◆ BPF_PROG() [4/15]

int BPF_PROG ( rg_inode_link ,
struct dentry * old_dentry,
struct inode * dir,
struct dentry * new_dentry,
int ret )

Definition at line 1529 of file rootguard.bpf.c.

1531{
1532 struct rg_file_key key = {};
1533 struct rg_protected_value *protected;
1534 __u32 verdict;
1535
1536 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1537 if (ret)
1538 return -EPERM;
1539 ret = guard_current_root_provenance(0, 0, 0);
1540 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1541 if (ret)
1542 return -EPERM;
1543 if (!file_key_from_dentry(old_dentry, &key))
1544 return 0;
1545 protected = bpf_map_lookup_elem(&protected_inodes, &key);
1546 if (!protected || !(protected->flags & RG_PROTECT_HARDLINK))
1547 return 0;
1548
1549 verdict = metadata_verdict(true,
1550 (protected->flags & RG_PROTECT_AUDIT_ONLY) != 0, false);
1552 RG_REASON_PROTECTED_HARDLINK, protected->rule_id,
1553 0, 0, 0, 0, &key, dentry_name_ptr(old_dentry), 0,
1554 0, 0, 0, 0, 0, 0);
1555 return lsm_result_from_verdict(verdict);
1556}
static __always_inline __u32 metadata_verdict(bool protected_change, bool protected_audit_only, bool global_change)
static __always_inline const char * dentry_name_ptr(const struct dentry *dentry)
static __always_inline bool file_key_from_dentry(const struct dentry *dentry, struct rg_file_key *key)
@ RG_REASON_PROTECTED_HARDLINK
@ RG_EVENT_PROTECTED_HARDLINK
@ RG_PROTECT_HARDLINK
@ RG_PROTECT_AUDIT_ONLY

References BPF_PROG(), dentry_name_ptr(), emit_event(), EPERM, file_key_from_dentry(), guard_current_root_provenance(), lsm_result_from_verdict(), metadata_verdict(), RG_EVENT_PROTECTED_HARDLINK, RG_PROTECT_AUDIT_ONLY, RG_PROTECT_HARDLINK, and RG_REASON_PROTECTED_HARDLINK.

◆ BPF_PROG() [5/15]

int BPF_PROG ( rg_inode_permission ,
struct inode * inode,
int mask,
int ret )

Definition at line 1086 of file rootguard.bpf.c.

1087{
1088 struct rg_file_key key = {};
1089 bool have_key;
1090
1091 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1092 if (ret)
1093 return -EPERM;
1094
1095 have_key = file_key_from_inode(inode, &key);
1096 return guard_current_root_provenance((__u32)mask, mask,
1097 have_key ? &key : 0);
1098}
static __always_inline bool file_key_from_inode(const struct inode *inode, struct rg_file_key *key)

References BPF_PROG(), EPERM, file_key_from_inode(), guard_current_root_provenance(), and rg_file_key::inode.

◆ BPF_PROG() [6/15]

int BPF_PROG ( rg_inode_remove_acl ,
struct mnt_idmap * idmap,
struct dentry * dentry,
const char * acl_name,
int ret )

Definition at line 1421 of file rootguard.bpf.c.

1423{
1424 struct rg_file_key key = {};
1425 struct rg_protected_value *protected;
1426 __u32 verdict;
1427
1428 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1429 if (ret)
1430 return -EPERM;
1431 ret = guard_current_root_provenance(0, 0, 0);
1432 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1433 if (ret)
1434 return -EPERM;
1435 if (!file_key_from_dentry(dentry, &key))
1436 return 0;
1437 protected = bpf_map_lookup_elem(&protected_inodes, &key);
1438 if (protected && (protected->flags & RG_PROTECT_ACL)) {
1439 verdict = metadata_verdict(true,
1440 (protected->flags & RG_PROTECT_AUDIT_ONLY) != 0, false);
1442 protected->rule_id, 0, 0, -1, 0,
1443 &key, dentry_name_ptr(dentry), acl_name,
1444 0, 0, 0, 0, 0, 0);
1445 } else {
1446 /* Unprotected ACL removals are intentionally silent for the same reason. */
1447 return 0;
1448 }
1449 return lsm_result_from_verdict(verdict);
1450}
@ RG_REASON_PROTECTED_ACL
@ RG_EVENT_ACL_CHANGE
@ RG_PROTECT_ACL

References BPF_PROG(), dentry_name_ptr(), emit_event(), EPERM, file_key_from_dentry(), guard_current_root_provenance(), lsm_result_from_verdict(), metadata_verdict(), RG_EVENT_ACL_CHANGE, RG_PROTECT_ACL, RG_PROTECT_AUDIT_ONLY, and RG_REASON_PROTECTED_ACL.

◆ BPF_PROG() [7/15]

int BPF_PROG ( rg_inode_removexattr ,
struct mnt_idmap * idmap,
struct dentry * dentry,
const char * name,
int ret )

Definition at line 1374 of file rootguard.bpf.c.

1376{
1377 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1378 if (ret)
1379 return -EPERM;
1380 return handle_xattr_change(dentry, name, -1);
1381}
static __always_inline int handle_xattr_change(struct dentry *dentry, const char *name, int operation_flags)

References BPF_PROG(), EPERM, and handle_xattr_change().

◆ BPF_PROG() [8/15]

int BPF_PROG ( rg_inode_rename ,
struct inode * old_dir,
struct dentry * old_dentry,
struct inode * new_dir,
struct dentry * new_dentry,
int ret )

Definition at line 1483 of file rootguard.bpf.c.

1486{
1487 struct rg_file_key key = {};
1488 struct rg_protected_value *protected = 0;
1489 __u32 verdict;
1490
1491 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1492 if (ret)
1493 return -EPERM;
1494 ret = guard_current_root_provenance(0, 0, 0);
1495 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1496 if (ret)
1497 return -EPERM;
1498
1499 if (file_key_from_dentry(old_dentry, &key))
1500 protected = bpf_map_lookup_elem(&protected_inodes, &key);
1501 if (!protected && file_key_from_dentry(new_dentry, &key))
1502 protected = bpf_map_lookup_elem(&protected_inodes, &key);
1503 if (!protected || !(protected->flags & RG_PROTECT_RENAME))
1504 return 0;
1505
1506 verdict = metadata_verdict(true,
1507 (protected->flags & RG_PROTECT_AUDIT_ONLY) != 0, false);
1509 RG_REASON_PROTECTED_RENAME, protected->rule_id,
1510 0, 0, 0, 0, &key, dentry_name_ptr(old_dentry), 0,
1511 0, 0, 0, 0, 0, 0);
1512 return lsm_result_from_verdict(verdict);
1513}
@ RG_REASON_PROTECTED_RENAME
@ RG_EVENT_PROTECTED_RENAME
@ RG_PROTECT_RENAME

References BPF_PROG(), dentry_name_ptr(), emit_event(), EPERM, file_key_from_dentry(), guard_current_root_provenance(), lsm_result_from_verdict(), metadata_verdict(), RG_EVENT_PROTECTED_RENAME, RG_PROTECT_AUDIT_ONLY, RG_PROTECT_RENAME, and RG_REASON_PROTECTED_RENAME.

◆ BPF_PROG() [9/15]

int BPF_PROG ( rg_inode_set_acl ,
struct mnt_idmap * idmap,
struct dentry * dentry,
const char * acl_name,
struct posix_acl * kacl,
int ret )

Definition at line 1384 of file rootguard.bpf.c.

1387{
1388 struct rg_file_key key = {};
1389 struct rg_protected_value *protected;
1390 __u32 verdict;
1391
1392 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1393 if (ret)
1394 return -EPERM;
1395 ret = guard_current_root_provenance(0, 0, 0);
1396 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1397 if (ret)
1398 return -EPERM;
1399 if (!file_key_from_dentry(dentry, &key))
1400 return 0;
1401 protected = bpf_map_lookup_elem(&protected_inodes, &key);
1402 if (protected && (protected->flags & RG_PROTECT_ACL)) {
1403 verdict = metadata_verdict(true,
1404 (protected->flags & RG_PROTECT_AUDIT_ONLY) != 0, false);
1406 protected->rule_id, 0, 0, 1, 0,
1407 &key, dentry_name_ptr(dentry), acl_name,
1408 0, 0, 0, 0, 0, 0);
1409 } else {
1410 /*
1411 * Do not emit every global ACL edit. The hook does not provide a
1412 * low-noise semantic verdict here, so unprotected ACL activity remains
1413 * silent instead of generating production false positives.
1414 */
1415 return 0;
1416 }
1417 return lsm_result_from_verdict(verdict);
1418}

References BPF_PROG(), dentry_name_ptr(), emit_event(), EPERM, file_key_from_dentry(), guard_current_root_provenance(), lsm_result_from_verdict(), metadata_verdict(), RG_EVENT_ACL_CHANGE, RG_PROTECT_ACL, RG_PROTECT_AUDIT_ONLY, and RG_REASON_PROTECTED_ACL.

◆ BPF_PROG() [10/15]

int BPF_PROG ( rg_inode_setattr ,
struct mnt_idmap * idmap,
struct dentry * dentry,
struct iattr * attr,
int ret )

Definition at line 1148 of file rootguard.bpf.c.

1150{
1151 struct rg_file_key key = {};
1152 struct rg_protected_value *protected;
1153 const struct rg_kernel_config *cfg;
1154 struct inode *inode;
1155 __u32 old_mode, new_mode, old_uid, new_uid, old_gid, new_gid;
1156 __u32 valid, reasons = 0, protected_reasons = 0, global_reasons = 0;
1157 __u32 verdict, event_type, metadata_decision = 0, burst_count = 0;
1158
1159 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1160 if (ret)
1161 return -EPERM;
1162 ret = guard_current_root_provenance(0, 0, 0);
1163 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1164 if (ret)
1165 return -EPERM;
1166 if (!file_key_from_dentry(dentry, &key))
1167 return 0;
1168
1169 inode = BPF_CORE_READ(dentry, d_inode);
1170 inode_metadata(inode, &old_mode, &old_uid, &old_gid);
1171 valid = BPF_CORE_READ(attr, ia_valid);
1172 new_mode = old_mode;
1173 new_uid = old_uid;
1174 new_gid = old_gid;
1175
1176 if (valid & ATTR_MODE)
1177 new_mode = BPF_CORE_READ(attr, ia_mode) & 07777U;
1178 if (valid & ATTR_UID) {
1179 kuid_t uid = {};
1180 BPF_CORE_READ_INTO(&uid, attr, ia_uid);
1181 new_uid = uid.val;
1182 }
1183 if (valid & ATTR_GID) {
1184 kgid_t gid = {};
1185 BPF_CORE_READ_INTO(&gid, attr, ia_gid);
1186 new_gid = gid.val;
1187 }
1188
1189 /*
1190 * Production filtering: ordinary chmod/chown traffic is not a security
1191 * incident and must not flood a desktop system. Global events are emitted
1192 * only for privilege-relevant transitions. Full metadata monitoring and
1193 * enforcement remain available for identities explicitly loaded into
1194 * protected_inodes.
1195 */
1196 protected = bpf_map_lookup_elem(&protected_inodes, &key);
1197 if (protected) {
1198 if ((valid & ATTR_MODE) &&
1199 (protected->flags & RG_PROTECT_MODE) &&
1200 new_mode != protected->baseline_mode)
1201 protected_reasons |= RG_REASON_PROTECTED_MODE;
1202 if (((valid & ATTR_UID) || (valid & ATTR_GID)) &&
1203 (protected->flags & RG_PROTECT_OWNER) &&
1204 (new_uid != protected->baseline_uid ||
1205 new_gid != protected->baseline_gid))
1206 protected_reasons |= RG_REASON_PROTECTED_OWNER;
1207 }
1208
1209 cfg = kernel_config();
1210
1211 /*
1212 * Do not feed the ring buffer with unprotected chmod/chown traffic below
1213 * /run. BastionGuard Backup mounts temporary backup filesystems there and
1214 * restoring file metadata would otherwise look like a stream of global
1215 * privilege changes. Explicit protected identities always take precedence.
1216 * Credential provenance was checked above and is not bypassed.
1217 */
1218 if (!protected && cfg && cfg->ignore_run_global_metadata &&
1221 return 0;
1222 }
1223
1224 if (cfg && cfg->monitor_global_privilege_metadata) {
1225 /* A newly added setuid/setgid bit can create a direct privilege path. */
1226 if ((valid & ATTR_MODE) &&
1227 ((new_mode & (S_ISUID | S_ISGID)) &
1228 ~(old_mode & (S_ISUID | S_ISGID))))
1230
1231 /* Changing a previously non-root object to UID 0 is security-relevant. */
1232 if ((valid & ATTR_UID) && old_uid != 0 && new_uid == 0)
1234
1235 /*
1236 * Alert when an object that was already root-owned gains write access
1237 * for group or other users.
1238 */
1239 if ((valid & ATTR_MODE) && old_uid == 0 && new_uid == 0 &&
1240 ((new_mode & (S_IWGRP | S_IWOTH)) &
1241 ~(old_mode & (S_IWGRP | S_IWOTH))))
1242 global_reasons |= RG_REASON_ROOT_OWNED_WRITE_EXPOSURE |
1244 }
1245
1246 if (!protected && cfg &&
1247 ((valid & ATTR_MODE) || (valid & ATTR_UID) || (valid & ATTR_GID))) {
1248 bool burst_candidate = false;
1249
1250 /*
1251 * Backups and package managers legitimately replay huge numbers of
1252 * chmod/chown operations. A ransomware-style lockout is materially
1253 * different: it removes the owner's ability to access the object, or
1254 * promotes ownership to root. Count only those destructive patterns
1255 * for burst escalation; routine metadata replay does not consume the
1256 * burst budget.
1257 */
1258 if ((valid & ATTR_MODE) &&
1259 (old_mode & (S_IRUSR | S_IWUSR)) != 0 &&
1260 (new_mode & (S_IRUSR | S_IWUSR)) == 0)
1261 burst_candidate = true;
1262 if ((valid & ATTR_UID) && old_uid != 0 && new_uid == 0)
1263 burst_candidate = true;
1264
1265 metadata_decision = observe_unprotected_metadata(
1266 burst_candidate, global_reasons != 0, &burst_count);
1267 if (metadata_decision & RG_METADATA_DECISION_BURST)
1269 if (global_reasons &&
1270 !(metadata_decision & RG_METADATA_DECISION_SAMPLE))
1271 global_reasons = 0;
1272 }
1273
1274 reasons |= global_reasons | protected_reasons;
1275 if (!reasons)
1276 return 0;
1277
1278 verdict = metadata_verdict(
1279 protected_reasons != 0,
1280 protected && (protected->flags & RG_PROTECT_AUDIT_ONLY),
1281 (reasons & RG_REASON_GLOBAL_METADATA) != 0);
1282 event_type = (((valid & ATTR_UID) && new_uid != old_uid) ||
1283 ((valid & ATTR_GID) && new_gid != old_gid))
1286
1287 {
1288 __u32 actor_euid = current_euid_value();
1289 emit_event(event_type, verdict, reasons,
1290 protected ? protected->rule_id : 0,
1291 actor_euid, actor_euid,
1292 (int)valid, burst_count, &key, dentry_name_ptr(dentry), 0,
1293 old_mode, new_mode, old_uid, new_uid, old_gid, new_gid);
1294 }
1295
1296 return lsm_result_from_verdict(verdict);
1297}
#define RG_METADATA_DECISION_BURST
#define ATTR_GID
#define S_ISUID
#define ATTR_MODE
static __always_inline void inode_metadata(const struct inode *inode, __u32 *mode, __u32 *uid, __u32 *gid)
static __always_inline bool pending_path_is_under_run(const struct rg_file_key *file_key)
#define S_IWOTH
static __always_inline const struct rg_kernel_config * kernel_config(void)
#define ATTR_UID
#define S_IRUSR
#define S_ISGID
static __always_inline __u32 observe_unprotected_metadata(bool count_burst_candidate, bool request_sample, __u32 *burst_count)
#define RG_METADATA_DECISION_SAMPLE
static __always_inline __u32 current_euid_value(void)
#define S_IWUSR
#define S_IWGRP
static __always_inline void discard_pending_path(void)
@ RG_REASON_PROTECTED_OWNER
@ RG_REASON_ROOT_OWNED_WRITE_EXPOSURE
@ RG_REASON_METADATA_BURST
@ RG_REASON_OWNER_TO_ROOT
@ RG_REASON_GLOBAL_METADATA
@ RG_REASON_SETID_ADDED
@ RG_REASON_PROTECTED_MODE
@ RG_EVENT_OWNER_CHANGE
@ RG_EVENT_PERMISSION_CHANGE
@ RG_PROTECT_MODE
@ RG_PROTECT_OWNER
__u32 monitor_global_privilege_metadata

References ATTR_GID, ATTR_MODE, ATTR_UID, BPF_PROG(), current_euid_value(), dentry_name_ptr(), discard_pending_path(), emit_event(), EPERM, file_key_from_dentry(), guard_current_root_provenance(), rg_kernel_config::ignore_run_global_metadata, inode_metadata(), kernel_config(), lsm_result_from_verdict(), metadata_verdict(), rg_kernel_config::monitor_global_privilege_metadata, observe_unprotected_metadata(), pending_path_is_under_run(), RG_EVENT_OWNER_CHANGE, RG_EVENT_PERMISSION_CHANGE, RG_METADATA_DECISION_BURST, RG_METADATA_DECISION_SAMPLE, RG_PROTECT_AUDIT_ONLY, RG_PROTECT_MODE, RG_PROTECT_OWNER, RG_REASON_GLOBAL_METADATA, RG_REASON_METADATA_BURST, RG_REASON_OWNER_TO_ROOT, RG_REASON_PROTECTED_MODE, RG_REASON_PROTECTED_OWNER, RG_REASON_ROOT_OWNED_WRITE_EXPOSURE, RG_REASON_SETID_ADDED, S_IRUSR, S_ISGID, S_ISUID, S_IWGRP, S_IWOTH, and S_IWUSR.

◆ BPF_PROG() [11/15]

int BPF_PROG ( rg_inode_setxattr ,
struct mnt_idmap * idmap,
struct dentry * dentry,
const char * name,
const void * value,
size_t size,
int flags,
int ret )

Definition at line 1363 of file rootguard.bpf.c.

1366{
1367 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1368 if (ret)
1369 return -EPERM;
1370 return handle_xattr_change(dentry, name, flags);
1371}

References BPF_PROG(), EPERM, and handle_xattr_change().

◆ BPF_PROG() [12/15]

int BPF_PROG ( rg_inode_unlink ,
struct inode * dir,
struct dentry * dentry,
int ret )

Definition at line 1453 of file rootguard.bpf.c.

1455{
1456 struct rg_file_key key = {};
1457 struct rg_protected_value *protected;
1458 __u32 verdict;
1459
1460 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1461 if (ret)
1462 return -EPERM;
1463 ret = guard_current_root_provenance(0, 0, 0);
1464 /* Preserve an earlier LSM denial with a verifier-safe errno. */
1465 if (ret)
1466 return -EPERM;
1467 if (!file_key_from_dentry(dentry, &key))
1468 return 0;
1469 protected = bpf_map_lookup_elem(&protected_inodes, &key);
1470 if (!protected || !(protected->flags & RG_PROTECT_UNLINK))
1471 return 0;
1472
1473 verdict = metadata_verdict(true,
1474 (protected->flags & RG_PROTECT_AUDIT_ONLY) != 0, false);
1476 RG_REASON_PROTECTED_UNLINK, protected->rule_id,
1477 0, 0, 0, 0, &key, dentry_name_ptr(dentry), 0,
1478 0, 0, 0, 0, 0, 0);
1479 return lsm_result_from_verdict(verdict);
1480}
@ RG_REASON_PROTECTED_UNLINK
@ RG_EVENT_PROTECTED_UNLINK
@ RG_PROTECT_UNLINK

References BPF_PROG(), dentry_name_ptr(), emit_event(), EPERM, file_key_from_dentry(), guard_current_root_provenance(), lsm_result_from_verdict(), metadata_verdict(), RG_EVENT_PROTECTED_UNLINK, RG_PROTECT_AUDIT_ONLY, RG_PROTECT_UNLINK, and RG_REASON_PROTECTED_UNLINK.

◆ BPF_PROG() [13/15]

int BPF_PROG ( rg_path_chmod ,
const struct path * path,
umode_t mode,
int ret )

Definition at line 1117 of file rootguard.bpf.c.

1118{
1119 if (ret)
1120 return -EPERM;
1122 return 0;
1123}
static __always_inline void record_pending_path(const struct path *path, __u32 operation)

References BPF_PROG(), EPERM, record_pending_path(), and RG_PENDING_CHMOD.

◆ BPF_PROG() [14/15]

int BPF_PROG ( rg_task_alloc ,
struct task_struct * task,
unsigned long clone_flags,
int ret )

Definition at line 856 of file rootguard.bpf.c.

858{
859 struct task_struct *current;
860 const struct cred *cred;
861 struct rg_task_state state = {};
862 struct rg_task_state *parent_state;
863 __u64 parent_key, child_key;
864
865 /*
866 * The chained LSM result arrives through a u64 tracing-context slot.
867 * Some Clang/verifier combinations lose the signed 32-bit range when
868 * returning that dynamic value directly. Never override a previous
869 * denial: normalize it to a verifier-safe negative errno instead.
870 */
871 if (ret)
872 return -EPERM;
873
874 current = (struct task_struct *)bpf_get_current_task_btf();
875 parent_key = task_key(current);
876 child_key = task_key(task);
877 parent_state = bpf_map_lookup_elem(&task_states, &parent_key);
878 if (parent_state) {
879 state = *parent_state;
880 } else {
881 cred = BPF_CORE_READ(current, cred);
882 state.observed_nonroot = cred_euid(cred) != 0;
883 state.baseline_uid = cred_uid(cred);
884 }
885
886 bpf_map_update_elem(&task_states, &child_key, &state, BPF_ANY);
887 return 0;
888}
static __always_inline __u64 task_key(const struct task_struct *task)
__u32 observed_nonroot

References rg_task_state::baseline_uid, BPF_PROG(), cred_euid(), cred_uid(), EPERM, rg_task_state::observed_nonroot, and task_key().

Referenced by BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), and BPF_PROG().

◆ BPF_PROG() [15/15]

int BPF_PROG ( rg_task_fix_setuid ,
struct cred * new,
const struct cred * old,
int flags,
int ret )

Definition at line 892 of file rootguard.bpf.c.

894{
895 struct rg_file_key file_key = {};
896 __u32 old_euid, new_euid, pid, tgid, ppid;
897 __u32 reason_flags, rule_id, verdict;
898 bool authorized, have_file_key;
899
900 /* Preserve an earlier LSM denial with a verifier-safe errno. */
901 if (ret)
902 return -EPERM;
903
905 old_euid = cred_euid(old);
906 new_euid = cred_euid(new);
907
908 /* A root service dropping to a user must establish a non-root baseline. */
909 if (new_euid != 0) {
910 struct task_struct *task =
911 (struct task_struct *)bpf_get_current_task_btf();
912 observe_task(task, cred_uid(new), new_euid);
913 return 0;
914 }
915 if (old_euid == 0)
916 return 0;
917
918 current_ids(&pid, &tgid, &ppid);
919 authorized = consume_token_for(tgid, ppid, new_euid);
920 have_file_key = current_executable_key(&file_key);
921 verdict = decide_escalation(&file_key, have_file_key, authorized,
922 &reason_flags, &rule_id);
923
924 if (verdict == RG_VERDICT_AUTHORIZED || verdict == RG_VERDICT_TRUSTED)
926
927 emit_event(RG_EVENT_SETUID, verdict, reason_flags, rule_id,
928 old_euid, new_euid, flags, 0,
929 have_file_key ? &file_key : 0, 0, 0,
930 0, 0, 0, 0, 0, 0);
931
932 return lsm_result_from_verdict(verdict);
933}
static __always_inline bool current_executable_key(struct rg_file_key *key)
@ RG_EVENT_SETUID

References BPF_PROG(), consume_token_for(), cred_euid(), cred_uid(), current_executable_key(), current_ids(), decide_escalation(), emit_event(), EPERM, lsm_result_from_verdict(), mark_current_authorized_root(), observe_current_task(), observe_task(), RG_EVENT_SETUID, RG_VERDICT_AUTHORIZED, and RG_VERDICT_TRUSTED.

◆ BPF_PROG2()

int BPF_PROG2 ( rg_path_chown ,
const struct path * ,
path ,
kuid_t ,
uid ,
kgid_t ,
gid ,
int ,
ret  )

Definition at line 1126 of file rootguard.bpf.c.

1131{
1132 /* kuid_t/kgid_t are small struct-by-value arguments. BPF_PROG performs
1133 * scalar casts from ctx[] and therefore cannot materialise them. Use
1134 * BPF_PROG2, which accounts for aggregate arguments in the trampoline
1135 * context. The values are not needed here; inode_setattr records the
1136 * resulting owner metadata change.
1137 */
1138 (void)uid;
1139 (void)gid;
1140
1141 if (ret)
1142 return -EPERM;
1144 return 0;
1145}

References BPF_PROG2(), EPERM, record_pending_path(), and RG_PENDING_CHOWN.

Referenced by BPF_PROG2().

◆ capture_exec_context()

__always_inline void capture_exec_context ( struct linux_binprm * bprm,
const struct rg_file_key * key )
static

Definition at line 447 of file rootguard.bpf.c.

449{
450 struct rg_exec_context context = {};
451 struct task_struct *task;
452 const char *filename;
453 __u64 ids;
454 __u32 tgid;
455
456 if (!bprm)
457 return;
458
459 ids = bpf_get_current_pid_tgid();
460 tgid = (__u32)(ids >> 32);
461 if (tgid == 0)
462 return;
463
464 if (key)
465 context.executable = *key;
466
467 task = (struct task_struct *)bpf_get_current_task_btf();
468 if (task)
469 context.start_boottime_ns = BPF_CORE_READ(task, start_boottime);
470
471 /*
472 * bprm->filename is captured synchronously at exec time. The executable
473 * device/inode above remains the authoritative identity even when this
474 * string is relative or later renamed.
475 */
476 filename = BPF_CORE_READ(bprm, filename);
477 if (filename)
478 bpf_probe_read_kernel_str(context.executable_path,
479 sizeof(context.executable_path),
480 filename);
481 bpf_map_update_elem(&exec_contexts, &tgid, &context, BPF_ANY);
482}
struct rg_file_key executable
char executable_path[RG_FILENAME_LEN]

References capture_exec_context(), rg_exec_context::executable, rg_exec_context::executable_path, and rg_exec_context::start_boottime_ns.

Referenced by BPF_PROG(), and capture_exec_context().

◆ consume_pending_path()

__always_inline bool consume_pending_path ( const struct rg_file_key * file_key,
char * destination,
__u32 * path_resolution )
static

Definition at line 536 of file rootguard.bpf.c.

539{
540 __u64 ids = bpf_get_current_pid_tgid();
541 struct rg_pending_path *pending =
542 bpf_map_lookup_elem(&pending_paths, &ids);
543
544 if (!pending)
545 return false;
546
547 if (!file_key_equal(file_key, &pending->file)) {
548 bpf_map_delete_elem(&pending_paths, &ids);
549 return false;
550 }
551
552 __builtin_memcpy_inline(destination, pending->path, RG_FILENAME_LEN);
553 *path_resolution = RG_PATH_LSM_EXACT;
554 bpf_map_delete_elem(&pending_paths, &ids);
555 return true;
556}
static __always_inline bool file_key_equal(const struct rg_file_key *left, const struct rg_file_key *right)
@ RG_PATH_LSM_EXACT
#define RG_FILENAME_LEN
struct rg_file_key file
char path[RG_FILENAME_LEN]

References consume_pending_path(), rg_pending_path::file, file_key_equal(), rg_pending_path::path, RG_FILENAME_LEN, and RG_PATH_LSM_EXACT.

Referenced by consume_pending_path(), and emit_event().

◆ consume_token_for()

__always_inline bool consume_token_for ( __u32 tgid,
__u32 ppid,
__u32 target_euid )
static

Definition at line 675 of file rootguard.bpf.c.

677{
678 __u64 now = bpf_ktime_get_ns();
679 struct rg_auth_token *token;
680 bool valid;
681
682 token = bpf_map_lookup_elem(&auth_tokens, &tgid);
683 if (token) {
684 valid = token->expires_ns >= now && token->target_euid == target_euid;
685 bpf_map_delete_elem(&auth_tokens, &tgid);
686 if (valid)
687 return true;
688 }
689
690 token = bpf_map_lookup_elem(&auth_tokens, &ppid);
691 if (token) {
692 valid = token->expires_ns >= now && token->target_euid == target_euid;
693 bpf_map_delete_elem(&auth_tokens, &ppid);
694 if (valid)
695 return true;
696 }
697
698 return false;
699}

References consume_token_for(), rg_auth_token::expires_ns, and rg_auth_token::target_euid.

Referenced by BPF_PROG(), BPF_PROG(), and consume_token_for().

◆ copy_map_string()

__always_inline void copy_map_string ( char * destination,
const char * source,
__u32 size )
static

Definition at line 435 of file rootguard.bpf.c.

438{
439 if (!destination || !source || size == 0)
440 return;
441 if (size == RG_FILENAME_LEN)
442 __builtin_memcpy_inline(destination, source, RG_FILENAME_LEN);
443 else if (size == RG_COMM_LEN)
444 __builtin_memcpy_inline(destination, source, RG_COMM_LEN);
445}
#define RG_COMM_LEN

References copy_map_string(), RG_COMM_LEN, and RG_FILENAME_LEN.

Referenced by copy_map_string(), and enrich_event_actor().

◆ cred_euid()

__always_inline __u32 cred_euid ( const struct cred * cred)
static

Definition at line 197 of file rootguard.bpf.c.

198{
199 kuid_t euid = {};
200
201 if (!cred)
202 return (__u32)-1;
203
204 BPF_CORE_READ_INTO(&euid, cred, euid);
205 return euid.val;
206}

References cred_euid().

Referenced by BPF_PROG(), BPF_PROG(), BPF_PROG(), cred_euid(), current_euid_value(), guard_current_root_provenance(), and observe_current_task().

◆ cred_uid()

__always_inline __u32 cred_uid ( const struct cred * cred)
static

Definition at line 186 of file rootguard.bpf.c.

187{
188 kuid_t uid = {};
189
190 if (!cred)
191 return (__u32)-1;
192
193 BPF_CORE_READ_INTO(&uid, cred, uid);
194 return uid.val;
195}

References cred_uid().

Referenced by BPF_PROG(), BPF_PROG(), BPF_PROG(), cred_uid(), guard_current_root_provenance(), and observe_current_task().

◆ current_cred_ptr()

__always_inline const struct cred * current_cred_ptr ( void )
static

Definition at line 208 of file rootguard.bpf.c.

209{
210 struct task_struct *task =
211 (struct task_struct *)bpf_get_current_task_btf();
212
213 return BPF_CORE_READ(task, cred);
214}

References current_cred_ptr().

Referenced by BPF_PROG(), current_cred_ptr(), and current_euid_value().

◆ current_euid_value()

__always_inline __u32 current_euid_value ( void )
static

Definition at line 216 of file rootguard.bpf.c.

217{
218 return cred_euid(current_cred_ptr());
219}

References cred_euid(), current_cred_ptr(), and current_euid_value().

Referenced by BPF_PROG(), and current_euid_value().

◆ current_executable_key()

__always_inline bool current_executable_key ( struct rg_file_key * key)
static

Definition at line 391 of file rootguard.bpf.c.

392{
393 struct task_struct *task;
394 struct mm_struct *mm;
395 struct file *file;
396
397 task = (struct task_struct *)bpf_get_current_task_btf();
398 mm = BPF_CORE_READ(task, mm);
399 if (!mm)
400 return false;
401
402 file = BPF_CORE_READ(mm, exe_file);
403 return file_key_from_file(file, key);
404}

References current_executable_key(), and file_key_from_file().

Referenced by BPF_PROG(), current_executable_key(), enrich_event_actor(), guard_current_root_provenance(), and observe_unprotected_metadata().

◆ current_ids()

__always_inline void current_ids ( __u32 * pid,
__u32 * tgid,
__u32 * ppid )
static

Definition at line 221 of file rootguard.bpf.c.

222{
223 __u64 id = bpf_get_current_pid_tgid();
224 struct task_struct *task = (struct task_struct *)bpf_get_current_task_btf();
225 struct task_struct *parent = BPF_CORE_READ(task, real_parent);
226
227 *pid = (__u32)id;
228 *tgid = (__u32)(id >> 32);
229 *ppid = parent ? (__u32)BPF_CORE_READ(parent, tgid) : 0;
230}

References current_ids().

Referenced by BPF_PROG(), BPF_PROG(), current_ids(), and emit_event().

◆ decide_escalation()

__always_inline __u32 decide_escalation ( const struct rg_file_key * file_key,
bool have_file_key,
bool authorized,
__u32 * reason_flags,
__u32 * rule_id )
static

Definition at line 701 of file rootguard.bpf.c.

704{
705 const struct rg_kernel_config *cfg;
706 struct rg_rule_value *rule;
707
708 *reason_flags = RG_REASON_NONE;
709 *rule_id = 0;
710
711 if (authorized) {
712 *reason_flags = RG_REASON_AUTH_TOKEN;
714 }
715
716 if (!have_file_key) {
718 } else {
719 rule = bpf_map_lookup_elem(&denied_execs, file_key);
720 if (rule) {
721 *reason_flags = RG_REASON_DENIED_EXEC;
722 *rule_id = rule->rule_id;
723 cfg = kernel_config();
724 return cfg && cfg->mode == RG_MODE_ENFORCE
727 }
728
729 rule = bpf_map_lookup_elem(&trusted_execs, file_key);
730 if (rule) {
731 *reason_flags = RG_REASON_TRUSTED_EXEC;
732 *rule_id = rule->rule_id;
733 return RG_VERDICT_TRUSTED;
734 }
735
736 *reason_flags = RG_REASON_UNKNOWN_EXEC;
737 }
738
739 cfg = kernel_config();
740 if (cfg && cfg->mode == RG_MODE_ENFORCE &&
742 return RG_VERDICT_BLOCKED;
743
744 return RG_VERDICT_AUDIT;
745}
@ RG_ACTION_BLOCK
@ RG_REASON_TRUSTED_EXEC
@ RG_REASON_AUTH_TOKEN
@ RG_REASON_FILE_ID_UNAVAILABLE
@ RG_REASON_NONE
@ RG_REASON_DENIED_EXEC
@ RG_REASON_UNKNOWN_EXEC
@ RG_MODE_ENFORCE
@ RG_VERDICT_AUDIT
@ RG_VERDICT_BLOCKED

References decide_escalation(), kernel_config(), rg_kernel_config::mode, RG_ACTION_BLOCK, RG_MODE_ENFORCE, RG_REASON_AUTH_TOKEN, RG_REASON_DENIED_EXEC, RG_REASON_FILE_ID_UNAVAILABLE, RG_REASON_NONE, RG_REASON_TRUSTED_EXEC, RG_REASON_UNKNOWN_EXEC, RG_VERDICT_AUDIT, RG_VERDICT_AUTHORIZED, RG_VERDICT_BLOCKED, RG_VERDICT_TRUSTED, rg_rule_value::rule_id, and rg_kernel_config::unknown_action.

Referenced by BPF_PROG(), BPF_PROG(), and decide_escalation().

◆ dentry_name_ptr()

__always_inline const char * dentry_name_ptr ( const struct dentry * dentry)
static

Definition at line 384 of file rootguard.bpf.c.

385{
386 if (!dentry)
387 return 0;
388 return (const char *)BPF_CORE_READ(dentry, d_name.name);
389}

References dentry_name_ptr().

Referenced by BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), dentry_name_ptr(), and handle_xattr_change().

◆ discard_pending_path()

__always_inline void discard_pending_path ( void )
static

Definition at line 507 of file rootguard.bpf.c.

508{
509 __u64 ids = bpf_get_current_pid_tgid();
510 bpf_map_delete_elem(&pending_paths, &ids);
511}

References discard_pending_path().

Referenced by BPF_PROG(), and discard_pending_path().

◆ emit_event()

__always_inline void emit_event ( __u32 type,
__u32 verdict,
__u32 reason_flags,
__u32 rule_id,
__u32 old_euid,
__u32 new_euid,
__s32 flags,
__u32 auxiliary,
const struct rg_file_key * file_key,
const char * kernel_filename,
const char * kernel_xattr,
__u32 old_mode,
__u32 new_mode,
__u32 old_uid,
__u32 new_uid,
__u32 old_gid,
__u32 new_gid )
static

Definition at line 763 of file rootguard.bpf.c.

769{
770 const struct rg_kernel_config *cfg;
771 struct rg_event *event;
772
774 cfg = kernel_config();
775 if (cfg && cfg->log_trusted == 0)
776 return;
777 }
778
779 event = bpf_ringbuf_reserve(&events, sizeof(*event), 0);
780 if (!event)
781 return;
782
783 __builtin_memset(event, 0, sizeof(*event));
784 event->timestamp_ns = bpf_ktime_get_ns();
785 current_ids(&event->pid, &event->tgid, &event->ppid);
786 event->old_euid = old_euid;
787 event->new_euid = new_euid;
788 event->event_type = type;
789 event->verdict = verdict;
790 event->reason_flags = reason_flags;
791 event->rule_id = rule_id;
792 event->lsm_flags = flags;
793 event->auxiliary = auxiliary;
794 event->old_mode = old_mode;
795 event->new_mode = new_mode;
796 event->old_uid = old_uid;
797 event->new_uid = new_uid;
798 event->old_gid = old_gid;
799 event->new_gid = new_gid;
800 if (file_key)
801 event->file = *file_key;
802 bpf_get_current_comm(event->comm, sizeof(event->comm));
803 enrich_event_actor(event);
804
805 if (!consume_pending_path(file_key, event->filename,
806 &event->path_resolution) &&
807 kernel_filename) {
808 bpf_probe_read_kernel_str(event->filename,
809 sizeof(event->filename),
810 kernel_filename);
811 event->path_resolution = RG_PATH_BASENAME;
812 }
813 if (kernel_xattr)
814 bpf_probe_read_kernel_str(event->xattr_name,
815 sizeof(event->xattr_name),
816 kernel_xattr);
817
818 bpf_ringbuf_submit(event, 0);
819}
static __always_inline bool consume_pending_path(const struct rg_file_key *file_key, char *destination, __u32 *path_resolution)
static __always_inline void enrich_event_actor(struct rg_event *event)
@ RG_PATH_BASENAME
char filename[RG_FILENAME_LEN]
char xattr_name[RG_XATTR_NAME_LEN]
char comm[RG_COMM_LEN]
__u32 path_resolution

References rg_event::auxiliary, rg_event::comm, consume_pending_path(), current_ids(), emit_event(), enrich_event_actor(), rg_event::filename, kernel_config(), rg_kernel_config::log_trusted, rg_event::new_euid, rg_event::new_gid, rg_event::new_mode, rg_event::new_uid, rg_event::old_euid, rg_event::old_gid, rg_event::old_mode, rg_event::old_uid, rg_event::path_resolution, rg_event::pid, rg_event::ppid, rg_event::reason_flags, RG_PATH_BASENAME, RG_VERDICT_TRUSTED, rg_event::rule_id, rg_event::tgid, rg_event::verdict, and rg_event::xattr_name.

Referenced by BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), emit_event(), guard_current_root_provenance(), and handle_xattr_change().

◆ enrich_event_actor()

__always_inline void enrich_event_actor ( struct rg_event * event)
static

Definition at line 558 of file rootguard.bpf.c.

559{
560 struct task_struct *task;
561 struct task_struct *parent;
562 struct rg_exec_context *context;
563 struct rg_exec_context *parent_context;
564 __u32 tgid;
565
566 if (!event)
567 return;
568
569 tgid = event->tgid;
570 if (current_executable_key(&event->actor_file)) {
571 /* Exact executable identity is always captured at event time. */
572 }
573
574 task = (struct task_struct *)bpf_get_current_task_btf();
575 if (task) {
576 event->actor_start_boottime_ns =
577 BPF_CORE_READ(task, start_boottime);
578 parent = BPF_CORE_READ(task, real_parent);
579 if (parent)
580 bpf_core_read_str(event->parent_comm,
581 sizeof(event->parent_comm),
582 &parent->comm);
583 }
584
585 context = bpf_map_lookup_elem(&exec_contexts, &tgid);
586 if (context) {
587 if (event->actor_file.inode == 0)
588 event->actor_file = context->executable;
589 if (event->actor_start_boottime_ns == 0)
590 event->actor_start_boottime_ns = context->start_boottime_ns;
593 }
594
595 if (event->ppid == 0)
596 return;
597 parent_context = bpf_map_lookup_elem(&exec_contexts, &event->ppid);
598 if (!parent_context)
599 return;
600 event->parent_file = parent_context->executable;
602 parent_context->executable_path,
604}
static __always_inline void copy_map_string(char *destination, const char *source, __u32 size)
struct rg_file_key actor_file
char parent_executable[RG_FILENAME_LEN]
char actor_executable[RG_FILENAME_LEN]
__u64 actor_start_boottime_ns
char parent_comm[RG_COMM_LEN]

References rg_event::actor_executable, rg_event::actor_file, rg_event::actor_start_boottime_ns, copy_map_string(), current_executable_key(), enrich_event_actor(), rg_exec_context::executable, rg_exec_context::executable_path, rg_file_key::inode, rg_event::parent_comm, rg_event::parent_executable, rg_event::ppid, RG_FILENAME_LEN, and rg_exec_context::start_boottime_ns.

Referenced by emit_event(), and enrich_event_actor().

◆ file_key_equal()

__always_inline bool file_key_equal ( const struct rg_file_key * left,
const struct rg_file_key * right )
static

Definition at line 427 of file rootguard.bpf.c.

429{
430 return left && right &&
431 left->device == right->device &&
432 left->inode == right->inode;
433}

References rg_file_key::device, file_key_equal(), and rg_file_key::inode.

Referenced by consume_pending_path(), file_key_equal(), and pending_path_is_under_run().

◆ file_key_from_dentry()

__always_inline bool file_key_from_dentry ( const struct dentry * dentry,
struct rg_file_key * key )
static

Definition at line 372 of file rootguard.bpf.c.

374{
375 struct inode *inode;
376
377 if (!dentry || !key)
378 return false;
379
380 inode = BPF_CORE_READ(dentry, d_inode);
381 return file_key_from_inode(inode, key);
382}

References file_key_from_dentry(), and file_key_from_inode().

Referenced by BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), file_key_from_dentry(), handle_xattr_change(), and record_pending_path().

◆ file_key_from_file()

__always_inline bool file_key_from_file ( const struct file * file,
struct rg_file_key * key )
static

Definition at line 360 of file rootguard.bpf.c.

362{
363 struct inode *inode;
364
365 if (!file || !key)
366 return false;
367
368 inode = BPF_CORE_READ(file, f_inode);
369 return file_key_from_inode(inode, key);
370}

References file_key_from_file(), and file_key_from_inode().

Referenced by BPF_PROG(), BPF_PROG(), current_executable_key(), file_key_from_file(), and parent_executable_key().

◆ file_key_from_inode()

__always_inline bool file_key_from_inode ( const struct inode * inode,
struct rg_file_key * key )
static

Definition at line 343 of file rootguard.bpf.c.

345{
346 struct super_block *superblock;
347
348 if (!inode || !key)
349 return false;
350
351 superblock = BPF_CORE_READ(inode, i_sb);
352 if (!superblock)
353 return false;
354
355 key->inode = BPF_CORE_READ(inode, i_ino);
356 key->device = BPF_CORE_READ(superblock, s_dev);
357 return key->inode != 0;
358}

References rg_file_key::device, file_key_from_inode(), and rg_file_key::inode.

Referenced by BPF_PROG(), file_key_from_dentry(), file_key_from_file(), and file_key_from_inode().

◆ guard_current_root_provenance()

__always_inline int guard_current_root_provenance ( __u32 auxiliary,
__s32 operation_flags,
const struct rg_file_key * file_key )
static

Definition at line 998 of file rootguard.bpf.c.

1001{
1002 struct task_struct *task;
1003 const struct cred *cred;
1004 struct rg_task_state *state;
1005 const struct rg_kernel_config *cfg;
1006 __u64 key;
1007 __u32 uid, euid, verdict;
1008
1009 task = (struct task_struct *)bpf_get_current_task_btf();
1010 cred = BPF_CORE_READ(task, cred);
1011 uid = cred_uid(cred);
1012 euid = cred_euid(cred);
1013
1014 if (euid != 0) {
1015 observe_task(task, uid, euid);
1016 return 0;
1017 }
1018
1019 key = task_key(task);
1020 state = bpf_map_lookup_elem(&task_states, &key);
1021 if (!state || !state->observed_nonroot || state->authorized_root)
1022 return 0;
1023
1024 /*
1025 * File-based setuid credentials can already be active before
1026 * bprm_check_security observes the final EUID. Resolve the current
1027 * executable here as a verifier-safe fallback. The userspace loader
1028 * admits only exact, root-owned, non-writable device+inode identities.
1029 */
1030 {
1031 struct rg_file_key executable_key = {};
1032 struct rg_rule_value *trusted_rule;
1033
1034 if (current_executable_key(&executable_key)) {
1035 trusted_rule = bpf_map_lookup_elem(&trusted_execs,
1036 &executable_key);
1037 if (trusted_rule) {
1038 state->authorized_root = 1;
1039 state->anomaly_reported = 0;
1040 return 0;
1041 }
1042 }
1043 }
1044
1045 cfg = kernel_config();
1046 verdict = cfg && cfg->mode == RG_MODE_ENFORCE &&
1050
1051 if (!state->anomaly_reported) {
1052 state->anomaly_reported = 1;
1055 state->baseline_uid, euid, operation_flags,
1056 auxiliary, file_key, 0, 0,
1057 0, 0, uid, uid, 0, 0);
1058 }
1059
1060 return lsm_result_from_verdict(verdict);
1061}
@ RG_REASON_DIRECT_CRED_ANOMALY
@ RG_EVENT_CREDENTIAL_ANOMALY
__u32 anomaly_reported
__u32 authorized_root

References rg_task_state::anomaly_reported, rg_task_state::authorized_root, rg_task_state::baseline_uid, cred_euid(), cred_uid(), rg_kernel_config::credential_action, current_executable_key(), emit_event(), guard_current_root_provenance(), kernel_config(), lsm_result_from_verdict(), rg_kernel_config::mode, observe_task(), rg_task_state::observed_nonroot, RG_ACTION_BLOCK, RG_EVENT_CREDENTIAL_ANOMALY, RG_MODE_ENFORCE, RG_REASON_DIRECT_CRED_ANOMALY, RG_VERDICT_AUDIT, RG_VERDICT_BLOCKED, and task_key().

Referenced by BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), guard_current_root_provenance(), and handle_xattr_change().

◆ handle_xattr_change()

__always_inline int handle_xattr_change ( struct dentry * dentry,
const char * name,
int operation_flags )
static

Definition at line 1299 of file rootguard.bpf.c.

1302{
1303 struct rg_file_key key = {};
1304 struct rg_protected_value *protected;
1305 const struct rg_kernel_config *cfg;
1306 struct inode *inode;
1307 __u32 current_mode = 0, current_uid = 0, current_gid = 0;
1308 __u32 reasons = 0, protected_reasons = 0, verdict, metadata_decision = 0;
1309 int kind;
1310
1311 int guard = guard_current_root_provenance(0, operation_flags, 0);
1312 if (guard)
1313 return guard;
1314 if (!file_key_from_dentry(dentry, &key))
1315 return 0;
1316
1317 inode = BPF_CORE_READ(dentry, d_inode);
1318 inode_metadata(inode, &current_mode, &current_uid, &current_gid);
1319
1320 kind = xattr_kind(name);
1321 if (!kind)
1322 return 0;
1323
1324 protected = bpf_map_lookup_elem(&protected_inodes, &key);
1325 if (protected) {
1326 if (kind == 1 &&
1327 (protected->flags & RG_PROTECT_CAPABILITY_XATTR))
1328 protected_reasons |= RG_REASON_SECURITY_CAPABILITY;
1329 else if (kind == 2 &&
1330 (protected->flags & RG_PROTECT_INTEGRITY_XATTR))
1331 protected_reasons |= RG_REASON_INTEGRITY_XATTR;
1332 else if (kind == 3 &&
1333 (protected->flags & RG_PROTECT_SECURITY_LABEL))
1334 protected_reasons |= RG_REASON_SECURITY_LABEL;
1335 }
1336
1337 cfg = kernel_config();
1338 if (!protected && kind == 1 && operation_flags >= 0 && cfg &&
1340 metadata_decision = observe_unprotected_metadata(false, true, 0);
1341 if (kind == 1 && cfg && cfg->monitor_global_privilege_metadata &&
1342 (protected || (metadata_decision & RG_METADATA_DECISION_SAMPLE)))
1344
1345 reasons |= protected_reasons;
1346 if (!reasons)
1347 return 0;
1348
1349 verdict = metadata_verdict(
1350 protected_reasons != 0,
1351 protected && (protected->flags & RG_PROTECT_AUDIT_ONLY),
1352 (reasons & RG_REASON_GLOBAL_METADATA) != 0);
1353 emit_event(RG_EVENT_XATTR_CHANGE, verdict, reasons,
1354 protected ? protected->rule_id : 0,
1355 0, 0, operation_flags, (__u32)kind,
1356 &key, dentry_name_ptr(dentry), name,
1357 current_mode, current_mode,
1358 current_uid, current_uid, current_gid, current_gid);
1359 return lsm_result_from_verdict(verdict);
1360}
static __always_inline int xattr_kind(const char *name)
@ RG_REASON_SECURITY_LABEL
@ RG_REASON_SECURITY_CAPABILITY
@ RG_REASON_INTEGRITY_XATTR
@ RG_EVENT_XATTR_CHANGE
@ RG_PROTECT_INTEGRITY_XATTR
@ RG_PROTECT_CAPABILITY_XATTR
@ RG_PROTECT_SECURITY_LABEL

References dentry_name_ptr(), emit_event(), file_key_from_dentry(), guard_current_root_provenance(), handle_xattr_change(), inode_metadata(), kernel_config(), lsm_result_from_verdict(), metadata_verdict(), rg_kernel_config::monitor_global_privilege_metadata, observe_unprotected_metadata(), RG_EVENT_XATTR_CHANGE, RG_METADATA_DECISION_SAMPLE, RG_PROTECT_AUDIT_ONLY, RG_PROTECT_CAPABILITY_XATTR, RG_PROTECT_INTEGRITY_XATTR, RG_PROTECT_SECURITY_LABEL, RG_REASON_GLOBAL_METADATA, RG_REASON_INTEGRITY_XATTR, RG_REASON_SECURITY_CAPABILITY, RG_REASON_SECURITY_LABEL, and xattr_kind().

Referenced by BPF_PROG(), BPF_PROG(), and handle_xattr_change().

◆ inode_metadata()

__always_inline void inode_metadata ( const struct inode * inode,
__u32 * mode,
__u32 * uid,
__u32 * gid )
static

Definition at line 841 of file rootguard.bpf.c.

843{
844 kuid_t inode_uid = {};
845 kgid_t inode_gid = {};
846
847 *mode = BPF_CORE_READ(inode, i_mode) & 07777U;
848 BPF_CORE_READ_INTO(&inode_uid, inode, i_uid);
849 BPF_CORE_READ_INTO(&inode_gid, inode, i_gid);
850 *uid = inode_uid.val;
851 *gid = inode_gid.val;
852}

References inode_metadata().

Referenced by BPF_PROG(), handle_xattr_change(), and inode_metadata().

◆ kernel_config()

__always_inline const struct rg_kernel_config * kernel_config ( void )
static

Definition at line 232 of file rootguard.bpf.c.

233{
234 __u32 key = 0;
235 return bpf_map_lookup_elem(&rg_config_map, &key);
236}

References kernel_config().

Referenced by BPF_PROG(), decide_escalation(), emit_event(), guard_current_root_provenance(), handle_xattr_change(), kernel_config(), metadata_verdict(), and observe_unprotected_metadata().

◆ lsm_result_from_verdict()

__always_inline int lsm_result_from_verdict ( __u32 verdict)
static

Definition at line 333 of file rootguard.bpf.c.

334{
335 if (verdict == RG_VERDICT_BLOCKED) {
336 asm volatile("" ::: "memory");
337 return -EPERM;
338 }
339
340 return 0;
341}

References EPERM, lsm_result_from_verdict(), and RG_VERDICT_BLOCKED.

Referenced by BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), BPF_PROG(), guard_current_root_provenance(), handle_xattr_change(), and lsm_result_from_verdict().

◆ mark_current_authorized_root()

__always_inline void mark_current_authorized_root ( void )
static

Definition at line 659 of file rootguard.bpf.c.

660{
661 struct task_struct *task =
662 (struct task_struct *)bpf_get_current_task_btf();
663 __u64 key = task_key(task);
664 struct rg_task_state state = {};
665 struct rg_task_state *existing =
666 bpf_map_lookup_elem(&task_states, &key);
667
668 if (existing)
669 state = *existing;
670 state.authorized_root = 1;
671 state.anomaly_reported = 0;
672 bpf_map_update_elem(&task_states, &key, &state, BPF_ANY);
673}

References rg_task_state::anomaly_reported, rg_task_state::authorized_root, mark_current_authorized_root(), and task_key().

Referenced by BPF_PROG(), BPF_PROG(), and mark_current_authorized_root().

◆ metadata_verdict()

__always_inline __u32 metadata_verdict ( bool protected_change,
bool protected_audit_only,
bool global_change )
static

◆ observe_current_task()

__always_inline void observe_current_task ( const struct cred * cred)
static

Definition at line 652 of file rootguard.bpf.c.

653{
654 struct task_struct *task =
655 (struct task_struct *)bpf_get_current_task_btf();
656 observe_task(task, cred_uid(cred), cred_euid(cred));
657}

References cred_euid(), cred_uid(), observe_current_task(), and observe_task().

Referenced by BPF_PROG(), BPF_PROG(), and observe_current_task().

◆ observe_task()

__always_inline void observe_task ( struct task_struct * task,
__u32 uid,
__u32 euid )
static

Definition at line 611 of file rootguard.bpf.c.

613{
614 __u64 key;
615 struct rg_task_state state = {};
616 struct rg_task_state *existing;
617
618 if (!task)
619 return;
620
621 key = task_key(task);
622 existing = bpf_map_lookup_elem(&task_states, &key);
623
624 if (euid != 0) {
625 /*
626 * The effective UID determines whether the task can exercise root
627 * privileges. A legitimate setuid-root process normally keeps the
628 * caller's real UID, so testing uid != 0 here would erase an
629 * authorization immediately after sudo/pkexec gained EUID 0.
630 */
631 /* inode_permission is hot: avoid rewriting an unchanged state. */
632 if (existing && existing->observed_nonroot &&
633 !existing->authorized_root &&
634 existing->baseline_uid == uid)
635 return;
636
637 if (existing)
638 state = *existing;
639 state.observed_nonroot = 1;
640 state.authorized_root = 0;
641 state.anomaly_reported = 0;
642 state.baseline_uid = uid;
643 bpf_map_update_elem(&task_states, &key, &state, BPF_ANY);
644 return;
645 }
646
647 /* Root tasks are inserted by task_alloc/authorized transitions only. */
648 if (!existing)
649 bpf_map_update_elem(&task_states, &key, &state, BPF_ANY);
650}

References rg_task_state::anomaly_reported, rg_task_state::authorized_root, rg_task_state::baseline_uid, observe_task(), rg_task_state::observed_nonroot, and task_key().

Referenced by BPF_PROG(), BPF_PROG(), guard_current_root_provenance(), observe_current_task(), and observe_task().

◆ observe_unprotected_metadata()

__always_inline __u32 observe_unprotected_metadata ( bool count_burst_candidate,
bool request_sample,
__u32 * burst_count )
static

Definition at line 248 of file rootguard.bpf.c.

251{
252 const struct rg_kernel_config *cfg = kernel_config();
253 struct rg_metadata_domain_state initial = {};
254 struct rg_metadata_domain_state *state;
255 struct rg_metadata_domain_key key = {};
256 __u64 now, window_ns, cooldown_ns;
257 __u32 monitor_global, detect_bursts, sample_limit, burst_threshold;
258 __u32 decision = 0;
259
260 if (burst_count)
261 *burst_count = 0;
262 if (!cfg)
263 return 0;
264
265 /* Copy config scalars before taking a map-value spin lock. */
266 monitor_global = cfg->monitor_global_privilege_metadata;
267 detect_bursts = cfg->detect_global_metadata_bursts;
268 sample_limit = cfg->global_metadata_sample_limit;
269 burst_threshold = cfg->metadata_burst_threshold;
270 window_ns = (__u64)cfg->metadata_burst_window_ms * 1000000ULL;
271 cooldown_ns = (__u64)cfg->metadata_burst_cooldown_ms * 1000000ULL;
272 if (!monitor_global && !detect_bursts)
273 return 0;
274
275 key.cgroup_id = bpf_get_current_cgroup_id();
276 if (!current_executable_key(&key.actor))
277 key.tgid_fallback = (__u32)(bpf_get_current_pid_tgid() >> 32);
278 (void)parent_executable_key(&key.parent);
279 now = bpf_ktime_get_ns();
280 if (window_ns == 0)
281 window_ns = 5000000000ULL;
282 if (cooldown_ns < window_ns)
283 cooldown_ns = window_ns;
284
285 state = bpf_map_lookup_elem(&metadata_domain_states, &key);
286 if (!state) {
287 initial.window_start_ns = now;
288 bpf_map_update_elem(&metadata_domain_states, &key, &initial, BPF_NOEXIST);
289 state = bpf_map_lookup_elem(&metadata_domain_states, &key);
290 if (!state)
291 return 0;
292 }
293
294 /* Multiple workers in one service/cgroup may run on different CPUs. */
295 bpf_spin_lock(&state->lock);
296
297 if (state->window_start_ns == 0 ||
298 now - state->window_start_ns >= window_ns) {
299 state->window_start_ns = now;
300 state->destructive_count = 0;
301 state->sampled_events = 0;
302 }
303
304 if (request_sample && monitor_global &&
305 state->sampled_events < sample_limit) {
306 ++state->sampled_events;
307 decision |= RG_METADATA_DECISION_SAMPLE;
308 }
309
310 if (count_burst_candidate && detect_bursts) {
311 if (state->destructive_count != (__u32)-1)
312 ++state->destructive_count;
313 if (state->destructive_count >= burst_threshold &&
314 (state->last_burst_ns == 0 ||
315 now - state->last_burst_ns >= cooldown_ns)) {
316 state->last_burst_ns = now;
317 decision |= RG_METADATA_DECISION_BURST;
318 if (burst_count)
319 *burst_count = state->destructive_count;
320 }
321 }
322
323 bpf_spin_unlock(&state->lock);
324 return decision;
325}
static __always_inline bool parent_executable_key(struct rg_file_key *key)
__u32 detect_global_metadata_bursts
struct rg_file_key parent
struct rg_file_key actor
struct bpf_spin_lock lock

References rg_metadata_domain_key::actor, rg_metadata_domain_key::cgroup_id, current_executable_key(), rg_metadata_domain_state::destructive_count, rg_kernel_config::detect_global_metadata_bursts, rg_kernel_config::global_metadata_sample_limit, kernel_config(), rg_metadata_domain_state::last_burst_ns, rg_metadata_domain_state::lock, rg_kernel_config::metadata_burst_cooldown_ms, rg_kernel_config::metadata_burst_threshold, rg_kernel_config::metadata_burst_window_ms, rg_kernel_config::monitor_global_privilege_metadata, observe_unprotected_metadata(), rg_metadata_domain_key::parent, parent_executable_key(), RG_METADATA_DECISION_BURST, RG_METADATA_DECISION_SAMPLE, rg_metadata_domain_state::sampled_events, rg_metadata_domain_key::tgid_fallback, and rg_metadata_domain_state::window_start_ns.

Referenced by BPF_PROG(), handle_xattr_change(), and observe_unprotected_metadata().

◆ parent_executable_key()

__always_inline bool parent_executable_key ( struct rg_file_key * key)
static

Definition at line 406 of file rootguard.bpf.c.

407{
408 struct task_struct *task, *parent;
409 struct mm_struct *mm;
410 struct file *file;
411
412 if (!key)
413 return false;
414 task = (struct task_struct *)bpf_get_current_task_btf();
415 if (!task)
416 return false;
417 parent = BPF_CORE_READ(task, real_parent);
418 if (!parent)
419 return false;
420 mm = BPF_CORE_READ(parent, mm);
421 if (!mm)
422 return false;
423 file = BPF_CORE_READ(mm, exe_file);
424 return file_key_from_file(file, key);
425}

References file_key_from_file(), and parent_executable_key().

Referenced by observe_unprotected_metadata(), and parent_executable_key().

◆ pending_path_is_under_run()

__always_inline bool pending_path_is_under_run ( const struct rg_file_key * file_key)
static

Definition at line 494 of file rootguard.bpf.c.

496{
497 __u64 ids = bpf_get_current_pid_tgid();
498 struct rg_pending_path *pending =
499 bpf_map_lookup_elem(&pending_paths, &ids);
500
501 if (!pending || !file_key_equal(file_key, &pending->file))
502 return false;
503
504 return runtime_path_is_run(pending->path);
505}
static __always_inline bool runtime_path_is_run(const char *path)

References rg_pending_path::file, file_key_equal(), rg_pending_path::path, pending_path_is_under_run(), and runtime_path_is_run().

Referenced by BPF_PROG(), and pending_path_is_under_run().

◆ record_pending_path()

__always_inline void record_pending_path ( const struct path * path,
__u32 operation )
static

Definition at line 513 of file rootguard.bpf.c.

515{
516 struct rg_pending_path pending = {};
517 __u64 ids;
518
519 if (!path || !bpf_path_d_path)
520 return;
521 if (!file_key_from_dentry(BPF_CORE_READ(path, dentry), &pending.file))
522 return;
523 /*
524 * Older BTF declares the first argument as struct path *, while newer
525 * kernels const-qualify it. A mutable pointer is accepted by both forms.
526 */
527 if (bpf_path_d_path((struct path *)path, pending.path,
528 sizeof(pending.path)) <= 0)
529 return;
530
531 pending.operation = operation;
532 ids = bpf_get_current_pid_tgid();
533 bpf_map_update_elem(&pending_paths, &ids, &pending, BPF_ANY);
534}
int bpf_path_d_path(const struct path *path, char *buf, size_t buf__sz) __weak __ksym

References bpf_path_d_path(), rg_pending_path::file, file_key_from_dentry(), rg_pending_path::operation, rg_pending_path::path, and record_pending_path().

Referenced by BPF_PROG(), BPF_PROG2(), and record_pending_path().

◆ rg_sched_process_exit()

int rg_sched_process_exit ( struct trace_event_raw_sched_process_template * ctx)

Definition at line 1516 of file rootguard.bpf.c.

1517{
1518 __u64 ids = bpf_get_current_pid_tgid();
1519 __u32 pid = (__u32)ids;
1520 __u32 tgid = (__u32)(ids >> 32);
1521
1522 if (pid == tgid)
1523 bpf_map_delete_elem(&exec_contexts, &tgid);
1524 bpf_map_delete_elem(&pending_paths, &ids);
1525 return 0;
1526}

References rg_sched_process_exit().

Referenced by rg_sched_process_exit().

◆ runtime_path_is_run()

__always_inline bool runtime_path_is_run ( const char * path)
static

Definition at line 484 of file rootguard.bpf.c.

485{
486 if (!path)
487 return false;
488
489 /* Match /run and /run/... but not /runtime, /runner, etc. */
490 return path[0] == '/' && path[1] == 'r' && path[2] == 'u' &&
491 path[3] == 'n' && (path[4] == '\0' || path[4] == '/');
492}

References runtime_path_is_run().

Referenced by pending_path_is_under_run(), and runtime_path_is_run().

◆ SEC() [1/2]

struct @046352274040072010274252261242260131256021005134 SEC ( ".maps" )

◆ SEC() [2/2]

char LICENSE[] SEC ( "license" )

References SEC.

◆ task_key()

__always_inline __u64 task_key ( const struct task_struct * task)
static

Definition at line 606 of file rootguard.bpf.c.

607{
608 return (__u64)(unsigned long)task;
609}

References task_key().

Referenced by BPF_PROG(), guard_current_root_provenance(), mark_current_authorized_root(), observe_task(), and task_key().

◆ xattr_kind()

__always_inline int xattr_kind ( const char * name)
static

Definition at line 821 of file rootguard.bpf.c.

822{
823 char value[RG_XATTR_NAME_LEN] = {};
824
825 if (!name)
826 return 0;
827 if (bpf_probe_read_kernel_str(value, sizeof(value), name) <= 0)
828 return 0;
829
830 if (__builtin_memcmp(value, "security.capability", 20) == 0)
831 return 1;
832 if (__builtin_memcmp(value, "security.ima", 13) == 0 ||
833 __builtin_memcmp(value, "security.evm", 13) == 0)
834 return 2;
835 if (__builtin_memcmp(value, "security.selinux", 17) == 0 ||
836 __builtin_memcmp(value, "security.apparmor", 18) == 0)
837 return 3;
838 return 0;
839}
#define RG_XATTR_NAME_LEN

References RG_XATTR_NAME_LEN, and xattr_kind().

Referenced by handle_xattr_change(), and xattr_kind().

Variable Documentation

◆ SEC

struct { ... } SEC

Referenced by SEC().