#!/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 performance" VPN_DEFAULT_SECURITY_POLICY="system" declare -A VPN_SUPPORTED_CIPHERS=( # 3DES-CBC [3DES-CBC]="168 bit 3DES-EDE-CBC" # AES-CBC [AES256-CBC]="256 bit AES-CBC" [AES192-CBC]="192 bit AES-CBC" [AES128-CBC]="128 bit AES-CBC" # AES-CTR [AES256-CTR]="256 bit AES-COUNTER" [AES192-CTR]="192 bit AES-COUNTER" [AES128-CTR]="128 bit AES-COUNTER" # AES-GCM [AES256-GCM128]="256 bit AES-GCM with 128 bit ICV" [AES192-GCM128]="192 bit AES-GCM with 128 bit ICV" [AES128-GCM128]="128 bit AES-GCM with 128 bit ICV" [AES256-GCM96]="256 bit AES-GCM with 96 bit ICV" [AES192-GCM96]="192 bit AES-GCM with 96 bit ICV" [AES128-GCM96]="128 bit AES-GCM with 96 bit ICV" [AES256-GCM64]="256 bit AES-GCM with 64 bit ICV" [AES192-GCM64]="192 bit AES-GCM with 64 bit ICV" [AES128-GCM64]="128 bit AES-GCM with 64 bit ICV" # AES-CCM [AES256-CCM128]="256 bit AES-CCM with 128 bit ICV" [AES192-CCM128]="192 bit AES-CCM with 128 bit ICV" [AES128-CCM128]="128 bit AES-CCM with 128 bit ICV" [AES256-CCM96]="256 bit AES-CCM with 96 bit ICV" [AES192-CCM96]="192 bit AES-CCM with 96 bit ICV" [AES128-CCM96]="128 bit AES-CCM with 96 bit ICV" [AES256-CCM64]="256 bit AES-CCM with 64 bit ICV" [AES192-CCM64]="192 bit AES-CCM with 64 bit ICV" [AES128-CCM64]="128 bit AES-CCM with 64 bit ICV" # CAMELLIA-CBC [CAMELLIA256-CBC]="256 bit CAMELLIA-CBC" [CAMELLIA192-CBC]="192 bit CAMELLIA-CBC" [CAMELLIA128-CBC]="128 bit CAMELLIA-CBC" # CAMELLIA-CTR [CAMELLIA256-CTR]="256 bit CAMELLIA-COUNTER" [CAMELLIA192-CTR]="192 bit CAMELLIA-COUNTER" [CAMELLIA128-CTR]="128 bit CAMELLIA-COUNTER" # CAMELLIA-GCM [CAMELLIA256-GCM128]="256 bit CAMELLIA-GCM with 128 bit ICV" [CAMELLIA192-GCM128]="192 bit CAMELLIA-GCM with 128 bit ICV" [CAMELLIA128-GCM128]="128 bit CAMELLIA-GCM with 128 bit ICV" [CAMELLIA256-GCM96]="256 bit CAMELLIA-GCM with 96 bit ICV" [CAMELLIA192-GCM96]="192 bit CAMELLIA-GCM with 96 bit ICV" [CAMELLIA128-GCM96]="128 bit CAMELLIA-GCM with 96 bit ICV" [CAMELLIA256-GCM64]="256 bit CAMELLIA-GCM with 64 bit ICV" [CAMELLIA192-GCM64]="192 bit CAMELLIA-GCM with 64 bit ICV" [CAMELLIA128-GCM64]="128 bit CAMELLIA-GCM with 64 bit ICV" # CAMELLIA-CCM [CAMELLIA256-CCM128]="256 bit CAMELLIA-CCM with 128 bit ICV" [CAMELLIA192-CCM128]="192 bit CAMELLIA-CCM with 128 bit ICV" [CAMELLIA128-CCM128]="128 bit CAMELLIA-CCM with 128 bit ICV" [CAMELLIA256-CCM96]="256 bit CAMELLIA-CCM with 96 bit ICV" [CAMELLIA192-CCM96]="192 bit CAMELLIA-CCM with 96 bit ICV" [CAMELLIA128-CCM96]="128 bit CAMELLIA-CCM with 96 bit ICV" [CAMELLIA256-CCM64]="256 bit CAMELLIA-CCM with 64 bit ICV" [CAMELLIA192-CCM64]="192 bit CAMELLIA-CCM with 64 bit ICV" [CAMELLIA128-CCM64]="128 bit CAMELLIA-CCM with 64 bit ICV" # No Encryption [NULL]="No Encryption" ) declare -A CIPHER_TO_STRONGSWAN=( # 3DES-CBC [3DES-CBC]="3des" # AES-CBC [AES256-CBC]="aes256" [AES192-CBC]="aes192" [AES128-CBC]="aes128" # AES-CTR [AES256-CTR]="aes256ctr" [AES192-CTR]="aes192ctr" [AES128-CTR]="aes128ctr" # AES-GCM [AES256-GCM128]="aes256gcm128" [AES192-GCM128]="aes192gcm128" [AES128-GCM128]="aes128gcm128" [AES256-GCM96]="aes256gcm96" [AES192-GCM96]="aes192gcm96" [AES128-GCM96]="aes128gcm96" [AES256-GCM64]="aes256gcm64" [AES192-GCM64]="aes192gcm64" [AES128-GCM64]="aes128gcm64" # AES-CCM [AES256-CCM128]="aes256ccm128" [AES192-CCM128]="aes192ccm128" [AES128-CCM128]="aes128ccm128" [AES256-CCM96]="aes256ccm96" [AES192-CCM96]="aes192ccm96" [AES128-CCM96]="aes128ccm96" [AES256-CCM64]="aes256ccm64" [AES192-CCM64]="aes192ccm64" [AES128-CCM64]="aes128ccm64" # CAMELLIA-CBC [CAMELLIA256-CBC]="camellia256" [CAMELLIA192-CBC]="camellia192" [CAMELLIA128-CBC]="camellia128" # CAMELLIA-CTR [CAMELLIA256-CTR]="camellia256ctr" [CAMELLIA192-CTR]="camellia192ctr" [CAMELLIA128-CTR]="camellia128ctr" # CAMELLIA-GCM [CAMELLIA256-GCM128]="camellia256gcm128" [CAMELLIA192-GCM128]="camellia192gcm128" [CAMELLIA128-GCM128]="camellia128gcm128" [CAMELLIA256-GCM96]="camellia256gcm96" [CAMELLIA192-GCM96]="camellia192gcm96" [CAMELLIA128-GCM96]="camellia128gcm96" [CAMELLIA256-GCM64]="camellia256gcm64" [CAMELLIA192-GCM64]="camellia192gcm64" [CAMELLIA128-GCM64]="camellia128gcm64" # CAMELLIA-CCM [CAMELLIA256-CCM128]="camellia256ccm128" [CAMELLIA192-CCM128]="camellia192ccm128" [CAMELLIA128-CCM128]="camellia128ccm128" [CAMELLIA256-CCM96]="camellia256ccm96" [CAMELLIA192-CCM96]="camellia192ccm96" [CAMELLIA128-CCM96]="camellia128ccm96" [CAMELLIA256-CCM64]="camellia256ccm64" [CAMELLIA192-CCM64]="camellia192ccm64" [CAMELLIA128-CCM64]="camellia128ccm64" # No Encryption [NULL]="null" ) declare -A VPN_SUPPORTED_INTEGRITY=( [MD5]="MD5-HMAC" # SHA [SHA1]="SHA1-HMAC" [SHA512]="512 bit SHA2-HMAC" [SHA384]="384 bit SHA2-HMAC" [SHA256]="256 bit SHA2-HMAC" # AES [AES-XCBC]="AES-XCBC" [AES-CMAC]="AES-CMAC" [AES256-GMAC]="256 bit AES-GMAC" [AES192-GMAC]="192 bit AES-GMAC" [AES128-GMAC]="128 bit AES-GMAC" ) declare -A INTEGRITY_TO_STRONGSWAN=( [MD5]="md5" # SHA [SHA1]="sha1" [SHA512]="sha512" [SHA384]="sha384" [SHA256]="sha256" # AES [AES-XCBC]="aesxcbc" [AES-CMAC]="aescmac" [AES256-GMAC]="aes256gmac" [AES192-GMAC]="aes192gmac" [AES128-GMAC]="aes128gmac" ) declare -A VPN_SUPPORTED_GROUP_TYPES=( # Regular Groups [MODP768]="768 bit Modulo Prime Group" [MODP1024]="1024 bit Modulo Prime Group" [MODP1536]="1536 bit Modulo Prime Group" [MODP2048]="2048 bit Modulo Prime Group" [MODP3072]="3072 bit Modulo Prime Group" [MODP4096]="4096 bit Modulo Prime Group" [MODP6144]="6144 bit Modulo Prime Group" [MODP8192]="8192 bit Modulo Prime Group" # NIST Elliptic Curve Groups [ECP192]="192 bit NIST Elliptic Curve Group" [ECP224]="224 bit NIST Elliptic Curve Group" [ECP256]="256 bit NIST Elliptic Curve Group" [ECP384]="384 bit NIST Elliptic Curve Group" [ECP521]="521 bit NIST Elliptic Curve Group" # Brainpool Elliptic Curve Groups [ECP224BP]="224 bit Brainpool Elliptic Curve Group" [ECP256BP]="256 bit Brainpool Elliptic Curve Group" [ECP384BP]="384 bit Brainpool Elliptic Curve Group" [ECP512BP]="512 bit Brainpool Elliptic Curve Group" # Curve25519 [CURVE25519]="256 bit Elliptic Curve 25519" ) declare -A GROUP_TYPE_TO_STRONGSWAN=( # Regular Groups [MODP768]="modp768" [MODP1024]="modp1024" [MODP1536]="modp1536" [MODP2048]="modp2048" [MODP3072]="modp3072" [MODP4096]="modp4096" [MODP6144]="modp6144" [MODP8192]="modp8192" # NIST Elliptic Curve Groups [ECP192]="ecp192" [ECP224]="ecp224" [ECP256]="ecp256" [ECP384]="ecp384" [ECP521]="ecp521" # Brainpool Elliptic Curve Groups [ECP224BP]="ecp224bp" [ECP256BP]="ecp256bp" [ECP384BP]="ecp384bp" [ECP512BP]="ecp512bp" # Curve25519 [CURVE25519]="curve25519" ) cli_vpn_security_policies() { local action local security_policy if vpn_security_policy_exists ${1}; then security_policy=${1} key=${2} shift 2 case "${key}" in cipher|compression|integrity|lifetime|pfs|show) vpn_security_policies_${key} ${security_policy} $@ ;; group-type) vpn_security_policies_group_type ${security_policy} $@ ;; key-exchange) vpn_security_policies_key_exchange ${security_policy} $@ ;; *) error "Unrecognized argument: ${key}" exit ${EXIT_ERROR} ;; esac else action=${1} shift case "${action}" in new) vpn_security_policies_new $@ ;; destroy) vpn_security_policies_destroy $@ ;; ""|*) if [ -n "${action}" ]; then error "Unrecognized argument: '${action}'" fi exit ${EXIT_ERROR} ;; esac fi } # 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 if ! vpn_security_policies_reload ${name}; then log WARNING "Could not reload the IPsec connection using this security policy" return ${EXIT_ERROR} fi } # reload IPsec connections using a special policy vpn_security_policies_reload() { local name=${1} local connection for connection in $(ipsec_list_connections); do local SECURITY_POLICY ENABLED if ! ipsec_connection_read_config "${connection}" "SECURITY_POLICY"; then continue fi if [[ "${SECURITY_POLICY}" = "${name}" ]] && enabled ENABLED; then if ! ipsec_connection_to_strongswan "${connection}"; then log ERROR "Could not generate strongswan config for ${connnection}" fi fi done ipsec_strongswan_load } # 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:" local cipher for cipher in ${CIPHER}; do cli_print_fmt1 2 "${VPN_SUPPORTED_CIPHERS[${cipher}]-${cipher}}" done cli_space cli_print_fmt1 1 "Integrity:" local integrity for integrity in ${INTEGRITY}; do cli_print_fmt1 2 "${VPN_SUPPORTED_INTEGRITY[${integrity}]-${integrity}}" done cli_space cli_print_fmt1 1 "Group Types:" local group_type for group_type in ${GROUP_TYPE}; do cli_print_fmt1 2 "${VPN_SUPPORTED_GROUP_TYPES[${group_type}]-${group_type}}" done 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})" local ciphers_added local ciphers_removed local ciphers_set while [ $# -gt 0 ]; do local arg="${1}" case "${arg}" in +*) list_append ciphers_added "${arg:1}" ;; -*) list_append ciphers_removed "${arg:1}" ;; [A-Z0-9]*) list_append ciphers_set "${arg}" ;; *) error "Invalid argument: ${arg}" return ${EXIT_ERROR} ;; esac shift done # Check if the user is trying a mixed operation if ! list_is_empty ciphers_set && (! list_is_empty ciphers_added || ! list_is_empty ciphers_removed); then error "You cannot reset the cipher list and add or remove ciphers at the same time" return ${EXIT_ERROR} fi # Set new cipher list if ! list_is_empty ciphers_set; then # Check if all ciphers are valid local cipher for cipher in ${ciphers_set}; do if ! vpn_security_policies_cipher_supported ${cipher}; then error "Unsupported cipher: ${cipher}" return ${EXIT_ERROR} fi done CIPHER="${ciphers_set}" # Perform incremental updates else local cipher # Perform all removals for cipher in ${ciphers_removed}; do if ! list_remove CIPHER ${cipher}; then warning "${cipher} was not on the list and could not be removed" fi done for cipher in ${ciphers_added}; do if vpn_security_policies_cipher_supported ${cipher}; then if ! list_append_unique CIPHER ${cipher}; then warning "${cipher} is already on the cipher list" fi else warning "${cipher} is unknown or unsupported and could not be added" fi done fi # Check if the list contain at least one valid cipher if list_is_empty CIPHER; then error "Cannot save an empty cipher list" return ${EXIT_ERROR} fi # Save everything 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 cli_headline 1 "Current cipher list for ${name}:" for cipher in ${CIPHER}; do cli_print_fmt1 1 "${cipher}" "${VPN_SUPPORTED_CIPHERS[${cipher}]}" done } # 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})" local group_types_added local group_types_removed local group_types_set while [ $# -gt 0 ]; do local arg="${1}" case "${arg}" in +*) list_append group_types_added "${arg:1}" ;; -*) list_append group_types_removed "${arg:1}" ;; [A-Z0-9]*) list_append group_types_set "${arg}" ;; *) error "Invalid argument: ${arg}" return ${EXIT_ERROR} ;; esac shift done # Check if the user is trying a mixed operation if ! list_is_empty group_types_set && (! list_is_empty group_types_added || ! list_is_empty group_types_removed); then error "You cannot reset the group type list and add or remove group types at the same time" return ${EXIT_ERROR} fi # Set new group type list if ! list_is_empty group_types_set; then # Check if all group types are valid local group_type for group_type in ${group_types_set}; do if ! vpn_security_policies_group_type_supported ${group_type}; then error "Unsupported group type: ${group_type}" return ${EXIT_ERROR} fi done GROUP_TYPE="${group_types_set}" # Perform incremental updates else local group_type # Perform all removals for group_type in ${group_types_removed}; do if ! list_remove GROUP_TYPE ${group_type}; then warning "${group_type} was not on the list and could not be removed" fi done for group_type in ${group_types_added}; do if vpn_security_policies_group_type_supported ${group_type}; then if ! list_append_unique GROUP_TYPE ${group_type}; then warning "${group_type} is already on the group type list" fi else warning "${group_type} is unknown or unsupported and could not be added" fi done fi # Check if the list contain at least one valid group_type if list_is_empty GROUP_TYPE; then error "Cannot save an empty group type list" return ${EXIT_ERROR} fi # Save everything 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 cli_headline 1 "Current group type list for ${name}:" for group_type in ${GROUP_TYPE}; do cli_print_fmt1 1 "${group_type}" "${VPN_SUPPORTED_GROUP_TYPES[${group_type}]}" done } # 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})" local integritys_added local integritys_removed local integritys_set while [ $# -gt 0 ]; do local arg="${1}" case "${arg}" in +*) list_append integritys_added "${arg:1}" ;; -*) list_append integritys_removed "${arg:1}" ;; [A-Z0-9]*) list_append integritys_set "${arg}" ;; *) error "Invalid argument: ${arg}" return ${EXIT_ERROR} ;; esac shift done # Check if the user is trying a mixed operation if ! list_is_empty integritys_set && (! list_is_empty integritys_added || ! list_is_empty integritys_removed); then error "You cannot reset the integrity hashes list and add or remove integrity hashes at the same time" return ${EXIT_ERROR} fi # Set new integrity list if ! list_is_empty integritys_set; then # Check if all integrity hashes are valid local integrity for integrity in ${integritys_set}; do if ! vpn_security_policies_integrity_supported ${integrity}; then error "Unsupported integrity hash: ${integrity}" return ${EXIT_ERROR} fi done INTEGRITY="${integritys_set}" # Perform incremental updates else local integrity # Perform all removals for integrity in ${integritys_removed}; do if ! list_remove INTEGRITY ${integrity}; then warning "${integrity} was not on the list and could not be removed" fi done for integrity in ${integritys_added}; do if vpn_security_policies_integrity_supported ${integrity}; then if ! list_append_unique INTEGRITY ${integrity}; then warning "${integrity} is already on the integrity list" fi else warning "${integrity} is unknown or unsupported and could not be added" fi done fi # Check if the list contain at least one valid integrity if list_is_empty INTEGRITY; then error "Cannot save an empty integrity hashes list" return ${EXIT_ERROR} fi # Save everything 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 cli_headline 1 "Current integrity hashes list for ${name}:" for integrity in ${INTEGRITY}; do cli_print_fmt1 1 "${integrity}" "${VPN_SUPPORTED_INTEGRITY[${integrity}]}" done } # 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}) # Delete cache rm -rf "${NETWORK_CACHE_DIR}/vpn/security-policies/${name}" done } vpn_security_policies_cipher_supported() { local cipher=${1} list_match ${cipher} ${!VPN_SUPPORTED_CIPHERS[@]} } vpn_security_policies_group_type_supported() { local group_type=${1} list_match ${group_type} ${!VPN_SUPPORTED_GROUP_TYPES[@]} } vpn_security_policies_integrity_supported() { local integrity=${1} list_match ${integrity} ${!VPN_SUPPORTED_INTEGRITY[@]} } vpn_security_policies_cipher_is_aead() { local cipher=${1} # All CCM and GCM ciphers are AEAD string_match "[CG]CM" "${cipher}" } vpn_security_policies_make_ike_proposal() { local name=${1} if ! vpn_security_policy_exists ${name}; then return ${EXIT_ERROR} fi local config_path="$(vpn_security_policies_path ${name})" local cache_path="${NETWORK_CACHE_DIR}/vpn/security-policies/${name}/ike-proposal" # Get data from cache if possible if file_exists "${cache_path}" && ! file_is_newer_than "${config_path}" "${cache_path}"; then fread "${cache_path}" return ${EXIT_OK} fi # No or invalid cache data found local proposal=$(_vpn_security_policies_make_ike_proposal "${name}") # Write proposal to cache if ! make_parent_dir "${cache_path}" || ! fwrite "${cache_path}" "${proposal}"; then log WARNING "Could not write to cache: ${cache_path}" fi print "${proposal}" } _vpn_security_policies_make_ike_proposal() { local name=${1} # Read the config settings local ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS} if ! vpn_security_policies_read_config "${name}"; then return ${EXIT_ERROR} fi local proposals local cipher for cipher in ${CIPHER}; do # Translate cipher local _cipher=${CIPHER_TO_STRONGSWAN[${cipher}]} if ! isset _cipher; then log WARN "Unsupported cipher: ${cipher}" continue fi local integrity for integrity in ${INTEGRITY}; do local _integrity=${INTEGRITY_TO_STRONGSWAN[${integrity}]} if ! isset _integrity; then log WARN "Unsupported integrity: ${integrity}" continue fi local group_type for group_type in ${GROUP_TYPE}; do local _group_type=${GROUP_TYPE_TO_STRONGSWAN[${group_type}]} if ! isset _group_type; then log WARN "Unsupported group-type: ${group_type}" continue fi # Put everything together list_append proposals "${_cipher}-${_integrity}-${_group_type}" done done done # Returns as a comma-separated list list_join proposals , } vpn_security_policies_make_esp_proposal() { local name=${1} if ! vpn_security_policy_exists ${name}; then return ${EXIT_ERROR} fi local config_path="$(vpn_security_policies_path ${name})" local cache_path="${NETWORK_CACHE_DIR}/vpn/security-policies/${name}/esp-proposal" # Get data from cache if possible if file_exists "${cache_path}" && ! file_is_newer_than "${config_path}" "${cache_path}"; then fread "${cache_path}" return ${EXIT_OK} fi # No or invalid cache data found local proposal=$(_vpn_security_policies_make_esp_proposal "${name}") # Write proposal to cache if ! make_parent_dir "${cache_path}" || ! fwrite "${cache_path}" "${proposal}"; then log WARNING "Could not write to cache: ${cache_path}" fi print "${proposal}" } _vpn_security_policies_make_esp_proposal() { local name=${1} # Read the config settings local ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS} if ! vpn_security_policies_read_config "${name}"; then return ${EXIT_ERROR} fi local proposals local cipher for cipher in ${CIPHER}; do # Translate cipher local _cipher=${CIPHER_TO_STRONGSWAN[${cipher}]} if ! isset _cipher; then log WARN "Unsupported cipher: ${cipher}" continue fi if vpn_security_policies_cipher_is_aead ${cipher}; then local group_type for group_type in ${GROUP_TYPE}; do local _group_type=${GROUP_TYPE_TO_STRONGSWAN[${group_type}]} if ! isset _group_type; then log WARN "Unsupported group-type: ${group_type}" continue fi # Put everything together list_append proposals "${_cipher}-${_group_type}" done else local integrity for integrity in ${INTEGRITY}; do local _integrity=${INTEGRITY_TO_STRONGSWAN[${integrity}]} if ! isset _integrity; then log WARN "Unsupported integrity: ${integrity}" continue fi local group_type for group_type in ${GROUP_TYPE}; do local _group_type=${GROUP_TYPE_TO_STRONGSWAN[${group_type}]} if ! isset _group_type; then log WARN "Unsupported group-type: ${group_type}" continue fi # Put everything together list_append proposals "${_cipher}-${_integrity}-${_group_type}" done done fi done # Returns as a comma-separated list list_join proposals , } # List all security policies vpn_security_policies_list_all() { local security_policy for security_policy in ${NETWORK_SHARE_DIR}/vpn/security-policies/*; do [ -f ${security_policy} ] || continue basename ${security_policy} done # Add all user-defined policies vpn_security_policies_list_user } vpn_security_policies_list_user() { local security_policy for security_policy in ${NETWORK_CONFIG_DIR}/vpn/security-policies/*; do [ -f ${security_policy} ] || continue basename ${security_policy} done }