Jump to content

Log onto wifi using wpa2-enterprise credentials


Recommended Posts

I have a project that requires I log into a specific wifi connection that is WPA2-enterprise PEAP. I have a username and a password that must be passed to authenticate. I have already created an XML to import the settings, so the profile is in place and that part works fine.

Having searched around on this site, I found numerous other examples of using just a password or passphrase but nothing that seems to suggest using a username/password combo, nothing that works anyhow. The computer(s) that this will run on will all be standalone kiosk-likes, must reconnect daily, and must "repair" themselves as best it can. The end users are general menial workers that will not be able to enter the authentication (read: not trusted with the network password), hence the automation.

I found this snippet to "work" but only if I click on the systray area to bring up the WiFi connection screen. Then the Windows Security box pops right up and populates, indicating that it "would" work. However this is not tenable as the user cannot interact with this. From the end-user's perspective it should "just work" with them not interacting with anything but the kiosk program.

$sSSID = "TEST_SSID"

$cmd = "netsh wlan connect name=" & $sSSID & " ssid=" & $sSSID
$dosOut = Run(@ComSpec & " /c " & $cmd, @SystemDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
ProcessWaitClose($dosOut)
WinWaitActive("Windows Security")
Send("username")
Send("{TAB}")
Send("password", 1) ;changed optional field of send function from 0 to 1.
;Send("{ENTER}")

In my ideal scenario, there'd be a UDF or better yet a built-in Windows function that I could run that would accept the username and password. Is there a way to enter the credentials and pass it to the wifi all command line like? Am I just looking at this the wrong way?

Link to comment
Share on other sites

Ooh, I didn't know of that little shortcut. I'll tuck that little nugget of wisdom away for later.

But to answer the question, yes when I manually click that dialog it opens the Windows Security dialog.

I've included that in my script and it does automagically pop the windows security dialog up. Worst case scenario that might work, still I'd rather it all be silent and invisible to the user.

Link to comment
Share on other sites

I agree that fully silent is the best but Microsoft has made some weird decisions window and dialog wise in the last few years. In those cases I couldn't make it hidden, I used

#include <AutoItConstants.au3>

BlockInput($BI_DISABLE)
  
  ; ... code here ...
  
BlockInput($BI_ENABLE)

There may be other ways to programmatically access what availablenetworks does, but we're at my knowledge limit at this point. 

Perhaps poll the IP status and when it goes APIPA (or otherwise not connected, not sure how it might work in your environment) then run your not-quite-invisible script? As long as the users are aware, it might not be so bad. 🤞

Link to comment
Share on other sites

  • Moderators
Posted (edited)

You can't directly connect to your wifi through the command prompt (eg. netsh), however there's always some work around.

I've never worked with NETSH before, but I played around for a while with it.  I'll be honest, I've never been a fan of using the command prompts of apps, for some reason I feel like I have less control.

These are make-shift udfs I just put together, some were my practice funcs, but if you look at the ones I point out at the top, they may help you.  The last one, if the first two function calls fail, is one where you can create a tmp xml file, add that profile, run the netsh command then delete the tmp xml file.  All of these worked on the my Windows 10 machine.  Should work on Windows 11 too from what I was reading.

;~ #RequireAdmin
; may need to have admin priviliges

#include-once
#include <AutoItConstants.au3>
#include <StringConstants.au3>
#include <FileConstants.au3>

Global $gsMySSID_WifiName = "MyNetwork" ; obviously change it to your own network name
If Not _AutNETSH_WLan_IsConnectedBy("SSID", $gsMySSID_WifiName) Then
    ConsoleWrite("Going to attempt to connect.." & @CRLF)
    _AutNETSH_WLan_ConnectionRequest($gsMySSID_WifiName)
    Sleep(5000) ; give it 5 seconds and check if it worked
    If Not _AutNETSH_WLan_IsConnectedBy("SSID", $gsMySSID_WifiName) Then
        ConsoleWrite("Still not connected, does the profile exist yet?" & @CRLF & _
            "If the profile doesn't exist, or the password/username changes often, try using: " & @CRLF & _
            "_AutNETSH_WLan_AddConnectionRequest() below somwwhere..." & @CRLF)
    EndIf
EndIf

Func _AutNETSH_WLan_IsConnectedBy($sType, $sFind, $sWkDir = Default)
    ; because @error may return error text
    ; we'll just return yes or no string

    If $sWkDir = Default Or $sWkDir = -1 Then
        $sWkDir = @UserProfileDir
    EndIf

    Local $iPID = Run("netsh wlan show interface", $sWkDir, @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))
    Local $aRead = _AutNETSH_WLan_STDRead($iPID)
    If @error Then
        Return SetError(1, 0, StringRegExpReplace($aRead[1], "(?s)^\s*|\s*$", ""))
    EndIf

    ; escape type and value to find
    $sType = __AutNETSH_WLan_EscapeDelimiters($sType)
    $sFind = __AutNETSH_WLan_EscapeDelimiters($sFind)

    Local $sStRE = "(*UCP)(?mi)\h*" & $sType & "\h*\:\h*" & $sFind & "\h*$"
    Return (StringRegExp($aRead[0], $sStRE) <> 0)
