diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..44778bc3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,77 @@ +# built application files +*.apk +*.ap_ + +# files for the dex VM +*.dex + +# Java class files +*.class + +# generated files +bin/ +gen/ + +# Local configuration file (sdk path, etc) +local.properties + +# Eclipse project files +.classpath +.project + +# Proguard folder generated by Eclipse +proguard/ + +# Intellij project files +*.iml +*.ipr +*.iws +.idea/ + +# CMake files +CMakeCache.txt +CMakeFiles +Makefile +cmake_install.cmake +install_manifest.txt + +# C, C++ +*.o +*.lo + +#project generated files +autom4te.cache/ +*.m4 +client/.deps/ +client/Makefile.in +common/libfko_util.a +common/Makefile.in +config.h +config.h.in +config.log +config.status +config/ +configure +doc/libfko.info +doc/Makefile.in +doc/stamp-vti +doc/version.texi +lib/libfko.la +lib/.deps/ +lib/.dirstamp +lib/.libs/ +lib/Makefile.in +libtool +Makefile.in +server/.deps/ +server/.libs/ +server/fwknopd +server/fwknopd.8 +server/Makefile.in +stamp-h1 + +android/project/libs +android/project/obj +android/project/jni/fwknop/fko.h +android/project/jni/libfwknop/*.h +android/project/jni/libfwknop/*.c diff --git a/CREDITS b/CREDITS index 2a9c586d..74687201 100644 --- a/CREDITS +++ b/CREDITS @@ -175,6 +175,9 @@ Gerry Reno - Provided guidance on Android client issues along with testing candidate patches to update various things - this work is being tracked in the android4.4_support branch. + - Implemented support for firewalld in the fwknopd daemon running on RHEL 7 + and CentOS 7 systems. This is a major addition to handle yet another + firewall architecture. Tim Heckman - Homebrew fwknop package maintainer for Mac OS X systems. diff --git a/ChangeLog b/ChangeLog index 4a4d0d5f..c486fb86 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,11 @@ fwknop-2.6.4 (09//2014): + - (Gerry Reno) Added support for firewalld to the fwknopd daemon on RHEL 7 + CentOS 7. This is implemented using the current firewalld '--direct + --passthrough' capability which accepts raw iptables commands. More + information on firewalld can be found here: + + https://fedoraproject.org/wiki/FirewallD + - (Bill Stubbs) submitted a patch to fix a bug where fwknopd could not handle Ethernet frames that include the Frame Check Sequence (FCS) header. This header is four bytes long, and is placed at the end of each diff --git a/README.md b/README.md index 32256f03..ea67305d 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ default-drop filtering stance. The main application of SPA is to use a firewall to drop all attempts to connect to services such as SSH in order to make the exploitation of vulnerabilities (both 0-day and unpatched code) more difficult. Because there are no open ports, any service that is concealed by SPA naturally -cannot be scanned for with Nmap. The fwknop project supports three different -firewalls: iptables on Linux systems, pf on OpenBSD, and ipfw on FreeBSD and -Mac OS X. +cannot be scanned for with Nmap. The fwknop project supports four different +firewalls: firewalld and iptables on Linux systems, pf on OpenBSD, and ipfw on +FreeBSD and Mac OS X. SPA is essentially next generation Port Knocking (PK), but solves many of the limitations exhibited by PK while retaining its core benefits. PK limitations @@ -103,6 +103,9 @@ the `INSTALL` file for the general basics on using autoconf. --with-gpgme-prefix=PFX prefix where GPGME is installed (optional) --with-gpg=/path/to/gpg Specify path to the gpg executable that gpgme will use [default=check path] + --with-firewalld=/path/to/firewalld + Specify path to the firewalld executable + [default=check path] --with-iptables=/path/to/iptables Specify path to the iptables executable [default=check path] @@ -115,6 +118,10 @@ the `INSTALL` file for the general basics on using autoconf. --with-ipf=/path/to/ipf Specify path to the ipf executable [default=check path] + Examples: + + ./configure --disable-client --with-firewalld=/bin/firewall-cmd + ./configure --disable-client --with-iptables=/sbin/iptables --with-firewalld=no ## Notes ### Migrating from the Perl version of fwknop diff --git a/android/project/src/com/max2idea/android/fwknop/Fwknop.java b/android/project/src/com/max2idea/android/fwknop/Fwknop.java index cdc45a3a..f099f33c 100644 --- a/android/project/src/com/max2idea/android/fwknop/Fwknop.java +++ b/android/project/src/com/max2idea/android/fwknop/Fwknop.java @@ -378,9 +378,12 @@ public class Fwknop extends Activity { this.mCheck.setChecked(prefs.getBoolean("app_start", false)); this.mPasswd = (EditText) findViewById(R.id.passwd); + this.mPasswd.setText(prefs.getString("passwd_str", "")); + this.mOutput = (TextView) findViewById(R.id.output); this.mHmac = (EditText) findViewById(R.id.hmac); + this.mHmac.setText(prefs.getString("hmac_str", "")); mUnlock = (ImageButton) findViewById(R.id.unlock); mUnlock.setOnClickListener(new OnClickListener() { diff --git a/client/config_init.c b/client/config_init.c index d59184fb..a6f6a804 100755 --- a/client/config_init.c +++ b/client/config_init.c @@ -2412,7 +2412,7 @@ usage(void) " '$HOME/.fwknoprc' file to provide some of all\n" " of the configuration parameters.\n" " If more arguments are set through the command\n" - " line, the configuration is updated accordingly\n" + " line, the configuration is updated accordingly.\n" " -A, --access Provide a list of ports/protocols to open\n" " on the server (e.g. 'tcp/22').\n" " -a, --allow-ip Specify IP address to allow within the SPA\n" @@ -2458,6 +2458,7 @@ usage(void) " -u, --user-agent Set the HTTP User-Agent for resolving the\n" " external IP via -R, or for sending SPA\n" " packets over HTTP.\n" + " -w, --wget-cmd Manually set the path to wget in -R mode.\n" " -H, --http-proxy Specify an HTTP proxy host through which the\n" " SPA packet will be sent. The port can also be\n" " specified here by following the host/ip with\n" @@ -2474,9 +2475,9 @@ usage(void) " -K, --key-gen-file Write generated Rijndael + HMAC keys to a\n" " file\n" " --key-rijndael Specify the Rijndael key. Since the password is\n" - " visible to utilities (like 'ps' under Unix) this\n" - " form should only be used where security is not\n" - " important.\n" + " visible to utilities (like 'ps' under Unix)\n" + " this form should only be used where security is\n" + " not important.\n" " --key-base64-rijndael Specify the base64 encoded Rijndael key. Since\n" " the password is visible to utilities (like 'ps'\n" " under Unix) this form should only be used where\n" diff --git a/common/common.h b/common/common.h index b73f7f18..5759be21 100755 --- a/common/common.h +++ b/common/common.h @@ -97,6 +97,8 @@ #define fdopen _fdopen #define close _close #define write _write + #define popen _popen + #define pclose _pclose #define O_WRONLY _O_WRONLY #define O_RDONLY _O_RDONLY #define O_RDWR _O_RDWR diff --git a/configure.ac b/configure.ac index 32875e2e..41d90111 100755 --- a/configure.ac +++ b/configure.ac @@ -493,6 +493,24 @@ AS_IF([test "$want_server" = yes], [ AM_CONDITIONAL([USE_NDBM], [test x$use_ndbm = xyes]) AM_CONDITIONAL([CONFIG_FILE_CACHE], [test x$want_file_cache = xyes]) +dnl Check for firewalld +dnl + AC_ARG_WITH([firewall-cmd], + [AS_HELP_STRING([--with-firewall-cmd=/path/to/firewall-cmd], + [Specify path to the firewall-cmd executable @<:@default=check path@:>@])], + [ + AS_IF([ test "x$withval" = xno ], [], + AS_IF([ test "x$withval" = x -o "x$withval" = xyes ], + [AC_MSG_ERROR([--with-firewall-cmd requires an argument specifying a path to firewall-cmd])], + [ FORCE_FIREWALLD_EXE=$withval ] + ) + ) + ], + [ + AC_PATH_PROG(FIREWALLD_EXE, [firewall-cmd], [], [$APP_PATH]) + ] + ) + dnl Check for iptables dnl AC_ARG_WITH([iptables], @@ -567,6 +585,9 @@ dnl dnl If a firewall was forced. set the appropriate _EXE var and clear the others. dnl + AS_IF([test "x$FORCE_FIREWALLD_EXE" != x], [ + FIREWALLD_EXE="$FORCE_FIREWALLD_EXE" + ],[ AS_IF([test "x$FORCE_IPTABLES_EXE" != x], [ IPTABLES_EXE="$FORCE_IPTABLES_EXE" ],[ @@ -588,12 +609,19 @@ dnl ] ] ] - )))) + ] + ))))) dnl Determine which firewall exe we use (if we have one). -dnl If iptables was found or specified, it wins, then we fallback to ipfw, -dnl then pf, and otherwise we try ipf. +dnl If firewalld was found or specified, it wins, then we fallback to iptables, +dnl then ipfw, pf, and otherwise we try ipf. dnl + AS_IF([test "x$FIREWALLD_EXE" != x], [ + FW_DEF="FW_FIREWALLD" + FIREWALL_TYPE="firewalld" + FIREWALL_EXE=$FIREWALLD_EXE + AC_DEFINE_UNQUOTED([FIREWALL_FIREWALLD], [1], [The firewall type: firewalld.]) + ],[ AS_IF([test "x$IPTABLES_EXE" != x], [ FW_DEF="FW_IPTABLES" FIREWALL_TYPE="iptables" @@ -621,7 +649,8 @@ dnl ] ] ] - )))) + ] + ))))) AC_DEFINE_UNQUOTED([FIREWALL_EXE], ["$FIREWALL_EXE"], [Path to firewall command executable (it should match the firewall type).]) diff --git a/doc/fwknopd.man.asciidoc b/doc/fwknopd.man.asciidoc index b9eba9f1..f9d57010 100644 --- a/doc/fwknopd.man.asciidoc +++ b/doc/fwknopd.man.asciidoc @@ -229,17 +229,6 @@ See the '@sysconfdir@/fwknop/fwknopd.conf'' file for the full list and correspon synchronization with the *fwknopd* server system (NTP is good). The default age is 120 seconds (two minutes). -*ACCESS_EXPIRE* '':: - Defines an expiration date for the access stanza in MM/DD/YYYY format. - All SPA packets that match an expired stanza will be ignored. This - parameter is optional. - -*ACCESS_EXPIRE_EPOCH* '':: - Defines an expiration date for the access stanza as the epoch time, and is - useful if a more accurate expiration time needs to be given than the day - resolution offered by the ACCESS_EXPIRE variable above. All SPA packets - that match an expired stanza will be ignored. This parameter is optional. - *ENABLE_DIGEST_PERSISTENCE* '':: Track digest sums associated with previous SPA packets processed by *fwknopd*. This allows digest sums to remain persistent across @@ -303,7 +292,7 @@ See the '@sysconfdir@/fwknop/fwknopd.conf'' file for the full list and correspon the '$HOME/.gnupg' directory of the user running *fwknopd* (most likely root). -GPG_EXE* '':: +*GPG_EXE* '':: Specify the path to GPG, and defaults to '/usr/bin/gpg' if not set. *LOCALE* '':: @@ -443,6 +432,17 @@ directive starts a new stanza. optional field, and if not specified then *fwknopd* defaults to using SHA256 if the access stanza requires an HMAC. +*ACCESS_EXPIRE* '':: + Defines an expiration date for the access stanza in MM/DD/YYYY format. + All SPA packets that match an expired stanza will be ignored. This + parameter is optional. + +*ACCESS_EXPIRE_EPOCH* '':: + Defines an expiration date for the access stanza as the epoch time, and is + useful if a more accurate expiration time needs to be given than the day + resolution offered by the ACCESS_EXPIRE variable above. All SPA packets + that match an expired stanza will be ignored. This parameter is optional. + *ENABLE_CMD_EXEC* '':: This instructs *fwknopd* to accept complete commands that are contained within an authorization packet. Any such command will be executed on @@ -468,6 +468,9 @@ directive starts a new stanza. client behind a NAT) or the client must know the external IP and set it via the *-a* argument. +*REQUIRE_SOURCE_ADDRESS* '':: + Synonym for ``REQUIRE_SOURCE_ADDRESS''. + *FORCE_NAT* ' ':: For any valid SPA packet, force the requested connection to be NAT'd through to the specified (usually internal) IP and port value. This is @@ -522,10 +525,14 @@ directive starts a new stanza. and/or pinentry to collect a passphrase. *GPG_REQUIRE_SIG* '':: - With this setting set to 'Y', fwknopd check all GPG-encrypted SPA + With this setting set to 'Y', fwknopd check all GPG-encrypted SPA messages for a signature (signed by the sender's key). If the incoming message is not signed, the decryption process will fail. If not set, the - default is 'N'. + default is 'Y'. + +*GPG_DISABLE_SIG* '':: + Disable signature verification for incoming SPA messages. This is not a + recommended setting, and the default is 'N'. *GPG_IGNORE_SIG_VERIFY_ERROR* '':: Setting this will allow fwknopd to accept incoming GPG-encrypted packets @@ -538,9 +545,16 @@ directive starts a new stanza. any incoming SPA message that has been encrypted with the *fwknopd* server key. This ensures that the verification of the remote user is accomplished via a strong cryptographic mechanism. - This setting only applies if the ``GPG_REQUIRE_SIG'' is set to 'Y'. + Signature verification is enabled by default, and can only be disabled + if ``GPG_DISABLE_SIG'' is set to 'Y' (not a recommended setting). Separate multiple entries with a comma. +*GPG_FINGERPRINT_ID* '':: + Specify a set of full-length GnuPG key fingerprints instead of the shorter + key identifiers set with the ``GPG_REMOTE_ID'' variable. Here is an + example fingerprint for one of the fwknop test suite keys: + '00CC95F05BC146B6AC4038C9E36F443C6A3FAD56'. + *GPG_HOME_DIR* '':: Define the path to the GnuPG directory to be used by the *fwknopd* server. If this keyword is not specified within '@sysconfdir@/fwknop/access.conf' diff --git a/fwknop.spec b/fwknop.spec index aa6084a7..8080759a 100644 --- a/fwknop.spec +++ b/fwknop.spec @@ -50,11 +50,11 @@ Requires: libfko => 2.0.3, libpcap, iptables %description Fwknop implements an authorization scheme known as Single Packet Authorization -(SPA) for Linux systems running iptables. This mechanism requires only a -single encrypted and non-replayed packet to communicate various pieces of -information including desired access through an iptables policy. The main -application of this program is to use iptables in a default-drop stance to -protect services such as SSH with an additional layer of security in order +(SPA) for Linux systems running firewalld or iptables. This mechanism requires +only a single encrypted and non-replayed packet to communicate various pieces of +information including desired access through a firewalld or iptables policy. The +main application of this program is to use firewalld or iptables in a default-drop +stance to protect services such as SSH with an additional layer of security in order to make the exploitation of vulnerabilities (both 0-day and unpatched code) much more difficult. diff --git a/server/Makefile.am b/server/Makefile.am index c27e3387..229e25cf 100755 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -8,6 +8,7 @@ fwknopd_SOURCES = fwknopd.c fwknopd.h config_init.c config_init.h \ access.c access.h fwknopd_errors.c fwknopd_errors.h \ tcp_server.c tcp_server.h extcmd.c extcmd.h \ fw_util.c fw_util.h fw_util_ipf.c fw_util_ipf.h \ + fw_util_firewalld.c fw_util_firewalld.h \ fw_util_iptables.c fw_util_iptables.h \ fw_util_ipfw.c fw_util_ipfw.h \ fw_util_pf.c fw_util_pf.h cmd_opts.h \ diff --git a/server/access.c b/server/access.c index ae3826a5..318e8eb4 100755 --- a/server/access.c +++ b/server/access.c @@ -170,7 +170,7 @@ add_acc_expire_time_epoch(fko_srv_options_t *opts, time_t *access_expire_time, c return 1; } -#if FIREWALL_IPTABLES +#if defined(FIREWALL_FIREWALLD) || defined(FIREWALL_IPTABLES) static int add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *val) { @@ -1590,7 +1590,20 @@ parse_access_file(fko_srv_options_t *opts) } else if(CONF_VAR_IS(var, "FORCE_NAT")) { -#if FIREWALL_IPTABLES +#if FIREWALL_FIREWALLD + if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_FORWARDING], "Y", 1) !=0 ) + { + log_msg(LOG_ERR, + "[*] FORCE_NAT requires ENABLE_FIREWD_FORWARDING to be enabled in fwknopd.conf"); + fclose(file_ptr); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + if(add_acc_force_nat(opts, curr_acc, val) != SUCCESS) + { + fclose(file_ptr); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } +#elif FIREWALL_IPTABLES if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1) !=0 ) { log_msg(LOG_ERR, @@ -1612,7 +1625,20 @@ parse_access_file(fko_srv_options_t *opts) } else if(CONF_VAR_IS(var, "FORCE_SNAT")) { -#if FIREWALL_IPTABLES +#if FIREWALL_FIREWALLD + if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_FORWARDING], "Y", 1) !=0 ) + { + log_msg(LOG_ERR, + "[*] FORCE_SNAT requires ENABLE_FIREWD_FORWARDING to be enabled in fwknopd.conf"); + fclose(file_ptr); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + if(add_acc_force_snat(opts, curr_acc, val) != SUCCESS) + { + fclose(file_ptr); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } +#elif FIREWALL_IPTABLES if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1) !=0 ) { log_msg(LOG_ERR, diff --git a/server/cmd_opts.h b/server/cmd_opts.h index 060eba32..db2d4885 100644 --- a/server/cmd_opts.h +++ b/server/cmd_opts.h @@ -66,7 +66,22 @@ static char *config_map[NUMBER_OF_CONFIG_ENTRIES] = { //"EXTERNAL_CMD_ALARM", //"ENABLE_EXT_CMD_PREFIX", //"EXT_CMD_PREFIX", -#if FIREWALL_IPTABLES +#if FIREWALL_FIREWALLD + "ENABLE_FIREWD_FORWARDING", + "ENABLE_FIREWD_LOCAL_NAT", + "ENABLE_FIREWD_SNAT", + "SNAT_TRANSLATE_IP", + "ENABLE_FIREWD_OUTPUT", + "FLUSH_FIREWD_AT_INIT", + "FLUSH_FIREWD_AT_EXIT", + "FIREWD_INPUT_ACCESS", + "FIREWD_OUTPUT_ACCESS", + "FIREWD_FORWARD_ACCESS", + "FIREWD_DNAT_ACCESS", + "FIREWD_SNAT_ACCESS", + "FIREWD_MASQUERADE_ACCESS", + "ENABLE_FIREWD_COMMENT_CHECK", +#elif FIREWALL_IPTABLES "ENABLE_IPT_FORWARDING", "ENABLE_IPT_LOCAL_NAT", "ENABLE_IPT_SNAT", @@ -121,6 +136,7 @@ enum { FW_FLUSH, GPG_HOME_DIR, GPG_EXE_PATH, + FIREWD_DISABLE_CHECK_SUPPORT, IPT_DISABLE_CHECK_SUPPORT, PCAP_FILE, ENABLE_PCAP_ANY_DIRECTION, @@ -158,6 +174,7 @@ static struct option cmd_opts[] = {"fw-list-all", 0, NULL, FW_LIST_ALL }, {"gpg-home-dir", 1, NULL, GPG_HOME_DIR }, {"gpg-exe", 1, NULL, GPG_EXE_PATH }, + {"no-firewd-check-support", 0, NULL, FIREWD_DISABLE_CHECK_SUPPORT }, {"no-ipt-check-support", 0, NULL, IPT_DISABLE_CHECK_SUPPORT }, {"locale", 1, NULL, 'l' }, {"rotate-digest-cache", 0, NULL, ROTATE_DIGEST_CACHE }, diff --git a/server/config_init.c b/server/config_init.c index f6fed9a3..55ca9d37 100644 --- a/server/config_init.c +++ b/server/config_init.c @@ -36,7 +36,9 @@ #include "utils.h" #include "log_msg.h" -#if FIREWALL_IPTABLES +#if FIREWALL_FIREWALLD + #include "fw_util_firewalld.h" +#elif FIREWALL_IPTABLES #include "fw_util_iptables.h" #endif @@ -428,7 +430,150 @@ validate_options(fko_srv_options_t *opts) if(opts->config[CONF_MAX_SNIFF_BYTES] == NULL) set_config_entry(opts, CONF_MAX_SNIFF_BYTES, DEF_MAX_SNIFF_BYTES); -#if FIREWALL_IPTABLES +#if FIREWALL_FIREWALLD + /* Enable FIREWD forwarding. + */ + if(opts->config[CONF_ENABLE_FIREWD_FORWARDING] == NULL) + set_config_entry(opts, CONF_ENABLE_FIREWD_FORWARDING, + DEF_ENABLE_FIREWD_FORWARDING); + + /* Enable FIREWD local NAT. + */ + if(opts->config[CONF_ENABLE_FIREWD_LOCAL_NAT] == NULL) + set_config_entry(opts, CONF_ENABLE_FIREWD_LOCAL_NAT, + DEF_ENABLE_FIREWD_LOCAL_NAT); + + /* Enable FIREWD SNAT. + */ + if(opts->config[CONF_ENABLE_FIREWD_SNAT] == NULL) + set_config_entry(opts, CONF_ENABLE_FIREWD_SNAT, + DEF_ENABLE_FIREWD_SNAT); + + /* Make sure we have a valid IP if SNAT is enabled + */ + if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_SNAT], "Y", 1) == 0) + { + /* Note that fw_config_init() will set use_masquerade if necessary + */ + if(opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) + { + if(! is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP])) + { + log_msg(LOG_ERR, + "Invalid IPv4 addr for SNAT_TRANSLATE_IP" + ); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + } + } + + /* Enable FIREWD OUTPUT. + */ + if(opts->config[CONF_ENABLE_FIREWD_OUTPUT] == NULL) + set_config_entry(opts, CONF_ENABLE_FIREWD_OUTPUT, + DEF_ENABLE_FIREWD_OUTPUT); + + /* Flush FIREWD at init. + */ + if(opts->config[CONF_FLUSH_FIREWD_AT_INIT] == NULL) + set_config_entry(opts, CONF_FLUSH_FIREWD_AT_INIT, DEF_FLUSH_FIREWD_AT_INIT); + + /* Flush FIREWD at exit. + */ + if(opts->config[CONF_FLUSH_FIREWD_AT_EXIT] == NULL) + set_config_entry(opts, CONF_FLUSH_FIREWD_AT_EXIT, DEF_FLUSH_FIREWD_AT_EXIT); + + /* FIREWD input access. + */ + if(opts->config[CONF_FIREWD_INPUT_ACCESS] == NULL) + set_config_entry(opts, CONF_FIREWD_INPUT_ACCESS, + DEF_FIREWD_INPUT_ACCESS); + + if(validate_firewd_chain_conf(opts->config[CONF_FIREWD_INPUT_ACCESS]) != 1) + { + log_msg(LOG_ERR, + "Invalid FIREWD_INPUT_ACCESS specification, see fwknopd.conf comments" + ); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + + /* FIREWD output access. + */ + if(opts->config[CONF_FIREWD_OUTPUT_ACCESS] == NULL) + set_config_entry(opts, CONF_FIREWD_OUTPUT_ACCESS, + DEF_FIREWD_OUTPUT_ACCESS); + + if(validate_firewd_chain_conf(opts->config[CONF_FIREWD_OUTPUT_ACCESS]) != 1) + { + log_msg(LOG_ERR, + "Invalid FIREWD_OUTPUT_ACCESS specification, see fwknopd.conf comments" + ); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + + /* FIREWD forward access. + */ + if(opts->config[CONF_FIREWD_FORWARD_ACCESS] == NULL) + set_config_entry(opts, CONF_FIREWD_FORWARD_ACCESS, + DEF_FIREWD_FORWARD_ACCESS); + + if(validate_firewd_chain_conf(opts->config[CONF_FIREWD_FORWARD_ACCESS]) != 1) + { + log_msg(LOG_ERR, + "Invalid FIREWD_FORWARD_ACCESS specification, see fwknopd.conf comments" + ); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + + /* FIREWD dnat access. + */ + if(opts->config[CONF_FIREWD_DNAT_ACCESS] == NULL) + set_config_entry(opts, CONF_FIREWD_DNAT_ACCESS, + DEF_FIREWD_DNAT_ACCESS); + + if(validate_firewd_chain_conf(opts->config[CONF_FIREWD_DNAT_ACCESS]) != 1) + { + log_msg(LOG_ERR, + "Invalid FIREWD_DNAT_ACCESS specification, see fwknopd.conf comments" + ); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + + /* FIREWD snat access. + */ + if(opts->config[CONF_FIREWD_SNAT_ACCESS] == NULL) + set_config_entry(opts, CONF_FIREWD_SNAT_ACCESS, + DEF_FIREWD_SNAT_ACCESS); + + if(validate_firewd_chain_conf(opts->config[CONF_FIREWD_SNAT_ACCESS]) != 1) + { + log_msg(LOG_ERR, + "Invalid FIREWD_SNAT_ACCESS specification, see fwknopd.conf comments" + ); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + + /* FIREWD masquerade access. + */ + if(opts->config[CONF_FIREWD_MASQUERADE_ACCESS] == NULL) + set_config_entry(opts, CONF_FIREWD_MASQUERADE_ACCESS, + DEF_FIREWD_MASQUERADE_ACCESS); + + if(validate_firewd_chain_conf(opts->config[CONF_FIREWD_MASQUERADE_ACCESS]) != 1) + { + log_msg(LOG_ERR, + "Invalid FIREWD_MASQUERADE_ACCESS specification, see fwknopd.conf comments" + ); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + + /* Check for the firewalld 'comment' match at init time + */ + if(opts->config[CONF_ENABLE_FIREWD_COMMENT_CHECK] == NULL) + set_config_entry(opts, CONF_ENABLE_FIREWD_COMMENT_CHECK, + DEF_ENABLE_FIREWD_COMMENT_CHECK); + +#elif FIREWALL_IPTABLES /* Enable IPT forwarding. */ if(opts->config[CONF_ENABLE_IPT_FORWARDING] == NULL) @@ -929,6 +1074,9 @@ config_init(fko_srv_options_t *opts, int argc, char **argv) case 'i': set_config_entry(opts, CONF_PCAP_INTF, optarg); break; + case FIREWD_DISABLE_CHECK_SUPPORT: + opts->firewd_disable_check_support = 1; + break; case IPT_DISABLE_CHECK_SUPPORT: opts->ipt_disable_check_support = 1; break; @@ -1069,6 +1217,8 @@ usage(void) " done in the access.conf file).\n" " --gpg-exe - Specify the path to GPG (this is normally done in\n" " the access.conf file).\n" + " --no-firewd-check-support\n" + " - Disable test for 'firewall-cmd ... -C' support.\n" " --no-ipt-check-support - Disable test for 'iptables -C' support.\n" "\n" ); diff --git a/server/fw_util.h b/server/fw_util.h index e94d2186..015b2e63 100644 --- a/server/fw_util.h +++ b/server/fw_util.h @@ -40,7 +40,9 @@ #define EXPIRE_COMMENT_PREFIX "_exp_" #define TMP_COMMENT "__TMPCOMMENT__" -#if FIREWALL_IPTABLES +#if FIREWALL_FIREWALLD + #include "fw_util_firewalld.h" +#elif FIREWALL_IPTABLES #include "fw_util_iptables.h" #elif FIREWALL_IPFW #include "fw_util_ipfw.h" diff --git a/server/fw_util_firewalld.c b/server/fw_util_firewalld.c new file mode 100644 index 00000000..4afd3102 --- /dev/null +++ b/server/fw_util_firewalld.c @@ -0,0 +1,1589 @@ +/* + ***************************************************************************** + * + * File: fw_util_firewalld.c + * + * Purpose: Fwknop routines for managing firewalld firewall rules. + * + * Fwknop is developed primarily by the people listed in the file 'AUTHORS'. + * Copyright (C) 2009-2014 fwknop developers and contributors. For a full + * list of contributors, see the file 'CREDITS'. + * + * License (GNU General Public License): + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + ***************************************************************************** +*/ + +#include "fwknopd_common.h" + +#ifdef FIREWALL_FIREWALLD + +#include "fw_util.h" +#include "utils.h" +#include "log_msg.h" +#include "extcmd.h" +#include "access.h" + +static struct fw_config fwc; +static char cmd_buf[CMD_BUFSIZE]; +static char err_buf[CMD_BUFSIZE]; +static char cmd_out[STANDARD_CMD_OUT_BUFSIZE]; + +/* assume 'firewall-cmd --direct --passthrough ipv4 -C' is offered + * (see firewd_chk_support()). +*/ +static int have_firewd_chk_support = 1; + +static void +zero_cmd_buffers(void) +{ + memset(cmd_buf, 0x0, CMD_BUFSIZE); + memset(err_buf, 0x0, CMD_BUFSIZE); + memset(cmd_out, 0x0, STANDARD_CMD_OUT_BUFSIZE); +} + +static void +chop_newline(char *str) +{ + if(str[0] != 0x0 && str[strlen(str)-1] == 0x0a) + str[strlen(str)-1] = 0x0; + return; +} + +static int +rule_exists_no_chk_support(const fko_srv_options_t * const opts, + const struct fw_chain * const fwc, const unsigned int proto, + const char * const ip, const unsigned int port, + const unsigned int exp_ts) +{ + int rule_exists = 0; + char cmd_buf[CMD_BUFSIZE] = {0}; + char line_buf[CMD_BUFSIZE] = {0}; + char target_search[CMD_BUFSIZE] = {0}; + char proto_search[CMD_BUFSIZE] = {0}; + char ip_search[CMD_BUFSIZE] = {0}; + char port_search[CMD_BUFSIZE] = {0}; + char exp_ts_search[CMD_BUFSIZE] = {0}; + FILE *firewd; + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_RULES_ARGS, + opts->fw_config->fw_command, + fwc->table, + fwc->to_chain + ); + + firewd = popen(cmd_buf, "r"); + + if(firewd == NULL) + { + log_msg(LOG_ERR, + "Got error %i trying to get rules list.\n", errno); + return(rule_exists); + } + + if(proto == IPPROTO_TCP) + snprintf(proto_search, CMD_BUFSIZE-1, " tcp "); + else if(proto == IPPROTO_UDP) + snprintf(proto_search, CMD_BUFSIZE-1, " udp "); + else if(proto == IPPROTO_ICMP) + snprintf(proto_search, CMD_BUFSIZE-1, " icmp "); + else + snprintf(proto_search, CMD_BUFSIZE-1, " %u ", proto); + + snprintf(port_search, CMD_BUFSIZE-1, ":%u ", port); + snprintf(target_search, CMD_BUFSIZE-1, " %s ", fwc->target); + snprintf(ip_search, CMD_BUFSIZE-1, " %s ", ip); + snprintf(exp_ts_search, CMD_BUFSIZE-1, "%u ", exp_ts); + + while((fgets(line_buf, CMD_BUFSIZE-1, firewd)) != NULL) + { + /* Get past comments and empty lines (note: we only look at the + * first character). + */ + if(IS_EMPTY_LINE(line_buf[0])) + continue; + + if((strstr(line_buf, exp_ts_search) != NULL) + && (strstr(line_buf, proto_search) != NULL) + && (strstr(line_buf, ip_search) != NULL) + && (strstr(line_buf, target_search) != NULL) + && (strstr(line_buf, port_search) != NULL)) + { + rule_exists = 1; + break; + } + } + + pclose(firewd); + + if(rule_exists) + log_msg(LOG_DEBUG, + "rule_exists_no_chk_support() %s %u -> %s expires: %u rule (already exists", + proto_search, port, ip, exp_ts); + else + log_msg(LOG_DEBUG, + "rule_exists_no_chk_support() %s %u -> %s expires: %u rule does not exist", + proto_search, port, ip, exp_ts); + + return(rule_exists); +} + +static int +rule_exists_chk_support(const fko_srv_options_t * const opts, + const char * const chain, const char * const rule) +{ + int rule_exists = 0; + int res = 0; + + zero_cmd_buffers(); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_CHK_RULE_ARGS, + opts->fw_config->fw_command, chain, rule); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + chop_newline(err_buf); + + log_msg(LOG_DEBUG, "rule_exists_chk_support() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + if(strncmp(err_buf, "success", strlen("success")) == 0) + { + rule_exists = 1; + log_msg(LOG_DEBUG, "rule_exists_chk_support() Rule : '%s' in %s already exists", + rule, chain); + } + else + { + log_msg(LOG_DEBUG, "rule_exists_chk_support() Rule : '%s' in %s does not exist", + rule, chain); + } + + return(rule_exists); +} + +static int +rule_exists(const fko_srv_options_t * const opts, + const struct fw_chain * const fwc, const char * const rule, + const unsigned int proto, const char * const ip, + const unsigned int port, const unsigned int exp_ts) +{ + int rule_exists = 0; + + if(have_firewd_chk_support == 1) + rule_exists = rule_exists_chk_support(opts, fwc->to_chain, rule); + else + rule_exists = rule_exists_no_chk_support(opts, fwc, proto, ip, port, exp_ts); + + if(rule_exists == 1) + log_msg(LOG_DEBUG, "rule_exists() Rule : '%s' in %s already exists", + rule, fwc->to_chain); + else + log_msg(LOG_DEBUG, "rule_exists() Rule : '%s' in %s does not exist", + rule, fwc->to_chain); + + return(rule_exists); +} + +static void +firewd_chk_support(const fko_srv_options_t * const opts) +{ + int res = 1; + struct fw_chain *in_chain = &(opts->fw_config->chain[FIREWD_INPUT_ACCESS]); + + zero_cmd_buffers(); + + /* Add a harmless rule to the firewalld INPUT chain and see if firewalld + * supports '-C' to check for it. Set "have_firewd_chk_support" accordingly, + * delete the rule, and return. + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_TMP_CHK_RULE_ARGS, + opts->fw_config->fw_command, + in_chain->table, + in_chain->from_chain, + 1, /* first rule */ + in_chain->target + ); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + chop_newline(err_buf); + + log_msg(LOG_DEBUG, "firewd_chk_support() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + zero_cmd_buffers(); + + /* Now see if '-C' works - any output indicates failure + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_TMP_VERIFY_CHK_ARGS, + opts->fw_config->fw_command, + in_chain->table, + in_chain->from_chain, + in_chain->target + ); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + chop_newline(err_buf); + + log_msg(LOG_DEBUG, "firewd_chk_support() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + if(strncmp(err_buf, "success", strlen("success")) == 0) + { + log_msg(LOG_DEBUG, "firewd_chk_support() -C supported"); + have_firewd_chk_support = 1; + } + else + { + log_msg(LOG_DEBUG, "firewd_chk_support() -C not supported"); + have_firewd_chk_support = 0; + } + + /* Delete the tmp rule + */ + zero_cmd_buffers(); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_RULE_ARGS, + opts->fw_config->fw_command, + in_chain->table, + in_chain->from_chain, + 1 + ); + run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + return; +} + +static int +comment_match_exists(const fko_srv_options_t * const opts) +{ + int res = 1; + char *ndx = NULL; + struct fw_chain *in_chain = &(opts->fw_config->chain[FIREWD_INPUT_ACCESS]); + + zero_cmd_buffers(); + + /* Add a harmless rule to the firewalld INPUT chain that uses the comment + * match and make sure it exists. If not, return zero. Otherwise, delete + * the rule and return true. + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_TMP_COMMENT_ARGS, + opts->fw_config->fw_command, + in_chain->table, + in_chain->from_chain, + 1, /* first rule */ + in_chain->target + ); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + chop_newline(err_buf); + + log_msg(LOG_DEBUG, "comment_match_exists() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + zero_cmd_buffers(); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_RULES_ARGS, + opts->fw_config->fw_command, + in_chain->table, + in_chain->from_chain + ); + + res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE, 0); + chop_newline(cmd_out); + + if(!EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, cmd_out); + + ndx = strstr(cmd_out, TMP_COMMENT); + if(ndx == NULL) + res = 0; /* did not find the tmp comment */ + else + res = 1; + + if(res == 1) + { + /* Delete the tmp comment rule + */ + zero_cmd_buffers(); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_RULE_ARGS, + opts->fw_config->fw_command, + in_chain->table, + in_chain->from_chain, + 1 + ); + run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + } + + return res; +} + +static int +add_jump_rule(const fko_srv_options_t * const opts, const int chain_num) +{ + int res = 0; + + zero_cmd_buffers(); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_ADD_JUMP_RULE_ARGS, + fwc.fw_command, + fwc.chain[chain_num].table, + fwc.chain[chain_num].from_chain, + fwc.chain[chain_num].jump_rule_pos, + fwc.chain[chain_num].to_chain + ); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + log_msg(LOG_DEBUG, "add_jump_rule() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + if(EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_INFO, "Added jump rule from chain: %s to chain: %s", + fwc.chain[chain_num].from_chain, + fwc.chain[chain_num].to_chain); + else + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + + return res; +} + +static int +chain_exists(const fko_srv_options_t * const opts, const int chain_num) +{ + int res = 0; + + zero_cmd_buffers(); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_CHAIN_EXISTS_ARGS, + fwc.fw_command, + fwc.chain[chain_num].table, + fwc.chain[chain_num].to_chain + ); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + chop_newline(err_buf); + + log_msg(LOG_DEBUG, "chain_exists() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + if(EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_DEBUG, "'%s' table '%s' chain exists", + fwc.chain[chain_num].table, + fwc.chain[chain_num].to_chain); + else + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + + return res; +} + +static int +jump_rule_exists_chk_support(const fko_srv_options_t * const opts, const int chain_num) +{ + int exists = 0; + char rule_buf[CMD_BUFSIZE] = {0}; + + snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_CHK_JUMP_RULE_ARGS, + fwc.chain[chain_num].table, + fwc.chain[chain_num].to_chain + ); + + if(rule_exists_chk_support(opts, fwc.chain[chain_num].from_chain, rule_buf) == 1) + { + log_msg(LOG_DEBUG, "jump_rule_exists_chk_support() jump rule found"); + exists = 1; + } + else + log_msg(LOG_DEBUG, "jump_rule_exists_chk_support() jump rule not found"); + + return exists; +} + +static int +jump_rule_exists_no_chk_support(const fko_srv_options_t * const opts, const int chain_num) +{ + int exists = 0; + char cmd_buf[CMD_BUFSIZE] = {0}; + char chain_search[CMD_BUFSIZE] = {0}; + char line_buf[CMD_BUFSIZE] = {0}; + FILE *firewd; + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_RULES_ARGS, + fwc.fw_command, + fwc.chain[chain_num].table, + fwc.chain[chain_num].from_chain + ); + + firewd = popen(cmd_buf, "r"); + + if(firewd == NULL) + { + log_msg(LOG_ERR, + "Got error %i trying to get rules list.\n", errno); + return(exists); + } + + /* include spaces on either side as produced by 'firewalld -L' output + */ + snprintf(chain_search, CMD_BUFSIZE-1, " %s ", + fwc.chain[chain_num].to_chain); + + while((fgets(line_buf, CMD_BUFSIZE-1, firewd)) != NULL) + { + /* Get past comments and empty lines (note: we only look at the + * first character). + */ + if(IS_EMPTY_LINE(line_buf[0])) + continue; + + if(strstr(line_buf, chain_search) != NULL) + { + exists = 1; + break; + } + } + + pclose(firewd); + + + if(exists) + log_msg(LOG_DEBUG, "jump_rule_exists_no_chk_support() jump rule found"); + else + log_msg(LOG_DEBUG, "jump_rule_exists_no_chk_support() jump rule not found"); + + return(exists); +} + +static int +jump_rule_exists(const fko_srv_options_t * const opts, const int chain_num) +{ + int exists = 0; + + if(have_firewd_chk_support == 1) + exists = jump_rule_exists_chk_support(opts, chain_num); + else + exists = jump_rule_exists_no_chk_support(opts, chain_num); + + return exists; +} + +/* Print all firewall rules currently instantiated by the running fwknopd + * daemon to stdout. +*/ +int +fw_dump_rules(const fko_srv_options_t * const opts) +{ + int i; + int res, got_err = 0; + + struct fw_chain *ch = opts->fw_config->chain; + + if (opts->fw_list_all == 1) + { + fprintf(stdout, "Listing all firewalld rules in applicable tables...\n"); + fflush(stdout); + + for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++) + { + + if(fwc.chain[i].target[0] == '\0') + continue; + + zero_cmd_buffers(); + + /* Create the list command + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_ALL_RULES_ARGS, + opts->fw_config->fw_command, + ch[i].table + ); + + res = system(cmd_buf); + + log_msg(LOG_DEBUG, "fw_dump_rules() CMD: '%s' (res: %d)", + cmd_buf, res); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + got_err++; + } + } + } + else + { + fprintf(stdout, "Listing rules in fwknopd firewalld chains...\n"); + fflush(stdout); + + for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++) + { + + if(fwc.chain[i].target[0] == '\0') + continue; + + zero_cmd_buffers(); + + /* Create the list command + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_RULES_ARGS, + opts->fw_config->fw_command, + ch[i].table, + ch[i].to_chain + ); + + fprintf(stdout, "\n"); + fflush(stdout); + res = system(cmd_buf); + + log_msg(LOG_DEBUG, "fw_dump_rules() CMD: '%s' (res: %d)", + cmd_buf, res); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + got_err++; + } + } + } + + return(got_err); +} + +/* Quietly flush and delete all fwknop custom chains. +*/ +static void +delete_all_chains(const fko_srv_options_t * const opts) +{ + int i, res, cmd_ctr = 0; + + for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++) + { + if(fwc.chain[i].target[0] == '\0') + continue; + + /* First look for a jump rule to this chain and remove it if it + * is there. + */ + cmd_ctr = 0; + while(cmd_ctr < CMD_LOOP_TRIES && (jump_rule_exists(opts, i) == 1)) + { + zero_cmd_buffers(); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_JUMP_RULE_ARGS, + fwc.fw_command, + fwc.chain[i].table, + fwc.chain[i].from_chain, + fwc.chain[i].to_chain + ); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + chop_newline(err_buf); + + log_msg(LOG_DEBUG, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + + cmd_ctr++; + } + + zero_cmd_buffers(); + + /* Now flush and remove the chain. + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, + "(%s " FIREWD_FLUSH_CHAIN_ARGS "; %s " FIREWD_DEL_CHAIN_ARGS ")", // > /dev/null 2>&1", + fwc.fw_command, + fwc.chain[i].table, + fwc.chain[i].to_chain, + fwc.fw_command, + fwc.chain[i].table, + fwc.chain[i].to_chain + ); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + chop_newline(err_buf); + + log_msg(LOG_DEBUG, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + } +} + +static int +create_chain(const fko_srv_options_t * const opts, const int chain_num) +{ + int res = 0; + + zero_cmd_buffers(); + + /* Create the custom chain. + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_NEW_CHAIN_ARGS, + fwc.fw_command, + fwc.chain[chain_num].table, + fwc.chain[chain_num].to_chain + ); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + chop_newline(err_buf); + + log_msg(LOG_DEBUG, "create_chain() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + + return res; +} + +/* Create the fwknop custom chains (at least those that are configured). +*/ +static int +create_fw_chains(const fko_srv_options_t * const opts) +{ + int i, got_err = 0; + + for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++) + { + if(fwc.chain[i].target[0] == '\0') + continue; + + if(chain_exists(opts, i) == 0) + { + + /* Create the chain + */ + if(! EXTCMD_IS_SUCCESS(create_chain(opts, i))) + got_err++; + + /* Then create the jump rule to that chain if it + * doesn't already exist (which is possible) + */ + if(jump_rule_exists(opts, i) == 0) + if(! EXTCMD_IS_SUCCESS(add_jump_rule(opts, i))) + got_err++; + } + } + + return(got_err); +} + +static int +set_fw_chain_conf(const int type, const char * const conf_str) +{ + int i, j, is_err; + char tbuf[MAX_LINE_LEN] = {0}; + const char *ndx = conf_str; + + char *chain_fields[FW_NUM_CHAIN_FIELDS]; + + struct fw_chain *chain = &(fwc.chain[type]); + + if(conf_str == NULL) + { + log_msg(LOG_ERR, "[*] NULL conf_str"); + return 0; + } + + chain->type = type; + + if(ndx != NULL) + chain_fields[0] = tbuf; + + i = 0; + j = 1; + while(*ndx != '\0') + { + if(*ndx != ' ') + { + if(*ndx == ',') + { + tbuf[i] = '\0'; + chain_fields[j++] = &(tbuf[++i]); + } + else + tbuf[i++] = *ndx; + } + if(*ndx != '\0' + && *ndx != ' ' + && *ndx != ',' + && *ndx != '_' + && isalnum(*ndx) == 0) + { + log_msg(LOG_ERR, "[*] Custom chain config parse error: " + "invalid character '%c' for chain type %i, " + "line: %s", *ndx, type, conf_str); + return 0; + } + ndx++; + } + + /* Sanity check - j should be the number of chain fields + * (excluding the type). + */ + if(j != FW_NUM_CHAIN_FIELDS) + { + log_msg(LOG_ERR, "[*] Custom chain config parse error: " + "wrong number of fields for chain type %i, " + "line: %s", type, conf_str); + return 0; + } + + /* Pull and set Target */ + strlcpy(chain->target, chain_fields[0], sizeof(chain->target)); + + /* Pull and set Table */ + strlcpy(chain->table, chain_fields[1], sizeof(chain->table)); + + /* Pull and set From_chain */ + strlcpy(chain->from_chain, chain_fields[2], sizeof(chain->from_chain)); + + /* Pull and set Jump_rule_position */ + chain->jump_rule_pos = strtol_wrapper(chain_fields[3], + 0, RCHK_MAX_FIREWD_RULE_NUM, NO_EXIT_UPON_ERR, &is_err); + if(is_err != FKO_SUCCESS) + { + log_msg(LOG_ERR, "[*] invalid jump rule position in Line: %s", + conf_str); + return 0; + } + + /* Pull and set To_chain */ + strlcpy(chain->to_chain, chain_fields[4], sizeof(chain->to_chain)); + + /* Pull and set to_chain rule position */ + chain->rule_pos = strtol_wrapper(chain_fields[5], + 0, RCHK_MAX_FIREWD_RULE_NUM, NO_EXIT_UPON_ERR, &is_err); + if(is_err != FKO_SUCCESS) + { + log_msg(LOG_ERR, "[*] invalid to_chain rule position in Line: %s", + conf_str); + return 0; + } + return 1; +} + +int +fw_config_init(fko_srv_options_t * const opts) +{ + + memset(&fwc, 0x0, sizeof(struct fw_config)); + + /* Set our firewall exe command path (firewall-cmd or iptables in most cases). + */ +#if FIREWALL_FIREWALLD + char cmd_passthru[512]; + snprintf(cmd_passthru, sizeof cmd_passthru, "%s %s ", opts->config[CONF_FIREWALL_EXE], " --direct --passthrough ipv4 "); + strlcpy(fwc.fw_command, cmd_passthru, sizeof(fwc.fw_command)); +#else + strlcpy(fwc.fw_command, opts->config[CONF_FIREWALL_EXE], sizeof(fwc.fw_command)); +#endif + +#if HAVE_LIBFIU + fiu_return_on("fw_config_init", 0); +#endif + + /* Pull the fwknop chain config info and setup our internal + * config struct. The FIREWD_INPUT is the only one that is + * required. The rest are optional. + */ + if(set_fw_chain_conf(FIREWD_INPUT_ACCESS, opts->config[CONF_FIREWD_INPUT_ACCESS]) != 1) + return 0; + + /* The FWKNOP_OUTPUT_ACCESS requires ENABLE_FIREWD_OUTPUT_ACCESS be Y + */ + if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_OUTPUT], "Y", 1)==0) + if(set_fw_chain_conf(FIREWD_OUTPUT_ACCESS, opts->config[CONF_FIREWD_OUTPUT_ACCESS]) != 1) + return 0; + + /* The remaining access chains require ENABLE_FIREWD_FORWARDING = Y + */ + if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_FORWARDING], "Y", 1)==0) + { + if(set_fw_chain_conf(FIREWD_FORWARD_ACCESS, opts->config[CONF_FIREWD_FORWARD_ACCESS]) != 1) + return 0; + + if(set_fw_chain_conf(FIREWD_DNAT_ACCESS, opts->config[CONF_FIREWD_DNAT_ACCESS]) != 1) + return 0; + + /* SNAT (whichever mode) requires ENABLE_FIREWD_SNAT = Y + */ + if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_SNAT], "Y", 1)==0) + { + if(opts->config[CONF_SNAT_TRANSLATE_IP] == NULL + || ! is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP])) + { + fwc.use_masquerade = 1; + if(set_fw_chain_conf(FIREWD_MASQUERADE_ACCESS, opts->config[CONF_FIREWD_MASQUERADE_ACCESS]) != 1) + return 0; + } + else + { + if(is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP])) + { + if(set_fw_chain_conf(FIREWD_SNAT_ACCESS, opts->config[CONF_FIREWD_SNAT_ACCESS]) != 1) + return 0; + } + else + { + return 0; + } + } + } + } + + /* Let us find it via our opts struct as well. + */ + opts->fw_config = &fwc; + + return 1; +} + +int +fw_initialize(const fko_srv_options_t * const opts) +{ + int res = 1; + + /* Flush the chains (just in case) so we can start fresh. + */ + if(strncasecmp(opts->config[CONF_FLUSH_FIREWD_AT_INIT], "Y", 1) == 0) + delete_all_chains(opts); + + /* Now create any configured chains. + */ + if(create_fw_chains(opts) != 0) + { + log_msg(LOG_WARNING, + "Warning: Errors detected during fwknop custom chain creation"); + res = 0; + } + + /* Make sure that the 'comment' match is available + */ + if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_COMMENT_CHECK], "Y", 1) == 0) + { + if(comment_match_exists(opts) == 1) + { + log_msg(LOG_INFO, "firewalld 'comment' match is available"); + } + else + { + log_msg(LOG_WARNING, "Warning: Could not use the 'comment' match"); + res = 0; + } + } + + /* See if firewalld offers the '-C' argument (older versions don't). If not, + * then switch to parsing firewalld -L output to find rules. + */ + if(opts->firewd_disable_check_support) + have_firewd_chk_support = 0; + else + firewd_chk_support(opts); + + return(res); +} + +int +fw_cleanup(const fko_srv_options_t * const opts) +{ + if(strncasecmp(opts->config[CONF_FLUSH_FIREWD_AT_EXIT], "N", 1) == 0 + && opts->fw_flush == 0) + return(0); + + delete_all_chains(opts); + return(0); +} + +static int +create_rule(const fko_srv_options_t * const opts, + const char * const fw_chain, const char * const fw_rule) +{ + int res = 0; + + zero_cmd_buffers(); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s -A %s %s", opts->fw_config->fw_command, fw_chain, fw_rule); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + chop_newline(err_buf); + + log_msg(LOG_DEBUG, "create_rule() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + if(EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_DEBUG, "create_rule() Rule: '%s' added to %s", fw_rule, fw_chain); + res = 1; + } + else + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + + return res; +} + +/****************************************************************************/ + +/* Rule Processing - Create an access request... +*/ +int +process_spa_request(const fko_srv_options_t * const opts, + const acc_stanza_t * const acc, spa_data_t * const spadat) +{ + char nat_ip[MAX_IPV4_STR_LEN] = {0}; + char snat_target[SNAT_TARGET_BUFSIZE] = {0}; + char rule_buf[CMD_BUFSIZE] = {0}; + char *ndx; + + unsigned int nat_port = 0; + + acc_port_list_t *port_list = NULL; + acc_port_list_t *ple = NULL; + + unsigned int fst_proto; + unsigned int fst_port; + + struct fw_chain * const in_chain = &(opts->fw_config->chain[FIREWD_INPUT_ACCESS]); + struct fw_chain * const out_chain = &(opts->fw_config->chain[FIREWD_OUTPUT_ACCESS]); + struct fw_chain * const fwd_chain = &(opts->fw_config->chain[FIREWD_FORWARD_ACCESS]); + struct fw_chain * const dnat_chain = &(opts->fw_config->chain[FIREWD_DNAT_ACCESS]); + struct fw_chain *snat_chain; /* We assign this later (if we need to). */ + + int res = 0, is_err, snat_chain_num = 0; + time_t now; + unsigned int exp_ts; + + /* Parse and expand our access message. + */ + if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1) + { + /* technically we would already have exited with an error if there were + * any memory allocation errors (see the add_port_list() function), but + * for completeness... + */ + free_acc_port_list(port_list); + return res; + } + + /* Start at the top of the proto-port list... + */ + ple = port_list; + + /* Remember the first proto/port combo in case we need them + * for NAT access requests. + */ + fst_proto = ple->proto; + fst_port = ple->port; + + /* Set our expire time value. + */ + time(&now); + exp_ts = now + spadat->fw_access_timeout; + + /* For straight access requests, we currently support multiple proto/port + * request. + */ + if((spadat->message_type == FKO_ACCESS_MSG + || spadat->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG) && !acc->force_nat) + { + + /* Check to make sure that the jump rules exist for each + * required chain + */ + if(chain_exists(opts, FIREWD_INPUT_ACCESS) == 0) + create_chain(opts, FIREWD_INPUT_ACCESS); + + if(jump_rule_exists(opts, FIREWD_INPUT_ACCESS) == 0) + add_jump_rule(opts, FIREWD_INPUT_ACCESS); + + if(strlen(out_chain->to_chain)) + { + if(chain_exists(opts, FIREWD_OUTPUT_ACCESS) == 0) + create_chain(opts, FIREWD_OUTPUT_ACCESS); + + if(jump_rule_exists(opts, FIREWD_OUTPUT_ACCESS) == 0) + add_jump_rule(opts, FIREWD_OUTPUT_ACCESS); + } + + /* Create an access command for each proto/port for the source ip. + */ + while(ple != NULL) + { + memset(rule_buf, 0, CMD_BUFSIZE); + + snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_RULE_ARGS, + in_chain->table, + ple->proto, + spadat->use_src_ip, + ple->port, + exp_ts, + in_chain->target + ); + + if(rule_exists(opts, in_chain, rule_buf, + ple->proto, spadat->use_src_ip, ple->port, exp_ts) == 0) + { + if(create_rule(opts, in_chain->to_chain, rule_buf)) + { + log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u", + in_chain->to_chain, spadat->use_src_ip, + spadat->spa_message_remain, exp_ts + ); + + in_chain->active_rules++; + + /* Reset the next expected expire time for this chain if it + * is warranted. + */ + if(in_chain->next_expire < now || exp_ts < in_chain->next_expire) + in_chain->next_expire = exp_ts; + } + } + + /* If we have to make an corresponding OUTPUT rule if out_chain target + * is not NULL. + */ + if(strlen(out_chain->to_chain)) + { + memset(rule_buf, 0, CMD_BUFSIZE); + + snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_OUT_RULE_ARGS, + out_chain->table, + ple->proto, + spadat->use_src_ip, + ple->port, + exp_ts, + out_chain->target + ); + + if(rule_exists(opts, out_chain, rule_buf, + ple->proto, spadat->use_src_ip, ple->port, exp_ts) == 0) + { + if(create_rule(opts, out_chain->to_chain, rule_buf)) + { + log_msg(LOG_INFO, "Added Rule in %s for %s, %s expires at %u", + out_chain->to_chain, spadat->use_src_ip, + spadat->spa_message_remain, exp_ts + ); + + out_chain->active_rules++; + + /* Reset the next expected expire time for this chain if it + * is warranted. */ + if(out_chain->next_expire < now || exp_ts < out_chain->next_expire) + out_chain->next_expire = exp_ts; + } + } + } + ple = ple->next; + } + } + /* NAT requests... */ + else if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG + || spadat->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG + || spadat->message_type == FKO_NAT_ACCESS_MSG + || spadat->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG + || acc->force_nat) + { + /* Parse out the NAT IP and Port components. + */ + if(acc->force_nat) + { + strlcpy(nat_ip, acc->force_nat_ip, sizeof(nat_ip)); + nat_port = acc->force_nat_port; + } + else + { + ndx = strchr(spadat->nat_access, ','); + if(ndx != NULL) + { + strlcpy(nat_ip, spadat->nat_access, (ndx-spadat->nat_access)+1); + if (! is_valid_ipv4_addr(nat_ip)) + { + log_msg(LOG_INFO, "Invalid NAT IP in SPA message"); + free_acc_port_list(port_list); + return res; + } + + nat_port = strtol_wrapper(ndx+1, 0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err); + if(is_err != FKO_SUCCESS) + { + log_msg(LOG_INFO, "Invalid NAT port in SPA message"); + free_acc_port_list(port_list); + res = is_err; + return res; + } + } + } + + if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG) + { + memset(rule_buf, 0, CMD_BUFSIZE); + + snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_RULE_ARGS, + in_chain->table, + fst_proto, + spadat->use_src_ip, + nat_port, + exp_ts, + in_chain->target + ); + + /* Check to make sure that the jump rules exist for each + * required chain + */ + if(chain_exists(opts, FIREWD_INPUT_ACCESS) == 0) + create_chain(opts, FIREWD_INPUT_ACCESS); + + if(jump_rule_exists(opts, FIREWD_INPUT_ACCESS) == 0) + add_jump_rule(opts, FIREWD_INPUT_ACCESS); + + if(rule_exists(opts, in_chain, rule_buf, + fst_proto, spadat->use_src_ip, nat_port, exp_ts) == 0) + { + if(create_rule(opts, in_chain->to_chain, rule_buf)) + { + log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u", + in_chain->to_chain, spadat->use_src_ip, + spadat->spa_message_remain, exp_ts + ); + + in_chain->active_rules++; + + /* Reset the next expected expire time for this chain if it + * is warranted. + */ + if(in_chain->next_expire < now || exp_ts < in_chain->next_expire) + in_chain->next_expire = exp_ts; + } + } + } + else if(strlen(fwd_chain->to_chain)) + { + /* Make our FORWARD and NAT rules, and make sure the + * required chain and jump rule exists + */ + if(chain_exists(opts, FIREWD_FORWARD_ACCESS) == 0) + create_chain(opts, FIREWD_FORWARD_ACCESS); + + if (jump_rule_exists(opts, FIREWD_FORWARD_ACCESS) == 0) + add_jump_rule(opts, FIREWD_FORWARD_ACCESS); + + memset(rule_buf, 0, CMD_BUFSIZE); + + snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_FWD_RULE_ARGS, + fwd_chain->table, + fst_proto, + spadat->use_src_ip, + nat_ip, + nat_port, + exp_ts, + fwd_chain->target + ); + + if(rule_exists(opts, fwd_chain, rule_buf, fst_proto, + spadat->use_src_ip, nat_port, exp_ts) == 0) + { + if(create_rule(opts, fwd_chain->to_chain, rule_buf)) + { + log_msg(LOG_INFO, "Added FORWARD Rule to %s for %s, %s expires at %u", + fwd_chain->to_chain, spadat->use_src_ip, + spadat->spa_message_remain, exp_ts + ); + + fwd_chain->active_rules++; + + /* Reset the next expected expire time for this chain if it + * is warranted. + */ + if(fwd_chain->next_expire < now || exp_ts < fwd_chain->next_expire) + fwd_chain->next_expire = exp_ts; + } + } + } + + if(strlen(dnat_chain->to_chain)) + { + /* Make sure the required chain and jump rule exist + */ + if(chain_exists(opts, FIREWD_DNAT_ACCESS) == 0) + create_chain(opts, FIREWD_DNAT_ACCESS); + + if (jump_rule_exists(opts, FIREWD_DNAT_ACCESS) == 0) + add_jump_rule(opts, FIREWD_DNAT_ACCESS); + + memset(rule_buf, 0, CMD_BUFSIZE); + + snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_DNAT_RULE_ARGS, + dnat_chain->table, + fst_proto, + spadat->use_src_ip, + fst_port, + exp_ts, + dnat_chain->target, + nat_ip, + nat_port + ); + + if(rule_exists(opts, dnat_chain, rule_buf, fst_proto, + spadat->use_src_ip, fst_port, exp_ts) == 0) + { + if(create_rule(opts, dnat_chain->to_chain, rule_buf)) + { + log_msg(LOG_INFO, "Added DNAT Rule to %s for %s, %s expires at %u", + dnat_chain->to_chain, spadat->use_src_ip, + spadat->spa_message_remain, exp_ts + ); + + dnat_chain->active_rules++; + + /* Reset the next expected expire time for this chain if it + * is warranted. + */ + if(dnat_chain->next_expire < now || exp_ts < dnat_chain->next_expire) + dnat_chain->next_expire = exp_ts; + } + } + } + + /* If SNAT (or MASQUERADE) is wanted, then we add those rules here as well. + */ + if(acc->force_snat || strncasecmp(opts->config[CONF_ENABLE_FIREWD_SNAT], "Y", 1) == 0) + { + /* Add SNAT or MASQUERADE rules. + */ + if(acc->force_snat && is_valid_ipv4_addr(acc->force_snat_ip)) + { + /* Using static SNAT */ + snat_chain = &(opts->fw_config->chain[FIREWD_SNAT_ACCESS]); + snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, + "--to-source %s:%i", acc->force_snat_ip, fst_port); + snat_chain_num = FIREWD_SNAT_ACCESS; + } + else if(acc->force_snat && acc->force_masquerade) + { + /* Using MASQUERADE */ + snat_chain = &(opts->fw_config->chain[FIREWD_MASQUERADE_ACCESS]); + snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, + "--to-ports %i", fst_port); + snat_chain_num = FIREWD_MASQUERADE_ACCESS; + } + else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) + && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP])) + { + /* Using static SNAT */ + snat_chain = &(opts->fw_config->chain[FIREWD_SNAT_ACCESS]); + snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, + "--to-source %s:%i", opts->config[CONF_SNAT_TRANSLATE_IP], + fst_port); + snat_chain_num = FIREWD_SNAT_ACCESS; + } + else + { + /* Using MASQUERADE */ + snat_chain = &(opts->fw_config->chain[FIREWD_MASQUERADE_ACCESS]); + snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, + "--to-ports %i", fst_port); + snat_chain_num = FIREWD_MASQUERADE_ACCESS; + } + + if(chain_exists(opts, snat_chain_num) == 0) + create_chain(opts, snat_chain_num); + + if(jump_rule_exists(opts, snat_chain_num) == 0) + add_jump_rule(opts, snat_chain_num); + + memset(rule_buf, 0, CMD_BUFSIZE); + + snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_SNAT_RULE_ARGS, + snat_chain->table, + fst_proto, + nat_ip, + nat_port, + exp_ts, + snat_chain->target, + snat_target + ); + + if(rule_exists(opts, snat_chain, rule_buf, fst_proto, + spadat->use_src_ip, nat_port, exp_ts) == 0) + { + if(create_rule(opts, snat_chain->to_chain, rule_buf)) + { + log_msg(LOG_INFO, "Added SNAT Rule to %s for %s, %s expires at %u", + snat_chain->to_chain, spadat->use_src_ip, + spadat->spa_message_remain, exp_ts + ); + + snat_chain->active_rules++; + + /* Reset the next expected expire time for this chain if it + * is warranted. + */ + if(snat_chain->next_expire < now || exp_ts < snat_chain->next_expire) + snat_chain->next_expire = exp_ts; + } + } + } + } + + /* Done with the port list for access rules. + */ + free_acc_port_list(port_list); + + return(res); +} + +/* Iterate over the configure firewall access chains and purge expired + * firewall rules. +*/ +void +check_firewall_rules(const fko_srv_options_t * const opts) +{ + char exp_str[12] = {0}; + char rule_num_str[6] = {0}; + char *ndx, *rn_start, *rn_end, *tmp_mark; + + int i, res, rn_offset, rule_num, is_err; + time_t now, rule_exp, min_exp = 0; + + struct fw_chain *ch = opts->fw_config->chain; + + time(&now); + + /* Iterate over each chain and look for active rules to delete. + */ + for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++) + { + /* If there are no active rules or we have not yet + * reached our expected next expire time, continue. + */ + if(ch[i].active_rules == 0 || ch[i].next_expire > now) + continue; + + zero_cmd_buffers(); + + rn_offset = 0; + + /* There should be a rule to delete. Get the current list of + * rules for this chain and delete the ones that are expired. + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_RULES_ARGS, + opts->fw_config->fw_command, + ch[i].table, + ch[i].to_chain + ); + + res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE, 0); + chop_newline(cmd_out); + + log_msg(LOG_DEBUG, "check_firewall_rules() CMD: '%s' (res: %d, cmd_out: %s)", + cmd_buf, res, cmd_out); + + if(!EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, cmd_out); + continue; + } + + log_msg(LOG_DEBUG, "RES=%i, CMD_BUF: %s\nRULES LIST: %s", res, cmd_buf, cmd_out); + + ndx = strstr(cmd_out, EXPIRE_COMMENT_PREFIX); + if(ndx == NULL) + { + /* we did not find an expected rule. + */ + log_msg(LOG_ERR, + "Did not find expire comment in rules list %i", i); + + if (ch[i].active_rules > 0) + ch[i].active_rules--; + + continue; + } + + /* walk the list and process rules as needed. + */ + while (ndx != NULL) { + /* Jump forward and extract the timestamp + */ + ndx += strlen(EXPIRE_COMMENT_PREFIX); + + /* remember this spot for when we look for the next + * rule. + */ + tmp_mark = ndx; + + strlcpy(exp_str, ndx, sizeof(exp_str)); + rule_exp = (time_t)atoll(exp_str); + + if(rule_exp <= now) + { + /* Backtrack and get the rule number and delete it. + */ + rn_start = ndx; + while(--rn_start > cmd_out) + { + if(*rn_start == '\n') + break; + } + + if(*rn_start != '\n') + { + /* This should not happen. But if it does, complain, + * decrement the active rule value, and go on. + */ + log_msg(LOG_ERR, + "Rule parse error while finding rule line start in chain %i", i); + + if (ch[i].active_rules > 0) + ch[i].active_rules--; + + break; + } + rn_start++; + + rn_end = strchr(rn_start, ' '); + if(rn_end == NULL) + { + /* This should not happen. But if it does, complain, + * decrement the active rule value, and go on. + */ + log_msg(LOG_ERR, + "Rule parse error while finding rule number in chain %i", i); + + if (ch[i].active_rules > 0) + ch[i].active_rules--; + + break; + } + + strlcpy(rule_num_str, rn_start, (rn_end - rn_start)+1); + + rule_num = strtol_wrapper(rule_num_str, rn_offset, RCHK_MAX_FIREWD_RULE_NUM, + NO_EXIT_UPON_ERR, &is_err); + if(is_err != FKO_SUCCESS) + { + log_msg(LOG_ERR, + "Rule parse error while finding rule number in chain %i", i); + + if (ch[i].active_rules > 0) + ch[i].active_rules--; + + break; + } + + zero_cmd_buffers(); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_RULE_ARGS, + opts->fw_config->fw_command, + ch[i].table, + ch[i].to_chain, + rule_num - rn_offset + ); + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + chop_newline(err_buf); + + log_msg(LOG_DEBUG, "check_firewall_rules() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + if(EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_INFO, "Removed rule %s from %s with expire time of %u", + rule_num_str, ch[i].to_chain, rule_exp + ); + + rn_offset++; + + if (ch[i].active_rules > 0) + ch[i].active_rules--; + } + else + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + + } + else + { + /* Track the minimum future rule expire time. + */ + if(rule_exp > now) + min_exp = (min_exp < rule_exp) ? min_exp : rule_exp; + } + + /* Push our tracking index forward beyond (just processed) _exp_ + * string so we can continue to the next rule in the list. + */ + ndx = strstr(tmp_mark, EXPIRE_COMMENT_PREFIX); + } + + /* Set the next pending expire time accordingly. 0 if there are no + * more rules, or whatever the next expected (min_exp) time will be. + */ + if(ch[i].active_rules < 1) + ch[i].next_expire = 0; + else if(min_exp) + ch[i].next_expire = min_exp; + } +} + +int +validate_firewd_chain_conf(const char * const chain_str) +{ + int j, rv = 1; + const char *ndx = chain_str; + + j = 1; + while(*ndx != '\0') + { + if(*ndx == ',') + j++; + + if(*ndx != '\0' + && *ndx != ' ' + && *ndx != ',' + && *ndx != '_' + && isalnum(*ndx) == 0) + { + rv = 0; + break; + } + ndx++; + } + + /* Sanity check - j should be the number of chain fields + * (excluding the type). + */ + if(j != FW_NUM_CHAIN_FIELDS) + rv = 0; + + return rv; +} + +#endif /* FIREWALL_FIREWALLD */ + +/***EOF***/ diff --git a/server/fw_util_firewalld.h b/server/fw_util_firewalld.h new file mode 100644 index 00000000..b27da5c5 --- /dev/null +++ b/server/fw_util_firewalld.h @@ -0,0 +1,62 @@ +/* + ***************************************************************************** + * + * File: fw_util_firewalld.h + * + * Purpose: Header file for fw_util_firewalld.c. + * + * Fwknop is developed primarily by the people listed in the file 'AUTHORS'. + * Copyright (C) 2009-2014 fwknop developers and contributors. For a full + * list of contributors, see the file 'CREDITS'. + * + * License (GNU General Public License): + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + ***************************************************************************** +*/ +#ifndef FW_UTIL_FIREWALLD_H +#define FW_UTIL_FIREWALLD_H + +#define SNAT_TARGET_BUFSIZE 64 + +/* firewalld command args +*/ +#define FIREWD_CHK_RULE_ARGS "-C %s %s" /* 2>&1 is always added in the second %s */ +#define FIREWD_RULE_ARGS "-t %s -p %i -s %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s 2>&1" +#define FIREWD_OUT_RULE_ARGS "-t %s -p %i -d %s --sport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s 2>&1" +#define FIREWD_FWD_RULE_ARGS "-t %s -p %i -s %s -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s 2>&1" +#define FIREWD_DNAT_RULE_ARGS "-t %s -p %i -s %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s --to-destination %s:%i 2>&1" +#define FIREWD_SNAT_RULE_ARGS "-t %s -p %i -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s %s 2>&1" +#define FIREWD_TMP_COMMENT_ARGS "-t %s -I %s %i -s 127.0.0.2 -m comment --comment " TMP_COMMENT " -j %s 2>&1" +#define FIREWD_TMP_CHK_RULE_ARGS "-t %s -I %s %i -s 127.0.0.2 -p udp -j %s 2>&1" +#define FIREWD_TMP_VERIFY_CHK_ARGS "-t %s -C %s -s 127.0.0.2 -p udp -j %s 2>&1" +#define FIREWD_DEL_RULE_ARGS "-t %s -D %s %i 2>&1" +#define FIREWD_NEW_CHAIN_ARGS "-t %s -N %s 2>&1" +#define FIREWD_FLUSH_CHAIN_ARGS "-t %s -F %s 2>&1" +#define FIREWD_CHAIN_EXISTS_ARGS "-t %s -L %s -n 2>&1" +#define FIREWD_DEL_CHAIN_ARGS "-t %s -X %s 2>&1" +#define FIREWD_CHK_JUMP_RULE_ARGS "-t %s -j %s 2>&1" +#define FIREWD_ADD_JUMP_RULE_ARGS "-t %s -I %s %i -j %s 2>&1" +#define FIREWD_DEL_JUMP_RULE_ARGS "-t %s -D %s -j %s 2>&1" /* let firewalld work out the rule number */ +#define FIREWD_LIST_RULES_ARGS "-t %s -L %s --line-numbers -n 2>&1" +#define FIREWD_LIST_ALL_RULES_ARGS "-t %s -v -n -L --line-numbers 2>&1" + +int validate_firewd_chain_conf(const char * const chain_str); + +#endif /* FW_UTIL_FIREWALLD_H */ + +/***EOF***/ diff --git a/server/fwknopd.8.in b/server/fwknopd.8.in index a318f686..bd349a78 100644 --- a/server/fwknopd.8.in +++ b/server/fwknopd.8.in @@ -2,12 +2,12 @@ .\" Title: fwknopd .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 06/05/2014 +.\" Date: 08/26/2014 .\" Manual: Fwknop Server .\" Source: Fwknop Server .\" Language: English .\" -.TH "FWKNOPD" "8" "06/05/2014" "Fwknop Server" "Fwknop Server" +.TH "FWKNOPD" "8" "08/26/2014" "Fwknop Server" "Fwknop Server" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -317,16 +317,6 @@ Defines the maximum age (in seconds) that an SPA packet will be accepted\&. This server system (NTP is good)\&. The default age is 120 seconds (two minutes)\&. .RE .PP -\fBACCESS_EXPIRE\fR \fI\fR -.RS 4 -Defines an expiration date for the access stanza in MM/DD/YYYY format\&. All SPA packets that match an expired stanza will be ignored\&. This parameter is optional\&. -.RE -.PP -\fBACCESS_EXPIRE_EPOCH\fR \fI\fR -.RS 4 -Defines an expiration date for the access stanza as the epoch time, and is useful if a more accurate expiration time needs to be given than the day resolution offered by the ACCESS_EXPIRE variable above\&. All SPA packets that match an expired stanza will be ignored\&. This parameter is optional\&. -.RE -.PP \fBENABLE_DIGEST_PERSISTENCE\fR \fI\fR .RS 4 Track digest sums associated with previous SPA packets processed by @@ -407,7 +397,7 @@ directory of the user running (most likely root)\&. .RE .PP -GPG_EXE* \fI\fR +\fBGPG_EXE\fR \fI\fR .RS 4 Specify the path to GPG, and defaults to \fI/usr/bin/gpg\fR @@ -551,6 +541,16 @@ Specify the digest algorithm for incoming SPA packet authentication\&. Must be o defaults to using SHA256 if the access stanza requires an HMAC\&. .RE .PP +\fBACCESS_EXPIRE\fR \fI\fR +.RS 4 +Defines an expiration date for the access stanza in MM/DD/YYYY format\&. All SPA packets that match an expired stanza will be ignored\&. This parameter is optional\&. +.RE +.PP +\fBACCESS_EXPIRE_EPOCH\fR \fI\fR +.RS 4 +Defines an expiration date for the access stanza as the epoch time, and is useful if a more accurate expiration time needs to be given than the day resolution offered by the ACCESS_EXPIRE variable above\&. All SPA packets that match an expired stanza will be ignored\&. This parameter is optional\&. +.RE +.PP \fBENABLE_CMD_EXEC\fR \fI\fR .RS 4 This instructs @@ -585,6 +585,11 @@ has to be used to automatically resolve the external address (if the client behi argument\&. .RE .PP +\fBREQUIRE_SOURCE_ADDRESS\fR \fI\fR +.RS 4 +Synonym for \(lqREQUIRE_SOURCE_ADDRESS\(rq\&. +.RE +.PP \fBFORCE_NAT\fR \fI \fR .RS 4 For any valid SPA packet, force the requested connection to be NAT\(cqd through to the specified (usually internal) IP and port value\&. This is useful if there are multiple internal systems running a service such as SSHD, and you want to give transparent access to only one internal system for each stanza in the access\&.conf file\&. This way, multiple external users can each directly access only one internal system per SPA key\&. @@ -633,6 +638,12 @@ to leverage a GnuPG key pair that does not have an associated password\&. While .RS 4 With this setting set to \fIY\fR, fwknopd check all GPG\-encrypted SPA messages for a signature (signed by the sender\(cqs key)\&. If the incoming message is not signed, the decryption process will fail\&. If not set, the default is +\fIY\fR\&. +.RE +.PP +\fBGPG_DISABLE_SIG\fR \fI\fR +.RS 4 +Disable signature verification for incoming SPA messages\&. This is not a recommended setting, and the default is \fIN\fR\&. .RE .PP @@ -646,8 +657,15 @@ Setting this will allow fwknopd to accept incoming GPG\-encrypted packets that a .RS 4 Define a list of gpg key ID\(cqs that are required to have signed any incoming SPA message that has been encrypted with the \fBfwknopd\fR -server key\&. This ensures that the verification of the remote user is accomplished via a strong cryptographic mechanism\&. This setting only applies if the \(lqGPG_REQUIRE_SIG\(rq is set to -\fIY\fR\&. Separate multiple entries with a comma\&. +server key\&. This ensures that the verification of the remote user is accomplished via a strong cryptographic mechanism\&. Signature verification is enabled by default, and can only be disabled if \(lqGPG_DISABLE_SIG\(rq is set to +\fIY\fR +(not a recommended setting)\&. Separate multiple entries with a comma\&. +.RE +.PP +\fBGPG_FINGERPRINT_ID\fR \fI\fR +.RS 4 +Specify a set of full\-length GnuPG key fingerprints instead of the shorter key identifiers set with the \(lqGPG_REMOTE_ID\(rq variable\&. Here is an example fingerprint for one of the fwknop test suite keys: +\fI00CC95F05BC146B6AC4038C9E36F443C6A3FAD56\fR\&. .RE .PP \fBGPG_HOME_DIR\fR \fI\fR diff --git a/server/fwknopd.conf b/server/fwknopd.conf index 1370b313..8faeebd6 100644 --- a/server/fwknopd.conf +++ b/server/fwknopd.conf @@ -196,6 +196,114 @@ #ENABLE_EXT_CMD_PREFIX N; #EXT_CMD_PREFIX FWKNOP_; +############################################################################## +# Parameters specific to firewalld: + +# Flush all existing rules in the fwknop chains at fwknop start time and/or +# exit time. They default to Y and it is a recommended setting for both. +# +#FLUSH_FIREWD_AT_INIT Y; +#FLUSH_FIREWD_AT_EXIT Y; +# + +# Allow SPA clients to request access to services through an firewalld +# firewall instead of just to it (i.e. access through the FWKNOP_FORWARD +# chain instead of the INPUT chain). +# +#ENABLE_FIREWD_FORWARDING N; + +# Allow SPA clients to request access to a local socket via NAT. This still +# puts an ACCEPT rule into the FWKNOP_INPUT chain, but a different port is +# translated via DNAT rules to the real one. So, the user would do +# "ssh -p " to access the local service (see the --NAT-local and +# --NAT-rand-port on the fwknop client command line). +# +#ENABLE_FIREWD_LOCAL_NAT Y; + +# By default, if forwarding access is enabled (see the ENABLE_FIREWD_FORWARDING +# variable above), then fwknop creates DNAT rules for incoming connections, +# but does not also complement these rules with SNAT rules at the same time. +# In some situations, internal systems may not have a route back out for the +# source address of the incoming connection, so it is necessary to also +# apply SNAT rules so that the internal systems see the IP of the internal +# interface where fwknopd is running. This functionality is only enabled +# when ENABLE_FIREWD_SNAT is set to "Y", and by default SNAT rules are built +# with the MASQUERADE target (since then the internal IP does not have to be +# defined here in the fwknop.conf file), but if you want fwknopd to use the +# SNAT target then also defined an IP address with the SNAT_TRANSLATE_IP +# variable. +# +#ENABLE_FIREWD_SNAT N; +#SNAT_TRANSLATE_IP __CHANGEME__; + +# Add ACCEPT rules to the FWKNOP_OUTPUT chain. This is usually only useful +# if there are no state tracking rules to allow connection responses out and +# the OUTPUT chain has a default-drop stance. +# +#ENABLE_FIREWD_OUTPUT N; + +# fwknopd adds allow rules to a custom firewalld chain "FWKNOP_INPUT". +# This chain is called from the INPUT chain, and by default no other +# firewalld chains are used. However, additional chains can be added +# (say, if access needs to be allowed through the local system via the +# FORWARD chain) by altering the FIREWD_FORWARD_ACCESS variable below. +# For a discussion of the format followed by these keywords, read on: +# +# Specify chain names to which firewalld blocking rules will be +# added with the FIREWD_INPUT_ACCESS and FIREWD_FORWARD_ACCESS keyword. +# The format for these variables is: +# +# ,,,,\ +# ,. +# +# "Target": +# Can be any legitimate firewalld target, but should usually just be "DROP". +# +# "Table": +# Can be any firewalld table, but the default is "filter". +# +# "From_chain": +# Is the chain from which packets will be jumped. +# +# "Jump_rule_position": +# Defines the position within the From_chain where the jump rule is added. +# +# "To_chain": +# Is the chain to which packets will be jumped. This is the main chain +# where fwknop rules are added. +# +# "Rule_position": +# Defines the position where rule are added within the To_chain. +# +#FIREWD_INPUT_ACCESS ACCEPT, filter, INPUT, 1, FWKNOP_INPUT, 1; + +# The FIREWD_OUTPUT_ACCESS variable is only used if ENABLE_FIREWD_OUTPUT is enabled +# +#FIREWD_OUTPUT_ACCESS ACCEPT, filter, OUTPUT, 1, FWKNOP_OUTPUT, 1; + +# The FIREWD_FORWARD_ACCESS variable is only used if ENABLE_FIREWD_FORWARDING is +# enabled. +# +#FIREWD_FORWARD_ACCESS ACCEPT, filter, FORWARD, 1, FWKNOP_FORWARD, 1; +#FIREWD_DNAT_ACCESS DNAT, nat, PREROUTING, 1, FWKNOP_PREROUTING, 1; + +# The FIREWD_SNAT_ACCESS variable is not used unless both ENABLE_FIREWD_SNAT and +# ENABLE_FIREWD_FORWARDING are enabled. Also, the external static IP must be +# set with the SNAT_TRANSLATE_IP variable. The default is to use the +# FIREWD_MASQUERADE_ACCESS variable. +# +#FIREWD_SNAT_ACCESS SNAT, nat, POSTROUTING, 1, FWKNOP_POSTROUTING, 1; +#FIREWD_MASQUERADE_ACCESS MASQUERADE, nat, POSTROUTING, 1, FWKNOP_POSTROUTING, 1; + +# The ENABLE_COMMENT_MATCH_CHECK variable instructs fwknopd to check for the +# firewalld 'comment' match at start up. If it's not found, then fwknopd will +# exit and throw an error. This variable is enabled by default, but can be +# disabled if you want fwknopd to run without being sure that the comment match +# if available (not recommended, since the comment match enables new SPA rules +# to be timed out). +# +#ENABLE_FIREWD_COMMENT_CHECK Y; + ############################################################################## # Parameters specific to iptables: @@ -372,6 +480,7 @@ # #PF_EXPIRE_INTERVAL 30; +############################################################################## # Directories - These can override compile-time defaults. # @@ -389,6 +498,7 @@ # System binaries # +#FIREWALL_EXE /bin/firewall-cmd; #FIREWALL_EXE /sbin/iptables; ###EOF### diff --git a/server/fwknopd_common.h b/server/fwknopd_common.h index 32738998..5622d36d 100644 --- a/server/fwknopd_common.h +++ b/server/fwknopd_common.h @@ -115,9 +115,29 @@ #define RCHK_MAX_PCAP_DISPATCH_COUNT (2 << 22) #define RCHK_MAX_FW_TIMEOUT (2 << 22) +/* FirewallD-specific defines +*/ +#if FIREWALL_FIREWALLD + + #define DEF_FLUSH_FIREWD_AT_INIT "Y" + #define DEF_FLUSH_FIREWD_AT_EXIT "Y" + #define DEF_ENABLE_FIREWD_FORWARDING "N" + #define DEF_ENABLE_FIREWD_LOCAL_NAT "Y" + #define DEF_ENABLE_FIREWD_SNAT "N" + #define DEF_ENABLE_FIREWD_OUTPUT "N" + #define DEF_ENABLE_FIREWD_COMMENT_CHECK "Y" + #define DEF_FIREWD_INPUT_ACCESS "ACCEPT, filter, INPUT, 1, FWKNOP_INPUT, 1" + #define DEF_FIREWD_OUTPUT_ACCESS "ACCEPT, filter, OUTPUT, 1, FWKNOP_OUTPUT, 1" + #define DEF_FIREWD_FORWARD_ACCESS "ACCEPT, filter, FORWARD, 1, FWKNOP_FORWARD, 1" + #define DEF_FIREWD_DNAT_ACCESS "DNAT, nat, PREROUTING, 1, FWKNOP_PREROUTING, 1" + #define DEF_FIREWD_SNAT_ACCESS "SNAT, nat, POSTROUTING, 1, FWKNOP_POSTROUTING, 1" + #define DEF_FIREWD_MASQUERADE_ACCESS "MASQUERADE, nat, POSTROUTING, 1, FWKNOP_POSTROUTING, 1" + + #define RCHK_MAX_FIREWD_RULE_NUM (2 << 15) + /* Iptables-specific defines */ -#if FIREWALL_IPTABLES +#elif FIREWALL_IPTABLES #define DEF_FLUSH_IPT_AT_INIT "Y" #define DEF_FLUSH_IPT_AT_EXIT "Y" @@ -215,7 +235,22 @@ enum { //CONF_EXTERNAL_CMD_ALARM, //CONF_ENABLE_EXT_CMD_PREFIX, //CONF_EXT_CMD_PREFIX, -#if FIREWALL_IPTABLES +#if FIREWALL_FIREWALLD + CONF_ENABLE_FIREWD_FORWARDING, + CONF_ENABLE_FIREWD_LOCAL_NAT, + CONF_ENABLE_FIREWD_SNAT, + CONF_SNAT_TRANSLATE_IP, + CONF_ENABLE_FIREWD_OUTPUT, + CONF_FLUSH_FIREWD_AT_INIT, + CONF_FLUSH_FIREWD_AT_EXIT, + CONF_FIREWD_INPUT_ACCESS, + CONF_FIREWD_OUTPUT_ACCESS, + CONF_FIREWD_FORWARD_ACCESS, + CONF_FIREWD_DNAT_ACCESS, + CONF_FIREWD_SNAT_ACCESS, + CONF_FIREWD_MASQUERADE_ACCESS, + CONF_ENABLE_FIREWD_COMMENT_CHECK, +#elif FIREWALL_IPTABLES CONF_ENABLE_IPT_FORWARDING, CONF_ENABLE_IPT_LOCAL_NAT, CONF_ENABLE_IPT_SNAT, @@ -352,7 +387,54 @@ typedef struct acc_stanza /* Firewall-related data and types. */ -#if FIREWALL_IPTABLES +#if FIREWALL_FIREWALLD + /* --DSS XXX: These are arbitrary. We should determine appropriate values. + */ + #define MAX_TABLE_NAME_LEN 64 + #define MAX_CHAIN_NAME_LEN 64 + #define MAX_TARGET_NAME_LEN 64 + + /* Fwknop custom chain types + */ + enum { + FIREWD_INPUT_ACCESS, + FIREWD_OUTPUT_ACCESS, + FIREWD_FORWARD_ACCESS, + FIREWD_DNAT_ACCESS, + FIREWD_SNAT_ACCESS, + FIREWD_MASQUERADE_ACCESS, + NUM_FWKNOP_ACCESS_TYPES /* Leave this entry last */ + }; + + /* Structure to define an fwknop firewall chain configuration. + */ + struct fw_chain { + int type; + char target[MAX_TARGET_NAME_LEN]; + //int direction; + char table[MAX_TABLE_NAME_LEN]; + char from_chain[MAX_CHAIN_NAME_LEN]; + int jump_rule_pos; + char to_chain[MAX_CHAIN_NAME_LEN]; + int rule_pos; + int active_rules; + time_t next_expire; + }; + + /* Based on the fw_chain fields (not counting type) + */ + #define FW_NUM_CHAIN_FIELDS 6 + + struct fw_config { + struct fw_chain chain[NUM_FWKNOP_ACCESS_TYPES]; + char fw_command[MAX_PATH_LEN]; + + /* Flag for firewalld SNAT vs. MASQUERADE usage + */ + unsigned char use_masquerade; + }; + +#elif FIREWALL_IPTABLES /* --DSS XXX: These are arbitrary. We should determine appropriate values. */ #define MAX_TABLE_NAME_LEN 64 @@ -484,6 +566,7 @@ typedef struct fko_srv_options unsigned char verbose; /* Verbose mode flag */ unsigned char exit_after_parse_config; /* Parse config and exit */ + unsigned char firewd_disable_check_support; /* Don't use firewall-cmd ... -C */ unsigned char ipt_disable_check_support; /* Don't use iptables -C */ /* Flag for permitting SPA packets regardless of directionality test diff --git a/server/incoming_spa.c b/server/incoming_spa.c index d21cdb6c..bbea1b1f 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -815,7 +815,17 @@ incoming_spa(fko_srv_options_t *opts) || spadat.message_type == FKO_NAT_ACCESS_MSG || spadat.message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG) { -#if FIREWALL_IPTABLES +#if FIREWALL_FIREWALLD + if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_FORWARDING], "Y", 1)!=0) + { + log_msg(LOG_WARNING, + "(stanza #%d) SPA packet from %s requested NAT access, but is not enabled", + stanza_num, spadat.pkt_source_ip + ); + acc = acc->next; + continue; + } +#elif FIREWALL_IPTABLES if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)!=0) { log_msg(LOG_WARNING, diff --git a/win32/config.h b/win32/config.h index 3fda1a18..86b6a9ff 100644 --- a/win32/config.h +++ b/win32/config.h @@ -178,7 +178,7 @@ #undef TIME_WITH_SYS_TIME /* Version number of package */ -#define VERSION "2.0" +#define VERSION "2.6.3" /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */