Skip to content

Commit

Permalink
api: implement user notification in libseccomp
Browse files Browse the repository at this point in the history
Kernel 5.0 includes the new user notification return code. Here's all the
infrastructure to handle that.

The idea behind the user notification return code is that the filter stops
the syscall, and forwards it to a "listener fd" that is created when
installing a filter. Then then some userspace task can listen and process
events accordingly by taking some (or no) action in userspace, and then
returning a value from the command.

Signed-off-by: Tycho Andersen <tycho@tycho.ws>
  • Loading branch information
tych0 committed Apr 23, 2019
1 parent 3385865 commit 1ad98b2
Show file tree
Hide file tree
Showing 14 changed files with 428 additions and 10 deletions.
4 changes: 4 additions & 0 deletions doc/man/man3/seccomp_api_get.3
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ The SCMP_FLTATR_CTL_LOG filter attribute and the SCMP_ACT_LOG action are support
.TP
.B 4
The SCMP_FLTATR_SPEC_ALLOW filter attribute is supported.
.TP
.B 5
The SCMP_FLTATR_NEW_LISTENER filter attribute and the SCMP_ACT_NEW_LISTENER
action are is supported.
.\" //////////////////////////////////////////////////////////////////////////
.SH RETURN VALUE
.\" //////////////////////////////////////////////////////////////////////////
Expand Down
6 changes: 6 additions & 0 deletions doc/man/man3/seccomp_attr_set.3
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ A flag to disable Speculative Store Bypass mitigations for this filter.
Defaults to off (
.I value
== 0).
.TP
.B SCMP_FLTATR_NEW_LISTENER
Generate a userspace listener file descriptor when this filter is installed.
Defaults to off (
.I value
== 0).
.\" //////////////////////////////////////////////////////////////////////////
.SH RETURN VALUE
.\" //////////////////////////////////////////////////////////////////////////
Expand Down
108 changes: 108 additions & 0 deletions include/seccomp.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <inttypes.h>
#include <asm/unistd.h>
#include <linux/audit.h>
#include <linux/seccomp.h>

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -66,6 +67,7 @@ enum scmp_filter_attr {
SCMP_FLTATR_API_TSKIP = 5, /**< allow rules with a -1 syscall */
SCMP_FLTATR_CTL_LOG = 6, /**< log not-allowed actions */
SCMP_FLTATR_SPEC_ALLOW = 7, /**< disable SSB mitigation */
SCMP_FLTATR_NEW_LISTENER = 8, /**< returns a listener fd */
_SCMP_FLTATR_MAX,
};

Expand Down Expand Up @@ -319,6 +321,10 @@ struct scmp_arg_cmp {
* Throw a SIGSYS signal
*/
#define SCMP_ACT_TRAP 0x00030000U
/**
* Notifies userspace
*/
#define SCMP_ACT_USER_NOTIF 0x7fc00000U
/**
* Return the specified error code
*/
Expand All @@ -336,6 +342,46 @@ struct scmp_arg_cmp {
*/
#define SCMP_ACT_ALLOW 0x7fff0000U

/*
* User notification bits. It's a little unfortunate that we don't export
* system.h, so we end up having to define all these structures again.
* SECCOMP_RET_USER_NOTIF was added in kernel 5.0.
*/
#ifndef SECCOMP_RET_USER_NOTIF
#define SECCOMP_RET_USER_NOTIF 0x7fc00000U

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

/*
* functions
*/
Expand Down Expand Up @@ -369,6 +415,7 @@ const struct scmp_version *seccomp_version(void);
* support for the SCMP_ACT_LOG action
* support for the SCMP_ACT_KILL_PROCESS action
* 4 : support for the SCMP_FLTATR_SPEC_ALLOW filter attrbute
* 5 : support for the SCMP_FLTATR_NEW_LISTENER filter attribute
*
*/
unsigned int seccomp_api_get(void);
Expand Down Expand Up @@ -695,6 +742,67 @@ int seccomp_export_pfc(const scmp_filter_ctx ctx, int fd);
*/
int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd);

/**
* 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_notif_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_notif_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_notif_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_notif_send_resp(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_notif_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
* SCMP_FLTATR_NEW_LISTENER set.
*/
int seccomp_notif_get_fd(const scmp_filter_ctx ctx);

/*
* pseudo syscall definitions
*/
Expand Down
66 changes: 66 additions & 0 deletions src/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/ioctl.h>

#include <seccomp.h>

#include "arch.h"
#include "db.h"
#include "gen_pfc.h"
#include "gen_bpf.h"
#include "helper.h"
#include "system.h"

#define API __attribute__((visibility("default")))
Expand Down Expand Up @@ -112,6 +114,10 @@ static unsigned int _seccomp_api_update(void)
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;
Expand Down Expand Up @@ -163,6 +169,16 @@ API int seccomp_api_set(unsigned int 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_USER_NOTIF, true);
break;
default:
return -EINVAL;
}
Expand Down Expand Up @@ -538,3 +554,53 @@ API int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd)