EndFunc

Func _AutNETSH_WLan_IsConnected($sWkDir = Default)
    ; because @error may return error text
    ; we'll just return yes or no string

    If $sWkDir = Default Or $sWkDir = -1 Then
        $sWkDir = @UserProfileDir
    EndIf

    Local $iPID = Run("netsh wlan show interface", $sWkDir, @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))
    Local $aRead = _AutNETSH_WLan_STDRead($iPID)
    If @error Then
        Return SetError(1, 0, StringRegExpReplace($aRead[1], "(?s)^\s*|\s*$", ""))
    EndIf

    Local Const $sStRE = "(*UCP)(?mi)\h*state\h*\:\h*(\w+)"
    Local $aRet = StringRegExp($aRead[0], $sStRE, $STR_REGEXPARRAYGLOBALMATCH)
    If Not IsArray($aRet) Then
        Return SetError(2, 0, "")
    EndIf

    Return $aRet[0]
EndFunc

Func _AutNETSH_WLan_ConnectionRequest($sSSID, $sWkDir = Default)

    If $sWkDir = Default Or $sWkDir = -1 Then
        $sWkDir = @UserProfileDir
    EndIf

    ; connect to profile
    Local $iPID = Run('netsh wlan connect name="' & $sSSID & '"', $sWkDir, @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))
    Local $aRead = _AutNETSH_WLan_STDRead($iPID)
    If @error Then
        Return SetError(1, 0, StringRegExpReplace($aRead[1], "(?s)^\s*|\s*$", ""))
    EndIf

    Return $aRead[0]
EndFunc

