[PATCH v3 2/7] add support for the uring filter list

Richard Guy Briggs
2021-10-28T20:06:00Z

Kernel support to audit io_uring operations filtering was added with
commit 67daf270cebc ("audit: add filtering for io_uring records"). Add support
for the "uring" filter list to auditctl.

Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
---
docs/audit.rules.7 | 19 ++++--
docs/audit_add_rule_data.3 | 4 ++
docs/auditctl.8 | 10 ++-
lib/flagtab.h | 11 ++--
lib/libaudit.c | 50 ++++++++++++---
lib/libaudit.h | 7 +++
lib/lookup_table.c | 20 ++++++
lib/private.h | 1 +
src/auditctl-listing.c | 52 ++++++++++------
src/auditctl.c | 121 ++++++++++++++++++++++++++++++++-----
10 files changed, 240 insertions(+), 55 deletions(-)

diff --git a/docs/audit.rules.7 b/docs/audit.rules.7
index 40263ec6807d..ec4fa7ececc7 100644
--- a/docs/audit.rules.7
+++ b/docs/audit.rules.7
@@ -3,7 +3,7 @@
audit.rules \- a set of rules loaded in the kernel audit system
.SH DESCRIPTION
\fBaudit.rules\fP is a file containing audit rules that will be loaded by the audit daemon's init script whenever the daemon is started. The auditctl program is used by the initscripts to perform this operation. The syntax for the rules is essentially the same as when typing in an auditctl command at a shell prompt except you do not need to type the auditctl command name since that is implied. The audit rules come in 3 varieties:
-.IR control ", " file ", and " syscall ".
+.IR control ", " file ", and " syscall/uringop ".

.SS Control
Control commands generally involve configuring the audit system rather than telling it what to watch for. These commands typically include deleting all rules, setting the size of the kernel's backlog queue, setting the failure mode, setting the event rate limit, or to tell auditctl to ignore syntax errors in the rules and continue loading. Generally, these rules are at the top of the rules file.
@@ -32,7 +32,7 @@ where the permission are any one of the following:
- change in the file's attribute
.RE

-Watches can also be created using the syscall format described below which allow for greater flexibility and options. Using syscall rules you can choose between
+Watches can also be created using the syscall format described below which allow for greater flexibility and options. Using syscall/uringop rules you can choose between
.B path
and
.B dir
@@ -43,9 +43,9 @@ rule.
.SS System Call
The system call rules are loaded into a matching engine that intercepts each syscall that all programs on the system makes. Therefore it is very important to only use syscall rules when you have to since these affect performance. The more rules, the bigger the performance hit. You can help the performance, though, by combining syscalls into one rule whenever possible.

-The Linux kernel has 5 rule matching lists or filters as they are sometimes called. They are: task, exit, user, exclude and filesystem. The task list is checked only during the fork or clone syscalls. It is rarely used in practice.
+The Linux kernel has 6 rule matching lists or filters as they are sometimes called. They are: task, exit, user, exclude, filesystem, and uring. The task list is checked only during the fork or clone syscalls. It is rarely used in practice.

-The exit filter is the place where all syscall and file system audit requests are evaluated.
+The exit filter is the place where all syscall and file system audit requests are evaluated. The uring filter is the place where all uring operations and file system audit requests are evaluated.

The user filter is used to filter (remove) some events that originate in user space. By default, any event originating in user space is allowed. So, if there are some events that you do not want to see, then this is a place where some can be removed. See auditctl(8) for fields that are valid.

@@ -71,7 +71,7 @@ option tells the kernel's rule matching engine that we want to append a rule at
.RE

The action and list are separated by a comma but no space in between. Valid lists are:
-.IR task ", " exit ", " user ", " exclude ", and " filesystem ". Their meaning was explained earlier.
+.IR task ", " exit ", " user ", " exclude ", " filesystem ", and " uring ". Their meaning was explained earlier.

Next in the rule would normally be the
.B \-S
@@ -95,6 +95,15 @@ These individual checks are "anded" and both have to be true.

The last thing to know about syscall rules is that you can add a key field which is a free form text string that you want inserted into the event to help identify its meaning. This is discussed in more detail in the NOTES section.

