Skip to content

Commit

Permalink
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.

Signed-off-by: Tycho Andersen <tycho@tycho.ws>
  • Loading branch information
tych0 committed Mar 20, 2019
1 parent 7e8bd40 commit 7f36c76
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 3 deletions.
101 changes: 101 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,48 @@ 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. It will 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.
*/
#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 +417,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 +744,58 @@ 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_alloc_notifications(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_free_notifications(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_receive_notif(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_send_notif_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);

/*
* 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,6 +27,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/ioctl.h>

#include <seccomp.h>

Expand Down Expand Up @@ -112,6 +113,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 @@ -530,3 +535,64 @@ API int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd)

return 0;
}

/* NOTE - function header comment in include/seccomp.h */
int seccomp_alloc_notifications(struct seccomp_notif **req,
struct seccomp_notif_resp **resp)
{
static struct seccomp_notif_sizes sizes = {};

if (sizes.seccomp_notif == 0) {
int ret = syscall(__NR_seccomp, SECCOMP_GET_NOTIF_SIZES, 0, &sizes);
if (ret < 0)
return errno;
} else {
return -1;
}

*req = malloc(sizes.seccomp_notif);
if (!*req)
return -ENOMEM;
memset(*req, 0, sizes.seccomp_notif);

*resp = malloc(sizes.seccomp_notif_resp);
if (!*resp) {
free(req);
return -ENOMEM;
}
memset(*resp, 0, sizes.seccomp_notif_resp);

return 0;
}

/* NOTE - function header comment in include/seccomp.h */
void seccomp_free_notifications(struct seccomp_notif *req,
struct seccomp_notif_resp *resp)
{
free(req);
free(resp);
}

/* NOTE - function header comment in include/seccomp.h */
int seccomp_receive_notif(int fd, struct seccomp_notif *req)
{
if (ioctl(fd, SECCOMP_IOCTL_NOTIF_RECV, req) < 0)
return errno;
return 0;
}

/* NOTE - function header comment in include/seccomp.h */
int seccomp_send_notif_resp(int fd, struct seccomp_notif_resp *resp)
{
if (ioctl(fd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0)
return errno;
return 0;
}

/* NOTE - function header comment in include/seccomp.h */
int seccomp_notif_id_valid(int fd, uint64_t id)
{
if (ioctl(fd, SECCOMP_IOCTL_NOTIF_ID_VALID) < 0)
return errno;
return 0;
}
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
2 changes: 2 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
16 changes: 15 additions & 1 deletion src/system.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ static int _support_seccomp_flag_tsync = -1;
static int _support_seccomp_flag_log = -1;
static int _support_seccomp_action_log = -1;
static int _support_seccomp_kill_process = -1;
static int _support_seccomp_user_notif = -1;

/**
* Check to see if the seccomp() syscall is supported
Expand Down Expand Up @@ -157,6 +158,17 @@ int sys_chk_seccomp_action(uint32_t action)
return _support_seccomp_action_log;
} else if (action == SCMP_ACT_ALLOW) {
return 1;
} else if (action == SCMP_ACT_USER_NOTIF) {
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 1;
}

return 0;
Expand Down Expand Up @@ -282,6 +294,8 @@ int sys_filter_load(const struct db_filter_col *col)
flgs |= SECCOMP_FILTER_FLAG_LOG;
if (col->attr.spec_allow)
flgs |= SECCOMP_FILTER_FLAG_SPEC_ALLOW;
if (col->attr.new_listener)
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 */
Expand All @@ -294,5 +308,5 @@ int sys_filter_load(const struct db_filter_col *col)
gen_bpf_release(prgm);
if (rc < 0)
return -errno;
return 0;
return rc;
}
1 change: 1 addition & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ util.pyc
47-live-kill_process
48-sim-32b_args
49-sim-64b_comparisons
50-user-notification
Loading

0 comments on commit 7f36c76

Please sign in to comment.