/** @file This driver is the WiFi Profile Sync with CSME through ASF supporting WLAN using the WifiConnectionManager driver. This is used only when One Click Recovery driver triggers an HTTPS boot recovery and the platfrom supports WLAN HTTPS image recovery and boot. @copyright INTEL CONFIDENTIAL Copyright 2021 Intel Corporation. The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material may contain trade secrets and proprietary and confidential information of Intel Corporation and its suppliers and licensors, and is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intel's prior express written permission. No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing. Unless otherwise agreed by Intel in writing, you may not remove or alter this notice or any other notice embedded in Materials by Intel or Intel's suppliers or licensors in any way. This file contains a 'Sample Driver' and is licensed as such under the terms of your license agreement with Intel or your vendor. This file may be modified by the user, subject to the additional terms of the license agreement. @par Specification Reference: **/ #include "WifiProfileSync.h" #include "WifiProfileSyncUtils.h" #include #include #include #include #include // // Libraries // #include #include #include #include #include #include #include #include #include #include // // GUID's // #include #include #include // // Protocol's // #include #include #include // // WiFi Profile Sync Protocol // GLOBAL_REMOVE_IF_UNREFERENCED WIFI_PROFILE_SYNC_PROTOCOL mWiFiProfileSyncProtocol = { WIFI_PROFILE_SYNC_PROTOCOL_REVISION, WifiProfileSyncSetConnectStatus, WifiProfileSyncGetConnectStatus, WifiProfileSyncGetProfile }; GLOBAL_REMOVE_IF_UNREFERENCED WIFI_PROFILE *mSyncProfile = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_STATUS mWcmConnectionStatus; /** Function to set the WiFi connection status recieved by the WiFiConnectionManager as EFI_80211_CONNECT_NETWORK_RESULT_CODE, this will get converted to EFI_STATUS type @param[in] EFI_80211_CONNECT_NETWORK_RESULT_CODE WiFi connection attempt results **/ VOID EFIAPI WifiProfileSyncSetConnectStatus ( IN EFI_80211_CONNECT_NETWORK_RESULT_CODE ConnectionStatus ) { switch (ConnectionStatus) { case ConnectSuccess: mWcmConnectionStatus = EFI_SUCCESS; break; case ConnectRefused: mWcmConnectionStatus = EFI_ACCESS_DENIED; break; case ConnectFailed: mWcmConnectionStatus = EFI_DEVICE_ERROR; break; case ConnectFailureTimeout: mWcmConnectionStatus = EFI_TIMEOUT; break; case ConnectFailedReasonUnspecified: mWcmConnectionStatus = EFI_DEVICE_ERROR; break; default: mWcmConnectionStatus = EFI_UNSUPPORTED; } } /** Function to retrieve the WiFi connection status when in OCR WLAN flow @return EFI_SUCCESS WiFi connection completed succesfully @return Others Error Occurred **/ EFI_STATUS EFIAPI WifiProfileSyncGetConnectStatus ( VOID ) { return mWcmConnectionStatus; } /** Setup data update to included wifi driver. @param [in] NetworkSupport Determine if we are adding or removing the WiFi device from PCI required for boot list @retval EFI_SUCCESS Setup data set successfully @retval EFI_UNSUPPORTED Failed to get or set the setup data @retval Others Error occurred **/ EFI_STATUS IncludeWifiSetupData ( IN BOOLEAN NetworkSupport ) { EFI_STATUS Status; SETUP_DATA SetupData; UINT32 SetupAttributes; UINTN VariableSize; EFI_EVENT ReadyToBootServiceEvent; VariableSize = sizeof (SETUP_DATA); Status = gRT->GetVariable ( L"Setup", &gSetupVariableGuid, &SetupAttributes, &VariableSize, &SetupData ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to get setup data EfiNetworkSupport during update.\n")); return EFI_UNSUPPORTED; } SetupData.EfiNetworkSupport = NetworkSupport ? EfiNetworkWifi : EfiNetworkDisabled; Status = gRT->SetVariable ( L"Setup", &gSetupVariableGuid, SetupAttributes, sizeof (SETUP_DATA), &SetupData ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to set setup data EfiNetworkSupport during update.\n")); ASSERT_EFI_ERROR (Status); return EFI_UNSUPPORTED; } if (NetworkSupport) { // Register Ready to Boot Event for resetting the setup data to avoid triggering Wifi connection on regular boot Status = EfiCreateEventReadyToBootEx ( TPL_CALLBACK, WifiProfileSyncDisableNetwork, NULL, &ReadyToBootServiceEvent ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to to register callback to disable network setup data.\n")); ASSERT_EFI_ERROR (Status); return EFI_DEVICE_ERROR; } } return EFI_SUCCESS; } /** Callback to reset EFI newtork data setup data @param[in] Event A pointer to the Event that triggered the callback @param[in] Context A pointer to private data registered with the callback function **/ VOID EFIAPI WifiProfileSyncDisableNetwork ( IN EFI_EVENT Event, IN VOID *Context ) { // Remove WiFi device from PCI device needed for boot IncludeWifiSetupData (FALSE); } /** Event function to clear memory and uninstall WiFi profile protocol from system once WiFi Connection Manager has aquired necessary profile data to during OCR WLAN boot flow. @return EFI_SUCCESS Profiles returned @return EFI_UNSUPPORTED Protocol not supported @return Others Error Occurred **/ EFI_STATUS WifiProfileSyncClean ( VOID ) { EFI_STATUS Status; EFI_HANDLE *HandleBuffer; UINTN NumHandle; UINTN Index; WIFI_PROFILE_SYNC_PROTOCOL *ProfileSyncProtocol; HandleBuffer = NULL; // Clean memory if (mSyncProfile != NULL) { ZeroMem (mSyncProfile, sizeof (mSyncProfile)); FreePool (mSyncProfile); } Status = gBS->LocateProtocol (&gWiFiProfileSyncProtocolGuid, NULL, (VOID **) &ProfileSyncProtocol); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to find profile protocol with status %r\n", Status)); return Status; } // Get handle for WiFi profile porotocol to uninstall protocol Status = gBS->LocateHandleBuffer ( ByProtocol, &gWiFiProfileSyncProtocolGuid, NULL, &NumHandle, &HandleBuffer ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to get handle to uninstall protocol with status %r\n", Status)); return Status; } // Uninstall the WiFi profile protocol for (Index = 0; Index < NumHandle; Index++) { Status = gBS->UninstallMultipleProtocolInterfaces ( HandleBuffer[Index], &gWiFiProfileSyncProtocolGuid, &mWiFiProfileSyncProtocol, NULL ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to uninstall WiFi profile protocol.")); } } return Status; } /** Callback set for exit boot services to clear the profile data from memory and unistall protocol @param[in] Event A pointer to the Event that triggered the callback @param[in] Context A pointer to private data registered with the callback function **/ VOID EFIAPI WifiProfileSyncCleanCallback ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; Status = WifiProfileSyncClean (); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to clean WiFi profile secrets!")); } gBS->CloseEvent (Event); } /** This API will be used by the WiFi connection manager to get the WiFi profile that ASF shared and stored in WiFi profile protocol. This will as well align the ASF to the WCM profile structure @param[in, out] WcmProfile WiFi Connection Manager profile structure @return EFI_SUCCESS Profiles returned @return EFI_UNSUPPORTED Profile protocol sharing not supported or enabled @return EFI_NOT_FOUND No profiles returned @return Others Error Occurred **/ EFI_STATUS EFIAPI WifiProfileSyncGetProfile ( IN OUT WIFI_MGR_NETWORK_PROFILE *WcmProfile ) { EFI_STATUS Status; UINT32 Oui_Ieee_80211i; // Send Sync'd ASF profile data to WCM profile data WcmProfile->Signature = WIFI_MGR_PROFILE_SIGNATURE; WcmProfile->NicIndex = 1; WcmProfile->ProfileIndex = 0; CopyMem (&WcmProfile->SSId, &mSyncProfile->ProfileData->SSId, mSyncProfile->ProfileData->SSIdLength); // Psk Key if (mSyncProfile->ProfileData->PskKeyType == PSK_NETWORK) { Status = BufferToHexArray ((CHAR16 *) &WcmProfile->Password, PASSWORD_STORAGE_SIZE, (UINT8 *) &mSyncProfile->ProfileData->PskKey, mSyncProfile->ProfileData->PskKeyLen); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "[WiFiProfileSync] Failed to convert password with status of %r\n", Status)); return Status; } } // CA Certificate WcmProfile->CACertSize = mSyncProfile->CaCert->CertSize; if (mSyncProfile->CaCert->CertSize > 0) { WcmProfile->CACertData = (VOID *) &mSyncProfile->CaCert->Cert; } else { WcmProfile->CACertData = (VOID *) NULL; } // Client certificate and key if (mSyncProfile->ProfileData->UserCredentialsClientCertificateAvailable) { WcmProfile->ClientCertData = (VOID *) &mSyncProfile->Cert->Certificate; WcmProfile->ClientCertSize = mSyncProfile->Cert->CertSize; WcmProfile->PrivateKeyData = (VOID *) &mSyncProfile->Key->Key; WcmProfile->PrivateKeyDataSize = mSyncProfile->Key->KeySize; } else { WcmProfile->ClientCertData = NULL; WcmProfile->ClientCertSize = 0; WcmProfile->PrivateKeyData = NULL; WcmProfile->PrivateKeyDataSize = 0; } // 802.1x EAP WcmProfile->IsAvailable = TRUE; WcmProfile->EapAuthMethod = MapEapAuthMethod (mSyncProfile->ProfileData->AuthenticationProtocol_eapMethod); if (StrLen ((CHAR16 *) &mSyncProfile->ProfileData->UserCredentialsRoamingIdentity) > 0 ) { CopyMem (&WcmProfile->EapIdentity, &mSyncProfile->ProfileData->UserCredentialsRoamingIdentity, sizeof (mSyncProfile->ProfileData->UserCredentialsRoamingIdentity)-1); } else { CopyMem (&WcmProfile->EapIdentity, &mSyncProfile->ProfileData->UserCredentialsUsername, sizeof (mSyncProfile->ProfileData->UserCredentialsUsername)-1); } CopyMem (&WcmProfile->EapPassword, &mSyncProfile->ProfileData->UserCredentialsPassword, sizeof (mSyncProfile->ProfileData->UserCredentialsPassword)); WcmProfile->EapSecondAuthMethod = MapAuthInnerMethodToEapSecondAuthMethod (mSyncProfile->ProfileData->AuthenticationProtocolInnerMethod); // Network authentication and encryption method WcmProfile->Network.BSSType = IeeeInfrastructureBSS; WcmProfile->Network.AKMSuite = (EFI_80211_AKM_SUITE_SELECTOR *) AllocateZeroPool (sizeof (EFI_80211_AKM_SUITE_SELECTOR)); WcmProfile->Network.CipherSuite = (EFI_80211_CIPHER_SUITE_SELECTOR *) AllocateZeroPool (sizeof (EFI_80211_CIPHER_SUITE_SELECTOR)); if ((WcmProfile->Network.AKMSuite == NULL) || (WcmProfile->Network.CipherSuite == NULL)) { return EFI_OUT_OF_RESOURCES; } WcmProfile->Network.SSId.SSIdLen = mSyncProfile->ProfileData->SSIdLength; CopyMem (&WcmProfile->Network.SSId.SSId, &mSyncProfile->ProfileData->SSId, sizeof (WcmProfile->Network.SSId.SSId)); WcmProfile->Network.AKMSuite->AKMSuiteCount = 1; WcmProfile->Network.CipherSuite->CipherSuiteCount = 1; Oui_Ieee_80211i = OUI_IEEE_80211I; CopyMem (&WcmProfile->Network.AKMSuite->AKMSuiteList[0].Oui, &Oui_Ieee_80211i, sizeof (Oui_Ieee_80211i)); CopyMem (&WcmProfile->Network.CipherSuite->CipherSuiteList[0].Oui, &Oui_Ieee_80211i, sizeof (Oui_Ieee_80211i)); WcmProfile->Network.AKMSuite->AKMSuiteList[0].SuiteType = MapAuthenticationToAKM (mSyncProfile->ProfileData->AuthenticationMethod); WcmProfile->Network.CipherSuite->CipherSuiteList[0].SuiteType = MapEncryptionToCipher (mSyncProfile->ProfileData->EncryptionMethod); return EFI_SUCCESS; } /** Retrieve WiFi profile information including client certificate and key using ASF @param[in,out] Profile WiFi profile protocol structure aquired from ASF @return EFI_SUCCESS Profiles returned @return EFI_OUT_OF_RESOURCES Not enough memory @return EFI_UNSUPPORTED Profile Sync is not supported via PCD or other parameter @return Others Error Occurred **/ EFI_STATUS GetAsfWifiProfile ( IN OUT WIFI_PROFILE *Profile ) { EFI_STATUS Status; UINT32 TimeoutCount = 0; if (Profile == NULL) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Invalid profile address supplied!\n")); return EFI_INVALID_PARAMETER; } while (TimeoutCount < ME_DELAY_COUNT) { // Get ASF profile name Profile->ProfileData = (WIFI_PROFILE_DATA *) AllocateZeroPool (sizeof (WIFI_PROFILE_DATA)); if (Profile->ProfileData == NULL) { return EFI_OUT_OF_RESOURCES; } Status = AsfWifiGetProfileName (Profile->ProfileData); if (!EFI_ERROR (Status) && (Profile->ProfileData->Status == AmtStatusSuccess)) { TimeoutCount = 0; break; } MicroSecondDelay (ME_WLAN_DELAY); TimeoutCount++; if ((TimeoutCount >= ME_DELAY_COUNT) || (Profile->ProfileData->Status != AmtStatusNotFound)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to get profile name within time limit with status 0x%X\n", Profile->ProfileData->Status)); return EFI_NOT_FOUND; } } // Get profile data Status = AsfWifiGetProfileData (Profile->ProfileData); if (EFI_ERROR (Status) || (Profile->ProfileData->Status != AmtStatusSuccess)) { return EFI_UNSUPPORTED; } if (Profile->ProfileData->UserCredentialsClientCertificateAvailable == 0x01) { // Get Certificate Profile->Cert = (WIFI_8021X_CLIENT_CERT *) AllocateZeroPool (sizeof (WIFI_8021X_CLIENT_CERT)); if (Profile->Cert == NULL) { return EFI_OUT_OF_RESOURCES; } Status = AsfWifiGetClientCertificate (Profile); if (EFI_ERROR (Status) || (Profile->Cert->Status != AmtStatusSuccess)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to get client certificate with status 0x%x\n", Profile->Cert->Status)); return Status; } // Get key Profile->Key = (WIFI_8021X_CLIENT_KEY *) AllocateZeroPool (sizeof (WIFI_8021X_CLIENT_KEY)); if (Profile->Key == NULL) { return EFI_OUT_OF_RESOURCES; } Status = AsfWifiGetClientKey (Profile); if (EFI_ERROR (Status) || (Profile->Key->Status != AmtStatusSuccess)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to get client key with status 0x%x\n", Profile->Key->Status)); return Status; } } if (Profile->ProfileData->RootCACertificateIndex != ROOT_CA_NOT_AVAILABLE) { Profile->CaCert = (ROOT_CA_CERTIFICATE *) AllocateZeroPool (sizeof (ROOT_CA_CERTIFICATE)); if (Profile->CaCert == NULL) { return EFI_OUT_OF_RESOURCES; } Status = AsfGetRootCaCertificate (0, Profile->CaCert); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to get root CA certificate with status 0x%x\n", Status)); } } return Status; } /** Enable or disable HECI in the WiFI driver during HTTP boot flows. @param[in] State Value corresponding to enable, TRUE, or disable, FALSE. @return EFI_SUCCESS Successfully set the flag for the WiFi driver @return EFI_UNSUPPORTED Failed to set the enable/disable flag @return Others Error Occurred **/ EFI_STATUS EnableWifiAmtCoex ( IN BOOLEAN State ) { EFI_STATUS Status; Status = gRT->SetVariable ( L"EnableWifiAmtCoex", &gUefiIntelCnvWlanVariablesGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof(State), &State ); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); } return Status; } /** Main flow for profile sync drivers getting profile from ASF @return EFI_SUCCESS Profiles returned @return EFI_UNSUPPORTED Profile Sync is not supported to retrieve ASF profile @return Others Error Occurred **/ EFI_STATUS WifiProfileSyncMain ( VOID ) { EFI_STATUS Status; EFI_EVENT ExitBootServiceEvent; WIFI_PROFILE_SYNC_PROTOCOL *ProfileSyncProtocol; mWcmConnectionStatus = EFI_NOT_READY; Status = gBS->LocateProtocol (&gWiFiProfileSyncProtocolGuid, NULL, (VOID **) &ProfileSyncProtocol); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to find profile protocol with status %r\n", Status)); ASSERT_EFI_ERROR (Status); return Status; } mSyncProfile = (WIFI_PROFILE *) AllocateZeroPool (sizeof (WIFI_PROFILE)); if (mSyncProfile == NULL) { return EFI_OUT_OF_RESOURCES; } // Get profile from ASF Status = GetAsfWifiProfile (mSyncProfile); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to get ASF WiFi profile data, certificate, and key with status %r!\n", Status)); WifiProfileSyncClean (); return EFI_LOAD_ERROR; } // Set supported network setup variable to WiFi network IncludeWifiSetupData (TRUE); // Set WiFi AMT coexistence flag true Status = EnableWifiAmtCoex (TRUE); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to set enable WiFi AMT coexistence flag to TRUE with status %r!\n", Status)); ASSERT_EFI_ERROR (Status); } // Register Exit Boot Services Event to clear profile data from memory Status = gBS->CreateEventEx ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, WifiProfileSyncCleanCallback, NULL, &gEfiEventExitBootServicesGuid, &ExitBootServiceEvent ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to register profile protocol clean callback with status %r\n", Status)); WifiProfileSyncClean (); ASSERT_EFI_ERROR (Status); } return Status; } /** Check for Secure boot, boot gaurd and proper boot options and pcd's are enabling for ProfileSync. @return EFI_SUCCESS Profiles returned @return EFI_UNSUPPORTED Protocol not supported @return Others Error Occurred **/ BOOLEAN IsProfileSyncSupported ( VOID ) { EFI_STATUS Status; UINT16 SpecialCommand; UINT8 SecureBootState; UINTN VarSize; UINT32 VarAttributes; UINT64 BootGuardBootStatus; UINT64 BootGuardOperationMode; AMT_WRAPPER_PROTOCOL *AmtWrapper; // Check for secure boot enabled VarSize = sizeof (UINT8); Status = gRT->GetVariable ( EFI_SECURE_BOOT_ENABLE_NAME, &gEfiSecureBootEnableDisableGuid, &VarAttributes, &VarSize, &SecureBootState ); if (EFI_ERROR (Status) || (SecureBootState == SECURE_BOOT_DISABLE)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Unsupported - Secureboot disabled!\n")); return FALSE; } if (IsBootGuardSupported ()) { BootGuardBootStatus = (*(UINT64 *) (UINTN) (TXT_PUBLIC_BASE + R_CPU_BOOT_GUARD_BOOTSTATUS) & (BIT63 | BIT62)); BootGuardOperationMode = AsmReadMsr64 (MSR_BOOT_GUARD_SACM_INFO) & (B_BOOT_GUARD_SACM_INFO_MEASURED_BOOT | B_BOOT_GUARD_SACM_INFO_VERIFIED_BOOT); if ((BootGuardBootStatus == V_CPU_BOOT_GUARD_LOAD_ACM_SUCCESS) && (BootGuardOperationMode != 0)) { SpecialCommand = AsfGetSpecialCommand (); if (SpecialCommand == ASF_INTEL_OEM_FORCE_HTTPS_BOOT_CMD) { return TRUE; } Status = gBS->LocateProtocol (&gAmtWrapperProtocolGuid, NULL, (VOID **) &AmtWrapper); if (!EFI_ERROR (Status) && AmtWrapper->IsKvmEnabled ()) { return TRUE; } } else { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Unsupported - Bootguard disabled!\n")); } } else { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Unsupported - Bootguard is not supported!\n")); } return FALSE; } /** The entry point for the Wifi Profile Sync driver. @param[in] ImageHandle The firmware allocated handle for the EFI image @param[in] SystemTable A pointer to the EFI System Table @retval EFI_SUCCESS The entry point is executed successfully @retval other Some error occurs when executing this entry point **/ EFI_STATUS EFIAPI WifiProfileSyncEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_TPL OldTpl; if (!IsProfileSyncSupported ()) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] WiFi profile sync not supported!\n")); return EFI_UNSUPPORTED; } Status = EnableWifiAmtCoex (FALSE); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to set enable WiFi AMT coexistence flag to FALSE with status %r!\n", Status)); ASSERT_EFI_ERROR (Status); } Status = gBS->InstallMultipleProtocolInterfaces ( &ImageHandle, &gWiFiProfileSyncProtocolGuid, &mWiFiProfileSyncProtocol, NULL ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[WiFiProfileSync] Failed to install protocol with status %r\n", Status)); return EFI_UNSUPPORTED; } OldTpl = gBS->RaiseTPL (TPL_CALLBACK); Status = WifiProfileSyncMain (); gBS->RestoreTPL (OldTpl); return Status; }