]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fanotify: allow to set errno in FAN_DENY permission response
authorAmir Goldstein <amir73il@gmail.com>
Fri, 15 Nov 2024 15:30:25 +0000 (10:30 -0500)
committerJan Kara <jack@suse.cz>
Tue, 10 Dec 2024 11:03:17 +0000 (12:03 +0100)
With FAN_DENY response, user trying to perform the filesystem operation
gets an error with errno set to EPERM.

It is useful for hierarchical storage management (HSM) service to be able
to deny access for reasons more diverse than EPERM, for example EAGAIN,
if HSM could retry the operation later.

Allow fanotify groups with priority FAN_CLASSS_PRE_CONTENT to responsd
to permission events with the response value FAN_DENY_ERRNO(errno),
instead of FAN_DENY to return a custom error.

Limit custom error values to errors expected on read(2)/write(2) and
open(2) of regular files. This list could be extended in the future.
Userspace can test for legitimate values of FAN_DENY_ERRNO(errno) by
writing a response to an fanotify group fd with a value of FAN_NOFD in
the fd field of the response.

The change in fanotify_response is backward compatible, because errno is
written in the high 8 bits of the 32bit response field and old kernels
reject respose value with high bits set.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/1e5fb6af84b69ca96b5c849fa5f10bdf4d1dc414.1731684329.git.josef@toxicpanda.com
fs/notify/fanotify/fanotify.c
fs/notify/fanotify/fanotify.h
fs/notify/fanotify/fanotify_user.c
include/linux/fanotify.h
include/uapi/linux/fanotify.h

index da6c3c1c7edf51e7c08b51f36678a202614be691..95646f7c46cab949e9b64f75a9702a4a28e95582 100644 (file)
@@ -223,7 +223,7 @@ static int fanotify_get_response(struct fsnotify_group *group,
                                 struct fanotify_perm_event *event,
                                 struct fsnotify_iter_info *iter_info)
 {
-       int ret;
+       int ret, errno;
 
        pr_debug("%s: group=%p event=%p\n", __func__, group, event);
 
@@ -262,14 +262,23 @@ static int fanotify_get_response(struct fsnotify_group *group,
                ret = 0;
                break;
        case FAN_DENY:
+               /* Check custom errno from pre-content events */
+               errno = fanotify_get_response_errno(event->response);
+               if (errno) {
+                       ret = -errno;
+                       break;
+               }
+               fallthrough;
        default:
                ret = -EPERM;
        }
 
        /* Check if the response should be audited */
-       if (event->response & FAN_AUDIT)
-               audit_fanotify(event->response & ~FAN_AUDIT,
-                              &event->audit_rule);
+       if (event->response & FAN_AUDIT) {
+               u32 response = event->response &
+                       (FANOTIFY_RESPONSE_ACCESS | FANOTIFY_RESPONSE_FLAGS);
+               audit_fanotify(response & ~FAN_AUDIT, &event->audit_rule);
+       }
 
        pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__,
                 group, event, ret);
index 7f06355afa1f35920d87c65de5e025186501da25..c12cbc270539c0f1e44053f0893a69dd0f60b385 100644 (file)
@@ -528,3 +528,8 @@ static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
 
        return mflags;
 }
+
+static inline u32 fanotify_get_response_errno(int res)
+{
+       return (res >> FAN_ERRNO_SHIFT) & FAN_ERRNO_MASK;
+}
index 6ef3cc7de5e42b6b2bde6394157704f897253068..19435cd2c41f58a33b26dedec03e89210a0c8b03 100644 (file)
@@ -327,11 +327,12 @@ static int process_access_response(struct fsnotify_group *group,
        struct fanotify_perm_event *event;
        int fd = response_struct->fd;
        u32 response = response_struct->response;
+       int errno = fanotify_get_response_errno(response);
        int ret = info_len;
        struct fanotify_response_info_audit_rule friar;
 
-       pr_debug("%s: group=%p fd=%d response=%u buf=%p size=%zu\n", __func__,
-                group, fd, response, info, info_len);
+       pr_debug("%s: group=%p fd=%d response=%x errno=%d buf=%p size=%zu\n",
+                __func__, group, fd, response, errno, info, info_len);
        /*
         * make sure the response is valid, if invalid we do nothing and either
         * userspace can send a valid response or we will clean it up after the
@@ -342,7 +343,31 @@ static int process_access_response(struct fsnotify_group *group,
 
        switch (response & FANOTIFY_RESPONSE_ACCESS) {
        case FAN_ALLOW:
+               if (errno)
+                       return -EINVAL;
+               break;
        case FAN_DENY:
+               /* Custom errno is supported only for pre-content groups */
+               if (errno && group->priority != FSNOTIFY_PRIO_PRE_CONTENT)
+                       return -EINVAL;
+
+               /*
+                * Limit errno to values expected on open(2)/read(2)/write(2)
+                * of regular files.
+                */
+               switch (errno) {
+               case 0:
+               case EIO:
+               case EPERM:
+               case EBUSY:
+               case ETXTBSY:
+               case EAGAIN:
+               case ENOSPC:
+               case EDQUOT:
+                       break;
+               default:
+                       return -EINVAL;
+               }
                break;
        default:
                return -EINVAL;
index c747af064d2c6dc0418eb9c20cc169b4b064dc89..78f660ebc3180d5a7697370fcbdcec3c2fc3d7ad 100644 (file)
 /* These masks check for invalid bits in permission responses. */
 #define FANOTIFY_RESPONSE_ACCESS (FAN_ALLOW | FAN_DENY)
 #define FANOTIFY_RESPONSE_FLAGS (FAN_AUDIT | FAN_INFO)
-#define FANOTIFY_RESPONSE_VALID_MASK (FANOTIFY_RESPONSE_ACCESS | FANOTIFY_RESPONSE_FLAGS)
+#define FANOTIFY_RESPONSE_VALID_MASK \
+       (FANOTIFY_RESPONSE_ACCESS | FANOTIFY_RESPONSE_FLAGS | \
+        (FAN_ERRNO_MASK << FAN_ERRNO_SHIFT))
 
 /* Do not use these old uapi constants internally */
 #undef FAN_ALL_CLASS_BITS
index 0636a9c85dd013e486deac1024cec84667ea9d9e..bd816797970771d9c54c85b5632464af0c3349ef 100644 (file)
@@ -235,6 +235,13 @@ struct fanotify_response_info_audit_rule {
 /* Legit userspace responses to a _PERM event */
 #define FAN_ALLOW      0x01
 #define FAN_DENY       0x02
+/* errno other than EPERM can specified in upper byte of deny response */
+#define FAN_ERRNO_BITS 8
+#define FAN_ERRNO_SHIFT (32 - FAN_ERRNO_BITS)
+#define FAN_ERRNO_MASK ((1 << FAN_ERRNO_BITS) - 1)
+#define FAN_DENY_ERRNO(err) \
+       (FAN_DENY | ((((__u32)(err)) & FAN_ERRNO_MASK) << FAN_ERRNO_SHIFT))
+
 #define FAN_AUDIT      0x10    /* Bitmask to create audit record for result */
 #define FAN_INFO       0x20    /* Bitmask to indicate additional information */