*/
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 },
#include "cmd_opts.h"
#include "utils.h"
-/* Convert a digest_type string to its intger value.
+/* 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(FKO_ENC_MODE_PCBC);
+ 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("\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 */
: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. The default (for now) is
+ ECB mode in order to remain backwards compatible with pre-2.0 versions of
+ fwknop which relied on the Crypt::CBC perl module. This will be changed
+ in an upcoming version of fwknop to use the more secure CBC mode. In the
+ meantime, it is recommend to use this option to specify CBC mode (use the
+ string "CBC" as the argument), and then also use the ENCRYPTION_MODE
+ variable in the 'access.conf' file on the server.
+
*-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 (for now) is
+ ECB mode in order to remain backwards compatible with pre-2.0 versions of
+ fwknop which relied on the Crypt::CBC perl module. This will be changed
+ in an upcoming version of fwknop to use the more secure CBC mode. In the
+ meantime, it is recommend to use this option to specify CBC mode (use the
+ string "CBC" as the argument), and then also use '--encryption-mode'
+ command line argument on the *fwknop* client.
+
*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
/* 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.
*/
- 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 *ondx = out;
- rijndael_init(&ctx, pass, NULL);
+ rijndael_init(&ctx, pass, NULL, encryption_mode);
/* Prepend the salt...
*/
/* 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 *pad_s;
unsigned char *ondx = out;
- rijndael_init(&ctx, pass, in);
+ rijndael_init(&ctx, pass, in, encryption_mode);
/* Remove the salt from the input.
*/
*/
#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_ECB
/* 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_spa_digest_type(fko_ctx_t ctx, const short digest_type);
DLL_API int fko_set_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_type(fko_ctx_t ctx, short *spa_digest_type);
DLL_API int fko_get_spa_digest(fko_ctx_t ctx, char **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;
return(FKO_ERROR_MEMORY_ALLOCATION);
cipher_len = rij_encrypt(
- (unsigned char*)plain, strlen(plain), (char*)enc_key, cipher
+ (unsigned char*)plain, strlen(plain), (char*)enc_key, cipher,
+ ctx->encryption_mode
);
/* Now make a bucket for the base64-encoded version and populate it.
/* 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;
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
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(FKO_ENC_MODE_PCBC);
+ 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)
/* 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)
+ {
+ acc->encryption_mode = FKO_ENC_MODE_ASYMMETRIC;
+ 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,
*/
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,