vpn
changeset 3 01314d620fe0
parent 2 5f08067bc677
child 4 74ef29e9ab00
equal deleted inserted replaced
2:5f08067bc677 3:01314d620fe0
     1 #! /usr/bin/perl -T
       
     2 # $Id$
       
     3 # $URL$
       
     4 # based on a version Uwe Werler downloaded from f5networks developer
       
     5 # area http://devcentral.f5.com/SDK/sslvpn.public.pl.txt
       
     6 
       
     7 use 5.8.8;
       
     8 use strict;
       
     9 use warnings;
       
    10 use Getopt::Long qw(:config no_ignore_case bundling);
       
    11 use Pod::Usage;
       
    12 use File::Basename;
       
    13 use English qw(-no_match_vars);
       
    14 
       
    15 ($EUID, $UID) = ($UID, $EUID);    # release ROOT
       
    16 ($0) = ($0 =~ /(\w+)/);           # untaint $0
       
    17 
       
    18 use constant ME => basename $0;
       
    19 
       
    20 delete @ENV{ grep /PATH/, keys %ENV };
       
    21 $ENV{PATH} = "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin";
       
    22 
       
    23 ###
       
    24 ### STEP 0 :: Verify that we have the necessary requirements.
       
    25 ###
       
    26 ### This script requires recent versions of Perl, OpenSSL and PPPD (including
       
    27 ### the 'chat' program), and that they all be in our PATH.  This script was
       
    28 ### written and tested with the following versions:
       
    29 ###	FirePass:	5.5 and 6.0
       
    30 ###	Perl:		5.8.8
       
    31 ###	OpenSSL:	0.9.8b
       
    32 ###	PPPD:		2.4.4
       
    33 
       
    34 ###
       
    35 ### STEP 1 :: Set up variables with the proper information to log in.
       
    36 ###
       
    37 
       
    38 # Default values for the FQDN or IP of the FirePass we wish to connect to, the
       
    39 # name of our Network Access favorite, and our username/pasword.  All of these
       
    40 # can be passed as arguments, if desired.
       
    41 
       
    42 my $opt_user;
       
    43 my $opt_passcode;
       
    44 my $opt_host = 'connectwdf.sap.com';
       
    45 my $opt_name = 'SAP Network Access';
       
    46 my $opt_help = 0;
       
    47 my $opt_man  = 0;
       
    48 my @ppp_opts = ();
       
    49 my $opt_debug;
       
    50 my $opt_script;
       
    51 
       
    52 MAIN: {
       
    53 
       
    54     GetOptions(
       
    55         "u|user=s"     => \$opt_user,
       
    56         "p|passcode=s" => \$opt_passcode,
       
    57         "H|host=s"     => \$opt_host,
       
    58         "n|favorite=s" => \$opt_name,
       
    59         "d|debug"      => \$opt_debug,
       
    60         "o|opts=s"     => \@ppp_opts,
       
    61         "h|help"       => \$opt_help,
       
    62         "m|man"        => \$opt_man,
       
    63         "s|script"     => \$opt_script,
       
    64     ) or pod2usage();
       
    65 
       
    66     pod2usage(-verbose => 1, -exitval => 0) if $opt_help;
       
    67     pod2usage(-verbose => 2, -exitval => 0) if $opt_man;
       
    68     pod2usage(-verbose => 0, -exitval => 0) if not defined $opt_user;
       
    69 
       
    70     (my $user) = ($opt_user =~ /([a-z]\d+)/i);
       
    71     die ME . ": username \"$opt_user\" didn't pass verification\n"
       
    72       if $user ne $opt_user;
       
    73 
       
    74     die ME . ": need to run with root permissions!\n"
       
    75       if not $EUID == 0 || $UID == 0;
       
    76 
       
    77     if (not defined $opt_passcode) {
       
    78         chomp(my $settings = qx{stty "-g"});
       
    79         open(IN, "/dev/tty") or die ME . ": Can't open /dev/tty: $!\n";
       
    80         print "Passcode for $opt_user: ";
       
    81         system stty => "-echo";
       
    82         chomp($opt_passcode = <IN>);
       
    83         system stty => $settings;
       
    84         print "\n";
       
    85     }
       
    86 
       
    87     pod2usage(-verbose => 0, -exitval => 0) if not defined $opt_passcode;
       
    88     (my $passcode) = ($opt_passcode =~ /(\d+)/i);
       
    89     die ME . ": passcode didn't pass verification\n"
       
    90       if $passcode ne $opt_passcode;
       
    91 
       
    92     -x $opt_script
       
    93       or die ME . ": Script $opt_script is not executable: $!\n"
       
    94       if defined $opt_script;
       
    95 
       
    96     push @ppp_opts, "nodetach" if $opt_debug;
       
    97     push @ppp_opts, $opt_debug ? "debug" : "nolog";
       
    98     push @ppp_opts, "nodefaultroute" if not grep /^defaultroute$/, @ppp_opts;
       
    99     print "\nPPP-Options: @ppp_opts\n" if $opt_debug;
       
   100 
       
   101     # Declare variables used throughout the rest of the script.
       
   102     my ($request, $response, $sessionid, $favorite);
       
   103 
       
   104     # Store the OpenSSL command in a variable for convienence.
       
   105     my $openssl =
       
   106         "openssl s_client"
       
   107       . (-d "/etc/ssl/certs" ? " -CApath /etc/ssl/certs" : "")
       
   108       . " -ign_eof -quiet -connect ${opt_host}:443";
       
   109 
       
   110     # Make initial request to get client_data
       
   111     $request =
       
   112         "GET /my.logon.php3?check=1 HTTP/1.0\r\n"
       
   113       . "Content-Type: application/x-www-form-urlencoded\r\n"
       
   114       . "Connection: close\r\n" . "\r\n";
       
   115 
       
   116     $response = qx(echo "${request}" | ${openssl} 2>/dev/null)
       
   117       or die ME . ": Invalid Host specified or not reachable: $opt_host\n";
       
   118 
       
   119     $response =~ s/<INPUT type="hidden" name="client_data" value="(.*)">/$1/;
       
   120     my $client_data = $1;
       
   121 
       
   122 ###
       
   123 ### STEP 2 :: Log in to FirePass.
       
   124 ###
       
   125 
       
   126   # This is the bare minimum required in order to successfully log in.  A normal
       
   127   # browser will make many more requests than this to complete the log in
       
   128   # sequence, but all that is required is this POST with our credentails.  This
       
   129   # may fail if the FirePass has End-Point Security Policies configured.
       
   130     $request =
       
   131 "check=1&username=${user}&password=${passcode}&mrhlogonform=1&client_data=${client_data}";
       
   132 
       
   133     $request =
       
   134         "POST /my.activation.php3 HTTP/1.0\r\n"
       
   135       . "Host: ${opt_host}\r\n"
       
   136       . "Content-Type: application/x-www-form-urlencoded\r\n"
       
   137       . "Content-Length: "
       
   138       . length($request) . "\r\n"
       
   139       . "Connection: close\r\n" . "\r\n"
       
   140       . "${request}\r\n";
       
   141 
       
   142     $response = qx(echo "${request}" | ${openssl} 2>/dev/null);
       
   143 
       
   144   # We can then parse the response for the MRHSession Cookie, which contains our
       
   145   # SessionID.  In this example, we print out the SessionID in order to verify
       
   146   # that our log in attempt worked.
       
   147     $response =~ /MRHSession=(\w+);/;
       
   148     $sessionid = $1;
       
   149     print "SessionID: ${sessionid}\n" if $opt_debug;
       
   150 
       
   151 ###
       
   152 ### STEP 3 :: Create the SSL VPN tunnel.
       
   153 ###
       
   154 
       
   155    # Now that we are authenticated and have a valid SessionID, we must request
       
   156    # specific pages/objects in order to initiate a SSL VPN tunnel.  Before we do
       
   157    # this, let's determine the resource locator for our Network Access favorite.
       
   158     $request = "GET /vdesk/vpn/index.php3?outform=xml HTTP/1.0\r\n"
       
   159       . "Cookie: MRHSession=${sessionid}\r\n" . "\r\n";
       
   160     $response = qx(echo "${request}" | ${openssl} 2>/dev/null);
       
   161 
       
   162  # The response is XML, so we can safely grab what we are looking for using some
       
   163  # regular expression magic.  Same with the SessionID, we're printing out the
       
   164  # final value to make sure we're on the right track.
       
   165     $response =~ /${opt_name}[^\n]+\n[^Z]+Z=\d+,(\d+)/;
       
   166     $favorite = $1;
       
   167     print "Favorite: ${favorite}\n" if $opt_debug;
       
   168 
       
   169     # We're all set!  Let's visit the necessary pages/objects to notify FirePass
       
   170     # that we wish to open a SSL VPN tunnel.
       
   171     foreach my $uri ("/vdesk/", "/vdesk/vpn/connect.php3?Z=0,${favorite}",) {
       
   172         $request = "GET ${uri} HTTP/1.0\r\n"
       
   173           . "Cookie: MRHSession=${sessionid}\r\n" . "\r\n";
       
   174         system("echo \"${request}\" | ${openssl} >/dev/null 2>&1");
       
   175     }
       
   176 
       
   177 # We are now authenticated, and have requested the necessary pre-tunnel
       
   178 # pages/objects.  It's time to start our SSL VPN connection.  To do this, we are
       
   179 # simply calling PPPD, and having it use OpenSSL as a psuedo terminal device.
       
   180     $request = "GET /myvpn?sess=${sessionid} HTTP/1.0\r\n"
       
   181       . "Cookie: MRHSession=${sessionid}\r\n" . "\r\n";
       
   182     $request = "chat -v '' '${request}'";
       
   183 
       
   184     {
       
   185         local $EUID = $UID;    # get ROOT, the pppd want's to see
       
   186                                # the UID and the EUID to be ROOT
       
   187         system(
       
   188             pppd     => @ppp_opts,
       
   189             pty      => ${openssl},
       
   190             connect  => ${request},
       
   191             linkname => "sap-vpn",
       
   192             qw(noauth crtscts passive noproxyarp
       
   193               local),
       
   194         );
       
   195     }
       
   196 
       
   197     # Voila!  We should now have a PPP connection running over SSL.  We can exit
       
   198     # from this script cleanly, and move on to setting up routes to the remote
       
   199     # network using our favorite networking tools.  Happy hacking!
       
   200 
       
   201     exit(0);
       
   202 
       
   203 }
       
   204 
       
   205 __END__
       
   206 
       
   207 =head1 SYNOPSIS
       
   208 
       
   209  vpn [-d|--debug] [-H|--host <host>] [-o|--opt <ppp option>]...
       
   210      [-s|--script <script>]
       
   211      [-u|--user <user>] [-p|--passcode <passcode>]
       
   212 
       
   213  vpn [-h|--help]
       
   214  vpn [-m|--man]
       
   215 
       
   216 =head1 DESCRIPTION
       
   217 
       
   218 Ths script establishes a VPN connecttion to a FirePass server.
       
   219 B<Note:> This script requires B<root> privileges and should be run via sudo
       
   220 or some other approbiate mechanism. (You may install it SUID root. This
       
   221 is the safest way, since the script rises its privileges only when
       
   222 necessary, instead of operating all the time as root.)
       
   223 
       
   224 =head1 OPTIONS
       
   225 
       
   226 =over
       
   227 
       
   228 =item B<-d>
       
   229 
       
   230 Print debug output from pppd. In this mode the script (and the pppd)
       
   231 will not detach from your terminal and the script code will not be
       
   232 executed.  (default: 0)
       
   233 
       
   234 =item B<-h>|B<--help>
       
   235 
       
   236 A short help. (defualt: off)
       
   237 
       
   238 =item B<-H>|B<--host> I<host>
       
   239 
       
   240 Host to connect to. (defaults to C<connectwdf.sap.corp>)
       
   241 
       
   242 =item B<-m>|B<--man>
       
   243 
       
   244 This man page. (default: off)
       
   245 
       
   246 =item B<-n>|B<--name> I<name>
       
   247 
       
   248 Name of the service(?). (default: to 'SAP Network Access')
       
   249 
       
   250 =item B<-o>|B<--opts> I<ppp options>
       
   251 
       
   252 Additional options for ppp. Can be specified multiple times. (default: nodetach)
       
   253 Good choices are C<detach>, or C<updetach>.
       
   254 B<Note:> The C<nodefaultroute> is set automatically.
       
   255 
       
   256 =item B<-p>|B<--passcode> I<passcode>
       
   257 
       
   258 Your passcode, if not supplied, F</dev/tty> is opened for reading
       
   259 the passcode. (no default)
       
   260 
       
   261 =item B<-s>|B<--script> I<script>
       
   262 
       
   263 Script to execute after establishing the IP link. (See the B<-d> option
       
   264 above!). (no default)
       
   265 
       
   266 B<Note:> You should consider using the F</etc/ppp/ip-up.d/> scripts.
       
   267 (see below)
       
   268 
       
   269 B<Note2:> NOT IMPLEMENTED YET!
       
   270 
       
   271 
       
   272 =item B<-u>|B<--user> I<user>
       
   273 
       
   274 Your D|C|I user, not really optional. (no default)
       
   275 
       
   276 =back
       
   277 
       
   278 =cut
       
   279 
       
   280 ###
       
   281 ### End of file.
       
   282 ###