CloverBootloader/NetworkPkg/WifiConnectionManagerDxe/WifiConnectionMgrImpl.c

1286 lines
41 KiB
C
Raw Normal View History

/** @file
The Mac Connection2 Protocol adapter functions for WiFi Connection Manager.
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "WifiConnectionMgrDxe.h"
EFI_EAP_TYPE mEapAuthMethod[] = {
EFI_EAP_TYPE_TTLS,
EFI_EAP_TYPE_PEAP,
EFI_EAP_TYPE_EAPTLS
};
EFI_EAP_TYPE mEapSecondAuthMethod[] = {
EFI_EAP_TYPE_MSCHAPV2
};
/**
The callback function for scan operation. This function updates networks
according to the latest scan result, and trigger UI refresh.
ASSERT when errors occur in config token.
@param[in] Event The GetNetworks token receive event.
@param[in] Context The context of the GetNetworks token.
**/
VOID
EFIAPI
WifiMgrOnScanFinished (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
WIFI_MGR_MAC_CONFIG_TOKEN *ConfigToken;
WIFI_MGR_DEVICE_DATA *Nic;
WIFI_MGR_NETWORK_PROFILE *Profile;
EFI_80211_NETWORK *Network;
UINTN DataSize;
EFI_80211_NETWORK_DESCRIPTION *NetworkDescription;
EFI_80211_GET_NETWORKS_RESULT *Result;
LIST_ENTRY *Entry;
UINT8 SecurityType;
BOOLEAN AKMSuiteSupported;
BOOLEAN CipherSuiteSupported;
CHAR8 *AsciiSSId;
UINTN Index;
ASSERT (Context != NULL);
ConfigToken = (WIFI_MGR_MAC_CONFIG_TOKEN *) Context;
ASSERT (ConfigToken->Nic != NULL);
ASSERT (ConfigToken->Type == TokenTypeGetNetworksToken);
//
// It is the GetNetworks token, set scan state to "ScanFinished"
//
ConfigToken->Nic->ScanState = WifiMgrScanFinished;
ASSERT (ConfigToken->Token.GetNetworksToken != NULL);
Result = ConfigToken->Token.GetNetworksToken->Result;
Nic = ConfigToken->Nic;
//
// Clean previous result, and update network list according to the scan result
//
Nic->AvailableCount = 0;
NET_LIST_FOR_EACH (Entry, &Nic->ProfileList) {
Profile = NET_LIST_USER_STRUCT_S (Entry, WIFI_MGR_NETWORK_PROFILE,
Link, WIFI_MGR_PROFILE_SIGNATURE);
Profile->IsAvailable = FALSE;
}
if (Result == NULL) {
gBS->SignalEvent (Nic->Private->NetworkListRefreshEvent);
WifiMgrFreeToken(ConfigToken);
return;
}
for (Index = 0; Index < Result->NumOfNetworkDesc; Index ++) {
NetworkDescription = Result->NetworkDesc + Index;
if (NetworkDescription == NULL) {
continue;
}
Network = &NetworkDescription->Network;
if (Network == NULL || Network->SSId.SSIdLen == 0) {
continue;
}
Status = WifiMgrCheckRSN (
Network->AKMSuite,
Network->CipherSuite,
Nic,
&SecurityType,
&AKMSuiteSupported,
&CipherSuiteSupported
);
if (EFI_ERROR (Status)) {
SecurityType = SECURITY_TYPE_UNKNOWN;
AKMSuiteSupported = FALSE;
CipherSuiteSupported = FALSE;
}
AsciiSSId = (CHAR8*) AllocateZeroPool(sizeof (CHAR8) * (Network->SSId.SSIdLen + 1));
if (AsciiSSId == NULL) {
continue;
}
CopyMem(AsciiSSId, (CHAR8 *) Network->SSId.SSId, sizeof (CHAR8) * Network->SSId.SSIdLen);
*(AsciiSSId + Network->SSId.SSIdLen) = '\0';
Profile = WifiMgrGetProfileByAsciiSSId (AsciiSSId, SecurityType, &Nic->ProfileList);
if (Profile == NULL) {
if (Nic->MaxProfileIndex >= NETWORK_LIST_COUNT_MAX) {
FreePool (AsciiSSId);
continue;
}
//
// Create a new profile
//
Profile = AllocateZeroPool (sizeof (WIFI_MGR_NETWORK_PROFILE));
if (Profile == NULL) {
FreePool (AsciiSSId);
continue;
}
Profile->Signature = WIFI_MGR_PROFILE_SIGNATURE;
Profile->NicIndex = Nic->NicIndex;
Profile->ProfileIndex = Nic->MaxProfileIndex + 1;
AsciiStrToUnicodeStrS (AsciiSSId, Profile->SSId, SSID_STORAGE_SIZE);
InsertTailList (&Nic->ProfileList, &Profile->Link);
Nic->MaxProfileIndex ++;
}
FreePool (AsciiSSId);
//
//May receive duplicate networks in scan results, check if it has already
//been processed.
//
if (!Profile->IsAvailable) {
Profile->IsAvailable = TRUE;
Profile->SecurityType = SecurityType;
Profile->AKMSuiteSupported = AKMSuiteSupported;
Profile->CipherSuiteSupported = CipherSuiteSupported;
Profile->NetworkQuality = NetworkDescription->NetworkQuality;
Nic->AvailableCount ++;
//
//Copy BSSType and SSId
//
CopyMem(&Profile->Network, Network, sizeof (EFI_80211_NETWORK));
//
//Copy AKMSuite list
//
if (Network->AKMSuite != NULL) {
if (Network->AKMSuite->AKMSuiteCount == 0) {
DataSize = sizeof (EFI_80211_AKM_SUITE_SELECTOR);
} else {
DataSize = sizeof (EFI_80211_AKM_SUITE_SELECTOR) + sizeof (EFI_80211_SUITE_SELECTOR)
* (Network->AKMSuite->AKMSuiteCount - 1);
}
Profile->Network.AKMSuite = (EFI_80211_AKM_SUITE_SELECTOR *) AllocateZeroPool (DataSize);
if (Profile->Network.AKMSuite == NULL) {
continue;
}
CopyMem (Profile->Network.AKMSuite, Network->AKMSuite, DataSize);
}
//
//Copy CipherSuite list
//
if (Network->CipherSuite != NULL) {
if (Network->CipherSuite->CipherSuiteCount == 0) {
DataSize = sizeof (EFI_80211_CIPHER_SUITE_SELECTOR);
} else {
DataSize = sizeof (EFI_80211_CIPHER_SUITE_SELECTOR) + sizeof (EFI_80211_SUITE_SELECTOR)
* (Network->CipherSuite->CipherSuiteCount - 1);
}
Profile->Network.CipherSuite = (EFI_80211_CIPHER_SUITE_SELECTOR *) AllocateZeroPool (DataSize);
if (Profile->Network.CipherSuite == NULL) {
continue;
}
CopyMem (Profile->Network.CipherSuite, Network->CipherSuite, DataSize);
}
} else {
//
// A duplicate network, update signal quality
//
if (Profile->NetworkQuality < NetworkDescription->NetworkQuality) {
Profile->NetworkQuality = NetworkDescription->NetworkQuality;
}
continue;
}
}
gBS->SignalEvent (Nic->Private->NetworkListRefreshEvent);
//
// The current connected network should always be available until disconnection
// happens in Wifi FW layer, even when it is not in this time's scan result.
//
if (Nic->ConnectState == WifiMgrConnectedToAp && Nic->CurrentOperateNetwork != NULL) {
if (!Nic->CurrentOperateNetwork->IsAvailable) {
Nic->CurrentOperateNetwork->IsAvailable = TRUE;
Nic->AvailableCount ++;
}
}
WifiMgrFreeToken(ConfigToken);
}
/**
Start scan operation, and send out a token to collect available networks.
@param[in] Nic Pointer to the device data of the selected NIC.
@retval EFI_SUCCESS The operation is completed.
@retval EFI_ALREADY_STARTED A former scan operation is already ongoing.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
@retval Other Errors Return errors when getting networks from low layer.
**/
EFI_STATUS
WifiMgrStartScan (
IN WIFI_MGR_DEVICE_DATA *Nic
)
{
EFI_STATUS Status;
EFI_TPL OldTpl;
WIFI_MGR_MAC_CONFIG_TOKEN *ConfigToken;
EFI_80211_GET_NETWORKS_TOKEN *GetNetworksToken;
UINT32 HiddenSSIdIndex;
UINT32 HiddenSSIdCount;
EFI_80211_SSID *HiddenSSIdList;
WIFI_HIDDEN_NETWORK_DATA *HiddenNetwork;
LIST_ENTRY *Entry;
if (Nic == NULL || Nic->Wmp == NULL) {
return EFI_INVALID_PARAMETER;
}
if (Nic->ScanState == WifiMgrScanning) {
return EFI_ALREADY_STARTED;
}
Nic->ScanState = WifiMgrScanning;
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
Status = EFI_SUCCESS;
HiddenSSIdList = NULL;
HiddenSSIdCount = Nic->Private->HiddenNetworkCount;
HiddenSSIdIndex = 0;
//
//create a new get network token
//
ConfigToken = AllocateZeroPool (sizeof (WIFI_MGR_MAC_CONFIG_TOKEN));
if (ConfigToken == NULL) {
gBS->RestoreTPL (OldTpl);
return EFI_OUT_OF_RESOURCES;
}
ConfigToken->Type = TokenTypeGetNetworksToken;
ConfigToken->Nic = Nic;
ConfigToken->Token.GetNetworksToken = AllocateZeroPool (sizeof (EFI_80211_GET_NETWORKS_TOKEN));
if (ConfigToken->Token.GetNetworksToken == NULL) {
WifiMgrFreeToken(ConfigToken);
gBS->RestoreTPL (OldTpl);
return EFI_OUT_OF_RESOURCES;
}
GetNetworksToken = ConfigToken->Token.GetNetworksToken;
//
// There are some hidden networks to scan, add them into scan list
//
if (HiddenSSIdCount > 0) {
HiddenSSIdList = AllocateZeroPool(HiddenSSIdCount * sizeof (EFI_80211_SSID));
if (HiddenSSIdList == NULL) {
WifiMgrFreeToken(ConfigToken);
gBS->RestoreTPL (OldTpl);
return EFI_OUT_OF_RESOURCES;
}
HiddenSSIdIndex = 0;
NET_LIST_FOR_EACH (Entry, &Nic->Private->HiddenNetworkList) {
HiddenNetwork = NET_LIST_USER_STRUCT_S (Entry, WIFI_HIDDEN_NETWORK_DATA,
Link, WIFI_MGR_HIDDEN_NETWORK_SIGNATURE);
HiddenSSIdList[HiddenSSIdIndex].SSIdLen = (UINT8) StrLen (HiddenNetwork->SSId);
UnicodeStrToAsciiStrS(HiddenNetwork->SSId,
(CHAR8 *) HiddenSSIdList[HiddenSSIdIndex].SSId, SSID_STORAGE_SIZE);
HiddenSSIdIndex ++;
}
GetNetworksToken->Data = AllocateZeroPool (sizeof (EFI_80211_GET_NETWORKS_DATA) +
(HiddenSSIdCount - 1) * sizeof (EFI_80211_SSID));
if (GetNetworksToken->Data == NULL) {
FreePool (HiddenSSIdList);
WifiMgrFreeToken(ConfigToken);
gBS->RestoreTPL (OldTpl);
return EFI_OUT_OF_RESOURCES;
}
GetNetworksToken->Data->NumOfSSID = HiddenSSIdCount;
CopyMem(GetNetworksToken->Data->SSIDList, HiddenSSIdList, HiddenSSIdCount * sizeof (EFI_80211_SSID));
FreePool(HiddenSSIdList);
} else {
GetNetworksToken->Data = AllocateZeroPool (sizeof (EFI_80211_GET_NETWORKS_DATA));
if (GetNetworksToken->Data == NULL) {
WifiMgrFreeToken(ConfigToken);
gBS->RestoreTPL (OldTpl);
return EFI_OUT_OF_RESOURCES;
}
GetNetworksToken->Data->NumOfSSID = 0;
}
//
//Create a handle when scan process ends
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
WifiMgrOnScanFinished,
ConfigToken,
&GetNetworksToken->Event
);
if (EFI_ERROR (Status)) {
WifiMgrFreeToken(ConfigToken);
gBS->RestoreTPL (OldTpl);
return Status;
}
//
//Start scan ...
//
Status = Nic->Wmp->GetNetworks (Nic->Wmp, GetNetworksToken);
if (EFI_ERROR (Status)) {
Nic->ScanState = WifiMgrScanFinished;
WifiMgrFreeToken(ConfigToken);
gBS->RestoreTPL (OldTpl);
return Status;
}
gBS->RestoreTPL (OldTpl);
return EFI_SUCCESS;
}
/**
Configure password to supplicant before connecting to a secured network.
@param[in] Nic Pointer to the device data of the selected NIC.
@param[in] Profile The target network to be connected.
@retval EFI_SUCCESS The operation is completed.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
@retval EFI_NOT_FOUND No valid password is found to configure.
@retval Other Errors Returned errors when setting data to supplicant.
**/
EFI_STATUS
WifiMgrConfigPassword (
IN WIFI_MGR_DEVICE_DATA *Nic,
IN WIFI_MGR_NETWORK_PROFILE *Profile
)
{
EFI_STATUS Status;
EFI_SUPPLICANT_PROTOCOL *Supplicant;
EFI_80211_SSID SSId;
UINT8 *AsciiPassword;
if (Nic == NULL || Nic->Supplicant == NULL || Profile == NULL) {
return EFI_INVALID_PARAMETER;
}
Supplicant = Nic->Supplicant;
//
//Set SSId to supplicant
//
SSId.SSIdLen = Profile->Network.SSId.SSIdLen;
CopyMem(SSId.SSId, Profile->Network.SSId.SSId, sizeof (Profile->Network.SSId.SSId));
Status = Supplicant->SetData(Supplicant,EfiSupplicant80211TargetSSIDName,
(VOID *)&SSId, sizeof(EFI_80211_SSID));
if (EFI_ERROR(Status)) {
return Status;
}
//
//Set password to supplicant
//
if (StrLen (Profile->Password) < PASSWORD_MIN_LEN) {
return EFI_NOT_FOUND;
}
AsciiPassword = AllocateZeroPool ((StrLen(Profile->Password) + 1) * sizeof (UINT8));
if (AsciiPassword == NULL) {
return EFI_OUT_OF_RESOURCES;
}
UnicodeStrToAsciiStrS (Profile->Password, (CHAR8 *) AsciiPassword, PASSWORD_STORAGE_SIZE);
Status = Supplicant->SetData (Supplicant, EfiSupplicant80211PskPassword,
AsciiPassword, (StrLen(Profile->Password) + 1) * sizeof (UINT8));
ZeroMem (AsciiPassword, AsciiStrLen ((CHAR8 *) AsciiPassword) + 1);
FreePool(AsciiPassword);
return Status;
}
/**
Conduct EAP configuration to supplicant before connecting to a EAP network.
Current WiFi Connection Manager only supports three kinds of EAP networks:
1). EAP-TLS (Two-Way Authentication is required in our implementation)
2). EAP-TTLS/MSCHAPv2 (One-Way Authentication is required in our implementation)
3). PEAPv0/MSCHAPv2 (One-Way Authentication is required in our implementation)
@param[in] Nic Pointer to the device data of the selected NIC.
@param[in] Profile The target network to be connected.
@retval EFI_SUCCESS The operation is completed.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_UNSUPPORTED The expected EAP method is not supported.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
@retval Other Errors Returned errors when setting data to supplicant.
**/
EFI_STATUS
WifiMgrConfigEap (
IN WIFI_MGR_DEVICE_DATA *Nic,
IN WIFI_MGR_NETWORK_PROFILE *Profile
)
{
EFI_STATUS Status;
EFI_EAP_CONFIGURATION_PROTOCOL *EapConfig;
EFI_EAP_TYPE EapAuthMethod;
EFI_EAP_TYPE EapSecondAuthMethod;
EFI_EAP_TYPE *AuthMethodList;
CHAR8 *Identity;
UINTN IdentitySize;
CHAR16 *Password;
UINTN PasswordSize;
UINTN EncryptPasswordLen;
CHAR8 *AsciiEncryptPassword;
UINTN AuthMethodListSize;
UINTN Index;
if (Nic == NULL || Nic->EapConfig == NULL || Profile == NULL) {
return EFI_INVALID_PARAMETER;
}
EapConfig = Nic->EapConfig;
if (Profile->EapAuthMethod >= EAP_AUTH_METHOD_MAX) {
return EFI_INVALID_PARAMETER;
}
EapAuthMethod = mEapAuthMethod[Profile->EapAuthMethod];
if (EapAuthMethod != EFI_EAP_TYPE_EAPTLS) {
if (Profile->EapSecondAuthMethod >= EAP_SEAUTH_METHOD_MAX) {
return EFI_INVALID_PARAMETER;
}
EapSecondAuthMethod = mEapSecondAuthMethod[Profile->EapSecondAuthMethod];
}
//
//The first time to get Supported Auth Method list, return the size.
//
AuthMethodListSize = 0;
AuthMethodList = NULL;
Status = EapConfig->GetData (EapConfig, EFI_EAP_TYPE_ATTRIBUTE, EfiEapConfigEapSupportedAuthMethod,
(VOID *) AuthMethodList, &AuthMethodListSize);
if (Status == EFI_SUCCESS) {
//
//No Supported Eap Auth Method
//
return EFI_UNSUPPORTED;
} else if (Status != EFI_BUFFER_TOO_SMALL) {
return Status;
}
//
// The second time to get Supported Auth Method list, return the list.
// In current design, only EAPTLS, TTLS and PEAP are supported
//
AuthMethodList = (EFI_EAP_TYPE *) AllocateZeroPool(AuthMethodListSize);
if (AuthMethodList == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = EapConfig->GetData (EapConfig, EFI_EAP_TYPE_ATTRIBUTE, EfiEapConfigEapSupportedAuthMethod,
(VOID *) AuthMethodList, &AuthMethodListSize);
if (EFI_ERROR (Status)) {
FreePool (AuthMethodList);
return Status;
}
//
//Check if EapAuthMethod is in supported Auth Method list, if found, skip the loop.
//
for (Index = 0; Index < AuthMethodListSize / sizeof (EFI_EAP_TYPE); Index ++) {
if (EapAuthMethod == AuthMethodList[Index]) {
break;
}
}
if (Index == AuthMethodListSize / sizeof (EFI_EAP_TYPE)) {
FreePool (AuthMethodList);
return EFI_UNSUPPORTED;
}
FreePool (AuthMethodList);
//
// Set Identity to Eap peer, Mandatory field for PEAP and TTLS
//
if (StrLen (Profile->EapIdentity) > 0) {
IdentitySize = sizeof(CHAR8) * (StrLen(Profile->EapIdentity) + 1);
Identity = AllocateZeroPool (IdentitySize);
if (Identity == NULL) {
return EFI_OUT_OF_RESOURCES;
}
UnicodeStrToAsciiStrS(Profile->EapIdentity, Identity, IdentitySize);
Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_IDENTITY, EfiEapConfigIdentityString,
(VOID *) Identity, IdentitySize - 1);
if (EFI_ERROR(Status)) {
FreePool (Identity);
return Status;
}
FreePool (Identity);
} else {
if (EapAuthMethod != EFI_EAP_TYPE_EAPTLS) {
return EFI_INVALID_PARAMETER;
}
}
//
//Set Auth Method to Eap peer, Mandatory field
//
Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_ATTRIBUTE, EfiEapConfigEapAuthMethod,
(VOID *) &EapAuthMethod, sizeof (EapAuthMethod));
if (EFI_ERROR(Status)) {
return Status;
}
if (EapAuthMethod == EFI_EAP_TYPE_TTLS || EapAuthMethod == EFI_EAP_TYPE_PEAP) {
Status = EapConfig->SetData (EapConfig, EapAuthMethod, EfiEapConfigEap2ndAuthMethod,
(VOID *) &EapSecondAuthMethod, sizeof (EapSecondAuthMethod));
if (EFI_ERROR(Status)) {
return Status;
}
//
// Set Password to Eap peer
//
if (StrLen (Profile->EapPassword) < PASSWORD_MIN_LEN) {
DEBUG ((DEBUG_ERROR, "[WiFi Connection Manager] Error: No Eap Password for Network: %s.\n", Profile->SSId));
return EFI_INVALID_PARAMETER;
}
PasswordSize = sizeof (CHAR16) * (StrLen (Profile->EapPassword) + 1);
Password = AllocateZeroPool (PasswordSize);
if (Password == NULL) {
return EFI_OUT_OF_RESOURCES;
}
StrCpyS (Password, PasswordSize, Profile->EapPassword);;
Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_MSCHAPV2, EfiEapConfigEapMSChapV2Password,
(VOID *) Password, PasswordSize);
ZeroMem (Password, PasswordSize);
FreePool (Password);
if (EFI_ERROR (Status)) {
return Status;
}
//
//If CA cert is required, set it to Eap peer
//
if (Profile->CACertData != NULL) {
Status = EapConfig->SetData (EapConfig, EapAuthMethod, EfiEapConfigEapTlsCACert,
Profile->CACertData, Profile->CACertSize);
if (EFI_ERROR(Status)) {
return Status;
}
} else {
return EFI_INVALID_PARAMETER;
}
} else if (EapAuthMethod == EFI_EAP_TYPE_EAPTLS) {
//
//Set CA cert to Eap peer
//
if (Profile->CACertData == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_EAPTLS, EfiEapConfigEapTlsCACert,
Profile->CACertData, Profile->CACertSize);
if (EFI_ERROR(Status)) {
return Status;
}
//
//Set Client cert to Eap peer
//
if (Profile->ClientCertData == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_EAPTLS, EfiEapConfigEapTlsClientCert,
Profile->ClientCertData, Profile->ClientCertSize);
if (EFI_ERROR(Status)) {
return Status;
}
//
//Set Private key to Eap peer
//
if (Profile->PrivateKeyData == NULL) {
DEBUG ((DEBUG_ERROR, "[WiFi Connection Manager] Error: No Private Key for Network: %s.\n", Profile->SSId));
return EFI_INVALID_PARAMETER;
}
Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_EAPTLS, EfiEapConfigEapTlsClientPrivateKeyFile,
Profile->PrivateKeyData, Profile->PrivateKeyDataSize);
if (EFI_ERROR(Status)) {
return Status;
}
if (StrLen (Profile->PrivateKeyPassword) > 0) {
EncryptPasswordLen = StrLen (Profile->PrivateKeyPassword);
AsciiEncryptPassword = AllocateZeroPool(EncryptPasswordLen + 1);
if (AsciiEncryptPassword == NULL) {
return EFI_OUT_OF_RESOURCES;
}
UnicodeStrToAsciiStrS(Profile->PrivateKeyPassword, AsciiEncryptPassword, EncryptPasswordLen + 1);
Status = EapConfig->SetData(EapConfig, EFI_EAP_TYPE_EAPTLS,
EfiEapConfigEapTlsClientPrivateKeyFilePassword,
(VOID *) AsciiEncryptPassword, EncryptPasswordLen + 1);
if (EFI_ERROR(Status)) {
ZeroMem (AsciiEncryptPassword, EncryptPasswordLen + 1);
FreePool (AsciiEncryptPassword);
return Status;
}
ZeroMem (AsciiEncryptPassword, EncryptPasswordLen + 1);
FreePool (AsciiEncryptPassword);
}
} else {
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
/**
Get current link state from low layer.
@param[in] Nic Pointer to the device data of the selected NIC.
@param[out] LinkState The pointer to buffer to retrieve link state.
@retval EFI_SUCCESS The operation is completed.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_UNSUPPORTED Adapter information protocol is not supported.
@retval Other Errors Returned errors when retrieving link state from low layer.
**/
EFI_STATUS
WifiMgrGetLinkState (
IN WIFI_MGR_DEVICE_DATA *Nic,
OUT EFI_ADAPTER_INFO_MEDIA_STATE *LinkState
)
{
EFI_STATUS Status;
EFI_TPL OldTpl;
UINTN DataSize;
EFI_ADAPTER_INFO_MEDIA_STATE *UndiState;
EFI_ADAPTER_INFORMATION_PROTOCOL *Aip;
if (Nic == NULL || LinkState == NULL) {
return EFI_INVALID_PARAMETER;
}
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
Status = gBS->OpenProtocol (
Nic->ControllerHandle,
&gEfiAdapterInformationProtocolGuid,
(VOID**) &Aip,
Nic->DriverHandle,
Nic->ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
gBS->RestoreTPL (OldTpl);
return EFI_UNSUPPORTED;
}
Status = Aip->GetInformation(
Aip,
&gEfiAdapterInfoMediaStateGuid,
(VOID **) &UndiState,
&DataSize
);
if (EFI_ERROR (Status)) {
gBS->RestoreTPL (OldTpl);
return Status;
}
gBS->RestoreTPL (OldTpl);
CopyMem (LinkState, UndiState, sizeof (EFI_ADAPTER_INFO_MEDIA_STATE));
FreePool (UndiState);
return EFI_SUCCESS;
}
/**
Prepare configuration work before connecting to the target network.
For WPA2 Personal networks, password should be checked; and for EAP networks, parameters
are different for different networks.
@param[in] Nic Pointer to the device data of the selected NIC.
@param[in] Profile The target network to be connected.
@retval EFI_SUCCESS The operation is completed.
@retval EFI_UNSUPPORTED This network is not supported.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
WifiMgrPrepareConnection (
IN WIFI_MGR_DEVICE_DATA *Nic,
IN WIFI_MGR_NETWORK_PROFILE *Profile
)
{
EFI_STATUS Status;
UINT8 SecurityType;
BOOLEAN AKMSuiteSupported;
BOOLEAN CipherSuiteSupported;
if (Profile == NULL || Nic == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = WifiMgrCheckRSN (Profile->Network.AKMSuite, Profile->Network.CipherSuite,
Nic, &SecurityType, &AKMSuiteSupported, &CipherSuiteSupported);
if (EFI_ERROR (Status)) {
return Status;
}
if (AKMSuiteSupported && CipherSuiteSupported) {
switch (SecurityType) {
case SECURITY_TYPE_WPA2_PERSONAL:
Status = WifiMgrConfigPassword (Nic, Profile);
if (EFI_ERROR (Status)) {
if (Status == EFI_NOT_FOUND) {
if (Nic->OneTimeConnectRequest) {
WifiMgrUpdateConnectMessage (Nic, FALSE, L"Connect Failed: Invalid Password!");
}
}
return Status;
}
break;
case SECURITY_TYPE_WPA2_ENTERPRISE:
Status = WifiMgrConfigEap (Nic, Profile);
if (EFI_ERROR (Status)) {
if (Status == EFI_INVALID_PARAMETER) {
if (Nic->OneTimeConnectRequest) {
WifiMgrUpdateConnectMessage (Nic, FALSE, L"Connect Failed: Invalid Configuration!");
}
}
return Status;
}
break;
case SECURITY_TYPE_NONE:
break;
default:
return EFI_UNSUPPORTED;
}
} else {
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
/**
The callback function for connect operation.
ASSERT when errors occur in config token.
@param[in] Event The Connect token receive event.
@param[in] Context The context of the connect token.
**/
VOID
EFIAPI
WifiMgrOnConnectFinished (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
WIFI_MGR_MAC_CONFIG_TOKEN *ConfigToken;
WIFI_MGR_NETWORK_PROFILE *ConnectedProfile;
UINT8 SecurityType;
UINT8 SSIdLen;
CHAR8 *AsciiSSId;
ASSERT (Context != NULL);
ConnectedProfile = NULL;
ConfigToken = (WIFI_MGR_MAC_CONFIG_TOKEN*) Context;
ASSERT (ConfigToken->Nic != NULL);
ConfigToken->Nic->ConnectState = WifiMgrDisconnected;
ASSERT (ConfigToken->Type == TokenTypeConnectNetworkToken);
ASSERT (ConfigToken->Token.ConnectNetworkToken != NULL);
if (ConfigToken->Token.ConnectNetworkToken->Status != EFI_SUCCESS) {
if (ConfigToken->Nic->OneTimeConnectRequest) {
//
// Only update message for user triggered connection
//
if (ConfigToken->Token.ConnectNetworkToken->Status == EFI_ACCESS_DENIED) {
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, L"Connect Failed: Permission Denied!");
} else {
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, L"Connect Failed!");
}
ConfigToken->Nic->OneTimeConnectRequest = FALSE;
}
ConfigToken->Nic->CurrentOperateNetwork = NULL;
return;
}
if (ConfigToken->Token.ConnectNetworkToken->ResultCode != ConnectSuccess) {
if (ConfigToken->Nic->OneTimeConnectRequest) {
if (ConfigToken->Token.ConnectNetworkToken->ResultCode == ConnectFailedReasonUnspecified) {
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, L"Connect Failed: Wrong Password or Unexpected Error!");
} else {
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, L"Connect Failed!");
}
}
goto Exit;
}
if (ConfigToken->Token.ConnectNetworkToken->Data == NULL ||
ConfigToken->Token.ConnectNetworkToken->Data->Network == NULL) {
//
// An unexpected error occurs, tell low layer to perform a disconnect
//
ConfigToken->Nic->HasDisconnectPendingNetwork = TRUE;
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);
goto Exit;
}
//
// A correct connect token received, terminate the connection process
//
Status = WifiMgrCheckRSN(ConfigToken->Token.ConnectNetworkToken->Data->Network->AKMSuite,
ConfigToken->Token.ConnectNetworkToken->Data->Network->CipherSuite,
ConfigToken->Nic, &SecurityType, NULL, NULL);
if (EFI_ERROR(Status)) {
SecurityType = SECURITY_TYPE_UNKNOWN;
}
SSIdLen = ConfigToken->Token.ConnectNetworkToken->Data->Network->SSId.SSIdLen;
AsciiSSId = (CHAR8*) AllocateZeroPool(sizeof (CHAR8) * (SSIdLen + 1));
if (AsciiSSId == NULL) {
ConfigToken->Nic->HasDisconnectPendingNetwork = TRUE;
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);
goto Exit;
}
CopyMem(AsciiSSId, ConfigToken->Token.ConnectNetworkToken->Data->Network->SSId.SSId, SSIdLen);
*(AsciiSSId + SSIdLen) = '\0';
ConnectedProfile = WifiMgrGetProfileByAsciiSSId(AsciiSSId, SecurityType, &ConfigToken->Nic->ProfileList);
FreePool(AsciiSSId);
if (ConnectedProfile == NULL) {
ConfigToken->Nic->HasDisconnectPendingNetwork = TRUE;
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);
goto Exit;
}
ConfigToken->Nic->ConnectState = WifiMgrConnectedToAp;
WifiMgrUpdateConnectMessage (ConfigToken->Nic, TRUE, NULL);
Exit:
if (ConfigToken->Nic->ConnectState == WifiMgrDisconnected) {
ConfigToken->Nic->CurrentOperateNetwork = NULL;
}
ConfigToken->Nic->OneTimeConnectRequest = FALSE;
WifiMgrFreeToken(ConfigToken);
}
/**
Start connect operation, and send out a token to connect to a target network.
@param[in] Nic Pointer to the device data of the selected NIC.
@param[in] Profile The target network to be connected.
@retval EFI_SUCCESS The operation is completed.
@retval EFI_ALREADY_STARTED Already in "connected" state, need to perform a disconnect
operation first.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
@retval Other Errors Return errors when connecting network on low layer.
**/
EFI_STATUS
WifiMgrConnectToNetwork (
IN WIFI_MGR_DEVICE_DATA *Nic,
IN WIFI_MGR_NETWORK_PROFILE *Profile
)
{
EFI_STATUS Status;
EFI_TPL OldTpl;
EFI_ADAPTER_INFO_MEDIA_STATE LinkState;
WIFI_MGR_MAC_CONFIG_TOKEN *ConfigToken;
EFI_80211_CONNECT_NETWORK_TOKEN *ConnectToken;
if (Nic == NULL || Nic->Wmp == NULL || Profile == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = WifiMgrGetLinkState (Nic, &LinkState);
if (EFI_ERROR (Status)) {
return Status;
}
if (LinkState.MediaState == EFI_SUCCESS) {
return EFI_ALREADY_STARTED;
}
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
Status = WifiMgrPrepareConnection (Nic, Profile);
if (EFI_ERROR (Status)) {
gBS->RestoreTPL (OldTpl);
return Status;
}
//
// Create a new connect token
//
ConfigToken = AllocateZeroPool (sizeof (WIFI_MGR_MAC_CONFIG_TOKEN));
if (ConfigToken == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Exit;
}
ConfigToken->Type = TokenTypeConnectNetworkToken;
ConfigToken->Nic = Nic;
ConfigToken->Token.ConnectNetworkToken = AllocateZeroPool (sizeof (EFI_80211_CONNECT_NETWORK_TOKEN));
if (ConfigToken->Token.ConnectNetworkToken == NULL) {
goto Exit;
}
ConnectToken = ConfigToken->Token.ConnectNetworkToken;
ConnectToken->Data = AllocateZeroPool (sizeof (EFI_80211_CONNECT_NETWORK_DATA));
if (ConnectToken->Data == NULL) {
goto Exit;
}
ConnectToken->Data->Network = AllocateZeroPool (sizeof (EFI_80211_NETWORK));
if (ConnectToken->Data->Network == NULL) {
goto Exit;
}
CopyMem(ConnectToken->Data->Network, &Profile->Network, sizeof (EFI_80211_NETWORK));
//
// Add event handle and start to connect
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
WifiMgrOnConnectFinished,
ConfigToken,
&ConnectToken->Event
);
if (EFI_ERROR (Status)) {
goto Exit;
}
Nic->ConnectState = WifiMgrConnectingToAp;
Nic->CurrentOperateNetwork = Profile;
WifiMgrUpdateConnectMessage (Nic, FALSE, NULL);
//
//Start Connecting ...
//
Status = Nic->Wmp->ConnectNetwork (Nic->Wmp, ConnectToken);
//
// Erase secrets after connection is triggered
//
WifiMgrCleanProfileSecrets (Profile);
if (EFI_ERROR (Status)) {
if (Status == EFI_ALREADY_STARTED) {
Nic->ConnectState = WifiMgrConnectedToAp;
WifiMgrUpdateConnectMessage (Nic, TRUE, NULL);
} else {
Nic->ConnectState = WifiMgrDisconnected;
Nic->CurrentOperateNetwork = NULL;
if (Nic->OneTimeConnectRequest) {
if (Status == EFI_NOT_FOUND) {
WifiMgrUpdateConnectMessage (Nic, FALSE, L"Connect Failed: Not Available!");
} else {
WifiMgrUpdateConnectMessage (Nic, FALSE, L"Connect Failed: Unexpected Error!");
}
}
}
goto Exit;
}
Exit:
if (EFI_ERROR (Status)) {
WifiMgrFreeToken (ConfigToken);
}
gBS->RestoreTPL (OldTpl);
DEBUG ((DEBUG_INFO, "[WiFi Connection Manager] WifiMgrConnectToNetwork: %r\n", Status));
return Status;
}
/**
The callback function for disconnect operation.
ASSERT when errors occur in config token.
@param[in] Event The Disconnect token receive event.
@param[in] Context The context of the Disconnect token.
**/
VOID
EFIAPI
WifiMgrOnDisconnectFinished (
IN EFI_EVENT Event,
IN VOID *Context
)
{
WIFI_MGR_MAC_CONFIG_TOKEN *ConfigToken;
ASSERT (Context != NULL);
ConfigToken = (WIFI_MGR_MAC_CONFIG_TOKEN*) Context;
ASSERT (ConfigToken->Nic != NULL);
ASSERT (ConfigToken->Type == TokenTypeDisconnectNetworkToken);
ASSERT (ConfigToken->Token.DisconnectNetworkToken != NULL);
if (ConfigToken->Token.DisconnectNetworkToken->Status != EFI_SUCCESS) {
ConfigToken->Nic->ConnectState = WifiMgrConnectedToAp;
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);
ConfigToken->Nic->OneTimeDisconnectRequest = FALSE;
goto Exit;
}
ConfigToken->Nic->ConnectState = WifiMgrDisconnected;
ConfigToken->Nic->CurrentOperateNetwork = NULL;
WifiMgrUpdateConnectMessage (ConfigToken->Nic, TRUE, NULL);
ConfigToken->Nic->OneTimeDisconnectRequest = FALSE;
//
// Disconnected network may not be in network list now, trigger a scan again!
//
ConfigToken->Nic->OneTimeScanRequest = TRUE;
Exit:
WifiMgrFreeToken(ConfigToken);
return;
}
/**
Start disconnect operation, and send out a token to disconnect from current connected
network.
@param[in] Nic Pointer to the device data of the selected NIC.
@retval EFI_SUCCESS The operation is completed.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval Other Errors Return errors when disconnecting a network on low layer.
**/
EFI_STATUS
WifiMgrDisconnectToNetwork (
IN WIFI_MGR_DEVICE_DATA *Nic
)
{
EFI_STATUS Status;
EFI_TPL OldTpl;
WIFI_MGR_MAC_CONFIG_TOKEN *ConfigToken;
EFI_80211_DISCONNECT_NETWORK_TOKEN *DisconnectToken;
if (Nic == NULL) {
return EFI_INVALID_PARAMETER;
}
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
Status = EFI_SUCCESS;
ConfigToken = AllocateZeroPool (sizeof (WIFI_MGR_MAC_CONFIG_TOKEN));
if (ConfigToken == NULL) {
gBS->RestoreTPL (OldTpl);
return EFI_OUT_OF_RESOURCES;
}
ConfigToken->Type = TokenTypeDisconnectNetworkToken;
ConfigToken->Nic = Nic;
ConfigToken->Token.DisconnectNetworkToken = AllocateZeroPool (sizeof (EFI_80211_DISCONNECT_NETWORK_TOKEN));
if (ConfigToken->Token.DisconnectNetworkToken == NULL) {
WifiMgrFreeToken(ConfigToken);
gBS->RestoreTPL (OldTpl);
return EFI_OUT_OF_RESOURCES;
}
DisconnectToken = ConfigToken->Token.DisconnectNetworkToken;
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
WifiMgrOnDisconnectFinished,
ConfigToken,
&DisconnectToken->Event
);
if (EFI_ERROR (Status)) {
WifiMgrFreeToken(ConfigToken);
gBS->RestoreTPL (OldTpl);
return Status;
}
Nic->ConnectState = WifiMgrDisconnectingToAp;
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);
Status = Nic->Wmp->DisconnectNetwork (Nic->Wmp, DisconnectToken);
if (EFI_ERROR (Status)) {
if (Status == EFI_NOT_FOUND) {
Nic->ConnectState = WifiMgrDisconnected;
Nic->CurrentOperateNetwork = NULL;
//
// This network is not in network list now, trigger a scan again!
//
Nic->OneTimeScanRequest = TRUE;
//
// State has been changed from Connected to Disconnected
//
WifiMgrUpdateConnectMessage (ConfigToken->Nic, TRUE, NULL);
Status = EFI_SUCCESS;
} else {
if (Nic->OneTimeDisconnectRequest) {
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, L"Disconnect Failed: Unexpected Error!");
}
Nic->ConnectState = WifiMgrConnectedToAp;
WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);
}
WifiMgrFreeToken(ConfigToken);
}
gBS->RestoreTPL (OldTpl);
return Status;
}
/**
The state machine of the connection manager, periodically check the state and
perform a corresponding operation.
@param[in] Event The timer event to be triggered.
@param[in] Context The context of the Nic device data.
**/
VOID
EFIAPI
WifiMgrOnTimerTick (
IN EFI_EVENT Event,
IN VOID *Context
)
{
WIFI_MGR_DEVICE_DATA *Nic;
EFI_STATUS Status;
EFI_ADAPTER_INFO_MEDIA_STATE LinkState;
WIFI_MGR_NETWORK_PROFILE *Profile;
if (Context == NULL) {
return;
}
Nic = (WIFI_MGR_DEVICE_DATA*) Context;
NET_CHECK_SIGNATURE (Nic, WIFI_MGR_DEVICE_DATA_SIGNATURE);
Status = WifiMgrGetLinkState (Nic, &LinkState);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "[WiFi Connection Manager] Error: Failed to get link state!\n"));
return;
}
if (Nic->LastLinkState.MediaState != LinkState.MediaState) {
if (Nic->LastLinkState.MediaState == EFI_SUCCESS && LinkState.MediaState == EFI_NO_MEDIA) {
Nic->HasDisconnectPendingNetwork = TRUE;
}
Nic->LastLinkState.MediaState = LinkState.MediaState;
}
Nic->ScanTickTime ++;
if ((Nic->ScanTickTime > WIFI_SCAN_FREQUENCY || Nic->OneTimeScanRequest) &&
Nic->ScanState == WifiMgrScanFinished) {
Nic->OneTimeScanRequest = FALSE;
Nic->ScanTickTime = 0;
DEBUG ((DEBUG_INFO, "[WiFi Connection Manager] Scan is triggered.\n"));
WifiMgrStartScan (Nic);
}
if (Nic->AvailableCount > 0 && Nic->ScanState == WifiMgrScanFinished) {
switch (Nic->ConnectState) {
case WifiMgrDisconnected:
if (Nic->HasDisconnectPendingNetwork) {
Nic->HasDisconnectPendingNetwork = FALSE;
}
if (Nic->ConnectPendingNetwork != NULL) {
Profile = Nic->ConnectPendingNetwork;
Status = WifiMgrConnectToNetwork(Nic, Profile);
Nic->ConnectPendingNetwork = NULL;
if (EFI_ERROR (Status)) {
//
// Some error happened, don't wait for a return connect token!
//
Nic->OneTimeConnectRequest = FALSE;
}
}
break;
case WifiMgrConnectingToAp:
break;
case WifiMgrDisconnectingToAp:
break;
case WifiMgrConnectedToAp:
if (Nic->ConnectPendingNetwork != NULL || Nic->HasDisconnectPendingNetwork) {
Status = WifiMgrDisconnectToNetwork(Nic);
if (EFI_ERROR (Status)) {
//
// Some error happened, don't wait for a return disconnect token!
//
Nic->OneTimeDisconnectRequest = FALSE;
}
}
break;
default:
break;
}
}
}