Skip to contentSkip to conversation

audit: Record fanotify access control decisions

PATCH Superseded Steve Grubb 2 participants 3 messages Sep 26, 2017 – Sep 30, 2017

Subsystems (4)

v6 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

PATCH v6 1/1 audit: Record fanotify access control decisions

Hello,

The fanotify interface allows user space daemons to make access control decisions. Under common criteria requirements, we need to optionally record decisions based on policy. This patch adds a bit mask, FAN_AUDIT, that a user space daemon can 'or' into the response decision which will tell the kernel that it made a decision and record it.

It would be used something like this in user space code:

  response.response = FAN_DENY | FAN_AUDIT;
  write(fd, &response, sizeof(struct fanotify_response));

When the syscall ends, the audit system will record the decision as a AUDIT_FANOTIFY auxiliary record to denote that the reason this event occurred is the result of an access control decision from fanotify rather than DAC or MAC policy.

A sample event looks like this:

type=PATH msg=audit(1504310584.332:290): item=0 name="./evil-ls" inode=1319561 dev=fc:03 mode=0100755 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 nametype=NORMAL type=CWD msg=audit(1504310584.332:290): cwd="/home/sgrubb" type=SYSCALL msg=audit(1504310584.332:290): arch=c000003e syscall=2 success=no exit=-1 a0=32cb3fca90 a1=0 a2=43 a3=8 items=1 ppid=901 pid=959 auid=1000 uid=1000 gid=1000 euid=1000 suid=1000 fsuid=1000 egid=1000 sgid=1000 fsgid=1000 tty=pts1 ses=3 comm="bash" exe="/usr/bin/bash" subj=unconfined_u:unconfined_r:unconfined_t:
s0-s0:c0.c1023 key=(null)
type=FANOTIFY msg=audit(1504310584.332:290): resp=2

Prior to using the audit flag, the developer needs to call fanotify_init or'ing in FAN_ENABLE_AUDIT to ensure that the kernel supports auditing. The calling process must also have the CAP_AUDIT_WRITE capability.

* This v6 update adds empty inline audit_fanotify function for when CONFIG_AUDITSYSCALL is not defined.

---
 fs/notify/fanotify/fanotify.c      |  8 +++++++-
 fs/notify/fanotify/fanotify_user.c | 16 +++++++++++++++-
 fs/notify/fdinfo.c                 |  3 +++
 include/linux/audit.h              | 10 ++++++++++
 include/linux/fsnotify_backend.h   |  1 +
 include/uapi/linux/audit.h         |  1 +
 include/uapi/linux/fanotify.h      |  3 +++
 kernel/auditsc.c                   |  6 ++++++
 8 files changed, 46 insertions(+), 2 deletions(-)
fs/notify/fanotify/fanotify.c +7 -1
index 2fa99ae..1968d21 100644
@@ -9,6 +9,7 @@
 #include <linux/sched/user.h>
 #include <linux/types.h>
 #include <linux/wait.h>
+#include <linux/audit.h>
 
 #include "fanotify.h"
 
