diff mbox

[v6,1/1] audit: Record fanotify access control decisions

Message ID 1723465.15OHkZpn6L@x2 (mailing list archive)
State New, archived
Headers show

Commit Message

Steve Grubb Sept. 26, 2017, 9:11 p.m. UTC
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.

Signed-off-by: sgrubb <sgrubb@redhat.com>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
---
 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(-)

Comments

kernel test robot Sept. 29, 2017, 7:35 p.m. UTC | #1
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 <pmoore@redhat.com>
:::::: CC: Paul Moore <paul@paul-moore.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot Sept. 30, 2017, 12:09 a.m. UTC | #2
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
diff mbox

Patch

diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index 2fa99ae..1968d21 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -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__,
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 907a481..ea3c458 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -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;
diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c
index dd63aa9..ed311e3 100644
--- a/fs/notify/fdinfo.c
+++ b/fs/notify/fdinfo.c
@@ -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);
 
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 2150bdc..9095617 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -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
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index c6c6931..f4131f2 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -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 */
 	};
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 0714a66..221f8b7 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -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 */
diff --git a/include/uapi/linux/fanotify.h b/include/uapi/linux/fanotify.h
index 030508d..5dda19a 100644
--- a/include/uapi/linux/fanotify.h
+++ b/include/uapi/linux/fanotify.h
@@ -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
 
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 3260ba2..e046de8 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -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;