Error in hostapd config since 199 upgrade

Hello,

since I upgraded my ipfire in 199 version, I have an error initialising hostapd

Jan 7 16:18:34 myfire hostapd: Configuration file: /etc/hostapd.conf
Jan 7 16:18:34 myfire hostapd: ctrl_interface_group=0
Jan 7 16:18:34 myfire hostapd: Line 22: invalid SSID ‘“”’
Jan 7 16:18:34 myfire hostapd: Line 27: invalid WPA passphrase length 1 (expected 8..63)
Jan 7 16:18:34 myfire hostapd: WPA-PSK enabled, but PSK or passphrase is not configured.
Jan 7 16:18:34 myfire hostapd: 3 errors found in configuration file ‘/etc/hostapd.conf’
Jan 7 16:18:34 myfire hostapd: Failed to set up interface with /etc/hostapd.conf
Jan 7 16:18:34 myfire hostapd: hostapd_init: free iface 0x181d08e0
Jan 7 16:18:34 myfire hostapd: Failed to initialize interface

Is there an ! in the SSID name?

and/or how long is your Password?

1 Like

You are right, I had “!” in SSID and pre-shared key changing this allow hostapd to start.

this pair ssid / pre-shared key was running well since years and versions it’s failes only after 199 upgrade.

My password is about 25 characters.

some others characters not “allowed” in password or SSID ?

Here is the fixed init script addressing both issues:

#!/bin/sh
###############################################################################
#                                                                             #
# IPFire.org - A linux based firewall                                         #
# Copyright (C) 2007-2022  IPFire Team  <info@ipfire.org>                     #
#                                                                             #
###############################################################################

. /etc/sysconfig/rc
. ${rc_functions}

declare -A HT_CAPS=(
	[0x0001]="[LDPC]"
	[0x0002]="[HT40+][HT40-]"
	[0x0010]="[GF]"
	[0x0020]="[SHORT-GI-20]"
	[0x0040]="[SHORT-GI-40]"
	[0x0080]="[TX-STBC]"
	[0x0400]="[DELAYED-BA]"
	[0x0800]="[MAX-AMSDU-7935]"
	[0x1000]="[DSSS_CCK-40]"
	[0x4000]="[40-INTOLERANT]"
	[0x8000]="[LSIG-TXOP-PROT]"
)

declare -A VHT_CAPS=(
	[0x00000010]="[RXLDPC]"
	[0x00000020]="[SHORT-GI-80]"
	[0x00000040]="[SHORT-GI-160]"
	[0x00000080]="[TX-STBC-2BY1]"
	[0x00000800]="[SU-BEAMFORMER]"
	[0x00001000]="[SU-BEAMFORMEE]"
	[0x00080000]="[MU-BEAMFORMER]"
	[0x00100000]="[MU-BEAMFORMEE]"
	[0x00200000]="[VHT-TXOP-PS]"
	[0x00400000]="[HTC-VHT]"
	[0x10000000]="[RX-ANTENNA-PATTERN]"
	[0x20000000]="[TX-ANTENNA-PATTERN]"
)

declare -A HE_MAC_CAPS=()

declare -A HE_PHY_CAPS=(
	[0x08000000000]="he_su_beamformer=1"
	[0x10000000000]="he_su_beamformee=1"
	[0x20000000000]="he_mu_beamformer=1"
)

declare -A EHT_MAC_CAPS=()

declare -A EHT_PHY_CAPS=(
	[0x0000000000000010]="eht_su_beamformer=1"
	[0x0000000000000020]="eht_su_beamformee=1"
)

