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