/** * Enhanced Seccomp Syscall Table Functions * * Copyright (c) 2012, 2020 Red Hat * Author: Paul Moore * gperf support: Giuseppe Scrivano */ /* * This library is free software; you can redistribute it and/or modify it * under the terms of version 2.1 of the GNU Lesser General Public License as * published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see . */ #include #include #include #include #include "db.h" #include "arch.h" #include "syscalls.h" /** * Resolve a syscall name to a number * @param arch the arch definition * @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 abi_syscall_resolve_name_munge(const struct arch_def *arch, const char *name) { #define _ABI_SYSCALL_RES_NAME_CHK(NAME) \ if (!strcmp(name, #NAME)) return __PNR_##NAME; _ABI_SYSCALL_RES_NAME_CHK(socket) _ABI_SYSCALL_RES_NAME_CHK(bind) _ABI_SYSCALL_RES_NAME_CHK(connect) _ABI_SYSCALL_RES_NAME_CHK(listen) _ABI_SYSCALL_RES_NAME_CHK(accept) _ABI_SYSCALL_RES_NAME_CHK(getsockname) _ABI_SYSCALL_RES_NAME_CHK(getpeername) _ABI_SYSCALL_RES_NAME_CHK(socketpair) _ABI_SYSCALL_RES_NAME_CHK(send) _ABI_SYSCALL_RES_NAME_CHK(recv) _ABI_SYSCALL_RES_NAME_CHK(sendto) _ABI_SYSCALL_RES_NAME_CHK(recvfrom) _ABI_SYSCALL_RES_NAME_CHK(shutdown) _ABI_SYSCALL_RES_NAME_CHK(setsockopt) _ABI_SYSCALL_RES_NAME_CHK(getsockopt) _ABI_SYSCALL_RES_NAME_CHK(sendmsg) _ABI_SYSCALL_RES_NAME_CHK(recvmsg) _ABI_SYSCALL_RES_NAME_CHK(accept4) _ABI_SYSCALL_RES_NAME_CHK(recvmmsg) _ABI_SYSCALL_RES_NAME_CHK(sendmmsg) _ABI_SYSCALL_RES_NAME_CHK(semop) _ABI_SYSCALL_RES_NAME_CHK(semget) _ABI_SYSCALL_RES_NAME_CHK(semctl) _ABI_SYSCALL_RES_NAME_CHK(semtimedop) _ABI_SYSCALL_RES_NAME_CHK(msgsnd) _ABI_SYSCALL_RES_NAME_CHK(msgrcv) _ABI_SYSCALL_RES_NAME_CHK(msgget) _ABI_SYSCALL_RES_NAME_CHK(msgctl) _ABI_SYSCALL_RES_NAME_CHK(shmat) _ABI_SYSCALL_RES_NAME_CHK(shmdt) _ABI_SYSCALL_RES_NAME_CHK(shmget) _ABI_SYSCALL_RES_NAME_CHK(shmctl) return arch->syscall_resolve_name_raw(name); } /** * Resolve a syscall number to a name * @param arch the arch definition * @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 *abi_syscall_resolve_num_munge(const struct arch_def *arch, int num) { #define _ABI_SYSCALL_RES_NUM_CHK(NAME) \ if (num == __PNR_##NAME) return #NAME; _ABI_SYSCALL_RES_NUM_CHK(socket) _ABI_SYSCALL_RES_NUM_CHK(bind) _ABI_SYSCALL_RES_NUM_CHK(connect) _ABI_SYSCALL_RES_NUM_CHK(listen) _ABI_SYSCALL_RES_NUM_CHK(accept) _ABI_SYSCALL_RES_NUM_CHK(getsockname) _ABI_SYSCALL_RES_NUM_CHK(getpeername) _ABI_SYSCALL_RES_NUM_CHK(socketpair) _ABI_SYSCALL_RES_NUM_CHK(send) _ABI_SYSCALL_RES_NUM_CHK(recv) _ABI_SYSCALL_RES_NUM_CHK(sendto) _ABI_SYSCALL_RES_NUM_CHK(recvfrom) _ABI_SYSCALL_RES_NUM_CHK(shutdown) _ABI_SYSCALL_RES_NUM_CHK(setsockopt) _ABI_SYSCALL_RES_NUM_CHK(getsockopt) _ABI_SYSCALL_RES_NUM_CHK(sendmsg) _ABI_SYSCALL_RES_NUM_CHK(recvmsg) _ABI_SYSCALL_RES_NUM_CHK(accept4) _ABI_SYSCALL_RES_NUM_CHK(recvmmsg) _ABI_SYSCALL_RES_NUM_CHK(sendmmsg) _ABI_SYSCALL_RES_NUM_CHK(semop) _ABI_SYSCALL_RES_NUM_CHK(semget) _ABI_SYSCALL_RES_NUM_CHK(semctl) _ABI_SYSCALL_RES_NUM_CHK(semtimedop) _ABI_SYSCALL_RES_NUM_CHK(msgsnd) _ABI_SYSCALL_RES_NUM_CHK(msgrcv) _ABI_SYSCALL_RES_NUM_CHK(msgget) _ABI_SYSCALL_RES_NUM_CHK(msgctl) _ABI_SYSCALL_RES_NUM_CHK(shmat) _ABI_SYSCALL_RES_NUM_CHK(shmdt) _ABI_SYSCALL_RES_NUM_CHK(shmget) _ABI_SYSCALL_RES_NUM_CHK(shmctl) return arch->syscall_resolve_num_raw(num); } /** * Check if a syscall is a socket syscall * @param arch the arch definition * @param sys the syscall number * * Returns true if the syscall is a socket related syscall, false otherwise. * */ static bool _abi_syscall_socket_test(const struct arch_def *arch, int sys) { const char *name; /* multiplexed pseudo-syscalls */ if (sys <= -100 && sys >= -120) return true; name = arch->syscall_resolve_num_raw(sys); if (!name) return false; #define _ABI_SYSCALL_SOCK_CHK(NAME) \ if (!strcmp(name, #NAME)) return true; _ABI_SYSCALL_SOCK_CHK(socket) _ABI_SYSCALL_SOCK_CHK(bind) _ABI_SYSCALL_SOCK_CHK(connect) _ABI_SYSCALL_SOCK_CHK(listen) _ABI_SYSCALL_SOCK_CHK(accept) _ABI_SYSCALL_SOCK_CHK(getsockname) _ABI_SYSCALL_SOCK_CHK(getpeername) _ABI_SYSCALL_SOCK_CHK(socketpair) _ABI_SYSCALL_SOCK_CHK(send) _ABI_SYSCALL_SOCK_CHK(recv) _ABI_SYSCALL_SOCK_CHK(sendto) _ABI_SYSCALL_SOCK_CHK(recvfrom) _ABI_SYSCALL_SOCK_CHK(shutdown) _ABI_SYSCALL_SOCK_CHK(setsockopt) _ABI_SYSCALL_SOCK_CHK(getsockopt) _ABI_SYSCALL_SOCK_CHK(sendmsg) _ABI_SYSCALL_SOCK_CHK(recvmsg) _ABI_SYSCALL_SOCK_CHK(accept4) _ABI_SYSCALL_SOCK_CHK(recvmmsg) _ABI_SYSCALL_SOCK_CHK(sendmmsg) return false; } /** * Check if a syscall is an ipc syscall * @param arch the arch definition * @param sys the syscall number * * Returns true if the syscall is an ipc related syscall, false otherwise. * */ static bool _abi_syscall_ipc_test(const struct arch_def *arch, int sys) { const char *name; /* multiplexed pseudo-syscalls */ if (sys <= -200 && sys >= -224) return true; name = arch->syscall_resolve_num_raw(sys); if (!name) return false; #define _ABI_SYSCALL_IPC_CHK(NAME) \ if (!strcmp(name, #NAME)) return true; _ABI_SYSCALL_IPC_CHK(semop) _ABI_SYSCALL_IPC_CHK(semget) _ABI_SYSCALL_IPC_CHK(semctl) _ABI_SYSCALL_IPC_CHK(semtimedop) _ABI_SYSCALL_IPC_CHK(msgsnd) _ABI_SYSCALL_IPC_CHK(msgrcv) _ABI_SYSCALL_IPC_CHK(msgget) _ABI_SYSCALL_IPC_CHK(msgctl) _ABI_SYSCALL_IPC_CHK(shmat) _ABI_SYSCALL_IPC_CHK(shmdt) _ABI_SYSCALL_IPC_CHK(shmget) _ABI_SYSCALL_IPC_CHK(shmctl) return false; } /** * Convert a multiplexed pseudo syscall into a direct syscall * @param arch the arch definition * @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 _abi_syscall_demux(const struct arch_def *arch, int syscall) { int sys = __NR_SCMP_UNDEF; #define _ABI_SYSCALL_DEMUX_CHK(NAME) \ case __PNR_##NAME: \ sys = arch->syscall_resolve_name_raw(#NAME); break; switch (syscall) { _ABI_SYSCALL_DEMUX_CHK(socket) _ABI_SYSCALL_DEMUX_CHK(bind) _ABI_SYSCALL_DEMUX_CHK(connect) _ABI_SYSCALL_DEMUX_CHK(listen) _ABI_SYSCALL_DEMUX_CHK(accept) _ABI_SYSCALL_DEMUX_CHK(getsockname) _ABI_SYSCALL_DEMUX_CHK(getpeername) _ABI_SYSCALL_DEMUX_CHK(socketpair) _ABI_SYSCALL_DEMUX_CHK(send) _ABI_SYSCALL_DEMUX_CHK(recv) _ABI_SYSCALL_DEMUX_CHK(sendto) _ABI_SYSCALL_DEMUX_CHK(recvfrom) _ABI_SYSCALL_DEMUX_CHK(shutdown) _ABI_SYSCALL_DEMUX_CHK(setsockopt) _ABI_SYSCALL_DEMUX_CHK(getsockopt) _ABI_SYSCALL_DEMUX_CHK(sendmsg) _ABI_SYSCALL_DEMUX_CHK(recvmsg) _ABI_SYSCALL_DEMUX_CHK(accept4) _ABI_SYSCALL_DEMUX_CHK(recvmmsg) _ABI_SYSCALL_DEMUX_CHK(sendmmsg) _ABI_SYSCALL_DEMUX_CHK(semop) _ABI_SYSCALL_DEMUX_CHK(semget) _ABI_SYSCALL_DEMUX_CHK(semctl) _ABI_SYSCALL_DEMUX_CHK(semtimedop) _ABI_SYSCALL_DEMUX_CHK(msgsnd) _ABI_SYSCALL_DEMUX_CHK(msgrcv) _ABI_SYSCALL_DEMUX_CHK(msgget) _ABI_SYSCALL_DEMUX_CHK(msgctl) _ABI_SYSCALL_DEMUX_CHK(shmat) _ABI_SYSCALL_DEMUX_CHK(shmdt) _ABI_SYSCALL_DEMUX_CHK(shmget) _ABI_SYSCALL_DEMUX_CHK(shmctl) } /* this looks odd because the arch resolver returns _ERROR if it can't * resolve the syscall, but we want to use _UNDEF for that, so we set * 'sys' to a sentinel value of _UNDEF and if it is error here we know * the resolve failed to find a match */ if (sys == __NR_SCMP_UNDEF) sys = __NR_SCMP_ERROR; else if (sys == __NR_SCMP_ERROR) sys = __NR_SCMP_UNDEF; return sys; } /** * Convert a direct syscall into multiplexed pseudo socket syscall * @param arch the arch definition * @param syscall the direct syscall * * Return the related multiplexed pseudo syscall number, __NR_SCMP_UNDEF is * there is no related pseudo syscall, or __NR_SCMP_ERROR otherwise. * */ static int _abi_syscall_mux(const struct arch_def *arch, int syscall) { const char *sys; sys = arch->syscall_resolve_num_raw(syscall); if (!sys) return __NR_SCMP_ERROR; #define _ABI_SYSCALL_MUX_CHK(NAME) \ if (!strcmp(sys, #NAME)) return __PNR_##NAME; _ABI_SYSCALL_MUX_CHK(socket) _ABI_SYSCALL_MUX_CHK(bind) _ABI_SYSCALL_MUX_CHK(connect) _ABI_SYSCALL_MUX_CHK(listen) _ABI_SYSCALL_MUX_CHK(accept) _ABI_SYSCALL_MUX_CHK(getsockname) _ABI_SYSCALL_MUX_CHK(getpeername) _ABI_SYSCALL_MUX_CHK(socketpair) _ABI_SYSCALL_MUX_CHK(send) _ABI_SYSCALL_MUX_CHK(recv) _ABI_SYSCALL_MUX_CHK(sendto) _ABI_SYSCALL_MUX_CHK(recvfrom) _ABI_SYSCALL_MUX_CHK(shutdown) _ABI_SYSCALL_MUX_CHK(setsockopt) _ABI_SYSCALL_MUX_CHK(getsockopt) _ABI_SYSCALL_MUX_CHK(sendmsg) _ABI_SYSCALL_MUX_CHK(recvmsg) _ABI_SYSCALL_MUX_CHK(accept4) _ABI_SYSCALL_MUX_CHK(recvmmsg) _ABI_SYSCALL_MUX_CHK(sendmmsg) _ABI_SYSCALL_MUX_CHK(semop) _ABI_SYSCALL_MUX_CHK(semget) _ABI_SYSCALL_MUX_CHK(semctl) _ABI_SYSCALL_MUX_CHK(semtimedop) _ABI_SYSCALL_MUX_CHK(msgsnd) _ABI_SYSCALL_MUX_CHK(msgrcv) _ABI_SYSCALL_MUX_CHK(msgget) _ABI_SYSCALL_MUX_CHK(msgctl) _ABI_SYSCALL_MUX_CHK(shmat) _ABI_SYSCALL_MUX_CHK(shmdt) _ABI_SYSCALL_MUX_CHK(shmget) _ABI_SYSCALL_MUX_CHK(shmctl) return __NR_SCMP_ERROR; } /** * Rewrite a syscall value to match the architecture * @param arch the arch definition * @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 abi_syscall_rewrite(const struct arch_def *arch, int *syscall) { int sys = *syscall; if (sys <= -100 && sys >= -120) *syscall = arch->sys_socketcall; else if (sys <= -200 && sys >= -224) *syscall = arch->sys_ipc; else if (sys < 0) return -EDOM; return 0; } /** * add a new rule to the abi 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 abi 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 abi_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 (_abi_syscall_socket_test(db->arch, sys)) { /* socket 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 = _abi_syscall_mux(db->arch, sys); if (sys_a == __NR_SCMP_ERROR) { rc = __NR_SCMP_ERROR; goto add_return; } sys_b = sys; } else { sys_a = sys; sys_b = _abi_syscall_demux(db->arch, 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 = db->arch->sys_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 (_abi_syscall_ipc_test(db->arch, sys)) { /* 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 = _abi_syscall_mux(db->arch, sys); if (sys_a == __NR_SCMP_ERROR) { rc = __NR_SCMP_ERROR; goto add_return; } sys_b = sys; } else { sys_a = sys; sys_b = _abi_syscall_demux(db->arch, 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 = db->arch->sys_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; }