History

fanotify: allow to set errno in FAN_DENY permission response

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.

Amir Goldstein committed 1 year ago

Commit b4b2ff41 parent 870499b5 files · +55 −7Browse files

Unified · Split

5 files
  1. fs/notify/fanotify/fanotify.c +13 −4
  2. fs/notify/fanotify/fanotify.h +5 −0
  3. fs/notify/fanotify/fanotify_user.c +27 −2
  4. include/linux/fanotify.h +3 −1
  5. include/uapi/linux/fanotify.h +7 −0
fs/notify/fanotify/fanotify.c+13−4
@@ -223,7 +223,7 @@ static int fanotify_get_response(struct fsnotify_group *group, · 222 unmodified lines
223 struct fanotify_perm_event *event,223 struct fanotify_perm_event *event,
224 struct fsnotify_iter_info *iter_info)224 struct fsnotify_iter_info *iter_info)
225{225{
226 int ret;226 int ret, errno;
227 227
228 pr_debug("%s: group=%p event=%p\n", __func__, group, event);228 pr_debug("%s: group=%p event=%p\n", __func__, group, event);
229 229
@@ -262,14 +262,23 @@ static int fanotify_get_response(struct fsnotify_group *group, · 32 unmodified lines
262 ret = 0;262 ret = 0;
263 break;263 break;
264 case FAN_DENY:264 case FAN_DENY:
265 /* Check custom errno from pre-content events */
266 errno = fanotify_get_response_errno(event->response);
267 if (errno) {
268 ret = -errno;
269 break;
270 }
271 fallthrough;
265 default:272 default:
266 ret = -EPERM;273 ret = -EPERM;
267 }274 }
268 275
269 /* Check if the response should be audited */276 /* Check if the response should be audited */
270 if (event->response & FAN_AUDIT)277 if (event->response & FAN_AUDIT) {
271 audit_fanotify(event->response & ~FAN_AUDIT,278 u32 response = event->response &
272 &event->audit_rule);279 (FANOTIFY_RESPONSE_ACCESS | FANOTIFY_RESPONSE_FLAGS);
280 audit_fanotify(response & ~FAN_AUDIT, &event->audit_rule);
281 }
273 282
274 pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__,283 pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__,
275 group, event, ret);284 group, event, ret);
fs/notify/fanotify/fanotify.h+5−0
@@ -528,3 +528,8 @@ static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark) · 527 unmodified lines
528 528
529 return mflags;529 return mflags;
530}530}
531
532static inline u32 fanotify_get_response_errno(int res)
533{
534 return (res >> FAN_ERRNO_SHIFT) & FAN_ERRNO_MASK;
535}
fs/notify/fanotify/fanotify_user.c+27−2
@@ -327,11 +327,12 @@ static int process_access_response(struct fsnotify_group *group, · 326 unmodified lines
327 struct fanotify_perm_event *event;327 struct fanotify_perm_event *event;
328 int fd = response_struct->fd;328 int fd = response_struct->fd;
329 u32 response = response_struct->response;329 u32 response = response_struct->response;
330 int errno = fanotify_get_response_errno(response);
330 int ret = info_len;331 int ret = info_len;
331 struct fanotify_response_info_audit_rule friar;332 struct fanotify_response_info_audit_rule friar;
332 333
333 pr_debug("%s: group=%p fd=%d response=%u buf=%p size=%zu\n", __func__,334 pr_debug("%s: group=%p fd=%d response=%x errno=%d buf=%p size=%zu\n",
334 group, fd, response, info, info_len);335 __func__, group, fd, response, errno, info, info_len);
335 /*336 /*
336 * make sure the response is valid, if invalid we do nothing and either337 * make sure the response is valid, if invalid we do nothing and either
337 * userspace can send a valid response or we will clean it up after the338 * 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, · 4 unmodified lines
342 343
343 switch (response & FANOTIFY_RESPONSE_ACCESS) {344 switch (response & FANOTIFY_RESPONSE_ACCESS) {
344 case FAN_ALLOW:345 case FAN_ALLOW:
346 if (errno)
347 return -EINVAL;
348 break;
345 case FAN_DENY:349 case FAN_DENY:
350 /* Custom errno is supported only for pre-content groups */
351 if (errno && group->priority != FSNOTIFY_PRIO_PRE_CONTENT)
352 return -EINVAL;
353
354 /*
355 * Limit errno to values expected on open(2)/read(2)/write(2)
356 * of regular files.
357 */
358 switch (errno) {
359 case 0:
360 case EIO:
361 case EPERM:
362 case EBUSY:
363 case ETXTBSY:
364 case EAGAIN:
365 case ENOSPC:
366 case EDQUOT:
367 break;
368 default:
369 return -EINVAL;
370 }
346 break;371 break;
347 default:372 default:
348 return -EINVAL;373 return -EINVAL;
include/linux/fanotify.h+3−1
@@ -132,7 +132,9 @@ · 131 unmodified lines
132/* These masks check for invalid bits in permission responses. */132/* These masks check for invalid bits in permission responses. */
133#define FANOTIFY_RESPONSE_ACCESS (FAN_ALLOW | FAN_DENY)133#define FANOTIFY_RESPONSE_ACCESS (FAN_ALLOW | FAN_DENY)
134#define FANOTIFY_RESPONSE_FLAGS (FAN_AUDIT | FAN_INFO)134#define FANOTIFY_RESPONSE_FLAGS (FAN_AUDIT | FAN_INFO)
135#define FANOTIFY_RESPONSE_VALID_MASK (FANOTIFY_RESPONSE_ACCESS | FANOTIFY_RESPONSE_FLAGS)135#define FANOTIFY_RESPONSE_VALID_MASK \
136 (FANOTIFY_RESPONSE_ACCESS | FANOTIFY_RESPONSE_FLAGS | \
137 (FAN_ERRNO_MASK << FAN_ERRNO_SHIFT))
136 138
137/* Do not use these old uapi constants internally */139/* Do not use these old uapi constants internally */
138#undef FAN_ALL_CLASS_BITS140#undef FAN_ALL_CLASS_BITS
include/uapi/linux/fanotify.h+7−0
@@ -235,6 +235,13 @@ struct fanotify_response_info_audit_rule { · 234 unmodified lines
235/* Legit userspace responses to a _PERM event */235/* Legit userspace responses to a _PERM event */
236#define FAN_ALLOW 0x01236#define FAN_ALLOW 0x01
237#define FAN_DENY 0x02237#define FAN_DENY 0x02
238/* errno other than EPERM can specified in upper byte of deny response */
239#define FAN_ERRNO_BITS 8
240#define FAN_ERRNO_SHIFT (32 - FAN_ERRNO_BITS)
241#define FAN_ERRNO_MASK ((1 << FAN_ERRNO_BITS) - 1)
242#define FAN_DENY_ERRNO(err) \
243 (FAN_DENY | ((((__u32)(err)) & FAN_ERRNO_MASK) << FAN_ERRNO_SHIFT))
244
238#define FAN_AUDIT 0x10 /* Bitmask to create audit record for result */245#define FAN_AUDIT 0x10 /* Bitmask to create audit record for result */
239#define FAN_INFO 0x20 /* Bitmask to indicate additional information */246#define FAN_INFO 0x20 /* Bitmask to indicate additional information */
240 247