This functions now: - return an error when the destination is a directory - creates the destination if the destination not exist
Signed-off-by: Jonatan Schlag jonatan.schlag@ipfire.org --- src/functions/functions.util | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/src/functions/functions.util b/src/functions/functions.util index 381208c..3bcd00f 100644 --- a/src/functions/functions.util +++ b/src/functions/functions.util @@ -179,12 +179,26 @@ fwrite() { assert isset file shift
- if [ ! -w "${file}" ]; then - log ERROR "${file}: No such file" + if [ -d "${file}" ]; then + log ERROR "${file} is a directory" return ${EXIT_ERROR} fi
- print "%s" "$@" >> ${file} 2>/dev/null + if [ -f "${file}" ]; then + if [ -w "${file}" ]; then + print "%s" "$@" >> ${file} 2>/dev/null + else + log ERROR "${file} is not writeable" + return ${EXIT_ERROR} + fi + else + if ! touch "${file}"; then + log ERROR "Could not touch ${file}" + return ${EXIT_ERROR} + fi + + print "%s" "$@" >> ${file} 2>/dev/null + fi }
make_parent_dir() {
Just one short note, I could also insted of rmoving the destintaion if she exists, allow fwrite to overired files. I did not do this because it would first maybe break code when the function fwrite is used. Second it is easier to remove the destintaion and so allow fwrite writing the content to a plain file, then changing the function so that fwrite override files. It is easy to remove a file bevore but appending to a file with a function that overried files is not possible. So I think it is the best to keep this feature of fwrite and when somebody wants to override a file he has to remove it before.
Signed-off-by: Jonatan Schlag jonatan.schlag@ipfire.org --- src/functions/functions.util | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/src/functions/functions.util b/src/functions/functions.util index 3bcd00f..7f691e0 100644 --- a/src/functions/functions.util +++ b/src/functions/functions.util @@ -766,3 +766,29 @@ hex2dec() { dec2hex() { printf "%02x\n" "${1}" } + +copy() { + # This function just copy config files + assert [ $# -eq 2 ] + local src=${1} + local dst=${2} + + # Wo do the decleration and the initialisation in two lines to get the return code of fread + local data + data=$(fread "${src}") + if [ ! $? -eq 0 ]; then + log ERROR "Could not read data from ${src}" + return ${EXIT_ERROR} + fi + + # If the file exist we will overwrite it + # fwrite would just append the contentet to the end of the file + if [ -f ${dst} ]; then + rm -f ${dst} + fi + + if ! fwrite "${dst}" "${data}"; then + log ERROR "Could not write data to ${dst}" + return ${EXIT_ERROR} + fi +}
Signed-off-by: Jonatan Schlag jonatan.schlag@ipfire.org --- src/functions/functions.constants | 1 + 1 file changed, 1 insertion(+)
diff --git a/src/functions/functions.constants b/src/functions/functions.constants index 271dbe5..8db9bea 100644 --- a/src/functions/functions.constants +++ b/src/functions/functions.constants @@ -35,6 +35,7 @@ NETWORK_DB_DIR="${RUN_DIR}/db" NETWORK_ZONE_DIR="${NETWORK_CONFIG_DIR}" NETWORK_HOOKS_DIR=/usr/lib/network/hooks NETWORK_TRIGGERS_DIR=/usr/lib/network/triggers +NETWORK_SHARE_DIR=/usr/share/network
# Network file configuration. NETWORK_SETTINGS_FILE=${NETWORK_CONFIG_DIR}/config
Signed-off-by: Jonatan Schlag jonatan.schlag@ipfire.org --- src/functions/functions.vpn-security-policies | 527 ++++++++++++++++++++++++++ 1 file changed, 527 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..9079a0c --- /dev/null +++ b/src/functions/functions.vpn-security-policies @@ -0,0 +1,527 @@ +#!/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/. # +# # +############################################################################### + +VPN_SECURITY_POLICIES_CONFIG_SETTINGS="CIPHER COMPRESSION GROUP_TYPE INTEGRITY KEY_EXCHANGE LIFETIME PFS" +VPN_SECURITY_POLICIES_READONLY="system" + +VPN_SUPPORTED_CIPHERS="AES192 AES256 AES512" +VPN_SUPPORTED_INTEGRITY="SHA512 SHA256 SHA128" +VPN_SUPPORTED_GROUP_TYPES="MODP8192 MODP4096" + +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 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 +} + +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} + 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} + +} + +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 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 +} + +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 "${NETWORK_SHARE_DIR}/vpn/security-policies/${name}" + else + echo "${NETWORK_CONFIG_DIR}/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} + + local ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS} + + # 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}" + # Check if lifetime is an integer + 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 + 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 + + 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 +} + +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 ]; 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}" +} + +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 + + 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 +} +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 + + 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 + +} + +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 key-exchange <name> + 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,,}" +} + +vpn_security_policies_lifetime(){ + # This function parses the parameters for the 'lifetime' command. + local name=${1} + shift + local value=$@ + + # Check if we get only one argument after lifetime <name> + 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 <hours>h <minutes>m <seconds>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}" +} + +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 pfs <name> + 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}" +} + +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 + if [ -z $@ ]; then + log ERROR "No name provided." + return ${EXIT_ERROR} + fi + + 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" + continue + fi + + if vpn_security_policies_check_readonly ${name}; then + log ERROR "The vpn security policy ${name} is readonly and can thats why not created." + continue + fi + + log DEBUG "Creating vpn security policy ${name}" + copy "$(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} cannot be deleted." + continue + fi + + log DEBUG "Deleting vpn security policy ${name}" + settings_remove $(vpn_security_policies_path ${name}) + done +}
Signed-off-by: Jonatan Schlag jonatan.schlag@ipfire.org --- src/network | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-)
diff --git a/src/network b/src/network index 154d253..65143e6 100644 --- a/src/network +++ b/src/network @@ -1194,7 +1194,6 @@ cli_reset() { fi
warning_log "Will reset the whole network configuration!!!" - # Force mode is disabled by default local force=0
@@ -1384,13 +1383,75 @@ cli_raw() { exit ${EXIT_OK} }
+cli_vpn() { + local action=${1} + shift 1 + + case "${action}" in + security-policies) + cli_vpn_security_policies $@ + ;; + *) + error "Unrecognized argument: ${action}" + exit ${EXIT_ERROR} + ;; + esac +} + +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 +} + # Process the given action case "${action}" in init) init_run ;;
- settings|hostname|port|device|zone|start|stop|restart|status|reset|route) + settings|hostname|port|device|zone|start|stop|restart|status|reset|route|vpn) cli_${action} $@ ;;