P.S.: Steffen Nurpmeso wrote in <20240707025234.j3oUaPFH@steffen%sdaoden.eu>: |Steffen Nurpmeso wrote in | <20240704180538.iV4uex29@steffen%sdaoden.eu>: ||Simon Josefsson wrote in || <87jzi1fg24.fsf@xxxxxxxxxxx>: |||Jochen Bern <Jochen.Bern@xxxxxxxxx> writes: |||> (And since you mention "port knocking", I'd like to repeat how fond I |||> am of upgrading that original concept to a single-packet |||> crypto-armored implementation like fwknop.) ||| |||I am reluctantly considering to use some kind of port knocking mechanism |||on some machines, however I really don't want to carry around shared ... |||Does anyone know of any implementation that allows me to configure a |||PGP/SSH/FIDO/TPM/whatever public key on the server side, and it then |||only listens to signed port knocks from the corresponding private keys? | ... ||No, but for many years i do have a super simple port-knock server ||to do the I/O plus sh(1)ell based client which can do .. whatever. | ... ||With the possibilities that ssh-keygen -Y sign|verify have added, ||one could easily adapt the server and client to send "user-name ||MSG", so that the server could look into authorized_keys of ||user-name and verify MSG, whatever that is. | |Hey! That vision of yours, in conjunction with that -Y |possibility of ssh-keygen thrilled me so much i wrote a draft. ... So no, despite that this did not freeaddrinfo() and did not correctly set exit status twice (in early program state), i do not like TCP and think TLS in addition makes it worse, with these multiple packets back and forth. I thus have built upon my old thing that i use for years, and will attach it. It has a very simple UDP server (-lsocket on OpenIndiana when doing "CC -o /sbin/s-port-knock-bin), and massively changed the script to produce packets like 1. password encrypted by X509 pubkey (base64) + LF 2. LF (gives as room to place a NUL upon receive) 3. SSH signature cipher-encrypted with password in 1. (base64) + LF Ie after placing some SSH principals in /tmp/.Zsigs, cd /tmp/ gcc -o ./zt ./s-port-knock-bin.c ./s-port-knock.sh create-server-key .Zkey we can do PORT_KNOCK_BIN=/tmp/zt ./s-port-knock.sh \ start-server -v 45045 \ /tmp/s-port-knock.sh /tmp/.Zkey-pri.pem /tmp/.Zsigs and in another window PORT_KNOCK_BIN=/tmp/zt ./s-port-knock.sh \ knock localhost 45045 .Zkey-pub.pem SOME-PUB-SSH-KEY and if that key is in .Zsigs it works. One can create a MAGIC and overwrite the block and allow hooks by starting the server (/ the client) with the environment variable PORT_KNOCK_RC pointing to some file. The environment is not cleared so that is passed through. Ie MAGIC=hallo act_block() { echo >&2 '.XRC blocking '$1; } act_allow() { echo >&2 '.XRC allowing '$1', principal: '$2; } The magic is what SSH with create a signature for, fwiw. Oh, and this does not work on OpenBSD for now, because they do not know about the AI_V4MAPPED flag, .. wait .., i quickly added C preprocessor shims. However, (then) only IPv6 is supported for now, there (untested, thus). On FreeBSD it seems you need to set sysctl net.inet6.ip6.v6only=0 (look for rc.conf:ipv6_ipv4mapping="NO" and change, maybe). On OpenIndiana, despite -lsocket, one should fill in the OPENSSL variable with the right path right away, like this: PORT_KNOCK_BIN=/tmp/zt OPENSSL=/usr/openssl/3.1/bin/openssl \ ./s-port-knock.sh knock localhost 45045 \ /tmp/.Zkey-pub.pem SSH-PUBKEY the rest works out of the box. Anyhow, this approach i like much better than TLS. It is a bit ugly that ssh-keygen does not give access to the PEM version of the OpenSSL private key, on the other hand encryption only works for RSA (in the western world, and libsodium, on the other hand, would allow this for Ed25519 it seems, too, and i also had a quick look at the report that claims this is a save thing to do, last week, but i am not a cryptographer and thus i would not do that, despite the fact that this is libsodium only; yes, there is a OpenSSL thing open for long, but it seems they talk(ed) him down .. i dunno, really). And the chinese algorithm, that much is plain, cannot be generated by OpenSSL (yet), and not portably anyway. I will attach these, i like them. Maybe i find time to make the OpenBSD variant work for IPv4 and IPv6, but i think it needs two sockets and select(2), so this is a bit of work. Ciao, and a nice Sunday everybody. --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt)
#!/bin/sh - #@ s-port-knock.sh: port knock user interface (for s-port-knock-bin.c server). #@ Requires (modern) openssl and ssh-keygen with -Y support. # syno() { echo >&2 'SYNOPSIS: '$0' create-server-key filename-prefix [rsa[:bits]]' echo >&2 ' - generate *-{pri,pub}.pem, -pub.pem is needed by clients' echo >&2 echo >&2 'SYNOPSIS: '$0' knock host port pubkey ssh-client-pubkey' echo >&2 ' - knock for ssh-client-pubkey at server (host:port, pubkey)' echo >&2 ' This requires, in $PATH: bash(1) or '$0 echo >&2 ' - with $DEBUG: save encrypted key and signature there, instead' echo >&2 echo >&2 'SYNOPSIS: '$0' verify ip-address [prikey allowed-sigs-db (enc-key enc-sig)]' echo >&2 ' - (called from server); if prikey and allowed-sigs-db are given' echo >&2 ' verify enc-key and enc-sig, else just block IP' echo >&2 ' - with $DEBUG set: fetch enc-key and enc-sig from $DEBUG, instead' echo >&2 echo >&2 'SYNOPSIS: '$0' start-server [-v] port cmd-path prikey allowed-sigs-db' echo >&2 ' - port is port where to listen on (>0, <65536)' echo >&2 ' - cmd-path is the path to a script to run, likely '$0 echo >&2 ' - prikey and allowed-sigs-db as for "verify"' echo >&2 ' - Usually started via start-stop-daemon or so, if -v is used' echo >&2 ' you should then pass --no-close and --output additionally to' echo >&2 ' --start --background --make-pidfile --pidfile XY --exec '$0 echo >&2 echo >&2 'The PORT_KNOCK_RC environment variable may specify a readable file to include.' echo >&2 'It may set $MAGIC to include a string in signatures, and overwrite defaults of' echo >&2 ' act_block() { echo >&2 "blocking "$1; }' echo >&2 ' act_allow() { echo >&2 "allowing $1, principal: $2"; }' exit 64 # EX_USAGE } # # 2020 - 2024 Steffen Nurpmeso <steffen@xxxxxxxxxx> # Public Domain : ${PORT_KNOCK_BIN:=/usr/sbin/s-port-knock-bin} : ${RSA_DEFBITS:=3072} : ${RSA_MINBITS:=1024} : ${SHELL:=/bin/sh} : ${AWK:=awk} : ${OPENSSL:=openssl} : ${TMPDIR:=/tmp} : ${ENV_TMP:="$TMPDIR:$TMP:$TEMP"} # >8 -- 8< : ${DEBUG:=} # For heaven's sake auto-redirect on SunOS/Solaris, and for pipefail if [ -z "$__PORT_KNOCK_UP" ]; then if [ $# -eq 0 ] || [ "$1" = -h ] || [ "$1" = --help ]; then syno elif [ -d /usr/xpg4 ]; then if [ x"$SHELL" = x/bin/sh ]; then __PORT_KNOCK_UP=y PATH=/usr/xpg4/bin:${PATH} SHELL=/usr/xpg4/bin/sh printf >&2 'WARNING: SunOS/Solaris, ' fi elif ( set -o pipefail ) >/dev/null 2>&1; then set -o pipefail else if [ -x /bin/bash ]; then __PORT_KNOCK_UP=y SHELL=/bin/bash elif [ -x /usr/bin/bash ]; then __PORT_KNOCK_UP=y SHELL=/usr/bin/bash else # TODO too lazy for es=$(exec 3>&1 1>&2; BLA | { XY; echo $? >&3; } | BLA) massage echo >&2 'WARNING: shell does not support "set -o pipefail", this HIDES ERRORS (TODO..)' fi [ -n "$__PORT_KNOCK_UP" ] && printf >&2 'WARNING: "set -o pipefail" unsupported, ' fi if [ -n "$__PORT_KNOCK_UP" ]; then printf >&2 'redirecting through $SHELL=%s\n' "$SHELL" export __PORT_KNOCK_UP PATH SHELL exec $SHELL "$0" "$@" fi elif ( set -o pipefail ) >/dev/null 2>&1; then set -o pipefail fi LC_ALL=C EX_DATAERR=65 EX_CANTCREAT=73 EX_TEMPFAIL=75 umask 077 # PORT_KNOCK_RC defaults MAGIC= act_block() { echo >&2 'blocking '$1; } act_allow() { echo >&2 'allowing '$1', principal: '$2; } tmp_dir= tmp_file= __tmp_no=1 __tmpfiles= tmp_file_new() { if [ -z "$tmp_dir" ]; then _tmp_dir() { i=$IFS IFS=: set -- $ENV_TMP IFS=$i # for i; do -- new in POSIX Issue 7 + TC1 for tmp_dir do [ -d "$tmp_dir" ] && return 0 done tmp_dir=$TMPDIR [ -d "$tmp_dir" ] && return 0 echo >&2 'Cannot find a temporary directory, please set $TMPDIR' exit $EX_TEMPFAIL } _tmp_dir trap "exit $EX_TEMPFAIL" HUP INT QUIT PIPE TERM trap "trap \"\" HUP INT QUIT PIPE TERM EXIT; rm -f \$__tmpfiles" EXIT fi while :; do tmp_file="$tmp_dir/port-knock-$$-$__tmp_no.dat" ( set -C > "$tmp_file" ) >/dev/null 2>&1 && break __tmp_no=$((__tmp_no + 1)) done __tmpfiles="$__tmpfiles $tmp_file" } ossl() ( set +e x= while :; do "$OPENSSL" "$@" [ $? -eq 0 ] && return 0 echo >&2 echo >&2 '$OPENSSL='$OPENSSL' seems incompatible; is it an old version?' echo >&2 ' Command was: '$* if [ -x /usr/openssl/3.1/bin/openssl ]; then x=/usr/openssl/3.1/bin/openssl elif [ -x /usr/openssl/1.1/bin/openssl ]; then x=/usr/openssl/1.1/bin/openssl elif [ -x /usr/pkg/bin/openssl ]; then x=/usr/pkg/bin/openssl elif [ -x /opt/csw/bin/openssl ]; then x=/opt/csw/bin/openssl fi if [ x = x"$x" ] || [ x"$x" = x"$OPENSSL" ]; then echo >&2 'Please place version >= 1.1.0 (early) in $PATH, or $OPENSSL=, rerun.' exit $EX_DATAERR fi OPENSSL=$x echo >&2 'I will try $OPENSSL='$OPENSSL' instead; restarting operation ...' echo >&2 done ) incrc() { [ -n "$PORT_KNOCK_RC" ] && [ -r "$PORT_KNOCK_RC" ] && . "$PORT_KNOCK_RC" } case "$1" in create-server-key) [ $# -gt 3 ] && syno fprefix=$2 algo=rsa opt= [ $# -gt 2 ] && algo=$3 case $algo in rsa|rsa:*) bits=${algo##*:} algo=${algo%%:*} if [ "$bits" = "$algo" ] || [ -z "$bits" ]; then bits=$RSA_DEFBITS else i=${bits#${bits%%[!0-9]*}} i=${i%${i##*[!0-9]}} # xxx why? if [ -n "$i" ]; then echo >&2 'RSA bits is not a number' exit $EX_DATAERR fi if [ $bits -lt $RSA_MINBITS ]; then echo >&2 'RSA bits insufficient (RFC 8301), need at least '$RSA_MINBITS exit $EX_DATAERR fi fi opt="-pkeyopt rsa_keygen_bits:$bits" ;; *) syno;; esac # We do not want to overwrite ( set -C > "$fprefix"-pri.pem > "$fprefix"-pub.pem ) || exit $EX_CANTCREAT set -e ossl genpkey -out "$fprefix"-pri.pem -outform PEM -algorithm $algo $opt ossl pkey -pubout -out "$fprefix"-pub.pem -outform PEM < "$fprefix"-pri.pem echo 'Server keys stored in' echo ' '"$fprefix"-pri.pem echo ' '"$fprefix"-pub.pem echo 'Server also needs a ssh-keygen(1) allowed_signers_file in format' echo ' IDENT(ie, email address) KEYTYPE PUBKEY [COMMENT]' echo '(ie, authorized_keys format but prefixed by an IDENT)' ;; knock) [ $# -ne 5 ] && syno if [ -n "$DEBUG" ]; then kpk= kexe= else incrc kpk= kexe= if [ -x "$PORT_KNOCK_BIN" ]; then kpk=y kexe=$PORT_KNOCK_BIN else set +e kexe=$(command -v bash 2>/dev/null) if [ $? -ne 0 ]; then echo >&2 'Knocking requires bash in $PATH; or '$PORT_KNOCK_BIN syno fi set -e fi fi k=$(dd if=/dev/urandom bs=64 count=1 2>/dev/null | tr -cd 'a-zA-Z0-9_.,=@%^+-') if [ $? -ne 0 ]; then echo >&2 'Failed to create a random key' exit $EX_TEMPFAIL fi ek=$(echo "$k" | ossl pkeyutl -encrypt -pubin -inkey "$4" | ossl base64 | tr -d '\012 ') if [ $? -ne 0 ]; then echo >&2 'Failed to pkeyutl encrypt' exit $EX_TEMPFAIL fi es=$(printf '%s' "$MAGIC" | ssh-keygen -Y sign -n pokno -f "$5" 2>/dev/null | sed -Ee '/^-+BEGIN SSH/d;/^-+END SSH/d;s/$/ /' | tr -d '\012 ' | ossl enc -aes256 -pass "pass:$k" -pbkdf2 -e -A -a) if [ $? -ne 0 ]; then echo >&2 'Failed to encrypt signature' exit $EX_TEMPFAIL fi if [ -n "$DEBUG" ]; then set -e { echo "$ek"; echo "$es"; } > "$DEBUG" else wW= if (ping -c1 -w1 localhost >/dev/null 2>&1); then wW=-w1 elif (ping -c1 -W500 localhost >/dev/null 2>&1); then wW=-W1000 fi while :; do printf 'knocking ... ' if [ -n "$kpk" ]; then "$kexe" client "$3" "$2" "$ek" "$es" else </dev/null bash -c ' set -e printf "%s\n\n%s\n" "'"$ek"'" "'"$es"'" > /dev/udp/"'"$2"'"/"'"$3"'" ' fi [ $? -ne 0 ] && break echo 'sent' sleep 1 ping -c1 $wW "$2" && break echo 'Did not work out, sleeping 60 seconds and retrying' sleep 60 done fi ;; verify) if [ -z "$DEBUG" ]; then [ $# -eq 2 ] || [ $# -eq 6 ] || syno incrc if [ $# -eq 6 ]; then ek=$5 es=$6 fi elif [ $# -eq 2 ]; then : else [ $# -eq 4 ] || syno if [ ! -f "$DEBUG" ]; then echo >&2 'The result of "knock" does not exist: '$DEBUG syno fi { read ek read es } < "$DEBUG" fi if [ $# -eq 2 ]; then act_block "$2" else # (of course if pkeyutl fails *after* reading off the pipe this will not do) tmp_file_new dk=$(echo "$ek" | ossl base64 -d | ossl pkeyutl -decrypt -inkey "$3") if [ $? -ne 0 ]; then act_block "$2" else echo "$es" | ossl enc -aes256 -pass "pass:$dk" -pbkdf2 -a -d | { printf '%s\n' '-----BEGIN SSH SIGNATURE-----' cat printf '\n%s\n' '-----END SSH SIGNATURE-----' } > "$tmp_file" p=$(ssh-keygen -Y find-principals -s "$tmp_file" -f "$4") if [ $? -eq 0 ]; then if [ -z "$MAGIC" ]; then act_allow "$2" "$p" elif printf '%s' "$MAGIC" | ssh-keygen -Y verify -n pokno -I "$p" -s "$tmp_file" -f "$4"; then act_allow "$2" "$p" else act_block "$2" fi else act_block "$2" fi fi fi ;; start-server) shift [ $# -eq 4 ] || [ $# -eq 5 ] || syno incrc v= if [ $# -eq 5 ]; then v=$1 shift fi exec "$PORT_KNOCK_BIN" $v server "$@" ;; *) echo >&2 'Invalid command: '$1 syno ;; esac exit 0 # s-sht-mode
/*@ s-port-knock-bin.c: C backend for s-port-knock.sh; please see there. *@ TODO - capsicum / pledge/unveil (fork from server a forker process that forks+execv the command) * * Copyright (c) 2020 - 2024 Steffen Nurpmeso <steffen@xxxxxxxxxx>. * SPDX-License-Identifier: ISC * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define _GNU_SOURCE #include <sys/select.h> #include <sys/socket.h> #include <sys/types.h> #include <netdb.h> #include <arpa/inet.h> #include <netinet/in.h> #include <ctype.h> #include <errno.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> /* For SO_{SND,RCV}TIMEO and SO_LINGER */ #define a_TIMEOUT_SECS 42 /* Largest possible pubkey encryption + 1 + 1 + SSH signature (ED25519=~295+, RSA=~1560+) + 1 +1; packet is * 1. password encrypted by X509 pubkey (base64) + LF * 2. LF (gives as room to place a NUL upon receive) * 3. SSH signature cipher-encrypted with password in 1. (base64) + LF */ #define a_BUF_LEN (2048 + 1024) /* Minimum bytes the encrypted SSH signature should have (do not even try to decrypt it, block sender). * ED25519 encrypted with -aes256 is 273 bytes */ #define a_SIG_LEN_MIN 256 /* */ #define a_BIN_SH "/bin/sh" /* >8 -- 8< */ enum{ a_EX_OK, a_EX_ERR, a_EX_USAGE = 64, a_EX_DATAERR = 65, a_EX_NOHOST = 68, a_EX_UNAVAILABLE = 69, a_EX_OSERR = 71, a_EX_IOERR = 74 }; static int a_verbose; static int volatile a_sig_seen; static int a_server(int port, char const *cmd_path, char const *privkey, char const *sigpool); static int a_client(char const *host, char const *port, char const *enckey, char const *encsig); static long a_fork(void); static void a_sig_hdl(int sig); int main(int argc, char **argv){ char const *prog; int es, srv, portno; es = 1; prog = (argc == 0) ? "port-knock" : argv[0]; fclose(stdin); fclose(stdout); if(argc > 0 && !strcmp(argv[1], "-v")){ a_verbose = 1; --argc, ++argv; } if(argc != 6) goto jesyn; if(!strcmp(argv[1], "server")) srv = 1; else if(!strcmp(argv[1], "client")) srv = 0; else goto jesyn; portno = atoi(argv[2]); if(portno <= 0 || portno > 65535){ fprintf(stderr, "Bad port (must be >0 and <65536): %s -> %d\n", argv[3], portno); goto jesyn; } /* TODO [chroot], pledge/unveil */ if(chdir("/") == -1){ fprintf(stderr, "Cannot chdir(2) to /\n"); goto jleave; } es = srv ? a_server(portno, argv[3], argv[4], argv[5]) : a_client(argv[3], argv[2], argv[4], argv[5]); jleave: return es; jesyn: fprintf(stderr, "Synopsis: %s [-v] server port cmd-path prikey allowed-sigs-db\n" "Synopsis: %s [-v] client port host enckey encsig\n", prog, prog); goto jleave; } static int a_server(int portno, char const *cmd, char const *privkey, char const *sigpool){ char dbuf[a_BUF_LEN], nbuf[INET6_ADDRSTRLEN +1]; char const *argv[9]; struct sigaction siac; struct sockaddr_in6 soa6; socklen_t soal; int sofd, es; if(!a_verbose) freopen("/dev/null", "w", stderr); while((sofd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1){ es = errno; if(es != EINTR){ fprintf(stderr, "Server socket creation failed: %s\n", strerror(es)); es = a_EX_OSERR; goto jleave; } } memset(&soa6, 0, sizeof(soa6)); soa6.sin6_family = AF_INET6; soa6.sin6_port = htons(portno); memcpy(&soa6.sin6_addr, &in6addr_any, sizeof soa6.sin6_addr); while(bind(sofd, (struct sockaddr const*)&soa6, sizeof soa6) == -1){ es = errno; if(es != EINTR){ fprintf(stderr, "Server socket cannot bind: %s\n", strerror(es)); es = a_EX_OSERR; goto jleave; } } memset(&siac, 0, sizeof siac); sigemptyset(&siac.sa_mask); siac.sa_handler = &a_sig_hdl; sigaction(SIGTERM, &siac, NULL); /*if(a_verbose)*/ sigaction(SIGINT, &siac, NULL); siac.sa_handler = SIG_IGN; sigaction(SIGPIPE, &siac, NULL); sigaction(SIGCHLD, &siac, NULL); argv[0] = "sh"; argv[1] = cmd; argv[2] = "verify"; argv[3] = nbuf; /* argv[4] = privkey or NULL, our trigger */ argv[5] = sigpool; argv[6] = dbuf; /* enckey */ /*argv[7] = encsig;*/ argv[8] = NULL; while(!a_sig_seen){ ssize_t rb, i; soal = sizeof soa6; rb = recvfrom(sofd, dbuf, sizeof(dbuf) -1, 0, (struct sockaddr*)&soa6, &soal); if(rb == 0) continue; if(rb == -1){ es = errno; if(es == EINTR) continue; fprintf(stderr, "Failed receiving packet: %s\n", strerror(es)); /* (xxx CONNRESET should trigger firewall rules, so do not care..) */ if(es != EBADF && es != EINVAL) continue; es = a_EX_OSERR; goto jleave; } if(inet_ntop(soa6.sin6_family, &soa6.sin6_addr, nbuf, sizeof nbuf) == NULL){ fprintf(stderr, "IMPL ERROR: cannot inet_ntop() client address after recvfrom(2)\n"); continue; } /* TODO Linux kernel iptables xt_recent does not understand a mapped address. * (I opened https://bugzilla.kernel.org/show_bug.cgi?id=219038, but dunno if that is right.) */ if(strchr(nbuf, '.') != NULL){ i = (size_t)strlen(nbuf); if((size_t)i <= sizeof("::ffff:") -1 + 4){ fprintf(stderr, "IMPL ERROR: IPv4 mapped address is bogus\n"); continue; } i -= sizeof("::ffff:") -1; memmove(nbuf, &nbuf[sizeof("::ffff:") -1], i +1); } /* Default: do not inspect packet, block this IP */ argv[4] = NULL; /* Buffer spaced so excess is error */ if(rb == (ssize_t)sizeof(dbuf) -1) goto jfork; dbuf[(size_t)rb] = '\0'; /* Inspect packet, find enckey and encsig boundaries (for format see a_BUF_LEN #define above) */ argv[7] = NULL; i = 0; for(;;){ char c; c = dbuf[(size_t)i]; if(c != '\n'){ if(((unsigned)c & 0x80) || iscntrl(c)) goto jfork; if(++i == rb) goto jfork; continue; } ++i; if(argv[7] == NULL){ c = dbuf[(size_t)i]; /* dbuf is NUL terminated, so never excess */ if(c != '\n') goto jepack; dbuf[(size_t)i] = '\0'; if(++i == rb) goto jepack; if(rb - i < a_SIG_LEN_MIN) goto jfork; argv[7] = &dbuf[(size_t)i]; }else if(i != rb){ jepack: if(a_verbose) fprintf(stderr, "Invalid packet content\n"); goto jfork; }else break; } /* Hook may inspect packet data and decide further */ argv[4] = privkey; jfork: if(a_verbose) fprintf(stderr, "execv: " a_BIN_SH " %s %s %s %s %s %s %s (%ld bytes input)\n", argv[1], argv[2], argv[3], (argv[4] != NULL ? argv[4] : ""), (argv[4] != NULL ? argv[5] : ""), (argv[4] != NULL ? argv[6] : ""), (argv[4] != NULL ? argv[7] : ""), (argv[4] != NULL ? (long)rb : 0)); i = a_fork(); if(i < 0){ es = (int)-i; fprintf(stderr, "Server fork failed: %s\n", strerror(es)); es = a_EX_OSERR; goto jleave; }else if(i == 0){ execv(a_BIN_SH, (char*const*)argv); for(;;) _exit(a_EX_OSERR); } } es = a_EX_OK; jleave: if(sofd != -1) close(sofd); return es; } static int a_client(char const *host, char const *port, char const *enckey, char const *encsig){ char buf[a_BUF_LEN]; struct addrinfo hints, *aip0, *aip; size_t l, yet; int sofd, es; memset(&hints, 0, sizeof hints); #undef a_HINTS #ifndef __OpenBSD__ # define a_HINTS &hints hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED | AI_NUMERICSERV; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; #else # define a_HINTS NULL #endif sofd = -1; aip0 = NULL; es = a_EX_DATAERR; l = strlen(enckey); if(l >= sizeof(buf) - 2 - 1 -1){ fprintf(stderr, "Encrypted key is too long\n"); goto jleave; } memcpy(buf, enckey, l); buf[l] = '\n'; buf[l + 1] = '\n'; l += 2; yet = strlen(encsig); if(yet >= sizeof(buf) - 1 -1 - l){ fprintf(stderr, "Encrypted signature is too long\n"); goto jleave; } memcpy(&buf[l], encsig, yet); buf[l += yet] = '\n'; buf[++l] = '\0'; es = getaddrinfo(host, port, a_HINTS, &aip0); #undef a_HINTS if(es != 0){ fprintf(stderr, "DNS lookup failed for: %s:%s %s\n", host, port, gai_strerror(es)); es = a_EX_NOHOST; goto jleave; } for(aip = aip0; aip != NULL; aip = aip->ai_next){ for(;;){ sofd = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); if(sofd != -1) goto jso; es = errno; if(es != EINTR) break; } fprintf(stderr, "Socket creation failed: %s\n", strerror(es)); } es = a_EX_UNAVAILABLE; goto jleave; jso: /* One tick though */ yet = 0; while(l > 0){ ssize_t wb; wb = sendto(sofd, &buf[yet], l, 0, aip->ai_addr, aip->ai_addrlen); if(wb == -1){ es = errno; if(es == EINTR) continue; fprintf(stderr, "Packet transmit failed: %s\n", strerror(es)); es = a_EX_IOERR; goto jleave; } yet += wb; l -= wb; } es = a_EX_OK; jleave: if(aip0 != NULL) freeaddrinfo(aip0); if(sofd != -1) close(sofd); return es; } static long a_fork(void){ struct timespec ts; long i; ts.tv_sec = 0; ts.tv_nsec = 250000000L; for(;;){ i = fork(); if(i != -1) break; i = -(errno); if(i == -ENOSYS) break; nanosleep(&ts, NULL); } return i; } static void a_sig_hdl(int sig){ (void)sig; a_sig_seen = 1; } /* s-itt-mode */
_______________________________________________ openssh-unix-dev mailing list openssh-unix-dev@xxxxxxxxxxx https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev