/** @file Copyright (C) 2017-2020, vit9696. All rights reserved. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include #include #include #include #include #include #include #include #include #include #include #include #include "OcSmcLibInternal.h" STATIC EFI_EVENT mAuthenticationKeyEraseEvent; STATIC VIRTUALSMC_KEY_VALUE mVirtualSmcKeyValue[6] = { { SMC_KEY_KEY, SmcKeyTypeUint32, 4, SMC_KEY_ATTRIBUTE_READ, {0, 0, 0, ARRAY_SIZE (mVirtualSmcKeyValue)} }, { SMC_KEY_RMde, SmcKeyTypeChar, 1, SMC_KEY_ATTRIBUTE_READ, {SMC_MODE_APPCODE} }, // // Requested yet unused (battery inside, causes missing battery in UI). // //{ SMC_KEY_BBIN, SmcKeyTypeFlag, 1, SMC_KEY_ATTRIBUTE_READ, {0} }, { SMC_KEY_BRSC, SmcKeyTypeUint16, 2, SMC_KEY_ATTRIBUTE_READ, {0} }, { SMC_KEY_MSLD, SmcKeyTypeUint8, 1, SMC_KEY_ATTRIBUTE_READ, {0} }, { SMC_KEY_BATP, SmcKeyTypeFlag, 1, SMC_KEY_ATTRIBUTE_READ, {0} }, // // HBKP must always be the last key in the list (see mAuthenticationKeyIndex). // { SMC_KEY_HBKP, SmcKeyTypeCh8s, 32, SMC_KEY_ATTRIBUTE_READ|SMC_KEY_ATTRIBUTE_WRITE, {0} } }; STATIC CONST UINT8 mAuthenticationKeyIndex = ARRAY_SIZE (mVirtualSmcKeyValue) - 1; STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcReadValue ( IN APPLE_SMC_IO_PROTOCOL *This, IN SMC_KEY Key, IN SMC_DATA_SIZE Size, OUT SMC_DATA *Value ) { UINTN Index; DEBUG ((DEBUG_INFO, "OCSMC: SmcReadValue Key %X Size %d\n", Key, Size)); if (Value == NULL) { return EFI_SMC_BAD_PARAMETER; } for (Index = 0; Index < ARRAY_SIZE (mVirtualSmcKeyValue); Index++) { if (mVirtualSmcKeyValue[Index].Key == Key) { if (mVirtualSmcKeyValue[Index].Size != Size) { return EFI_SMC_KEY_MISMATCH; } CopyMem (Value, mVirtualSmcKeyValue[Index].Data, Size); return EFI_SMC_SUCCESS; } } return EFI_SMC_NOT_FOUND; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcWriteValue ( IN APPLE_SMC_IO_PROTOCOL *This, IN SMC_KEY Key, IN SMC_DATA_SIZE Size, IN SMC_DATA *Value ) { UINTN Index; DEBUG ((DEBUG_INFO, "OCSMC: SmcWriteValue Key %X Size %d\n", Key, Size)); if (!Value || Size == 0) { return EFI_SMC_BAD_PARAMETER; } // // Handle HBKP separately to let boot.efi erase its contents as early as it wants. // if (Key == SMC_KEY_HBKP && Size <= SMC_HBKP_SIZE) { SecureZeroMem (mVirtualSmcKeyValue[mAuthenticationKeyIndex].Data, SMC_HBKP_SIZE); CopyMem (mVirtualSmcKeyValue[mAuthenticationKeyIndex].Data, Value, Size); for (Index = 0; Index < SMC_HBKP_SIZE; Index++) { if (mVirtualSmcKeyValue[mAuthenticationKeyIndex].Data[Index] != 0) { DEBUG ((DEBUG_INFO, "OCSMC: Not updating key with non-zero data\n")); break; } } return EFI_SUCCESS; } return EFI_SMC_NOT_WRITABLE; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcMakeKey ( IN CHAR8 *Name, OUT SMC_KEY *Key ) { UINTN Index; DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcMakeKey\n")); if (Name != NULL && Key != NULL) { *Key = 0; Index = 0; do { if (SMC_KEY_IS_VALID_CHAR (Name[Index])) { *Key <<= 8; *Key |= Name[Index]; ++Index; } else { *Key = 0; return EFI_SMC_BAD_PARAMETER; } } while (Index < sizeof (*Key) / sizeof (*Name)); return EFI_SMC_SUCCESS; } return EFI_SMC_BAD_PARAMETER; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcGetKeyCount ( IN APPLE_SMC_IO_PROTOCOL *This, OUT UINT32 *Count ) { DEBUG (( DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcGetKeyCount %u\n", (UINT32) ARRAY_SIZE (mVirtualSmcKeyValue) )); if (Count == NULL) { return EFI_SMC_BAD_PARAMETER; } *Count = SwapBytes32 (ARRAY_SIZE (mVirtualSmcKeyValue)); return EFI_SMC_SUCCESS; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcGetKeyFromIndex ( IN APPLE_SMC_IO_PROTOCOL *This, IN SMC_KEY_INDEX Index, OUT SMC_KEY *Key ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcGetKeyFromIndex\n")); if (Key == NULL) { return EFI_SMC_BAD_PARAMETER; } if (Index < ARRAY_SIZE (mVirtualSmcKeyValue)) { *Key = mVirtualSmcKeyValue[Index].Key; return EFI_SMC_SUCCESS; } return EFI_SMC_NOT_FOUND; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcGetKeyInfo ( IN APPLE_SMC_IO_PROTOCOL *This, IN SMC_KEY Key, OUT SMC_DATA_SIZE *Size, OUT SMC_KEY_TYPE *Type, OUT SMC_KEY_ATTRIBUTES *Attributes ) { UINTN Index; DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcGetKeyFromIndex %X\n", Key)); if (Size == NULL || Type == NULL || Attributes == NULL) { return EFI_SMC_BAD_PARAMETER; } for (Index = 0; Index < ARRAY_SIZE (mVirtualSmcKeyValue); Index++) { if (mVirtualSmcKeyValue[Index].Key == Key) { *Size = mVirtualSmcKeyValue[Index].Size; *Type = mVirtualSmcKeyValue[Index].Type; *Attributes = mVirtualSmcKeyValue[Index].Attributes; return EFI_SMC_SUCCESS; } } return EFI_SMC_NOT_FOUND; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcReset ( IN APPLE_SMC_IO_PROTOCOL *This, IN UINT32 Mode ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcReset %X\n", Mode)); return EFI_SMC_SUCCESS; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcFlashType ( IN APPLE_SMC_IO_PROTOCOL *This, IN SMC_FLASH_TYPE Type ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcFlashType %X\n", Type)); return EFI_SMC_UNSUPPORTED_FEATURE; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcUnsupported ( VOID ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcUnsupported\n")); return EFI_SMC_UNSUPPORTED_FEATURE; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcFlashWrite ( IN APPLE_SMC_IO_PROTOCOL *This, IN UINT32 Unknown, IN SMC_FLASH_SIZE Size, IN SMC_DATA *Data ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcFlashWrite %d\n", Size)); return EFI_SMC_UNSUPPORTED_FEATURE; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcFlashAuth ( IN APPLE_SMC_IO_PROTOCOL *This, IN SMC_FLASH_SIZE Size, IN SMC_DATA *Data ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcFlashAuth %d\n", Size)); return EFI_SMC_UNSUPPORTED_FEATURE; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcUnknown1 ( VOID ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcUnknown1\n")); return EFI_SMC_UNSUPPORTED_FEATURE; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcUnknown2 ( IN APPLE_SMC_IO_PROTOCOL *This, IN UINTN Ukn1, IN UINTN Ukn2 ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcUnknown2\n")); return EFI_SMC_UNSUPPORTED_FEATURE; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcUnknown3 ( IN APPLE_SMC_IO_PROTOCOL *This, IN UINTN Ukn1, IN UINTN Ukn2 ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcUnknown3\n")); return EFI_SMC_UNSUPPORTED_FEATURE; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcUnknown4 ( IN APPLE_SMC_IO_PROTOCOL *This, IN UINTN Ukn1 ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcUnknown4\n")); return EFI_SMC_UNSUPPORTED_FEATURE; } STATIC EFI_STATUS EFIAPI SmcIoVirtualSmcUnknown5 ( IN APPLE_SMC_IO_PROTOCOL *This, IN UINT8 *Data ) { DEBUG ((DEBUG_VERBOSE, "OCSMC: SmcIoVirtualSmcUnknown5\n")); return EFI_SMC_UNSUPPORTED_FEATURE; } STATIC APPLE_SMC_IO_PROTOCOL mSmcIoProtocol = { APPLE_SMC_IO_PROTOCOL_REVISION, SmcIoVirtualSmcReadValue, SmcIoVirtualSmcWriteValue, SmcIoVirtualSmcGetKeyCount, SmcIoVirtualSmcMakeKey, SmcIoVirtualSmcGetKeyFromIndex, SmcIoVirtualSmcGetKeyInfo, SmcIoVirtualSmcReset, SmcIoVirtualSmcFlashType, SmcIoVirtualSmcUnsupported, SmcIoVirtualSmcFlashWrite, SmcIoVirtualSmcFlashAuth, 0, SMC_PORT_BASE, FALSE, SmcIoVirtualSmcUnknown1, SmcIoVirtualSmcUnknown2, SmcIoVirtualSmcUnknown3, SmcIoVirtualSmcUnknown4, SmcIoVirtualSmcUnknown5 }; STATIC VOID EFIAPI EraseAuthenticationKey ( IN EFI_EVENT Event, IN VOID *Context ) { SecureZeroMem (mVirtualSmcKeyValue[mAuthenticationKeyIndex].Data, SMC_HBKP_SIZE); } STATIC BOOLEAN ExtractAuthentificationKey ( UINT8 *Buffer, UINT32 Size ) { UINT8 Index; AES_CONTEXT Context; UINT8 EncryptKey[CONFIG_AES_KEY_SIZE]; CONST UINT8 *InitVector; UINT8 *Payload; UINT32 PayloadSize; UINT32 RealSize; if (Size < sizeof (UINT32) + SMC_HBKP_SIZE) { DEBUG ((DEBUG_INFO, "OCSMC: Invalid key length - %u\n", (UINT32) Size)); return FALSE; } if (Buffer[0] == 'V' && Buffer[1] == 'S' && Buffer[2] == 'P' && Buffer[3] == 'T') { // // Perform an as-is copy of stored contents. // CopyMem (mVirtualSmcKeyValue[mAuthenticationKeyIndex].Data, Buffer + sizeof (UINT32), SMC_HBKP_SIZE); } else if (Buffer[0] == 'V' && Buffer[1] == 'S' && Buffer[2] == 'E' && Buffer[3] == 'N') { // // The magic is followed by an IV and at least one AES block containing at least SMC_HBKP_SIZE bytes. // if (Size < sizeof (UINT32) + AES_BLOCK_SIZE + SMC_HBKP_SIZE || (Size - sizeof (UINT32)) % AES_BLOCK_SIZE != 0) { DEBUG ((DEBUG_INFO, "OCSMC: Invalid encrypted key length - %u\n", (UINT32) Size)); return FALSE; } // // Read and erase the temporary encryption key from CMOS memory. // for (Index = 0; Index < sizeof (EncryptKey); Index++) { EncryptKey[Index] = OcRtcRead (0xD0 + Index); OcRtcWrite (0xD0 + Index, 0); } // // Perform the decryption. // InitVector = Buffer + sizeof (UINT32); Payload = Buffer + sizeof (UINT32) + AES_BLOCK_SIZE; PayloadSize = Size - (sizeof (UINT32) + AES_BLOCK_SIZE); AesInitCtxIv (&Context, EncryptKey, InitVector); AesCbcDecryptBuffer (&Context, Payload, PayloadSize); SecureZeroMem (&Context, sizeof (Context)); SecureZeroMem (EncryptKey, sizeof (EncryptKey)); RealSize = *(const UINT32 *)Payload; // // Ensure the size matches SMC_HBKP_SIZE. // if (RealSize != SMC_HBKP_SIZE) { DEBUG ((DEBUG_INFO, "OCSMC: Invalid decrypted key length - %d\n", RealSize)); return FALSE; } // // Copy the decrypted contents. // CopyMem (mVirtualSmcKeyValue[mAuthenticationKeyIndex].Data, Payload + sizeof (UINT32), SMC_HBKP_SIZE); } else { DEBUG ((DEBUG_INFO, "OCSMC: Invalid key magic - %02X %02X %02X %02X\n", Buffer[0], Buffer[1], Buffer[2], Buffer[3])); return FALSE; } return TRUE; } STATIC VOID LoadAuthenticationKey ( VOID ) { EFI_STATUS Status; VOID *Buffer = NULL; UINT32 Attributes = 0; UINTN Size = 0; // // Load encryption key contents. // Status = gRT->GetVariable (VIRTUALSMC_ENCRYPTION_KEY, &gOcWriteOnlyVariableGuid, &Attributes, &Size, NULL); if (Status == EFI_BUFFER_TOO_SMALL) { Buffer = AllocateZeroPool (Size); if (Buffer != NULL) { Status = gRT->GetVariable (VIRTUALSMC_ENCRYPTION_KEY, &gOcWriteOnlyVariableGuid, &Attributes, &Size, Buffer); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCSMC: Layer key (%u, %X) obtain failure - %r\n", (UINT32) Size, Attributes, Status)); } } else { DEBUG ((DEBUG_INFO, "OCSMC: Key buffer (%u) allocation failure - %r\n", (UINT32) Size, Status)); Status = EFI_OUT_OF_RESOURCES; } } else { DEBUG ((DEBUG_INFO, "OCSMC: Initial key obtain failure - %r\n", Status)); } // // Nullify or (at least) remove existing vsmc-key variable. // if (Buffer != NULL) { // // Parse encryption contents if any. // if (!EFI_ERROR (Status)) { ExtractAuthentificationKey (Buffer, (UINT32)Size); } SecureZeroMem (Buffer, Size); Status = gRT->SetVariable (VIRTUALSMC_ENCRYPTION_KEY, &gOcWriteOnlyVariableGuid, Attributes, Size, Buffer); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCSMC: Failed to zero key - %r\n", Status)); Status = gRT->SetVariable (VIRTUALSMC_ENCRYPTION_KEY, &gOcWriteOnlyVariableGuid, 0, 0, NULL); } gBS->FreePool (Buffer); Buffer = NULL; } else { Status = gRT->SetVariable (VIRTUALSMC_ENCRYPTION_KEY, &gOcWriteOnlyVariableGuid, 0, 0, NULL); } if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCSMC: Failed to remove key - %r\n", Status)); } // // Erase local HBKP key copy at exit boot services. // Status = gBS->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, EraseAuthenticationKey, NULL, &mAuthenticationKeyEraseEvent ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCSMC: Failed to create exit bs event for hbkp erase\n")); } } STATIC VOID ExportStatusKey ( VOID ) { EFI_STATUS Status; UINT32 StatusBuffer[2]; UINT8 *StatusBufferMagic; StatusBufferMagic = (UINT8 *) &StatusBuffer[0]; // // Build the structure. // StatusBufferMagic[0] = 'V'; StatusBufferMagic[1] = 'S'; StatusBufferMagic[2] = 'M'; StatusBufferMagic[3] = 'C'; StatusBuffer[1] = 1; // // Set status key for kext frontend. // Status = gRT->SetVariable ( VIRTUALSMC_STATUS_KEY, &gOcReadOnlyVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof (StatusBuffer), StatusBuffer ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCSMC: Failed to create status - %r\n", Status)); } } APPLE_SMC_IO_PROTOCOL * OcSmcIoInstallProtocol ( IN BOOLEAN Reinstall, IN BOOLEAN AuthRestart ) { EFI_STATUS Status; APPLE_SMC_IO_PROTOCOL *Protocol; EFI_HANDLE NewHandle; if (AuthRestart) { LoadAuthenticationKey (); ExportStatusKey (); } if (Reinstall) { Status = OcUninstallAllProtocolInstances (&gAppleSmcIoProtocolGuid); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "OCSMC: Uninstall failed: %r\n", Status)); return NULL; } } else { Status = gBS->LocateProtocol ( &gAppleSmcIoProtocolGuid, NULL, (VOID *) &Protocol ); if (!EFI_ERROR (Status)) { return Protocol; } } NewHandle = NULL; Status = gBS->InstallMultipleProtocolInterfaces ( &NewHandle, &gAppleSmcIoProtocolGuid, (VOID *) &mSmcIoProtocol, NULL ); if (EFI_ERROR (Status)) { return NULL; } return &mSmcIoProtocol; }