diff -r 13b84a92a65a -r 16e6b1683c63 sap-vpn.pl --- a/sap-vpn.pl Fri Nov 14 23:07:00 2008 +0000 +++ b/sap-vpn.pl Tue Jan 06 07:27:55 2009 +0000 @@ -11,13 +11,15 @@ use Pod::Usage; use File::Basename; use English qw(-no_match_vars); +use lib "."; + +my $use_lwp = -f "lwp"; ($EUID, $UID) = ($UID, $EUID); # release ROOT, doesn't harm, if not suid ($0) = ($0 =~ /([.\/\w-]+)/); # untaint $0 use constant ME => basename $0; - delete @ENV{ grep /PATH/, keys %ENV }; $ENV{PATH} = "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin"; @@ -63,6 +65,7 @@ sub verbose(@) { print STDERR @_ if $opt_verbose } sub debug(@) { warn @_ if $opt_debug } +sub ohshit(@) { die ME . ": ", @_ } sub do_kill($); MAIN: { @@ -91,34 +94,34 @@ $opt_verbose += $opt_debug; - untaint($opt_linkname) or die ME . ": linkname didn't pass verification\n"; + untaint($opt_linkname) or ohshit "linkname didn't pass verification\n"; untaint($opt_script) - or die ME . ": script name didn't pass verification\n" + or ohshit "script name didn't pass verification\n" if $opt_script; map { untaint } @ppp_opts; - die ME . ": need to run with root permissions!\n" + ohshit "need to run with root permissions!\n" if not $EUID == 0 || $UID == 0; exit do_kill($opt_linkname) if $opt_kill; untaint($opt_user) - or die ME . ": username didn't pass verification\n"; + or ohshit "username didn't pass verification\n"; if (not defined $opt_passcode) { chomp(my $settings = qx{stty "-g"}); - open(IN, "/dev/tty") or die ME . ": Can't open /dev/tty: $!\n"; + open(IN, "/dev/tty") or ohshit "Can't open /dev/tty: $!\n"; print "Passcode for $opt_user: "; system stty => "-echo"; chomp($opt_passcode = ); system stty => $settings; print "\n"; } - untaint($opt_passcode) or die ME . ": passcode didn't pass verification\n"; + untaint($opt_passcode) or ohshit "passcode didn't pass verification\n"; pod2usage(-verbose => 0, -exitval => 0) if not defined $opt_passcode; -x $opt_script - or die ME . ": Script $opt_script is not executable: $!\n" + or ohshit "Script $opt_script is not executable: $!\n" if defined $opt_script; push @ppp_opts, $opt_debug ? qw(nodetach debug dump) : qw(nolog updetach); @@ -138,16 +141,42 @@ # Make initial request to get client_data verbose "% initial request\n"; - $request = - "GET /my.logon.php3?check=1 HTTP/1.0\r\n" - . "Content-Type: application/x-www-form-urlencoded\r\n" - . "Connection: close\r\n" . "\r\n"; + + my $client_data; + my $ua; + my $cookies; + + if (!$use_lwp) { + $request = + "GET /my.logon.php3?check=1 HTTP/1.0\r\n" + . "Content-Type: application/x-www-form-urlencoded\r\n" + . "Connection: close\r\n" . "\r\n"; + + $response = qx(echo "${request}" | ${openssl} 2>/dev/null) + or ohshit "Invalid Host specified or not reachable: $opt_host\n"; - $response = qx(echo "${request}" | ${openssl} 2>/dev/null) - or die ME . ": Invalid Host specified or not reachable: $opt_host\n"; + } + else { + use LWP::UserAgent; + use HTTP::Request::Common; + use HTTP::Status; + use HTTP::Cookies; + #use LWP::Debug qw(+); + $cookies = new HTTP::Cookies; + $ua = new LWP::UserAgent; + $ua->agent(""); + $ua->env_proxy; + $ua->cookie_jar($cookies); + $response = $ua->get("https://$opt_host:443/my.logon.php3?check=1"); + + ohshit "initial failed with http code @{[$response->message]}\n" + unless $response->is_success; + + $response = $response->as_string; + } $response =~ s//$1/; - my $client_data = $1; + $client_data = $1; ### ### STEP 2 :: Log in to FirePass. @@ -159,27 +188,49 @@ # browser will make many more requests than this to complete the log in # sequence, but all that is required is this POST with our credentails. This # may fail if the FirePass has End-Point Security Policies configured. - $request = + + if (!$use_lwp) { + $request = "check=1&username=${opt_user}&password=${opt_passcode}&mrhlogonform=1&client_data=${client_data}"; - $request = - "POST /my.activation.php3 HTTP/1.0\r\n" - . "Host: ${opt_host}\r\n" - . "Content-Type: application/x-www-form-urlencoded\r\n" - . "Content-Length: " - . length($request) . "\r\n" - . "Connection: close\r\n" . "\r\n" - . "${request}\r\n"; + $request = + "POST /my.activation.php3 HTTP/1.0\r\n" + . "Host: ${opt_host}\r\n" + . "Content-Type: application/x-www-form-urlencoded\r\n" + . "Content-Length: " + . length($request) . "\r\n" + . "Connection: close\r\n" . "\r\n" + . "${request}\r\n"; - $response = qx(echo "${request}" | ${openssl} 2>/dev/null); + $response = qx(echo "${request}" | ${openssl} 2>/dev/null); + } + else { + $response = $ua->post( + "https://$opt_host:443/my.activation.php3", + check => 1, + username => $opt_user, + password => $opt_passcode, + mrhlogonform => 1, + client_data => $client_data + ); + + ohshit "login failed with http code @{[$response->message]}\n" + unless $response->is_success; + + $response = $response->as_string; + } # We can then parse the response for the MRHSession Cookie, which contains our # SessionID. In this example, we print out the SessionID in order to verify # that our log in attempt worked. + $response =~ /MRHSession=(\w+);/; $sessionid = $1; verbose "% session id ${sessionid}\n"; + $cookies->set_cookie(0, MRHSession => $sessionid, "/", $opt_host, 443, 1, 0, 10, 0, {}); + #warn $cookies->as_string; + ### ### STEP 3 :: Create the SSL VPN tunnel. ### @@ -189,17 +240,33 @@ # Now that we are authenticated and have a valid SessionID, we must request # specific pages/objects in order to initiate a SSL VPN tunnel. Before we do # this, let's determine the resource locator for our Network Access favorite. - $request = "GET /vdesk/vpn/index.php3?outform=xml HTTP/1.0\r\n" - . "Cookie: MRHSession=${sessionid}\r\n" . "\r\n"; - $response = qx(echo "${request}" | ${openssl} 2>/dev/null); + #if (!$use_lwp) { + if (1) { + $request = "GET /vdesk/vpn/index.php3?outform=xml HTTP/1.0\r\n" + . "Cookie: MRHSession=${sessionid}\r\n" . "\r\n"; + $response = qx(echo "${request}" | ${openssl} 2>/dev/null); + } + else { + $response = $ua->get("https://$opt_host:443/vdesk/vpn/index.php3?outform=xml"); + + ohshit "creating tunnel failed with http code @{[$response->message]}\n" + unless $response->is_success; + + $response = $response->as_string; + } # The response is XML, so we can safely grab what we are looking for using some # regular expression magic. Same with the SessionID, we're printing out the # final value to make sure we're on the right track. + open(X, ">response.out") or die; + print X $response; + $response =~ /${opt_name}[^\n]+\n[^Z]+Z=\d+,(\d+)/; $favorite = $1; verbose "% favorite ${favorite}\n"; + exit; + # We're all set! Let's visit the necessary pages/objects to notify FirePass # that we wish to open a SSL VPN tunnel. foreach my $uri ("/vdesk/", "/vdesk/vpn/connect.php3?Z=0,${favorite}",) { @@ -217,7 +284,7 @@ { my $pid = fork(); # this way we've better control - die ME . ": can't fork: $!\n" if not defined $pid; + ohshit "can't fork: $!\n" if not defined $pid; if ($pid == 0) { $EUID = $UID; # get ROOT, the pppd want's to see @@ -228,11 +295,11 @@ connect => ${request}, linkname => $opt_linkname, qw(noauth crtscts passive noproxyarp local) - ) or die ME . ": Can't exec: $!\n"; + ) or ohshit "Can't exec: $!\n"; } wait; - die ME . ": pppd didn't return sucessfully (rc: @{[$? >> 8]})\n" if $?; + ohshit "pppd didn't return sucessfully (rc: @{[$? >> 8]})\n" if $?; } # now executing the script @@ -241,13 +308,13 @@ if (defined $opt_script and not $opt_debug) { my $pid = fork(); - die ME . ": Can't fork: $!\n" if not defined $pid; + ohshit "Can't fork: $!\n" if not defined $pid; if ($pid == 0) { # child if ($UID != $EUID) { my ($owner, $group) = (stat $opt_script)[ 4, 5 ]; - die ME . ": can't stat $opt_script\n" if not defined $owner; + ohshit "can't stat $opt_script\n" if not defined $owner; $UID = $EUID = $owner; $GID = $EGID = $group; @@ -257,7 +324,7 @@ "% executing script $opt_script with uid $UID and gid $GID\n"; exec $opt_script - or die ME . ": Can't exec $opt_script: $!\n"; + or ohshit "Can't exec $opt_script: $!\n"; } $pid = wait; verbose "% script returned $?\n"; @@ -275,7 +342,7 @@ sub do_kill($) { my $linkname = shift; open((my $fh), $_ = "/var/run/ppp-$linkname.pid") - or die ME . ": Can't open the link file \"$_\": $!\n"; + or ohshit "Can't open the link file \"$_\": $!\n"; my $pid = <$fh>; my $iface = <$fh>; @@ -284,10 +351,10 @@ # become root doesn't seem to be necessary, as long as our # real uid is 0 - kill 15, $pid or die ME . ": Can't kill pid $pid: $!\n"; + kill 15, $pid or ohshit "Can't kill pid $pid: $!\n"; waitpid $pid, 0 != -1 - or die ME . ": was not able to wait for the ppp process: $!\n"; + or ohshit "was not able to wait for the ppp process: $!\n"; verbose "% process killed, rc: " . ($? >> 8) @@ -308,8 +375,8 @@ sap-vpn [-l|--linkname ] -k|--kill - sap-vpn [-h|--help] - sap-vpn [-m|--man] + sap-vpn -h|--help + sap-vpn -m|--man =head1 DESCRIPTION