/*
* Copyright 2015 IBM
* Author: Jan Willeke <willeke@linux.vnet.com.com>
*/
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <linux/audit.h>
#include "db.h"
#include "syscalls.h"
#include "arch.h"
#include "arch-s390.h"
/* s390 syscall numbers */
#define __s390_NR_socketcall 102
#define __s390_NR_ipc 117
/**
* Resolve a syscall name to a number
* @param name the syscall name
*
* Resolve the given syscall name to the syscall number using the syscall table.
* Returns the syscall number on success, including negative pseudo syscall
* numbers; returns __NR_SCMP_ERROR on failure.
*
*/
int s390_syscall_resolve_name_munge(const char *name)
{
if (strcmp(name, "accept") == 0)
return __PNR_accept;
if (strcmp(name, "accept4") == 0)
return __PNR_accept4;
else if (strcmp(name, "bind") == 0)
return __PNR_bind;
else if (strcmp(name, "connect") == 0)
return __PNR_connect;
else if (strcmp(name, "getpeername") == 0)
return __PNR_getpeername;
else if (strcmp(name, "getsockname") == 0)
return __PNR_getsockname;
else if (strcmp(name, "getsockopt") == 0)
return __PNR_getsockopt;
else if (strcmp(name, "listen") == 0)
return __PNR_listen;
else if (strcmp(name, "msgctl") == 0)
return __PNR_msgctl;
else if (strcmp(name, "msgget") == 0)
return __PNR_msgget;
else if (strcmp(name, "msgrcv") == 0)
return __PNR_msgrcv;
else if (strcmp(name, "msgsnd") == 0)
return __PNR_msgsnd;
else if (strcmp(name, "recv") == 0)
return __PNR_recv;
else if (strcmp(name, "recvfrom") == 0)
return __PNR_recvfrom;
else if (strcmp(name, "recvmsg") == 0)
return __PNR_recvmsg;
else if (strcmp(name, "semctl") == 0)
return __PNR_semctl;
else if (strcmp(name, "semget") == 0)
return __PNR_semget;
else if (strcmp(name, "semtimedop") == 0)
return __PNR_semtimedop;
else if (strcmp(name, "recvmmsg") == 0)
return __PNR_recvmmsg;
else if (strcmp(name, "send") == 0)
return __PNR_send;
else if (strcmp(name, "sendmsg") == 0)
return __PNR_sendmsg;
else if (strcmp(name, "sendmmsg") == 0)
return __PNR_sendmmsg;
else if (strcmp(name, "sendto") == 0)
return __PNR_sendto;
else if (strcmp(name, "setsockopt") == 0)
return __PNR_setsockopt;
else if (strcmp(name, "shmat") == 0)
return __PNR_shmat;
else if (strcmp(name, "shmdt") == 0)
return __PNR_shmdt;
else if (strcmp(name, "shmget") == 0)
return __PNR_shmget;
else if (strcmp(name, "shmctl") == 0)
return __PNR_shmctl;
else if (strcmp(name, "shutdown") == 0)
return __PNR_shutdown;
else if (strcmp(name, "socket") == 0)
return __PNR_socket;
else if (strcmp(name, "socketpair") == 0)
return __PNR_socketpair;
return s390_syscall_resolve_name(name);
}
/**
* Resolve a syscall number to a name
* @param num the syscall number
*
* Resolve the given syscall number to the syscall name using the syscall table.
* Returns a pointer to the syscall name string on success, including pseudo
* syscall names; returns NULL on failure.
*
*/
const char *s390_syscall_resolve_num_munge(int num)
{
if (num == __PNR_accept)
return "accept";
else if (num == __PNR_accept4)
return "accept4";
else if (num == __PNR_bind)
return "bind";
else if (num == __PNR_connect)
return "connect";
else if (num == __PNR_getpeername)
return "getpeername";
else if (num == __PNR_getsockname)
return "getsockname";
else if (num == __PNR_getsockopt)
return "getsockopt";
else if (num == __PNR_listen)
return "listen";
else if (num == __PNR_msgctl)
return "msgctl";
else if (num == __PNR_msgget)
return "msgget";
else if (num == __PNR_msgrcv)
return "msgrcv";
else if (num == __PNR_msgsnd)
return "msgsnd";
else if (num == __PNR_recv)
return "recv";
else if (num == __PNR_recvfrom)
return "recvfrom";
else if (num == __PNR_recvmsg)
return "recvmsg";
else if (num == __PNR_recvmmsg)
return "recvmmsg";
else if (num == __PNR_semctl)
return "semctl";
else if (num == __PNR_semget)
return "semget";
else if (num == __PNR_semtimedop)
return "semtimedop";
else if (num == __PNR_send)
return "send";
else if (num == __PNR_sendmsg)
return "sendmsg";
else if (num == __PNR_sendmmsg)
return "sendmmsg";
else if (num == __PNR_sendto)
return "sendto";
else if (num == __PNR_setsockopt)
return "setsockopt";
else if (num == __PNR_shmat)
return "shmat";
else if (num == __PNR_shmdt)
return "shmdt";
else if (num == __PNR_shmget)
return "shmget";
else if (num == __PNR_shmctl)
return "shmctl";
else if (num == __PNR_shutdown)
return "shutdown";
else if (num == __PNR_socket)
return "socket";
else if (num == __PNR_socketpair)
return "socketpair";
return s390_syscall_resolve_num(num);
}
/**
* Convert a multiplexed pseudo syscall into a direct syscall
* @param syscall the multiplexed pseudo syscall number
*
* Return the related direct syscall number, __NR_SCMP_UNDEF is there is
* no related syscall, or __NR_SCMP_ERROR otherwise.
*
*/
static int _s390_syscall_demux(int syscall)
{
switch (syscall) {
case -101:
/* socket */
return 359;
case -102:
/* bind */
return 361;
case -103:
/* connect */
return 362;
case -104:
/* listen */
return 363;
case -105:
/* accept - not defined */
return __NR_SCMP_UNDEF;
case -106:
/* getsockname */
return 367;
case -107:
/* getpeername */
return 368;
case -108:
/* socketpair */
return 360;
case -109:
/* send - not defined */
return __NR_SCMP_UNDEF;
case -110:
/* recv - not defined */
return __NR_SCMP_UNDEF;
case -111:
/* sendto */
return 369;
case -112:
/* recvfrom */
return 371;
case -113:
/* shutdown */
return 373;
case -114:
/* setsockopt */
return 366;
case -115:
/* getsockopt */
return 365;
case -116:
/* sendmsg */
return 370;
case -117:
/* recvmsg */
return 372;
case -118:
/* accept4 */
return 364;
case -119:
/* recvmmsg */
return 337;
case -120:
/* sendmmsg */
return 345;
case -201:
/* semop - not defined */
return __NR_SCMP_UNDEF;
case -202:
/* semget */
return 393;
case -203:
/* semctl */
return 394;
case -204:
/* semtimedop */
return 392;
case -211:
/* msgsnd */
return 400;
case -212:
/* msgrcv */
return 401;
case -213:
/* msgget */
return 399;
case -214:
/* msgctl */
return 402;
case -221:
/* shmat */
return 397;
case -222:
/* shmdt */
return 398;
case -223:
/* shmget */
return 395;
case -224:
/* shmctl */
return 396;
}
return __NR_SCMP_ERROR;
}
/**
* Convert a direct socket syscall into multiplexed pseudo socket syscall
* @param syscall the direct syscall
*
* Return the related multiplexed pseduo syscall number, __NR_SCMP_UNDEF is
* there is no related pseudo syscall, or __NR_SCMP_ERROR otherwise.
*
*/
static int _s390_syscall_mux(int syscall)
{
switch (syscall) {
case 337:
/* recvmmsg */
return -119;
case 345:
/* sendmmsg */
return -120;
case 359:
/* socket */
return -101;
case 360:
/* socketpair */
return -108;
case 361:
/* bind */
return -102;
case 362:
/* connect */
return -103;
case 363:
/* listen */
return -104;
case 364:
/* accept4 */
return -118;
case 365:
/* getsockopt */
return -115;
case 366:
/* setsockopt */
return -114;
case 367:
/* getsockname */
return -106;
case 368:
/* getpeername */
return -107;
case 369:
/* sendto */
return -111;
case 370:
/* sendmsg */
return -116;
case 371:
/* recvfrom */
return -112;
case 372:
/* recvmsg */
return -117;
case 373:
/* shutdown */
return -113;
case 393:
/* semget */
return -202;
case 394:
/* semctl */
return -203;
case 400:
/* msgsnd */
return -211;
case 401:
/* msgrcv */
return -212;
case 399:
/* msgget */
return -213;
case 402:
/* msgctl */
return -214;
case 397:
/* shmat */
return -221;
case 398:
/* shmdt */
return -222;
case 395:
/* shmget */
return -223;
case 396:
/* shmctl */
return -224;
case 392:
/* semtimedop */
return -204;
}
return __NR_SCMP_ERROR;
}
/**
* Rewrite a syscall value to match the architecture
* @param syscall the syscall number
*
* Syscalls can vary across different architectures so this function rewrites
* the syscall into the correct value for the specified architecture. Returns
* zero on success, negative values on failure.
*
*/
int s390_syscall_rewrite(int *syscall)
{
int sys = *syscall;
if (sys <= -100 && sys >= -120)
*syscall = __s390_NR_socketcall;
else if (sys <= -200 && sys >= -224)
*syscall = __s390_NR_ipc;
else if (sys < 0)
return -EDOM;
return 0;
}
/**
* add a new rule to the s390 seccomp filter
* @param db the seccomp filter db
* @param rule the filter rule
*
* This function adds a new syscall filter to the seccomp filter db, making any
* necessary adjustments for the s390 ABI. Returns zero on success, negative
* values on failure.
*
* It is important to note that in the case of failure the db may be corrupted,
* the caller must use the transaction mechanism if the db integrity is
* important.
*
*/
int s390_rule_add(struct db_filter *db, struct db_api_rule_list *rule)
{
int rc = 0;
unsigned int iter;
int sys = rule->syscall;
int sys_a, sys_b;
struct db_api_rule_list *rule_a, *rule_b, *rule_dup = NULL;
if ((sys <= -100 && sys >= -120) || (sys >= 359 && sys <= 373)) {
/* (-100 to -120) : multiplexed socket syscalls
(359 to 373) : direct socket syscalls, Linux 4.3+ */
/* strict check for the multiplexed socket syscalls */
for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
if ((rule->args[iter].valid != 0) && (rule->strict)) {
rc = -EINVAL;
goto add_return;
}
}
/* determine both the muxed and direct syscall numbers */
if (sys > 0) {
sys_a = _s390_syscall_mux(sys);
if (sys_a == __NR_SCMP_ERROR) {
rc = __NR_SCMP_ERROR;
goto add_return;
}
sys_b = sys;
} else {
sys_a = sys;
sys_b = _s390_syscall_demux(sys);
if (sys_b == __NR_SCMP_ERROR) {
rc = __NR_SCMP_ERROR;
goto add_return;
}
}
/* use rule_a for the multiplexed syscall and use rule_b for
* the direct wired syscall */
if (sys_a == __NR_SCMP_UNDEF) {
rule_a = NULL;
rule_b = rule;
} else if (sys_b == __NR_SCMP_UNDEF) {
rule_a = rule;
rule_b = NULL;
} else {
/* need two rules, dup the first and link together */
rule_a = rule;
rule_dup = db_rule_dup(rule_a);
rule_b = rule_dup;
if (rule_b == NULL) {
rc = -ENOMEM;
goto add_return;
}
rule_b->prev = rule_a;
rule_b->next = NULL;
rule_a->next = rule_b;
}
/* multiplexed socket syscalls */
if (rule_a != NULL) {
rule_a->syscall = __s390_NR_socketcall;
rule_a->args[0].arg = 0;
rule_a->args[0].op = SCMP_CMP_EQ;
rule_a->args[0].mask = DATUM_MAX;
rule_a->args[0].datum = (-sys_a) % 100;
rule_a->args[0].valid = 1;
}
/* direct wired socket syscalls */
if (rule_b != NULL)
rule_b->syscall = sys_b;
/* we should be protected by a transaction checkpoint */
if (rule_a != NULL) {
rc = db_rule_add(db, rule_a);
if (rc < 0)
goto add_return;
}
if (rule_b != NULL) {
rc = db_rule_add(db, rule_b);
if (rc < 0)
goto add_return;
}
} else if ((sys <= -200 && sys >= -224) || (sys >= 393 && sys <= 402)) {
/* (-200 to -224) : multiplexed ipc syscalls
(393 to 402) : direct ipc syscalls */
/* strict check for the multiplexed socket syscalls */
for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
if ((rule->args[iter].valid != 0) && (rule->strict)) {
rc = -EINVAL;
goto add_return;
}
}
/* determine both the muxed and direct syscall numbers */
if (sys > 0) {
sys_a = _s390_syscall_mux(sys);
if (sys_a == __NR_SCMP_ERROR) {
rc = __NR_SCMP_ERROR;
goto add_return;
}
sys_b = sys;
} else {
sys_a = sys;
sys_b = _s390_syscall_demux(sys);
if (sys_b == __NR_SCMP_ERROR) {
rc = __NR_SCMP_ERROR;
goto add_return;
}
}
/* use rule_a for the multiplexed syscall and use rule_b for
* the direct wired syscall */
if (sys_a == __NR_SCMP_UNDEF) {
rule_a = NULL;
rule_b = rule;
} else if (sys_b == __NR_SCMP_UNDEF) {
rule_a = rule;
rule_b = NULL;
} else {
/* need two rules, dup the first and link together */
rule_a = rule;
rule_dup = db_rule_dup(rule_a);
rule_b = rule_dup;
if (rule_b == NULL)
goto add_return;
rule_b->prev = rule_a;
rule_b->next = NULL;
rule_a->next = rule_b;
}
/* multiplexed socket syscalls */
if (rule_a != NULL) {
rule_a->syscall = __s390_NR_ipc;
rule_a->args[0].arg = 0;
rule_a->args[0].op = SCMP_CMP_EQ;
rule_a->args[0].mask = DATUM_MAX;
rule_a->args[0].datum = (-sys_a) % 200;
rule_a->args[0].valid = 1;
}
/* direct wired socket syscalls */
if (rule_b != NULL)
rule_b->syscall = sys_b;
/* we should be protected by a transaction checkpoint */
if (rule_a != NULL) {
rc = db_rule_add(db, rule_a);
if (rc < 0)
goto add_return;
}
if (rule_b != NULL) {
rc = db_rule_add(db, rule_b);
if (rc < 0)
goto add_return;
}
} else if (sys >= 0) {
/* normal syscall processing */
rc = db_rule_add(db, rule);
if (rc < 0)
goto add_return;
} else if (rule->strict) {
rc = -EDOM;
goto add_return;
}
add_return:
if (rule_dup != NULL)
free(rule_dup);
return rc;
}
const struct arch_def arch_def_s390 = {
.token = SCMP_ARCH_S390,
.token_bpf = AUDIT_ARCH_S390,
.size = ARCH_SIZE_32,
.endian = ARCH_ENDIAN_BIG,
.syscall_resolve_name = s390_syscall_resolve_name_munge,
.syscall_resolve_num = s390_syscall_resolve_num_munge,
.syscall_rewrite = s390_syscall_rewrite,
.rule_add = s390_rule_add,
};