diff options
Diffstat (limited to 'iv/orodja/ldmitm/ldmitm.c')
-rw-r--r-- | iv/orodja/ldmitm/ldmitm.c | 153 |
1 files changed, 106 insertions, 47 deletions
diff --git a/iv/orodja/ldmitm/ldmitm.c b/iv/orodja/ldmitm/ldmitm.c index 66cba41..2b8b815 100644 --- a/iv/orodja/ldmitm/ldmitm.c +++ b/iv/orodja/ldmitm/ldmitm.c @@ -5,7 +5,7 @@ Kako deluje: https://tbrindus.ca/correct-ld-preload-hooking-libc/ Prevedi z: gcc -shared -fPIC -ldl ldmitm.c -o ldmitm.so Poženi z: LD_PRELOAD=$PWD/ldmitm.so php -S 0:1234 A je treba beležit execve in fopen? Po mojem ne. Odtekanje flagov itak vidimo v TCP sessionu. -TODO: add mutex locks!!! +TODO: add mutex locks, openssl bio mitm, read, write, rtt, timestamps (if possible), namesto trenutnega mtu rajši dobi advertised mss iz tcp_info, dobi last ack timestamp option value: https://elixir.bootlin.com/linux/v6.11-rc3/source/include/linux/tcp.h#L302 <= tu notri je mogoče z BPF??? */ #include <stdio.h> #include <dlfcn.h> @@ -18,20 +18,25 @@ TODO: add mutex locks!!! #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> +#include <sys/param.h> +#include <netinet/ip.h> #if SQLITE_VERSION_NUMBER < 3037000 #error "we require sqlite newer than 3037000, yours is " #SQLITE_VERSION_NUMBER " --- because of STRICT!" #endif #define LOG(x, ...) if (getenv("LDMITM_LOG")) printf("[ldmitm %s] " x "\n", __func__ __VA_OPT__(,) __VA_ARGS__) struct stream { - bool active; - bool silenced; - int id; // id in sqlite3 database + bool active; // only stream connections are true, listening sockets and inactive fds are false + bool silenced; // only makes sense for stream connections, true to silence traffic + int id; // id in sqlite3 database -- only makes sense for stream connections + int bound; // for listening sockets: port, for stream sockets: fd of listening socket }; typedef int (*accept_t)(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); typedef int (*close_t)(int fd); -static accept_t real_accept; -static close_t real_close; -static __thread sqlite3 * db = NULL; +typedef int (*bind_t)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +static accept_t real_accept = NULL; +static close_t real_close = NULL; +static bind_t real_bind = NULL; +static sqlite3 * db = NULL; struct stream * streams = NULL; int streams_sizeof = 0; static void setup_db () { // should be able to ignore being called in case db is already set up on this thread @@ -74,8 +79,8 @@ static void setup_db () { // should be able to ignore being called in case db is LOG("failed to step create " name ": %s", sqlite3_errstr(ret)); \ goto fail; \ } - CREATE_TABLE("connections", "id INTEGER PRIMARY KEY, peer TEXT NOT NULL, accepted TEXT DEFAULT (strftime('%FT%R:%f', 'now')) NOT NULL, closed TEXT, silenced TEXT"); // accepted, closed and silenced are iso datestrings - CREATE_TABLE("messages", "connection INTEGER NOT NULL, direction INTEGER NOT NULL CHECK (direction IN (0, 1)), time TEXT DEFAULT (strftime('%FT%R:%f', 'now')) NOT NULL, FOREIGN KEY(connection) REFERENCES connections(id)"); + CREATE_TABLE("connections", "id INTEGER PRIMARY KEY, bound INTEGER NOT NULL, peer TEXT NOT NULL, accepted TEXT DEFAULT (strftime('%FT%R:%f', 'now')) NOT NULL, closed TEXT, silenced TEXT, mtu INTEGER NOT NULL"); // accepted, closed and silenced are iso datestrings, bound is listening port + CREATE_TABLE("messages", "connection INTEGER NOT NULL, direction INTEGER NOT NULL CHECK (direction IN (0, 1)), time TEXT DEFAULT (strftime('%FT%R:%f', 'now')) NOT NULL, rtt INTEGER NOT NULL, FOREIGN KEY(connection) REFERENCES connections(id)"); return; fail: if (db) @@ -114,6 +119,74 @@ static void end_stream (int fd) { // cleanup function, should be able to handle fail: memset(&(streams[fd]), 0, sizeof streams[fd]); } +static bool more_fds (int largest_fd) { + if (largest_fd >= streams_sizeof) { + int streams_sizeof_new; + if (streams_sizeof == 0) + streams_sizeof_new = 128; + else + streams_sizeof_new = streams_sizeof * 2; + struct stream * streams_new = realloc(streams, streams_sizeof_new*sizeof *streams); + if (!streams_new) { + LOG("ENOMEM realloc streams!"); + return false; + } + memset(streams_new+streams_sizeof, 0, (streams_sizeof_new-streams_sizeof)*sizeof *streams); + streams = streams_new; + streams_sizeof = streams_sizeof_new; + } + if (largest_fd < streams_sizeof) + return true; + return more_fds(largest_fd); +} +static int port_from_sa (const struct sockaddr * sa) { // gets port from sockaddr + switch (sa->sa_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *) sa)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *) sa)->sin6_port); + default: + return -1; + } +} +static void str_from_sa (char * peeraddress, const struct sockaddr * address) { + switch (address->sa_family) { + case AF_INET: + if (!inet_ntop(AF_INET, &(((struct sockaddr_in *) address)->sin_addr), peeraddress, *address_len)) { + int myerrno = errno; + strcpy(peeraddress, "!"); + strcat(peeraddress, strerror(myerrno)); + } + sprintf(peeraddress+strlen(peeraddress), "/%d", ntohs(((struct sockaddr_in *) address)->sin_port)); + break; + case AF_INET6: + if (!inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) address)->sin6_addr), peeraddress, *address_len)) { + int myerrno = errno; + strcpy(peeraddress, "!"); + strcat(peeraddress, strerror(myerrno)); + } + sprintf(peeraddress+strlen(peeraddress), "/%d", ntohs(((struct sockaddr_in6 *) address)->sin6_port)); + break; + default: + strcpy(peeraddress, "unknown family"); + break; + } +} +int bind (int sockfd, const struct sockaddr * addr, socklen_t addrlen) { + if (!real_bind) + real_bind = dlsym(RTLD_NEXT, "bind"); + LOG("called bind()"); + int ret = real_bind(sockfd, addr, addrlen); + int old_errno = errno; + if (ret == -1) + goto fail; + if (!more_fds(sockfd)) + goto fail; // enomem + streams[sockfd].bound = port_from_sa(addr); +fail: + errno = old_errno; + return ret; +} int close (int fd) { if (!real_close) real_close = dlsym(RTLD_NEXT, "close"); @@ -130,56 +203,42 @@ int accept (int socket, struct sockaddr *restrict address, socklen_t *restrict a LOG("called accept()"); int ret = real_accept(socket, address, address_len); int saved_errno = errno; - if (ret >= streams_sizeof) { - int streams_sizeof_new; - if (streams_sizeof == 0) - streams_sizeof_new = 128; - else - streams_sizeof_new = streams_sizeof * 2; - struct stream * streams_new = realloc(streams, streams_sizeof_new*sizeof *streams); - if (!streams_new) { - LOG("ENOMEM realloc streams!"); - goto fail; - } - memset(streams_new+streams_sizeof, 0, (streams_sizeof_new-streams_sizeof)*sizeof *streams); - streams = streams_new; - streams_sizeof = streams_sizeof_new; - } + if (ret == -1) + goto fail; + if (!more_fds(MAX(ret, socket)) + goto fail; // enomem end_stream(ret); streams[ret].active = true; + streams[ret].bound = socket; setup_db(); if (db) { sqlite3_stmt * stmt = NULL; - int sqlret = sqlite3_prepare_v3(db, "insert into connections (peer) values (:p);", -1, 0, &stmt, NULL); + int sqlret = sqlite3_prepare_v3(db, "insert into connections (peer, bound, mtu) values (:p, :b, :m);", -1, 0, &stmt, NULL); if (sqlret != SQLITE_OK) { LOG("failed to prepare insert connections: %s", sqlite3_errstr(sqlret)); goto sqlfail; } char peeraddress[INET_ADDRSTRLEN+128]; - switch (address->sa_family) { - case AF_INET: - if (!inet_ntop(AF_INET, &(((struct sockaddr_in *) address)->sin_addr), peeraddress, *address_len)) { - int myerrno = errno; - strcpy(peeraddress, "!"); - strcat(peeraddress, strerror(myerrno)); - } - sprintf(peeraddress+strlen(peeraddress), "/%d", ntohs(((struct sockaddr_in *) address)->sin_port)); - break; - case AF_INET6: - if (!inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) address)->sin6_addr), peeraddress, *address_len)) { - int myerrno = errno; - strcpy(peeraddress, "!"); - strcat(peeraddress, strerror(myerrno)); - } - sprintf(peeraddress+strlen(peeraddress), "/%d", ntohs(((struct sockaddr_in6 *) address)->sin6_port)); - break; - default: - strcpy(peeraddress, "unknown family"); - break; - } + str_from_sa(peeraddress, address); sqlret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":p"), peeraddress, -1, SQLITE_STATIC); if (sqlret != SQLITE_OK) { - LOG("failed to bind insert connection: %s", sqlite3_errstr(sqlret)); + LOG("failed to bind insert connection text: %s", sqlite3_errstr(sqlret)); + goto sqlfail; + } + sqlret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":b"), streams[streams[ret].bound].bound); + if (sqlret != SQLITE_OK) { + LOG("failed to bind insert connection bound: %s", sqlite3_errstr(sqlret)); + goto sqlfail; + } + int mtu; + int mtusize = sizeof mtu; + if (getsockopt(socket, IPPROTO_IP, IP_MTU, &mtu, &mtusize) == -1) { + mtu = -errno; + LOG("failed to get MTU: %s", -mtu); + } + sqlret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":m"), mtu); + if (sqlret != SQLITE_OK) { + LOG("failed to bind insert connection mtu: %s", sqlite3_errstr(sqlret)); goto sqlfail; } sqlret = sqlite3_step(stmt); |