root/fwknop/trunk/fwknop

Revision 1353, 69.8 kB (checked in by mbr, 1 week ago)

bumped version to 1.9.9

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Id Revision
Line 
1 #!/usr/bin/perl -w
2 #
3 #############################################################################
4 #
5 # File: fwknop
6 #
7 # URL: http://www.cipherdyne.org/fwknop/
8 #
9 # Purpose: fwknop implements an authorization scheme known as Single Packet
10 #          Authorization (SPA) that requires only a single encrypted packet to
11 #          communicate various pieces of information including desired access
12 #          through an iptables/ipfw policy and/or specific commands to execute
13 #          on the target system.  The main application of this program is to
14 #          protect services such as SSH with an additional layer of security
15 #          in order to make the exploitation of vulnerabilities (both 0-day
16 #          and unpatched code) much more difficult.  fwknop also supports
17 #          encrypted port knocking, but this is a legacy authentication mode
18 #          when compared to SPA.
19 #
20 #          More information can be found in the fwknop(8) and fwknopd(8) man
21 #          pages, and also online here:
22 #
23 #          http://www.cipherdyne.org/fwknop/docs/
24 #
25 # Author: Michael Rash (mbr@cipherdyne.org)
26 #
27 # Version: 1.9.9
28 #
29 # Copyright (C) 2004-2008 Michael Rash (mbr@cipherdyne.org)
30 #
31 # License - GNU Public License version 2 (GPLv2):
32 #
33 #    This program is distributed in the hope that it will be useful,
34 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
35 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36 #    GNU General Public License for more details.
37 #
38 #    You should have received a copy of the GNU General Public License
39 #    along with this program; if not, write to the Free Software
40 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
41 #    USA
42 #
43 #############################################################################
44 #
45 # $Id$
46 #
47
48 use IO::Socket;
49 use IO::Handle;
50 use MIME::Base64;
51 use Data::Dumper;
52 use POSIX;
53 use Getopt::Long;
54 use strict;
55
56 my $version = '1.9.9';
57 my $revision_svn = '$Revision$';
58 my $rev_num = '1';
59 ($rev_num) = $revision_svn =~ m|\$Rev.*:\s+(\S+)|;
60
61 my $lib_dir = '/usr/lib/fwknop';
62 my $print_version = 0;
63 my $print_help    = 0;
64 my $run_last_args = 0;
65 my $debug         = 0;
66 my $quiet         = 0;
67 my $verbose       = 0;
68 my $test_mode     = 0;
69 my $cmdl_homedir  = '';
70 my $knock_sleep   = 1;  ### default to 1 second difference between port knocks
71 my $knock_dst     = '';
72 my $homedir       = '';
73 my $min_port      = 10000;
74 my $max_port      = 65535;
75 my $spoof_src     = '';
76 my $server_mode   = 'pcap';
77 my $user_rc_file  = '';
78 my $server_proto  = '';
79 my $run_last_host = '';
80 my $total_digest  = '';
81 my $show_last_host_cmd = '';
82 my $show_last_cmd = 0;
83 my $time_offset_plus = '';
84 my $time_offset_minus = '';
85 my $use_md5       = 0;
86 my $use_sha1      = 0;
87 my $use_sha256    = 0;
88 my $gpg_home_dir  = '';
89 my $gpg_recipient = '';
90 my $use_gpg_agent = 0;
91 my $max_msg_len   = 1500;
92 my $max_resolve_http_recv = 1500;
93 my $gpg_verbose   = 0;
94 my $gpg_no_options = 0;
95 my $gpg_agent_info = '';
96 my $include_salted = 0;
97 my $client_src_port = 0;
98 my $gpg_default_key = 0;
99 my $gpg_use_options = 0;
100 my $err_wait_timer  = 30;  ### seconds
101 my $resolve_ip_url  = 'http://www.whatismyip.org/';
102 my $gpg_signing_key = '';
103 my $save_packet_mode = 0;
104 my $save_packet_file = '';
105 my $save_packet_append = 0;
106 my $cmdline_pcap_cmd   = '';
107 my $no_save_last_args  = 0;
108 my $save_destination   = 0;
109 my $server_auth_method = '';
110 my $spa_established_tcp  = 0;
111 my $resolve_external_ip  = 0;
112 my $server_auth_crypt_pw = '';
113 my $pcap_sleep_interval  = 1;  ### seconds
114 my $knock_dst_pre_resolve = '';
115 my $selected_random_nat_port = 0;
116 my $include_base64_trailing_equals = 0;
117 my $include_base64_gnupg_prefix = 0;
118 my $rand_port     = 0;  ### for SPA packet destination port
119 my $NAT_rand_port = 0;  ### for randomized access based on
120                         ### NAT rules (e.g. ssh -p <randport>).
121 my $NAT_local = 0; ### Flag for forwarding a port to local socket.
122
123 my $locale = 'C'### default LC_ALL env variable
124 my $no_locale = 0;
125 my $gpg_prefix = 'hQ'### base64 encoded version of 0x8502
126 my $gpg_path = '';
127
128 ### User agent for contacting http://www.whatismyip.org/, (can
129 ### override with --User-agent)
130 my $ext_resolve_user_agent = "Fwknop/$version";
131 $ext_resolve_user_agent =~ s|-pre\d+||;
132
133 ### ACCESS message:
134 ###     random data : user : client_timestamp : client_version : \
135 ###     type (1) : access_request : message digest
136 my $SPA_ACCESS_MODE  = 1;  ### default
137
138 ### COMMAND message:
139 ###     random data : user : client_timestamp : client_version : \
140 ###     type (0) : command : message digest
141 my $SPA_COMMAND_MODE = 0;
142
143 ### NAT ACCESS message:
144 ###     random data : user : client_timestamp : client_version : \
145 ###     type (2) : access_request : NAT_info : message digest
146 my $SPA_NAT_ACCESS_MODE = 2;
147
148 ### ACCESS message with client-defined firewall timeout:
149 ###     random data : user : client_timestamp : client_version : \
150 ###     type (3) : access_request : timeout : message digest
151 my $SPA_CLIENT_TIMEOUT_ACCESS_MODE = 3;
152
153 ### NAT ACCESS message with client-defined firewall timeout:
154 ###     random data : user : client_timestamp : client_version : \
155 ###     type (4) : access_request : NAT_info : timeout : message digest
156 my $SPA_CLIENT_TIMEOUT_NAT_ACCESS_MODE = 4;
157
158 ### local NAT ACCESS message:
159 ###     random data : user : client_timestamp : client_version : \
160 ###     type (5) : access_request : NAT_info : message digest
161 my $SPA_LOCAL_NAT_ACCESS_MODE = 5;
162
163 ### local NAT ACCESS message with client-defined firewall timeout:
164 ###     random data : user : client_timestamp : client_version : \
165 ###     type (6) : access_request : NAT_info : timeout : message digest
166 my $SPA_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MODE = 6;
167
168 ### default time values
169 my $knock_interval  = 60;
170 my $cmdl_fw_timeout = 0;
171
172 ### Digest types and command argument flags
173 my $MD5_DIGEST    = 1;
174 my $SHA1_DIGEST   = 2;
175 my $SHA256_DIGEST = 3;
176 my $digest_type = $SHA256_DIGEST; ### default
177 my $cmdl_digest_alg = '';
178
179 ### default destination port; you can change with --Server-port,
180 ### --rand-port, or by appending the ":<port>" syntax to the
181 ## destination host
182 my $DEFAULT_PORT = 62201;
183
184 ### default to root (client must run as root in this mode)
185 my $spoof_username = '';
186 my $spoof_proto    = 'udp'### default to udp
187
188 ### encrypted port knock vars (these are only used in the legacy
189 ### port knocking mode).
190 my $cmdline_offset  = 0;
191 my $enc_port_offset = 61000;  ### default offset
192 my $enc_key         = '';
193 my $enc_alg         = 'Rijndael';
194 my $enc_blocksize   = 32;
195
196 ### there is a constant "RIJNDAEL_KEYSIZE" in the Crypt::Rijndael sources, but
197 ### it is not used; a 16 byte key size is fine.
198 my $enc_keysize = 16;
199
200 my $enc_shared_secret = '';
201 my $enc_allow_ip      = '';
202 my $enc_source_ip     = '';
203 my $enc_rotate_proto  = 0;
204 my $get_key_file      = ''### get key from file
205 my $enc_pcap_port     = $DEFAULT_PORT;
206 my $access_str        = '';
207 my $NAT_access_str = ''### for access through the iptables FORWARD chain
208
209 ### packet counters
210 my $tcp_ctr  = 0;
211 my $udp_ctr  = 0;
212 my $icmp_ctr = 0;
213
214 ### tcp option types
215 my $tcp_nop_type       = 1;
216 my $tcp_mss_type       = 2;
217 my $tcp_win_scale_type = 3;
218 my $tcp_sack_type      = 4;
219 my $tcp_timestamp_type = 8;
220
221 my %tcp_p0f_opt_types = (
222     'N' => $tcp_nop_type,
223     'M' => $tcp_mss_type,
224     'W' => $tcp_win_scale_type,
225     'S' => $tcp_sack_type,
226     'T' => $tcp_timestamp_type
227 );
228
229 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|;
230
231 my @args_cp = @ARGV;
232
233 ### run GetOpt() to get comand line args
234 &handle_command_line();
235
236 &usage(0) if $print_help;
237
238 if ($print_version) {
239     print "[+] fwknop v$version (file revision: $rev_num)\n",
240         "      by Michael Rash <mbr\@cipherdyne.org>\n";
241     exit 0;
242 }
243
244 ### set LC_ALL env variable
245 $ENV{'LC_ALL'} = $locale unless $no_locale;
246
247 &set_digest_type() if $cmdl_digest_alg;
248 $digest_type = $MD5_DIGEST    if $use_md5;
249 $digest_type = $SHA1_DIGEST   if $use_sha1;
250 $digest_type = $SHA256_DIGEST if $use_sha256;
251
252 ### import fwknop perl modules
253 &import_perl_modules();
254
255 ### this is only necessary for older versions of perl (newer versions
256 ### call srand() automatically at the first usage of rand() if srand()
257 ### was not already called).
258 srand();
259
260 &get_homedir();
261
262 ### save a copy
263 $knock_dst_pre_resolve = $knock_dst;
264
265 if ($rand_port and $enc_pcap_port != $DEFAULT_PORT) {
266     die "[*] Cannot use --Server-port and --rand-port at the same time";
267 }
268
269 ### allow for ":<port>" extension to -D arg
270 if ($knock_dst =~ /(.*):(\d+)/) {
271     $knock_dst     = $1;
272     $enc_pcap_port = $2;
273
274     die "[*] Cannot use --rand-port with a manually specified -D <host>:<port>"
275         if $rand_port;
276 }
277
278 if ($rand_port) {
279     ### send the SPA packet over a random port between 10,000 and 65535
280     $enc_pcap_port = &rand_port();
281 }
282
283 if ($run_last_args or $show_last_cmd) {
284
285     ### run fwknop with same command line args as the previous
286     ### execution
287     &run_last_cmdline();
288
289 } elsif ($run_last_host or $show_last_host_cmd) {
290
291     $run_last_host = $show_last_host_cmd if $show_last_host_cmd;
292
293     ### run fwknop with the last args for this particular knock destination
294     &run_last_host_cmdline();
295 }
296
297 die "[*] Must specify a destination server with -D <IP|Host>"
298     unless $knock_dst;
299
300 if ($cmdl_fw_timeout ne '0') {
301     die "[*] Must specify a firewall timeout > 0"
302         unless $cmdl_fw_timeout > 0;
303 }
304
305 my $print_mode = '';
306 if (lc($server_mode) eq 'pcap') {
307     $print_mode = 'SPA';
308 } elsif (lc($server_mode) eq 'knock') {
309     $print_mode = 'encrypted port knocking';
310 } elsif (lc($server_mode) eq 'shared') {
311     $print_mode = 'shared sequence port knocking';
312 } else {
313     die "[*] Unknown server mode: $server_mode ",
314         qq|(must be "pcap", "knock", or "shared").|;
315 }
316
317 if ($debug) {
318     print "\n[+] ***DEBUG*** Starting fwknop client ($print_mode mode)...\n";
319 } else {
320     print "\n[+] Starting fwknop client ($print_mode mode)...\n"
321         unless $quiet;
322 }
323
324 if ($verbose) {
325     print "[+] fwknop Command line: @args_cp\n";
326 }
327
328 unless ($knock_dst =~ /$ip_re/) {
329     print "[+] Resolving hostname: $knock_dst\n" unless $quiet;
330     ### resolve to an IP
331     my $iaddr = inet_aton($knock_dst)
332         or die "[*] Could not resolve $knock_dst to an IP.";
333     my $addr = inet_ntoa($iaddr)
334         or die "[*] Could not resolve $knock_dst to an IP.";
335     $knock_dst = $addr;
336 }
337
338 if ($NAT_local and not $NAT_access_str) {
339     if ($NAT_rand_port) {
340         my $rand_port = &rand_port();
341         $NAT_access_str = "$knock_dst,$rand_port";
342         print "[+] Requesting NAT access for randomized port: $rand_port\n";
343         $selected_random_nat_port = 1;
344     } else {
345         $NAT_access_str = "$knock_dst,55000";
346         print
347 "[+] Requesting NAT support for port 55,000; use --NAT-rand-port for a\n",
348 "    random port.\n";
349     }
350 }
351
352 &validate_access_str() if $access_str;
353 &validate_NAT_access_str() if $NAT_access_str;
354
355 if (lc($server_mode) eq 'pcap' or lc($server_mode) eq 'knock') {
356     die "[*] Must also specify: -D <destination>\n"
357         unless $knock_dst;
358
359     unless ($enc_allow_ip
360             or $enc_source_ip
361             or $resolve_external_ip) {
362         die "[*] Must either specify: --allow-IP <IP>, ",
363             "--source-IP, or --Resolve-external-IP\n";
364     }
365
366     ### make fwknop server see "0.0.0.0" in the encrypted sequence.
367     ### This will instruct the server to open the port for whatever
368     ### source IP the sequence comes from.  This is useful for
369     ### clients that are behind a NAT device.
370     $enc_allow_ip = '0.0.0.0' if $enc_source_ip;
371
372     ### resolve the extenal IP via http://www.whatismyip.org
373     $enc_allow_ip = &resolve_external_ip() if $resolve_external_ip;
374
375     if ($spoof_src) {
376         $< == 0 && $> == 0 or
377             die '[*] You must be root (or equivalent ',
378                 "UID 0 account) to spoof the source address.\n";
379     }
380     unless ($enc_allow_ip =~ /$ip_re/) {
381         ### resolve to an IP
382         my $iaddr = inet_aton($enc_allow_ip)
383             or die "[*] Could not resolve $enc_allow_ip to IP.";
384         my $addr = inet_ntoa($iaddr)
385             or die "[*] Could not resolve $enc_allow_ip to IP.";
386         $enc_allow_ip = $addr;
387     }
388     if ($cmdline_offset) {
389         if (lc($server_mode) eq 'pcap') {
390             die "[*] Port offset is meaningless in pcap mode ",
391                 "(only a single packet is sent).";
392         }
393         unless ($cmdline_offset < 65280 and $cmdline_offset > 0) {
394             die "[*] Port offset must be 0 < port < 65280";
395         }
396         $enc_port_offset = $cmdline_offset;
397     }
398     if (lc($server_mode) eq 'pcap') {
399         unless ($enc_pcap_port < 65535 and $enc_pcap_port > 0) {
400             die "[*] Port offset must be 0 < port < 65535";
401         }
402     }
403 } else {
404     if ($enc_rotate_proto) {
405         die '[*] Can only specify --rotate-proto with ',
406             'encrypted sequences.';
407     }
408 }
409
410 if ($save_packet_mode) {
411     ### save of copy of the packet
412     unless ($save_packet_file) {
413         $save_packet_file = "$homedir/fwknop_save_packet.$$";
414     }
415     unless ($save_packet_append) {
416         unlink $save_packet_file if -e $save_packet_file;
417     }
418 }
419
420 ### save our command line args (so -l can be used next time)
421 unless ($run_last_args or $run_last_host or $no_save_last_args
422         or $show_last_cmd or $show_last_host_cmd) {
423     &save_args();
424 }
425
426 if (lc($server_mode) eq 'pcap' or lc($server_mode) eq 'knock') {
427
428     ### get the encryption key from the --get-key file
429     ### or from STDIN if it's not in the file.
430     &get_key();
431
432     &handle_server_auth_method() if $server_auth_method;
433
434     if (lc($server_mode) eq 'pcap') {
435
436         ### construct and send the encrypted message to the server
437         ### (sends a single packet).
438         &pcap_send_encrypted_msg(&pcap_build_enc_msg());
439
440     } else {
441         ### we are running in port knocking mode, so get the
442         ### encrypted port sequence (16 ports)
443         &knock_ports(&encrypt_sequence());
444     }
445 } else {
446     ### we are running in non-encrypted port knocking mode, so get
447     ### the port sequence
448     &knock_ports(&import_shared_sequence());
449 }
450 exit 0;
451 #============================ end main ==============================
452
453 sub pcap_build_enc_msg() {
454
455     ### message format (all fields are separated by ":" characters
456     #
457     #  random number (16 bytes)
458     #  username
459     #  timestamp
460     #  software version
461     #  message type and content:
462     #    0 => command mode / command to execute
463     #    1 => access mode / IP,proto,port
464     #    2 => nat access mode / IP,proto,port / internalIP,externalNATPort
465     #  (optional) server_auth (post 0.9.2 release)
466     #  message digest (SHA256 / SHA1 / MD5 )
467
468     my $msg = '';
469
470     unless ($quiet) {
471         print "\n[+] Building encrypted Single Packet Authorization (SPA) ",
472             "message...\n";
473         print "[+] Packet fields:\n\n";
474     }
475
476     ### start the SPA message with 16 bytes of random data
477     $msg = &SPA_random_number();
478
479     ### append the username
480     $msg .= &SPA_user();
481
482     ### append the timestamp
483     $msg .= &SPA_timestamp();
484
485     ### append the fwknop client version
486     $msg .= &SPA_version();
487
488     ### append the message type (integer)
489     $msg .= &SPA_message_type();
490
491     ### append the SPA message (this is usually just a request for
492     ### access to a port/protocol combination)
493     $msg .= &SPA_message();
494
495     ### append NAT access requirement (optional)
496     $msg .= &SPA_nat_access();
497
498     ### append server authentication method (optional)
499     $msg .= &SPA_server_auth();
500
501     ### append any client defined fw timeout (optional)
502     $msg .= &SPA_client_timeout();
503
504     ### append Message Digest
505     $msg =~ s/\n//g;
506     $msg .= &SPA_digest($msg);
507
508     if ($debug) {
509         print "\n[+] Clear text message (some fields base64 encoded): $msg\n",
510             "    Digest: $total_digest\n";
511     }
512
513     my $encrypted_msg = '';
514     if ($gpg_signing_key or $gpg_default_key) {
515         $encrypted_msg = &pcap_GPG_encrypt_msg($msg);
516     } else {
517         $encrypted_msg = &pcap_Rijndael_encrypt_msg($msg);
518     }
519
520     unless ($include_base64_trailing_equals) {
521         print "[+] Stripping trailing equals chars from base64 encoding.\n"
522             if $debug;
523         $encrypted_msg =~ s/=*$//;
524     }
525
526     return $encrypted_msg;
527 }
528
529 sub SPA_random_number() {
530     my $random_num = int(rand(100000000000000));
531     $random_num .= int(rand(10)) while (length($random_num) < 16);
532
533     print "        Random data:    $random_num\n" unless $quiet;
534
535     return $random_num;
536 }
537
538 sub SPA_user() {
539     my $user = 'root';
540     if ($spoof_src) {
541         if ($spoof_username) {
542             $user = $spoof_username;
543         }
544     } else {
545         ### getlogin() is better than using ENV{'USER'}, which is
546         ### easily manipulated, so only use as a last resort.
547         if ($spoof_username) {
548             $user = $spoof_username;
549         } else {
550             $user = getlogin() || getpwuid($<) ||
551                 die "[*] Could not determine user; try using the ",
552                     "--Spoof-user option";
553         }
554     }
555     print  "        Username:       $user\n" unless $quiet;
556     return ':' . encode_base64($user, '');
557 }
558
559 sub SPA_timestamp() {
560     my $timestamp = time();
561
562
563     if ($time_offset_plus) {
564         my $offset = &time_offset($time_offset_plus);
565         $timestamp += $offset;
566     }
567
568     if ($time_offset_minus) {
569         my $offset = &time_offset($time_offset_minus);
570         $timestamp -= $offset;
571     }
572
573     print "        Timestamp:      $timestamp\n" unless $quiet;
574     return ':' . $timestamp;
575 }
576
577 sub SPA_version() {
578     print "        Version:        $version\n" unless $quiet;
579     return ':' . $version;
580 }
581
582 sub SPA_message_type() {
583     if ($cmdline_pcap_cmd) {
584         print "        Type:           $SPA_COMMAND_MODE (command mode)\n"
585             unless $quiet;
586         return ':' . $SPA_COMMAND_MODE;
587     } elsif ($NAT_access_str) {
588         if ($NAT_local) {
589             if ($cmdl_fw_timeout > 0) {
590                 print "        Type:           " .
591                     "$SPA_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MODE ",
592                     "(Local NAT client-timeout access mode)\n" unless $quiet;
593                 return ':' . $SPA_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MODE;
594             } else {
595                 print "        Type:           $SPA_LOCAL_NAT_ACCESS_MODE ",
596                     "(Local NAT access mode)\n" unless $quiet;
597                 return ':' . $SPA_LOCAL_NAT_ACCESS_MODE;
598             }
599         } else {
600             if ($cmdl_fw_timeout > 0) {
601                 print "        Type:           " .
602                     "$SPA_CLIENT_TIMEOUT_NAT_ACCESS_MODE ",
603                     "(NAT client-timeout access mode)\n" unless $quiet;
604                 return ':' . $SPA_CLIENT_TIMEOUT_NAT_ACCESS_MODE;
605             } else {
606                 print "        Type:           $SPA_NAT_ACCESS_MODE ",
607                     "(NAT access mode)\n" unless $quiet;
608                 return ':' . $SPA_NAT_ACCESS_MODE;
609             }
610         }
611     }
612     if ($cmdl_fw_timeout > 0) {
613         print "        Type:           " .
614             "$SPA_CLIENT_TIMEOUT_ACCESS_MODE (access ",
615             "client-timeout mode)\n" unless $quiet;
616         return ':' . $SPA_CLIENT_TIMEOUT_ACCESS_MODE;
617     } else {
618         print "        Type:           $SPA_ACCESS_MODE (access mode)\n"
619             unless $quiet;
620         return ':' . $SPA_ACCESS_MODE;
621     }
622 }
623
624 sub SPA_message() {
625     ### a specific command will be executed on the server.  Note we
626     ### prepend the command string with the $enc_allow_ip so that the
627     ### fwknopd server can apply the REQUIRE_SOURCE_ADDRESS criteria.
628     if ($cmdline_pcap_cmd) {
629         print "        Cmd:            $cmdline_pcap_cmd\n" unless $quiet;
630         return ':' . encode_base64("$enc_allow_ip,$cmdline_pcap_cmd", '');
631
632     }
633     unless ($access_str) {
634         $access_str = 'none,0';
635     }
636     ### access to port(s)/protocol(s) will be granted on the
637     ### server
638     print "        Access:         $enc_allow_ip,$access_str\n"
639         unless $quiet;
640     return ':' . encode_base64("$enc_allow_ip,$access_str", '');
641 }
642
643 sub SPA_client_timeout() {
644     return '' unless $cmdl_fw_timeout;
645     return ':' . $cmdl_fw_timeout;
646 }
647
648 sub SPA_server_auth() {
649     if (lc($server_auth_method) eq 'crypt') {
650         unless ($quiet) {
651             print "        Server auth:   $server_auth_method,";
652             for (my $i=0; $i<length($server_auth_crypt_pw); $i++) {
653                 print '*';
654             }
655             print "\n";
656         }
657         return ':' . encode_base64("crypt,$server_auth_crypt_pw", '');
658     }
659     return '';
660 }
661
662 sub SPA_nat_access() {
663     if ($NAT_access_str) {
664         print "        NAT access:     $NAT_access_str\n"
665             unless $quiet;
666         return ':' . encode_base64($NAT_access_str, '');
667     }
668     return '';
669 }
670
671 sub SPA_digest() {
672     my $msg = shift;
673
674     my $digest = '';
675
676     if ($digest_type == $MD5_DIGEST) {
677         require Digest::MD5;
678         Digest::MD5->import(qw(md5_base64));
679         if ($debug) {
680             print "[+] Digest::MD5 $Digest::MD5::VERSION\n";
681         }
682         $digest = md5_base64($msg);
683         print "        MD5 digest:     $digest\n" unless $quiet;
684         if ($debug) {
685             $total_digest = md5_base64("$msg:$digest");
686         }
687     } elsif ($digest_type == $SHA1_DIGEST) {
688         require Digest::SHA;
689         Digest::SHA->import(qw(sha1_base64));
690         if ($debug) {
691             print "[+] Digest::SHA::VERSION $Digest::SHA::VERSION\n";
692         }
693         $digest = sha1_base64($msg);
694         print "        SHA1 digest:    $digest\n" unless $quiet;
695         if ($debug) {
696             $total_digest = sha1_base64("$msg:$digest");
697         }
698     } elsif ($digest_type == $SHA256_DIGEST) {
699         require Digest::SHA;
700         Digest::SHA->import(qw(sha256_base64));
701         if ($debug) {
702             print "[+] Digest::SHA::VERSION $Digest::SHA::VERSION\n";
703         }
704         $digest = sha256_base64($msg);
705         print "        SHA256 digest:  $digest\n" unless $quiet;
706         if ($debug) {
707             $total_digest = sha256_base64("$msg:$digest");
708         }
709     } else {
710         die "[*] Improper digest algorithm, use --help";
711     }
712     return ':' . $digest;
713 }
714
715 sub pcap_GPG_encrypt_msg() {
716     my $msg = shift;
717
718     my $gnupg = GnuPG::Interface->new();
719
720     $gpg_home_dir = "$homedir/.gnupg" unless $gpg_home_dir;
721
722     my %gnupg_options = (
723         'batch' => 1,
724         'homedir' => $gpg_home_dir,
725         'no_options' => 1
726     );
727
728     delete $gnupg_options{'batch'} if $gpg_verbose;
729     delete $gnupg_options{'no_options'} if $gpg_use_options;
730
731     $gnupg->options->hash_init(%gnupg_options);
732
733     ### if --gpg-default-key is given, then we trust that the user has
734     ### set the default key with the default-key variable in ~/.gnupg/options
735     ### and we need to enable options
736     if ($gpg_default_key) {
737         delete $gnupg_options{'no_options'}
738             if defined delete $gnupg_options{'no_options'};
739     } else {
740         $gnupg->options->default_key($gpg_signing_key);
741     }
742
743     $gnupg->options->push_recipients($gpg_recipient);
744
745     if ($gpg_path) {
746         ### normally gpg is in the local path, but if not --gpg-path can
747         ### provide a custom path
748         $gnupg->call($gpg_path);
749     }
750
751     my ($input, $output, $error, $pw, $status) =
752         (IO::Handle->new(),
753         IO::Handle->new(),
754         IO::Handle->new(),
755         IO::Handle->new(),
756         IO::Handle->new());
757
758     my $handles = GnuPG::Handles->new(
759         stdin  => $input,
760         stdout => $output,
761         stderr => $error,
762         passphrase => $pw,
763         status => $status
764     );
765
766     my $pid;
767
768     if ($use_gpg_agent or $gpg_agent_info) {
769         if ($gpg_agent_info) {
770             $ENV{'GPG_AGENT_INFO'} = $gpg_agent_info;
771         }
772         $pid = $gnupg->sign_and_encrypt('handles' => $handles,
773             'command_args' => [ qw( --use-agent ) ]);
774     } else {
775         $pid = $gnupg->sign_and_encrypt('handles' => $handles);
776     }
777
778     print $pw $enc_key;
779     close $pw;
780
781     print $input $msg;
782     close $input;
783
784     my @ciphertext = <$output>;
785     close $output;
786
787     my @errors = <$error>;
788     close $error;
789
790     waitpid $pid, 0;
791
792     my $ctext = '';
793     if (@ciphertext) {
794         $ctext = join '', @ciphertext;
795     }
796
797     unless ($ctext) {
798         print "[*] GnuPG encrypt failed.\n";
799         unless ($gpg_verbose) {
800             print "    GnuPG errors:\n";
801             print for @errors;
802         }
803         exit 1;
804     }
805
806     if ($verbose) {
807         print "[+] Encrypted msg hex dump (" .
808             length($ctext) . " bytes):\n";
809         &hex_dump($ctext);
810     }
811
812     my $encoded_msg = encode_base64($ctext, '');
813
814     if ($verbose and $debug) {
815         print "[+] base64-encoded message before stripping identifying chars:\n",
816             $encoded_msg, "\n";
817     }
818
819     if ($encoded_msg =~ /^$gpg_prefix/) {
820         unless ($include_base64_gnupg_prefix) {
821             print qq|[+] Stripping encoded "$gpg_prefix" prefix from |,
822                 "outgoing encoded SPA packet.\n" if $debug;
823             ### perl -MMIME::Base64 -e 'print encode_base64("\x85\x02\n")'
824             ### The 'magic' database (via the 'file') command identifies GnuPG
825             ### encrypted files as starting with 0x8502
826             $encoded_msg =~ s/^$gpg_prefix//;
827         }
828     } else {
829         print
830 "[-] Warning: GnuPG encrypted SPA packet does not begin with: $gpg_prefix\n",
831 "    It is recommend to set GPG_NO_PREFIX_ADD in access.conf on the fwknopd\n",
832 "  &nbs