return 0;
}

/* NOTE - function header comment in include/seccomp.h */
API int seccomp_notif_alloc(struct seccomp_notif **req,
struct seccomp_notif_resp **resp)
{
return sys_notif_alloc(req, resp);
}

/* NOTE - function header comment in include/seccomp.h */
API void seccomp_notif_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_notif_receive(int fd, struct seccomp_notif *req)
{
return sys_notif_receive(fd, req);
}

/* NOTE - function header comment in include/seccomp.h */
API int seccomp_notif_send_resp(int fd, struct seccomp_notif_resp *resp)
{
return sys_notif_send_resp(fd, resp);
}

/* NOTE - function header comment in include/seccomp.h */
API int seccomp_notif_id_valid(int fd, uint64_t id)
{
return sys_notif_id_valid(fd, id);
}

/* NOTE - function header comment in include/seccomp.h */
API int seccomp_notif_get_fd(const scmp_filter_ctx ctx)
{
struct db_filter_col *col;

if (_ctx_valid(ctx))
return -EINVAL;
col = (struct db_filter_col *)ctx;

if (!col->attr.new_listener)
return -EINVAL;

return col->notif_fd;
}
14 changes: 14 additions & 0 deletions src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,9 @@ int db_col_attr_get(const struct db_filter_col *col,
case SCMP_FLTATR_SPEC_ALLOW:
*value = col->attr.spec_allow;
break;
case SCMP_FLTATR_NEW_LISTENER:
*value = col->attr.new_listener;
break;
default:
rc = -EEXIST;
break;
Expand Down Expand Up @@ -1355,6 +1358,17 @@ int db_col_attr_set(struct db_filter_col *col,
rc = -EOPNOTSUPP;
}
break;
case SCMP_FLTATR_NEW_LISTENER:
rc = sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_NEW_LISTENER);
if (rc == 1) {
/* supported */
rc = 0;
col->attr.new_listener = (value ? 1 : 0);
} else if (rc == 0) {
/* unsupported */
rc = -EOPNOTSUPP;
}
break;
default:
rc = -EEXIST;
break;
Expand Down
5 changes: 5 additions & 0 deletions src/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ struct db_filter_attr {
uint32_t log_enable;
/* SPEC_ALLOW related attributes */
uint32_t spec_allow;
/* SECCOMP_FILTER_FLAG_NEW_LISTENER attributes */
uint32_t new_listener;
};

struct db_filter {
Expand Down Expand Up @@ -153,6 +155,9 @@ struct db_filter_col {

/* transaction snapshots */
struct db_filter_snap *snapshots;

/* notification fd that was returned from seccomp() */
int notif_fd;
};

/**
Expand Down
3 changes: 2 additions & 1 deletion src/python/libseccomp.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ cdef extern from "seccomp.h":
SCMP_FLTATR_API_TSKIP
SCMP_FLTATR_CTL_LOG
SCMP_FLTATR_SPEC_ALLOW
SCMP_FLTATR_NEW_LISTENER

cdef enum scmp_compare:
SCMP_CMP_NE
Expand All @@ -75,6 +76,7 @@ cdef extern from "seccomp.h":
SCMP_ACT_TRAP
SCMP_ACT_LOG
SCMP_ACT_ALLOW
SCMP_ACT_USER_NOTIF
unsigned int SCMP_ACT_ERRNO(int errno)
unsigned int SCMP_ACT_TRACE(int value)

Expand Down Expand Up @@ -132,6 +134,5 @@ cdef extern from "seccomp.h":

int seccomp_export_pfc(scmp_filter_ctx ctx, int fd)
int seccomp_export_bpf(scmp_filter_ctx ctx, int fd)

# kate: syntax python;
# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off;
Loading

0 comments on commit 1ad98b2

Please sign in to comment.