Func _AutNETSH_WLan_AddConnectionRequest($sSSID, $sPassword, $sConnectionType = Default, _
                                        $sConnectionMode = Default, $sAuthentication = Default, _
                                        $sEncryption = Default, $sUseOneX = Default, $sKeyType = Default, _
                                        $sProtected = Default, $sEnableRandomization = Default, _
                                        $sWkDir = Default, $sTmpPath = Default)

    If $sConnectionType = Default Or $sConnectionType = -1 Then
        $sConnectionType = "ESS"
    EndIf

    If $sConnectionMode = Default Or $sConnectionMode = -1 Then
        $sConnectionMode = "auto"
    EndIf

    If $sAuthentication = Default Or $sAuthentication = -1 Then
        $sAuthentication = "WPA2PSK"
    EndIf

    If $sEncryption = Default Or $sEncryption = -1 Then
        $sEncryption = "AES"
    EndIf

    If $sUseOneX = Default Or $sUseOneX = -1 Then
        $sUseOneX = "false"
    EndIf

    If $sKeyType = Default Or $sKeyType = -1 Then
        $sKeyType = "passPhrase"
    EndIf

    If $sProtected = Default Or $sProtected = -1 Then
        $sProtected = "false"
    EndIf

    If $sEnableRandomization = Default Or $sEnableRandomization = -1 Then
        $sEnableRandomization = "false"
    EndIf

    If $sWkDir = Default Or $sWkDir = -1 Then
        $sWkDir = @UserProfileDir
    EndIf

    If $sTmpPath = Default Or $sTmpPath = -1 Or Not FileExists($sTmpPath) Then
        $sTmpPath = @ScriptDir
    EndIf

    Local $sXMLFile = "WLAN." & StringRegExpReplace($sSSID, "(*UCP)\W", "")
    $sXMLFile &= ".tmp(" & @YEAR & @MON & @MDAY & @HOUR & @MIN & @SEC & @MSEC & ").xml"
    Local $sOutXML = StringRegExpReplace($sTmpPath, "[\\/]+$", "") & "\" & $sXMLFile

    Local $sXML = ""
    $sXML &= '<?xml version="1.0"?>' & @CRLF
    $sXML &= '  <WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">' & @CRLF
    $sXML &= '  <name>' & $sSSID & '</name>' & @CRLF
    $sXML &= '  <SSIDConfig>' & @CRLF
    $sXML &= '      <SSID>' & @CRLF
    $sXML &= '          <name>' & $sSSID & '</name>' & @CRLF
    $sXML &= '      </SSID>' & @CRLF
    $sXML &= '  </SSIDConfig>' & @CRLF
    $sXML &= '  <connectionType>' & $sConnectionType & '</connectionType>' & @CRLF
    $sXML &= '  <connectionMode>' & $sConnectionMode & '</connectionMode>' & @CRLF
    $sXML &= '  <MSM>' & @CRLF
    $sXML &= '      <security>' & @CRLF
    $sXML &= '          <authEncryption>' & @CRLF
    $sXML &= '              <authentication>' & $sAuthentication & '</authentication>' & @CRLF
    $sXML &= '              <encryption>' $sEncryption & '</encryption>' & @CRLF
    $sXML &= '              <useOneX>' & $sUseOneX & '</useOneX>' & @CRLF
    $sXML &= '          </authEncryption>' & @CRLF
    $sXML &= '          <sharedKey>' & @CRLF
    $sXML &= '              <keyType>' & $sKeyType & '</keyType>' & @CRLF
    $sXML &= '              <protected>' & $sProtected & '</protected>' & @CRLF
    $sXML &= '              <keyMaterial>' & $sPassword & '</keyMaterial>' & @CRLF
    $sXML &= '          </sharedKey>' & @CRLF
    $sXML &= '      </security>' & @CRLF
    $sXML &= '  </MSM>' & @CRLF
    $sXML &= '  <MacRandomization xmlns="http://www.microsoft.com/networking/WLAN/profile/v3">' & @CRLF
    $sXML &= '      <enableRandomization>' & $sEnableRandomization & '</enableRandomization>' & @CRLF
    $sXML &= '  </MacRandomization>' & @CRLF
    $sXML &= '</WLANProfile>'

    Local $hOpen = FileOpen($sOutXML, $FO_OVERWRITE)
    FileWrite($hOpen, $sXML)
    FileClose($hOpen)

    ; add profile to cache
    Local $iPID = Run('netsh wlan add profile filename="' & $sOutXML & '"', $sWkDir, @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))
    Local $aRead = _AutNETSH_WLan_STDRead($iPID)
    If @error Then
        FileDelete($sOutXML)
        Return SetError(1, 0, StringRegExpReplace($aRead[1], "(?s)^\s*|\s*$", ""))
    EndIf

    Local $sRet = $aRead[0]

    ; connect to profile
    $iPID = Run('netsh wlan connect name="' & $sSSID & '"', $sWkDir, @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))
    $aRead = _AutNETSH_WLan_STDRead($iPID)
    If @error Then
        FileDelete($sOutXML)
        Return SetError(2, 0, StringRegExpReplace($aRead[1], "(?s)^\s*|\s*$", ""))
    EndIf
    FileDelete($sOutXML)

    $sRet &= " : " & $aRead[0]
    Return $sRet
