*/
enum {
FKO_DIGEST_NAME = 0x100,
+ ENCRYPTION_MODE,
NAT_LOCAL,
NAT_PORT,
NAT_RAND_PORT,
/* Our getopt_long options string.
*/
-#define GETOPTS_OPTION_STRING "a:A:bB:C:D:f:gG:hH:lm:n:N:p:P:Q:rRsS:Tu:U:vV"
+#define GETOPTS_OPTION_STRING "a:A:bB:C:D:f:gG:hH:lm:M:n:N:p:P:Q:rRsS:Tu:U:vV"
/* Our program command-line options...
*/
{"server-cmd", 1, NULL, 'C'},
{"digest-type", 1, NULL, FKO_DIGEST_NAME},
{"destination", 1, NULL, 'D'},
+ {"encryption-mode", 1, NULL, ENCRYPTION_MODE},
{"fw-timeout", 1, NULL, 'f'},
{"gpg-encryption", 0, NULL, 'g'},
{"gpg-recipient-key", 1, NULL, GPG_RECIP_KEY },
/* Convert a digest_type string to its integer value.
*/
-static int
+static short
digest_strtoint(const char *dt_str)
{
if(strcasecmp(dt_str, "md5") == 0)
return(-1);
}
-/* Convert a protocol string to its intger value.
+/* Convert an encryption_mode string to its integer value.
+*/
+static int
+enc_mode_strtoint(const char *enc_mode_str)
+{
+ if(strcasecmp(enc_mode_str, "cbc") == 0)
+ return(FKO_ENC_MODE_CBC);
+ else if(strcasecmp(enc_mode_str, "ecb") == 0)
+ return(FKO_ENC_MODE_ECB);
+ else if(strcasecmp(enc_mode_str, "cfb") == 0)
+ return(FKO_ENC_MODE_CFB);
+ else if(strcasecmp(enc_mode_str, "pcbc") == 0)
+ return(-1); /* not supported yet */
+ else if(strcasecmp(enc_mode_str, "ofb") == 0)
+ return(FKO_ENC_MODE_OFB);
+ else if(strcasecmp(enc_mode_str, "ctr") == 0)
+ return(FKO_ENC_MODE_CTR);
+ else
+ return(-1);
+}
+
+/* Convert a protocol string to its integer value.
*/
static int
proto_strtoint(const char *pr_str)
else
options->time_offset_plus = parse_time_offset(val);
}
+ /* symmetric encryption mode */
+ else if(CONF_VAR_IS(var, "ENCRYPTION_MODE"))
+ {
+ tmpint = enc_mode_strtoint(val);
+ if(tmpint < 0)
+ return(-1);
+ else
+ options->encryption_mode = tmpint;
+ }
/* Use GPG ? */
else if(CONF_VAR_IS(var, "USE_GPG"))
{
case FKO_DIGEST_NAME:
if((options->digest_type = digest_strtoint(optarg)) < 0)
{
- fprintf(stderr, "* Invalid digest type: %s\n", optarg);
+ fprintf(stderr,
+ "* Invalid digest type: %s, use {md5,sha1,sha256,sha384,sha512}\n",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'M':
+ case ENCRYPTION_MODE:
+ if((options->encryption_mode = enc_mode_strtoint(optarg)) < 0)
+ {
+ fprintf(stderr,
+ "* Invalid encryption mode: %s, use {cbc,ecb}\n",
+ optarg);
exit(EXIT_FAILURE);
}
break;
return(EXIT_FAILURE);
}
}
+
+ res = fko_set_spa_encryption_mode(ctx, FKO_ENC_MODE_ASYMMETRIC);
+ if(res != FKO_SUCCESS)
+ {
+ errmsg("fko_set_spa_encryption_mode", res);
+ return(EXIT_FAILURE);
+ }
+ }
+
+ if(options.encryption_mode && !options.use_gpg)
+ {
+ res = fko_set_spa_encryption_mode(ctx, options.encryption_mode);
+ if(res != FKO_SUCCESS)
+ {
+ errmsg("fko_set_spa_encryption_mode", res);
+ return(EXIT_FAILURE);
+ }
}
/* Set Digest type.
* an empty context, populate it with the encrypted data, set our
* options, then decode it.
*/
- res = fko_new_with_data(&ctx2, spa_data, NULL);
+ res = fko_new_with_data(&ctx2, spa_data, NULL, ctx->encryption_mode);
if(res != FKO_SUCCESS)
{
errmsg("fko_new_with_data", res);
return(EXIT_FAILURE);
}
+ res = fko_set_spa_encryption_mode(ctx2, ctx->encryption_mode);
+ if(res != FKO_SUCCESS)
+ {
+ errmsg("fko_set_spa_encryption_mode", res);
+ return(EXIT_FAILURE);
+ }
+
/* See if we are using gpg and if we need to set the GPG home dir.
*/
if(options.use_gpg)
time_t timestamp = 0;
short msg_type = -1;
short digest_type = -1;
+ int encryption_mode = -1;
int client_timeout = -1;
/* Should be checking return values, but this is temp code. --DSS
fko_get_spa_server_auth(ctx, &server_auth);
fko_get_spa_client_timeout(ctx, &client_timeout);
fko_get_spa_digest_type(ctx, &digest_type);
+ fko_get_spa_encryption_mode(ctx, &encryption_mode);
fko_get_encoded_data(ctx, &enc_data);
fko_get_spa_digest(ctx, &spa_digest);
fko_get_spa_data(ctx, &spa_data);
printf(" Nat Access: %s\n", nat_access == NULL ? "<NULL>" : nat_access);
printf(" Server Auth: %s\n", server_auth == NULL ? "<NULL>" : server_auth);
printf(" Client Timeout: %u\n", client_timeout);
- printf(" Digest Type: %u\n", digest_type);
+ printf(" Digest Type: %d\n", digest_type);
+ printf("Encryption Mode: %d\n", encryption_mode);
printf("\n Encoded Data: %s\n", enc_data == NULL ? "<NULL>" : enc_data);
- printf("\nSPA Data Digest: %s\n", spa_digest == NULL ? "<NULL>" : spa_digest);
+ printf("SPA Data Digest: %s\n", spa_digest == NULL ? "<NULL>" : spa_digest);
+
+ if (enc_data != NULL && spa_digest != NULL)
+ printf(" Plaintext: %s:%s\n", enc_data, spa_digest);
+
printf("\nFinal Packed/Encrypted/Encoded Data:\n\n%s\n\n", spa_data);
}
unsigned int spa_dst_port;
unsigned int spa_src_port; /* only used with --source-port */
- unsigned int digest_type;
+ short digest_type;
+ int encryption_mode;
/* Various command-line flags */
unsigned char verbose; /* --verbose mode */
AC_PROG_MAKE_SET
AC_PROG_LIBTOOL
+dnl Decide whether or not to build binaries with profiling coverage support
+dnl
+want_profile_coverage=no
+AC_ARG_ENABLE([profile-coverage],
+ [AS_HELP_STRING([--enable-profile-coverage],
+ [Build fwknop binaries with profile coverage support @<:@default is to disable@:>@])],
+ [want_profile_coverage=$enableval],
+ [])
+
+if test "x$want_profile_coverage" = "xyes"; then
+ CFLAGS="-g -O0"
+ FKO_CHECK_COMPILER_ARG([-fprofile-arcs -ftest-coverage -fno-inline])
+fi
+
dnl Decide whether or not to enable all warnings with -Wall
dnl
use_wall=yes
:man source: Fwknop Client
-:man manual: Fwknop Client
+:man manual: Fwknop Client
FWKNOP(8)
=========
SPA packets can easily be spoofed as well (this is a good thing in this
context), and this makes it possible to make it appear as though, say,
www.yahoo.com is trying to authenticate to a target system but in reality
-the actual connection will come from a seemingly unrelated IP.
+the actual connection will come from a seemingly unrelated IP.
Authorization packets are either encrypted with the 'Rijndael' block cipher
or via 'GnuPG' and associated asymmetric ciphers. If the symmetric encryption
*-a, --allow-ip*='<IP-address>'::
Specify IP address that should be permitted through the destination
*fwknopd* server firewall (this IP is encrypted within the SPA packet
- itself). This is useful to prevent a MTIM attack where a SPA packet
+ itself). This is useful to prevent a MITM attack where a SPA packet
can be intercepted en-route and sent from a different IP than the
original. Hence, if the *fwknopd* server trusts the source address
- on the SPA packet IP header then the attacker gains access.
+ on the SPA packet IP header then the attacker gains access.
The *-a* option puts the source address within the encrypted SPA
packet, and so thwarts this attack. The *-a* option is also
useful to specify the IP that will be granted access when the
Specify the message digest algorithm to use in the SPA data. Choices
are: *MD5*, *SHA1*, *SHA256* (the default), *SHA384*, and *SHA512*.
+*-M, --encryption-mode*='<mode>'::
+ Specify the encryption mode when AES is used for encrypting SPA packets.
+ The default is CBC mode, but others can be chosen such as CFB or OFB
+ as long as this is also specified in the 'access.conf' file on the
+ server side via the ENCRYPTION_MODE variable. In general, it is
+ recommended to not use this argument and just use the default.
+
*-N, --nat-access*='<internalIP:forwardPort>'::
The *fwknopd* server offers the ability to provide SPA access through
an iptables firewall to an internal service by interfacing with the
client can request that the server port forward an external port to an
internal IP, i.e. ``+--NAT-access 192.168.10.2,55000+''. In this case,
access will be granted to 192.168.10.2 via port 55000 to whatever
- service is requested via the *--access* argument (usually tcp/22).
+ service is requested via the *--access* argument (usually tcp/22).
Hence, after sending such an SPA packet, one would then do
``ssh -p 55000 user@host'' and the connection would be forwarded on
through to the internal 192.168.10.2 system automatically. Note that
Set a value to apply to the timestamp in the SPA packet. This can
be either a positive or negative value ('--time-offset-plus/minus').
+*ENCRYPTION_MODE*::
+ Specify the encryption mode when AES is used. This variable is a synonym
+ for the '--encryption-mode' command line argument.
+
*USE_GPG*::
Set to 'Y' to specify the use of GPG for encryption ('--gpg-encryption').
AUTHORS
-------
-Damien Stuart <dstuart@dstuart.org>,
+Damien Stuart <dstuart@dstuart.org>,
Michael Rash <mbr@cipherdyne.org>
CONTRIBUTORS
``FW_ACCESS_TIMEOUT'' is not set then the default timeout of 30 seconds
will automatically be set.
+*ENCRYPTION_MODE*: '<mode>'::
+ Specify the encryption mode when AES is used. The default is CBC mode,
+ but other modes can be selected such as OFB and CFB. In general, it is
+ recommended to not use this variable and leave it as the default.
+
*ENABLE_CMD_EXEC*: '<Y/N>'::
This instructs *fwknopd* to accept complete commands that are contained
within an authorization packet. Any such command will be executed on
--- /dev/null
+#!/usr/bin/perl -w
+#
+# File: spa-entropy.pl
+#
+# Purpose: To measure cross-packet SPA entropy on a byte by byte slice basis
+# and produce gunplot graphs. This is useful to measure SPA packet
+# randomness after encryption.
+#
+# Author: Michael Rash <mbr@cipherdyne.org>
+#
+# License: GPL v2
+#
+
+use MIME::Base64;
+use IPC::Open2;
+use Getopt::Long 'GetOptions';
+use strict;
+
+my $use_ent = 1;
+my $base64_decode = 1;
+my $packets = 0;
+my $prefix = 'entropy';
+my $file_to_measure = '';
+my $run_fwknop_client = 0;
+my $min_len = 0;
+my $lib_dir = '../../lib/.libs';
+my $fwknop_client_path = '../../client/.libs/fwknop';
+my $enc_mode = 'cbc';
+my $enable_fwknop_client_gpg = 0;
+my $spa_key_file = '../../test/local_spa.key';
+my $help = 0;
+
+my $use_openssl = 0;
+my $openssl_salt = '0000000000000000';
+my $openssl_mode = 'aes-256-cbc';
+
+my %min_max_entropy = (
+ 'min' => {
+ 'val' => -1,
+ 'pos' => 0,
+ },
+ 'max' => {
+ 'val' => -1,
+ 'pos' => 0,
+ }
+);
+
+my @encrypted_data = ();
+my @plaintext_data = ();
+my @cross_pkt_data = ();
+
+Getopt::Long::Configure('no_ignore_case');
+die "[*] See '$0 -h' for usage information" unless (GetOptions(
+ 'file-to-measure=s' => \$file_to_measure,
+ 'base64-decode' => \$base64_decode,
+ 'count=i' => \$packets,
+ 'prefix=s' => \$prefix,
+ 'run-fwknop-client' => \$run_fwknop_client,
+ 'enc-mode=s' => \$enc_mode,
+ 'gpg' => \$enable_fwknop_client_gpg,
+ 'lib-dir=s' => \$lib_dir,
+ 'Client-path=s' => \$fwknop_client_path,
+ 'use-openssl' => \$use_openssl,
+ 'openssl-salt=s' => \$openssl_salt,
+ 'openssl-mode=s' => \$openssl_mode,
+ 'help' => \$help,
+));
+&usage() if $help;
+
+die "[*] Must execute --run-fwknop-client in --use-openssl mode"
+ if $use_openssl and not $run_fwknop_client;
+
+&run_fwknop_client() if $run_fwknop_client;
+
+&read_data();
+
+&get_min_len();
+
+&build_data_slices();
+
+open F, "> $prefix.dat" or die $!;
+my $pos = 0;
+for my $str (@cross_pkt_data) {
+
+ my $entropy = &get_entropy($str);
+
+# print F "$pos $entropy\n";
+ print F "$pos $entropy ### " . &hex_dump($str) . "\n";
+
+ if ($min_max_entropy{'min'}{'val'} == -1
+ and $min_max_entropy{'max'}{'val'} == -1) {
+ $min_max_entropy{'min'}{'val'} = $entropy;
+ $min_max_entropy{'min'}{'pos'} = $pos;
+ $min_max_entropy{'max'}{'val'} = $entropy;
+ $min_max_entropy{'max'}{'pos'} = $pos;
+ } else {
+ if ($entropy < $min_max_entropy{'min'}{'val'}) {
+ $min_max_entropy{'min'}{'val'} = $entropy;
+ $min_max_entropy{'min'}{'pos'} = $pos;
+ }
+ if ($entropy > $min_max_entropy{'max'}{'val'}) {
+ $min_max_entropy{'max'}{'val'} = $entropy;
+ $min_max_entropy{'max'}{'pos'} = $pos;
+ }
+ }
+ $pos++;
+}
+close F;
+
+my $min = sprintf "%.2f", $min_max_entropy{'min'}{'val'};
+my $max = sprintf "%.2f", $min_max_entropy{'max'}{'val'};
+
+print "[+] Min entropy: $min at byte: $min_max_entropy{'min'}{'pos'}\n";
+print "[+] Max entropy: $max at byte: $min_max_entropy{'max'}{'pos'}\n";
+
+&run_gnuplot();
+
+exit 0;
+
+sub read_data() {
+
+ if ($use_openssl) {
+
+ ### we've already gotten plaintext information from the fwknop client,
+ ### so encrypt this data with openssl and use it to re-write the
+ ### $file_to_measure
+ unlink $file_to_measure if -e $file_to_measure;
+
+ my @openssl_encrypted_data = ();
+
+ ### encrypt the plaintext and use it to re-write the -f file
+ for my $line (@plaintext_data) {
+
+ my $ptext_file = 'ptext.tmp';
+ my $enc_file = 'ptext.enc';
+
+ open F, "> $ptext_file" or die $!;
+ print F $line;
+ close F;
+
+ unlink $enc_file if -e $enc_file;
+
+ system "openssl enc -$openssl_mode -a -S $openssl_salt " .
+ "-in ptext.tmp -out ptext.enc -k fwknoptest000000";
+
+ my $base64_enc_data = '';
+ open F, "< $enc_file" or die $!;
+ while (<F>) {
+ chomp;
+ $base64_enc_data .= $_;
+ }
+ close F;
+
+ push @openssl_encrypted_data, $base64_enc_data;
+
+ }
+
+ open F, "> $file_to_measure" or die $!;
+ for my $line (@openssl_encrypted_data) {
+ print F $line, "\n";
+ }
+ close F;
+ }
+
+ my $fh = *STDIN;
+ if ($file_to_measure) {
+ open IN, "< $file_to_measure" or die "[*] Could not open $file_to_measure: $!";
+ $fh = *IN;
+ }
+
+ my $l_ctr = 0;
+ while (<$fh>) {
+ next unless $_ =~ /\S/;
+ chomp;
+
+ if ($base64_decode) {
+ if (&is_base64($_)) {
+ my $base64_str = $_;
+
+ if ($enable_fwknop_client_gpg) {
+ unless ($base64_str =~ /^hQ/) {
+ $base64_str = 'hQ' . $base64_str;
+ }
+ } else {
+ ### base64-encoded "Salted__" prefix
+ unless ($base64_str =~ /^U2FsdGVkX1/) {
+ $base64_str = 'U2FsdGVkX1' . $base64_str;
+ }
+ }
+
+ my ($equals_rv, $equals_padding) = &base64_equals_padding($base64_str);
+ if ($equals_padding) {
+ $base64_str .= $equals_padding;
+ }
+ my $str = decode_base64($base64_str);
+
+ if ($enable_fwknop_client_gpg) {
+ $str =~ s/^\x85\x02//;
+ } else {
+ $str =~ s/^Salted__//;
+ }
+ push @encrypted_data, $str;
+ } else {
+ push @encrypted_data, $_;
+ }
+ } else {
+ push @encrypted_data, $_;
+ }
+
+ $l_ctr++;
+ if ($packets > 0) {
+ last if $l_ctr == $packets;
+ }
+ }
+
+ ### hex dump encrypted data
+ open HEX, "> hex_dump.data" or die $!;
+ for my $line (@encrypted_data) {
+ print HEX &hex_dump($line), "\n";
+ }
+ close HEX;
+
+ print "[+] Read in $l_ctr SPA packets...\n";
+ return;
+}
+
+sub run_fwknop_client() {
+ die "[*] Must set packets file with -f <file>" unless $file_to_measure;
+ die "[*] Must set packet count with -c <count>" unless $packets;
+
+ if (-e $file_to_measure) {
+ unlink $file_to_measure or die $!;
+ }
+
+ my $cmd = "LD_LIBRARY_PATH=$lib_dir $fwknop_client_path -A tcp/22 " .
+ "-a 127.0.0.2 -D 127.0.0.1 --get-key $spa_key_file " .
+ "-B $file_to_measure -b -v --test";
+
+ if ($enable_fwknop_client_gpg) {
+ $cmd .= ' --gpg-recipient-key 361BBAD4 --gpg-signer-key 6A3FAD56 ' .
+ '--gpg-home-dir ../../test/conf/client-gpg';
+ } else {
+ $cmd .= " -M $enc_mode";
+ }
+ $cmd .= " 2> /dev/null";
+
+ print "[+] Running fwknop client via the following command:\n\n$cmd\n\n";
+
+ for (my $i=0; $i < $packets; $i++) {
+ open C, "$cmd |" or die $!;
+ while (<C>) {
+ if (/Plaintext\:\s+(\S+)/) {
+ push @plaintext_data, $1;
+ last;
+ }
+ }
+ close C;
+ }
+
+ return;
+}
+
+sub get_min_len() {
+
+ ### calculate minimum length
+ for my $line (@encrypted_data) {
+ chomp $line;
+ next unless $line =~ /\S/;
+ my $len = length($line);
+ if ($min_len == 0) {
+ $min_len = $len;
+ } else {
+ if ($len < $min_len) {
+ $min_len = $len;
+ }
+ }
+ }
+ return;
+}
+
+sub build_data_slices() {
+ for my $line (@encrypted_data) {
+ my @chars = split //, $line;
+ my $c_ctr = 0;
+ for my $char (@chars) {
+ $cross_pkt_data[$c_ctr] .= $char;
+ last if $c_ctr == $min_len;
+ $c_ctr++;
+ }
+ }
+ return;
+}
+
+sub run_gnuplot() {
+ open F, "> $prefix.gnu" or die $!;
+
+ my $enc_str = $enc_mode;
+ $enc_str = 'gpg' if $enable_fwknop_client_gpg;
+
+ my $yrange = '[0:9]';
+ print F <<_GNUPLOT_;
+set title "SPA slice entropy (encryption mode: $enc_str)"
+set terminal gif nocrop enhanced
+set output "$prefix.gif"
+set grid
+set yrange $yrange
+plot '$prefix.dat' using 1:2 with lines title 'min: $min \\@ byte: $min_max_entropy{'min'}{'pos'}, max: $max \\@ byte: $min_max_entropy{'max'}{'pos'}'
+_GNUPLOT_
+ close F;
+
+ print "[+] Creating $prefix.gif gnuplot graph...\n\n";
+ system "gnuplot $prefix.gnu";
+
+ return;
+}
+
+sub get_entropy() {
+ my $data = shift;
+
+ my $entropy = '';
+
+ my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'ent');
+
+ print CHLD_IN $data;
+ close CHLD_IN;
+
+ while (<CHLD_OUT>) {
+ ### Entropy = 5.637677 bits per byte.
+ if (/Entropy\s=\s(\d\S+)/) {
+ $entropy = $1;
+ last;
+ }
+ }
+
+ close CHLD_OUT;
+
+ waitpid $pid, 0;
+ my $child_exit_status = $? >> 8;
+
+ return $entropy;
+}
+
+sub base64_equals_padding() {
+ my $msg = shift;
+ my $padding = '';
+
+ return 1, $padding if $msg =~ /=$/;
+
+ my $remainder = 4 - length($msg) % 4;
+
+ if ($remainder == 3) {
+ ### not possible for valid base64 data - should only have
+ ### pad with one or two '=' chars
+ return 0, $padding;
+ }
+
+ unless ($remainder == 4) {
+ $padding .= '='x$remainder;
+ }
+ return 1, $padding;
+}
+
+sub hex_dump() {
+ my $data = shift;
+
+ my @chars = split //, $data;
+ my $ctr = 0;
+
+ my $hex_part = '';
+ my $ascii_part = '';
+
+ for my $char (@chars) {
+
+ $hex_part .= sprintf "%.2x", ord($char);
+
+ if ($char =~ /[^\x20-\x7e]/) {
+ $ascii_part .= '.';
+ } else {
+ $ascii_part .= $char;
+ }
+ $ctr++;
+ }
+ return "$hex_part $ascii_part";
+# return "$ascii_part";
+}
+
+sub is_base64() {
+ my $data = shift;
+
+ ### check to make sure the packet data only contains base64 encoded
+ ### characters per RFC 3548: 0-9, A-Z, a-z, +, /, =
+ if ($data =~ /[^\x30-\x39\x41-\x5a\x61-\x7a\x2b\x2f\x3d]/) {
+ return 0;
+ }
+ if ($data =~ /=[^=]/) {
+ return 0;
+ }
+ return 1;
+}
+
+sub usage() {
+ print "$0 [options]\n";
+ exit 0;
+}
/* Now generate the key and initialization vector.
* (again it is the perl Crypt::CBC way, with a touch of
* fwknop).
- */
+ */
memcpy(tmp_buf+16, pw_buf, 16);
memcpy(tmp_buf+32, ctx->salt, 8);
/* Initialization entry point.
*/
static void
-rijndael_init(RIJNDAEL_context *ctx, const char *pass, const unsigned char *data)
+rijndael_init(RIJNDAEL_context *ctx, const char *pass,
+ const unsigned char *data, int encryption_mode)
{
- /* Use ECB mode to be compatible with the Crypt::CBC perl module.
+ /* The default (set in fko.h) is ECB mode to be compatible with the
+ * Crypt::CBC perl module.
*/
- ctx->mode = MODE_ECB;
+ ctx->mode = encryption_mode;
/* Generate the salt and initialization vector.
*/
* module would.
*/
size_t
-rij_encrypt(unsigned char *in, size_t in_len, const char *pass, unsigned char *out)
+rij_encrypt(unsigned char *in, size_t in_len,
+ const char *pass, unsigned char *out, int encryption_mode)
{
RIJNDAEL_context ctx;
- unsigned char plaintext[RIJNDAEL_BLOCKSIZE];
- unsigned char mixtext[RIJNDAEL_BLOCKSIZE];
- unsigned char ciphertext[RIJNDAEL_BLOCKSIZE];
int i, pad_val;
-
unsigned char *ondx = out;
- rijndael_init(&ctx, pass, NULL);
+ rijndael_init(&ctx, pass, NULL, encryption_mode);
- /* Prepend the salt...
+ /* Prepend the salt to the ciphertext...
*/
memcpy(ondx, "Salted__", 8);
ondx+=8;
memcpy(ondx, ctx.salt, 8);
ondx+=8;
- /* Now iterate of the input data and encrypt in 16-byte chunks.
+ /* Add padding to the original plaintext to ensure that it is a
+ * multiple of the Rijndael block size
*/
- while(in_len)
- {
- for(i=0; i<sizeof(plaintext); i++)
- {
- if(in_len < 1)
- break;
-
- plaintext[i] = *in++;
- in_len--;
- }
-
- pad_val = sizeof(plaintext) - i;
-
- for(; i < sizeof(plaintext); i++)
- plaintext[i] = pad_val;
+ pad_val = RIJNDAEL_BLOCKSIZE - (in_len % RIJNDAEL_BLOCKSIZE);
+ for (i = in_len; i < in_len+pad_val; i++)
+ in[i] = pad_val;
- for(i=0; i<RIJNDAEL_BLOCKSIZE; i++)
- mixtext[i] = plaintext[i] ^ ctx.iv[i];
+ block_encrypt(&ctx, in, in_len+pad_val, ondx, ctx.iv);
- block_encrypt(&ctx, mixtext, RIJNDAEL_BLOCKSIZE, ciphertext, ctx.iv);
-
- memcpy(ctx.iv, ciphertext, RIJNDAEL_BLOCKSIZE);
-
- for(i=0; i<sizeof(ciphertext); i++)
- *ondx++ = ciphertext[i];
- }
+ ondx += in_len+pad_val;
return(ondx - out);
}
/* Decrypt the given data.
*/
size_t
-rij_decrypt(unsigned char *in, size_t in_len, const char *pass, unsigned char *out)
+rij_decrypt(unsigned char *in, size_t in_len,
+ const char *pass, unsigned char *out, int encryption_mode)
{
RIJNDAEL_context ctx;
- unsigned char plaintext[RIJNDAEL_BLOCKSIZE];
- unsigned char mixtext[RIJNDAEL_BLOCKSIZE];
- unsigned char ciphertext[RIJNDAEL_BLOCKSIZE];
int i, pad_val, pad_err = 0;
unsigned char *pad_s;
unsigned char *ondx = out;
- rijndael_init(&ctx, pass, in);
+ rijndael_init(&ctx, pass, in, encryption_mode);
- /* Remove the salt from the input.
+ /* Remove the first block since it contains the salt (it was consumed
+ * by the rijndael_init() function above).
*/
- in_len -= 16;
- memmove(in, in+16, in_len);
+ in_len -= RIJNDAEL_BLOCKSIZE;
+ memmove(in, in+RIJNDAEL_BLOCKSIZE, in_len);
- while(in_len)
- {
- for(i=0; i<sizeof(ciphertext); i++)
- {
- if(in_len < 1)
- break;
-
- ciphertext[i] = *in++;
- in_len--;
- }
+ block_decrypt(&ctx, in, in_len, out, ctx.iv);
- block_decrypt(&ctx, ciphertext, RIJNDAEL_BLOCKSIZE, mixtext, ctx.iv);
-
- for(i=0; i<sizeof(ciphertext); i++)
- plaintext[i] = mixtext[i] ^ ctx.iv[i];
-
- memcpy(ctx.iv, ciphertext, RIJNDAEL_BLOCKSIZE);
-
- for(i=0; i<sizeof(plaintext); i++)
- *ondx++ = plaintext[i];
- }
+ ondx += in_len;
/* Find and remove padding.
*/
*/
#define PREDICT_ENCSIZE(x) (1+(x>>4)+(x&0xf?1:0))<<4
-size_t rij_encrypt(unsigned char *in, size_t len, const char *key, unsigned char *out);
-size_t rij_decrypt(unsigned char *in, size_t len, const char *key, unsigned char *out);
+size_t rij_encrypt(unsigned char *in, size_t len,
+ const char *key, unsigned char *out, int encryption_mode);
+size_t rij_decrypt(unsigned char *in, size_t len,
+ const char *key, unsigned char *out, int encryption_mode);
#endif /* CIPHER_FUNCS_H */
#include <time.h>
+#include "rijndael.h" /* For encryption modes */
+
#ifdef __cplusplus
extern "C" {
#endif
FKO_LAST_ENCRYPTION_TYPE /* Always leave this as the last one */
} fko_encryption_type_t;
+/* Symmetric encryption modes derived from rijndael.h
+*/
+typedef enum {
+ FKO_ENC_MODE_UNKNOWN = 0,
+ FKO_ENC_MODE_ECB = MODE_ECB,
+ FKO_ENC_MODE_CBC = MODE_CBC,
+ FKO_ENC_MODE_CFB = MODE_CFB,
+ FKO_ENC_MODE_PCBC = MODE_PCBC,
+ FKO_ENC_MODE_OFB = MODE_OFB,
+ FKO_ENC_MODE_CTR = MODE_CTR,
+ FKO_ENC_MODE_ASYMMETRIC, /* placeholder when GPG is used */
+ FKO_LAST_ENC_MODE /* Always leave this as the last one */
+} fko_encryption_mode_t;
+
/* FKO ERROR_CODES
*
* Note: If you change this list in any way, please be sure to make the
#define FKO_DEFAULT_MSG_TYPE FKO_ACCESS_MSG
#define FKO_DEFAULT_DIGEST FKO_DIGEST_SHA256
#define FKO_DEFAULT_ENCRYPTION FKO_ENCRYPTION_RIJNDAEL
+#define FKO_DEFAULT_ENC_MODE MODE_CBC
/* The context holds the global state and config options, as
* well as some intermediate results during processing. This
/* General api calls
*/
DLL_API int fko_new(fko_ctx_t *ctx);
-DLL_API int fko_new_with_data(fko_ctx_t *ctx, const char *enc_msg, const char *dec_key);
+DLL_API int fko_new_with_data(fko_ctx_t *ctx, const char *enc_msg, const char *dec_key,
+ int encryption_mode);
DLL_API void fko_destroy(fko_ctx_t ctx);
DLL_API int fko_spa_data_final(fko_ctx_t ctx, const char *enc_key);
DLL_API int fko_set_raw_spa_digest_type(fko_ctx_t ctx, const short raw_digest_type);
DLL_API int fko_set_raw_spa_digest(fko_ctx_t ctx);
DLL_API int fko_set_spa_encryption_type(fko_ctx_t ctx, const short encrypt_type);
+DLL_API int fko_set_spa_encryption_mode(fko_ctx_t ctx, const int encrypt_mode);
DLL_API int fko_set_spa_data(fko_ctx_t ctx, const char *enc_msg);
/* Data processing and misc utility functions
DLL_API int fko_get_spa_digest(fko_ctx_t ctx, char **spa_digest);
DLL_API int fko_get_raw_spa_digest(fko_ctx_t ctx, char **raw_spa_digest);
DLL_API int fko_get_spa_encryption_type(fko_ctx_t ctx, short *spa_enc_type);
+DLL_API int fko_get_spa_encryption_mode(fko_ctx_t ctx, int *spa_enc_mode);
DLL_API int fko_get_spa_data(fko_ctx_t ctx, char **spa_data);
DLL_API int fko_get_version(fko_ctx_t ctx, char **version);
/* FKO SPA user-settable message encoding types */
short digest_type;
short encryption_type;
+ int encryption_mode;
/* Computed or predefined data */
char *version;
static int
_rijndael_encrypt(fko_ctx_t ctx, const char *enc_key)
{
- char *plain;
- char *b64cipher;
- unsigned char *cipher;
+ char *plaintext;
+ char *b64ciphertext;
+ unsigned char *ciphertext;
int cipher_len;
/* Make a bucket big enough to hold the enc msg + digest (plaintext)
* and populate it appropriately.
*/
- plain = malloc(strlen(ctx->encoded_msg) + strlen(ctx->digest) + 2);
- if(plain == NULL)
+ plaintext = calloc(1, strlen(ctx->encoded_msg)
+ + strlen(ctx->digest) + RIJNDAEL_BLOCKSIZE + 2);
+
+ if(plaintext == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
- sprintf(plain, "%s:%s", ctx->encoded_msg, ctx->digest);
+ sprintf(plaintext, "%s:%s", ctx->encoded_msg, ctx->digest);
/* Make a bucket for the encrypted version and populate it.
*/
- cipher = malloc(strlen(plain) + 32); /* Plus padding for salt and Block */
- if(cipher == NULL)
+ ciphertext = calloc(1, strlen(plaintext) + 32); /* Plus padding for salt and Block */
+ if(ciphertext == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
cipher_len = rij_encrypt(
- (unsigned char*)plain, strlen(plain), (char*)enc_key, cipher
+ (unsigned char*)plaintext, strlen(plaintext), (char*)enc_key, ciphertext,
+ ctx->encryption_mode
);
/* Now make a bucket for the base64-encoded version and populate it.
*/
- b64cipher = malloc(((cipher_len / 3) * 4) + 8);
- if(b64cipher == NULL)
+ b64ciphertext = malloc(((cipher_len / 3) * 4) + 8);
+ if(b64ciphertext == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
- b64_encode(cipher, b64cipher, cipher_len);
- strip_b64_eq(b64cipher);
+ b64_encode(ciphertext, b64ciphertext, cipher_len);
+ strip_b64_eq(b64ciphertext);
- ctx->encrypted_msg = strdup(b64cipher);
+ ctx->encrypted_msg = strdup(b64ciphertext);
/* Clean-up
*/
- free(plain);
- free(cipher);
- free(b64cipher);
+ free(plaintext);
+ free(ciphertext);
+ free(b64ciphertext);
if(ctx->encrypted_msg == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
/* Decode, decrypt, and parse SPA data into the context.
*/
static int
-_rijndael_decrypt(fko_ctx_t ctx, const char *dec_key)
+_rijndael_decrypt(fko_ctx_t ctx, const char *dec_key, int encryption_mode)
{
char *tbuf;
unsigned char *ndx;
cipher_len = b64_decode(ctx->encrypted_msg, cipher);
+ /* Since we're using AES, make sure the incoming data is a multiple of
+ * the blocksize
+ */
+ if((cipher_len % RIJNDAEL_BLOCKSIZE) != 0)
+ {
+ free(cipher);
+ return(FKO_ERROR_INVALID_DATA);
+ }
+
/* Create a bucket for the plaintext data and decrypt the message
* data into it.
*/
if(ctx->encoded_msg == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
- pt_len = rij_decrypt(cipher, cipher_len, dec_key, (unsigned char*)ctx->encoded_msg);
+ pt_len = rij_decrypt(cipher, cipher_len, dec_key,
+ (unsigned char*)ctx->encoded_msg, encryption_mode);
/* Done with cipher...
*/
return(FKO_SUCCESS);
}
+/* Set the SPA encryption mode.
+*/
+int
+fko_set_spa_encryption_mode(fko_ctx_t ctx, const int encrypt_mode)
+{
+ /* Must be initialized
+ */
+ if(!CTX_INITIALIZED(ctx))
+ return(FKO_ERROR_CTX_NOT_INITIALIZED);
+
+ if(encrypt_mode < 0 || encrypt_mode >= FKO_LAST_ENC_MODE)
+ return(FKO_ERROR_INVALID_DATA);
+
+ ctx->encryption_mode = encrypt_mode;
+
+ ctx->state |= FKO_ENCRYPT_MODE_MODIFIED;
+
+ return(FKO_SUCCESS);
+}
+
+/* Return the SPA encryption mode.
+*/
+int
+fko_get_spa_encryption_mode(fko_ctx_t ctx, int *enc_mode)
+{
+ /* Must be initialized
+ */
+ if(!CTX_INITIALIZED(ctx))
+ return(FKO_ERROR_CTX_NOT_INITIALIZED);
+
+ *enc_mode = ctx->encryption_mode;
+
+ return(FKO_SUCCESS);
+}
+
/* Encrypt the encoded SPA data.
*/
int
else if(enc_type == FKO_ENCRYPTION_RIJNDAEL)
{
ctx->encryption_type = FKO_ENCRYPTION_RIJNDAEL;
- res = _rijndael_decrypt(ctx, dec_key);
+ res = _rijndael_decrypt(ctx, dec_key, ctx->encryption_mode);
}
else
return(FKO_ERROR_INVALID_DATA);
return res;
}
+ /* Default Encryption Mode (Rijndael in EBC mode for backwards
+ * compatibility - it recommended to change this to CBC mode)
+ */
+ ctx->initval = FKO_CTX_INITIALIZED;
+ res = fko_set_spa_encryption_mode(ctx, FKO_DEFAULT_ENC_MODE);
+ ctx->initval = 0;
+ if(res != FKO_SUCCESS)
+ {
+ fko_destroy(ctx);
+ return res;
+ }
+
#if HAVE_LIBGPGME
/* Set gpg signature verify on.
*/
* and parsing the provided data into the context data.
*/
int
-fko_new_with_data(fko_ctx_t *r_ctx, const char *enc_msg, const char *dec_key)
+fko_new_with_data(fko_ctx_t *r_ctx, const char *enc_msg,
+ const char *dec_key, int encryption_mode)
{
fko_ctx_t ctx;
int res = FKO_SUCCESS; /* Are we optimistic or what? */
return(FKO_ERROR_MEMORY_ALLOCATION);
}
+ /* Default Encryption Mode (Rijndael in CBC mode)
+ */
+ ctx->initval = FKO_CTX_INITIALIZED;
+ res = fko_set_spa_encryption_mode(ctx, encryption_mode);
+ ctx->initval = 0;
+ if(res != FKO_SUCCESS)
+ {
+ fko_destroy(ctx);
+ return res;
+ }
+
/* Consider it initialized here.
*/
ctx->initval = FKO_CTX_INITIALIZED;
FKO_SET_CTX_INITIALIZED(ctx);
- /* If a decryption password is provided, go ahead and decrypt and
- * decode.
+ /* If a decryption key is provided, go ahead and decrypt and decode.
*/
if(dec_key != NULL)
{
FKO_DIGEST_TYPE_MODIFIED = 1 << 12,
FKO_ENCRYPT_TYPE_MODIFIED = 1 << 13,
STATE_RESERVED_14 = 1 << 14,
- FKO_BACKWARD_COMPATIBLE = 1 << 15
+ FKO_BACKWARD_COMPATIBLE = 1 << 15,
+ FKO_ENCRYPT_MODE_MODIFIED = 1 << 16,
} fko_state_flags_t;
/* This is used in conjunction with the ctx->initial value as a means to
#include "fko_common.h"
/* Other block sizes and key lengths are possible, but in the context of
- * the ssh protocols, 256 bits is the default.
+ * the ssh protocols, 256 bits is the default.
*/
#define RIJNDAEL_BLOCKSIZE 16
#define RIJNDAEL_KEYSIZE 32
* bits). If a value other than these three is specified, the key will be
* truncated to the closest value less than the key size specified, e.g.
* specifying 7 will use only the first 6 bytes of the key given. DO NOT
- * PASS A VALUE LESS THAN 16 TO KEYSIZE!
+ * PASS A VALUE LESS THAN 16 TO KEYSIZE!
*/
void
rijndael_setup(RIJNDAEL_context *ctx, size_t keysize, const uint8_t *key);
*
* Before this function can be used, rijndael_setup() must be used in order
* to set up the key schedule required for the decryption algorithm.
- *
+ *
* This function always decrypts 16 bytes of ciphertext to 16 bytes of
* plaintext. The memory areas of the plaintext and the ciphertext can
* overlap.
return;
}
+/* Convert an encryption_mode string to its integer value.
+*/
+static int
+enc_mode_strtoint(const char *enc_mode_str)
+{
+ if(strcasecmp(enc_mode_str, "cbc") == 0)
+ return(FKO_ENC_MODE_CBC);
+ else if(strcasecmp(enc_mode_str, "ecb") == 0)
+ return(FKO_ENC_MODE_ECB);
+ else if(strcasecmp(enc_mode_str, "cfb") == 0)
+ return(FKO_ENC_MODE_CFB);
+ else if(strcasecmp(enc_mode_str, "pcbc") == 0)
+ return(-1); /* not supported yet */
+ else if(strcasecmp(enc_mode_str, "ofb") == 0)
+ return(FKO_ENC_MODE_OFB);
+ else if(strcasecmp(enc_mode_str, "ctr") == 0)
+ return(FKO_ENC_MODE_CTR);
+ else
+ return(-1);
+}
+
#if FIREWALL_IPTABLES
static void
add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *val)
* comparisons of incoming source IPs against this mask.
*/
static void
-add_source_mask(acc_stanza_t *acc, const char *ip)
+add_source_mask(fko_srv_options_t *opts, acc_stanza_t *acc, const char *ip)
{
char *ndx;
char ip_str[MAX_IPV4_STR_LEN] = {0};
log_msg(LOG_ERR,
"Fatal memory allocation error adding stanza source_list entry"
);
- exit(EXIT_FAILURE);
+ clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
/* If this is not the first entry, we walk our pointer to the
if(inet_aton(ip_str, &in) == 0)
{
log_msg(LOG_ERR,
- "Error parsing IP to int for: %s", ip_str
+ "Fatal error parsing IP to int for: %s", ip_str
);
-
- free(new_sle);
- new_sle = NULL;
-
- return;
+ clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
/* Store our mask converted from CIDR to a 32-bit value.
/* Expand the access SOURCE string to a list of masks.
*/
void
-expand_acc_source(acc_stanza_t *acc)
+expand_acc_source(fko_srv_options_t *opts, acc_stanza_t *acc)
{
char *ndx, *start;
char buf[32];
start++;
strlcpy(buf, start, (ndx-start)+1);
- add_source_mask(acc, buf);
+ add_source_mask(opts, acc, buf);
start = ndx+1;
}
}
start++;
strlcpy(buf, start, (ndx-start)+1);
- add_source_mask(acc, buf);
+ add_source_mask(opts, acc, buf);
}
static int
{
/* Expand the source string to 32-bit integer masks foreach entry.
*/
- expand_acc_source(acc);
+ expand_acc_source(opts, acc);
/* Now expand the open_ports string.
*/
/* set default gpg keyring path if necessary
*/
- if(acc->gpg_decrypt_pw != NULL && acc->gpg_home_dir == NULL)
- add_acc_string(&(acc->gpg_home_dir), opts->config[CONF_GPG_HOME_DIR]);
+ if(acc->gpg_decrypt_pw != NULL)
+ {
+ if(acc->gpg_home_dir == NULL)
+ add_acc_string(&(acc->gpg_home_dir), opts->config[CONF_GPG_HOME_DIR]);
+ }
+
+ if (acc->encryption_mode == FKO_ENC_MODE_UNKNOWN)
+ acc->encryption_mode = FKO_DEFAULT_ENC_MODE;
acc = acc->next;
}
{
add_acc_int(&(curr_acc->fw_access_timeout), val);
}
+ else if(CONF_VAR_IS(var, "ENCRYPTION_MODE"))
+ {
+ if((curr_acc->encryption_mode = enc_mode_strtoint(val)) < 0)
+ {
+ fprintf(stderr,
+ "[*] Unrecognized ENCRYPTION_MODE '%s', use {cbc,ecb}\n",
+ val);
+ clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
+ }
+ }
else if(CONF_VAR_IS(var, "ENABLE_CMD_EXEC"))
{
add_acc_bool(&(curr_acc->enable_cmd_exec), val);
expand_acc_ent_lists(opts);
/* Make sure default values are set where needed.
- * a default value.
*/
set_acc_defaults(opts);
acc_string_list_t *gpg_remote_id_list;
time_t access_expire_time;
int expired;
+ int encryption_mode;
unsigned char force_nat;
char *force_nat_ip;
char *force_nat_proto;
if(enc_type == FKO_ENCRYPTION_RIJNDAEL)
{
if(acc->key != NULL)
- res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key);
+ res = fko_new_with_data(&ctx,
+ (char *)spa_pkt->packet_data, acc->key, acc->encryption_mode);
else
{
log_msg(LOG_ERR,
else if(enc_type == FKO_ENCRYPTION_GPG)
{
/* For GPG we create the new context without decrypting on the fly
- * so we can set some GPG parameters first.
+ * so we can set some GPG parameters first.
*/
if(acc->gpg_decrypt_pw != NULL)
{
- res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL);
+ res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL,
+ acc->encryption_mode);
if(res != FKO_SUCCESS)
{
log_msg(LOG_WARNING,
--- /dev/null
+SOURCE: ANY;
+KEY: fwknoptest;
+FW_ACCESS_TIMEOUT: 3;
+ENCRYPTION_MODE: CFB;
--- /dev/null
+SOURCE: ANY;
+KEY: fwknoptest;
+FW_ACCESS_TIMEOUT: 3;
+ENCRYPTION_MODE: CTR;
--- /dev/null
+SOURCE: ANY;
+KEY: fwknoptest;
+FW_ACCESS_TIMEOUT: 3;
+ENCRYPTION_MODE: ECB;
--- /dev/null
+SOURCE :ANY;
+KEY: fwknoptest;
+FW_ACCESS_TIMEOUT: 3;
+
+SOURCE: ANY;
+KEY: fwknoptest2;
+FW_ACCESS_TIMEOUT: 3;
--- /dev/null
+SOURCE: ANY;
+KEY: fwknoptest;
+FW_ACCESS_TIMEOUT: 3;
+ENCRYPTION_MODE: OFB;
--- /dev/null
+[+] TEST: [profile coverage] gcov profile coverage
+CMD: grep "called 0 returned" ../client/*.gcov
+../client/config_init.c.gcov:function digest_strtoint called 0 returned 0% blocks executed 0%
+../client/config_init.c.gcov:function proto_strtoint called 0 returned 0% blocks executed 0%
+../client/config_init.c.gcov:function create_fwknoprc called 0 returned 0% blocks executed 0%
+../client/config_init.c.gcov:function parse_rc_param called 0 returned 0% blocks executed 0%
+../client/fwknop.c.gcov:function show_last_command called 0 returned 0% blocks executed 0%
+../client/fwknop.c.gcov:function run_last_args called 0 returned 0% blocks executed 0%
+../client/getpasswd.c.gcov:function getpasswd called 0 returned 0% blocks executed 0%
+../client/http_resolve_host.c.gcov:function parse_url called 0 returned 0% blocks executed 0%
+../client/http_resolve_host.c.gcov:function resolve_ip_http called 0 returned 0% blocks executed 0%
+../client/spa_comm.c.gcov:function chksum called 0 returned 0% blocks executed 0%
+../client/spa_comm.c.gcov:function send_spa_packet_tcp_raw called 0 returned 0% blocks executed 0%
+../client/spa_comm.c.gcov:function send_spa_packet_icmp called 0 returned 0% blocks executed 0%
+../client/spa_comm.c.gcov:function send_spa_packet_http called 0 returned 0% blocks executed 0%
+../client/spa_comm.c.gcov:function write_spa_packet_data called 0 returned 0% blocks executed 0%
+../client/utils.c.gcov:function hex_dump called 0 returned 0% blocks executed 0%
+CMD: grep "called 0 returned" ../server/*.gcov
+../server/access.c.gcov:function acc_check_gpg_remote_id called 0 returned 0% blocks executed 0%
+../server/config_init.c.gcov:function config_entry_index called 0 returned 0% blocks executed 0%
+../server/extcmd.c.gcov:function run_extcmd_as called 0 returned 0% blocks executed 0%
+../server/fwknopd.c.gcov:function make_dir_path called 0 returned 0% blocks executed 0%
+../server/fwknopd.c.gcov:function daemonize_process called 0 returned 0% blocks executed 0%
+../server/log_msg.c.gcov:function set_log_facility called 0 returned 0% blocks executed 0%
+../server/replay_cache.c.gcov:function rotate_digest_cache_file called 0 returned 0% blocks executed 0%
+../server/tcp_server.c.gcov:function run_tcp_server called 0 returned 0% blocks executed 0%
+../server/utils.c.gcov:function hex_dump called 0 returned 0% blocks executed 0%
+CMD: grep "called 0 returned" ../lib/.libs/*.gcov
+../lib/.libs/digest.c.gcov:function digest_to_hex called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function md5_hex called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function md5_base64 called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function sha1 called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function sha1_hex called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function sha1_base64 called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function sha256_hex called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function sha384 called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function sha384_hex called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function sha384_base64 called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function sha512 called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function sha512_hex called 0 returned 0% blocks executed 0%
+../lib/.libs/digest.c.gcov:function sha512_base64 called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_spa_encryption_type called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_set_gpg_exe called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_gpg_exe called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_gpg_recipient called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_gpg_signer called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_gpg_home_dir called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_gpg_signature_verify called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_gpg_ignore_verify_error called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_gpg_signature_fpr called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_gpg_signature_id called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_gpg_signature_summary called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_get_gpg_signature_status called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_gpg_signature_id_match called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_encryption.c.gcov:function fko_gpg_signature_fpr_match called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_funcs.c.gcov:function fko_set_spa_data called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_message.c.gcov:function validate_cmd_msg called 0 returned 0% blocks executed 0%
+../lib/.libs/fko_server_auth.c.gcov:function fko_set_spa_server_auth called 0 returned 0% blocks executed 0%
+../lib/.libs/gpgme.h.gcov:function gpgme_err_code called 0 returned 0% blocks executed 0%
+../lib/.libs/gpgme_funcs.c.gcov:function process_sigs called 0 returned 0% blocks executed 0%
+../lib/.libs/sha1.c.gcov:function sha1_transform called 0 returned 0% blocks executed 0%
+../lib/.libs/sha1.c.gcov:function sha1_init called 0 returned 0% blocks executed 0%
+../lib/.libs/sha1.c.gcov:function sha1_update called 0 returned 0% blocks executed 0%
+../lib/.libs/sha1.c.gcov:function sha1_transform_and_copy called 0 returned 0% blocks executed 0%
+../lib/.libs/sha1.c.gcov:function sha1_final called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA256_End called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA256_Data called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA512_Init called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA512_Transform called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA512_Update called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA512_Last called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA512_Final called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA512_End called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA512_Data called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA384_Init called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA384_Update called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA384_Final called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA384_End called 0 returned 0% blocks executed 0%
+../lib/.libs/sha2.c.gcov:function SHA384_Data called 0 returned 0% blocks executed 0%
use File::Path;
use IO::Socket;
use Data::Dumper;
+use Cwd;
use Getopt::Long 'GetOptions';
use strict;
my $nat_conf = "$conf_dir/nat_fwknopd.conf";
my $default_conf = "$conf_dir/default_fwknopd.conf";
my $default_access_conf = "$conf_dir/default_access.conf";
+my $ecb_mode_access_conf = "$conf_dir/ecb_mode_access.conf";
+my $ctr_mode_access_conf = "$conf_dir/ctr_mode_access.conf";
+my $cfb_mode_access_conf = "$conf_dir/cfb_mode_access.conf";
+my $ofb_mode_access_conf = "$conf_dir/ofb_mode_access.conf";
my $expired_access_conf = "$conf_dir/expired_stanza_access.conf";
my $future_expired_access_conf = "$conf_dir/future_expired_stanza_access.conf";
my $expired_epoch_access_conf = "$conf_dir/expired_epoch_stanza_access.conf";
my $invalid_expire_access_conf = "$conf_dir/invalid_expire_access.conf";
+my $invalid_source_access_conf = "$conf_dir/invalid_source_access.conf";
my $force_nat_access_conf = "$conf_dir/force_nat_access.conf";
my $dual_key_usage_access_conf = "$conf_dir/dual_key_usage_access.conf";
my $gpg_access_conf = "$conf_dir/gpg_access.conf";
my $saved_last_results = 0;
my $diff_mode = 0;
my $enable_recompilation_warnings_check = 0;
+my $enable_profile_coverage_check = 0;
my $sudo_path = '';
+my $gcov_path = '';
my $platform = '';
my $help = 0;
my $YES = 1;
'test-exclude=s' => \$test_exclude,
'exclude=s' => \$test_exclude, ### synonym
'enable-recompile-check' => \$enable_recompilation_warnings_check,
+ 'enable-profile-coverage-check' => \$enable_profile_coverage_check,
'List-mode' => \$list_mode,
'enable-valgrind' => \$use_valgrind,
'valgrind-path=s' => \$valgrindCmd,
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
+ 'detail' => 'invalid SOURCE (tcp/22 ssh)',
+ 'err_msg' => 'SPA packet accepted',
+ 'function' => \&spa_cycle,
+ 'cmdline' => $default_client_args,
+ 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+ "$fwknopdCmd -c $default_conf -a $invalid_source_access_conf " .
+ "-d $default_digest_file -p $default_pid_file $intf_str",
+ 'server_positive_output_matches' => [qr/Fatal\serror\sparsing\sIP\sto\sint/],
+ 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'Rijndael SPA',
+ 'subcategory' => 'client+server',
'detail' => 'expired stanza (tcp/22 ssh)',
'err_msg' => 'SPA packet accepted',
'function' => \&spa_cycle,
'server_conf' => $nat_conf,
'fatal' => $NO
},
+ {
+ 'category' => 'Rijndael SPA',
+ 'subcategory' => 'client+server',
+ 'detail' => 'ECB mode (tcp/22 ssh)',
+ 'err_msg' => 'could not complete SPA cycle',
+ 'function' => \&spa_cycle,
+ 'cmdline' => "$default_client_args -M ecb",
+ 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+ "$fwknopdCmd -c $default_conf -a $ecb_mode_access_conf " .
+ "-d $default_digest_file -p $default_pid_file $intf_str",
+ 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
+ 'fw_rule_created' => $NEW_RULE_REQUIRED,
+ 'fw_rule_removed' => $NEW_RULE_REMOVED,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'Rijndael SPA',
+ 'subcategory' => 'client+server',
+ 'detail' => 'CFB mode (tcp/22 ssh)',
+ 'err_msg' => 'could not complete SPA cycle',
+ 'function' => \&spa_cycle,
+ 'cmdline' => "$default_client_args -M cfb",
+ 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+ "$fwknopdCmd -c $default_conf -a $cfb_mode_access_conf " .
+ "-d $default_digest_file -p $default_pid_file $intf_str",
+ 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
+ 'fw_rule_created' => $NEW_RULE_REQUIRED,
+ 'fw_rule_removed' => $NEW_RULE_REMOVED,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'Rijndael SPA',
+ 'subcategory' => 'client+server',
+ 'detail' => 'CTR mode (tcp/22 ssh)',
+ 'err_msg' => 'could not complete SPA cycle',
+ 'function' => \&spa_cycle,
+ 'cmdline' => "$default_client_args -M ctr",
+ 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+ "$fwknopdCmd -c $default_conf -a $ctr_mode_access_conf " .
+ "-d $default_digest_file -p $default_pid_file $intf_str",
+ 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
+ 'fw_rule_created' => $NEW_RULE_REQUIRED,
+ 'fw_rule_removed' => $NEW_RULE_REMOVED,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'Rijndael SPA',
+ 'subcategory' => 'client+server',
+ 'detail' => 'OFB mode (tcp/22 ssh)',
+ 'err_msg' => 'could not complete SPA cycle',
+ 'function' => \&spa_cycle,
+ 'cmdline' => "$default_client_args -M ofb",
+ 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+ "$fwknopdCmd -c $default_conf -a $ofb_mode_access_conf " .
+ "-d $default_digest_file -p $default_pid_file $intf_str",
+ 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
+ 'fw_rule_created' => $NEW_RULE_REQUIRED,
+ 'fw_rule_removed' => $NEW_RULE_REMOVED,
+ 'fatal' => $NO
+ },
+
+ {
+ 'category' => 'Rijndael SPA',
+ 'subcategory' => 'client+server',
+ 'detail' => 'mode mismatch (tcp/22 ssh)',
+ 'err_msg' => 'server accepted mismatch enc mode',
+ 'function' => \&spa_cycle,
+ 'cmdline' => "$default_client_args -M ecb",
+ 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+ "$fwknopdCmd -c $default_conf -a $default_access_conf " .
+ "-d $default_digest_file -p $default_pid_file $intf_str",
+ 'server_positive_output_matches' => [qr/Decryption\sfailed/i],
+ 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
+ 'fatal' => $NO
+ },
{
'category' => 'Rijndael SPA',
'function' => \&digest_cache_structure,
'fatal' => $NO
},
+
+ {
+ 'category' => 'profile coverage',
+ 'detail' => 'gcov profile coverage',
+ 'err_msg' => 'profile coverage failed',
+ 'function' => \&profile_coverage,
+ 'fatal' => $NO
+ },
);
if ($use_valgrind) {
### look for compilation warnings - something like:
### warning: ‘test’ is used uninitialized in this function
- return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/], $current_test_file);
+ return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/],
+ $current_test_file);
### the new binaries should exist
unless (-e $fwknopCmd and -x $fwknopCmd) {
return 1;
}
+sub profile_coverage() {
+
+ ### check for any *.gcno files - if they don't exist, then fwknop was
+ ### not compiled with profile support
+ unless (glob('../client/*.gcno') and glob('../server/*.gcno')) {
+ &write_test_file("[-] ../client/*.gcno and " .
+ "../server/*.gcno files do not exist.\n", $current_test_file);
+ return 0;
+ }
+
+ my $curr_dir = getcwd() or die $!;
+
+ ### gcov -b ../client/*.gcno
+ for my $dir ('../client', '../server', '../lib/.libs') {
+ next unless -d $dir;
+ chdir $dir or die $!;
+ system "$gcov_path -b -u *.gcno > /dev/null 2>&1";
+ chdir $curr_dir or die $!;
+
+ &run_cmd(qq|grep "called 0 returned" $dir/*.gcov|,
+ $cmd_out_tmp, $current_test_file);
+ }
+
+ return 1;
+}
+
sub binary_exists() {
my $test_hr = shift;
return 0 unless $test_hr->{'binary'};
push @tests_to_exclude, 'recompilation';
}
+ unless ($enable_profile_coverage_check) {
+ push @tests_to_exclude, 'profile coverage';
+ }
+
$sudo_path = &find_command('sudo');
unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
push @tests_to_exclude, 'recompilation';
}
+ $gcov_path = &find_command('gcov');
+
+ if ($gcov_path) {
+ if ($enable_profile_coverage_check) {
+ for my $extension ('*.gcov', '*.gcda') {
+ ### remove profile output from any previous run
+ system qq{find .. -name $extension | xargs rm 2> /dev/null};
+ }
+ }
+ } else {
+ push @tests_to_exclude, 'profile coverage';
+ }
+
open UNAME, "uname |" or die "[*] Could not execute uname: $!";
while (<UNAME>) {
if (/linux/i) {