diff options
Diffstat (limited to 'bsdconfig/share/media/wlan.subr')
-rw-r--r-- | bsdconfig/share/media/wlan.subr | 1392 |
1 files changed, 1392 insertions, 0 deletions
diff --git a/bsdconfig/share/media/wlan.subr b/bsdconfig/share/media/wlan.subr new file mode 100644 index 000000000000..f25cddff94cd --- /dev/null +++ b/bsdconfig/share/media/wlan.subr @@ -0,0 +1,1392 @@ +if [ ! "$_MEDIA_WLAN_SUBR" ]; then _MEDIA_WLAN_SUBR=1 +# +# Copyright (c) 2013-2016 Devin Teske +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." media/wlan.subr +f_include $BSDCFG_SHARE/device.subr +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/strings.subr +f_include $BSDCFG_SHARE/sysrc.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ GLOBALS + +NWIRELESS_CONFIGS=0 +NWSCAN_RESULTS=0 + +# +# Settings used while interacting with various dialog(1) menus +# +: ${DIALOG_MENU_WLAN_SCAN_DURATION:=5} +: ${DIALOG_MENU_WLAN_SHOW_ALL=} +: ${DIALOG_MENU_WLAN_SHOW_CONFIGURED=1} +: ${DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS=1} + +# +# Structure to contain the wpa_supplicant.conf(5) default overrides +# +f_struct_define WPA_DEFAULTS \ + ap_scan \ + ctrl_interface \ + ctrl_interface_group \ + eapol_version \ + fast_reauth + +# +# Structure of wpa_supplicant.conf(5) network={ ... } entry +# +f_struct_define WPA_NETWORK \ + anonymous_identity \ + auth_alg \ + bssid \ + ca_cert \ + ca_cert2 \ + client_cert \ + client_cert2 \ + dh_file \ + dh_file2 \ + eap \ + eap_workaround \ + eapol_flags \ + eappsk \ + engine \ + engine_id \ + frequency \ + group \ + identity \ + key_id \ + key_mgmt \ + mixed_cell \ + mode \ + nai \ + pac_file \ + pairwise \ + password \ + pcsc \ + phase1 \ + phase2 \ + pin \ + priority \ + private_key \ + private_key2 \ + private_key2_passwd \ + private_key_passwd \ + proto \ + psk \ + scan_ssid \ + server_nai \ + ssid \ + subject_match \ + subject_match2 \ + wep_key0 \ + wep_key1 \ + wep_key2 \ + wep_key3 \ + wpa_ptk_rekey \ + wep_tx_keyidx + +# +# The following properties are ``Lists'' and as such should not be quoted. +# Everything else should be quoted. +# +WPA_NETWORK_LIST_PROPERTIES=" + auth_algo + eap + group + key_mgmt + pairwise + proto +" # END-QUOTE + +# +# Structure of wpa_cli(8) `scan_results' entry +# +f_struct_define WPA_SCAN_RESULT \ + bssid \ + flags \ + freq \ + siglev \ + ssid + +# +# Structure of a menu item in the wireless editor +# +f_struct_define WLAN_MENU_ITEM \ + letter \ + ssid \ + nconfigs \ + nfound \ + help + +############################################################ FUNCTIONS + +# f_wpa_supplicant_init $file +# +# Initialize $file with basic contents of new wpa_supplicant.conf(5). +# +f_wpa_supplicant_init() +{ + local funcname=f_wpa_supplicant_init + local conf_file="$1" tmpfile + + # Create a temporary file + f_eval_catch -k tmpfile $funcname mktemp 'mktemp -t "%s"' "$pgm" || + return $FAILURE + + # Make it unreadable by anyone but ourselves + f_eval_catch $funcname chmod \ + 'chmod 0600 "%s"' "$tmpfile" || return $FAILURE + + # Make it owned by root/wheel + f_eval_catch $funcname chown \ + 'chown 0:0 "%s"' "$tmpfile" || return $FAILURE + + # Populate it + cat <<-EOF >> "$tmpfile" + ctrl_interface=/var/run/wpa_supplicant + eapol_version=2 + ap_scan=1 + fast_reauth=1 + EOF + echo >> "$tmpfile" + + # Move it into place + f_eval_catch $funcname mv 'mv "%s" "%s"' "$tmpfile" "$conf_file" +} + +# f_wpa_supplicant_parse $file [struct_prefix [count_var]] +# +# Parse wpa_supplicant.conf(5) $file. Default overrides are stored in a struct +# (see struct.subr for additional details) named `{struct_prefix}defaults'. See +# WPA_DEFAULTS struct definition in the GLOBALS section above. +# +# In addition, for each one of the wireless networks we parse from $file, +# create a struct named `struct_prefixN' where `N' is a number starting from 1 +# and ending in $count_var (zero means no networks). See WPA_NETWORK struct +# definition in the GLOBALS section above. +# +# If a `blob-base64-*={ ... }' entry appears, a struct named +# `{struct_prefix}blob_base64_*' is created and the `data' property holds the +# base64 encoded binary data without whitespace. +# +# Custom `*={ ... }' definitions are also supported, but should be unique +# (unlike the `network' definition). A struct named `{struct_prefix}*' is +# created if at least one property is defined in the block. +# +f_wpa_supplicant_parse() +{ + local file="$1" struct_prefix="$2" count_var="$3" + + [ "$count_var" ] && setvar "$count_var" 0 + + [ "$file" ] || file=$( f_sysrc_get wpa_supplicant_conf_file ) + if [ ! -e "$file" ]; then + f_dprintf "%s: No such file or directory" "$file" + return $FAILURE + fi + + local list_properties + f_replaceall "$WPA_NETWORK_LIST_PROPERTIES" "$NL" "" list_properties + eval "$( awk \ + -v count_var="$count_var" \ + -v struct_prefix="$struct_prefix" \ + -v list_properties="$list_properties" ' + BEGIN { + if (!count_var && !struct_prefix) exit + blob = count = custom_struct = network = 0 + split(list_properties, lists, FS) + } + function set_value(struct, prop, value) + { + quoted = substr(value, 0, 1) == "\"" + for (l in lists) if (list = prop == lists[l]) break + # Remove data after whitespace if unquoted and not a list + if (!quoted && !list) sub("[[:space:]].*", "", value) + # Otherwise if quoted and not a list, remove the quotes + # NB: wep_keyN needs to retain quoting if/when present + else if (quoted && !list && prop !~ /^wep_key[[:digit:]]+/) { + sub("^\"", "", value) + sub("\".*", "", value) + } + gsub(/'\''/, "'\''\\'\'\''", value) # Sanitize the value + if (!created[struct]) { + print "debug= f_struct_free", struct + print "debug= f_struct_new WPA_NETWORK", struct + created[struct] = 1 + } + printf "debug= %s set %s '\'%s\''\n", struct, prop, value + } + { + if ($1 ~ /^network={/) { + empty = 1 # We do not increment count unless !empty + network = 1 + next + } else if (match($1, "^blob-base64-[[:alnum:]_./-]+={")) { + blob = 1 + blob_data = "" + struct = struct_prefix "blob_bas64_" + struct = struct substr($1, 13, RLENGTH - 14) + next + } else if (match($1, "^[[:alnum:]_./-]+={")) { + empty = 1 + custom_struct = 1 + struct = struct_prefix substr($1, 0, RLENGTH - 2) + gsub(/[^[:alnum:]_]/, "_", struct) + next + } else if ($1 ~ /^}/) { + if (blob) { + gsub("[[:space:]]", "", blob_data) + set_value(struct, "data", blob_data) + } + blob = custom_struct = network = 0 + next + } else if (!match($0, /^[[:space:]]*[[:alnum:]_]+=/)) + next + + if (blob) { + blob_data = blob_data $0 + next + } else if (network) { + if (empty) { count++; empty = 0 } + struct = struct_prefix count + } else if (!custom_struct) + struct = struct_prefix "defaults" + + if (!struct_prefix) next + + prop = substr($0, 0, RLENGTH - 1) + sub(/^[[:space:]]*/, "", prop) + value = substr($0, RSTART + RLENGTH) + + set_value(struct, prop, value) + } + END { if (count_var) print count_var "=" count }' "$file" )" +} + +# f_wpa_scan_results_parse [struct_prefix [count_var]] +# +# Parse the results of wpa_cli(8) `scan_results' into a series of structs (see +# struct.subr for additional details) named `struct_prefixN' where `N' is a +# number starting from 1 and ending in $count_var (zero means no results). See +# WPA_SCAN_RESULT struct definition in the GLOBALS section above. +# +f_wpa_scan_results_parse() +{ + local struct_prefix="$1" count_var="$2" + + [ "$count_var" ] && setvar "$count_var" 0 + + eval "$( wpa_cli scan_results 2> /dev/null | awk \ + -v count_var="$count_var" \ + -v struct_prefix="$struct_prefix" ' + BEGIN { + if (!count_var && !struct_prefix) exit + count = 0 + seg = "[[:xdigit:]][[:xdigit:]]" + bssid = seg":"seg":"seg":"seg":"seg":"seg + freq = siglev = flags = "[^[:space:]]+" + S = "[[:space:]]+" + line = bssid S freq S siglev S flags + line = "^[[:space:]]*" line "[[:space:]]*" + } + function set_value(struct, prop, value) + { + gsub(/'\''/, "'\''\\'\'\''", value) # Sanitize the value + if (!created[struct]) { + print "debug= f_struct_free", struct + print "debug= f_struct_new WPA_SCAN_RESULT", struct + created[struct] = 1 + } + printf "debug= %s set %s '\'%s\''\n", struct, prop, value + } + { + if (!match($0, line)) next + ssid = substr($0, RLENGTH + 1) + + count++ + if (!struct_prefix) next + + struct = struct_prefix count + set_value(struct, "ssid", ssid) + set_value(struct, "bssid", $1) + set_value(struct, "freq", $2) + set_value(struct, "siglev", $3) + set_value(struct, "flags", $4) + } + END { if (count_var) print count_var "=" count }' )" +} + +# f_wpa_scan_match_network WPA_SCAN_RESULT WPA_NETWORK +# +# Compares a WPA_SCAN_RESULT struct to a WPA_NETWORK struct. If they appear to +# be a match returns success, otherwise failure. +# +f_wpa_scan_match_network() +{ + local scan_struct="$1" wireless_struct="$2" + local cp debug= + + f_struct "$scan_struct" || return $FAILURE + f_struct "$wireless_struct" || return $FAILURE + + local scan_ssid scan_bssid + $scan_struct get ssid scan_ssid + $scan_struct get bssid scan_bssid + local wireless_ssid wireless_bssid + $wireless_struct get ssid wireless_ssid + $wireless_struct get bssid wireless_bssid + + local id_matched= + if [ "$wireless_ssid" -a "$wireless_bssid" ]; then + # Must match both SSID and BSSID + [ "$scan_ssid" = "$wireless_ssid" -a \ + "$scan_bssid" = "$wireless_bssid" ] && id_matched=1 + elif [ "$wireless_ssid" ]; then + # Must match SSID only + [ "$scan_ssid" = "$wireless_ssid" ] && id_matched=1 + elif [ "$wireless_bssid" ]; then + # Must match BSSID only + [ "$scan_bssid" = "$wireless_bssid" ] && id_matched=1 + fi + [ "$id_matched" ] || return $FAILURE + + + # + # Get the scanned flags for the next few comparisons + # + local flags + $scan_struct get flags flags + + # + # Compare configured key management against scanned network + # + if $wireless_struct get key_mgmt cp && [ "$cp" -a "$cp" != "NONE" ] + then + local mgmt mgmt_matched= + for mgmt in $cp; do + local mgmt2="$mgmt" + [ "$mgmt" != "${mgmt#WPA-}" ] && + mgmt2="WPA2${mgmt#WPA}" + case "$flags" in + "$mgmt"|"$mgmt"-*|*-"$mgmt"|*-"$mgmt"-*) + mgmt_matched=1 break ;; + "$mgmt2"|"$mgmt2"-*|*-"$mgmt2"|*-"$mgmt2"-*) + mgmt_matched=1 break ;; + esac + done + [ "$mgmt_matched" ] || return $FAILURE + fi + + local enc type flag + + # + # Compare configured encryption against scanned network + # + for enc in psk:PSK eap:EAP \ + wep_key0:WEP wep_key1:WEP wep_key2:WEP wep_key3:WEP + do + type=${enc%%:*} + flag=${enc#*:} + { debug= $wireless_struct get $type cp && [ "$cp" ]; } || + continue + # Configured network requires encryption + case "$flags" in "[$flag]"|*"-$flag-"*) + break # Success; stop after first match + esac + return $FAILURE + done + cp="" # sensitive info + + # + # Compare scanned network encryption against configuration + # NB: Scanned network flags indicates _one_ of PSK EAP or WEP + # NB: Otherwise, no encryption (so encryption won't match) + # + local enc_wanted= + for enc in -PSK-:psk -EAP-:eap; do + flag=${enc%%:*} + type=${enc#*:} + case "$flags" in *"$flag"*) + enc_wanted=1 + { debug= $wireless_struct get $type cp && + [ "$cp" ]; } || return $FAILURE + break # success + esac + done + case "$flags" in *"[WEP]"*) + enc_wanted=1 + local wep_found= + for type in wep_key0 wep_key1 wep_key2 wep_key3; do + debug= $wireless_struct get $type cp && [ "$cp" ] && + wep_found=1 break + done + [ "$wep_found" ] || return $FAILURE + esac + if [ ! "$enc_wanted" ]; then + # No match if the network specifies encryption + for type in psk eap wep_key0 wep_key1 wep_key2 wep_key3; do + debug= $wireless_struct get $type cp && [ "$cp" ] && + return $FAILURE + done + fi + cp="" # sensitive info + + return $SUCCESS +} + +# f_wpa_scan_find_matches scans_prefix $scans_count \ +# wireless_prefix $wireless_count +# +# For each struct from `{scans_prefix}1' up to `{scans_prefix}$scans_count' +# (see struct.subr for additional details) compare the wireless network info +# (defined as struct WPA_SCAN_RESULT) to that of each configured wireless +# stored in `{wireless_prefix}1' (defined as struct WPA_NETWORK) up to +# `{wireless_prefix}$wireless_count'. +# +# If a scanned network is deemed to be a match to a configured wireless +# network, a new `match' property is set on the WPA_NETWORK struct with a value +# of `{scans_prefix}N' (where N represents the scanned network that matched). +# At the same time, a new `matched' property is set on the WPA_SCAN_RESULT +# struct with a value of 1, indicating that this network has been matched to a +# stored [known] configuration and that it should not be displayed in menus. +# +# NB: If a matching entry is not correct, the user can optionally `Forget' the +# network and that will cause the WPA_SCAN_RESULT to no longer match anything, +# causing it to appear in the menus again. +# +# Return status should be ignored. +# +f_wpa_scan_find_matches() +{ + local scans_prefix="$1" scans_count="$2" + local wireless_prefix="$3" wireless_count="$4" + local matches + + [ "$scans_count" -a "$wireless_count" ] || return $SUCCESS + f_isinteger "$scans_count" || return $FAILURE + f_isinteger "$wireless_count" || return $FAILURE + + # + # Go through and eradicate any flags we set in a prior run, as things + # might have changed on us (either from the config side or scan side) + # + local w=1 + while [ $w -le $wireless_count ]; do + f_struct "$wireless_prefix$w" set matches "" + w=$(( $w + 1 )) + done + + # + # Find matches and set match data on structs + # + local s=1 + while [ $s -le $scans_count ]; do + f_struct "$scans_prefix$s" set matched "" + w=1 + while [ $w -le $wireless_count ]; do + if f_wpa_scan_match_network \ + "$scans_prefix$s" "$wireless_prefix$w" + then + f_struct "$scans_prefix$s" set matched 1 + debug= f_struct "$wireless_prefix$w" \ + get matches matches + matches="$matches${matches:+ }$scans_prefix$s" + f_struct "$wireless_prefix$w" \ + set matches "$matches" + break # to next scan result + fi + w=$(( $w + 1 )) + done + s=$(( $s + 1 )) + done +} + +# f_dialog_menu_wlandev_edit $wlandev [$defaultitem] +# +# Display a list of wireless network devices (wlan*) associated with +# $wlandev (e.g., `iwn0'). Allow the user to create and destroy wlan interfaces +# while selecting ones to be cloned at startup (by setting `wlans_$wlandev'). +# +f_dialog_menu_wlandev_edit() +{ + local funcname=f_dialog_menu_wlandev_edit + local wlandev="$1" defaultitem="$2" + local title="$DIALOG_TITLE" + local btitle="$DIALOG_BACKTITLE" + local prompt # Calculated below + local hline="$hline_arrows_tab_enter" + + [ "$wlandev" ] || return $FAILURE + + f_sprintf prompt "$msg_select_wlan_interfaces_for" "wlandev" + + # + # Initially mark wlan devices with a %parent of $wlandev + # + local dev devs if list_to_save= + f_device_find "" $DEVICE_TYPE_NETWORK devs + for dev in $devs; do + f_struct "$dev" get name if || continue + case "$if" in wlan[0-9]*) + parent=$( sysctl -n net.wlan.${if#wlan}.%parent \ + 2> /dev/null ) + if [ "$parent" = "$if" ]; then + local _wlanmark_$if="X" + list_to_save="$list_to_save $if" + fi + esac + done + list_to_save="${list_to_save# }" + + # + # Operate in a loop so we can create/destroy interfaces from here + # + while :; do + # + # Refresh list of wlan interfaces + # + local wlanlist= + f_device_rescan_network + f_device_find "" $DEVICE_TYPE_NETWORK devs + for dev in $devs; do + f_struct "$dev" get name if || continue + case "$if" in wlan[0-9]*) + wlanlist="$wlanlist $if" + esac + done + + # + # Build menu list of wlan devices + # + local menu_list=" + '> $msg_save_exit' '$msg_return_to_previous_menu' + '> $msg_create_new' 'wlan' + '> $msg_destroy' '...' + " # END-QUOTE + local parent X + for if in $wlanlist; do + f_getvar _wlanmark_$if-" " X + menu_list="$menu_list '[$X] $if' '%parent: $parent'" + [ "$defaultitem" = "$if" ] && defaultitem="[$X] $if" + done + + # + # Ask user to make a choice + # + local height width rows + eval f_dialog_menu_size height width rows \ + \"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" \ + $menu_list + local menu_choice + menu_choice=$( eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_select\" \ + --cancel-label \"\$msg_cancel\" \ + --default-item \"\$defaultitem\" \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) || return $FAILURE + f_dialog_data_sanitize menu_choice + + case "$menu_choice" in + "> $msg_save_exit") # Save list to rc.conf(5) `wlans_$wlandev' + f_eval_catch $funcname f_sysrc_set \ + 'f_sysrc_set "wlans_%s" "%s"' \ + "$wlandev" "$list_to_save" || continue + break # to success + ;; + "> $msg_create_new") # Create new wlan interface for wlandev + local wlan + f_eval_catch -k wlan $funcname ifconfig \ + 'ifconfig wlan create wlandev "%s"' \ + "$wlandev" || continue + local _wlanmark_$wlan="X" + list_to_save="$list_to_save${list_to_save:+ }$wlan" + ;; + "> $msg_destroy") # Display a menu to pick one item to destroy + [ "$wlanlist" ] || continue # Nothing to destroy + + menu_list= + for if in $wlanlist; do + menu_list="$menu_list '$if' ''" + done + local msg="$msg_pick_an_interface_to_destroy" + eval f_dialog_menu_size height width rows \ + \"\$title\" \"$btitle\" \"\$msg\" \"\" $menu_list + menu_choice=$( eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_destroy\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$msg\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) || continue + f_dialog_data_sanitize menu_choice + f_eval_catch $funcname ifconfig \ + 'ifconfig "%s" destroy' "$menu_choice" + ;; + "[ ] wlan"[0-9]*) # Unmarked; Mark + if="${menu_choice#??? }" + local _wlanmark_$if="X" + list_to_save="$list_to_save${list_to_save:+ }$if" + ;; + "[X] wlan"[0-9]*) # Marked; Unmark + menu_choice="${menu_choice#??? }" + local _wlanmark_$menu_choice=" " + local new_list_to_save= + for if in $list_to_save; do + [ "$if" = "$menu_choice" ] && continue + new_list_to_save="$new_list_to_save $if" + done + list_to_save="${new_list_to_save# }" + ;; + esac + done + + return $SUCCESS +} + +# f_dialog_scan_wireless +# +# Initiate a scan for wireless networks. If wpa_supplicant(8) is not running +# but a wlan interface has been created, start an instance of wpa_supplicant(8) +# with the first wlan(4) interface we find. After initiating the scan, displays +# a message for 5 seconds (with option to dismiss). Returns failure if an error +# occurs, otherwise success. +# +f_dialog_scan_wireless() +{ + local funcname=f_dialog_scan_wireless + + # + # Try to communicate with a running wpa_supplicant(8) + # + if ! f_eval_catch -d $funcname wpa_cli 'wpa_cli ping'; then + + # If there is indeed one running, bail! + if ps axo ucomm= | grep -qw wpa_supplicant; then + f_show_msg "$msg_failed_to_reach_wpa_supplicant" \ + "$msg_wpa_cli_ping_failed" + return $FAILURE + fi + + # Try and find a wlan device so we can start wpa_supplicant + local dev devs if wlan= + f_device_rescan_network + f_device_find "" $DEVICE_TYPE_NETWORK devs + for dev in $devs; do + f_struct "$dev" get name if || continue + case "$if" in wlan[0-9]*) + wlan=$if + break + esac + done + if [ ! "$wlan" ]; then + # We can't start wpa_supplicant without wlan interface + # Tell the user they have to create one by navigating + # to a Wireless device to create a wlan interface. But + # let's go one step further and find an interface that + # we can provide in the prompt text. + local wlandev= + for if in $devs; do + case "$if" in wlan[0-9]*) next; esac + if f_device_is_wireless $if; then + wlandev=$if + break + fi + done + if [ "$wlandev" ]; then + f_show_msg "$msg_cant_start_wpa_supplicant" \ + "$wlandev" + else + # Warn user, appears no wireless available + f_show_msg "$msg_warning_no_wireless_devices" + fi + return $FAILURE + fi + + # NB: Before we can proceed to fire up wpa_supplicant(8), let's + # make sure there is a bare-bones wpa_supplicant.conf(5) for it + local conf_file + conf_file=$( f_sysrc_get wpa_supplicant_conf_file ) + if [ ! -e "$conf_file" ]; then + f_wpa_supplicant_init "$conf_file" || return $FAILURE + f_eval_catch -d $funcname wpa_cli 'wpa_cli reconfigure' + fi + + # Try and start wpa_supplicant(8) + f_eval_catch $funcname wpa_supplicant \ + '/etc/rc.d/wpa_supplicant start "%s"' "$wlan" || + return $FAILURE + + # Try to reach this new wpa_supplicant(8) + if ! f_eval_catch -d $funcname wpa_cli 'wpa_cli ping'; then + f_show_msg "$msg_failed_to_reach_wpa_supplicant" \ + "$msg_wpa_cli_ping_failed" + return $FAILURE + fi + + fi # ! f_quietly wpa_cli ping + + # If we reach hear, then it should be OK to scan the airwaves + f_eval_catch -d $funcname wpa_cli 'wpa_cli scan' || return $FAILURE + + # Return immediately if a duration is: null or not a number >= 1 + local duration="$DIALOG_MENU_WLAN_SCAN_DURATION" + f_isinteger "$duration" || return $SUCCESS + [ $duration -gt 0 ] || return $SUCCESS + + # Display a message that times-out if not dismissed manually + local prompt + f_sprintf prompt "$msg_scanning_wireless_pausing" "$duration" + f_dialog_pause "$prompt" "$duration" +} + +# f_dialog_wireless_edit $ssid +# +# Display a menu to allow the user to either create a new entry for the +# wpa_supplicants.conf(5) file, or to edit values for an existing entry. +# +# If more than one wireless network is found to match $ssid, a sub-menu is +# presented, allowing the user to select the desired network. +# +f_dialog_wireless_edit() +{ + local title="$DIALOG_TITLE" + local btitle="$DIALOG_BACKTITLE" + local prompt1="$msg_select_the_configuration_you_would_like" + local prompt2 # Calculated below + local hline="$hline_alnum_arrows_punc_tab_enter" + local ssid="$1" bssid="$2" + + f_sprintf prompt2 "$msg_wireless_network_configuration_for" "$ssid" + + # + # Find one or more configurations that match the SSID selection + # + local height1 width1 rows1 menu_list1= + local n=0 nmatches=0 tag wssid wbssid help matches= + while [ $n -lt $NWIRELESS_CONFIGS ]; do + n=$(( $n + 1 )) + + debug= f_struct WIRELESS_$n get ssid wssid + [ "$ssid" = "$wssid" ] || continue + debug= f_struct WIRELESS_$n get bssid wbssid + [ "${bssid:-$wbssid}" = "$wbssid" ] || continue + + nmatches=$(( $nmatches + 1 )) + [ $nmatches -le ${#DIALOG_MENU_TAGS} ] || break + f_substr -v tag "$DIALOG_MENU_TAGS" $nmatches 1 + + f_wireless_describe WIRELESS_$n help + menu_list1="$menu_list1 + '$tag $wssid' '$wbssid' '$help' + " # END-QUOTE + + matches="$matches WIRELESS_$n" + done + if [ $nmatches -eq 0 ]; then + f_show_msg "$msg_cannot_edit_wireless_ssid" "$ssid" + return $FAILURE + elif [ $nmatches -eq 1 ]; then + struct=${matches# } + else + eval f_dialog_menu_with_help_size height1 width1 rows1 \ + \"\$title\" \"\$btitle\" \"\$prompt1\" \"\$hline\" \ + $menu_list1 + fi + + # + # Operate in a loop; for the case of $nmatches > 1, we can cycle back + # to allow the user to make another choice after inspecting each one. + # + local menu_choice index struct defaultitem1= + while :; do + if [ $nmatches -gt 1 ]; then + menu_choice=$( eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_select\" \ + --cancel-label \"\$msg_cancel\" \ + --item-help \ + --default-item \"\$defaultitem1\" \ + --menu \"\$prompt1\" \ + $height1 $width1 $rows1 \ + $menu_list1 \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) || return $FAILURE + f_dialog_data_sanitize menu_choice + defaultitem1="$menu_choice" + index=$( eval f_dialog_menutag2index_with_help \ + \"\$menu_choice\" $menu_list1 ) + struct=$( set -- $matches; eval echo \${$index} ) + fi + + # + # Operate within another loop to allow editing multiple values + # + local menu_list2 height2 width2 rows2 member + while :; do + menu_list2=" + '> $msg_save_exit' + '$msg_return_to_previous_menu' + " # END-QUOTE + n=0 + for member in $_struct_typedef_WPA_NETWORK; do + [ "$member" = "ssid" ] && continue + debug= $struct get $member value || continue + n=$(( $n + 1 )) + [ $n -le ${#DIALOG_MENU_TAGS} ] || break + f_substr -v tag "$DIALOG_MENU_TAGS" $n 1 + if [ ${#value} -gt 32 ]; then + f_snprintf value 29 "%s" "$value" + value="$value..." + fi + case "$member" in + password|pin|private_key_passwd|psk|wep_key*) + f_replaceall "$value" "?" "*" value ;; + esac + f_shell_escape "$value" value + menu_list2="$menu_list2 + '$tag $member' '$value' + " # END-QUOTE + done + eval f_dialog_menu_size height2 width2 rows2 \ + \"\$title\" \"\$btitle\" \"\$prompt2\" \ + \"\$hline\" $menu_list2 + menu_choice=$( eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_select\" \ + --cancel-label \"\$msg_cancel\" \ + --default-item \"\$defaultitem2\" \ + --menu \"\$prompt2\" \ + $height2 $width2 $rows2 \ + $menu_list2 \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) || break + f_dialog_data_sanitize menu_choice + defaultitem2="$menu_choice" + + # XXXDT Unfinished + done + [ $nmatches -eq 1 ] && break + done + + # + # XXXDT Unfinished + # This is where we display a menu that edits the entry + # And then we modify the wpa_supplicants.conf(5) config file + # XXXDT Unfinished + # + + return $FAILURE # XXXDT Simulating DIALOG_CANCEL to mean ``no changes'' +} + +# f_wireless_describe WPA_NETWORK [$var_to_set] +# +# Provide a description of the WPA_NETWORK struct. If $var_to_set is missing or +# NULL, the description is provided on standard output (which is less preferred +# due to performance; e.g., if called in a loop). +# +f_wireless_describe() +{ + local __struct="$1" __var_to_set="$2" debug= + + [ "$__var_to_set" ] && setvar "$__var_to_set" "" + f_struct "$__struct" || return $FAILURE + + # + # Basic description is `proto key_mgmt group eap' + # + local __member __cp __desc= + for __member in proto key_mgmt group eap; do + $__struct get $__member __cp && [ "$__cp" ] && + __desc="$__desc${__desc:+ }$__cp" + done + + local __check __kk + + # + # Make sure we add WEP40/WEP140 even if omitted from the key_mgmt + # section of entry + # + local __wep_keyN __f_wireless_describe_first_char __length + for __wep_keyN in wep_key0 wep_key1 wep_key2 wep_key3; do + $__struct get $__wep_keyN __kk + [ "$__kk" ] || continue + + # What type is it? ASCII or HEX? + __check=WEP + f_substr -v __f_wireless_describe_first_char "$__kk" 1 1 + case "$__f_wireless_describe_first_char" in + \") # ASCII + __length=$(( ${#__kk} - 2 )) + if [ $__length -le 5 ]; then + __check=WEP40 + elif [ $__length -le 13 ]; then + __check=WEP104 + fi ;; + *) # HEX + __length=${#__kk} + if [ $__length -eq 10 ]; then + __check=WEP40 + elif [ $__length -le 26 ]; then + __check=WEP104 + fi + esac + __kk="" # sensitive info + + case "$__desc" in + *"$__check"*) : already there ;; + *) __desc="$__desc${__desc:+ }$__check" + esac + done + + # + # Make sure we display PSK even if omitted + # from the key_mgmt section of the entry + # + $__struct get psk __kk + if [ "$__kk" ]; then + __kk="" # sensitive info + __check=PSK + case "$__desc" in + *"$__check"*) : already there ;; + *) __desc="$__desc${__desc:+ }$__check" + esac + fi + + # + # Produce results + # + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" "${__desc:-NONE}" + else + echo "$__desc" + fi +} + +# f_menu_wireless_configs +# +# Generates the tag/item/help triplets for wireless network menu (`--item-help' +# required) from wpa_supplicant.conf(5) [WPA_NETWORK] structs. +# +f_menu_wireless_configs() +{ + [ "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" ] || return $SUCCESS + + echo "' - $msg_configured_ssids -' ' - $msg_details -' ''" + + local n=0 nunique=0 debug= + local ssid ussid matches nmatches nconfigs nfound help desc w + while [ $n -lt $NWIRELESS_CONFIGS ]; do + n=$(( $n + 1 )) + + f_struct WIRELESS_$n get ssid ssid + [ ! "$DIALOG_MENU_WLAN_SHOW_ALL" -a ! "$ssid" ] && continue + + local u=0 unique=1 + while [ $u -lt $nunique ]; do + u=$(( $u + 1 )) + menuitem_$u get ssid ussid + [ "$ssid" != "$ussid" ] || unique= break + done + if [ "$unique" ]; then + nunique=$(( $nunique + 1 )) + u=$nunique + + # Set SSID and initialize number of configs found (1) + f_struct_new WLAN_MENU_ITEM menuitem_$u + menuitem_$u set ssid "$ssid" + menuitem_$u set nconfigs 1 + + # Set number of wireless networks that match config + WIRELESS_$n get matches matches + f_count nmatches $matches + menuitem_$u set nfound $nmatches + + # Set help to description of the wireless config + f_wireless_describe WIRELESS_$n desc + menuitem_$u set help "$desc" + else + # Increment number of configs found with this SSID + menuitem_$u get nconfigs nconfigs + nconfigs=$(( $nconfigs + 1 )) + menuitem_$u set nconfigs $nconfigs + + # Add number of matched networks to existing count + WIRELESS_$n get matches matches + f_count nmatches $matches + menuitem_$u get nfound nfound + nfound=$(( $nfound + $nmatches )) + menuitem_$u set nfound $nfound + + # Combine description with existing help + menuitem_$u get help help + f_wireless_describe WIRELESS_$n desc + for w in $desc; do + case "$help" in + "$w"|"$w "*|*" $w"|*" $w "*) : already there ;; + *) help="$help $w" + esac + done + menuitem_$u set help "${help# }" + fi + done + + n=0 + while [ $n -lt $nunique ]; do + n=$(( $n + 1 )) + menuitem_$n get ssid ssid + + menuitem_$n get nconfigs nconfigs + desc="$nconfigs $msg_configured_lc" + [ $nconfigs -lt 10 ] && desc=" $desc" + menuitem_$n get nfound nfound + [ $nfound -gt 0 ] && desc="$desc $nfound $msg_found" + + menuitem_$n get help help + echo "'[X] $ssid' '$desc' '$help'" + done | sort -bf | awk 'BEGIN { prefix = "" } + { + cur_prefix = toupper(substr($0, 6, 1)) + if (cur_prefix != "'\''" && prefix != cur_prefix ) { + prefix = cur_prefix + printf "'\''%c%s\n", prefix, substr($0, 2) + } else + printf "'\'' %s\n", substr($0, 2) + }' +} + +# f_menu_wpa_scan_results +# +# Generates the tag/item/help triplets for wireless network menu (`--item-help' +# required) from wpa_cli(8) `scan_results' [WPA_SCAN_RESULT] structs. +# +f_menu_wpa_scan_results() +{ + [ "$DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS" ] || return $SUCCESS + + if [ "$DIALOG_MENU_WLAN_SHOW_ALL" ]; then + echo "' - $msg_discovered_ssids -' ' - $msg_details -' ''" + else + echo "' - $msg_discovered_ssids -' '' ''" + fi + + local n=0 nunique=0 debug= + local ssid ussid matched nfound help flags f + while [ $n -lt $NWSCAN_RESULTS ]; do + n=$(( $n + 1 )) + + WSCANS_$n get ssid ssid + [ ! "$DIALOG_MENU_WLAN_SHOW_ALL" -a ! "$ssid" ] && continue + + WSCANS_$n get matched matched + [ "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" -a "$matched" ] && + continue + + local u=0 unique=1 + while [ $u -lt $nunique ]; do + u=$(( $u + 1 )) + menuitem_$u get ssid ussid + [ "$ssid" != "$ussid" ] || unique= break + done + if [ "$unique" ]; then + nunique=$(( $nunique + 1 )) + u=$nunique + + # Set SSID and initialize number of networks found (1) + f_struct_new WLAN_MENU_ITEM menuitem_$u + menuitem_$u set ssid "$ssid" + menuitem_$u set nfound 1 + + # Set help to flags + WSCANS_$n get flags flags + f_replaceall "$flags" "[" " " flags + f_replaceall "$flags" "]" "" flags + flags="${flags# }" + case "$flags" in + "") flags="NONE" ;; + ESS) flags="NONE ESS" ;; + esac + menuitem_$u set help "$flags" + else + # Increment number of networks found with this SSID + menuitem_$u get nfound nfound + nfound=$(( $nfound + 1 )) + menuitem_$u set nfound $nfound + + # Combine flags into existing help + WSCANS_$n get flags flags + f_replaceall "$flags" "[" " " flags + f_replaceall "$flags" "]" "" flags + local flags_ess= + case "$flags" in *" ESS") + flags_ess=1 + flags="${flags% ESS}" + esac + local help_ess= + menuitem_$u get help help + case "$help" in *" ESS") + help_ess=1 + help="${help% ESS}" + esac + for f in ${flags:-NONE}; do + case "$help" in + "$f"|"$f "*|*" $f"|*" $f "*) : already there ;; + *) help="$help $f" + esac + done + [ "$flags_ess" -a ! "$help_ess" ] && help="$help ESS" + menuitem_$u set help "${help# }" + fi + done + + local desc n=0 + while [ $n -lt $nunique ]; do + n=$(( $n + 1 )) + menuitem_$n get ssid ssid + + desc= + if [ "$DIALOG_MENU_WLAN_SHOW_ALL" ]; then + menuitem_$n get nfound nfound + desc="$nfound $msg_found" + [ $nfound -lt 10 ] && desc=" $desc" + fi + + menuitem_$n get help help + echo "'[ ] $ssid' '$desc' '$help'" + done | sort -bf | awk 'BEGIN { prefix = "" } + { + cur_prefix = toupper(substr($0, 6, 1)) + if (cur_prefix != "'\''" && prefix != cur_prefix ) { + prefix = cur_prefix + printf "'\''%c%s\n", prefix, substr($0, 2) + } else + printf "'\'' %s\n", substr($0, 2) + }' +} + +# f_dialog_menu_wireless_edit +# +# Display a list of wireless networks configured in wpa_supplicants.conf(5) and +# (if wpa_supplicant(8) is running) also displays scan results for unconfigured +# wireless networks. +# +f_dialog_menu_wireless_edit() +{ + local funcname=f_dialog_menu_wireless_edit + local title="$DIALOG_TITLE" + local btitle="$DIALOG_BACKTITLE" + local prompt="$msg_wireless_networks_text" + local menu_list # Calculated below + local defaultitem= # Calculated below + local hline="$hline_alnum_arrows_punc_tab_enter" + + f_show_info "$msg_loading_wireless_menu" + + local conf_file + conf_file=$( f_sysrc_get wpa_supplicant_conf_file ) + + # + # Operate in a loop so we can edit wpa_supplicant.conf(5) and rescan + # for new wireless networks from here. + # + local do_parse=1 remake_menu=1 item + while :; do + # + # If this is the first time here, parse wpa_supplicant.conf(5), + # scan the airwaves, and compare to find matches. + # + if [ "$do_parse" -a "$DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS" ] + then + f_dprintf "$funcname: Parsing wireless scan results" + f_dialog_scan_wireless && + f_wpa_scan_results_parse WSCANS_ NWSCAN_RESULTS + f_dprintf "$funcname: Parsed %i scanned networks" \ + $NWSCAN_RESULTS + fi + if [ "$do_parse" -a "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" ] + then + f_dprintf "$funcname: Parsing wpa_supplicants.conf(5)" + f_wpa_supplicant_parse "$conf_file" \ + WIRELESS_ NWIRELESS_CONFIGS + f_dprintf "%s: Parsed %i wireless configurations" \ + $funcname $NWIRELESS_CONFIGS + f_wpa_scan_find_matches WSCANS_ $NWSCAN_RESULTS \ + WIRELESS_ $NWIRELESS_CONFIGS + fi + do_parse= + + if [ "$remake_menu" ]; then + remake_menu= + + # + # Add both items scanned from the airwaves and networks + # parsed from wpa_supplicants.conf(5). Latter items are + # marked, sorted, and added to top of list above the + # former (which are unmarked and sorted separately). + # + f_dprintf "$funcname: Building menu list..." + menu_list=$( + # Process wpa_supplicant.conf(5) structs + f_menu_wireless_configs + # Process wpa_cli(8) `scan_results' structs + f_menu_wpa_scan_results + ) + f_dprintf "$funcname: menu list built." + + # + # Add static top-level menu items + # + local XA=" " XC=" " XS=" " + [ "$DIALOG_MENU_WLAN_SHOW_ALL" ] && XA="X" + [ "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" ] && XC="X" + [ "$DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS" ] && XS="X" + menu_list=" + '> $msg_exit' '$msg_return_to_previous_menu' + '' + '> $msg_rescan_wireless' '*' + '$msg_rescan_wireless_help' + '> $msg_forget_all' '*' + '$msg_forget_all_help' + '> $msg_show_configured' '[$XC]' + '$msg_show_configured_help' + '> $msg_show_scan_results' '[$XS]' + '$msg_show_scan_results_help' + '> $msg_show_all' '[$XA]' + '$msg_show_all_help' + '> $msg_manually_connect' '...' + '$msg_manually_connect_help' + $menu_list" # END-QUOTE + fi + + local height width rows + eval f_dialog_menu_with_help_size height width rows \ + \"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" \ + $menu_list + + local menu_choice + menu_choice=$( eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_select\" \ + --cancel-label \"\$msg_cancel\" \ + --item-help \ + --default-item \"\$defaultitem\" \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) || break + f_dialog_data_sanitize menu_choice + defaultitem="$menu_choice" + + case "$menu_choice" in + "> $msg_exit") break ;; + "> $msg_rescan_wireless") do_parse=1 remake_menu=1 ;; + "> $msg_forget_all") + if f_noyes "$msg_forget_all_confirm"; then + f_eval_catch $funcname rm \ + 'rm -f "%s"' "$conf_file" || continue + f_wpa_supplicant_init "$conf_file" || continue + f_eval_catch -d $funcname wpa_cli \ + 'wpa_cli reconfigure' + f_wpa_supplicant_parse "$conf_file" \ + WIRELESS_ NWIRELESS_CONFIGS + f_wpa_scan_find_matches \ + WSCANS_ $NWSCAN_RESULTS \ + WIRELESS_ $NWIRELESS_CONFIGS + do_parse=1 remake_menu=1 + fi ;; + "> $msg_show_configured") + item=$( eval f_dialog_menutag2item_with_help \ + \"\$menu_choice\" $menu_list ) + if [ "$item" = "[ ]" ]; then + DIALOG_MENU_WLAN_SHOW_CONFIGURED=1 + else + DIALOG_MENU_WLAN_SHOW_CONFIGURED= + fi + remake_menu=1 ;; + "> $msg_show_scan_results") + item=$( eval f_dialog_menutag2item_with_help \ + \"\$menu_choice\" $menu_list ) + if [ "$item" = "[ ]" ]; then + DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS=1 + else + DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS= + fi + remake_menu=1 ;; + "> $msg_show_all") + item=$( eval f_dialog_menutag2item_with_help \ + \"\$menu_choice\" $menu_list ) + if [ "$item" = "[ ]" ]; then + DIALOG_MENU_WLAN_SHOW_ALL=1 + else + DIALOG_MENU_WLAN_SHOW_ALL= + fi + remake_menu=1 ;; + "> $msg_manually_connect") + f_dialog_wireless_edit && remake_menu=1 ;; + ?"[X] "*) + ssid="${menu_choice#??X? }" + f_dialog_wireless_edit "$ssid" || continue + do_parse=1 remake_menu=1 ;; + "[ ] "*) + : + : XXXDT Unfinished + : + ;; + esac + done + + # + # XXXDT Unfinished + # +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." media/wlan.subr + +fi # ! $_MEDIA_WLAN_SUBR |