+.SS Uring Operations
+Uring operations are similar to system calls in that they are initiated by user actions, but once the action is set up, information is passed between userspace and kernel space bidirectionally via shared buffers. There is a different list of operations that use the same operations list mechanism so system calls and uring operations are mutually exclusive.
+
+Uring op rules take the general form of:
+
+.nf
+.B \-a action,list \-U uringop \-F field=value \-k keyname
+.fi
+
.SH NOTES
The purpose of auditing is to be able to do an investigation periodically or whenever an incident occurs. A few simple steps in planning up front will make this job easier. The best advice is to use keys in both the watches and system call rules to give the rule a meaning. If rules are related or together meet a specific requirement, then give them a common key name. You can use this during your investigation to select only results with a specific meaning.

diff --git a/docs/audit_add_rule_data.3 b/docs/audit_add_rule_data.3
index 61d1902e702b..e86c3a1b0fef 100644
--- a/docs/audit_add_rule_data.3
+++ b/docs/audit_add_rule_data.3
@@ -27,6 +27,10 @@ AUDIT_FILTER_EXCLUDE - Apply rule at audit_log_start. This is the exclude filter
\(bu
AUDIT_FILTER_FS - Apply rule when adding PATH auxiliary records to SYSCALL events. This is the filesystem filter. This is used to ignore PATH records that are not of interest.
.LP
+.TP
+\(bu
+AUDIT_FILTER_URING_EXIT - Apply rule at uring exit. This is the main filter that is used for uring ops and filesystem watches. Normally all uring ops do not trigger events, so this is normally used to specify events that are of interest.
+.LP

.PP
The rule's action has two possible values:
diff --git a/docs/auditctl.8 b/docs/auditctl.8
index 8069259bcb47..515c5a71f861 100644
--- a/docs/auditctl.8
+++ b/docs/auditctl.8
@@ -92,6 +92,9 @@ Add a rule to the event type exclusion filter list. This list is used to filter
.TP
.B filesystem
Add a rule that will be applied to a whole filesystem. The filesystem must be identified with a fstype field. Normally this filter is used to exclude any events for a whole filesystem such as tracefs or debugfs.
+.TP
+.B uring
+Add a rule to the uring op exit list. This list is used upon exit from a uring operation call to determine if an audit event should be created.
.RE

The following describes the valid \fIactions\fP for the rule:
@@ -101,7 +104,7 @@ The following describes the valid \fIactions\fP for the rule:
No audit records will be generated. This can be used to suppress event generation. In general, you want suppressions at the top of the list instead of the bottom. This is because the event triggers on the first matching rule.
.TP
.B always
-Allocate an audit context, always fill it in at syscall entry time, and always write out a record at syscall exit time.
+Allocate an audit context, always fill it in at syscall/uringop entry time, and always write out a record at syscall/uringop exit time.
.RE
.TP
.BI \-A\ list , action
@@ -120,7 +123,7 @@ The two groups of uid and gid cannot be mixed. But any comparison within the gro

.TP
.BI \-d\ list , action
-Delete rule from \fIlist\fP with \fIaction\fP. The rule is deleted only if it exactly matches syscall name(s) and every field name and value.
+Delete rule from \fIlist\fP with \fIaction\fP. The rule is deleted only if it exactly matches syscall/uringop name(s) and every field name and value.
.TP
\fB\-F\fP [\fIn\fP\fB=\fP\fIv\fP | \fIn\fP\fB!=\fP\fIv\fP | \fIn\fP\fB<\fP\fIv\fP | \fIn\fP\fB>\fP\fIv\fP | \fIn\fP\fB<=\fP\fIv\fP | \fIn\fP\fB>=\fP\fIv\fP | \fIn\fP\fB&\fP\fIv\fP | \fIn\fP\fB&=\fP\fIv\fP]
Build a rule field: name, operation, value. You may have up to 64 fields passed on a single command line. Each one must start with \fB\-F\fP. Each field equation is anded with each other (as well as equations starting with \fB\-C\fP) to trigger an audit record. There are 8 operators supported - equal, not equal, less than, greater than, less than or equal, and greater than or equal, bit mask, and bit test respectively. Bit test will "and" the values and check that they are equal, bit mask just "ands" the values. Fields that take a user ID may instead have the user's name; the program will convert the name to user ID. The same is true of group names. Valid fields are:
@@ -260,6 +263,9 @@ Describe the permission access type that a file system watch will trigger on. \f
\fB\-S\fP [\fISyscall name or number\fP|\fBall\fP]
Any \fIsyscall name\fP or \fInumber\fP may be used. The word '\fBall\fP' may also be used. If the given syscall is made by a program, then start an audit record. If a field rule is given and no syscall is specified, it will default to all syscalls. You may also specify multiple syscalls in the same rule by using multiple \-S options in the same rule. Doing so improves performance since fewer rules need to be evaluated. Alternatively, you may pass a comma separated list of syscall names. If you are on a bi-arch system, like x86_64, you should be aware that auditctl simply takes the text, looks it up for the native arch (in this case b64) and sends that rule to the kernel. If there are no additional arch directives, IT WILL APPLY TO BOTH 32 & 64 BIT SYSCALLS. This can have undesirable effects since there is no guarantee that any syscall has the same number on both 32 and 64 bit interfaces. You will likely want to control this and write 2 rules, one with arch equal to b32 and one with
b64 to make sure the kernel finds the events that you intend. See the arch field discussion for more info.
.TP
+\fB\-U\fP [\fIUring operation name or number\fP|\fBall\fP]
+Any \fIuring operation name\fP or \fInumber\fP may be used. The word '\fBall\fP' may also be used. If the given uring operation is made by a program, then start an audit record. If a field rule is given and no uring operation is specified, it will default to all uring operations. You may also specify multiple uring operations in the same rule by using multiple \-S options in the same rule. Doing so improves performance since fewer rules need to be evaluated. Alternatively, you may pass a comma separated list of uring operation names.
+.TP
.BI \-w\ path
Insert a watch for the file system object at \fIpath\fP. You cannot insert a watch to the top level directory. This is prohibited by the kernel. Wildcards are not supported either and will generate a warning. The way that watches work is by tracking the inode internally. If you place a watch on a file, its the same as using the \-F path option on a syscall rule. If you place a watch on a directory, its the same as using the \-F dir option on a syscall rule. The \-w form of writing watches is for backwards compatibility and the syscall based form is more expressive. Unlike most syscall auditing rules, watches do not impact performance based on the number of rules sent to the kernel. The only valid options when using a watch are the \-p and \-k. If you need to do anything fancy like audit a specific user accessing a file, then use the syscall auditing form with the path or dir fields. See the EXAMPLES section for an example of converting one form to another.
.TP
diff --git a/lib/flagtab.h b/lib/flagtab.h
index 7a618e0fe126..0fa4443e6ce3 100644
--- a/lib/flagtab.h
+++ b/lib/flagtab.h
@@ -20,8 +20,9 @@
* Steve Grubb <sgrubb@redhat.com>
* Richard Guy Briggs <rgb@redhat.com>
*/
-_S(AUDIT_FILTER_TASK, "task" )
-_S(AUDIT_FILTER_EXIT, "exit" )
-_S(AUDIT_FILTER_USER, "user" )
-_S(AUDIT_FILTER_EXCLUDE, "exclude" )
-_S(AUDIT_FILTER_FS, "filesystem")
+_S(AUDIT_FILTER_TASK, "task" )
+_S(AUDIT_FILTER_EXIT, "exit" )
+_S(AUDIT_FILTER_USER, "user" )
+_S(AUDIT_FILTER_EXCLUDE, "exclude" )
+_S(AUDIT_FILTER_FS, "filesystem")
+_S(AUDIT_FILTER_URING_EXIT, "uring" )
diff --git a/lib/libaudit.c b/lib/libaudit.c
index 54e276156ef0..3790444f4497 100644
--- a/lib/libaudit.c
+++ b/lib/libaudit.c
@@ -86,6 +86,7 @@ static const struct nv_list failure_actions[] =
int _audit_permadded = 0;
int _audit_archadded = 0;
int _audit_syscalladded = 0;
+int _audit_uringopadded = 0;
int _audit_exeadded = 0;
int _audit_filterfsadded = 0;
unsigned int _audit_elf = 0U;
@@ -999,6 +1000,26 @@ int audit_rule_syscallbyname_data(struct audit_rule_data *rule,
return -1;
}

+int audit_rule_uringopbyname_data(struct audit_rule_data *rule,
+ const char *uringop)
+{
+ int nr, i;
+
+ if (!strcmp(uringop, "all")) {
+ for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+ rule->mask[i] = ~0;
+ return 0;
+ }
+ nr = audit_name_to_uringop(uringop);
+ if (nr < 0) {
+ if (isdigit(uringop[0]))
+ nr = strtol(uringop, NULL, 0);
+ }
+ if (nr >= 0)
+ return audit_rule_syscall_data(rule, nr);
+ return -1;
+}
+
int audit_rule_interfield_comp_data(struct audit_rule_data **rulep,
const char *pair,
int flags)
@@ -1044,7 +1065,7 @@ int audit_rule_interfield_comp_data(struct audit_rule_data **rulep,
return -EAU_COMPVALUNKNOWN;

/* Interfield comparison can only be in exit filter */
- if (flags != AUDIT_FILTER_EXIT)
+ if (flags != AUDIT_FILTER_EXIT && flags != AUDIT_FILTER_URING_EXIT)
return -EAU_EXITONLY;

// It should always be AUDIT_FIELD_COMPARE
@@ -1557,7 +1578,8 @@ int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair,
}
break;
case AUDIT_EXIT:
- if (flags != AUDIT_FILTER_EXIT)
+ if (flags != AUDIT_FILTER_EXIT &&
+ flags != AUDIT_FILTER_URING_EXIT)
return -EAU_EXITONLY;
vlen = strlen(v);
if (isdigit((char)*(v)))
@@ -1599,7 +1621,8 @@ int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair,
case AUDIT_DIR:
/* Watch & object filtering is invalid on anything
* but exit */
- if (flags != AUDIT_FILTER_EXIT)
+ if (flags != AUDIT_FILTER_EXIT &&
+ flags != AUDIT_FILTER_URING_EXIT)
return -EAU_EXITONLY;
if (field == AUDIT_WATCH || field == AUDIT_DIR)
_audit_permadded = 1;
@@ -1621,9 +1644,11 @@ int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair,
_audit_exeadded = 1;
}
if (field == AUDIT_FILTERKEY &&
- !(_audit_syscalladded || _audit_permadded ||
- _audit_exeadded ||
- _audit_filterfsadded))
+ !(_audit_syscalladded ||
+ _audit_uringopadded ||
+ _audit_permadded ||
+ _audit_exeadded ||
+ _audit_filterfsadded))
return -EAU_KEYDEP;
vlen = strlen(v);
if (field == AUDIT_FILTERKEY &&
@@ -1712,7 +1737,8 @@ int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair,
}
break;
case AUDIT_FILETYPE:
- if (!(flags == AUDIT_FILTER_EXIT))
+ if (!(flags == AUDIT_FILTER_EXIT ||
+ flags == AUDIT_FILTER_URING_EXIT))
return -EAU_EXITONLY;
rule->values[rule->field_count] =
audit_name_to_ftype(v);
@@ -1754,7 +1780,8 @@ int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair,
return -EAU_FIELDNOSUPPORT;
if (flags != AUDIT_FILTER_EXCLUDE &&
flags != AUDIT_FILTER_USER &&
- flags != AUDIT_FILTER_EXIT)
+ flags != AUDIT_FILTER_EXIT &&
+ flags != AUDIT_FILTER_URING_EXIT)
return -EAU_FIELDNOFILTER;
// Do positive & negative separate for 32 bit systems
vlen = strlen(v);
@@ -1775,7 +1802,8 @@ int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair,
break;
case AUDIT_DEVMAJOR...AUDIT_INODE:
case AUDIT_SUCCESS:
- if (flags != AUDIT_FILTER_EXIT)
+ if (flags != AUDIT_FILTER_EXIT &&
+ flags != AUDIT_FILTER_URING_EXIT)
return -EAU_EXITONLY;
/* fallthrough */
default:
@@ -1785,7 +1813,9 @@ int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair,
return -EAU_OPEQNOTEQ;
}

