.TP
.B 4
The SCMP_FLTATR_CTL_SSB filter attribute is supported.
+.TP
+.B 5
+The SCMP_ACT_NOTIFY action is supported.
.\" //////////////////////////////////////////////////////////////////////////
.SH RETURN VALUE
.\" //////////////////////////////////////////////////////////////////////////
--- /dev/null
+.TH "seccomp_notify_alloc" 3 "24 April 2019" "tycho@tycho.ws" "libseccomp Documentation"
+.\" //////////////////////////////////////////////////////////////////////////
+.SH NAME
+.\" //////////////////////////////////////////////////////////////////////////
+seccomp_notify_alloc, seccomp_notify_free, seccomp_notify_receive,
+seccomp_notify_respond, seccomp_notify_id_valid, seccomp_notify_fd \- Manage seccomp notifications
+.\" //////////////////////////////////////////////////////////////////////////
+.SH SYNOPSIS
+.\" //////////////////////////////////////////////////////////////////////////
+.nf
+.B #include <seccomp.h>
+.sp
+.BI "int seccomp_notify_alloc(struct seccomp_notif **" req ", struct seccomp_notif_resp **" resp ")"
+.BI "void seccomp_notify_free(struct seccomp_notif *" req ", struct seccomp_notif_resp *" resp ")"
+.BI "int seccomp_notify_receive(int " fd ", struct seccomp_notif *" req ")"
+.BI "int seccomp_notify_respond(int " fd ", struct seccomp_notif_resp *" resp ")"
+.BI "int seccomp_notify_id_valid(int " fd ", uint64_t " id ")"
+.BI "int seccomp_notify_fd(const scmp_filter_ctx " ctx ")"
+.sp
+Link with \fI\-lseccomp\fP.
+.fi
+.\" //////////////////////////////////////////////////////////////////////////
+.SH DESCRIPTION
+.\" //////////////////////////////////////////////////////////////////////////
+.P
+The
+.BR seccomp_notify_alloc ()
+function dynamically allocates enough memory for a seccomp notification and
+response. Note that one should always use these functions and not depend on the
+structure sizes in headers, since the size can vary depending on the kernel
+version. This function takes care to ask the kernel how big each structure
+should be, and allocates the right amount of memory. The
+.BR seccomp_notify_free ()
+function frees memory allocated by
+.BR seccomp_notify_alloc ().
+.P
+The
+.BR seccomp_notify_receive ()
+function receives a notification from a seccomp notify fd (obtained from
+.BR seccomp_notify_fd ()).
+.P
+The
+.BR seccomp_notify_respond ()
+function sends a response to a particular notification. The id field should be
+the same as the id from the request, so that the kernel knows which request
+this response corresponds to.
+.P
+The
+.BR seccomp_notify_id_valid ()
+function checks to see if the syscall from a particualr notification request is
+still valid, i.e. if the task is still alive. See NOTES below for details on
+race conditions.
+.P
+The
+.BR seccomp_notify_fd ()
+returns the notification fd of a filter after it has been loaded.
+.\" //////////////////////////////////////////////////////////////////////////
+.SH RETURN VALUE
+.\" //////////////////////////////////////////////////////////////////////////
+.P
+The
+.BR seccomp_notify_alloc (),
+.BR seccomp_notify_receive (),
+and
+.BR seccomp_notify_respond ()
+functions all return 0 on success, -1 on failure.
+.P
+The
+.BR seccomp_notify_id_valid ()
+returns 0 if the id is valid, and -ENOENT if it is not.
+.P
+The
+.BR seccomp_notify_fd ()
+returns the notification fd of the loaded filter.
+.\" //////////////////////////////////////////////////////////////////////////
+.SH NOTES
+.\" //////////////////////////////////////////////////////////////////////////
+.P
+Care should be taken to avoid two different time of check/time of use errors.
+First, after opening any resources relevant to the pid for a notification (e.g.
+/proc/pid/mem for reading tracee memory to make policy decisions), applications
+should call
+.BR seccomp_notify_id_valid ()
+to make sure that the resources the application has opened correspond to the
+right pid, i.e. that the pid didn't die and a different task take its place.
+.P
+Second, the classic time of check/time of use issue with seccomp memory should
+also be avoided: applications should copy any memory they wish to use to make
+decisions from the tracee into its own address space before applying any policy
+decisions, since a multi-threaded tracee may edit the memory at any time,
+including after it's used to make a policy decision.
+.P
+A complete example of how to avoid these two races is available in the Linux
+Kernel source tree at
+.BR /samples/seccomp/user-trap.c.
+.\" //////////////////////////////////////////////////////////////////////////
+.SH AUTHOR
+.\" //////////////////////////////////////////////////////////////////////////
+Tycho Andersen <tycho@tycho.ws>
+.\" //////////////////////////////////////////////////////////////////////////
--- /dev/null
+.so man3/seccomp_notify_alloc.3
--- /dev/null
+.so man3/seccomp_notify_alloc.3
--- /dev/null
+.so man3/seccomp_notify_alloc.3
--- /dev/null
+.so man3/seccomp_notify_alloc.3
--- /dev/null
+.so man3/seccomp_notify_alloc.3
#include <inttypes.h>
#include <asm/unistd.h>
#include <linux/audit.h>
+#include <linux/types.h>
+#include <linux/seccomp.h>
#ifdef __cplusplus
extern "C" {
* Throw a SIGSYS signal
*/
#define SCMP_ACT_TRAP 0x00030000U
+/**
+ * Notifies userspace
+ */
+#define SCMP_ACT_NOTIFY 0x7fc00000U
/**
* Return the specified error code
*/
*/
#define SCMP_ACT_ALLOW 0x7fff0000U
+/* SECCOMP_RET_USER_NOTIF was added in kernel v5.0. */
+#ifndef SECCOMP_RET_USER_NOTIF
+#define SECCOMP_RET_USER_NOTIF 0x7fc00000U
+
+struct seccomp_notif {
+ __u64 id;
+ __u32 pid;
+ __u32 flags;
+ struct seccomp_data data;
+};
+
+struct seccomp_notif_resp {
+ __u64 id;
+ __s64 val;
+ __s32 error;
+ __u32 flags;
+};
+#endif
+
/*
* functions
*/
* support for the SCMP_ACT_LOG action
* support for the SCMP_ACT_KILL_PROCESS action
* 4 : support for the SCMP_FLTATR_CTL_SSB filter attrbute
+ * 5 : support for the SCMP_ACT_NOTIFY action
*
*/
unsigned int seccomp_api_get(void);
unsigned int arg_cnt,
const struct scmp_arg_cmp *arg_array);
+/**
+ * Allocate a pair of notification request/response structures.
+ * @param req the request location
+ * @param resp the response location
+ *
+ * This function allocates a pair of request/response structure by computing
+ * the correct sized based on the currently running kernel. It returns zero on
+ * success, and negative values on failure.
+ *
+ */
+int seccomp_notify_alloc(struct seccomp_notif **req,
+ struct seccomp_notif_resp **resp);
+
+/**
+ * Free a pair of notification request/response structures.
+ * @param req the request location
+ * @param resp the response location
+ */
+void seccomp_notify_free(struct seccomp_notif *req,
+ struct seccomp_notif_resp *resp);
+/**
+ * Receive a notification from a seccomp notification fd.
+ * @param fd the notification fd
+ * @param req the request buffer to save into
+ *
+ * Blocks waiting for a notification on this fd. This function is thread safe
+ * (synchronization is performed in the kernel). Returns zero on success,
+ * negative values on error.
+ *
+ */
+int seccomp_notify_receive(int fd, struct seccomp_notif *req);
+
+/**
+ * Send a notification response to a seccomp notification fd.
+ * @param fd the notification fd
+ * @param resp the response buffer to use
+ *
+ * Sends a notification response on this fd. This function is thread safe
+ * (synchronization is performed in the kernel). Returns zero on success,
+ * negative values on error.
+ *
+ */
+int seccomp_notify_respond(int fd, struct seccomp_notif_resp *resp);
+
+/**
+ * Check if a notification id is still valid.
+ * @param fd the notification fd
+ * @param id the id to test
+ *
+ * Checks to see if a notification id is still valid. Returns 0 on success, and
+ * negative values on failure.
+ *
+ */
+int seccomp_notify_id_valid(int fd, uint64_t id);
+
+/**
+ * Return the notification fd from a filter that has already been loaded.
+ * @param ctx the filter context
+ *
+ * This returns the listener fd that was generated when the seccomp policy was
+ * loaded. This is only valid after seccomp_load() with a filter that makes
+ * use of SCMP_ACT_NOTIFY.
+ *
+ */
+int seccomp_notify_fd(const scmp_filter_ctx ctx);
+
/**
* Generate seccomp Pseudo Filter Code (PFC) and export it to a file
* @param ctx the filter context
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
+#include <sys/ioctl.h>
#include <seccomp.h>
#include "db.h"
#include "gen_pfc.h"
#include "gen_bpf.h"
+#include "helper.h"
#include "system.h"
#define API __attribute__((visibility("default")))
sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW) == 1)
level = 4;
+ if (level == 4 &&
+ sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_NEW_LISTENER) == 1)
+ level = 5;
+
/* update the stored api level and return */
seccomp_api_level = level;
return seccomp_api_level;
sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, true);
sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW, true);
break;
+ case 5:
+ sys_set_seccomp_syscall(true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, true);
+ sys_set_seccomp_action(SCMP_ACT_LOG, true);
+ sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_NEW_LISTENER, true);
+ sys_set_seccomp_action(SCMP_ACT_NOTIFY, true);
+ break;
default:
return -EINVAL;
}
return rc;
}
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_notify_alloc(struct seccomp_notif **req,
+ struct seccomp_notif_resp **resp)
+{
+ return sys_notify_alloc(req, resp);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API void seccomp_notify_free(struct seccomp_notif *req,
+ struct seccomp_notif_resp *resp)
+{
+ if (req)
+ free(req);
+ if (resp)
+ free(resp);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_notify_receive(int fd, struct seccomp_notif *req)
+{
+ return sys_notify_receive(fd, req);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_notify_respond(int fd, struct seccomp_notif_resp *resp)
+{
+ return sys_notify_respond(fd, resp);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_notify_id_valid(int fd, uint64_t id)
+{
+ return sys_notify_id_valid(fd, id);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_notify_fd(const scmp_filter_ctx ctx)
+{
+ struct db_filter_col *col;
+
+ if (_ctx_valid(ctx))
+ return -EINVAL;
+ col = (struct db_filter_col *)ctx;
+
+ return col->notify_fd;
+}
+
/* NOTE - function header comment in include/seccomp.h */
API int seccomp_export_pfc(const scmp_filter_ctx ctx, int fd)
{
if (col->filters)
free(col->filters);
col->filters = NULL;
+ col->notify_fd = -1;
/* set the endianess to undefined */
col->endian = 0;
/* transaction snapshots */
struct db_filter_snap *snapshots;
+
+ /* notification fd that was returned from seccomp() */
+ int notify_fd;
};
/**
#define _GNU_SOURCE
#include <unistd.h>
+#include "system.h"
+
#include <seccomp.h>
#include "arch.h"
#include "db.h"
#include "gen_bpf.h"
-#include "system.h"
+#include "helper.h"
/* NOTE: the seccomp syscall whitelist is currently disabled for testing
* purposes, but unless we can verify all of the supported ABIs before
static int _support_seccomp_action_log = -1;
static int _support_seccomp_kill_process = -1;
static int _support_seccomp_flag_spec_allow = -1;
+static int _support_seccomp_flag_new_listener = -1;
+static int _support_seccomp_user_notif = -1;
/**
* Check to see if the seccomp() syscall is supported
return _support_seccomp_action_log;
} else if (action == SCMP_ACT_ALLOW) {
return 1;
+ } else if (action == SCMP_ACT_NOTIFY) {
+ if (_support_seccomp_user_notif < 0) {
+ struct seccomp_notif_sizes sizes;
+ if (sys_chk_seccomp_syscall() == 1 &&
+ syscall(_nr_seccomp, SECCOMP_GET_NOTIF_SIZES, 0,
+ &sizes) == 0)
+ _support_seccomp_user_notif = 1;
+ else
+ _support_seccomp_user_notif = 0;
+ }
+
+ return _support_seccomp_user_notif;
}
return 0;
*/
void sys_set_seccomp_action(uint32_t action, bool enable)
{
- if (action == SCMP_ACT_LOG)
+ switch (action) {
+ case SCMP_ACT_LOG:
_support_seccomp_action_log = (enable ? 1 : 0);
- else if (action == SCMP_ACT_KILL_PROCESS)
+ break;
+ case SCMP_ACT_KILL_PROCESS:
_support_seccomp_kill_process = (enable ? 1 : 0);
+ break;
+ case SCMP_ACT_NOTIFY:
+ _support_seccomp_user_notif = (enable ? 1 : 0);
+ break;
+ }
}
/**
_support_seccomp_flag_spec_allow = _sys_chk_seccomp_flag_kernel(flag);
return _support_seccomp_flag_spec_allow;
+ case SECCOMP_FILTER_FLAG_NEW_LISTENER:
+ if (_support_seccomp_flag_new_listener < 0)
+ _support_seccomp_flag_new_listener = _sys_chk_seccomp_flag_kernel(flag);
+
+ return _support_seccomp_flag_new_listener;
}
return -EOPNOTSUPP;
case SECCOMP_FILTER_FLAG_SPEC_ALLOW:
_support_seccomp_flag_spec_allow = (enable ? 1 : 0);
break;
+ case SECCOMP_FILTER_FLAG_NEW_LISTENER:
+ _support_seccomp_flag_new_listener = (enable ? 1 : 0);
+ break;
}
}
* error.
*
*/
-int sys_filter_load(const struct db_filter_col *col)
+int sys_filter_load(struct db_filter_col *col)
{
int rc;
struct bpf_program *prgm = NULL;
flgs |= SECCOMP_FILTER_FLAG_LOG;
if (col->attr.spec_allow)
flgs |= SECCOMP_FILTER_FLAG_SPEC_ALLOW;
+ if (_support_seccomp_user_notif > 0)
+ flgs |= SECCOMP_FILTER_FLAG_NEW_LISTENER;
rc = syscall(_nr_seccomp, SECCOMP_SET_MODE_FILTER, flgs, prgm);
if (rc > 0 && col->attr.tsync_enable)
/* always return -ESRCH if we fail to sync threads */
errno = ESRCH;
+ if (rc > 0 && _support_seccomp_user_notif > 0) {
+ /* return 0 on NEW_LISTENER success, but save the fd */
+ col->notify_fd = rc;
+ rc = 0;
+ }
} else
rc = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, prgm);
gen_bpf_release(prgm);
if (rc < 0)
return -errno;
+ return rc;
+}
+
+int sys_notify_alloc(struct seccomp_notif **req,
+ struct seccomp_notif_resp **resp)
+{
+ int rc;
+ static struct seccomp_notif_sizes sizes = { 0, 0, 0 };
+
+ if (_support_seccomp_syscall <= 0)
+ return -EOPNOTSUPP;
+
+ if (sizes.seccomp_notif == 0 && sizes.seccomp_notif_resp == 0) {
+ rc = syscall(__NR_seccomp, SECCOMP_GET_NOTIF_SIZES, 0, &sizes);
+ if (rc < 0)
+ return -errno;
+ }
+ if (sizes.seccomp_notif == 0 || sizes.seccomp_notif_resp == 0)
+ return -EFAULT;
+
+ if (req) {
+ *req = zmalloc(sizes.seccomp_notif);
+ if (!*req)
+ return -ENOMEM;
+ }
+
+ if (resp) {
+ *resp = zmalloc(sizes.seccomp_notif_resp);
+ if (!*resp) {
+ if (req)
+ free(*req);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+int sys_notify_receive(int fd, struct seccomp_notif *req)
+{
+ if (_support_seccomp_user_notif <= 0)
+ return -EOPNOTSUPP;
+
+ if (ioctl(fd, SECCOMP_IOCTL_NOTIF_RECV, req) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int sys_notify_respond(int fd, struct seccomp_notif_resp *resp)
+{
+ if (_support_seccomp_user_notif <= 0)
+ return -EOPNOTSUPP;
+
+ if (ioctl(fd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0)
+ return -errno;
+ return 0;
+}
+
+int sys_notify_id_valid(int fd, uint64_t id)
+{
+ if (_support_seccomp_user_notif <= 0)
+ return -EOPNOTSUPP;
+
+ if (ioctl(fd, SECCOMP_IOCTL_NOTIF_ID_VALID, &id) < 0)
+ return -errno;
return 0;
}
#ifndef _SYSTEM_H
#define _SYSTEM_H
+#include <inttypes.h>
+#include <stdbool.h>
#include <linux/filter.h>
+#include <linux/types.h>
#include <sys/prctl.h>
-
+#include <sys/ioctl.h>
#include "configure.h"
/* NOTE: this was taken from the Linux Kernel sources */
#else
/* NOTE: the definitions below were taken from the Linux Kernel sources */
-#include <linux/types.h>
/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, <mode>) */
#define SECCOMP_MODE_DISABLED 0 /* seccomp is not in use. */
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
#define SECCOMP_RET_USER_NOTIF 0x7fc00000U /* notifies userspace */
#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */
-#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */
/* Masks for the return value sections. */
-#define SECCOMP_RET_ACTION_FULL 0xffff0000U
#define SECCOMP_RET_ACTION 0x7fff0000U
#define SECCOMP_RET_DATA 0x0000ffffU
__u64 args[6];
};
-struct seccomp_notif_sizes {
- __u16 seccomp_notif;
- __u16 seccomp_notif_resp;
- __u16 seccomp_data;
-};
-
-struct seccomp_notif {
- __u64 id;
- __u32 pid;
- __u32 flags;
- struct seccomp_data data;
-};
-
-struct seccomp_notif_resp {
- __u64 id;
- __s64 val;
- __s32 error;
- __u32 flags;
-};
-
-#define SECCOMP_IOC_MAGIC '!'
-#define SECCOMP_IO(nr) _IO(SECCOMP_IOC_MAGIC, nr)
-#define SECCOMP_IOR(nr, type) _IOR(SECCOMP_IOC_MAGIC, nr, type)
-#define SECCOMP_IOW(nr, type) _IOW(SECCOMP_IOC_MAGIC, nr, type)
-#define SECCOMP_IOWR(nr, type) _IOWR(SECCOMP_IOC_MAGIC, nr, type)
-
-/* Flags for seccomp notification fd ioctl. */
-#define SECCOMP_IOCTL_NOTIF_RECV SECCOMP_IOWR(0, struct seccomp_notif)
-#define SECCOMP_IOCTL_NOTIF_SEND SECCOMP_IOWR(1, \
- struct seccomp_notif_resp)
-#define SECCOMP_IOCTL_NOTIF_ID_VALID SECCOMP_IOR(2, __u64)
-
#endif /* HAVE_LINUX_SECCOMP_H */
/* rename some of the socket filter types to make more sense */
#define SECCOMP_FILTER_FLAG_LOG (1UL << 1)
#endif
#ifndef SECCOMP_FILTER_FLAG_SPEC_ALLOW
-#define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2)
+#define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2)
#endif
#ifndef SECCOMP_FILTER_FLAG_NEW_LISTENER
#define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
#endif
#ifndef SECCOMP_RET_LOG
-#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */
+#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */
#endif
-/* SECCOMP_RET_ACTION_FULL was added in kernel v4.14. It may not be
- * defined on older kernels
- */
+/* SECCOMP_RET_ACTION_FULL was added in kernel v4.14. */
#ifndef SECCOMP_RET_ACTION_FULL
#define SECCOMP_RET_ACTION_FULL 0xffff0000U
#endif
-/* SECCOMP_RET_LOG was added in kernel v4.14. It may not be defined on
- * older kernels.
- */
+/* SECCOMP_RET_LOG was added in kernel v4.14. */
#ifndef SECCOMP_RET_LOG
#define SECCOMP_RET_LOG 0x7fc00000U
#endif
-/* SECCOMP_RET_USER_NOTIF was added in kernel 5.0. It may not be defined on
- * older kernels. This version also added the structures below, so let's define
- * those if the header doesn't have this definiton.
- */
+/* SECCOMP_RET_USER_NOTIF was added in kernel v5.0. */
#ifndef SECCOMP_RET_USER_NOTIF
-#define SECCOMP_RET_USER_NOTIF 0x7fc00000U
+#define SECCOMP_RET_USER_NOTIF 0x7fc00000U
struct seccomp_notif_sizes {
__u16 seccomp_notif;
#define SECCOMP_IOW(nr, type) _IOW(SECCOMP_IOC_MAGIC, nr, type)
#define SECCOMP_IOWR(nr, type) _IOWR(SECCOMP_IOC_MAGIC, nr, type)
-/* Flags for seccomp notification fd ioctl. */
+/* flags for seccomp notification fd ioctl */
#define SECCOMP_IOCTL_NOTIF_RECV SECCOMP_IOWR(0, struct seccomp_notif)
#define SECCOMP_IOCTL_NOTIF_SEND SECCOMP_IOWR(1, \
struct seccomp_notif_resp)
#define SECCOMP_IOCTL_NOTIF_ID_VALID SECCOMP_IOR(2, __u64)
-#endif
+#endif /* SECCOMP_RET_USER_NOTIF */
int sys_chk_seccomp_syscall(void);
void sys_set_seccomp_syscall(bool enable);
int sys_chk_seccomp_flag(int flag);
void sys_set_seccomp_flag(int flag, bool enable);
-int sys_filter_load(const struct db_filter_col *col);
+int sys_filter_load(struct db_filter_col *col);
+int sys_notify_alloc(struct seccomp_notif **req,
+ struct seccomp_notif_resp **resp);
+int sys_notify_receive(int fd, struct seccomp_notif *req);
+int sys_notify_respond(int fd, struct seccomp_notif_resp *resp);
+int sys_notify_id_valid(int fd, uint64_t id);
#endif