/* * Copyright 2015 IBM * Author: Jan Willeke */ #include #include #include #include #include "arch.h" #include "arch-s390x.h" /* s390x syscall numbers */ #define __s390x_NR_socketcall 102 #define __s390x_NR_ipc 117 const struct arch_def arch_def_s390x = { .token = SCMP_ARCH_S390X, .token_bpf = AUDIT_ARCH_S390X, .size = ARCH_SIZE_64, .endian = ARCH_ENDIAN_BIG, .syscall_resolve_name = s390x_syscall_resolve_name, .syscall_resolve_num = s390x_syscall_resolve_num, .syscall_rewrite = s390x_syscall_rewrite, .rule_add = s390x_rule_add, }; /** * Convert a multiplexed pseudo socket 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 _s390x_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 _s390x_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 392: /* semtimedop */ return -204; 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; } 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 s390x_syscall_rewrite(int *syscall) { int sys = *syscall; if (sys <= -100 && sys >= -120) *syscall = __s390x_NR_socketcall; else if (sys <= -200 && sys >= -224) *syscall = __s390x_NR_ipc; else if (sys < 0) return -EDOM; return 0; } /** * add a new rule to the s390x 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 s390x 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 s390x_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 = _s390x_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 = _s390x_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 = __s390x_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 >= 392 && sys <= 402)) { /* (-200 to -224) : multiplexed ipc syscalls (392 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 = _s390x_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 = _s390x_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 = __s390x_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; }