- Add full Telegram bot functionality with Z.AI API integration
- Implement 4 tools: Bash, FileEdit, WebSearch, Git
- Add 3 agents: Code Reviewer, Architect, DevOps Engineer
- Add 6 skills for common coding tasks
- Add systemd service file for 24/7 operation
- Add nginx configuration for HTTPS webhook
- Add comprehensive documentation
- Implement WebSocket server for real-time updates
- Add logging system with Winston
- Add environment validation
🤖 zCode CLI X - Agentic coder with Z.AI + Telegram integration
149 lines
5.3 KiB
C
149 lines
5.3 KiB
C
/*
|
|
* Seccomp BPF filter generator to block Unix domain socket creation
|
|
*
|
|
* This program generates a seccomp-bpf filter that blocks the socket() syscall
|
|
* when called with AF_UNIX as the domain argument. This prevents creation of
|
|
* Unix domain sockets while allowing all other socket types (AF_INET, AF_INET6, etc.)
|
|
* and all other syscalls.
|
|
*
|
|
* The filter is exported in a format compatible with bubblewrap's --seccomp flag.
|
|
*
|
|
* SECURITY LIMITATION - 32-bit x86 (ia32):
|
|
* TODO: This filter does NOT block socketcall() syscall, which is a security issue
|
|
* on 32-bit x86 systems. On ia32, the socket() syscall doesn't exist - instead,
|
|
* all socket operations are multiplexed through socketcall():
|
|
* - socketcall(SYS_SOCKET, [AF_UNIX, ...]) - can bypass this filter
|
|
* - socketcall(SYS_SOCKETPAIR, [AF_UNIX, ...]) - can bypass this filter
|
|
*
|
|
* To fix this, we need to add conditional rules that:
|
|
* 1. Check if socketcall() exists on the current architecture (32-bit x86 only)
|
|
* 2. Block socketcall(SYS_SOCKET, ...) when first arg of sub-call is AF_UNIX
|
|
* 3. Block socketcall(SYS_SOCKETPAIR, ...) when first arg of sub-call is AF_UNIX
|
|
*
|
|
* This requires inspecting the arguments passed to socketcall, which is more
|
|
* complex BPF logic. For now, 32-bit x86 is not supported.
|
|
*
|
|
* Compilation:
|
|
* gcc -o seccomp-unix-block seccomp-unix-block.c -lseccomp
|
|
*
|
|
* Usage:
|
|
* ./seccomp-unix-block <output-file> [arch]
|
|
*
|
|
* If arch is given (x86_64 or aarch64), the filter is generated for that
|
|
* architecture instead of the native one. Lets a single-arch builder emit
|
|
* filters for both x64 and arm64.
|
|
*
|
|
* Dependencies:
|
|
* - libseccomp (libseccomp-dev package on Debian/Ubuntu)
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <seccomp.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
int main(int argc, char *argv[]) {
|
|
scmp_filter_ctx ctx;
|
|
int rc;
|
|
|
|
if (argc < 2 || argc > 3) {
|
|
fprintf(stderr, "Usage: %s <output-file> [x86_64|aarch64]\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
const char *output_file = argv[1];
|
|
const char *arch_name = (argc == 3) ? argv[2] : NULL;
|
|
|
|
/* Create seccomp context with default action ALLOW */
|
|
ctx = seccomp_init(SCMP_ACT_ALLOW);
|
|
if (ctx == NULL) {
|
|
fprintf(stderr, "Error: Failed to initialize seccomp context\n");
|
|
return 1;
|
|
}
|
|
|
|
if (arch_name != NULL) {
|
|
uint32_t target;
|
|
if (strcmp(arch_name, "x86_64") == 0) {
|
|
target = SCMP_ARCH_X86_64;
|
|
} else if (strcmp(arch_name, "aarch64") == 0) {
|
|
target = SCMP_ARCH_AARCH64;
|
|
} else {
|
|
fprintf(stderr, "Error: Unsupported arch '%s'\n", arch_name);
|
|
seccomp_release(ctx);
|
|
return 1;
|
|
}
|
|
if (target != seccomp_arch_native()) {
|
|
rc = seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE);
|
|
if (rc == 0) rc = seccomp_arch_add(ctx, target);
|
|
if (rc < 0) {
|
|
fprintf(stderr, "Error: Failed to set target arch: %s\n", strerror(-rc));
|
|
seccomp_release(ctx);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add rule to block socket(AF_UNIX, ...) */
|
|
/* socket() syscall signature: int socket(int domain, int type, int protocol) */
|
|
/* arg0 = domain (AF_UNIX = 1) */
|
|
/* Use SCMP_CMP_MASKED_EQ with a 32-bit mask: the domain argument is a 32-bit
|
|
* int, so the kernel ignores the upper 32 bits of the register. A plain
|
|
* SCMP_CMP_EQ would compare all 64 bits and miss calls where the upper bits
|
|
* are set. */
|
|
rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(socket), 1,
|
|
SCMP_A0(SCMP_CMP_MASKED_EQ, 0xffffffff, AF_UNIX));
|
|
if (rc < 0) {
|
|
fprintf(stderr, "Error: Failed to add seccomp rule: %s\n", strerror(-rc));
|
|
seccomp_release(ctx);
|
|
return 1;
|
|
}
|
|
|
|
/* Block io_uring entirely. IORING_OP_SOCKET (Linux 5.19+) creates sockets
|
|
* in kernel context without going through the socket() syscall, bypassing
|
|
* the rule above. seccomp cannot inspect io_uring SQEs (they live in a
|
|
* shared-memory ring), so the only safe option is to deny ring creation
|
|
* and use. Blocking all three syscalls also covers the case of an
|
|
* inherited ring fd. */
|
|
int io_uring_calls[] = {
|
|
SCMP_SYS(io_uring_setup),
|
|
SCMP_SYS(io_uring_enter),
|
|
SCMP_SYS(io_uring_register),
|
|
};
|
|
for (size_t i = 0; i < sizeof(io_uring_calls) / sizeof(io_uring_calls[0]); i++) {
|
|
rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), io_uring_calls[i], 0);
|
|
if (rc < 0) {
|
|
fprintf(stderr, "Error: Failed to add io_uring rule: %s\n", strerror(-rc));
|
|
seccomp_release(ctx);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Export the filter to a file */
|
|
int fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0600);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "Error: Failed to open output file: %s\n", strerror(errno));
|
|
seccomp_release(ctx);
|
|
return 1;
|
|
}
|
|
|
|
rc = seccomp_export_bpf(ctx, fd);
|
|
if (rc < 0) {
|
|
fprintf(stderr, "Error: Failed to export seccomp filter: %s\n", strerror(-rc));
|
|
close(fd);
|
|
seccomp_release(ctx);
|
|
return 1;
|
|
}
|
|
|
|
/* Clean up */
|
|
close(fd);
|
|
seccomp_release(ctx);
|
|
|
|
return 0;
|
|
}
|