#!/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 . # # # ############################################################################### WIRELESS_NETWORK_SUPPORTED_PSK_MODES="WPA2-PSK-SHA256 WPA2-PSK WPA-PSK-SHA256 WPA-PSK" WIRELESS_NETWORK_SUPPORTED_MODES="${WIRELESS_NETWORK_SUPPORTED_PSK_MODES} \ 802.1X WPA-EAP NONE" WIRELESS_NETWORK_CONFIG_SETTINGS="ANONYMOUS_IDENTITY EAP_MODES HIDDEN \ IDENTITY MODES PASSWORD PRIORITY PSK SSID" cli_wireless_network() { case "${1}" in new) wireless_network_new "${@:2}" ;; destroy) wireless_network_destroy "${@:2}" ;; *) local ssid="${1}" local key="${2//-/_}" shift 2 if ! wireless_network_exists "${ssid}"; then error "No such wireless network: ${ssid}" return ${EXIT_ERROR} fi # Convert SSID into usable format local handle="$(wireless_network_hash "${ssid}")" case "${key}" in modes|pre_shared_key|priority) wireless_network_${key} "${handle}" "$@" ;; show) wireless_network_show "${handle}" exit $? ;; *) error "Unrecognized argument: ${key}" exit ${EXIT_ERROR} ;; esac ;; esac } wireless_network_list() { list_directory "${NETWORK_WIRELESS_NETWORKS_DIR}" } wireless_network_list_ssids() { local handle for handle in $(wireless_network_list); do local ${WIRELESS_NETWORK_CONFIG_SETTINGS} if ! wireless_network_read_config "${handle}"; then continue fi print "${SSID}" done } # This function writes all values to a via ${ssid} specificated wireless network configuration file wireless_network_write_config() { assert [ $# -ge 1 ] local handle="${1}" local path="${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/settings" if ! settings_write "${path}" ${WIRELESS_NETWORK_CONFIG_SETTINGS}; then log ERROR "Could not write configuration" return ${EXIT_ERROR} fi # When we get here the writing of the config file was successful return ${EXIT_OK} } # This funtion writes the value for one key to a via ${ssid} specificated # wireless network configuration file wireless_network_write_config_key() { assert [ $# -ge 3 ] local handle="${1}" local key="${2}" shift 2 local value="$@" local ${WIRELESS_NETWORK_CONFIG_SETTINGS} # Read the config settings if ! wireless_network_read_config "${handle}"; then return ${EXIT_ERROR} fi log DEBUG "Set '${key}' to new value '${value}' in wireless network '${SSID}'" # Set the key to a new value assign "${key}" "${value}" if ! wireless_network_write_config "${handle}"; then return ${EXIT_ERROR} fi return ${EXIT_OK} } # Reads one or more keys out of a settings file or all if no key is provided. wireless_network_read_config() { assert [ $# -ge 1 ] local handle="${1}" shift local args if [ $# -eq 0 ] && [ -n "${WIRELESS_NETWORK_CONFIG_SETTINGS}" ]; then list_append args ${WIRELESS_NETWORK_CONFIG_SETTINGS} else list_append args "$@" fi local path="${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/settings" if ! settings_read "${path}" ${args}; then log ERROR "Could not read settings for wireless network ${handle}" return ${EXIT_ERROR} fi } # This function checks if a wireless network exists # Returns True when yes and false when not wireless_network_exists() { local ssid="${1}" local handle="$(wireless_network_hash "${ssid}")" assert isset handle # We cannot use wireless_network_read_config here beacuse we would end in a loop local SSID if ! settings_read "${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/settings" SSID; then return ${EXIT_FALSE} fi if [ "${SSID}" = "${ssid}" ]; then return ${EXIT_TRUE} else return ${EXIT_FALSE} fi } wireless_network_hash() { assert [ $# -eq 1 ] local string="${1}" local hash=$(echo -n "${string}" | md5sum ) hash=${hash%% -} local path="${NETWORK_WIRELESS_NETWORKS_DIR}/*${hash}" if [ -d "${path}" ]; then basename "${path}" else local normalized=$(normalize "${string}") normalized=${normalized%-} echo "${normalized}-${hash}" fi } wireless_network_new() { if [ $# -gt 1 ]; then error "Too many arguments" return ${EXIT_ERROR} fi local ssid="${1}" if ! isset ssid; then error "Please provide a SSID" return ${EXIT_ERROR} fi # Check for duplicates if wireless_network_exists "${ssid}"; then error "The wireless network ${ssid} already exists" return ${EXIT_ERROR} fi local handle="$(wireless_network_hash "${ssid}")" assert isset handle log DEBUG "Creating wireless network '${ssid}'" if ! mkdir -p "${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}"; then log ERROR "Could not create config directory for wireless network ${ssid}" return ${EXIT_ERROR} fi local ${WIRELESS_NETWORK_CONFIG_SETTINGS} MODES="${WIRELESS_NETWORK_SUPPORTED_MODES}" SSID="${ssid}" PRIORITY=0 if ! wireless_network_write_config "${handle}"; then log ERROR "Could not write new config file" return ${EXIT_ERROR} fi } # Deletes a wireless network wireless_network_destroy() { local ssid="${1}" if ! wireless_network_exists "${ssid}"; then error "No such wireless network: ${ssid}" return ${EXIT_ERROR} fi local handle="$(wireless_network_hash "${ssid}")" assert isset handle if ! rm -rf "${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}"; then error "Could not delete the wireless network" return ${EXIT_ERROR} fi log INFO "Successfully destroyed wireless network ${ssid}" return ${EXIT_OK} } wireless_networks_mode_is_valid() { assert [ $# -eq 1 ] local mode=${1} if isoneof mode ${WIRELESS_NETWORK_SUPPORTED_MODES}; then return ${EXIT_TRUE} else return ${EXIT_FALSE} fi } # WIRELESS_NETWORK_SUPPORTED_MODES wireless_network_modes() { if [ ! $# -ge 2 ]; then log ERROR "Not enough arguments" return ${EXIT_ERROR} fi local handle="${1}" shift if [ $# -eq 0 ]; then log ERROR "You must pass at least one value after mode" return ${EXIT_ERROR} fi local ${WIRELESS_NETWORK_CONFIG_SETTINGS} if ! wireless_network_read_config "${handle}"; then error "Could not read configuration" return ${EXIT_ERROR} fi # Remove duplicated entries to proceed the list safely MODES="$(list_unique ${MODES})" local modes_added local modes_removed local modes_set while [ $# -gt 0 ]; do local arg="${1}" case "${arg}" in +*) list_append modes_added "${arg:1}" ;; -*) list_append modes_removed "${arg:1}" ;; [A-Z0-9]*) list_append modes_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 modes_set && (! list_is_empty modes_added || ! list_is_empty modes_removed); then error "You cannot reset the modes list and add or remove modes at the same time" return ${EXIT_ERROR} fi # Set new modes list if ! list_is_empty modes_set; then # Check if all modes are valid local mode for mode in ${modes_set}; do if ! wireless_networks_mode_is_valid ${mode}; then error "Unsupported mode: ${mode}" return ${EXIT_ERROR} fi done MODES="${modes_set}" # Perform incremental updates else local modes # Perform all removals for mode in ${modes_removed}; do if ! list_remove MODES ${mode}; then warning "${mode} was not on the list and could not be removed" fi done for mode in ${modes_added}; do if wireless_networks_mode_is_valid ${mode}; then if ! list_append_unique MODES ${mode}; then warning "${mode} is already on the modes list" fi else warning "${mode} is unknown or unsupported and could not be added" fi done fi # Check if the list contain at least one valid mode if list_is_empty MODES; then error "Cannot save an empty mode list" return ${EXIT_ERROR} fi if ! wireless_network_write_config "${handle}"; then log ERROR "Could not write configuration settings" return ${EXIT_ERROR} fi } wireless_network_pre_shared_key() { if [ ! $# -eq 2 ]; then log ERROR "Not enough arguments" return ${EXIT_ERROR} fi local handle="${1}" local psk="${2}" local ${WIRELESS_NETWORK_CONFIG_SETTINGS} if ! wireless_network_read_config "${handle}"; then error "Could not read configuration" return ${EXIT_ERROR} fi # Validate the key if encryption mode is known if isset ENCRYPTION_MODE && [ "${ENCRYPTION_MODE}" != "NONE" ]; then if ! wireless_pre_share_key_is_valid "${ENCRYPTION_MODE}" "${psk}"; then error "The pre-shared-key is invalid for this wireless network: ${psk}" return ${EXIT_ERROR} fi fi if ! wireless_network_write_config_key "${handle}" "PSK" "${psk}"; then log ERROR "Could not write configuration settings" return ${EXIT_ERROR} fi } wireless_networks_priority_is_valid() { assert [ $# -eq 1 ] local priority=${1} if ! isinteger priority || [ ! ${priority} -ge 0 ] || [ ! ${priority} -le 999 ]; then return ${EXIT_FALSE} fi return ${EXIT_TRUE} } wireless_network_priority() { if [ ! $# -eq 2 ]; then log ERROR "Not enough arguments" return ${EXIT_ERROR} fi local handle="${1}" local priority=${2} if ! wireless_networks_priority_is_valid ${priority}; then error "The priority must be an integer greater or eqal zero and and less then 1000" return ${EXIT_ERROR} fi if ! wireless_network_write_config_key "${handle}" "PRIORITY" "${priority}"; then log ERROR "Could not write configuration settings" return ${EXIT_ERROR} fi } wireless_networks_write_wpa_supplicant_configuration() { local device="${1}" local file="${WPA_SUPPLICANT_CONF_DIR}/${device}.conf" # Ensure we can write the file make_parent_directory "${file}" ( # Write a config header wpa_supplicant_config_header wireless_networks_to_wpa_supplicant ) > ${file} } wireless_networks_to_wpa_supplicant() { local handle for handle in $(wireless_network_list); do wireless_network_to_wpa_supplicant "${handle}" done } wireless_network_to_wpa_supplicant() { local handle="${1}" local ${WIRELESS_NETWORK_CONFIG_SETTINGS} if ! wireless_network_read_config "${handle}"; then error "Could not read configuration for ${handle}" return ${EXIT_ERROR} fi local auth_alg local group local key_mgmt local pairwise local proto local mode for mode in ${WIRELESS_NETWORK_SUPPORTED_MODES}; do # Skip any disabled modes if isset MODES && ! list_match "${mode}" ${MODES}; then continue fi case "${mode}" in # WPA2 (802.11i) WPA2-PSK|WPA2-PSK-SHA256) list_append_unique auth_alg "OPEN" list_append_unique key_mgmt "${mode/WPA2/WPA}" list_append_unique proto "RSN" local p for p in CCMP TKIP; do list_append_unique pairwise "${p}" done local g for g in CCMP TKIP WEP104 WEP40; do list_append_unique group "${g}" done ;; # WPA WPA-PSK|WPA-PSK-SHA256) list_append_unique auth_alg "OPEN" list_append_unique key_mgmt "${mode}" list_append_unique proto "WPA" local p for p in CCMP TKIP; do list_append_unique pairwise "${p}" done local g for g in CCMP TKIP WEP104 WEP40; do list_append_unique group "${g}" done ;; # 802.1X 802.1X) list_append_unique key_mgmt "IEEE8021X" ;; # No encryption. DANGEROUS! NONE) list_append_unique auth_alg "OPEN" list_append_unique key_mgmt "NONE" ;; esac done assert isset auth_alg assert isset key_mgmt # Certificate Paths local ca_cert_path="${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/ca.pem" local client_cert_path="${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/client.pem" local client_key_path="${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/client.key" print_indent 0 "# ${SSID}" print_indent 0 "network={" print_indent 1 "ssid=\"${SSID}\"" # Actively scan for hidden networks if enabled HIDDEN; then print_indent 1 "scan_ssid=1" fi # Priority if isinteger PRIORITY; then print_indent 1 "priority=${PRIORITY}" fi print # Authentication print_indent 1 "# Authentication" print_indent 1 "auth_alg=${auth_alg}" print_indent 1 "key_mgmt=${key_mgmt}" local i for i in proto pairwise group; do print_indent 1 "${i}=${!i}" done print # PSK if isset PSK; then print_indent 1 "# Pre Shared Key" print_indent 1 "psk=\"${PSK}\"" fi if isset EAP_MODES; then print_indent 1 "# EAP" print_indent 1 "eap=${EAP_MODES}" print fi if isset IDENTITY; then print_indent 1 "# Credentials" print_indent 1 "identity=\"${IDENTITY}\"" if isset PASSWORD; then print_indent 1 "password=\"${PASSWORD}\"" fi if isset ANONYMOUS_IDENTITY; then print_indent 1 "anonymous_identity=\"${ANONYMOUS_IDENTITY}\"" fi print fi # Client Certificate if file_exists "${client_cert_path}" && file_exists "${client_key_path}"; then print_indent 1 "# Client Certificate" print_indent 1 "client_cert=\"${client_cert_path}\"" print_indent 1 "private_key=\"${client_key_path}\"" print fi # Validate server certificates if file_exists "${ca_cert_path}"; then print_indent 1 "ca_cert=\"${ca_cert_path}\"" elif isset CA_BUNDLE; then print_indent 1 "ca_cert=\"${CA_BUNDLE}\"" fi print_indent 0 "}" print }