From fa9d0d5d8b180fc284fbfc6c73b3e444ae0baa53 Mon Sep 17 00:00:00 2001 From: remittor Date: Thu, 17 Apr 2025 20:46:17 +0300 Subject: [PATCH] gateway: Add installer for dropbearmulti (for RD18 and etc) --- data/payload_ssh/dropbear2.init.d.sh | 225 ++++++++++++++++++++++++++ data/payload_ssh/dropbear2.install.sh | 31 ++++ gateway.py | 124 ++++++++++---- 3 files changed, 349 insertions(+), 31 deletions(-) create mode 100644 data/payload_ssh/dropbear2.init.d.sh create mode 100644 data/payload_ssh/dropbear2.install.sh diff --git a/data/payload_ssh/dropbear2.init.d.sh b/data/payload_ssh/dropbear2.init.d.sh new file mode 100644 index 0000000..95b4fda --- /dev/null +++ b/data/payload_ssh/dropbear2.init.d.sh @@ -0,0 +1,225 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006-2010 OpenWrt.org +# Copyright (C) 2006 Carlos Sobrinho + +START=19 +STOP=50 + +USE_PROCD=1 +XDIR=/etc/crontabs/dropbearmulti +PROG=$XDIR/dropbear +NAME=dropbear +PIDCOUNT=0 +EXTRA_COMMANDS="killclients" +EXTRA_HELP=" killclients Kill ${NAME} processes except servers and yourself" + +append_ports() +{ + local ipaddrs="$1" + local port="$2" + + [ -z "$ipaddrs" ] && { + procd_append_param command -p "$port" + return + } + + for addr in $ipaddrs; do + procd_append_param command -p "$addr:$port" + done +} + +validate_section_dropbear() +{ + uci_validate_section dropbear dropbear "${1}" \ + 'PasswordAuth:bool:1' \ + 'enable:bool:1' \ + 'Interface:string' \ + 'GatewayPorts:bool:0' \ + 'RootPasswordAuth:bool:1' \ + 'RootLogin:bool:1' \ + 'rsakeyfile:file' \ + 'BannerFile:file' \ + 'Port:list(port):22' \ + 'SSHKeepAlive:uinteger:300' \ + 'IdleTimeout:uinteger:0' \ + 'MaxAuthTries:uinteger:3' \ + 'RecvWindowSize:uinteger:0' \ + 'mdns:bool:1' +} + +dropbear_instance() +{ + local PasswordAuth enable Interface GatewayPorts \ + RootPasswordAuth RootLogin rsakeyfile \ + BannerFile Port SSHKeepAlive IdleTimeout \ + MaxAuthTries RecvWindowSize mdns ipaddrs + + validate_section_dropbear "${1}" || { + echo "validation failed" + return 1 + } + + [ -n "${Interface}" ] && { + [ -n "${BOOT}" ] && return 0 + + network_get_ipaddrs_all ipaddrs "${Interface}" || { + echo "interface ${Interface} has no physdev or physdev has no suitable ip" + return 1 + } + } + + [ "${enable}" = "0" ] && return 1 + PIDCOUNT="$(( ${PIDCOUNT} + 1))" + local pid_file="/var/run/${NAME}.${PIDCOUNT}.pid" + + procd_open_instance + procd_set_param command "$PROG" -F -P "$pid_file" + [ "${PasswordAuth}" -eq 0 ] && procd_append_param command -s + [ "${GatewayPorts}" -eq 1 ] && procd_append_param command -a + [ "${RootPasswordAuth}" -eq 0 ] && procd_append_param command -g + [ "${RootLogin}" -eq 0 ] && procd_append_param command -w + [ -n "${rsakeyfile}" ] && procd_append_param command -r "${rsakeyfile}" + [ -n "${BannerFile}" ] && procd_append_param command -b "${BannerFile}" + append_ports "${ipaddrs}" "${Port}" + [ "${IdleTimeout}" -ne 0 ] && procd_append_param command -I "${IdleTimeout}" + [ "${SSHKeepAlive}" -ne 0 ] && procd_append_param command -K "${SSHKeepAlive}" + [ "${MaxAuthTries}" -ne 0 ] && procd_append_param command -T "${MaxAuthTries}" + [ "${RecvWindowSize}" -gt 0 -a "${RecvWindowSize}" -le 1048576 ] && \ + procd_append_param command -W "${RecvWindowSize}" + [ "${mdns}" -ne 0 ] && procd_add_mdns "ssh" "tcp" "$Port" "daemon=dropbear" + procd_set_param respawn + procd_close_instance +} + +keygen() +{ + for keytype in rsa; do + # check for keys + key=dropbear/dropbear_${keytype}_host_key + [ -f /tmp/$key -o -s /etc/$key ] || { + # generate missing keys + mkdir -p /tmp/dropbear + [ -x $PROG ] && { + $PROG dropbearkey -t $keytype -f /tmp/$key 2>&- >&- && exec /etc/rc.common "$initscript" start + } & + exit 0 + } + done + + lock /tmp/.switch2jffs + mkdir -p /etc/dropbear + mv /tmp/dropbear/dropbear_* /etc/dropbear/ + lock -u /tmp/.switch2jffs + chown root /etc/dropbear + chmod 0700 /etc/dropbear +} + +load_interfaces() +{ + config_get interface "$1" Interface + config_get enable "$1" enable 1 + + [ "${enable}" = "1" ] && interfaces=" ${interface} ${interfaces}" +} + +boot() +{ + BOOT=1 + start "$@" +} + +start_service() +{ + #[ -s /etc/dropbear/dropbear_rsa_host_key ] || keygen + rm -f /etc/dropbear/dropbear_rsa_host_key + + if [ ! -e /etc/dropbear/dropbear_ed25519_host_key ]; then + $XDIR/dropbear dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key 0<&- 2>&- >&- + fi + if [ ! -e /etc/dropbear/dropbear_ecdsa_host_key ]; then + $XDIR/dropbear dropbearkey -t ecdsa -f /etc/dropbear/dropbear_ecdsa_host_key 0<&- 2>&- >&- + fi + + . /lib/functions.sh + . /lib/functions/network.sh + + config_load "${NAME}" + config_foreach dropbear_instance dropbear +} + +service_triggers() +{ + local interfaces + + procd_add_config_trigger "config.change" "dropbear" /etc/init.d/dropbear reload + + config_load "${NAME}" + config_foreach load_interfaces dropbear + + [ -n "${interfaces}" ] && { + for n in $interfaces ; do + procd_add_interface_trigger "interface.*" $n /etc/init.d/dropbear reload + done + } + + procd_add_validation validate_section_dropbear +} + +shutdown() { + # close all open connections + killall dropbear +} + +killclients() +{ + local ignore='' + local server + local pid + + # if this script is run from inside a client session, then ignore that session + pid="$$" + while [ "${pid}" -ne 0 ] + do + # get parent process id + pid=`cut -d ' ' -f 4 "/proc/${pid}/stat"` + [ "${pid}" -eq 0 ] && break + + # check if client connection + grep -F -q -e "${PROG}" "/proc/${pid}/cmdline" && { + append ignore "${pid}" + break + } + done + + # get all server pids that should be ignored + for server in `cat /var/run/${NAME}.*.pid` + do + append ignore "${server}" + done + + # get all running pids and kill client connections + local skip + for pid in `pidof "${NAME}"` + do + # check if correct program, otherwise process next pid + grep -F -q -e "${PROG}" "/proc/${pid}/cmdline" || { + continue + } + + # check if pid should be ignored (servers, ourself) + skip=0 + for server in ${ignore} + do + if [ "${pid}" = "${server}" ] + then + skip=1 + break + fi + done + [ "${skip}" -ne 0 ] && continue + + # kill process + echo "${initscript}: Killing ${pid}..." + kill -KILL ${pid} + done +} diff --git a/data/payload_ssh/dropbear2.install.sh b/data/payload_ssh/dropbear2.install.sh new file mode 100644 index 0000000..61468d0 --- /dev/null +++ b/data/payload_ssh/dropbear2.install.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +XDIR=/etc/crontabs/dropbearmulti + +if [ ! -d /etc/dropbear ]; then + mkdir -p /etc/dropbear + chown root /etc/dropbear + chmod 0700 /etc/dropbear +fi + +kill -9 `pgrep dropbear` &>/dev/null +rm -f /etc/dropbear/dropbear_rsa_host_key +rm -f /etc/init.d/dropbear +rm -f /usr/sbin/dropbear + +if [ ! -f /etc/config/dropbear ]; then + cp -f $XDIR/uci.cfg /etc/config/dropbear +fi + +cp -f $XDIR/init.d.sh /etc/init.d/dropbear +chmod +x /etc/init.d/dropbear + +#rm -f /etc/rc.d/K??dropbear +#ln -s /etc/init.d/dropbear /etc/rc.d/K50dropbear &>/dev/null + +#rm -f /etc/rc.d/S??dropbear +#ln -s /etc/init.d/dropbear /etc/rc.d/S19dropbear &>/dev/null + +kill -9 `pgrep dropbear` &>/dev/null +/etc/init.d/dropbear enable +/etc/init.d/dropbear start diff --git a/gateway.py b/gateway.py index 223ace1..3f4adb1 100644 --- a/gateway.py +++ b/gateway.py @@ -1170,6 +1170,7 @@ class Gateway(): return hasher.hexdigest() def post_connect(self, exec_cmd, contimeout = 20, passw = 'root'): + telnet_en = False self.use_ssh = True if passw is not None: self.passw = passw @@ -1181,7 +1182,6 @@ class Gateway(): if not ssh_en: print("") - print('Unlock TelNet server ...') exec_cmd("bdata set telnet_en=1 ; bdata commit") print('Run TelNet server on port 23 ...') exec_cmd("/etc/init.d/telnet enable ; /etc/init.d/telnet restart") @@ -1189,43 +1189,105 @@ class Gateway(): self.use_ssh = False telnet_en = self.ping(verbose = 2) if not telnet_en: + # FIXME print(f"ERROR: TelNet server not responding (IP: {self.ip_addr})") return -2 - print("") print('#### TelNet server are activated! ####') - #print("") - #print('Run FTP server on port 21 ...') - cmd = r'''#!/bin/sh /etc/rc.common -SERVICE_DAEMONIZE=1 -SERVICE_WRITE_PID=1 -start() { - service_start /usr/sbin/inetd -f -} -stop() { - service_stop /usr/sbin/inetd -} -''' - cmd = cmd.replace('\r\n', ';') - cmd = cmd.replace('\n', ';') - cfg = r'ftp\tstream\ttcp\tnowait\troot\t/usr/sbin/ftpd\tftpd -w\t/' - self.run_cmd(r"echo -e '" + cfg + "' > /etc/inetd.conf") - self.run_cmd(r"echo '" + cmd + "' | tr ';' '\n' > /etc/init.d/inetd") - self.run_cmd(r"chmod +x /etc/init.d/inetd") - self.run_cmd(r'/etc/init.d/inetd enable') - self.run_cmd(r'/etc/init.d/inetd restart') - ftp_en = self.check_ftp(timeout = 5) - if ftp_en <= -10: - print(f'WARNING: FTP server is running, but upload mode is blocked!') - elif ftp_en != 0: - print(f"WARNING: FTP server not responding (IP: {self.ip_addr})") - else: - self.use_ftp = True - print('#### FTP server are activated! ####') if ssh_en or telnet_en: self.run_cmd('nvram set uart_en=1; nvram set boot_wait=on; nvram commit') self.run_cmd('nvram set bootdelay=3; nvram set bootmenu_delay=5; nvram commit') - return 0 + + if ssh_en: + return True + + if telnet_en: + arch = self.run_cmd("cat /etc/openwrt_release | grep DISTRIB_ARCH=") + if not arch: + die('TELNET: Cannot read remote file /etc/openwrt_release') + pos = arch.find("DISTRIB_ARCH='") + if pos < 0: + die('TELNET: Cannot detect arch for remote device') + arch = arch[pos+1:].split("'")[1] + print(f'ARCH = {arch}') + arch_suffix = None + if arch.startswith('arm_'): + arch_suffix = '_armv7a' + if arch.startswith('aarch64'): + arch_suffix = '_arm64' + if arch.startswith('mips'): + arch_suffix = '_mips' + if not arch_suffix: + die(f'TELNET: Unknown arch = "{arch}"') + self.run_cmd(r'echo -e "root\nroot" | passwd root') + self.run_cmd(r'kill -9 `pgrep dropbearmulti` &>/dev/null') + fn_local = f'data/payload_ssh/dropbearmulti{arch_suffix}' + fn_remote = '/tmp/dropbearmulti' + md5_local = self.get_md5_for_local_file(fn_local) + if not(md5_local) or isinstance(md5_local, int) or len(md5_local) != 32: + die(f'File "{fn_local}" not found') + md5 = self.get_md5_for_remote_file(fn_remote) + if md5 != md5_local: + if not self.upload(fn_local, fn_remote): + die(f'TELNET: Cannot upload file "{fn_local}" to device') + self.run_cmd('chmod +x /tmp/dropbearmulti') + self.run_cmd('[ -d /etc/dropbear ] || { mkdir -p /etc/dropbear ; chown root /etc/dropbear ; chmod 0755 /etc/dropbear; }') + if False: + # generate host keys + fn = '/etc/dropbear/dropbear_ed25519_host_key' + fsize = self.get_remote_file_size(fn) + if not fsize or fsize <= 0: + self.run_cmd(f'rm -f {fn} ; /tmp/dropbearmulti dropbearkey -t ed25519 -f {fn} 2>&- >&-', timeout = 11) + fn = '/etc/dropbear/dropbear_ecdsa_host_key' + fsize = self.get_remote_file_size(fn) + if not fsize or fsize <= 0: + self.run_cmd(f'rm -f {fn} ; /tmp/dropbearmulti dropbearkey -t ecdsa -f {fn} 2>&- >&-', timeout = 11) + # run dropbearmulti + self.run_cmd('/tmp/dropbearmulti -p 122') + pass + print(f'Install XMiR-SSH ...') + xdir = '/etc/crontabs/dropbearmulti' + fn_bin = '/usr/sbin/dropbear' + fn_BIN = f'{xdir}/dropbear' + fn_conf = '/etc/config/dropbear' + fn_CONF = f'{xdir}/uci.cfg' + fn_initd = '/etc/init.d/dropbear' + fn_INITD = f'{xdir}/init.d.sh' + fn_INSTALL = f'{xdir}/install.sh' + fsize_bin = self.get_remote_file_size(fn_bin) + fsize_initd = self.get_remote_file_size(fn_initd) + if fsize_bin and fsize_initd and fsize_bin > 0 and fsize_initd > 0: + die('SSH: dropbear is found, but it cannot run!') + # install XMiR dropbear + self.run_cmd(f'kill -9 `pgrep dropbear` &>/dev/null') + #self.run_cmd(f'rm -f {fn_bin} &>/dev/null') + self.run_cmd(f'rm -f {fn_conf} &>/dev/null') + self.run_cmd(f'mkdir -p {xdir}') + self.run_cmd(f'cp -f /tmp/dropbearmulti {fn_BIN}') + self.run_cmd(f'chmod +x {fn_BIN}') + if not self.upload('data/payload_ssh/dropbear.uci.cfg', fn_CONF): + die(f'TELNET: Cannot upload file "{fn_CONF}" to device') + if not self.upload('data/payload_ssh/dropbear2.init.d.sh', fn_INITD): + die(f'TELNET: Cannot upload file "{fn_INITD}" to device') + if not self.upload('data/payload_ssh/dropbear2.install.sh', fn_INSTALL): + die(f'TELNET: Cannot upload file "{fn_INSTALL}" to device') + uci = [ "uci set firewall.dropbearmulti=include", + "uci set firewall.dropbearmulti.type='script'", + f"uci set firewall.dropbearmulti.path='{fn_INSTALL}'", + "uci set firewall.dropbearmulti.enabled='1'", + "uci commit firewall", + ] + self.run_cmd(';'.join(uci)) + self.run_cmd(f'chmod +x {fn_INITD} ; chmod +x {fn_INSTALL}') + print(f'Run XMiR-SSH ...') + self.run_cmd(f'{fn_INSTALL}', timeout = 20) + self.use_ssh = True + ssh_en = self.ping(verbose = 0, contimeout = 8) + if ssh_en: + print('#### XMiR-SSH server are activated! ####') + return True + print(f"ERROR: installed XMiR-SSH server not responding (IP: {self.ip_addr})") + return False #===============================================================================