From 0e1d50c8f0faba9cf50a2e5c90f5e8e82e90e4b3 Mon Sep 17 00:00:00 2001 From: Tom Smeding Date: Mon, 17 Aug 2020 23:50:52 +0200 Subject: Initial working version --- lib/libintercept.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/libintercept.h | 10 +++ 2 files changed, 205 insertions(+) create mode 100644 lib/libintercept.c create mode 100644 lib/libintercept.h (limited to 'lib') diff --git a/lib/libintercept.c b/lib/libintercept.c new file mode 100644 index 0000000..8503308 --- /dev/null +++ b/lib/libintercept.c @@ -0,0 +1,195 @@ +#define _GNU_SOURCE // RTLD_NEXT +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // posix_spawn +#include +#include +#include "libintercept.h" + + +// Returns whether successful +static bool sendall(int sock, const char *buffer, size_t length) { + size_t cursor = 0; + while (cursor < length) { + ssize_t nw = send(sock, buffer + cursor, length - cursor, 0); + if (nw <= 0) return false; + cursor += nw; + } + return true; +} + +static void try_transmit_invocation(const char *pathname, char *const argv[]) { + fprintf(stderr, "try_transmit_invocation: %s ...\n", pathname); + + const char *socketpath = getenv(COMM_SOCKET_ENVVAR); + if (socketpath == NULL) { + fprintf(stderr, " socket path not given\n"); + return; + } + + // The SOCK_CLOEXEC option is technically unnecessary, but let's be careful. + int sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sock < 0) { + fprintf(stderr, " cannot create socket: %s\n", strerror(errno)); + return; + } + + struct sockaddr_un addr; + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path) - 1); + + int ret = connect(sock, (const struct sockaddr*)&addr, sizeof addr); + if (ret < 0) { + fprintf(stderr, " cannot connect to socket\n"); + return; + } + + size_t nargs = 0; + while (argv[nargs] != NULL) nargs++; + + bool ok = false; + if (!sendall(sock, (const char*)&nargs, 8)) goto cleanup; + if (!sendall(sock, pathname, strlen(pathname) + 1)) goto cleanup; + for (size_t i = 0; i < nargs; i++) { + if (!sendall(sock, argv[i], strlen(argv[i]) + 1)) goto cleanup; + } + + ok = true; + +cleanup: + if (!ok) fprintf(stderr, " failed to write to socket\n"); + close(sock); +} + +__attribute__((constructor)) +static void constructor(void) { + fprintf(stderr, "constructor...\n"); +} + +// Abridged from glibc posix/execl.c, LGPL copyright FSF +int execl(const char *pathname, const char *arg, ...) { + size_t argc; + va_list ap; + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char*) && argc < INT_MAX; argc++) {} + va_end(ap); + if (argc >= INT_MAX) { + errno = E2BIG; + return -1; + } + + char *argv[argc + 1]; + argv[0] = (char*)arg; + va_start(ap, arg); + for (size_t i = 1; i < argc; i++) argv[i] = va_arg(ap, char*); + argv[argc] = NULL; + va_end(ap); + + try_transmit_invocation(pathname, argv); + + int (*real_execv)(const char*, char *const[]) = dlsym(RTLD_NEXT, "execv"); + return real_execv(pathname, argv); +} + +int execlp(const char *file, const char *arg, ...) { + size_t argc; + va_list ap; + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char*) && argc < INT_MAX; argc++) {} + va_end(ap); + if (argc >= INT_MAX) { + errno = E2BIG; + return -1; + } + + char *argv[argc + 1]; + argv[0] = (char*)arg; + va_start(ap, arg); + for (size_t i = 1; i < argc; i++) argv[i] = va_arg(ap, char*); + argv[argc] = NULL; + va_end(ap); + + try_transmit_invocation(file, argv); + + int (*real_execvp)(const char*, char *const[]) = dlsym(RTLD_NEXT, "execvp"); + return real_execvp(file, argv); +} + +int execle(const char *pathname, const char *arg, ...) { + size_t argc; + va_list ap; + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char*) && argc < INT_MAX; argc++) {} + char *const *envp = va_arg(ap, char *const *); + va_end(ap); + if (argc >= INT_MAX) { + errno = E2BIG; + return -1; + } + + char *argv[argc + 1]; + argv[0] = (char*)arg; + va_start(ap, arg); + for (size_t i = 1; i < argc; i++) argv[i] = va_arg(ap, char*); + argv[argc] = NULL; + va_end(ap); + + try_transmit_invocation(pathname, argv); + + int (*real_execvpe)(const char*, char *const[], char *const[]) = dlsym(RTLD_NEXT, "execvpe"); + return real_execvpe(pathname, argv, envp); +} + +int execv(const char *pathname, char *const argv[]) { + try_transmit_invocation(pathname, argv); + + int (*real_execv)(const char*, char *const[]) = dlsym(RTLD_NEXT, "execv"); + return real_execv(pathname, argv); +} + +int execvp(const char *file, char *const argv[]) { + try_transmit_invocation(file, argv); + + int (*real_execvp)(const char*, char *const[]) = dlsym(RTLD_NEXT, "execvp"); + return real_execvp(file, argv); +} + +int execve(const char *pathname, char *const argv[], char *const envp[]) { + try_transmit_invocation(pathname, argv); + + int (*real_execve)(const char*, char *const[], char *const[]) = dlsym(RTLD_NEXT, "execve"); + return real_execve(pathname, argv, envp); +} + +int execvpe(const char *file, char *const argv[], char *const envp[]) { + try_transmit_invocation(file, argv); + + int (*real_execvpe)(const char*, char *const[], char *const[]) = dlsym(RTLD_NEXT, "execvpe"); + return real_execvpe(file, argv, envp); +} + +int posix_spawn( + pid_t *pid, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[] +) { + try_transmit_invocation(path, argv); + + int (*real_posix_spawn)( + pid_t*, const char*, + const posix_spawn_file_actions_t*, + const posix_spawnattr_t*, + char *const[], char *const[] + ) = dlsym(RTLD_NEXT, "posix_spawn"); + return real_posix_spawn(pid, path, file_actions, attrp, argv, envp); +} diff --git a/lib/libintercept.h b/lib/libintercept.h new file mode 100644 index 0000000..0efb3a8 --- /dev/null +++ b/lib/libintercept.h @@ -0,0 +1,10 @@ +#pragma once + + +#define COMM_SOCKET_ENVVAR "EXEC_INTERCEPT_SOCKET" + +// Socket protocol: the client process sends one message per execve invocation. +// Format: +// - Number of arguments (8 byte little-endian unsigned integer) +// - Program path (null-terminated string) +// - Arguments (null-terminated strings) -- cgit v1.2.3-70-g09d2