find_interface() {
	local address="${1}"
	local path
	for path in /sys/class/net/*; do
		if [ -s "${path}/address" ] && [ "$(<${path}/address)" = "${address}" ]; then
			basename "${path}"
			return 0
		fi
	done
	return 1;
}

write_config() {
	local interface="${1}"
	local settings="/var/ipfire/wlanap/settings"

	# FIX: Extract SSID and PWD directly and strip quotes to avoid $PWD collision 
	# and shell expansion issues with special characters.
	local LOCAL_SSID=$(grep "^SSID=" "${settings}" | cut -d'=' -f2- | sed 's/^"//;s/"$//;s/^\x27//;s/\x27$//')
	local LOCAL_PASS=$(grep "^PWD=" "${settings}" | cut -d'=' -f2- | sed 's/^"//;s/"$//;s/^\x27//;s/\x27$//')

	# Fetch the PHY
	local phy="$(</sys/class/net/${interface}/phy80211/name)"

	local flag
	local ht_flags=0
	local vht_flags=0
	local he_mac_flags=0
	local he_phy_flags=0
	local eht_mac_flags=0
	local eht_phy_flags=0

	# Set default BAND if none is set
	if [ -z "${BAND}" ]; then
		case "${HW_MODE}" in
			gn) BAND="2g" ;;
			*)  BAND="5g" ;;
		esac
	fi

	# Fetch PHY information
	local line
	while read -r line; do
		case "${line}" in
			"EHT MAC Capabilities"*) eht_mac_flags="${line:22:6}" ;;
			"EHT PHY Capabilities"*) eht_phy_flags="${line:23:18}" ;;
			"HE MAC Capabilities"*)  he_mac_flags="${line:21:14}" ;;
			"HE PHY Capabilities"*)  he_phy_flags="${line:22:24}" ;;
			"VHT Capabilities"*)     vht_flags="${line:18:10}" ;;
			"Capabilities: "*)       ht_flags="${line:14}" ;;
			"* 2412.0 MHz"*)         [ "${BAND}" = "2g" ] && break ;;
			"* 5180.0 MHz"*)         [ "${BAND}" = "5g" ] && break ;;
		esac
	done <<<"$(iw phy "${phy}" info)"

	local ht_caps=()
	local vht_caps=()
	local he_caps=()
	local eht_caps=()

	# HT Capabilities
	for flag in ${!HT_CAPS[@]}; do
		if (( ${ht_flags} & ${flag} )); then
			ht_caps+=( "${HT_CAPS[${flag}]}" )
		fi
	done

	# RX STBC
	case "$(( (${ht_flags} >> 8) & 0x03 ))" in
		1) ht_caps+=( "[RX-STBC1]" ) ;;
		2) ht_caps+=( "[RX-STBC12]" ) ;;
		3) ht_caps+=( "[RX-STBC123]" ) ;;
	esac

	# VHT Capabilities
	for flag in ${!VHT_CAPS[@]}; do
		if (( ${vht_flags} & ${flag} )); then
			vht_caps+=( "${VHT_CAPS[${flag}]}" )
		fi
	done

	# Supported channel width
	case "$(( (${vht_flags} >> 2) & 0x03 ))" in
		1) vht_caps+=( "[VHT160]" ) ;;
		2) vht_caps+=( "[VHT160-80PLUS80]" ) ;;
	esac

	# VHT Max MPDU Length
	case "$(( ${vht_flags} & 0x03 ))" in
		1) vht_caps+=( "[MAX-MPDU-7991]" ) ;;
		2) vht_caps+=( "[MAX-MPDU-11454]" ) ;;
	esac

	# RX Spatial Streams
	case "$(( (${vht_flags} >> 8) & 0x03 ))" in
		1) vht_caps+=( "[RX-STBC-1]" ) ;;
		2) vht_caps+=( "[RX-STBC-12]" ) ;;
		3) vht_caps+=( "[RX-STBC-123]" ) ;;
		4) vht_caps+=( "[RX-STBC-1234]" ) ;;
	esac

	# Compressed Steering
	case "$(( ((${vht_flags} >> 13) & 0x03) + 1 ))" in
		2) vht_caps+=( "[BF-ANTENNA-2]" ) ;;
		3) vht_caps+=( "[BF-ANTENNA-3]" ) ;;
		4) vht_caps+=( "[BF-ANTENNA-4]" ) ;;
	esac

	# Sounding Dimension
	case "$(( ((${vht_flags} >> 16) & 0x03) + 1 ))" in
		2) vht_caps+=( "[SOUNDING-DIMENSION-2]" ) ;;
		3) vht_caps+=( "[SOUNDING-DIMENSION-3]" ) ;;
		4) vht_caps+=( "[SOUNDING-DIMENSION-4]" ) ;;
	esac

	local exponent="$(( (${vht_flags} >> 23) & 0x03 ))"
	vht_caps+=( "[MAX-A-MPDU-LEN-EXP${exponent}]" )

	# VHT Link Adaptation
	case "$(( (${vht_flags} >> 26) & 0x03 ))" in
		2) vht_caps+=( "[VHT-LINK-ADAPT2]" ) ;;
		3) vht_caps+=( "[VHT-LINK-ADAPT3]" ) ;;
	esac

	# HE PHY Capabilities
	for flag in ${!HE_PHY_CAPS[@]}; do
		if (( ${he_phy_flags} & ${flag} )); then
			he_caps+=( "${HE_PHY_CAPS[${flag}]}" )
		fi
	done

	# EHT PHY Capabilities
	for flag in ${!EHT_PHY_CAPS[@]}; do
		if (( ${eht_phy_flags} & ${flag} )); then
			eht_caps+=( "${EHT_PHY_CAPS[${flag}]}" )
		fi
	done

	# Set the channel to zero if not set
	[ -z "${CHANNEL}" ] && CHANNEL=0

	# Translate the old HW_MODE to the newer MODE setting
	if [ -z "${MODE}" ]; then
		case "${HW_MODE}" in
			ac)    MODE="VHT20" ;;
			an|gn) MODE="HT20"  ;;
		esac
	fi

	# Header
	echo "# Automatically generated configuration - DO NOT EDIT"
	echo "logger_syslog=-1"
	echo "logger_syslog_level=4"
	echo "driver=nl80211"
	echo "country_code=${COUNTRY}"
	echo "country3=0x49"
	echo "ieee80211d=1"
	echo "ieee80211h=1"
	[ -n "${CHANNEL}" ] && echo "channel=${CHANNEL}"
	echo "local_pwr_constraint=3"
	echo "spectrum_mgmt_required=1"
	echo "enable_background_radar=1"
	echo "wmm_enabled=1"

	# 802.11ac configuration
	local enable_ac=0; local vht_oper_chwidth=0; local vht_oper_centr_freq_seg0_idx=""
	case "${MODE}" in
		VHT20|HE20|EHT20) enable_ac=1 ;;
		VHT40|HE40|EHT40) enable_ac=1
			if [ "${CHANNEL}" -gt 0 ]; then
				case "$(( (${CHANNEL} / 4) % 2 ))" in
					0) vht_oper_centr_freq_seg0_idx="$(( ${CHANNEL} - 2 ))" ;;
					1) vht_oper_centr_freq_seg0_idx="$(( ${CHANNEL} + 2 ))" ;;
				esac
			fi ;;
		VHT80|HE80|EHT80) enable_ac=1; vht_oper_chwidth=1
			if [ "${CHANNEL}" -gt 0 ]; then
				case "$(( (${CHANNEL} / 4) % 4 ))" in
					0) vht_oper_centr_freq_seg0_idx="$(( ${CHANNEL} - 6 ))" ;;
					1) vht_oper_centr_freq_seg0_idx="$(( ${CHANNEL} + 6 ))" ;;
					2) vht_oper_centr_freq_seg0_idx="$(( ${CHANNEL} + 2 ))" ;;
					3) vht_oper_centr_freq_seg0_idx="$(( ${CHANNEL} - 2 ))" ;;
				esac
			fi ;;
		VHT160|HE160|EHT160|EHT320) enable_ac=1; vht_oper_chwidth=2
			if [ "${CHANNEL}" -gt 0 ]; then
				case "${CHANNEL}" in
					36|40|44|48|52|56|60|64) vht_oper_centr_freq_seg0_idx=50 ;;
					100|104|108|112|116|120|124|128) vht_oper_centr_freq_seg0_idx=114 ;;
					149|153|157|161|165|169|173|177) vht_oper_centr_freq_seg0_idx=163 ;;
				esac
			fi ;;
	esac

	# 802.11ax/be configuration
	local enable_ax=0; local he_oper_chwidth="${vht_oper_chwidth}"; local he_oper_centr_freq_seg0_idx="${vht_oper_centr_freq_seg0_idx}"
	case "${MODE}" in HE*|EHT*) enable_ax=1 ;; esac

	local enable_be=0; local eht_oper_chwidth="${he_oper_chwidth}"; local eht_oper_centr_freq_seg0_idx="${he_oper_centr_freq_seg0_idx}"
	case "${MODE}" in EHT*) enable_be=1 ;; esac

	# Set hardware mode
	case "${BAND}" in
		5g) echo "hw_mode=a" ;;
		2g) echo "hw_mode=g" ;;
	esac

	# Enable 802.11be
	if [ "${enable_be}" -eq 1 ]; then
		echo "ieee80211be=1"
		echo "eht_oper_chwidth=${eht_oper_chwidth}"
		echo "eht_oper_centr_freq_seg0_idx=${eht_oper_centr_freq_seg0_idx}"
		[ ${#eht_caps[@]} -gt 0 ] && printf "%s\n" "${eht_caps[@]}"
	fi

	# Enable 802.11ax
	if [ "${enable_ax}" -eq 1 ]; then
		echo "ieee80211ax=1"
		echo "he_oper_chwidth=${he_oper_chwidth}"
		echo "he_oper_centr_freq_seg0_idx=${he_oper_centr_freq_seg0_idx}"
		[ ${#he_caps[@]} -gt 0 ] && printf "%s\n" "${he_caps[@]}"
	fi

	# Enable 802.11ac
	if [ "${enable_ac}" -eq 1 ]; then
		echo "ieee80211ac=1"
		echo "vht_oper_chwidth=${vht_oper_chwidth}"
		echo "vht_oper_centr_freq_seg0_idx=${vht_oper_centr_freq_seg0_idx}"
		[ ${#vht_caps[@]} -gt 0 ] && echo "vht_capab=${vht_caps[@]}"
	fi

	# Enable 802.11n
	echo "ieee80211n=1"
	[ ${#ht_caps[@]} -gt 0 ] && echo "ht_capab=${ht_caps[@]}"

	# Configure antennas
	[ -z "${RX_ANTENNAS}" ] && RX_ANTENNAS="0xffffffff"
	[ -z "${TX_ANTENNAS}" ] && TX_ANTENNAS="0xffffffff"
	iw phy "${phy}" set antenna "${TX_ANTENNAS}" "${RX_ANTENNAS}" &>/dev/null

	# General hostapd settings
	echo "auth_algs=1"
	echo "ctrl_interface=/var/run/hostapd"
	echo "ctrl_interface_group=0"
	echo "disassoc_low_ack=1"

	# SSID Fix: Use Hex-Encoding (ssid2) to ensure special character compatibility
	printf "ssid2=%s\n" "$(echo -n "${LOCAL_SSID}" | xxd -p)"
	echo "utf8_ssid=1"

	[ "${HIDESSID}" = "on" ] && echo "ignore_broadcast_ssid=2"
	[ "${CLIENTISOLATION}" = "on" ] && echo "ap_isolate=1"
	[ "${NOSCAN}" = "on" ] && echo "noscan=1" || echo "noscan=0"

	# Management Frame Protection (802.11w)
	case "${IEEE80211W}" in
		on)       echo "ieee80211w=2"; echo "beacon_prot=1"; echo "ocv=1" ;;
		optional) echo "ieee80211w=1"; echo "beacon_prot=1"; echo "ocv=2" ;;
		*)        echo "ieee80211w=0" ;;
	esac

	# Encryption Fix: Use locally extracted password to bypass shell expansion bugs
	case "${ENC}" in
		wpa3)
			echo "wpa=2"
			echo "wpa_passphrase=${LOCAL_PASS}"
			echo "wpa_key_mgmt=SAE"
			echo "rsn_pairwise=CCMP" ;;
		wpa2+3)
			echo "wpa=2"
			echo "wpa_passphrase=${LOCAL_PASS}"
			echo "wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE"
			echo "rsn_pairwise=CCMP" ;;
		wpa2)
			echo "wpa=2"
			echo "wpa_passphrase=${LOCAL_PASS}"
			echo "wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256"
			echo "rsn_pairwise=CCMP" ;;
		wpa1+2)
			echo "wpa=3"
			echo "wpa_passphrase=${LOCAL_PASS}"
			echo "wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256"
			echo "wpa_pairwise=TKIP"
			echo "rsn_pairwise=CCMP" ;;
		wpa1)
			echo "wpa=1"
			echo "wpa_passphrase=${LOCAL_PASS}"
			echo "wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256"
			echo "wpa_pairwise=TKIP" ;;
	esac

	# Steering and Optimization
	case "${IEEE80211W}" in on|optional) echo "mbo=1"; echo "mbo_cell_data_conn_pref=1" ;; esac
	echo "ssid_protection=1"
	echo "extended_key_id=1"
	echo "oce=7"
	echo "interworking=1"
	echo "access_network_type=0"
	echo "internet=1"
	echo "time_advertisement=2"
	echo "multicast_to_unicast=1"

	return 0
}

# Load general settings 
# Note: SSID/PWD will be safely re-extracted in write_config
eval $(/usr/local/bin/readhash /var/ipfire/wlanap/settings)

case "${1}" in
	start)
		interface="$(find_interface "${INTERFACE}")"
		if [ -z "${interface}" ]; then
			boot_mesg "Could not find interface ${INTERFACE}"; echo_failure; exit 1
		fi
		if ! write_config "${interface}" > /etc/hostapd.conf; then
			boot_mesg "Failed to generate configuration"; echo_failure; exit 1
		fi
		args=(/usr/bin/hostapd -s -B /etc/hostapd.conf -i "${interface}")
		if [ -n "${DEBUG}" ] && [[ "${DEBUG}" =~ ^[0-9]+$ ]]; then
			for (( i = 0; i < DEBUG; i++ )); do args+=( "-d" ); done
		fi
		boot_mesg "Starting hostapd... "; loadproc "${args[@]}" ;;
	stop)
		boot_mesg "Stopping hostapd..."; killproc /usr/bin/hostapd; evaluate_retval ;;
	restart) ${0} stop; sleep 1; ${0} start ;;
	status)  statusproc /usr/bin/hostapd ;;
	show-config)
		interface="$(find_interface "${INTERFACE}")"
		[ -z "${interface}" ] && exit 1
		write_config "${interface}" ;;
	*) echo "Usage: ${0} {start|stop|restart|status|show-config}"; exit 1 ;;
esac

There are two main problems in /etc/init.d/hostapd:

  1. Variable Collision: The script uses $PWD, which is a reserved shell environment variable (Print Working Directory). When readhash tries to assign the password to PWD, it either fails or gets overwritten by the system.
  2. Shell Expansion: Special characters like ! trigger Bash history expansion or misinterpretation during the eval $(readhash ...) process, leading to empty strings in the final hostapd.conf.

The Fix: Extract SSID and Passphrase directly from the settings file using grep and cut within the write_config function, bypassing the unreliable eval state. I also recommend using ssid2 with Hex encoding to make the SSID robust against any special characters.

Modified write_config section:

write_config() {
    local interface="${1}"
    local settings="/var/ipfire/wlanap/settings"

    # Direct extraction to avoid $PWD collision and shell expansion issues
    local LOCAL_SSID=$(grep "^SSID=" "${settings}" | cut -d'=' -f2- | sed 's/^"//;s/"$//;s/^\x27//;s/\x27$//')
    local LOCAL_PASS=$(grep "^PWD=" "${settings}" | cut -d'=' -f2- | sed 's/^"//;s/"$//;s/^\x27//;s/\x27$//')

    # ... (PHY detection logic) ...

    # Fix for SSID (using hex encoding for safety)
    printf "ssid2=%s\n" "$(echo -n "${LOCAL_SSID}" | xxd -p)"
    echo "utf8_ssid=1"

    # ... (other config) ...

    # Fix for PSK assignment
    case "${ENC}" in
        wpa3|wpa2+3|wpa2|wpa1+2|wpa1)
            echo "wpa_passphrase=${LOCAL_PASS}" ;;
    esac
}

This approach ensures that the password and SSID are treated as literal strings, regardless of the characters they contain.

Have fun :slight_smile:

7 Likes

Hopehully you can submit this as a patch somewhwere?

Nice work.