#!/bin/bash ############################################################################### # # # IPFire.org - A linux based firewall # # Copyright (C) 2017 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 . # # # ############################################################################### VPN_SECURITY_POLICIES_CONFIG_SETTINGS="CIPHER COMPRESSION GROUP_TYPE INTEGRITY KEY_EXCHANGE LIFETIME PFS" VPN_SECURITY_POLICIES_READONLY="system" VPN_DEFAULT_SECURITY_POLICY="system" VPN_SUPPORTED_CIPHERS="AES192 AES256 AES512" VPN_SUPPORTED_INTEGRITY="SHA512 SHA256 SHA128" VPN_SUPPORTED_GROUP_TYPES="MODP8192 MODP4096" # This functions checks if a policy is readonly # returns true when yes and false when no vpn_security_policies_check_readonly() { if isoneof name ${VPN_SECURITY_POLICIES_READONLY}; then return ${EXIT_TRUE} else return ${EXIT_FALSE} fi } # This function writes all values to a via ${name} specificated vpn security policy configuration file vpn_security_policies_write_config() { assert [ $# -ge 1 ] local name="${1}" if ! vpn_security_policy_exists "${name}"; then log ERROR "No such vpn security policy: ${name}" return ${EXIT_ERROR} fi if vpn_security_policies_check_readonly "${name}"; then log ERROR "The ${name} vpn security policy cannot be changed." return ${EXIT_ERROR} fi local path="$(vpn_security_policies_path "${name}")" if [ ! -w ${path} ]; then log ERROR "${path} is not writeable" return ${EXIT_ERROR} fi if ! settings_write "${path}" ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS}; then log ERROR "Could not write configuration settings for vpn security policy ${name}" return ${EXIT_ERROR} fi # TODO everytime we successfully write a config we should call some trigger to take the changes into effect } # This funtion writes the value for one key to a via ${name} specificated vpn security policy configuration file vpn_security_policies_write_config_key() { assert [ $# -ge 3 ] local name=${1} local key=${2} shift 2 local value="$@" if ! vpn_security_policy_exists "${name}"; then log ERROR "No such vpn security policy: ${name}" return ${EXIT_ERROR} fi log DEBUG "Set '${key}' to new value '${value}' in vpn security policy ${name}" local ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS} # Read the config settings if ! vpn_security_policies_read_config "${name}"; then return ${EXIT_ERROR} fi # Set the key to a new value assign "${key}" "${value}" if ! vpn_security_policies_write_config "${name}"; then return ${EXIT_ERROR} fi return ${EXIT_TRUE} } # Reads one or more keys out of a settings file or all if no key is provided. vpn_security_policies_read_config() { assert [ $# -ge 1 ] local name="${1}" shift 1 if ! vpn_security_policy_exists "${name}"; then log ERROR "No such vpn security 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 ! settings_read "${path}" ${args}; then log ERROR "Could not read settings for vpn security policy ${name}" return ${EXIT_ERROR} fi } # Returns the path to a the configuration fora given name vpn_security_policies_path() { assert [ $# -eq 1 ] local name=${1} if vpn_security_policies_check_readonly "${name}"; then echo "${NETWORK_SHARE_DIR}/vpn/security-policies/${name}" else echo "${NETWORK_CONFIG_DIR}/vpn/security-policies/${name}" fi } # Print the content of a vpn security policy configuration file in a nice way vpn_security_policies_show() { assert [ $# -eq 1 ] local name=${1} local ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS} 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}" # Key Lifetime if isinteger LIFETIME && [ ${LIFETIME} -gt 0 ]; then cli_print_fmt1 1 "Key Lifetime:" "$(format_time ${LIFETIME})" else log ERROR "The value for Key Lifetime is not a valid integer greater zero." fi # PFS if enabled PFS; then cli_print_fmt1 1 "Perfect Forward Secrecy:" "enabled" else cli_print_fmt1 1 "Perfect Forward Secrecy:" "disabled" fi cli_space # Compression if enabled COMPRESSION; then cli_print_fmt1 1 "Compression:" "enabled" else cli_print_fmt1 1 "Compression:" "disabled" fi cli_space } # This function checks if a vpn security policy exists # Returns True when yes and false when not vpn_security_policy_exists() { assert [ $# -eq 1 ] local name=${1} local path=$(vpn_security_policies_path "${name}") [ -f ${path} ] && return ${EXIT_TRUE} || return ${EXIT_FALSE} } # This function parses the parameters for the 'cipher' command vpn_security_policies_cipher(){ local name=${1} shift if [ $# -eq 0 ]; then log ERROR "You must pass at least one value after cipher" return ${EXIT_ERROR} fi local CIPHER 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}; then list_remove CIPHER ${value} else # We do not break here because this error does not break the processing of the next maybe valid values. log ERROR "Can not remove ${value} from the list of Ciphers because ${value} is not in the list." fi ;; +*) value=${1#+} # Check if the Ciphers is in the list of supported ciphers. if ! isoneof value ${VPN_SUPPORTED_CIPHERS}; then # We do not break here because this error does not break the processing of the next maybe valid values. 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 # Check if the list contain at least one valid cipher if [ $(list_length ${CIPHER}) -ge 1 ]; then if ! vpn_security_policies_write_config_key ${name} "CIPHER" ${CIPHER}; then log ERROR "The changes for the vpn security policy ${name} could not be written." fi else log ERROR "After proceding all ciphers the list is empty and thats why no changes are written." return ${EXIT_ERROR} fi } # This function parses the parameters for the 'compression' command vpn_security_policies_compression(){ local name=${1} local value=${2} # Check if we get only one argument after compression if [ ! $# -eq 2 ]; then log ERROR "The number of arguments do not match. Only one argument after compression is allowed." return ${EXIT_ERROR} fi if ! isbool value; then # We suggest only two values to avoid overburding the user. log ERROR "Invalid Argument ${value}" return ${EXIT_ERROR} fi vpn_security_policies_write_config_key "${name}" "COMPRESSION" "${value}" } # This function parses the parameters for the 'group-type' command vpn_security_policies_group_type(){ local name=${1} shift if [ $# -eq 0 ]; then log ERROR "You must pass at least one value after group-type" return ${EXIT_ERROR} fi local GROUP_TYPE 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}; then list_remove GROUP_TYPE ${value} else # We do not break here because this error does not break the processing of the next maybe valid values. log ERROR "Can not remove ${value} from the list of group types because ${value} is not in the list." fi ;; +*) value=${1#+} # Check if the group type is in the list of supported group types. if ! isoneof value ${VPN_SUPPORTED_GROUP_TYPES}; then # We do not break here because the processing of other maybe valid values are indepent from this error. 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_TYPE ${value} fi fi ;; esac shift done # Check if the list contain at least one valid group-type if [ $(list_length ${GROUP_TYPE}) -ge 1 ]; then if ! vpn_security_policies_write_config_key ${name} "GROUP_TYPE" ${GROUP_TYPE}; then log ERROR "The changes for the vpn security policy ${name} could not be written." fi else log ERROR "After proceding all group types the list is empty and thats why no changes are written." return ${EXIT_ERROR} fi } # This function parses the parameters for the 'integrity' command vpn_security_policies_integrity(){ local name=${1} shift if [ $# -eq 0 ]; then log ERROR "You must pass at least one value after integrity." return ${EXIT_ERROR} fi local INTEGRITY 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}; then list_remove INTEGRITY ${value} else # We do not break here because the processing of other maybe valid values are indepent from this error. log ERROR "Can not remove ${value} from the list of integrity hashes because ${value} is not in the list." fi ;; +*) value=${1#+} # Check if the Ciphers is in the list of supported integrity hashes. if ! isoneof value ${VPN_SUPPORTED_INTEGRITY}; then # We do not break here because the processing of other maybe valid values are indepent from this error. log ERROR "${value} is not a supported integrity hash 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 # Check if the list contain at least one valid group-type if [ $(list_length ${INTEGRITY}) -ge 1 ]; then if ! vpn_security_policies_write_config_key ${name} "INTEGRITY" ${INTEGRITY}; then log ERROR "The changes for the vpn security policy ${name} could not be written." fi else log ERROR "After proceding all integrity hashes the list is empty and thats why no changes are written." return ${EXIT_ERROR} fi } # This function parses the parameters for the 'key-exchange' command vpn_security_policies_key_exchange() { local name=${1} local value=${2} # Check if we get only one argument after key-exchange if [ ! $# -eq 2 ]; then log ERROR "The number of arguments do not match. Only argument after key-exchange is allowed." return ${EXIT_ERROR} fi if ! isoneof value "ikev1" "ikev2" "IKEV1" "IKEV2"; then log ERROR "Invalid Argument ${value}" return ${EXIT_ERROR} fi vpn_security_policies_write_config_key "${name}" "KEY_EXCHANGE" "${value,,}" } # This function parses the parameters for the 'lifetime' command. vpn_security_policies_lifetime(){ local name=${1} shift local value=$@ # Check if we get only one argument after lifetime if [ ! $# -ge 1 ]; then log ERROR "The number of arguments do not match you must provide at least one integer value or a valid time with the format h m s" return ${EXIT_ERROR} fi if ! isinteger value; then value=$(parse_time $@) if [ ! $? -eq 0 ]; then log ERROR "Parsing the passed time was not sucessful please check the passed values." return ${EXIT_ERROR} fi fi if [ ${value} -le 0 ]; then log ERROR "The passed time value must be in the sum greater zero seconds." return ${EXIT_ERROR} fi vpn_security_policies_write_config_key "${name}" "LIFETIME" "${value}" } # This function parses the parameters for the 'pfs' command vpn_security_policies_pfs(){ local name=${1} local value=${2} # Check if we get only one argument after pfs if [ ! $# -eq 2 ]; then log ERROR "The number of arguments do not match. Only argument after pfs is allowed." return ${EXIT_ERROR} fi if [ ! $# -eq 2 ] || ! isbool value; then # We suggest only two values to avoid overburding the user. log ERROR "Invalid Argument ${value}" return ${EXIT_ERROR} fi vpn_security_policies_write_config_key "${name}" "PFS" "${value}" } # This function checks if a vpn security policy name is valid # Allowed are only A-Za-z0-9 vpn_security_policies_check_name() { assert [ $# -eq 1 ] local name=${1} [[ ${name} =~ [^[:alnum:]$] ]] } # Function that creates based on the paramters one ore more new vpn security policies vpn_security_policies_new() { if [ $# -gt 1 ]; then error "Too many arguments" return ${EXIT_ERROR} fi local name="${1}" if ! isset name; then error "Please provide a name" return ${EXIT_ERROR} fi # Check for duplicates if vpn_security_policy_exists "${name}"; then error "The VPN security policy with name ${name} already exists" return ${EXIT_ERROR} fi # Check if name is valid if vpn_security_policies_check_name "${name}"; then error "'${name}' contains illegal characters" return ${EXIT_ERROR} fi # Check if we have a read-only policy with the same name if vpn_security_policies_check_readonly "${name}"; then error "The VPN security policy ${name} is read-only" return ${EXIT_ERROR} fi # Check if our source policy exists if ! vpn_security_policy_exists "${VPN_DEFAULT_SECURITY_POLICY}"; then error "Default VPN Security Policy '${VPN_DEFAULT_SECURITY_POLICY}' does not exist" return ${EXIT_ERROR} fi log DEBUG "Creating VPN Security Policy ${name}" if copy "$(vpn_security_policies_path "${VPN_DEFAULT_SECURITY_POLICY}")" "$(vpn_security_policies_path ${name})"; then log INFO "VPN Security Policy ${name} successfully created" else log ERROR "Could not create VPN Security Policy ${name}" return ${EXIT_ERROR} fi # Show the newly created policy vpn_security_policies_show "${name}" } # Function that deletes based on the passed parameters one ore more vpn security policies vpn_security_policies_destroy() { 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} cannot be deleted." continue fi log DEBUG "Deleting vpn security policy ${name}" settings_remove $(vpn_security_policies_path ${name}) done }