summaryrefslogtreecommitdiffstats
path: root/iv/orodja/ldmitm/ldmitm.c
diff options
context:
space:
mode:
Diffstat (limited to 'iv/orodja/ldmitm/ldmitm.c')
-rw-r--r--iv/orodja/ldmitm/ldmitm.c153
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);