@@ -78,7 +79,7 @@ static int fanotify_get_response(struct fsnotify_group *group,
 	fsnotify_finish_user_wait(iter_info);
 out:
 	/* userspace responded, convert to something usable */
-	switch (event->response) {
+	switch (event->response & ~FAN_AUDIT) {
 	case FAN_ALLOW:
 		ret = 0;
 		break;
@@ -86,6 +87,11 @@ static int fanotify_get_response(struct fsnotify_group *group,
 	default:
 		ret = -EPERM;
 	}
+
+	/* Check if the response should be audited */
+	if (event->response & FAN_AUDIT)
+		audit_fanotify(event->response & ~FAN_AUDIT);
+
 	event->response = 0;
 
 	pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__,
fs/notify/fanotify/fanotify_user.c +15 -1
index 907a481..ea3c458 100644
@@ -179,7 +179,7 @@ static int process_access_response(struct fsnotify_group *group,
 	 * userspace can send a valid response or we will clean it up after the
 	 * timeout
 	 */
-	switch (response) {
+	switch (response & ~FAN_AUDIT) {
 	case FAN_ALLOW:
 	case FAN_DENY:
 		break;
@@ -190,6 +190,9 @@ static int process_access_response(struct fsnotify_group *group,
 	if (fd < 0)
 		return -EINVAL;
 
+	if ((response & FAN_AUDIT) && !group->fanotify_data.audit_enabled)
+		return -EINVAL;
+
 	event = dequeue_event(group, fd);
 	if (!event)
 		return -ENOENT;
@@ -721,7 +724,11 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
+#ifdef CONFIG_AUDITSYSCALL
+	if (flags & ~(FAN_ALL_INIT_FLAGS | FAN_ENABLE_AUDIT))
+#else
 	if (flags & ~FAN_ALL_INIT_FLAGS)
+#endif
 		return -EINVAL;
 
 	if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS)
@@ -805,6 +812,13 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
 		group->fanotify_data.max_marks = FANOTIFY_DEFAULT_MAX_MARKS;
 	}
 
+	if (flags & FAN_ENABLE_AUDIT) {
+		fd = -EPERM;
+		if (!capable(CAP_AUDIT_WRITE))
+			goto out_destroy_group;
+		group->fanotify_data.audit_enabled = true;
+	}
+
 	fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags);
 	if (fd < 0)
 		goto out_destroy_group;
fs/notify/fdinfo.c +3 -0
index dd63aa9..ed311e3 100644
@@ -156,6 +156,9 @@ void fanotify_show_fdinfo(struct seq_file *m, struct file *f)
 	if (group->fanotify_data.max_marks == UINT_MAX)
 		flags |= FAN_UNLIMITED_MARKS;
 
+	if (group->fanotify_data.audit_enabled)
+		flags |= FAN_ENABLE_AUDIT;
+
 	seq_printf(m, "fanotify flags:%x event-flags:%x\n",
 		   flags, group->fanotify_data.f_flags);
 
include/linux/audit.h +10 -0
index 2150bdc..9095617 100644
@@ -360,6 +360,7 @@ extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
 extern void __audit_log_capset(const struct cred *new, const struct cred *old);
 extern void __audit_mmap_fd(int fd, int flags);
 extern void __audit_log_kern_module(char *name);
+extern void __audit_fanotify(unsigned int response);
 
 static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
 {
@@ -456,6 +457,12 @@ static inline void audit_log_kern_module(char *name)
 		__audit_log_kern_module(name);
 }
 
+static inline void audit_fanotify(unsigned int response)
+{
+	if (!audit_dummy_context())
+		__audit_fanotify(response);
+}
+
 extern int audit_n_rules;
 extern int audit_signals;
 #else /* CONFIG_AUDITSYSCALL */
@@ -572,6 +579,9 @@ static inline void audit_log_kern_module(char *name)
 {
 }
 
+static inline void audit_fanotify(unsigned int response)
+{ }
+
 static inline void audit_ptrace(struct task_struct *t)
 { }
 #define audit_n_rules 0
include/linux/fsnotify_backend.h +1 -0
index c6c6931..f4131f2 100644
@@ -190,6 +190,7 @@ struct fsnotify_group {
 			int f_flags;
 			unsigned int max_marks;
 			struct user_struct *user;
+			bool audit_enabled;
 		} fanotify_data;
 #endif /* CONFIG_FANOTIFY */
 	};
include/uapi/linux/audit.h +1 -0
index 0714a66..221f8b7 100644
@@ -112,6 +112,7 @@
 #define AUDIT_FEATURE_CHANGE	1328	/* audit log listing feature changes */
 #define AUDIT_REPLACE		1329	/* Replace auditd if this packet unanswerd */
 #define AUDIT_KERN_MODULE	1330	/* Kernel Module events */
+#define AUDIT_FANOTIFY		1331	/* Fanotify access decision */
 
 #define AUDIT_AVC		1400	/* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR	1401	/* Internal SE Linux Errors */
include/uapi/linux/fanotify.h +3 -0
index 030508d..5dda19a 100644
@@ -35,6 +35,7 @@
 
 #define FAN_UNLIMITED_QUEUE	0x00000010
 #define FAN_UNLIMITED_MARKS	0x00000020
+#define FAN_ENABLE_AUDIT	0x00000040
 
 #define FAN_ALL_INIT_FLAGS	(FAN_CLOEXEC | FAN_NONBLOCK | \
 				 FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |\
@@ -99,6 +100,8 @@ struct fanotify_response {
 /* Legit userspace responses to a _PERM event */
 #define FAN_ALLOW	0x01
 #define FAN_DENY	0x02
+#define FAN_AUDIT	0x10	/* Bit mask to create audit record for result */
+
 /* No fd set in event */
 #define FAN_NOFD	-1
 
kernel/auditsc.c +6 -0
index 3260ba2..e046de8 100644
@@ -2390,6 +2390,12 @@ void __audit_log_kern_module(char *name)
 	context->type = AUDIT_KERN_MODULE;
 }
 
+void __audit_fanotify(unsigned int response)
+{
+	audit_log(current->audit_context, GFP_KERNEL,
+		AUDIT_FANOTIFY,	"resp=%u", response);
+}
+
 static void audit_log_task(struct audit_buffer *ab)
 {
 	kuid_t auid, uid;
Signature · 1 line
2.9.5
#1 lore raw
url: https://github.com/0day-ci/linux/commits/Steve-Grubb/audit-Record-fanotify-access-control-decisions/20170930-005627 config: i386-randconfig-x0-09300058 (attached as .config) compiler: gcc-6…

Hi Steve,

[auto build test ERROR on linus/master]
[also build test ERROR on v4.14-rc2 next-20170929]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Steve-Grubb/audit-Record-fanotify-access-control-decisions/20170930-005627
config: i386-randconfig-x0-09300058 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901 reproduce:

        # save the attached .config to linux build tree
        make ARCH=i386 

All error/warnings (new ones prefixed by >>):

In file included from fs/exec.c:56:0:

>> include/linux/audit.h:215:23: error: expected identifier or '(' before numeric constant
    #define audit_enabled 0
                          ^
>> include/linux/fsnotify_backend.h:193:9: note: in expansion of macro 'audit_enabled'
       bool audit_enabled;
            ^~~~~~~~~~~~~
   In file included from include/linux/fsnotify.h:14:0,
                    from fs/exec.c:59:
>> include/linux/fsnotify_backend.h:194:3: warning: no semicolon at end of struct or union
      } fanotify_data;
      ^

vim +215 include/linux/audit.h

96368701 Paul Moore 2016-01-13  168  
96368701 Paul Moore 2016-01-13  169  extern u32 audit_enabled;
96368701 Paul Moore 2016-01-13  170  #else /* CONFIG_AUDIT */
96368701 Paul Moore 2016-01-13  171  static inline __printf(4, 5)
96368701 Paul Moore 2016-01-13  172  void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type,
96368701 Paul Moore 2016-01-13  173  	       const char *fmt, ...)
96368701 Paul Moore 2016-01-13  174  { }
96368701 Paul Moore 2016-01-13  175  static inline struct audit_buffer *audit_log_start(struct audit_context *ctx,
96368701 Paul Moore 2016-01-13  176  						   gfp_t gfp_mask, int type)
96368701 Paul Moore 2016-01-13  177  {
96368701 Paul Moore 2016-01-13  178  	return NULL;
96368701 Paul Moore 2016-01-13  179  }
96368701 Paul Moore 2016-01-13  180  static inline __printf(2, 3)
96368701 Paul Moore 2016-01-13  181  void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
96368701 Paul Moore 2016-01-13  182  { }
96368701 Paul Moore 2016-01-13  183  static inline void audit_log_end(struct audit_buffer *ab)
96368701 Paul Moore 2016-01-13  184  { }
96368701 Paul Moore 2016-01-13  185  static inline void audit_log_n_hex(struct audit_buffer *ab,
96368701 Paul Moore 2016-01-13  186  				   const unsigned char *buf, size_t len)
96368701 Paul Moore 2016-01-13  187  { }
96368701 Paul Moore 2016-01-13  188  static inline void audit_log_n_string(struct audit_buffer *ab,
96368701 Paul Moore 2016-01-13  189  				      const char *buf, size_t n)
96368701 Paul Moore 2016-01-13  190  { }
96368701 Paul Moore 2016-01-13  191  static inline void  audit_log_n_untrustedstring(struct audit_buffer *ab,
96368701 Paul Moore 2016-01-13  192  						const char *string, size_t n)
96368701 Paul Moore 2016-01-13  193  { }
96368701 Paul Moore 2016-01-13  194  static inline void audit_log_untrustedstring(struct audit_buffer *ab,
96368701 Paul Moore 2016-01-13  195  					     const char *string)
96368701 Paul Moore 2016-01-13  196  { }
96368701 Paul Moore 2016-01-13  197  static inline void audit_log_d_path(struct audit_buffer *ab,
96368701 Paul Moore 2016-01-13  198  				    const char *prefix,
96368701 Paul Moore 2016-01-13  199  				    const struct path *path)
96368701 Paul Moore 2016-01-13  200  { }
96368701 Paul Moore 2016-01-13  201  static inline void audit_log_key(struct audit_buffer *ab, char *key)
96368701 Paul Moore 2016-01-13  202  { }
96368701 Paul Moore 2016-01-13  203  static inline void audit_log_link_denied(const char *string,
96368701 Paul Moore 2016-01-13  204  					 const struct path *link)
96368701 Paul Moore 2016-01-13  205  { }
96368701 Paul Moore 2016-01-13  206  static inline void audit_log_secctx(struct audit_buffer *ab, u32 secid)
96368701 Paul Moore 2016-01-13  207  { }
96368701 Paul Moore 2016-01-13  208  static inline int audit_log_task_context(struct audit_buffer *ab)
96368701 Paul Moore 2016-01-13  209  {
96368701 Paul Moore 2016-01-13  210  	return 0;
96368701 Paul Moore 2016-01-13  211  }
96368701 Paul Moore 2016-01-13  212  static inline void audit_log_task_info(struct audit_buffer *ab,
96368701 Paul Moore 2016-01-13  213  				       struct task_struct *tsk)
96368701 Paul Moore 2016-01-13  214  { }

96368701 Paul Moore 2016-01-13 @215 #define audit_enabled 0 96368701 Paul Moore 2016-01-13 216 #endif /* CONFIG_AUDIT */ 96368701 Paul Moore 2016-01-13 217

:::::: The code at line 215 was first introduced by commit
:::::: 96368701e1c89057bbf39222e965161c68a85b4b audit: force seccomp event logging to honor the audit_enabled flag

:::::: TO: Paul Moore <[email protected]>
:::::: CC: Paul Moore <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

#2 lore raw
In reply to Steve Grubb
url: https://github.com/0day-ci/linux/commits/Steve-Grubb/audit-Record-fanotify-access-control-decisions/20170930-005627 config: x86_64-randconfig-b0-09300453 (attached as .config) compiler: gcc-4.4…

Hi Steve,

[auto build test ERROR on linus/master]
[also build test ERROR on v4.14-rc2 next-20170929]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Steve-Grubb/audit-Record-fanotify-access-control-decisions/20170930-005627
config: x86_64-randconfig-b0-09300453 (attached as .config)
compiler: gcc-4.4 (Debian 4.4.7-8) 4.4.7 reproduce:

        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

In file included from include/linux/fsnotify.h:14,

                    from fs/exec.c:59:
>> include/linux/fsnotify_backend.h:193: error: expected identifier or '(' before numeric constant

include/linux/fsnotify_backend.h:194: warning: no semicolon at end of struct or union

vim +193 include/linux/fsnotify_backend.h

122
123 /*
124 * A group is a "thing" that wants to receive notification about filesystem
125 * events. The mask holds the subset of event types this group cares about.
126 * refcnt on a group is up to the implementor and at any moment if it goes 0
127 * everything will be cleaned up.
128 */
129 struct fsnotify_group {
130 /*
131 * How the refcnt is used is up to each group. When the refcnt hits 0
132 * fsnotify will clean up all of the resources associated with this group.
133 * As an example, the dnotify group will always have a refcnt=1 and that
134 * will never change. Inotify, on the other hand, has a group per
135 * inotify_init() and the refcnt will hit 0 only when that fd has been
136 * closed.
137 */
138 atomic_t refcnt; /* things with interest in this group */
139
140 const struct fsnotify_ops *ops; /* how this group handles things */
141
142 /* needed to send notification to userspace */
143 spinlock_t notification_lock; /* protect the notification_list */
144 struct list_head notification_list; /* list of event_holder this group needs to send to userspace */
145 wait_queue_head_t notification_waitq; /* read() on the notification file blocks on this waitq */
146 unsigned int q_len; /* events on the queue */
147 unsigned int max_events; /* maximum events allowed on the list */
148 /*
149 * Valid fsnotify group priorities. Events are send in order from highest
150 * priority to lowest priority. We default to the lowest priority.
151 */
152 #define FS_PRIO_0 0 /* normal notifiers, no permissions */
153 #define FS_PRIO_1 1 /* fanotify content based access control */
154 #define FS_PRIO_2 2 /* fanotify pre-content access */
155 unsigned int priority;
156 bool shutdown; /* group is being shut down, don't queue more events */
157
158 /* stores all fastpath marks assoc with this group so they can be cleaned on unregister */
159 struct mutex mark_mutex; /* protect marks_list */
160 atomic_t num_marks; /* 1 for each mark and 1 for not being
161 * past the point of no return when freeing
162 * a group */
163 struct list_head marks_list; /* all inode marks for this group */
164
165 struct fasync_struct *fsn_fa; /* async notification */
166
167 struct fsnotify_event *overflow_event; /* Event we queue when the
168 * notification list is too
169 * full */
170 atomic_t user_waits; /* Number of tasks waiting for user
171 * response */
172
173 /* groups can define private fields here or use the void *private */
174 union {
175 void *private;
176 #ifdef CONFIG_INOTIFY_USER
177 struct inotify_group_private_data {
178 spinlock_t idr_lock;
179 struct idr idr;
180 struct ucounts *ucounts;
181 } inotify_data;
182 #endif
183 #ifdef CONFIG_FANOTIFY
184 struct fanotify_group_private_data {
185 #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
186 /* allows a group to block waiting for a userspace response */
187 struct list_head access_list;
188 wait_queue_head_t access_waitq;
189 #endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */
190 int f_flags;
191 unsigned int max_marks;
192 struct user_struct *user;

 > 193				bool audit_enabled;

194 } fanotify_data;
195 #endif /* CONFIG_FANOTIFY */
196 };
197 };
198

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation