diff --git a/projects/ArchR/packages/archr/sources/scripts/wifictl b/projects/ArchR/packages/archr/sources/scripts/wifictl index 8435fefbdd..37a25629a8 100755 --- a/projects/ArchR/packages/archr/sources/scripts/wifictl +++ b/projects/ArchR/packages/archr/sources/scripts/wifictl @@ -66,13 +66,57 @@ connect_wifi() { iwctl device ${WIFI_DEV} set-property Mode station 2>/dev/null sleep 1 + # Validar a passphrase. WPA-PSK exige 8..63 bytes de printable ASCII + # ou exatamente 64 hex chars. Um PSK fora desse intervalo faz iwd + # rejeitar o profile e o iwctl connect ficar esperando passphrase via + # stdin — wifictl não tem TTY, então o iwctl trava até o timeout do + # autoconnect (30+s). Falhar cedo dá feedback útil para a UI no ES. + if [ -n "${PSK}" ]; then + PSK_LEN=$(printf '%s' "${PSK}" | wc -c) + if [ "${PSK_LEN}" -lt 8 ] || [ "${PSK_LEN}" -gt 63 ]; then + if [ "${PSK_LEN}" -ne 64 ] || ! printf '%s' "${PSK}" | grep -qE '^[0-9a-fA-F]{64}$'; then + echo "wifictl: passphrase has ${PSK_LEN} bytes; WPA-PSK accepts 8-63 printable bytes or 64 hex chars" >&2 + return 2 + fi + fi + fi + + # iwd codifica caracteres não-imprimíveis e alguns metacaracteres do + # SSID como =XX no nome do .psk (man iwd.network "Network file names"). + # Sem isso, redes com espaços, +, /, etc. no SSID têm o arquivo + # gravado em um nome que iwd não procura, e o iwctl cai em prompt + # interativo aguardando a passphrase — sintoma visível como "trava". + # encode_iwd_name implementa a transformação canônica. + encode_iwd_name() { + printf '%s' "$1" | awk ' + BEGIN { for (i = 0; i < 256; i++) byteToChar[sprintf("%c", i)] = sprintf("%02x", i) } + { + out = "" + n = length($0) + for (i = 1; i <= n; i++) { + c = substr($0, i, 1) + # iwd allows: letters, digits, ".", "-", "_". Outros = hex. + if (c ~ /[A-Za-z0-9._-]/) out = out c + else out = out "=" toupper(byteToChar[c]) + } + print out + }' + } + IWD_NAME=$(encode_iwd_name "${SSID}") + # Create iwd network profile for persistent connection IWD_DIR="/var/lib/iwd" mkdir -p "${IWD_DIR}" + # Limpa qualquer perfil antigo para o mesmo SSID antes de regravar + # (caso usuário mude entre rede aberta e PSK, ou troque a senha — sem + # o rm o iwd pode preferir o profile antigo conflitante). + rm -f "${IWD_DIR}/${IWD_NAME}.psk" "${IWD_DIR}/${IWD_NAME}.open" 2>/dev/null if [ -n "${PSK}" ]; then - printf '[Security]\nPassphrase=%s\n' "${PSK}" > "${IWD_DIR}/${SSID}.psk" + # umask 077 antes do printf garante que o profile (com a senha em + # texto) não fique world-readable em /var/lib/iwd. + ( umask 077; printf '[Security]\nPassphrase=%s\n' "${PSK}" > "${IWD_DIR}/${IWD_NAME}.psk" ) else - printf '[Security]\n' > "${IWD_DIR}/${SSID}.open" + printf '[Security]\n' > "${IWD_DIR}/${IWD_NAME}.open" fi # Scan and connect @@ -80,10 +124,19 @@ connect_wifi() { sleep 2 iwctl station ${WIFI_DEV} connect "${SSID}" 2>/dev/null - # Wait for connection + # Wait for connection. O "state=connected" sozinho não basta: se a + # estação já estava em outra rede o teste retornaria sucesso falso. + # Comparar também o "Connected network" com o SSID solicitado. for i in $(seq 1 15); do - STATE=$(iwctl station ${WIFI_DEV} show 2>/dev/null | grep -i "state" | awk '{print $NF}') - [ "${STATE}" = "connected" ] && return 0 + SHOW=$(iwctl station ${WIFI_DEV} show 2>/dev/null) + STATE=$(printf '%s\n' "${SHOW}" | awk '/^[[:space:]]*State[[:space:]]/ {print $NF}') + NET=$(printf '%s\n' "${SHOW}" | sed -n 's/^[[:space:]]*Connected network[[:space:]]*//p') + # Trim espaços ao redor do nome da rede + NET="${NET#"${NET%%[![:space:]]*}"}" + NET="${NET%"${NET##*[![:space:]]}"}" + if [ "${STATE}" = "connected" ] && [ "${NET}" = "${SSID}" ]; then + return 0 + fi sleep 1 done return 1 @@ -92,20 +145,30 @@ connect_wifi() { create_adhoc() { wait_for_wifi - # Create an iwd AP profile file - cat <"${IWD_AP_CFG_DIR}/${SSID}.ap" -[General] -Channel=${ADHOC_CHAN} -DisableHT=true + # Heredoc com EOF não-quoted permitia que um $ na senha disparasse + # expansão de variável bash; agora montamos o profile com printf %s + # para preservar a passphrase byte-a-byte. AP filename usa o mesmo + # encoding de connect_wifi. + IWD_NAME=$(printf '%s' "${SSID}" | awk ' + BEGIN { for (i = 0; i < 256; i++) bc[sprintf("%c", i)] = sprintf("%02x", i) } + { out=""; n=length($0) + for (i = 1; i <= n; i++) { + c = substr($0, i, 1) + if (c ~ /[A-Za-z0-9._-]/) out = out c + else out = out "=" toupper(bc[c]) + } + print out }') -[Security] -Passphrase=${PSK} - -[IPv4] -Address=${NETWORK_ADDRESS}.${ADHOC_CLIENT_ID} -Netmask=255.255.255.0 -Gateway=${NETWORK_ADDRESS}.${ADHOC_CLIENT_ID} -EOF + mkdir -p "${IWD_AP_CFG_DIR}" + ( umask 077 + { + printf '[General]\nChannel=%s\nDisableHT=true\n\n' "${ADHOC_CHAN}" + printf '[Security]\nPassphrase=%s\n\n' "${PSK}" + printf '[IPv4]\nAddress=%s.%s\nNetmask=255.255.255.0\nGateway=%s.%s\n' \ + "${NETWORK_ADDRESS}" "${ADHOC_CLIENT_ID}" \ + "${NETWORK_ADDRESS}" "${ADHOC_CLIENT_ID}" + } > "${IWD_AP_CFG_DIR}/${IWD_NAME}.ap" + ) iwctl device ${WIFI_DEV} set-property Mode ap # Start the AP profile @@ -114,7 +177,19 @@ EOF destroy_adhoc() { iwctl ap ${WIFI_DEV} stop 2>/dev/null + # Apaga ambos os possíveis nomes (versão antiga sem encoding + nova + # com encoding) para não deixar profile órfão depois do upgrade. rm -f "${IWD_AP_CFG_DIR}/${SSID}.ap" 2>/dev/null + IWD_NAME=$(printf '%s' "${SSID}" | awk ' + BEGIN { for (i = 0; i < 256; i++) bc[sprintf("%c", i)] = sprintf("%02x", i) } + { out=""; n=length($0) + for (i = 1; i <= n; i++) { + c = substr($0, i, 1) + if (c ~ /[A-Za-z0-9._-]/) out = out c + else out = out "=" toupper(bc[c]) + } + print out }') + rm -f "${IWD_AP_CFG_DIR}/${IWD_NAME}.ap" 2>/dev/null } has_ap_mode() {