- if (field == AUDIT_PPID && !(flags==AUDIT_FILTER_EXIT))
+ if (field == AUDIT_PPID &&
+ !(flags == AUDIT_FILTER_EXIT ||
+ flags == AUDIT_FILTER_URING_EXIT))
return -EAU_EXITONLY;

if (!isdigit((char)*(v)))
diff --git a/lib/libaudit.h b/lib/libaudit.h
index 08b7d22678aa..a73edc677df0 100644
--- a/lib/libaudit.h
+++ b/lib/libaudit.h
@@ -341,6 +341,9 @@ extern "C" {
#ifndef AUDIT_FILTER_EXCLUDE
#define AUDIT_FILTER_EXCLUDE AUDIT_FILTER_TYPE
#endif
+#ifndef AUDIT_FILTER_URING_EXIT
+#define AUDIT_FILTER_URING_EXIT 0x07 /* filter on exit from io_uring op */
+#endif
#define AUDIT_FILTER_MASK 0x07 /* Mask to get actual filter */
#define AUDIT_FILTER_UNSET 0x80 /* This value means filter is unset */

@@ -612,6 +615,8 @@ extern int audit_name_to_field(const char *field);
extern const char *audit_field_to_name(int field);
extern int audit_name_to_syscall(const char *sc, int machine);
extern const char *audit_syscall_to_name(int sc, int machine);
+extern int audit_name_to_uringop(const char *uringopop);
+extern const char *audit_uringop_to_name(int uringop);
extern int audit_name_to_flag(const char *flag);
extern const char *audit_flag_to_name(int flag);
extern int audit_name_to_action(const char *action);
@@ -706,6 +711,8 @@ extern struct audit_rule_data *audit_rule_create_data(void);
extern void audit_rule_init_data(struct audit_rule_data *rule);
extern int audit_rule_syscallbyname_data(struct audit_rule_data *rule,
const char *scall);
+extern int audit_rule_uringopbyname_data(struct audit_rule_data *rule,
+ const char *uringop);
/* Note that the following function takes a **, where audit_rule_fieldpair()
* takes just a *. That structure may need to be reallocated as a result of
* adding new fields */
diff --git a/lib/lookup_table.c b/lib/lookup_table.c
index 23678a4d142e..ca619fba930d 100644
--- a/lib/lookup_table.c
+++ b/lib/lookup_table.c
@@ -142,6 +142,18 @@ int audit_name_to_syscall(const char *sc, int machine)
return -1;
}

