]> git.ipfire.org Git - network.git/commitdiff
ipsec: add new functions
authorJonatan Schlag <jonatan.schlag@ipfire.org>
Sat, 29 Jul 2017 08:39:36 +0000 (10:39 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 30 Jul 2017 13:08:59 +0000 (15:08 +0200)
Signed-off-by: Jonatan Schlag <jonatan.schlag@ipfire.org>
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/functions/functions.ipsec [new file with mode: 0644]

index 761e849ee258fa333fd9a5786cb3a197fc55074b..2ce7a34140f80ea81e6a6dbbbcec2f144965a1b7 100644 (file)
@@ -160,6 +160,7 @@ dist_network_SCRIPTS = \
        src/functions/functions.usb \
        src/functions/functions.util \
        src/functions/functions.vlan \
+       src/functions/functions.ipsec \
        src/functions/functions.vpn-security-policies \
        src/functions/functions.wireless \
        src/functions/functions.wpa_supplicant \
diff --git a/src/functions/functions.ipsec b/src/functions/functions.ipsec
new file mode 100644 (file)
index 0000000..5e9327b
--- /dev/null
@@ -0,0 +1,579 @@
+#!/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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+IPSEC_CONNECTION_CONFIG_SETTINGS="AUTH_MODE INACTIVITY_TIMEOUT LOCAL_ID LOCAL_PREFIX"
+IPSEC_CONNECTION_CONFIG_SETTINGS="${IPSEC_CONNECTION_CONFIG_SETTINGS} MODE PEER PSK"
+IPSEC_CONNECTION_CONFIG_SETTINGS="${IPSEC_CONNECTION_CONFIG_SETTINGS} REMOTE_ID REMOTE_PREFIX"
+IPSEC_CONNECTION_CONFIG_SETTINGS="${IPSEC_CONNECTION_CONFIG_SETTINGS} SECURITY_POLICY"
+
+# Default values
+IPSEC_DEFAULT_MODE="tunnel"
+IPSEC_DEFAULT_AUTH_MODE="psk"
+IPSEC_DEFAULT_INACTIVITY_TIMEOUT="0"
+IPSEC_DEFAULT_SECURITY_POLICY="system"
+
+IPSEC_VALID_MODES="gre-transport tunnel vti"
+IPSEC_VALID_AUTH_MODES="PSK psk"
+
+# This function writes all values to a via ${connection} specificated VPN IPsec configuration file
+ipsec_connection_write_config() {
+       assert [ $# -ge 1 ]
+
+       local connection="${1}"
+
+       if ! ipsec_connection_exists "${connection}"; then
+               log ERROR "No such VPN IPsec connection: ${connection}"
+               return ${EXIT_ERROR}
+       fi
+
+       local path="$(ipsec_connection_path "${connection}")/settings"
+
+       if ! settings_write "${path}" ${IPSEC_CONNECTION_CONFIG_SETTINGS}; then
+               log ERROR "Could not write configuration settings for VPN IPsec connection ${connection}"
+               return ${EXIT_ERROR}
+       fi
+
+       ipsec_reload ${connection}
+}
+
+# This funtion writes the value for one key to a via ${connection} specificated VPN IPsec connection configuration file
+ipsec_connection_write_config_key() {
+       assert [ $# -ge 3 ]
+
+       local connection=${1}
+       local key=${2}
+       shift 2
+
+       local value="$@"
+
+       if ! ipsec_connection_exists "${connection}"; then
+               log ERROR "No such VPN ipsec connection: ${connection}"
+               return ${EXIT_ERROR}
+       fi
+
+       log DEBUG "Set '${key}' to new value '${value}' in VPN ipsec connection '${connection}'"
+
+       local ${IPSEC_CONNECTION_CONFIG_SETTINGS}
+
+       # Read the config settings
+       if ! ipsec_connection_read_config "${connection}"; then
+               return ${EXIT_ERROR}
+       fi
+
+       # Set the key to a new value
+       assign "${key}" "${value}"
+
+       if ! ipsec_connection_write_config "${connection}"; 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.
+ipsec_connection_read_config() {
+       assert [ $# -ge 1 ]
+
+       local connection="${1}"
+       shift 1
+
+       if ! ipsec_connection_exists "${connection}"; then
+               log ERROR "No such VPN IPsec connection : ${connection}"
+               return ${EXIT_ERROR}
+       fi
+
+
+       local args
+       if [ $# -eq 0 ] && [ -n "${IPSEC_CONNECTION_CONFIG_SETTINGS}" ]; then
+               list_append args ${IPSEC_CONNECTION_CONFIG_SETTINGS}
+       else
+               list_append args $@
+       fi
+
+       local path="$(ipsec_connection_path "${connection}")/settings"
+
+       if ! settings_read "${path}" ${args}; then
+               log ERROR "Could not read settings for VPN IPsec connection ${connection}"
+               return ${EXIT_ERROR}
+       fi
+}
+
+# Returns the path to a the directory for given connection
+ipsec_connection_path() {
+       assert [ $# -eq 1 ]
+
+       local connection=${1}
+
+       echo "${NETWORK_CONFIG_DIR}/vpn/ipsec/connection/${connection}"
+}
+
+# This function checks if a vpn ipsec connection exists
+# Returns True when yes and false when not
+ipsec_connection_exists() {
+       assert [ $# -eq 1 ]
+
+       local connection=${1}
+
+       local path=$(ipsec_connection_path "${connection}")
+
+       [ -d "${path}" ] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
+}
+
+# Reloads the connection after config changes
+ipsec_reload() {
+       return ${EXIT_TRUE}
+}
+
+# Handle the cli after authentification
+ipsec_connection_authentication() {
+       if [ ! $# -gt 1 ]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+
+       local connection=${1}
+       local cmd=${2}
+       shift 2
+
+       case ${cmd} in
+               mode)
+                       ipsec_connection_authentication_mode "${connection}" $@
+                       ;;
+               pre-shared-key)
+                       ipsec_connection_authentication_psk "${connection}" $@
+                       ;;
+               *)
+                       log ERROR "Unrecognized argument: ${cmd}"
+                       return ${EXIT_ERROR}
+                       ;;
+       esac
+}
+
+# Set the authentification mode
+ipsec_connection_authentication_mode() {
+       if [ ! $# -eq 2 ]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+       local connection=${1}
+       local mode=${2}
+
+       if ! isoneof mode ${IPSEC_VALID_AUTH_MODES}; then
+               log ERROR "Auth mode '${mode}' is invalid"
+               return ${EXIT_ERROR}
+       fi
+
+       if ! ipsec_connection_write_config_key "${connection}" "AUTH_MODE" ${mode,,}; then
+               log ERROR "Could not write configuration settings"
+               return ${EXIT_ERROR}
+       fi
+}
+
+# Set the psk
+ipsec_connection_authentication_psk() {
+       if [ ! $# -eq 2]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+       local connection=${1}
+       local psk=${2}
+
+       # TODO Check if psk is valid 
+
+       if ! ipsec_connection_write_config_key "${connection}" "PSK" ${psk}; then
+               log ERROR "Could not write configuration settings"
+               return ${EXIT_ERROR}
+       fi
+
+       return ${EXIT_OK}
+}
+
+# Handle the cli after local
+ipsec_connection_local() {
+       if [ ! $# -ge 2 ]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+
+       local connection=${1}
+       local cmd=${2}
+       shift 2
+
+       case ${cmd} in
+               id)
+                       ipsec_connection_id "${connection}" "LOCAL" $@
+                       ;;
+               prefix)
+                       ipsec_connection_prefix "${connection}" "LOCAL" $@
+                       ;;
+               *)
+                       log ERROR "Unrecognized argument: ${cmd}"
+                       return ${EXIT_ERROR}
+                       ;;
+       esac
+
+       return ${EXIT_OK}
+}
+
+# Set the connection mode
+ipsec_connection_mode() {
+       if [ ! $# -eq 2]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+       local connection=${1}
+       local mode=${2}
+
+       if ! isoneof mode ${IPSEC_VALID_MODES}; then
+               log ERROR "Mode '${mode}' is invalid"
+               return ${EXIT_ERROR}
+       fi
+
+       if ! ipsec_connection_write_config_key "${connection}" "MODE" ${mode}; then
+               log ERROR "Could not write configuration settings"
+               return ${EXIT_ERROR}
+       fi
+
+       return ${EXIT_OK}
+}
+
+# Set the peer to connect to
+ipsec_connection_peer() {
+       if [ ! $# -eq 2]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+       local connection=${1}
+       local peer=${2}
+
+       if ! ipsec_connection_check_peer ${peer}; then
+               log ERROR "Peer '${peer}' is invalid"
+               return ${EXIT_ERROR}
+       fi
+
+       if ! ipsec_connection_write_config_key "${connection}" "PEER" ${peer}; then
+               log ERROR "Could not write configuration settings"
+               return ${EXIT_ERROR}
+       fi
+
+       return ${EXIT_OK}
+}
+
+#Set the local or remote id
+ipsec_connection_id() {
+       if [ ! $# -eq 3 ]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+       local connection=${1}
+       local type=${2}
+       local id=${3}
+
+       if ! ipsec_connection_check_id ${id}; then
+               log ERROR "Id '${id}' is invalid"
+               return ${EXIT_ERROR}
+       fi
+       
+       if ! ipsec_connection_write_config_key "${connection}" "${type}_ID" ${id}; then
+               log ERROR "Could not write configuration settings"
+               return ${EXIT_ERROR}
+       fi
+       
+       return ${EXIT_OK}
+}
+
+# Set the local or remote prefix 
+ipsec_connection_prefix() {
+       if [ ! $# -ge 3 ]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+       local connection=${1}
+       local type=${2}
+       shift 2
+       
+       local _prefix="${type}_PREFIX"
+       local "${_prefix}"
+       if ! ipsec_connection_read_config "${connection}" "${_prefix}"; then
+               return ${EXIT_ERROR}
+       fi
+
+       # Remove duplicated entries to proceed the list safely
+       assign "${_prefix}" "$(list_unique ${!_prefix} )"
+
+       local prefixes_added
+       local prefixes_removed
+       local prefixes_set
+
+       while [ $# -gt 0 ]; do
+               local arg="${1}"
+
+               case "${arg}" in
+                       +*)
+                               list_append prefixes_added "${arg:1}"
+                               ;;
+                       -*)
+                               list_append prefixes_removed "${arg:1}"
+                               ;;
+                       [A-Fa-f0-9]*)
+                               list_append prefixes_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 prefixes_set && (! list_is_empty prefixes_added || ! list_is_empty prefixes_removed); then
+               error "You cannot reset the prefix list and add or remove prefixes at the same time"
+               return ${EXIT_ERROR}
+       fi
+
+       # Set new prefix list
+       if ! list_is_empty prefixes_set; then
+               # Check if all prefixes are valid
+               local prefix
+               for prefix in ${prefixes_set}; do
+                       if ! ip_net_is_valid ${prefix}; then
+                               error "Unsupported prefix: ${prefix}"
+                               return ${EXIT_ERROR}
+                       fi
+               done
+
+               assign "${_prefix}" "${prefixes_set}"
+
+       # Perform incremental updates
+       else
+               local prefix
+
+               # Perform all removals
+               for prefix in ${prefixes_removed}; do
+                       if ! list_remove "${_prefix}" ${prefix}; then
+                               warning "${prefix} was not on the list and could not be removed"
+                       fi
+               done
+
+
+               for prefix in ${prefixes_added}; do
+                       if ip_net_is_valid ${prefix}; then
+                               if ! list_append_unique "${_prefix}" ${prefix}; then
+                                       warning "${prefix} is already on the prefix list"
+                               fi
+                       else
+                               warning "${prefix} is not a valiv IP net and could not be added"
+                       fi
+               done
+       fi
+
+       # Check if the list contain at least one valid prefix
+       if list_is_empty ${_prefix}; then
+               error "Cannot save an empty prefix list"
+               return ${EXIT_ERROR}
+       fi
+
+       # Save everything
+       if ! ipsec_connection_write_config_key "${connection}" "${_prefix}" ${!_prefix}; then
+               log ERROR "Could not write configuration settings"
+       fi
+
+       return ${EXIT_OK}
+}
+
+# Handle the cli after remote
+ipsec_connection_remote() {
+       if [ ! $# -ge 2 ]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+
+       local connection=${1}
+       local cmd=${2}
+       shift 2
+
+       case ${cmd} in
+               id)
+                       ipsec_connection_id "${connection}" "REMOTE" $@
+                       ;;
+
+               prefix)
+                       ipsec_connection_prefix "${connection}" "REMOTE" $@
+                       ;;
+               *)
+                       log ERROR "Unrecognized argument: ${cmd}"
+                       return ${EXIT_ERROR}
+                       ;;
+       esac
+
+       return ${EXIT_OK}
+}
+
+# Set the inactivity timeout
+ipsec_connection_inactivity_timeout() {
+       if [ ! $# -ge 2 ]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+
+       local connection=${1}
+       shift 1
+       local value=$@
+
+       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
+
+       if ! ipsec_connection_write_config_key "${connection}" "INACTIVITY_TIMEOUT" ${value}; then
+               log ERROR "Could not write configuration settings"
+               return ${EXIT_ERROR}
+       fi
+
+       return ${EXIT_OK}
+}
+
+
+# Set the security policy to use
+ipsec_connection_security_policy() {
+       if [ ! $# -eq 2 ]; then
+               log ERROR "Not enough arguments"
+               return ${EXIT_ERROR}
+       fi
+       local connection=${1}
+       local security_policy=${2}
+
+       if ! vpn_security_policy_exists ${security_policy}; then
+               log ERROR "No such vpn security policy '${security_policy}'"
+               return ${EXIT_ERROR}
+       fi
+
+       if ! ipsec_connection_write_config_key "${connection}" "SECURITY_POLICY" ${security_policy}; then
+               log ERROR "Could not write configuration settings"
+               return ${EXIT_ERROR}
+       fi
+}
+
+# Check if a id is valid
+ipsec_connection_check_id() {
+       assert [ $# -eq 1 ]
+       local id=${1}
+
+       if [[ ${id} =~ ^@[[:alnum:]]+$ ]] || ip_is_valid ${id}; then
+               return ${EXIT_TRUE}
+       else
+               return ${EXIT_FALSE}
+       fi
+}
+
+# Checks if a peer is valid
+ipsec_connection_check_peer() {
+       assert [ $# -eq 1 ]
+       local peer=${1}
+
+       # TODO Accept also FQDNs
+       if ip_is_valid ${peer}; then
+               return ${EXIT_TRUE}
+       else
+               return ${EXIT_FALSE}
+       fi
+}
+
+# This function checks if a VPN IPsec connection name is valid
+# Allowed are only A-Za-z0-9
+ipsec_connection_check_name() {
+       assert [ $# -eq 1 ]
+
+       local connection=${1}
+
+       [[ "${connection}" =~ [^[:alnum:]$] ]]
+}
+
+# Function that creates one VPN IPsec connection
+ipsec_connection_new() {
+       if [ $# -gt 1 ]; then
+               error "Too many arguments"
+               return ${EXIT_ERROR}
+       fi
+
+       local connection="${1}"
+       if ! isset connection; then
+               error "Please provide a connection name"
+               return ${EXIT_ERROR}
+       fi
+
+       # Check for duplicates
+       if ipsec_connection_exists "${connection}"; then
+               error "The VPN IPsec connection ${connection} already exists"
+               return ${EXIT_ERROR}
+       fi
+
+       # Check if the name of the connection is valid
+       if  ipsec_connection_check_name "${connection}"; then
+               error "'${connection}' contains illegal characters"
+               return ${EXIT_ERROR}
+       fi
+
+       log DEBUG "Creating VPN IPsec connection ${connection}"
+
+       if ! mkdir -p $(ipsec_connection_path "${connection}"); then
+               log ERROR "Could not create config directory for ${connection}"
+               return ${EXIT_ERROR}
+       fi
+
+       local ${IPSEC_CONNECTION_CONFIG_SETTINGS}
+
+       MODE=${IPSEC_DEFAULT_MODE}
+       AUTH_MODE=${IPSEC_DEFAULT_AUTH_MODE}
+       INACTIVITY_TIMEOUT=${IPSEC_DEFAULT_INACTIVITY_TIMEOUT}
+       SECURITY_POLICY=${IPSEC_DEFAULT_SECURITY_POLICY}
+
+       if ! ipsec_connection_write_config "${connection}"; then
+               log ERROR "Could not write new config file"
+               return ${EXIT_ERROR}
+       fi
+}
+
+# Function that deletes based on the passed parameters one ore more vpn security policies
+ipsec_connection_destroy() {
+       local connection
+       for connection in $@; do
+               if ! ipsec_connection_exists "${connection}"; then
+                       log ERROR "The VPN IPsec connection ${connection} does not exist."
+                       continue
+               fi
+
+               log DEBUG "Deleting VPN IPsec connection ${connection}"
+               if ! rm -rf $(ipsec_connection_path "${connection}"); then
+                       log ERROR "Deleting the VPN IPsec connection ${connection} was not sucessful"
+                       return ${EXIT_ERROR}
+               fi
+       done
+}