From 4858d50fbcfad7970c827e0ae4784da2f2230e25 Mon Sep 17 00:00:00 2001 From: "David A. Madore" Date: Mon, 22 Feb 2010 18:42:16 +0100 Subject: Add a new SHEL command, which tries to provide a shell back to sender. * SHEL: this command takes a mandatory numeric argument, which is interpreted as a TCP port number on the computer at the source of the datagram; the server tries to connect to that port and spawn a shell attached to it (beware, though: this shell will have no terminal). Incidentally, curse Unix with a thousand curses for making this so grossly difficult. --- emergencyd.pl | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/emergencyd.pl b/emergencyd.pl index e9bd8f3..6920e95 100755 --- a/emergencyd.pl +++ b/emergencyd.pl @@ -55,6 +55,12 @@ # # * SYRQ: this command takes a mandatory argument; the server writes # this argument to /proc/sysrq-trigger and responds with DONE. +# +# * SHEL: this command takes a mandatory numeric argument, which is +# interpreted as a TCP port number on the computer at the source of +# the datagram; the server tries to connect to that port and spawn a +# shell attached to it (beware, though: this shell will have no +# terminal). # *** The emergency daemon itself *** # @@ -78,6 +84,9 @@ use Digest::SHA qw(hmac_sha256_hex); use Socket; use Socket6; use POSIX (); +use Fcntl; +use Errno qw(EINPROGRESS); +use IO::Poll; use Sys::Syslog qw(:standard :macros); use Getopt::Std; @@ -121,7 +130,7 @@ if ( defined(*IPV6_V6ONLY{CODE}) ) { setsockopt $socket, IPPROTO_IPV6, IPV6_V6ONLY, 0 or die "Can't set IPV6_V6ONLY option to 0: $!"; } -bind $socket, sockaddr_in6($port, in6addr_any) or die "Can't bind socket: $!"; +bind $socket, pack_sockaddr_in6($port, in6addr_any) or die "Can't bind socket: $!"; openlog(SYSLOG_IDENT, "ndelay,pid", LOG_DAEMON); @@ -140,6 +149,7 @@ if ( $opts{f} ) { close STDERR; POSIX::setsid; } +$SIG{CLD} = "IGNORE"; # F*ck archaic unices where this produces zombies. sub curtime { my $fiddle = shift // 0; @@ -228,6 +238,50 @@ sub cmd_syrq { return "DONE\n"; } +sub cmd_shel { + my $port = shift or die [ "!BAD", "Missing argument to SHEL" ]; + die [ "!BAD", "Invalid port number (SHEL command) $port" ] + unless $port =~ /^\d+$/; + my $proto = getprotobyname("tcp") or die "Can't resolve tcp protocol: $!"; + socket my $socket, PF_INET6, SOCK_STREAM, $proto + or die "Can't create socket: $!"; + my $flags; + $flags = fcntl ($socket, F_GETFL, 0); + fcntl ($socket, F_SETFL, $flags|O_NONBLOCK) if defined($flags); + my $sender = shift; + my (undef, undef, $saddr, $scopeid) = unpack_sockaddr_in6_all $sender; + my $target = pack_sockaddr_in6_all($port, 0, $saddr, $scopeid); + unless ( connect $socket, $target) { + # This is so GROSS! :-((( + die "Can't connect to port $port: $!" unless $! == EINPROGRESS; + my $poll = new IO::Poll; + $poll->mask($socket, POLLOUT); + my $ev = $poll->poll(15); + die "Failed to poll: $!" unless defined($ev); + die "Timed out connecting to port $port" unless $ev == 1; + my $err = getsockopt $socket, SOL_SOCKET, SO_ERROR; + die "Failed to retrieve socket error: $!" unless defined($err); + $err = unpack("I",$err); + if ( $err ) { + $! = $err; + die "Can't connect to port $port: $!"; + } + } + my $v = fork; + die "Can't fork: $!" unless defined($v); + if ( $v ) { + close $socket; + return [ "DONE", $v ]; + } else { + fcntl ($socket, F_SETFL, $flags&~O_NONBLOCK) if defined($flags); + open STDIN, "<&", $socket; + open STDOUT, ">&", $socket; + open STDERR, ">&", $socket; + exec "/bin/sh" or print STDERR "exec failed: $!"; + exit -1; + } +} + my %dispatch = ( "PING" => \&cmd_ping, "DATE" => \&cmd_date, @@ -237,6 +291,7 @@ my %dispatch = ( "RKEY" => \&cmd_rkey, "LOGM" => \&cmd_logm, "SYRQ" => \&cmd_syrq, + "SHEL" => \&cmd_shel, ); PACKET: @@ -261,7 +316,7 @@ while ( $running ) { } my $sub = $dispatch{$command}; die "!UNK\n" unless defined($sub); - $resp = &{$sub}($arg); + $resp = &{$sub}($arg, $sender); }; if ( $@ ) { $resp = $@; -- cgit v1.2.3