diff --git a/preview/MsixCore/Tests/notepadplus.msix b/preview/MsixCore/Tests/notepadplus.msix index 83803e71..097fe6a1 100644 --- a/preview/MsixCore/Tests/notepadplus.msix +++ b/preview/MsixCore/Tests/notepadplus.msix @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c09888b086af669f4a334324b698fe51f4457f551183133d49c3136306ff785 -size 3111880 +oid sha256:bc838717529809a5ac5eefed213205b89e7c51b1ae03cdb0b23670c87c24888a +size 3112115 diff --git a/preview/MsixCore/msixmgr/AutoPlay.cpp b/preview/MsixCore/msixmgr/AutoPlay.cpp new file mode 100644 index 00000000..44293bb5 --- /dev/null +++ b/preview/MsixCore/msixmgr/AutoPlay.cpp @@ -0,0 +1,617 @@ +#include + +#include +#include +#include +#include + +#include "RegistryKey.hpp" +#include "AutoPlay.hpp" +#include "GeneralUtil.hpp" +#include +#include "MsixTraceLoggingProvider.hpp" +#include "Constants.hpp" +#include "CryptoProvider.hpp" +#include "Base32Encoding.hpp" +#include + +using namespace MsixCoreLib; + +const PCWSTR AutoPlay::HandlerName = L"AutoPlay"; + +HRESULT AutoPlay::ExecuteForAddRequest() +{ + for (auto autoPlay = m_autoPlay.begin(); autoPlay != m_autoPlay.end(); ++autoPlay) + { + RETURN_IF_FAILED(ProcessAutoPlayForAdd(*autoPlay)); + } + return S_OK; +} + +HRESULT AutoPlay::ExecuteForRemoveRequest() +{ + for (auto autoPlay = m_autoPlay.begin(); autoPlay != m_autoPlay.end(); ++autoPlay) + { + RETURN_IF_FAILED(ProcessAutoPlayForRemove(*autoPlay)); + } + return S_OK; +} + +HRESULT AutoPlay::ProcessAutoPlayForRemove(AutoPlayObject& autoPlayObject) +{ + RegistryKey explorerKey; + RETURN_IF_FAILED(explorerKey.Open(HKEY_LOCAL_MACHINE, explorerRegKeyName.c_str(), KEY_READ | KEY_WRITE)); + + RegistryKey handlerRootKey; + HRESULT hrCreateSubKey = explorerKey.CreateSubKey(handlerKeyName.c_str(), KEY_READ | KEY_WRITE, &handlerRootKey); + if (SUCCEEDED(hrCreateSubKey)) + { + const HRESULT hrDeleteSubKeyTree = handlerRootKey.DeleteTree(autoPlayObject.generatedhandlerName.c_str()); + if (FAILED(hrDeleteSubKeyTree) && hrDeleteSubKeyTree != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + TraceLoggingWrite(g_MsixTraceLoggingProvider, + "Unable to delete autoplay generatedHandlerName", + TraceLoggingLevel(WINEVENT_LEVEL_WARNING), + TraceLoggingValue(hrDeleteSubKeyTree, "HR"), + TraceLoggingValue(autoPlayObject.generatedhandlerName.c_str(), "GeneratedHandlerName"), + TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName ")); + } + } + else + { + TraceLoggingWrite(g_MsixTraceLoggingProvider, + "Unable to delete autoplay reg key", + TraceLoggingLevel(WINEVENT_LEVEL_WARNING), + TraceLoggingValue(hrCreateSubKey, "HR"), + TraceLoggingValue(autoPlayObject.generatedhandlerName.c_str(), "GeneratedHandlerName"), + TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName ")); + } + + if (autoPlayObject.autoPlayType == DesktopAppxContent) + { + RegistryKey classesRootKey; + HRESULT hrCreateSubKey = classesRootKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC); + + if (SUCCEEDED(hrCreateSubKey)) + { + const HRESULT hrDeleteSubKeyTree = classesRootKey.DeleteTree(autoPlayObject.generatedProgId.c_str()); + if (FAILED(hrDeleteSubKeyTree) && hrDeleteSubKeyTree != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + TraceLoggingWrite(g_MsixTraceLoggingProvider, + "Unable to delete autoplay progId reg key", + TraceLoggingLevel(WINEVENT_LEVEL_WARNING), + TraceLoggingValue(hrDeleteSubKeyTree, "HR"), + TraceLoggingValue(autoPlayObject.generatedProgId.c_str(), "GeneratedProgId"), + TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName ")); + } + } + + } + + RegistryKey handleEventRootKey; + hrCreateSubKey = explorerKey.CreateSubKey(eventHandlerRootRegKeyName.c_str(), KEY_READ | KEY_WRITE, &handleEventRootKey); + + if (SUCCEEDED(hrCreateSubKey)) + { + RegistryKey handleEventKey; + HRESULT hrCreateHandleSubKey = handleEventRootKey.CreateSubKey(autoPlayObject.handleEvent.c_str(), KEY_READ | KEY_WRITE, &handleEventKey); + + if (SUCCEEDED(hrCreateHandleSubKey)) + { + const HRESULT hrDeleteValue = handleEventKey.DeleteValue(autoPlayObject.generatedhandlerName.c_str()); + if (FAILED(hrDeleteValue) && hrDeleteValue != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + TraceLoggingWrite(g_MsixTraceLoggingProvider, + "Unable to delete autoplay handleEventKey generatedHandlerName reg key", + TraceLoggingLevel(WINEVENT_LEVEL_WARNING), + TraceLoggingValue(hrDeleteValue, "HR"), + TraceLoggingValue(autoPlayObject.generatedhandlerName.c_str(), "GeneratedHandlerName"), + TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName ")); + return hrDeleteValue; + } + } + else + { + TraceLoggingWrite(g_MsixTraceLoggingProvider, + "Unable to delete autoplay reg key", + TraceLoggingLevel(WINEVENT_LEVEL_WARNING), + TraceLoggingValue(hrCreateHandleSubKey, "HR"), + TraceLoggingValue(autoPlayObject.generatedhandlerName.c_str(), "GeneratedHandlerName"), + TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName ")); + return hrCreateHandleSubKey; + } + } + else + { + // Log failure of creating the handleEventRootKey + TraceLoggingWrite(g_MsixTraceLoggingProvider, + "Unable to delete autoplay reg key", + TraceLoggingLevel(WINEVENT_LEVEL_WARNING), + TraceLoggingValue(hrCreateSubKey, "HR"), + TraceLoggingValue(autoPlayObject.generatedhandlerName.c_str(), "GeneratedHandlerName"), + TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName ")); + return hrCreateSubKey; + } + + return S_OK; +} + +HRESULT AutoPlay::ParseManifest() +{ + ComPtr domElement; + RETURN_IF_FAILED(m_msixRequest->GetPackageInfo()->GetManifestReader()->QueryInterface(UuidOfImpl::iid, reinterpret_cast(&domElement))); + + ComPtr element; + RETURN_IF_FAILED(domElement->GetDocumentElement(&element)); + + ComPtr extensionEnum; + RETURN_IF_FAILED(element->GetElements(extensionQuery.c_str(), &extensionEnum)); + BOOL hasCurrent = FALSE; + RETURN_IF_FAILED(extensionEnum->GetHasCurrent(&hasCurrent)); + + while (hasCurrent) + { + ComPtr extensionElement; + RETURN_IF_FAILED(extensionEnum->GetCurrent(&extensionElement)); + Text extensionCategory; + RETURN_IF_FAILED(extensionElement->GetAttributeValue(categoryAttribute.c_str(), &extensionCategory)); + + if (wcscmp(extensionCategory.Get(), desktopAppXExtensionCategory.c_str()) == 0) + { + BOOL hc_invokeAction = FALSE; + ComPtr invokeActionEnum; + RETURN_IF_FAILED(extensionElement->GetElements(invokeActionQuery.c_str(), &invokeActionEnum)); + RETURN_IF_FAILED(invokeActionEnum->GetHasCurrent(&hc_invokeAction)); + + while (hc_invokeAction) + { + ComPtr invokeActionElement; + RETURN_IF_FAILED(invokeActionEnum->GetCurrent(&invokeActionElement)); + + //desktop appx content element + BOOL has_DesktopAppxContent = FALSE; + ComPtr desktopAppxContentEnum; + RETURN_IF_FAILED(invokeActionElement->GetElements(invokeActionContentQuery.c_str(), &desktopAppxContentEnum)); + RETURN_IF_FAILED(desktopAppxContentEnum->GetHasCurrent(&has_DesktopAppxContent)); + + while (has_DesktopAppxContent) + { + ComPtr desktopAppxContentElement; + RETURN_IF_FAILED(desktopAppxContentEnum->GetCurrent(&desktopAppxContentElement)); + + AutoPlayObject autoPlay; + + //autoplay type + autoPlay.autoPlayType = DesktopAppxContent; + + //action + Text action; + RETURN_IF_FAILED(invokeActionElement->GetAttributeValue(actionAttributeName.c_str(), &action)); + autoPlay.action = action.Get(); + + //provider + Text provider; + RETURN_IF_FAILED(invokeActionElement->GetAttributeValue(providerAttributeName.c_str(), &provider)); + autoPlay.provider = provider.Get(); + + // Get the App's app user model id + autoPlay.appUserModelId = m_msixRequest->GetPackageInfo()->GetId(); + + //get the logo + autoPlay.defaultIcon = m_msixRequest->GetPackageInfo()->GetPackageDirectoryPath() + m_msixRequest->GetPackageInfo()->GetRelativeLogoPath(); + + //verb + Text id; + RETURN_IF_FAILED(desktopAppxContentElement->GetAttributeValue(idAttributeName.c_str(), &id)); + autoPlay.id = id.Get(); + + //content event + Text handleEvent; + RETURN_IF_FAILED(desktopAppxContentElement->GetAttributeValue(contentEventAttributeName.c_str(), &handleEvent)); + autoPlay.handleEvent = handleEvent.Get(); + + //drop target handler + Text dropTargetHandler; + RETURN_IF_FAILED(desktopAppxContentElement->GetAttributeValue(dropTargetHandlerAttributeName.c_str(), &dropTargetHandler)); + if (dropTargetHandler.Get() != nullptr) + { + autoPlay.dropTargetHandler = dropTargetHandler.Get(); + } + + //parameters + Text parameters; + RETURN_IF_FAILED(desktopAppxContentElement->GetAttributeValue(parametersAttributeName.c_str(), ¶meters)); + if (parameters.Get() != nullptr) + { + autoPlay.parameters = parameters.Get(); + } + + //GenerateProgId + std::wstring uniqueProgId; + uniqueProgId.append(id.Get()); + uniqueProgId.append(handleEvent.Get()); + + std::wstring generatedProgId; + RETURN_IF_FAILED(GenerateProgId(desktopAppXExtensionCategory.c_str(), uniqueProgId.c_str(), generatedProgId)); + autoPlay.generatedProgId = generatedProgId.c_str(); + + //GenerateHandlerName + std::wstring uniqueHandlerName; + uniqueHandlerName.append(id.Get()); + uniqueHandlerName.append(handleEvent.Get()); + + std::wstring generatedHandlerName; + RETURN_IF_FAILED(GenerateHandlerName(L"DesktopAppXContent", uniqueHandlerName.c_str(), generatedHandlerName)); + autoPlay.generatedhandlerName = generatedHandlerName.c_str(); + + m_autoPlay.push_back(autoPlay); + RETURN_IF_FAILED(desktopAppxContentEnum->MoveNext(&has_DesktopAppxContent)); + + } + + //desktop appx device element + BOOL has_DesktopAppxDevice = FALSE; + ComPtr desktopAppxDeviceEnum; + RETURN_IF_FAILED(invokeActionElement->GetElements(invokeActionDeviceQuery.c_str(), &desktopAppxDeviceEnum)); + RETURN_IF_FAILED(desktopAppxDeviceEnum->GetHasCurrent(&has_DesktopAppxDevice)); + + while (has_DesktopAppxDevice) + { + ComPtr desktopAppxDeviceElement; + RETURN_IF_FAILED(desktopAppxDeviceEnum->GetCurrent(&desktopAppxDeviceElement)); + + AutoPlayObject autoPlay; + + //autoplay type + autoPlay.autoPlayType = DesktopAppxDevice; + + //action + Text action; + RETURN_IF_FAILED(invokeActionElement->GetAttributeValue(actionAttributeName.c_str(), &action)); + autoPlay.action = action.Get(); + + //provider + Text provider; + RETURN_IF_FAILED(invokeActionElement->GetAttributeValue(providerAttributeName.c_str(), &provider)); + autoPlay.provider = provider.Get(); + + // Get the App's app user model id + autoPlay.appUserModelId = m_msixRequest->GetPackageInfo()->GetId(); + + //get the logo + autoPlay.defaultIcon = m_msixRequest->GetPackageInfo()->GetPackageDirectoryPath() + m_msixRequest->GetPackageInfo()->GetRelativeLogoPath(); + + //handle event + Text handleEvent; + RETURN_IF_FAILED(desktopAppxDeviceElement->GetAttributeValue(deviceEventAttributeName.c_str(), &handleEvent)); + autoPlay.handleEvent = handleEvent.Get(); + + //hwEventHandler + Text hwEventHandler; + RETURN_IF_FAILED(desktopAppxDeviceElement->GetAttributeValue(hwEventHandlerAttributeName.c_str(), &hwEventHandler)); + autoPlay.hwEventHandler = hwEventHandler.Get(); + + //init cmd line + Text initCmdLine; + RETURN_IF_FAILED(desktopAppxDeviceElement->GetAttributeValue(InitCmdLineAttributeName.c_str(), &initCmdLine)); + if (initCmdLine.Get() != nullptr) + { + autoPlay.initCmdLine = initCmdLine.Get(); + } + + //GenerateHandlerName + std::wstring uniqueHandlerName; + uniqueHandlerName.append(handleEvent.Get()); + + std::wstring generatedHandlerName; + RETURN_IF_FAILED(GenerateHandlerName(L"DesktopAppXDevice", uniqueHandlerName.c_str(), generatedHandlerName)); + autoPlay.generatedhandlerName = generatedHandlerName.c_str(); + + m_autoPlay.push_back(autoPlay); + RETURN_IF_FAILED(desktopAppxDeviceEnum->MoveNext(&has_DesktopAppxDevice)); + + } + + RETURN_IF_FAILED(invokeActionEnum->MoveNext(&hc_invokeAction)); + } + } + + RETURN_IF_FAILED(extensionEnum->MoveNext(&hasCurrent)); + } + + return S_OK; +} + +HRESULT AutoPlay::GenerateProgId(std::wstring categoryName, std::wstring subCategory, std::wstring & generatedProgId) +{ + std::wstring packageFamilyName = m_msixRequest->GetPackageInfo()->GetPackageFamilyName(); + std::wstring applicationId = m_msixRequest->GetPackageInfo()->GetApplicationId(); + + if (packageFamilyName.empty() || applicationId.empty() || categoryName.empty()) + { + return E_INVALIDARG; + } + + // Constants + std::wstring AppXPrefix = L"AppX"; + static const size_t MaxProgIDLength = 39; + + // The maximum number of characters we can have as base32 encoded is 32. + // We arrive at this due to the interface presented by the GetChars function; + // it is byte only. Ideally, the MaxBase32EncodedStringLength would be + // 35 [39 total for the progID, minus 4 for the prefix]. 35 characters means + // a maximum of 22 bytes in the digest [ceil(35*5/8)]. However, 22 bytes of digest + // actually encodes 36 characters [ceil(22/8*5)]. So we have to reduce the + // character count of the encoded string. At 33 characters, this translates into + // a 21 byte buffer. A 21 byte buffer translates into 34 characters. Although + // this meets the requirements, it is confusing since a 33 character limit + // results in a 34 character long encoding. In comparison, a 32 character long + // encoding divides evenly and always results in a 20 byte digest. + + static const size_t MaxBase32EncodedStringLength = 32; + + // The maximum number of bytes the digest can be is 20. We arrive at this by: + // - The maximum count of characters [without prefix] in the encoding (32): + // - multiplied by 5 (since each character in base 32 encoding is based off of 5 bits) + // - divided by 8 (to find how many bytes are required) + // - The initial plus 7 is to enable integer math (equivalent of writing ceil) + + static const ULONG MaxByteCountOfDigest = (MaxBase32EncodedStringLength * 5 + 7) / 8; + + // Build the progIdSeed by appending the incoming strings + // The package family name and the application ID are case sensitive + std::wstring tempProgIDBuilder; + tempProgIDBuilder.append(packageFamilyName); + std::transform(tempProgIDBuilder.begin(), tempProgIDBuilder.end(), tempProgIDBuilder.begin(), ::tolower); + tempProgIDBuilder.append(applicationId); + + // The category name and the subcategory are not case sensitive + // so we should lower case them + + std::wstring tempLowerBuffer; + tempLowerBuffer.assign(categoryName); + std::transform(tempLowerBuffer.begin(), tempLowerBuffer.end(), tempLowerBuffer.begin(), ::tolower); + tempProgIDBuilder.append(tempLowerBuffer); + + if (!subCategory.empty()) + { + tempLowerBuffer.assign(subCategory); + std::transform(tempLowerBuffer.begin(), tempLowerBuffer.end(), tempLowerBuffer.begin(), ::tolower); + tempProgIDBuilder.append(tempLowerBuffer); + } + + // Create the crypto provider and start the digest / hash + AutoPtr cryptoProvider; + RETURN_IF_FAILED(CryptoProvider::Create(&cryptoProvider)); + RETURN_IF_FAILED(cryptoProvider->StartDigest()); + COMMON_BYTES data = { 0 }; + data.length = (ULONG)tempProgIDBuilder.size() * sizeof(WCHAR); + data.bytes = (LPBYTE)tempProgIDBuilder.c_str(); + + RETURN_IF_FAILED(cryptoProvider->DigestData(&data)); + + // Grab the crypto digest + COMMON_BYTES digest = { 0 }; + RETURN_IF_FAILED(cryptoProvider->GetDigest(&digest)); + + // Ensure the string buffer has enough capacity + std::wstring base32EncodedDigest; + base32EncodedDigest.resize(MaxBase32EncodedStringLength); + + // Base 32 encode the bytes of the digest and put them into the string buffer + ULONG base32EncodedDigestCharCount = 0; + RETURN_IF_FAILED(Base32Encoding::GetChars( + digest.bytes, + std::min(digest.length, MaxByteCountOfDigest), + MaxBase32EncodedStringLength, + base32EncodedDigest.data(), + &base32EncodedDigestCharCount)); + + // Set the length of the string buffer to the appropriate value + base32EncodedDigest.resize(base32EncodedDigestCharCount); + + // ProgID name is formed by appending the encoded digest to the "AppX" prefix string + tempProgIDBuilder.clear(); + + tempProgIDBuilder.append(AppXPrefix); + tempProgIDBuilder.append(base32EncodedDigest.c_str()); + + // Set the return value + generatedProgId.assign(tempProgIDBuilder.c_str()); + + return S_OK; +} + +HRESULT AutoPlay::GenerateHandlerName(LPWSTR type, const std::wstring handlerNameSeed, std::wstring & generatedHandlerName) +{ + // Constants + static const ULONG HashedByteCount = 32; // SHA256 generates 256 hashed bits, which is 32 bytes + static const ULONG Base32EncodedLength = 52; // SHA256 generates 256 hashed bits, which is 52 characters after base 32 encoding (5 bits per character) + + std::wstring packageFamilyName = m_msixRequest->GetPackageInfo()->GetPackageFamilyName(); + std::wstring applicationId = m_msixRequest->GetPackageInfo()->GetApplicationId(); + std::wstring handlerNameBuilder; + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + DWORD hashLength; + BYTE bytes[HashedByteCount]; + ULONG base32EncodedDigestCharCount; + std::wstring base32EncodedDigest; + size_t typeLength; + + // First, append Package family name and App Id to a std::wstring variable for convenience - lowercase the values so that the comparison + // in future versions or other code will be case insensitive + handlerNameBuilder.append(packageFamilyName); + handlerNameBuilder.append(applicationId); + std::transform(handlerNameBuilder.begin(), handlerNameBuilder.end(), handlerNameBuilder.begin(), ::tolower); + + // Next, SHA256 hash the Package family name and Application Id + if (!CryptAcquireContext(&hProv, nullptr, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (!CryptHashData(hHash, (BYTE *)handlerNameBuilder.c_str(), (DWORD)handlerNameBuilder.size() * sizeof(wchar_t), 0)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + hashLength = HashedByteCount; + if (!CryptGetHashParam(hHash, HP_HASHVAL, bytes, &hashLength, 0)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // Ensure the string has enough capacity for the string and a null terminator + base32EncodedDigest.resize(Base32EncodedLength + 1); + + // Base 32 encode the bytes of the digest and put them into the string buffer + RETURN_IF_FAILED(Base32Encoding::GetChars( + bytes, + HashedByteCount, + Base32EncodedLength, + base32EncodedDigest.data(), + &base32EncodedDigestCharCount)); + + // Set the length of the string to the appropriate value + base32EncodedDigest.resize(base32EncodedDigestCharCount); + + // Find the length of the type string + RETURN_IF_FAILED(StringCchLength(type, STRSAFE_MAX_CCH, &typeLength)); + + // Finally, construct the string + handlerNameBuilder.clear(); + handlerNameBuilder.append(base32EncodedDigest.c_str()); + handlerNameBuilder.append(L"!", 1); + handlerNameBuilder.append(type, (ULONG)typeLength); + handlerNameBuilder.append(L"!", 1); + handlerNameBuilder.append(handlerNameSeed); + + // Set the return value + generatedHandlerName.assign(handlerNameBuilder.c_str()); + std::transform(generatedHandlerName.begin(), generatedHandlerName.end(), generatedHandlerName.begin(), ::tolower); + + return S_OK; +} + +HRESULT AutoPlay::ProcessAutoPlayForAdd(AutoPlayObject& autoPlayObject) +{ + RegistryKey explorerKey; + RETURN_IF_FAILED(explorerKey.Open(HKEY_LOCAL_MACHINE, explorerRegKeyName.c_str(), KEY_READ | KEY_WRITE)); + + RegistryKey handlerRootKey; + RETURN_IF_FAILED(explorerKey.CreateSubKey(handlerKeyName.c_str(), KEY_WRITE, &handlerRootKey)); + + //generatedhandlername + RegistryKey handlerKey; + RETURN_IF_FAILED(handlerRootKey.CreateSubKey(autoPlayObject.generatedhandlerName.c_str(), KEY_WRITE, &handlerKey)); + + // Keys associated with all types + RETURN_IF_FAILED(handlerKey.SetStringValue(L"Action", autoPlayObject.action)); + + RETURN_IF_FAILED(handlerKey.SetStringValue(L"Provider", autoPlayObject.provider)); + + //Get the default icon + RETURN_IF_FAILED(handlerKey.SetStringValue(L"DefaultIcon", autoPlayObject.defaultIcon)); + + RegistryKey handleEventRootKey; + RETURN_IF_FAILED(explorerKey.CreateSubKey(eventHandlerRootRegKeyName.c_str(), KEY_WRITE, &handleEventRootKey)); + + RegistryKey handleEventKey; + RETURN_IF_FAILED(handleEventRootKey.CreateSubKey(autoPlayObject.handleEvent.c_str(), KEY_WRITE, &handleEventKey)); + + RETURN_IF_FAILED(handleEventKey.SetStringValue(autoPlayObject.generatedhandlerName.c_str(), L"")); + + RETURN_IF_FAILED(handlerKey.SetUInt32Value(L"DesktopAppX", 1)); + + if (autoPlayObject.autoPlayType == DesktopAppxContent) + { + RETURN_IF_FAILED(handlerKey.SetStringValue(L"InvokeProgID", autoPlayObject.generatedProgId)); + + RETURN_IF_FAILED(handlerKey.SetStringValue(L"InvokeVerb", autoPlayObject.id)); + + RegistryKey verbRootKey; + RETURN_IF_FAILED(BuildVerbKey(autoPlayObject.generatedProgId, autoPlayObject.id, verbRootKey)); + + if (autoPlayObject.dropTargetHandler.size() > 0) + { + RegistryKey dropTargetKey; + RETURN_IF_FAILED(verbRootKey.CreateSubKey(dropTargetRegKeyName.c_str(), KEY_WRITE, &dropTargetKey)); + + std::wstring regDropTargetHandlerBuilder; + regDropTargetHandlerBuilder.append(L"{"); + regDropTargetHandlerBuilder.append(autoPlayObject.dropTargetHandler); + regDropTargetHandlerBuilder.append(L"}"); + + RETURN_IF_FAILED(dropTargetKey.SetStringValue(L"CLSID", regDropTargetHandlerBuilder)); + } + else + { + RETURN_IF_FAILED(verbRootKey.SetStringValue(L"AppUserModelID", autoPlayObject.appUserModelId)); + + std::wstring resolvedExecutableFullPath = m_msixRequest->GetPackageDirectoryPath() + L"\\" + m_msixRequest->GetPackageInfo()->GetRelativeExecutableFilePath(); + RETURN_IF_FAILED(verbRootKey.SetStringValue(L"PackageRelativeExecutable", resolvedExecutableFullPath)); + + RETURN_IF_FAILED(verbRootKey.SetStringValue(L"Parameters", autoPlayObject.parameters)); + + RETURN_IF_FAILED(verbRootKey.SetStringValue(L"ContractId", L"Windows.File")); + + RETURN_IF_FAILED(verbRootKey.SetUInt32Value(L"DesiredInitialViewState", 0)); + + RETURN_IF_FAILED(verbRootKey.SetStringValue(L"PackageId", m_msixRequest->GetPackageFullName())); + + RegistryKey commandKey; + RETURN_IF_FAILED(verbRootKey.CreateSubKey(commandKeyRegName.c_str(), KEY_WRITE, &commandKey)); + + RETURN_IF_FAILED(commandKey.SetStringValue(L"DelegateExecute", desktopAppXProtocolDelegateExecuteValue)); + } + } + else if (autoPlayObject.autoPlayType == DesktopAppxDevice) + { + std::wstring regHWEventHandlerBuilder; + regHWEventHandlerBuilder.append(L"{"); + regHWEventHandlerBuilder.append(autoPlayObject.hwEventHandler); + regHWEventHandlerBuilder.append(L"}"); + + RETURN_IF_FAILED(handlerKey.SetStringValue(L"CLSID", regHWEventHandlerBuilder)); + + RETURN_IF_FAILED(handlerKey.SetStringValue(L"InitCmdLine", autoPlayObject.initCmdLine)); + } + + return S_OK; +} + +HRESULT AutoPlay::BuildVerbKey(std::wstring generatedProgId, std::wstring id, RegistryKey & verbRootKey) +{ + RegistryKey classesRootKey; + RETURN_IF_FAILED(classesRootKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC)); + + RegistryKey progIdRootKey; + RETURN_IF_FAILED(classesRootKey.CreateSubKey(generatedProgId.c_str(), KEY_READ | KEY_WRITE, &progIdRootKey)); + + RegistryKey shellRootKey; + RETURN_IF_FAILED(progIdRootKey.CreateSubKey(shellKeyName.c_str(), KEY_READ | KEY_WRITE, &shellRootKey)); + + RETURN_IF_FAILED(shellRootKey.CreateSubKey(id.c_str(), KEY_READ | KEY_WRITE, &verbRootKey)); + return S_OK; +} + +HRESULT AutoPlay::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance) +{ + std::unique_ptr localInstance(new AutoPlay(msixRequest)); + if (localInstance == nullptr) + { + return E_OUTOFMEMORY; + } + + RETURN_IF_FAILED(localInstance->ParseManifest()); + + *instance = localInstance.release(); + + return S_OK; +} \ No newline at end of file diff --git a/preview/MsixCore/msixmgr/AutoPlay.hpp b/preview/MsixCore/msixmgr/AutoPlay.hpp new file mode 100644 index 00000000..80e72091 --- /dev/null +++ b/preview/MsixCore/msixmgr/AutoPlay.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include "GeneralUtil.hpp" +#include "IPackageHandler.hpp" +#include "MsixRequest.hpp" + +namespace MsixCoreLib +{ + enum AutoPlayType + { + InvalidAutoPlayType = 0, + DesktopAppxContent, + DesktopAppxDevice + }; + + /// the autoplay structure + struct AutoPlayObject + { + std::wstring id; + std::wstring action; + std::wstring handleEvent; + std::wstring provider; + std::wstring defaultIcon; + std::wstring appUserModelId; + std::wstring generatedProgId; + std::wstring generatedhandlerName; + std::wstring dropTargetHandler; + std::wstring parameters; + std::wstring hwEventHandler; + std::wstring initCmdLine; + AutoPlayType autoPlayType; + }; + + class AutoPlay : IPackageHandler + { + public: + HRESULT ExecuteForAddRequest(); + + HRESULT ExecuteForRemoveRequest(); + + static const PCWSTR HandlerName; + static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance); + private: + MsixRequest * m_msixRequest = nullptr; + std::vector m_autoPlay; + + AutoPlay() {} + AutoPlay(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {} + + HRESULT ParseManifest(); + + HRESULT ProcessAutoPlayForAdd(AutoPlayObject& autoPlayObject); + + HRESULT ProcessAutoPlayForRemove(AutoPlayObject& autoPlayObject); + + HRESULT GenerateProgId(_In_ std::wstring categoryName, _In_opt_ std::wstring subCategory, _Out_ std::wstring & generatedProgId); + + HRESULT GenerateHandlerName(_In_ LPWSTR type, _In_ const std::wstring handlerNameSeed, _Out_ std::wstring & generatedHandlerName); + + HRESULT BuildVerbKey(_In_ std::wstring generatedProgId, _In_ std::wstring id, _Out_ RegistryKey & verbRootKey); + + }; +} \ No newline at end of file diff --git a/preview/MsixCore/msixmgr/Base32Encoding.cpp b/preview/MsixCore/msixmgr/Base32Encoding.cpp new file mode 100644 index 00000000..358d41c5 --- /dev/null +++ b/preview/MsixCore/msixmgr/Base32Encoding.cpp @@ -0,0 +1,160 @@ +#include +#include + +#include "GeneralUtil.hpp" +#include +#include "MsixTraceLoggingProvider.hpp" +#include + +namespace MsixCoreLib +{ + HRESULT Base32Encoding::GetCharCount( + _In_ ULONG byteCount, + _Out_ ULONG* outWCharCount) + { + HRESULT hr = S_OK; + + assert(outWCharCount != NULL); + + if (byteCount != 0) + { + static const ULONG bitsPerByte = 8; + static const ULONG bitsPerBase32Digit = 5; + ULONG bitCount; + + RETURN_IF_FAILED(Multiply(byteCount, bitsPerByte, &bitCount)); + + *outWCharCount = bitCount / bitsPerBase32Digit; + + // We need to round up the division. When there aren't exactly enough bits to fill in the final base32 digit, + // the remaining bits are set to zero. + if (bitCount % bitsPerBase32Digit > 0) + { + ++*outWCharCount; + } + } + else + { + *outWCharCount = 0; + } + + return hr; + } + + HRESULT Base32Encoding::GetChars( + _In_reads_(byteCount) const BYTE* bytes, + _In_ ULONG byteCount, + _In_ ULONG maxWCharCount, + __out_ecount_part_opt(maxWCharCount, *outWCharCount) WCHAR* wchars, + __out_range(0, maxWCharCount) ULONG* outWCharCount) + { + HRESULT hr = S_OK; + + assert(bytes != NULL); + assert(wchars != NULL); + assert(outWCharCount != NULL); + + ULONG wcharCount = 0; + ULONG wcharsIdx = 0; + + RETURN_IF_FAILED(GetCharCount(byteCount, &wcharCount)); + if (wcharCount > maxWCharCount) + { + return HRESULT_FROM_WIN32(E_INVALIDARG); + } + + // Consider groups of five bytes. This is the smallest number of bytes that has a number of bits + // that's evently divisible by five. + // Every five bits starting with the most significant of the first byte are made into a base32 value. + // Each value is used to index into the alphabet array to produce a base32 digit. + // When out of bytes but the corresponding base32 value doesn't yet have five bits, 0 is used. + // Normally in these cases a particular number of '=' characters are appended to the resulting base32 + // string to indicate how many bits didn't come from the actual byte value. For our purposes no + // such padding characters are necessary. + // + // Bytes: aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee + // Base32 Values: 000aaaaa 000aaabb 000bbbbb 000bcccc 000ccccd 000ddddd 000ddeee 000eeeee + // + // Combo of byte a & F8 a & 07 b & 3E b & 01 c & 0F d & 7C d & 03 e & 1F + // values except b & C0 c & F0 d & 80 e & E0 + // for shifting + + // Make sure the following math doesn't overflow. + if (maxWCharCount > MaxValue / sizeof(*wchars)) + { + return HRESULT_FROM_WIN32(E_INVALIDARG); + } + + DebugFillMemory(wchars, maxWCharCount * sizeof(*wchars)); + + for (ULONG byteIdx = 0; byteIdx < byteCount; byteIdx += 5) + { + BYTE firstByte = bytes[byteIdx]; + BYTE secondByte = (byteIdx + 1) < byteCount ? bytes[byteIdx + 1] : 0; + + __assume(wcharsIdx + 2 <= wcharCount); + wchars[wcharsIdx++] = ValueToDigit((firstByte & 0xF8) >> 3); + wchars[wcharsIdx++] = ValueToDigit( + ((firstByte & 0x07) << 2) | ((secondByte & 0xC0) >> 6)); + + if (byteIdx + 1 < byteCount) + { + BYTE thirdByte = (byteIdx + 2) < byteCount ? bytes[byteIdx + 2] : 0; + + __assume(wcharsIdx + 2 <= wcharCount); + wchars[wcharsIdx++] = ValueToDigit((secondByte & 0x3E) >> 1); + wchars[wcharsIdx++] = ValueToDigit( + ((secondByte & 0x01) << 4) | ((thirdByte & 0xF0) >> 4)); + + if (byteIdx + 2 < byteCount) + { + BYTE fourthByte = (byteIdx + 3) < byteCount ? bytes[byteIdx + 3] : 0; + + __assume(wcharsIdx + 1 <= wcharCount); + wchars[wcharsIdx++] = ValueToDigit( + ((thirdByte & 0x0F) << 1) | ((fourthByte & 0x80) >> 7)); + + if (byteIdx + 3 < byteCount) + { + BYTE fifthByte = (byteIdx + 4) < byteCount ? bytes[byteIdx + 4] : 0; + + __assume(wcharsIdx + 2 <= wcharCount); + wchars[wcharsIdx++] = ValueToDigit((fourthByte & 0x7C) >> 2); + wchars[wcharsIdx++] = ValueToDigit( + ((fourthByte & 0x03) << 3) | ((fifthByte & 0xE0) >> 5)); + + if (byteIdx + 4 < byteCount) + { + __assume(wcharsIdx + 1 <= wcharCount); + wchars[wcharsIdx++] = ValueToDigit(fifthByte & 0x1F); + } + } + } + } + } + + *outWCharCount = wcharCount; + + return hr; + } + + WCHAR Base32Encoding::ValueToDigit( + _In_ BYTE value) + { + assert(value < 0x20); + // Douglas Crockford's base 32 alphabet variant is 0-9, A-Z except for i, l, o, and u. + static const WCHAR base32DigitList[] = L"0123456789abcdefghjkmnpqrstvwxyz"; + C_ASSERT(ARRAYSIZE(base32DigitList) == 0x20 + 1); // Plus one due to NULL terminator + return base32DigitList[value]; + } + + HRESULT Base32Encoding::Multiply(ULONG d1, ULONG d2, ULONG * result) + { + (*result) = d1 * d2; + if ((d2 == 0 || d1 <= MaxValue / d2)) + { + return NOERROR; + } + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + } +} diff --git a/preview/MsixCore/msixmgr/Base32Encoding.hpp b/preview/MsixCore/msixmgr/Base32Encoding.hpp new file mode 100644 index 00000000..54dee326 --- /dev/null +++ b/preview/MsixCore/msixmgr/Base32Encoding.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "GeneralUtil.hpp" + +#define DebugFillMemory(pv, cb) + +namespace MsixCoreLib +{ + class Base32Encoding + { + public: + + static const ULONG MaxValue = 0xFFFFFFFF; + + static HRESULT GetCharCount( + _In_ ULONG byteCount, + _Out_ ULONG* outWCharCount); + + // The resulting string is not NULL terminated and + // the base32 string produced does not use trailing padding characters. + // This means that the result is not decodable unless the original number + // of bytes is known. + static HRESULT GetChars( + _In_reads_(byteCount) const BYTE* bytes, + _In_ ULONG byteCount, + _In_ ULONG maxWCharCount, + __out_ecount_part_opt(maxWCharCount, *outWCharCount) WCHAR* wchars, + __out_range(0, maxWCharCount) ULONG* outWCharCount); + + private: + static WCHAR ValueToDigit( + _In_ BYTE value); + + static HRESULT Multiply( + _In_ ULONG d1, + _In_ ULONG d2, + _Out_ ULONG *result); + }; +} diff --git a/preview/MsixCore/msixmgr/BcryptLibrary.cpp b/preview/MsixCore/msixmgr/BcryptLibrary.cpp new file mode 100644 index 00000000..a67e96cd --- /dev/null +++ b/preview/MsixCore/msixmgr/BcryptLibrary.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +/// @note We intentionally load BCRYPT once on first use and never explicitly unload it; +/// it's intentionally kept loaded until process termination unloads it for us. +/// +/// @note We initialize the module handle to nullptr @ compile-time and only Load() modifies it, +/// and then only on first need. +/// +/// @note In the exceedingly rare event 2 threads concurrently call Load(), we'd call +/// LoadLibrary() twice but producing the same result (the 2nd call finds the DLL already +/// in the LoadedModuleTable and returns the same handle after bumping its reference +/// count). Thus we don't need any special synchronization to manage this, as worst case +/// we'll wind up overwriting the global with the same value on the 2nd load. +volatile PVOID MsixCoreLib::BcryptLibrary::bcryptModule = nullptr; +FARPROC MsixCoreLib::BcryptLibrary::functions[MsixCoreLib::BcryptLibrary::FunctionCount]; +PCSTR MsixCoreLib::BcryptLibrary::functionNames[MsixCoreLib::BcryptLibrary::FunctionCount] = { + "BCryptOpenAlgorithmProvider", + "BCryptCloseAlgorithmProvider", + "BCryptGetProperty", + "BCryptCreateHash", + "BCryptHashData", + "BCryptFinishHash", + "BCryptDestroyHash" +}; + +_Check_return_ HRESULT MsixCoreLib::BcryptLibrary::Load() +{ + if (ReadPointerAcquire(&bcryptModule) == nullptr) + { + HMODULE h = LoadLibraryExW(L"Bcrypt.dll", nullptr, 0); + if (h == nullptr) + { + _Analysis_assume_(GetLastError() != ERROR_SUCCESS); + return HRESULT_FROM_WIN32(GetLastError()); + } + + C_ASSERT(ARRAYSIZE(functions) == ARRAYSIZE(functionNames)); + FARPROC exports[ARRAYSIZE(functions)]; + for (size_t i = 0; i < ARRAYSIZE(functions); ++i) + { + exports[i] = GetProcAddress(h, functionNames[i]); + if (exports[i] == nullptr) + { + DWORD lastError = GetLastError(); + _Analysis_assume_(lastError != ERROR_SUCCESS); + assert(FreeLibrary(h)); + return HRESULT_FROM_WIN32(lastError); + } + } + + CopyMemory(functions, exports, sizeof(functions)); + WritePointerRelease(&bcryptModule, h); + } + return S_OK; +} diff --git a/preview/MsixCore/msixmgr/BcryptLibrary.hpp b/preview/MsixCore/msixmgr/BcryptLibrary.hpp new file mode 100644 index 00000000..9e86d0b3 --- /dev/null +++ b/preview/MsixCore/msixmgr/BcryptLibrary.hpp @@ -0,0 +1,116 @@ +#pragma once +#include + +///@note All functions are pretty much passthru to BCRYPT.DLL so prototypes are identical to BCRYPT.H +/// and not necessarily compliant with AppX Coding Standards (e.g. Hungarian Notation). + +namespace MsixCoreLib +{ + class BcryptLibrary + { + private: + enum + { + OpenAlgorithmProviderFunction = 0, + CloseAlgorithmProviderFunction, + GetPropertyFunction, + CreateHashFunction, + HashDataFunction, + FinishHashFunction, + DestroyHashFunction, + + FunctionCount + }; + + private: + static volatile PVOID bcryptModule; + static FARPROC functions[FunctionCount]; + static PCSTR functionNames[FunctionCount]; + + public: + static _Check_return_ HRESULT Load(); + + inline static _Must_inspect_result_ NTSTATUS BCryptOpenAlgorithmProvider( + _Out_ BCRYPT_ALG_HANDLE* phAlgorithm, + _In_ PCWSTR pszAlgId, + _In_opt_ PCWSTR pszImplementation, + _In_ ULONG dwFlags) + { + FARPROC function = functions[OpenAlgorithmProviderFunction]; + assert(function != NULL); + return (((NTSTATUS(WINAPI*) (BCRYPT_ALG_HANDLE*, PCWSTR, PCWSTR, ULONG)) + (function)))(phAlgorithm, pszAlgId, pszImplementation, dwFlags); + } + + inline static NTSTATUS BCryptCloseAlgorithmProvider( + _Inout_ BCRYPT_ALG_HANDLE hAlgorithm, + _In_ ULONG dwFlags) + { + FARPROC function = functions[CloseAlgorithmProviderFunction]; + assert(function != NULL); + return (((NTSTATUS(WINAPI*)(BCRYPT_ALG_HANDLE, ULONG))(function)))(hAlgorithm, dwFlags); + } + + inline static NTSTATUS BCryptGetProperty( + _In_ BCRYPT_HANDLE hObject, + _In_ PCWSTR pszProperty, + _Out_writes_bytes_to_opt_(cbOutput, *pcbResult) PUCHAR pbOutput, + _In_ ULONG cbOutput, + _Out_ ULONG* pcbResult, + _In_ ULONG dwFlags) + { + FARPROC function = functions[GetPropertyFunction]; + assert(function != NULL); +#pragma prefast(suppress:26045, "Annotations on function pointers don't work") + return (((NTSTATUS(WINAPI*)(BCRYPT_HANDLE, PCWSTR, PUCHAR, ULONG, ULONG*, ULONG)) + (function)))(hObject, pszProperty, pbOutput, cbOutput, pcbResult, dwFlags); + } + + inline static _Must_inspect_result_ NTSTATUS BCryptCreateHash( + _Inout_ BCRYPT_ALG_HANDLE hAlgorithm, + _Out_ BCRYPT_HASH_HANDLE* phHash, + _Out_writes_bytes_all_opt_(cbHashObject) PUCHAR pbHashObject, + _In_ ULONG cbHashObject, + _In_reads_bytes_opt_(cbSecret) PUCHAR pbSecret, + _In_ ULONG cbSecret, + _In_ ULONG dwFlags) + { + FARPROC function = functions[CreateHashFunction]; + assert(function != NULL); + return (((NTSTATUS(WINAPI*)(BCRYPT_ALG_HANDLE, BCRYPT_HASH_HANDLE*, PUCHAR, ULONG, PUCHAR, ULONG, ULONG)) + (function)))(hAlgorithm, phHash, pbHashObject, cbHashObject, pbSecret, cbSecret, dwFlags); + } + + inline static _Must_inspect_result_ NTSTATUS BCryptHashData( + _Inout_ BCRYPT_HASH_HANDLE hHash, + _In_reads_bytes_(cbInput) PUCHAR pbInput, + _In_ ULONG cbInput, + _In_ ULONG dwFlags) + { + FARPROC function = functions[HashDataFunction]; + assert(function != NULL); + return (((NTSTATUS(WINAPI*)(BCRYPT_HASH_HANDLE, PUCHAR, ULONG, ULONG)) + (function)))(hHash, pbInput, cbInput, dwFlags); + } + + inline static _Must_inspect_result_ NTSTATUS BCryptFinishHash( + _Inout_ BCRYPT_HASH_HANDLE hHash, + _Out_writes_bytes_all_(cbOutput) PUCHAR pbOutput, + _In_ ULONG cbOutput, + _In_ ULONG dwFlags) + { + FARPROC function = functions[FinishHashFunction]; + assert(function != NULL); + return (((NTSTATUS(WINAPI*)(BCRYPT_HASH_HANDLE, PUCHAR, ULONG, ULONG)) + (function)))(hHash, pbOutput, cbOutput, dwFlags); + } + + inline static NTSTATUS BCryptDestroyHash( + _Inout_ BCRYPT_HASH_HANDLE hHash) + { + FARPROC function = functions[DestroyHashFunction]; + assert(function != NULL); + return (((NTSTATUS(WINAPI*)(BCRYPT_HASH_HANDLE))(function)))(hHash); + } + }; +} diff --git a/preview/MsixCore/msixmgr/Constants.hpp b/preview/MsixCore/msixmgr/Constants.hpp index 64df3582..80b88aed 100644 --- a/preview/MsixCore/msixmgr/Constants.hpp +++ b/preview/MsixCore/msixmgr/Constants.hpp @@ -60,6 +60,7 @@ static const std::wstring appendMenuFlagAttribute = L"AppendMenuFlag"; static const std::wstring oleVerbFlagAttribute = L"OleVerbFlag"; static const std::wstring resourceIndexAttribute = L"ResourceIndex"; static const std::wstring taskIdAttribute = L"TaskId"; +static const std::wstring IdAttribute = L"Id"; static const std::wstring extensionQuery = L"/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='Extensions']/*[local-name()='Extension']"; static const std::wstring ftaQuery = L"*[local-name()='FileTypeAssociation']"; @@ -88,6 +89,10 @@ static const std::wstring defaultIconQuery = L"*[local-name()='DefaultIcon']"; static const std::wstring toolboxBitmapQuery = L"*[local-name()='ToolboxBitmap32']"; static const std::wstring comVerbQuery = L"*[local-name()='Verbs']/*[local-name()='Verb']"; static const std::wstring startupTaskQuery = L"*[local-name()='StartupTask']"; +static const std::wstring launchActionQuery = L"*[local-name()='AutoPlayContent']/*[local-name()='LaunchAction']"; +static const std::wstring invokeActionQuery = L"*[local-name()='AutoPlayHandler']/*[local-name()='InvokeAction']"; +static const std::wstring invokeActionContentQuery = L"*[local-name()='Content']"; +static const std::wstring invokeActionDeviceQuery = L"*[local-name()='Device']"; /// Constants for Firewall DEH static const std::wstring firewallExtensionQuery = L"/*[local-name()='Package']/*[local-name()='Extensions']/*[local-name()='Extension']/*[local-name()='FirewallRules']/*[local-name()='Rule']"; @@ -101,6 +106,29 @@ static const std::wstring remotePortMaxAttribute = L"RemotePortMax"; static const std::wstring directionIn = L"in"; static const std::wstring directionOut = L"out"; +/// Constants for AutoPlay DEH +static const std::wstring desktopAppXExtensionCategory = L"windows.autoPlayHandler"; + +static const std::wstring desktopAppXContentSubCategory = L"Windows.AutoPlayDesktopAppX.Content"; +static const std::wstring desktopAppXDeviceSubCategory = L"Windows.AutoPlayDesktopAppX.Device"; + +static const std::wstring idAttributeName = L"Verb"; +static const std::wstring actionAttributeName = L"ActionDisplayName"; +static const std::wstring providerAttributeName = L"ProviderDisplayName"; +static const std::wstring contentEventAttributeName = L"ContentEvent"; +static const std::wstring deviceEventAttributeName = L"DeviceEvent"; +static const std::wstring dropTargetHandlerAttributeName = L"DropTargetHandler"; +static const std::wstring parametersAttributeName = L"Parameters"; +static const std::wstring hwEventHandlerAttributeName = L"HWEventHandler"; +static const std::wstring InitCmdLineAttributeName = L"InitCmdLine"; +static const std::wstring dropTargetRegKeyName = L"DropTarget"; +static const std::wstring commandKeyRegName = L"command"; +static const wchar_t desktopAppXProtocolDelegateExecuteValue[] = L"{BFEC0C93-0B7D-4F2C-B09C-AFFFC4BDAE78}"; + +static const std::wstring explorerRegKeyName = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"; +static const std::wstring handlerKeyName = L"AutoPlayHandlers\\Handlers"; +static const std::wstring eventHandlerRootRegKeyName = L"AutoPlayHandlers\\EventHandlers"; + static const std::wstring clsidKeyName = L"CLSID"; static const std::wstring inprocHandlerKeyName = L"InprocHandler32"; static const std::wstring defaultInprocHandler = L"ole32.dll"; diff --git a/preview/MsixCore/msixmgr/CryptoProvider.cpp b/preview/MsixCore/msixmgr/CryptoProvider.cpp new file mode 100644 index 00000000..790d5a3d --- /dev/null +++ b/preview/MsixCore/msixmgr/CryptoProvider.cpp @@ -0,0 +1,180 @@ +#include +#include +#include +#include "GeneralUtil.hpp" +#include +#include +#include "MsixTraceLoggingProvider.hpp" + +namespace MsixCoreLib +{ + CryptoProvider::CryptoProvider() + { + this->providerHandle = NULL; + this->hashHandle = NULL; + this->digest.bytes = NULL; + this->digest.length = 0; + } + + CryptoProvider::~CryptoProvider() + { + Reset(); + } + + void CryptoProvider::Reset() + { + this->digest.bytes = NULL; + this->digest.length = 0; + + if (NULL != this->hashHandle) + { + if (!SUCCEEDED(BcryptLibrary::BCryptDestroyHash(this->hashHandle))) + { + return; + } + this->hashHandle = NULL; + } + + if (NULL != this->providerHandle) + { + if (!SUCCEEDED(BcryptLibrary::BCryptCloseAlgorithmProvider(this->providerHandle, 0))) + { + return; + } + this->providerHandle = NULL; + } + } + + HRESULT CryptoProvider::Create( + _Outptr_ CryptoProvider** provider) + { + RETURN_IF_FAILED(BcryptLibrary::Load()); + + std::unique_ptr cp(new APPXCOMMON_NEW_TAG CryptoProvider()); + if (cp == nullptr) + { + return HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + } + + *provider = cp.release(); + return S_OK; + } + + HRESULT CryptoProvider::OpenProvider() + { + PCWSTR algorithmId = BCRYPT_SHA256_ALGORITHM; + PCWSTR implementation = NULL; + ULONG flags = 0; + + BcryptLibrary::BCryptOpenAlgorithmProvider( + &this->providerHandle, + algorithmId, + implementation, + flags); + + return S_OK; + } + + HRESULT CryptoProvider::StartDigest() + { + BYTE* hashObject; + ULONG hashObjectSize; + ULONG resultSize; + + RETURN_IF_FAILED(OpenProvider()); + + BcryptLibrary::BCryptGetProperty( + this->providerHandle, + BCRYPT_OBJECT_LENGTH, + (PUCHAR)&hashObjectSize, + sizeof(hashObjectSize), + &resultSize, + 0); + + if (sizeof(this->quickHashObjectBuffer) >= hashObjectSize) + { + hashObject = this->quickHashObjectBuffer; + } + else + { + hashObject = hashObjectBuffer.data(); + } + + BcryptLibrary::BCryptCreateHash( + this->providerHandle, + &this->hashHandle, + hashObject, + hashObjectSize, + NULL, + NULL, + 0); + + return S_OK; + } + + HRESULT CryptoProvider::DigestData( + _In_ const COMMON_BYTES* data) + { + BcryptLibrary::BCryptHashData( + this->hashHandle, + data->bytes, + data->length, + 0); + + return S_OK; + } + + HRESULT CryptoProvider::GetDigest( + _Out_ COMMON_BYTES* digest) + { + BYTE* digestPtr; + ULONG digestSize; + + if (0 == this->digest.length) + { + NTSTATUS status; + + ULONG resultSize; + + status = BcryptLibrary::BCryptGetProperty( + this->providerHandle, + BCRYPT_HASH_LENGTH, + (PUCHAR)&digestSize, + sizeof(digestSize), + &resultSize, + 0); + + if (FAILED(HRESULT_FROM_NT(status))) + { + return HRESULT_FROM_NT(status); + } + + if (sizeof(this->quickDigestBuffer) >= digestSize) + { + digestPtr = this->quickDigestBuffer; + } + else + { + digestPtr = this->digestBuffer.data(); + } + + status = BcryptLibrary::BCryptFinishHash( + this->hashHandle, + digestPtr, + digestSize, + 0); + + if (FAILED(HRESULT_FROM_NT(status))) + { + return HRESULT_FROM_NT(status); + } + + this->digest.bytes = digestPtr; + this->digest.length = digestSize; + } + + *digest = this->digest; + + return S_OK; + } +} diff --git a/preview/MsixCore/msixmgr/CryptoProvider.hpp b/preview/MsixCore/msixmgr/CryptoProvider.hpp new file mode 100644 index 00000000..6f95c8bd --- /dev/null +++ b/preview/MsixCore/msixmgr/CryptoProvider.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#define APPXCOMMON_NEW_TAG (std::nothrow) + +namespace MsixCoreLib +{ + struct COMMON_BYTES + { + ULONG length; + __field_ecount(length) BYTE* bytes; + }; + + // CryptoProvider objects are not thread-safe, hence should not be called from multiple threads simultaneously. + // Usage: + // (StartDigest DigestData* GetDigest* Reset)* + class CryptoProvider + { + private: + + BCRYPT_ALG_HANDLE providerHandle; + BCRYPT_HASH_HANDLE hashHandle; + std::vector hashObjectBuffer; + std::vector digestBuffer; + BYTE quickHashObjectBuffer[700]; // Tests shows that HMAC with 256-bit or 512-bit keys requires 600+ bytes of hash object space. + BYTE quickDigestBuffer[64]; // accommodates up to 64-byte hashes + COMMON_BYTES digest; + + HRESULT OpenProvider(); + + CryptoProvider(); + + public: + ~CryptoProvider(); + + void Reset(); + + HRESULT StartDigest(); + + HRESULT DigestData( + _In_ const COMMON_BYTES* data); + + HRESULT GetDigest( + _Out_ COMMON_BYTES* digest); + + static HRESULT Create( + _Outptr_ CryptoProvider** provider); + }; +} \ No newline at end of file diff --git a/preview/MsixCore/msixmgr/MsixRequest.cpp b/preview/MsixCore/msixmgr/MsixRequest.cpp index 1ab06195..ac5c89df 100644 --- a/preview/MsixCore/msixmgr/MsixRequest.cpp +++ b/preview/MsixCore/msixmgr/MsixRequest.cpp @@ -35,6 +35,7 @@ #include "PrepareDevirtualizedRegistry.hpp" #include "WriteDevirtualizedRegistry.hpp" #include "FirewallRules.hpp" +#include "AutoPlay.hpp" #include "VirtualFileHandler.hpp" #include "Constants.hpp" @@ -81,7 +82,8 @@ std::map AddHandlers = {ComServer::HandlerName, {ComServer::CreateHandler, StartupTask::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}}, {StartupTask::HandlerName, {StartupTask::CreateHandler, FileTypeAssociation::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}}, {FileTypeAssociation::HandlerName, {FileTypeAssociation::CreateHandler, FirewallRules::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}}, - {FirewallRules::HandlerName, {FirewallRules::CreateHandler, InstallComplete::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}}, + {FirewallRules::HandlerName, {FirewallRules::CreateHandler, AutoPlay::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}}, + {AutoPlay::HandlerName, {AutoPlay::CreateHandler, InstallComplete::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}}, {InstallComplete::HandlerName, {InstallComplete::CreateHandler, nullptr, ExecuteErrorHandler, ErrorHandler::HandlerName}}, {ErrorHandler::HandlerName, {ErrorHandler::CreateHandler, nullptr, ReturnError, nullptr}}, }; @@ -98,7 +100,8 @@ std::map RemoveHandlers = {ComServer::HandlerName, {ComServer::CreateHandler, StartupTask::HandlerName, IgnoreAndProcessNextHandler}}, {StartupTask::HandlerName, {StartupTask::CreateHandler, FileTypeAssociation::HandlerName, IgnoreAndProcessNextHandler}}, {FileTypeAssociation::HandlerName, {FileTypeAssociation::CreateHandler, FirewallRules::HandlerName, IgnoreAndProcessNextHandler}}, - {FirewallRules::HandlerName, {FirewallRules::CreateHandler, VirtualFileHandler::HandlerName, IgnoreAndProcessNextHandler}}, + {FirewallRules::HandlerName, {FirewallRules::CreateHandler, AutoPlay::HandlerName, IgnoreAndProcessNextHandler}}, + {AutoPlay::HandlerName, {AutoPlay::CreateHandler, VirtualFileHandler::HandlerName, IgnoreAndProcessNextHandler}}, {VirtualFileHandler::HandlerName, {VirtualFileHandler::CreateHandler, WriteDevirtualizedRegistry::HandlerName, IgnoreAndProcessNextHandler}}, {WriteDevirtualizedRegistry::HandlerName, {WriteDevirtualizedRegistry::CreateHandler, Extractor::HandlerName, IgnoreAndProcessNextHandler}}, {Extractor::HandlerName, {Extractor::CreateHandler, nullptr, IgnoreAndProcessNextHandler}}, diff --git a/preview/MsixCore/msixmgr/Package.hpp b/preview/MsixCore/msixmgr/Package.hpp index 13a0a3ba..04ed6724 100644 --- a/preview/MsixCore/msixmgr/Package.hpp +++ b/preview/MsixCore/msixmgr/Package.hpp @@ -3,6 +3,7 @@ #include "AppxPackaging.hpp" #include "MSIXWindows.hpp" #include "IPackage.hpp" +#include "FilePaths.hpp" #include namespace MsixCoreLib @@ -21,6 +22,7 @@ namespace MsixCoreLib std::wstring m_publisher; std::wstring m_publisherName; std::wstring m_relativeLogoPath; + std::wstring m_packageDirectoryPath; std::vector m_capabilities; ComPtr m_manifestReader; @@ -36,6 +38,14 @@ namespace MsixCoreLib std::wstring GetVersion(); std::wstring GetPublisher() { return m_publisher; } std::wstring GetPublisherDisplayName() { return m_publisherName; } + std::wstring GetApplicationId() { return m_applicationId; } + std::wstring GetRelativeLogoPath() { + return m_relativeLogoPath; + } + std::wstring GetPackageDirectoryPath() { + m_packageDirectoryPath = FilePathMappings::GetInstance().GetMsixCoreDirectory() + m_packageFullName + L"\\"; + return m_packageDirectoryPath; + } std::vector GetCapabilities() { @@ -87,6 +97,8 @@ namespace MsixCoreLib std::wstring GetPublisher() { return m_publisher; } std::wstring GetPublisherDisplayName() { return m_publisherName; } std::unique_ptr GetLogo(); + std::wstring GetApplicationId() { return m_applicationId; } + std::vector GetCapabilities() { return m_capabilities; @@ -121,6 +133,7 @@ namespace MsixCoreLib std::wstring GetVersion() { return PackageBase::GetVersion(); } std::wstring GetPublisher() { return m_publisher; } std::wstring GetPublisherDisplayName() { return m_publisherName; } + std::wstring GetApplicationId() { return m_applicationId; } std::unique_ptr GetLogo(); diff --git a/preview/MsixCore/msixmgr/PopulatePackageInfo.cpp b/preview/MsixCore/msixmgr/PopulatePackageInfo.cpp index 88ba8844..38074e11 100644 --- a/preview/MsixCore/msixmgr/PopulatePackageInfo.cpp +++ b/preview/MsixCore/msixmgr/PopulatePackageInfo.cpp @@ -83,7 +83,7 @@ HRESULT PopulatePackageInfo::ExecuteForRemoveRequest() std::shared_ptr package; RETURN_IF_FAILED(GetPackageInfoFromManifest(packageDirectoryPath, MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_ALLOWSIGNATUREORIGINUNKNOWN, &package)); - + if (package == nullptr) { return E_FAIL; diff --git a/preview/MsixCore/msixmgr/RegistryKey.cpp b/preview/MsixCore/msixmgr/RegistryKey.cpp index f2667b7c..76578f60 100644 --- a/preview/MsixCore/msixmgr/RegistryKey.cpp +++ b/preview/MsixCore/msixmgr/RegistryKey.cpp @@ -147,4 +147,11 @@ HRESULT RegistryKey::KeyExists(PCWSTR subkey, bool& exists) return S_OK; } return HRESULT_FROM_WIN32(rc); +} + +HRESULT RegistryKey::SetUInt32Value( + _In_opt_ PCWSTR name, + _In_ UINT32 value) +{ + return SetValue(name, &value, static_cast(sizeof(value)), REG_DWORD); } \ No newline at end of file diff --git a/preview/MsixCore/msixmgr/RegistryKey.hpp b/preview/MsixCore/msixmgr/RegistryKey.hpp index 80c9984e..9a6f8040 100644 --- a/preview/MsixCore/msixmgr/RegistryKey.hpp +++ b/preview/MsixCore/msixmgr/RegistryKey.hpp @@ -129,6 +129,10 @@ public: _In_ PCWSTR subkey, _Out_ bool& exists); + HRESULT SetUInt32Value( + _In_opt_ PCWSTR name, + _In_ UINT32 value); + template static HRESULT EnumKeyAndDoActionForAllSubkeys( _In_ RegistryKey* registryKey, _In_ TAction subkeyActionFunction) diff --git a/preview/MsixCore/msixmgrLib/msixmgrLib.vcxproj b/preview/MsixCore/msixmgrLib/msixmgrLib.vcxproj index 7113bc9e..5a133fb1 100644 --- a/preview/MsixCore/msixmgrLib/msixmgrLib.vcxproj +++ b/preview/MsixCore/msixmgrLib/msixmgrLib.vcxproj @@ -1,4 +1,4 @@ - + @@ -187,6 +187,10 @@ + + + + @@ -227,6 +231,10 @@ + + + +