For further explanation see: http://wiki.ipfire.org/devel/network/security-policies
Signed-off-by: Jonatan Schlag jonatan.schlag@ipfire.org --- src/functions/functions.vpn-security-policies | 437 ++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 src/functions/functions.vpn-security-policies
diff --git a/src/functions/functions.vpn-security-policies b/src/functions/functions.vpn-security-policies new file mode 100644 index 0000000..9e24e4f --- /dev/null +++ b/src/functions/functions.vpn-security-policies @@ -0,0 +1,437 @@ +#!/bin/bash +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2013 IPFire Network Development Team # +# # +# 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 3 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, see http://www.gnu.org/licenses/. # +# # +############################################################################### + +VPN_SECURITY_POLICIES_CONFIG_SETTINGS="CIPHER COMPRESSION GROUP_TYPE INTEGRITY KEY_EXCHANGE LIFETIME PFS" +VPN_SECURITY_POLICIES_READONLY="system" + +VPN_SUPPORTED_CIPHERS="AES192 AES256 AES128" +VPN_SUPPORTED_INTEGRITY="SHA512 SHA256 SHA128" +VPN_SUPPORTED_GROUP_TYPES="MODP4096 MODP8192" + +vpn_security_policies_check_readonly() { + # This functions checks if a policy is readonly + # returns true when yes and false when no + + if isoneof name ${VPN_SECURITY_POLICIES_READONLY}; then + return ${EXIT_TRUE} + else + return ${EXIT_FALSE} + fi +} + +vpn_security_policies_write_config() { + # This function writes all values to a via ${name} specificated vpn security policy configuration file + assert [ $# -ge 1 ] + + local name="${1}" + + if ! vpn_security_policy_exists ${name}; then + log ERROR "No such policy: ${name}" + return ${EXIT_ERROR} + fi + + if vpn_security_policies_check_readonly ${name}; then + log ERROR "${name} is a readable only vpn security policy" + return ${EXIT_ERROR} + fi + + local args + list_append args ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS} + + local path="$(vpn_security_policies_path ${name})" + if [ ! -w ${path} ]; then + log ERROR "${path} is not writeable" + return ${EXIT_ERROR} + fi + + settings_write "${path}" ${args} + # TODO everytime we successfully write a config we should call some trigger to take the changes into effect +} + +vpn_security_policies_write_config_key() { + # This funtion writes the value for one key to a via ${name} specificated vpn security policy configuration file + assert [ $# -ge 3 ] + local name=${1} + + if ! vpn_security_policy_exists ${name}; then + log ERROR "No such policy: ${name}" + return ${EXIT_ERROR} + fi + + local key=${2} + shift 2 + local value="$@" + log DEBUG "Set '${key}' to new value '${value}'" + + # Read the config settings + if ! vpn_security_policies_read_config ${name}; then + return ${EXIT_ERROR} + fi + + # Set the key to a new value + eval "${key}="${value}"" + + if ! vpn_security_policies_write_config ${name}; then + return ${EXIT_ERROR} + fi + + return ${EXIT_TRUE} + +} + +vpn_security_policies_read_config() { + # Reads one or more keys out of a settings file or all if no key is provided. + assert [ $# -ge 1 ] + + local name="${1}" + shift 1 + + if ! vpn_security_policy_exists ${name}; then + log ERROR "No such policy: ${name}" + return ${EXIT_ERROR} + fi + + + local args + if [ $# -eq 0 ] && [ -n "${VPN_SECURITY_POLICIES_CONFIG_SETTINGS}" ]; then + list_append args ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS} + else + list_append args $@ + fi + + local path="$(vpn_security_policies_path ${name})" + if [ ! -r ${path} ]; then + log ERROR "${path} is not readable" + return ${EXIT_ERROR} + fi + + settings_read "${path}" ${args} +} + +vpn_security_policies_path() { + # Returns the path to a the configuration fora given name + assert [ $# -eq 1 ] + local name=${1} + + if vpn_security_policies_check_readonly ${name}; then + echo "/usr/share/network/vpn/security-policies/${name}" + else + echo "/etc/network/vpn/security-policies/${name}" + fi +} + +vpn_security_policies_show() { + # Print the content of a vpn security policy configuration file in a nice way + assert [ $# -eq 1 ] + local name=${1} + + # Break if read fails + if ! vpn_security_policies_read_config ${name}; then + return ${EXIT_ERROR} + fi + + cli_print_fmt1 0 "Security Policy: ${name}" + cli_space + + # This could be done in a loop but a loop is much more complicated + # because we print 'Group Types' but the variable is named 'GROUP_TYPES' + cli_print_fmt1 1 "Ciphers:" + cli_print_fmt1 2 "${CIPHER}" + cli_space + cli_print_fmt1 1 "Integrity:" + cli_print_fmt1 2 "${INTEGRITY}" + cli_space + cli_print_fmt1 1 "Group Types:" + cli_print_fmt1 2 "${GROUP_TYPE}" + cli_space + + cli_print_fmt1 1 "Key exchange:" "${KEY_EXCHANGE}" + cli_print_fmt1 1 "Key Lifetime in seconds:" "${LIFETIME}" + if enabled PFS; then + cli_print_fmt1 1 "Perfect Forward Secrecy:" "enabled" + else + cli_print_fmt1 1 "Perfect Forward Secrecy:" "disabled" + fi + cli_space + if enabled COMPRESSION; then + cli_print_fmt1 1 "Compression:" "enabled" + else + cli_print_fmt1 1 "Compression:" "disabled" + fi + cli_space +} + +vpn_security_policy_exists() { + # This function checks if a vpn security policy exists + # Returns True when yes and false when not + assert [ $# -eq 1 ] + local name=${1} + + local path=$(vpn_security_policies_path ${name}) + [ -f ${path} ] +} + + +vpn_security_policies_cipher(){ + # This function parses the parameters for the 'cipher' command + local name=${1} + shift + + if [ $# -eq 0 ]; then + log ERROR "You must pass at least one value after cipher" + return ${EXIT_ERROR} + fi + + if ! vpn_security_policies_read_config ${name} "CIPHER"; then + return ${EXIT_ERROR} + fi + + # Remove duplicated entries to proceed the list safely + CIPHER="$(list_unique ${CIPHER})" + + while [ $# -gt 0 ]; do + case "${1}" in + -*) + value=${1#-} + # Check if the cipher is in the list of ciphers and + # check if the list has after removing this cipher at least one valid value + if list_match ${value} ${CIPHER} && [ $(list_length ${CIPHER}) -gt 1 ]; then + list_remove CIPHER ${value} + else + log ERROR "Can not remove ${value} from the list of Ciphers because ${value} is not in the list or the list would be empty after removing this cipher" + fi + ;; + +*) + value=${1#+} + # Check if the Ciphers is in the list of supported ciphers. + if ! isoneof value ${VPN_SUPPORTED_CIPHERS}; then + log ERROR "${value} is not a supported cipher and can thats why not added to the list of ciphers" + else + if list_match ${value} ${CIPHER}; then + log WARNING "${value} is already in the list of ciphers of this policy" + else + list_append CIPHER ${value} + fi + fi + ;; + esac + shift + done + + vpn_security_policies_write_config_key ${name} "CIPHER" ${CIPHER} +} + +vpn_security_policies_compression(){ + # This function parses the parameters for the 'compression' command + local name=${1} + local value=${2} + # Check if we get only one argument after compression <name> + if [ ! $# -eq 2 ] || ! isbool value; then + log ERROR "You can pass only one parameter after compression and this one must be 'on' or 'off'" + return ${EXIT_ERROR} + fi + + vpn_security_policies_write_config_key "${name}" "COMPRESSION" "${value}" +} + +vpn_security_policies_group_type(){ + # This function parses the parameters for the 'group-type' command. + local name=${1} + shift + + if [ $# -eq 0 ]; then + log ERROR "You must pass at least one value after group-type" + return ${EXIT_ERROR} + fi + + if ! vpn_security_policies_read_config ${name} "GROUP_TYPE"; then + return ${EXIT_ERROR} + fi + + # Remove duplicated entries to proceed the list safely + GROUP_TYPE="$(list_unique ${GROUP_TYPE})" + + while [ $# -gt 0 ]; do + case "${1}" in + -*) + value=${1#-} + # Check if the group type is in the list of group types and + # check if the list has after removing this group type at leatst one valid value + if list_match ${value} ${GROUP_TYPE} && [ $(list_length ${GROUP_TYPE}) -gt 1 ]; then + list_remove GROUP_TYPE ${value} + else + log ERROR "Can not remove ${value} from the list of group types because ${value} is not in the list or the list would be empty after removing this group type" + fi + ;; + +*) + value=${1#+} + # Check if the group type is in the list of supported group types. + if ! isoneof value ${VPN_SUPPORTED_GROUP_TYPES}; then + log ERROR "${value} is not a supported group type and can thats why not added to the list of group types" + else + if list_match ${value} ${GROUP_TYPE}; then + log WARNING "${value} is already in the list of group-types of this policy" + else + list_append GROUP_TYPES ${value} + fi + fi + ;; + esac + shift + done + + vpn_security_policies_write_config_key ${name} "GROUP_TYPE" ${GROUP_TYPE} +} + +vpn_security_policies_integrity(){ + # This function parses the parameters for the 'integrity' command + local name=${1} + shift + + if [ $# -eq 0 ]; then + log ERROR "You must pass at least one value after integrity" + return ${EXIT_ERROR} + fi + + if ! vpn_security_policies_read_config ${name} "INTEGRITY"; then + return ${EXIT_ERROR} + fi + + # Remove duplicated entries to proceed the list safely + INTEGRITY="$(list_unique ${INTEGRITY})" + + while [ $# -gt 0 ]; do + case "${1}" in + -*) + value=${1#-} + # Check if the integrity hash is in the list of integrity hashes and + # check if the list has after removing this integrity hash at least one valid value + if list_match ${value} ${INTEGRITY} && [ $(list_length ${INTEGRITY}) -gt 1 ]; then + list_remove INTEGRITY ${value} + else + log ERROR "Can not remove ${value} from the list of integrity hashes because ${value} is not in the list or the list would be empty after removing this integrity hash" + fi + ;; + +*) + value=${1#+} + # Check if the Ciphers is in the list of supported integrity hashes. + if ! isoneof value ${VPN_SUPPORTED_INTEGRITY}; then + log ERROR "${value} is not a supported integrity hashes and can thats why not added to the list of integrity hashes" + else + if list_match ${value} ${INTEGRITY}; then + log WARNING "${value} is already in the list of integrety hashes of this policy" + else + list_append INTEGRITY ${value} + fi + fi + ;; + esac + shift + done + + vpn_security_policies_write_config_key ${name} "INTEGRITY" ${INTEGRITY} +} + +vpn_security_policies_key_exchange() { + # This function parses the parameters for the 'key-exchange' command + local name=${1} + local value=${2} + # Check if we get only one argument after compression <name> + if [ ! $# -eq 2 ] || ! isoneof value "ikev1" "ikev2"; then + log ERROR "You can pass only one parameter after key-exchange and this one must be 'ikev1' or 'ikev2'" + return ${EXIT_ERROR} + fi + + vpn_security_policies_write_config_key "${name}" "KEY_EXCHANGE" "${value}" +} + +vpn_security_policies_lifetime(){ + # This function parses the parameters for the 'lifetime' command + local name=${1} + local value=${2} + # Check if we get only one argument after compression <name> + if [ ! $# -eq 2 ] || ! isinteger value || [ ${value} -le 0 ]; then + log ERROR "You can pass only one parameter after liftime and this one must be an valid integer greater zero" + return ${EXIT_ERROR} + fi + + vpn_security_policies_write_config_key "${name}" "LIFETIME" "${value}" +} + +vpn_security_policies_pfs(){ + # This function parses the parameters for the 'pfs' command + local name=${1} + local value=${2} + # Check if we get only one argument after compression <name> + if [ ! $# -eq 2 ] || ! isbool value; then + log ERROR "You can pass only one parameter after pfs and this one must be 'on' or 'off'" + return ${EXIT_ERROR} + fi + + vpn_security_policies_write_config_key "${name}" "PFS" "${value}" +} + +vpn_security_policies_check_name() { + # This function checks if a vpn security policy name is valid + # Allowed are only A-Za-z0-9 + assert [ $# -eq 1 ] + local name=${1} + [[ ${name} =~ [[:alnum:]] ]] +} + +vpn_security_policies_new() { + # Function that creates based on the paramters one ore more new vpn security policies + local name + for name in $@; do + if vpn_security_policy_exists ${name}; then + log ERROR "The vpn security policy ${name} does already exist" + continue + fi + + if vpn_security_policies_check_name ${name}; then + log ERROR "'${name}' contains illegal characters. Allowed are only A-Za-z0-9 are allowed" + continue + fi + + log DEBUG "Creating vpn security policy ${name}" + cp "$(vpn_security_policies_path "system")" "$(vpn_security_policies_path ${name})" + done + +} + +vpn_security_policies_destroy() { + # Function that deletes based on the passed parameters one ore more vpn security policies + local name + for name in $@; do + if ! vpn_security_policy_exists ${name}; then + log ERROR "The vpn security policy ${name} does not exist" + continue + fi + + if vpn_security_policies_check_readonly ${name}; then + log ERROR "The vpn security policy ${name} is readonly and can thats why not deleted" + continue + fi + + log DEBUG "Deleting vpn security policy ${name}" + rm -f $(vpn_security_policies_path ${name}) + done +}