EndFunc

Func _AutNETSH_WLan_ShowNetworks($sWkDir = Default)
    ; return network array

    If $sWkDir = Default Or $sWkDir = -1 Then
        $sWkDir = @UserProfileDir
    EndIf

    Local $iPID = Run("netsh wlan show networks", $sWkDir, @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))

    Local $aRead = _AutNETSH_WLan_STDRead($iPID)
    If @error Or Not IsArray($aRead) Then
        Return SetError(1, @error, StringRegExpReplace($aRead[1], "(?s)^\s*|\s*$", ""))
    EndIf

    Local Const $sSSIDRE = "(?si)(SSID\h*\d+\h*\:\h*.+?)(?:\v{3,}|$)"
    Local $aSSIDs = StringRegExp($aRead[0], $sSSIDRE, $STR_REGEXPARRAYGLOBALMATCH)
    If @error Or Not IsArray($aSSIDs) Then
        Return SetError(2, @error, "")
    EndIf

    Local $aHeaders = __AutNETSH_WLan_stdoutgetheader($aSSIDs)
    If @error Then
        Return SetError(3, @error, "")
    EndIf

    Local $aHData = __AutNETSH_WLan_stdoutgethdata($aSSIDs, $aHeaders)
    If @error Then
        Return SetError(4, @error, "")
    EndIf

    Return $aHData
EndFunc

; See RunAs() in help file, may need to sign on as admin acct
Func _AutNETSH_WLan_ShowNetworksAsUser($sUserName, $sDomain, $sPassword, $sLogonFlag, $sWkDir = Default)
    ; return network array

    If $sWkDir = Default Or $sWkDir = -1 Then
        $sWkDir = @UserProfileDir
    EndIf

    Local $iPID = RunAs($sUserName, $sDomain, $sPassword, $sLogonFlag, _
        "netsh wlan show networks", $sWkDir, @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))

    Local $aRead = _AutNETSH_WLan_STDRead($iPID)
    If @error Or Not IsArray($aRead) Then
        Return SetError(1, 0, StringRegExpReplace($aRead[1], "(?s)^\s*|\s*$", ""))
    EndIf

    Local Const $sSSIDRE = "(?si)(SSID\h*\d+\h*\:\h*.+?)(?:\v{3,}|$)"
    Local $aSSIDs = StringRegExp($aRead[0], $sSSIDRE, $STR_REGEXPARRAYGLOBALMATCH)
    If @error Or Not IsArray($aSSIDs) Then
        Return SetError(2, @error, "")
    EndIf

    Local $aHeaders = __AutNETSH_WLan_stdoutgetheader($aSSIDs)
    If @error Then
        Return SetError(3, @error, "")
    EndIf

    Local $aHData = __AutNETSH_WLan_stdoutgethdata($aSSIDs, $aHeaders)
    If @error Then
        Return SetError(4, @error, "")
    EndIf

    Return $aHData
EndFunc

Func _AutNETSH_WLan_STDRead($iPID)

    $iPID = ProcessExists($iPID)
    If Not $iPID Then
        Return SetError(1, 0, 0)
    EndIf

    Local $sTmp, $sORead, $sOErr
    While ProcessExists($iPID)
        $sTmp = StdoutRead($iPID)
        If @error Then
            $sOErr = StderrRead($iPID)
            ExitLoop
        EndIf
        $sORead &= $sTmp
    WEnd
    ; clean up leading and traling
    $sORead = StringRegExpReplace($sORead, "(?s)^\s*|\s*$", "")
    $sOErr = StringRegExpReplace($sOErr, "(?s)^\s*|\s*$", "")

    Local $aRet[2] = [$sORead, $sOErr]
    If StringLen($sORead) = 0 Then
        Return SetError(1, 0, $aRet)
    EndIf

    Return $aRet
EndFunc

