From 68b09cdb8ca13ab4141e763793ab0145b78b255f Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Sun, 14 Jan 2024 17:57:08 +0100 Subject: [PATCH] Implement basic introspection into what is passed. --- sopass.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/sopass.py b/sopass.py index 0afbb6a..7d4b6c8 100755 --- a/sopass.py +++ b/sopass.py @@ -9,6 +9,7 @@ import selectors from dataclasses import dataclass import logging as log import math +import termcolor # external dep, TODO # Note: get/setrlimit are in resource module def setup_logging(): @@ -36,9 +37,30 @@ def hexdump(data: bytes) -> list[str]: # We will be prefixing the lines with sig result.append(line) return result +# fd types from inode(7) +fdtypes = { + S_IFSOCK: 'socket', + S_IFLNK: 'symlink', + S_IFREG: 'regular file', + S_IFBLK: 'blockdev', + S_IFDIR: 'directory', + S_IFCHR: 'chardev', + S_IFIFO: 'fifo', + } + def fdinfo(fd: int) -> str: + result = [] stat = os.fstat(fd) - # TODO: return something + type = S_IFMT(stat.st_mode) + result.append(f'type: {fdtypes.get(type, f"UNKNOWN: {type}")}') + if S_ISREG(stat.st_mode): + result.append(f'size: {stat.st_size}') + orig_offset = os.lseek(fd, 0, os.SEEK_CUR) + os.lseek(fd, 0, os.SEEK_SET) + head = os.read(fd, 256) + result.extend(hexdump(head)) + os.lseek(fd, orig_offset, os.SEEK_SET) + return result def fd_readlink(fd: int, pid: int | None =None) -> str: if pid is None: @@ -49,12 +71,66 @@ def fd_readlink(fd: int, pid: int | None =None) -> str: except OSError as e: return f"Could not read {fd_link}: {e.strerror} (errno {e.errno})" +def dirprintlines(dir: Direction, lines, file=sys.stdout, indent=''): + if isinstance(lines, str): lines = [lines] + lines_to_print = [f'{dir.sigil} {indent}{line}' for line in lines] + if True or COLOR: + lines_to_print = [termcolor.colored(line, dir.color) for line in lines_to_print] + print(*lines_to_print, sep='\n', file=file) + +def show_fd(dir, fd, /, indent=' '): + # Here we decide what to show and format it. We leave any particular + # decisions on other functions. + dirprintlines(dir, f'link: {fd_readlink(fd)}', indent=indent) + dirprintlines(dir, fdinfo(fd), indent=indent) + +# Maps from integers to symbolic names, acc to unix(7) +sockopts = {getattr(sk, name): name for name in( + 'SO_PASSCRED', + 'SO_PASSSEC', +# 'SO_PEEK_OFF', # wtf python does not have this + 'SO_PEERCRED', + 'SO_PEERSEC', + )} +anclevels = {getattr(sk, name): name for name in( + 'SOL_SOCKET', + )} +anctypes = {getattr(sk, name): name for name in( + 'SCM_RIGHTS', + 'SCM_CREDENTIALS', +# 'SCM_SECURITY', # wtf python does not have this + )} + + +def show_anc(dir, a, /, indent=' ') -> list[int]: + "returns a possible list of file descriptors" + result = [] + level, type, data = a + if level == sk.SOL_SOCKET and type == sk.SCM_RIGHTS: + # A file descriptor + fds = array('i') + fds.frombytes(data) + dirprintlines(dir, f'Received {len(fds)} file descriptor{"s"*(len(fds)!=1)}', indent=indent) + result = list(fds) + for fd in fds: + show_fd(dir, fd, indent=2*indent) + else: + dirprintlines(dir, 'Cannot show details.', indent=indent) + return result + def fwd_traffic(frm: sk.socket, to: sk.socket, dir: Direction): - print(f'{dir.sigil} there be data from {frm}') chunk_size = 640*1024 # … idk, ought to be enough for anyone… - data, anc, flags, _addr = frm.recvmsg(chunk_size, chunk_size) - # TODO: inspect + fds = [] # potential fds + data, anc, flags, addr = frm.recvmsg(chunk_size, chunk_size) + dirprintlines(dir, f'Forwarding {len(data)} bytes and {len(anc)} ancillary from {addr} to {to}') + dirprintlines(dir, hexdump(data), indent=' ') + for i,a in enumerate(anc): + anclevel, anctype, _ancdata = a + dirprintlines(dir, f'Ancillary #{i}: level {anclevels[anclevel]}, type {anctypes[anctype]}') + fds = show_anc(dir, a) to.sendmsg([data], anc, flags) + if fds: log.debug(f'Closing fds: {fds}') + for fd in fds: os.close(fd) def main(): setup_logging()