+int audit_name_to_uringop(const char *uringop)
+{
+ int res = -1, found = 0;
+
+#ifndef NO_TABLES
+ //found = uringop_s2i(uringop, &res);
+#endif
+ if (found)
+ return res;
+ return -1;
+}
+
const char *audit_syscall_to_name(int sc, int machine)
{
#ifndef NO_TABLES
@@ -172,6 +184,14 @@ const char *audit_syscall_to_name(int sc, int machine)
return NULL;
}

+const char *audit_uringop_to_name(int uringop)
+{
+#ifndef NO_TABLES
+ //return uringop_i2s(uringop);
+#endif
+ return NULL;
+}
+
int audit_name_to_flag(const char *flag)
{
int res;
diff --git a/lib/private.h b/lib/private.h
index c3a7364fcfb8..b0d3fa4109c5 100644
--- a/lib/private.h
+++ b/lib/private.h
@@ -135,6 +135,7 @@ AUDIT_HIDDEN_END
extern int _audit_permadded;
extern int _audit_archadded;
extern int _audit_syscalladded;
+extern int _audit_uringopadded;
extern int _audit_exeadded;
extern int _audit_filterfsadded;
extern unsigned int _audit_elf;
diff --git a/src/auditctl-listing.c b/src/auditctl-listing.c
index a5d6bc2b046f..3d80906ffd24 100644
--- a/src/auditctl-listing.c
+++ b/src/auditctl-listing.c
@@ -137,15 +137,22 @@ static int print_syscall(const struct audit_rule_data *r, unsigned int *sc)
int all = 1;
unsigned int i;
int machine = audit_detect_machine();
-
- /* Rules on the following filters do not take a syscall */
- if (((r->flags & AUDIT_FILTER_MASK) == AUDIT_FILTER_USER) ||
- ((r->flags & AUDIT_FILTER_MASK) == AUDIT_FILTER_TASK) ||
- ((r->flags &AUDIT_FILTER_MASK) == AUDIT_FILTER_EXCLUDE) ||
- ((r->flags &AUDIT_FILTER_MASK) == AUDIT_FILTER_FS))
+ int uring = 0;
+
+ /* Rules on the following filters do not take a syscall (or uringop) */
+ switch (r->flags & AUDIT_FILTER_MASK) {
+ case AUDIT_FILTER_USER:
+ case AUDIT_FILTER_TASK:
+ case AUDIT_FILTER_EXCLUDE:
+ case AUDIT_FILTER_FS:
return 0;
+ break;
+ case AUDIT_FILTER_URING_EXIT:
+ uring = 1;
+ break;
+ }

- /* See if its all or specific syscalls */
+ /* See if its all or specific syscalls/uringops */
for (i = 0; i < (AUDIT_BITMASK_SIZE-1); i++) {
if (r->mask[i] != (uint32_t)~0) {
all = 0;
@@ -154,21 +161,32 @@ static int print_syscall(const struct audit_rule_data *r, unsigned int *sc)
}

if (all) {
- printf(" -S all");
+ if (uring)
+ printf(" -U all");
+ else
+ printf(" -S all");
count = i;
} else for (i = 0; i < AUDIT_BITMASK_SIZE * 32; i++) {
int word = AUDIT_WORD(i);
int bit = AUDIT_BIT(i);
if (r->mask[word] & bit) {
const char *ptr;
- if (_audit_elf)
- machine = audit_elf_to_machine(_audit_elf);
- if (machine < 0)
- ptr = NULL;
- else
- ptr = audit_syscall_to_name(i, machine);
+
+ if (uring)
+ ptr = audit_uringop_to_name(i);
+ else {
+ if (_audit_elf)
+ machine = audit_elf_to_machine(_audit_elf);
+ if (machine < 0)
+ ptr = NULL;
+ else
+ ptr = audit_syscall_to_name(i, machine);
+ }
if (!count)
- printf(" -S ");
+ if (uring)
+ printf(" -U ");
+ else
+ printf(" -S ");
if (ptr)
printf("%s%s", !count ? "" : ",", ptr);
else
@@ -297,7 +315,7 @@ static void print_rule(const struct audit_rule_data *r)
int mach = -1, watch = is_watch(r);
unsigned long long a0 = 0, a1 = 0;

- if (!watch) { /* This is syscall auditing */
+ if (!watch) { /* This is syscall or uring auditing */
printf("-a %s,%s",
audit_action_to_name((int)r->action),
audit_flag_to_name(r->flags));
@@ -310,7 +328,7 @@ static void print_rule(const struct audit_rule_data *r)
mach = print_arch(r->values[i], op);
}
}
- // And last do the syscalls
+ // And last do the syscalls/uringops
count = print_syscall(r, &sc);
}

diff --git a/src/auditctl.c b/src/auditctl.c
index f9bfc2a247d2..74df4f17f887 100644
--- a/src/auditctl.c
+++ b/src/auditctl.c
@@ -76,6 +76,7 @@ static int reset_vars(void)
{
list_requested = 0;
_audit_syscalladded = 0;
+ _audit_uringopadded = 0;
_audit_permadded = 0;
_audit_archadded = 0;
_audit_exeadded = 0;
@@ -110,7 +111,7 @@ static void usage(void)
" -C f=f Compare collected fields if available:\n"
" Field name, operator(=,!=), field name\n"
" -d <l,a> Delete rule from <l>ist with ction\n"
- " l=task,exit,user,exclude,filesystem\n"
+ " l=task,exit,user,exclude,filesystem,uring\n"
" a=never,always\n"
" -D Delete all rules and watches\n"
" -e [0..2] Set enabled flag\n"
@@ -132,6 +133,7 @@ static void usage(void)
" -S syscall Build rule: syscall name or number\n"
" --signal <signal> Send the specified signal to the daemon\n"
" -t Trim directory watches\n"
+ " -U uringop Build rule: uring op name or number\n"
" -v Version\n"
" -w <path> Insert watch at <path>\n"
" -W <path> Remove watch at <path>\n"
@@ -164,6 +166,8 @@ static int lookup_filter(const char *str, int *filter)
exclude = 1;
} else if (strcmp(str, "filesystem") == 0)
*filter = AUDIT_FILTER_FS;
+ else if (strcmp(str, "uring") == 0)
+ *filter = AUDIT_FILTER_URING_EXIT;
else
return 2;
return 0;
@@ -541,6 +545,36 @@ static int parse_syscall(const char *optarg)
return audit_rule_syscallbyname_data(rule_new, optarg);
}

+static int parse_uringop(const char *optarg)
+{
+ int retval = 0;
+ char *saved;
+
+ if (strchr(optarg, ',')) {
+ char *ptr, *tmp = strdup(optarg);
+ if (tmp == NULL)
+ return -1;
+ ptr = strtok_r(tmp, ",", &saved);
+ while (ptr) {
+ retval = audit_rule_uringopbyname_data(rule_new, ptr);
+ if (retval != 0) {
+ if (retval == -1) {
+ audit_msg(LOG_ERR,
+ "Uring op name unknown: %s",
+ ptr);
+ retval = -3; // error reported
+ }
+ break;
+ }
+ ptr = strtok_r(NULL, ",", &saved);
+ }
+ free(tmp);
+ return retval;
+ }
+
+ return audit_rule_uringopbyname_data(rule_new, optarg);
+}
+
static struct option long_opts[] =
{
#if HAVE_DECL_AUDIT_FEATURE_VERSION == 1
@@ -576,7 +610,7 @@ static int setopt(int count, int lineno, char *vars[])
keylen = AUDIT_MAX_KEY_LEN;

while ((retval >= 0) && (c = getopt_long(count, vars,
- "hicslDvtC:e:f:r:b:a:A:d:S:F:m:R:w:W:k:p:q:",
+ "hicslDvtC:e:f:r:b:a:A:d:S:U:F:m:R:w:W:k:p:q:",
long_opts, &lidx)) != EOF) {
int flags = AUDIT_FILTER_UNSET;
rc = 10; // Init to something impossible to see if unused.
@@ -715,9 +749,10 @@ static int setopt(int count, int lineno, char *vars[])
retval = -1;
break;
case 'a':
- if (strstr(optarg, "task") && _audit_syscalladded) {
+ if (strstr(optarg, "task") && (_audit_syscalladded ||
+ _audit_uringopadded)) {
audit_msg(LOG_ERR,
- "Syscall auditing requested for task list");
+ "Syscall or uring op auditing requested for task list");
retval = -1;
} else {
rc = audit_rule_setup(optarg, &add, &action);
@@ -739,9 +774,10 @@ static int setopt(int count, int lineno, char *vars[])
}
break;
case 'A':
- if (strstr(optarg, "task") && _audit_syscalladded) {
- audit_msg(LOG_ERR,
- "Error: syscall auditing requested for task list");
+ if (strstr(optarg, "task") && (_audit_syscalladded ||
+ _audit_uringopadded)) {
+ audit_msg(LOG_ERR,
+ "Syscall or uring op auditing requested for task list");
retval = -1;
} else {
rc = audit_rule_setup(optarg, &add, &action);
@@ -809,6 +845,10 @@ static int setopt(int count, int lineno, char *vars[])
audit_msg(LOG_ERR,
"Error: syscall auditing cannot be put on exclude list");
return -1;
+ } else if (((add | del) & AUDIT_FILTER_MASK) == AUDIT_FILTER_URING_EXIT) {
+ audit_msg(LOG_ERR,
+ "Error: syscall auditing cannot be put on uringop list");
+ return -1;
} else {
if (unknown_arch) {
int machine;
@@ -853,14 +893,63 @@ static int setopt(int count, int lineno, char *vars[])
break;
}}
break;
+ case 'U':
+ /* Do some checking to make sure that we are not adding a
+ * uring op rule to a list that does not make sense. */
+ if (((add & (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) ==
+ AUDIT_FILTER_TASK || (del &
+ (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) ==
+ AUDIT_FILTER_TASK)) {
+ audit_msg(LOG_ERR,
+ "Error: uring op auditing being added to task list");
+ return -1;
+ } else if (((add & (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) ==
+ AUDIT_FILTER_USER || (del &
+ (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) ==
+ AUDIT_FILTER_USER)) {
+ audit_msg(LOG_ERR,
+ "Error: uring op auditing being added to user list");
+ return -1;
+ } else if (((add & (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) ==
+ AUDIT_FILTER_FS || (del &
+ (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) ==
+ AUDIT_FILTER_FS)) {
+ audit_msg(LOG_ERR,
+ "Error: uring op auditing being added to filesystem list");
+ return -1;
+ } else if (exclude) {
+ audit_msg(LOG_ERR,
+ "Error: uring op auditing cannot be put on exclude list");
+ return -1;
+ } else if (((add | del) & AUDIT_FILTER_MASK) == AUDIT_FILTER_EXIT) {
+ audit_msg(LOG_ERR,
+ "Error: uringop auditing cannot be put on syscall list");
+ return -1;
+ }
+ rc = parse_uringop(optarg);
+ switch (rc)
+ {
+ case 0:
+ _audit_uringopadded = 1;
+ break;
+ case -1:
+ audit_msg(LOG_ERR, "Uring op name unknown: %s",
+ optarg);
+ retval = -1;
+ break;
+ case -3: // Error reported - do nothing here
+ retval = -1;
+ break;
+ }
+ break;
case 'F':
if (add != AUDIT_FILTER_UNSET)
flags = add & AUDIT_FILTER_MASK;
else if (del != AUDIT_FILTER_UNSET)
flags = del & AUDIT_FILTER_MASK;
- // if the field is arch & there is a -t option...we
+ // if the field is arch & there is a -t option...we
// can allow it
- else if ((optind >= count) || (strstr(optarg, "arch=") == NULL)
+ else if ((optind >= count) || (strstr(optarg, "arch=") == NULL && _audit_uringopadded != 1)
|| (strcmp(vars[optind], "-t") != 0)) {
audit_msg(LOG_ERR, "List must be given before field");
retval = -1;
@@ -989,12 +1078,12 @@ static int setopt(int count, int lineno, char *vars[])
}
break;
case 'k':
- if (!(_audit_syscalladded || _audit_permadded ||
- _audit_exeadded ||
+ if (!(_audit_syscalladded || _audit_uringopadded ||
+ _audit_permadded || _audit_exeadded ||
_audit_filterfsadded) ||
(add==AUDIT_FILTER_UNSET && del==AUDIT_FILTER_UNSET)) {
audit_msg(LOG_ERR,
- "key option needs a watch or syscall given prior to it");
+ "key option needs a watch, syscall or uring op given prior to it");
retval = -1;
break;
} else if (!optarg) {
@@ -1031,7 +1120,7 @@ process_keys:
retval = audit_setup_perms(rule_new, optarg);
break;
case 'q':
- if (_audit_syscalladded) {
+ if (_audit_syscalladded || _audit_uringopadded) {
audit_msg(LOG_ERR,
"Syscall auditing requested for make equivalent");
retval = -1;
@@ -1466,7 +1555,7 @@ int main(int argc, char *argv[])
static int handle_request(int status)
{
if (status == 0) {
- if (_audit_syscalladded) {
+ if (_audit_syscalladded || _audit_uringopadded) {
audit_msg(LOG_ERR, "Error - no list specified");
return -1;
}
@@ -1478,7 +1567,7 @@ static int handle_request(int status)
if (add != AUDIT_FILTER_UNSET) {
// if !task add syscall any if not specified
if ((add & AUDIT_FILTER_MASK) != AUDIT_FILTER_TASK &&
- _audit_syscalladded != 1) {
+ (_audit_syscalladded != 1 && _audit_uringopadded != 1)) {
audit_rule_syscallbyname_data(
rule_new, "all");
}
@@ -1502,7 +1591,7 @@ static int handle_request(int status)
}
else if (del != AUDIT_FILTER_UNSET) {
if ((del & AUDIT_FILTER_MASK) != AUDIT_FILTER_TASK &&
- _audit_syscalladded != 1) {
+ (_audit_syscalladded != 1 && _audit_uringopadded != 1)) {
audit_rule_syscallbyname_data(
rule_new, "all");
}