Func __AutNETSH_WLan_stdoutgetheader(ByRef $aData)

    If Not IsArray($aData) Then
        Return SetError(1, 0, 0)
    EndIf

    ; how many heades, some will have more/less based on connection type
    Local $iExt, $iHeaderCount = 0, $aHeaders
    For $i = 0 To UBound($aData) - 1
        StringRegExpReplace($aData[$i], "(?m)\h+\:", "")
        $iExt = @extended
        If $iExt > $iHeaderCount Then
            $iHeaderCount = $iExt
            $aHeaders = StringRegExp($aData[$i], "(?m)\h*(.+?)\h+\:", $STR_REGEXPARRAYGLOBALMATCH)
            If IsArray($aHeaders) Then
                For $j = 0 To UBound($aHeaders) - 1
                    If StringInStr($aHeaders[$j], "SSID") Then
                        $aHeaders[$j] = "SSID"
                        ExitLoop
                    EndIf
                Next
            EndIf
        EndIf
    Next

    If $iHeaderCount = 0 Then
        Return SetError(2, 0, 0)
    EndIf

    Return SetExtended($iHeaderCount, $aHeaders)
EndFunc

Func __AutNETSH_WLan_stdoutgethdata(ByRef $aData, ByRef $aHeaders)

    If Not IsArray($aData) Then
        Return SetError(1, 0, 0)
    EndIf

    If Not IsArray($aHeaders) Then
        Return SetError(2, 0, 0)
    EndIf

    ; [0][n] = headers
    Local $aRet[UBound($aData) + 1][UBound($aHeaders)]
    ; fill headers into ret array
    For $i = 0 To UBound($aHeaders) - 1
        $aRet[0][$i] = $aHeaders[$i]
        ; turn headers into escaped chars
        $aHeaders[$i] = __AutNETSH_WLan_EscapeDelimiters($aHeaders[$i])
    Next

    Local $aFound, $aLines, $iEnum, $iColCount = 0
    ; start at 1, 0 is headers
    For $i = 0 To UBound($aData) - 1
        $iEnum = 0
        ; split into individual rows
        $aLines = StringRegExp($aData[$i], "\h*(.+?)\h*(\v+|$)", $STR_REGEXPARRAYGLOBALMATCH)

        For $x = 0 To UBound($aLines) - 1
            For $j = 0 To UBound($aHeaders) - 1
                If StringRegExp($aLines[$x], "(?i)^\h*" & $aHeaders[$j]) Then
                    $aFound = StringRegExp($aLines[$x], "(?mi)\h*" & $aHeaders[$j] & ".*?\:\h*(.+?)\h*$", $STR_REGEXPARRAYGLOBALMATCH)
                    If @error Or Not IsArray($aFound) Then ContinueLoop
                    $aRet[$i + 1][$j] = $aFound[0]
                    $iEnum += 1
                    ExitLoop
                EndIf
            Next
        Next
        If $iEnum > 0 Then $iColCount += 1
    Next
    If $iColCount < UBound($aData) Then
        ; oops
    EndIf

    Return $aRet
EndFunc

Func __AutNETSH_WLan_EscapeDelimiters($sData)
    Local Const $sRE = "([\\\.\^\$\|\[|\]\(\)\{\}\*\+\?\#])"
    Local Const $sRep = "\\$1"
    Return StringRegExpReplace($sData, $sRE, $sRep)
EndFunc

If you're going to do this across a private network, you may want to look into RunAs(), I have one example function in there with that that you can kind of get the idea from.

Anyway, I did more than I thought I would for something I can never see myself using... so hope it helps.

Edit:
You may need to change this section for enterprise:

$sXML &= '          <authEncryption>' & @CRLF
    $sXML &= '              <authentication>WPA2PSK</authentication>' & @CRLF
    $sXML &= '              <encryption>AES</encryption>' & @CRLF
    $sXML &= '              <useOneX>false</useOneX>' & @CRLF
    $sXML &= '          </authEncryption>' & @CRLF

