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. # # ############################################################ 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_supplicant.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_supplicant.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_supplicant.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_supplicant.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_supplicant.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