I'm to lazy to make something that does it atm.

Edit2:
Changed _AutNETSH_WLan_AddConnectionRequest() so you can customize all the xml options if needed.  I'm not 100% on WPA2 Enterprise, but it looks like you can change Authentication = WPA2 and Encryption to TKIP... anyway, really really really done this time :whistle:

Edited by SmOke_N

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

@Underpaid So I was thinking about how it can't possibly be that Windows can't do true enterprise Wi-Fi management on the command line and I remembered that it's something it did do up until about 6 ish years ago. Microsoft had a tool pre-1809 that only shipped in the IoT build of LTSB: netcmd.exe!

Spoiler
NetCmd.exe /ActionType:Help
Running command Help
/ActionType:<AddProfile,DeleteProfile,DeleteAllProfiles,ExportProfiles,ConnectProfile,ConnectName,Disconnect,ShowInterfaces,ShowProfiles,SetAutoConfig,ShowNetworks,ShowNetDevices,ShowSystemCapabilities,ShowAdapterCapabilities,ShowRoutes,AddRoute,DeleteRoute,Help>

/ActionType:AddProfile
          /FilePath:<String>
          [/Password:<String>]       (short form /p)
          [/UserName:<String>]       (short form /u)
          [/Domain:<String>]         (short form /d)
          [/Enterprise:<Boolean>]    (short form /e)
          [/InterfaceName:<String>]  (short form /i)

/ActionType:DeleteProfile
          /ProfileName:<String>
          [/InterfaceName:<String>]  (short form /i)

/ActionType:DeleteAllProfiles
          [/InterfaceName:<String>]  (short form /i)

/ActionType:ExportProfiles
          /FilePath:<String>
          [/InterfaceName:<String>]  (short form /i)

/ActionType:ConnectProfile
          /ProfileName:<String>
          [/Password:<String>]       (short form /p)
          [/UserName:<String>]       (short form /u)
          [/Domain:<String>]         (short form /d)
          [/Enterprise:<Boolean>]    (short form /e)
          [/InterfaceName:<String>]  (short form /i)

/ActionType:ConnectName
          /Ssid:<String>
          [/ManualConnect:<Boolean>]  (short form /m)
          [/Password:<String>]        (short form /p)
          [/UserName:<String>]        (short form /u)
          [/Domain:<String>]          (short form /d)
          [/Enterprise:<Boolean>]     (short form /e)
          [/RootCAS:<String>]         (short form /r)
          [/InterfaceName:<String>]   (short form /i)

/ActionType:Disconnect
          [/InterfaceName:<String>]  (short form /i)

/ActionType:ShowInterfaces
/ActionType:ShowProfiles
          [/InterfaceName:<String>]  (short form /i)

/ActionType:SetAutoConfig
          /AutoConfig:<Boolean>
          [/InterfaceName:<String>]  (short form /i)

/ActionType:ShowNetworks
          [/InterfaceName:<String>]  (short form /i)

/ActionType:ShowNetDevices
/ActionType:ShowSystemCapabilities
/ActionType:ShowAdapterCapabilities
/ActionType:ShowRoutes
          [/InterfaceName:<String>]  (short form /i)

/ActionType:AddRoute
          /Destination:<IPAddress>
          /Gateway:<IPAddress>
          /PrefixLength:<UInt16>
          /AdapterIndex:<Int32>

/ActionType:DeleteRoute
          /Destination:<IPAddress>
          /Gateway:<IPAddress>
          /PrefixLength:<UInt16>
          /AdapterIndex:<Int32>

/ActionType:Help

 

It looks like if you can secure a copy of the above, "/ActionType:ConnectName" or "/ActionType:ConnectProfile" would be the way to quietly do what you want.

Link to comment
Share on other sites

  • Moderators
Posted (edited)

wpa+tkip or wpa2+tkip does give authentication of WPA/WPA2 Enterprise

image.png.4138e96568fb796b7ca8aa8f55d98bf6.png

Edited by SmOke_N

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...