Loading Cargo.lock +0 −14 Changes for Cargo.lock: 0 added lines, 14 removed lines. Original line number Diff line number Diff line Loading @@ -2629,7 +2629,6 @@ dependencies = [ "rure", "rusqlite 0.33.0", "rust_minidump_writer_linux", "shell_windows", "signature_cache", "static_prefs", "storage", Loading Loading @@ -6153,19 +6152,6 @@ dependencies = [ "keccak", ] [[package]] name = "shell_windows" version = "0.1.0" dependencies = [ "base64 0.22.1", "log", "nserror", "nsstring", "sha2", "windows", "xpcom", ] [[package]] name = "shlex" version = "1.3.0" Loading browser/components/build/components.conf +0 −6 Changes for browser/components/build/components.conf: 0 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -46,10 +46,4 @@ elif buildconfig.substs['OS_ARCH'] == 'WINNT': 'type': 'nsIEHistoryEnumerator', 'headers': ['/browser/components/migration/nsIEHistoryEnumerator.h'], }, { 'cid': '{115f4862-f302-42a2-a58c-d09f636ab601}', 'contract_ids': ['@mozilla.org/limited-access-feature-service;1'], 'headers': ['/browser/components/shell/rust/shell_windows/src/LimitedAccessFeatureService.h'], 'legacy_constructor': 'new_limited_access_feature_service', }, ] browser/components/shell/Windows11LimitedAccessFeatures.cpp 0 → 100644 +370 −0 Changes for browser/components/shell/Windows11LimitedAccessFeatures.cpp: 370 added lines, 0 removed lines. Original line number Diff line number Diff line /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * This file exists so that LaunchModernSettingsDialogDefaultApps can be called * without linking to libxul. */ #include "Windows11LimitedAccessFeatures.h" #include "mozilla/Logging.h" static mozilla::LazyLogModule sLog("Windows11LimitedAccessFeatures"); #define LAF_LOG(level, msg, ...) MOZ_LOG(sLog, level, (msg, ##__VA_ARGS__)) // MINGW32 is not supported for these features // Fall back function defined in the #else #ifndef __MINGW32__ # include "mozilla/ErrorResult.h" # include "nsString.h" # include "nsCOMPtr.h" # include "nsComponentManagerUtils.h" # include "nsIWindowsRegKey.h" # include "nsWindowsHelpers.h" # include "nsICryptoHash.h" # include "mozilla/Atomics.h" # include "mozilla/Base64.h" # include "mozilla/Char16.h" # include "mozilla/WinHeaderOnlyUtils.h" # include "WinUtils.h" # include <wrl.h> # include <inspectable.h> # include <roapi.h> # include <windows.services.store.h> # include <windows.foundation.h> using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::ApplicationModel; using namespace mozilla; /** * To unlock features, we need: * a feature identifier * a token, * an attestation string * a token * * The token is generated by Microsoft and must * match the publisher id Microsoft thinks we have, for a particular * feature. * * To get a token, find the right microsoft email address by doing * a search on the web for the feature you want unlocked and reach * out to the right people at Microsoft. * * The token is generated from Microsoft. * The jumbled code in the attestation string is a publisher id and * must match the code in the resources / .rc file for the identity, * looking like this for non-MSIX builds: * * Identity LimitedAccessFeature {{ L"MozillaFirefox_pcsmm0jrprpb2" }} * * Broken down: * Identity LimitedAccessFeature {{ L"PRODUCTNAME_PUBLISHERID" }} * * That is injected into our build in create_rc.py and is necessary * to unlock the taskbar pinning feature / APIs from an unpackaged * build. * * In the above, the token is generated from the publisher id (pcsmm0jrprpb2) * and the product name (MozillaFirefox) * * All tokens listed here were provided to us by Microsoft. * * Below and in create_rc.py, we used this set: * * Token: "kRFiWpEK5uS6PMJZKmR7MQ==" * Product Name: "MozillaFirefox" * Publisher ID: "pcsmm0jrprpb2" * * Microsoft also provided these other tokens, which will will * work if accompanied by the matching changes to create_rc.py: * ----- * Token: "RGEhsYgKhmPLKyzkEHnMhQ==" * Product Name: "FirefoxBeta" * Publisher ID: "pcsmm0jrprpb2" * * ----- * * Token: "qbVzns/9kT+t15YbIwT4Jw==" * Product Name: "FirefoxNightly" * Publisher ID: "pcsmm0jrprpb2" * * To use those instead, you have to ensure that the LimitedAccessFeature * generated in create_rc.py has the product name and publisher id * matching the token used in this file. * * For non-packaged (non-MSIX) builds, any of the above sets will work. * Just make sure the right (ProductName_PublisherID) value is in the * generated resource data for the executable, and the matching * (Token) and attestation string * * To get MSIX/packaged builds to work, the product name and publisher in * the final manifest (searchfox.org/mozilla-central/search?q=APPX_PUBLISHER) * should match the token in this file. For that case, the identity value * in the resources does not matter. * * See here for Microsoft examples: https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/TaskbarManager/CppUnpackagedDesktopTaskbarPin */ /** * Unlocks a Windows Limited Access Feature (LAF) by generating a token and * attestation. * * This function first retrieves the LAF key from the registry using * the LAF identifier and then combines the lafId, lafKey, and PFN * into a token. * * Applying Base64(SHA256Encode("<lafId>!<lafKey>!<PFN>")[0..16]) yields the * complete LAF token for unlocking. * * Taking the last 13 characters of the PFN yields the publisher identifier * which is used in the following boilerplate: * "<PFN[-13]> has registered their use of <lafId> with Microsoft and * agrees to the terms of use." * * @return {LimitedAccessFeatureInfo} containing the generated * token and attestation upon success. Contains empty strings for * these fields upon failure. */ static mozilla::Result<LimitedAccessFeatureInfo, nsresult> GenerateLimitedAccessFeatureInfo(const nsCString& debugName, const nsString& lafId) { nsresult rv; // Read registry key for a given Limited Access Feature with ID lafId. nsAutoString keyData; nsCOMPtr<nsIWindowsRegKey> regKey = do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); NS_ENSURE_SUCCESS(rv, Err(rv)); const nsAutoString regPath = u"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModel\\LimitedAccessFeatures\\"_ns + lafId; rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, regPath, nsIWindowsRegKey::ACCESS_READ); NS_ENSURE_SUCCESS(rv, Err(rv)); rv = regKey->ReadStringValue(u""_ns, keyData); NS_ENSURE_SUCCESS(rv, Err(rv)); // Get Package Family Name (PFN) and assemble a string to hash. // First convert this string to SHA256, take the first 16 bytes // only, and then read as base 64. nsAutoCString hashStringResult; nsAutoString encodedToken; // The non-MSIX family name must match whatever value is in create_rc.py // Currently this is MozillaFirefox_pcsmm0jrprpb2 nsAutoString familyName; if (widget::WinUtils::HasPackageIdentity()) { familyName = nsDependentString(mozilla::GetPackageFamilyName().get()); } else { familyName = u"MozillaFirefox_pcsmm0jrprpb2"_ns; } const nsAutoCString hashString = NS_ConvertUTF16toUTF8(lafId + u"!"_ns + keyData + u"!"_ns + familyName); nsCOMPtr<nsICryptoHash> cryptoHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv); NS_ENSURE_SUCCESS(rv, Err(rv)); rv = cryptoHash->Init(nsICryptoHash::SHA256); NS_ENSURE_SUCCESS(rv, Err(rv)); rv = cryptoHash->Update(reinterpret_cast<const uint8_t*>(hashString.get()), hashString.Length()); NS_ENSURE_SUCCESS(rv, Err(rv)); rv = cryptoHash->Finish(false, hashStringResult); NS_ENSURE_SUCCESS(rv, Err(rv)); // Keep only first 16 bytes and encode hashStringResult.Truncate(hashStringResult.Length() - 16); rv = Base64Encode(hashStringResult, encodedToken); NS_ENSURE_SUCCESS(rv, Err(rv)); // The PFN contains a package ID in the last 13 characters. // This ID is based on the value in the publisher field of the // AppManifest. This ID is used to assemble the attestation. familyName.Cut(0, familyName.Length() - 13); nsAutoString attestation = familyName + u" has registered their use of "_ns + lafId + u" with Microsoft and agrees to the terms of use."_ns; LimitedAccessFeatureInfo result = {debugName, lafId, encodedToken, attestation}; return result; } /** Implementation of the Win11LimitedAccessFeaturesInterface. */ class Win11LimitedAccessFeatures : public Win11LimitedAccessFeaturesInterface { public: using AtomicState = Atomic<int, SequentiallyConsistent>; Result<bool, HRESULT> Unlock(Win11LimitedAccessFeatureType feature) override; private: AtomicState& GetState(Win11LimitedAccessFeatureType feature); Result<bool, HRESULT> UnlockImplementation( const LimitedAccessFeatureInfo& lafInfo); /** * Store the state as an atomic so that it can be safely accessed from * different threads. */ static AtomicState mTaskbarState; static AtomicState mDefaultState; enum State { Uninitialized, Locked, Unlocked, }; }; Win11LimitedAccessFeatures::AtomicState Win11LimitedAccessFeatures::mTaskbarState( Win11LimitedAccessFeatures::Uninitialized); Win11LimitedAccessFeatures::AtomicState Win11LimitedAccessFeatures::mDefaultState( Win11LimitedAccessFeatures::Uninitialized); RefPtr<Win11LimitedAccessFeaturesInterface> CreateWin11LimitedAccessFeaturesInterface() { RefPtr<Win11LimitedAccessFeaturesInterface> result( new Win11LimitedAccessFeatures()); return result; } Result<bool, HRESULT> Win11LimitedAccessFeatures::Unlock( Win11LimitedAccessFeatureType feature) { AtomicState& atomicState = GetState(feature); // Win11LimitedAccessFeatureType::Taskbar auto taskbarLafInfo = GenerateLimitedAccessFeatureInfo( "Win11LimitedAccessFeatureType::Taskbar"_ns, u"com.microsoft.windows.taskbar.pin"_ns); if (taskbarLafInfo.isErr()) { LAF_LOG(LogLevel::Debug, "Unlocking taskbar failed with error %d", NS_ERROR_GET_CODE(taskbarLafInfo.unwrapErr())); return Err(E_FAIL); } LimitedAccessFeatureInfo limitedAccessFeatureInfo[] = { taskbarLafInfo.unwrap()}; const auto& lafInfo = limitedAccessFeatureInfo[static_cast<int>(feature)]; LAF_LOG(LogLevel::Debug, "Limited Access Feature Info for %s. Feature %ls, %ls, %ls", lafInfo.debugName.get(), lafInfo.token.getW(), lafInfo.token.getW(), lafInfo.attestation.getW()); int state = atomicState; if (state != Uninitialized) { LAF_LOG(LogLevel::Debug, "%s already initialized! State = %s", lafInfo.debugName.get(), (state == Unlocked) ? "true" : "false"); return (state == Unlocked); } // If multiple threads read the state at the same time, and it's unitialized, // both threads will unlock the feature. This situation is unlikely, but even // if it happens, it's not a problem. auto result = UnlockImplementation(lafInfo); int newState = Locked; if (!result.isErr() && result.unwrap()) { newState = Unlocked; } atomicState = newState; return result; } Win11LimitedAccessFeatures::AtomicState& Win11LimitedAccessFeatures::GetState( Win11LimitedAccessFeatureType feature) { switch (feature) { case Win11LimitedAccessFeatureType::Taskbar: return mTaskbarState; default: LAF_LOG(LogLevel::Debug, "Missing feature type for %d", static_cast<int>(feature)); MOZ_ASSERT(false, "Unhandled feature type! Add a new atomic state variable, add " "that entry to the switch statement above, and add the proper " "entries for the feature and the token."); return mDefaultState; } } Result<bool, HRESULT> Win11LimitedAccessFeatures::UnlockImplementation( const LimitedAccessFeatureInfo& lafInfo) { ComPtr<ILimitedAccessFeaturesStatics> limitedAccessFeatures; ComPtr<ILimitedAccessFeatureRequestResult> limitedAccessFeaturesResult; HRESULT hr = RoGetActivationFactory( HStringReference( RuntimeClass_Windows_ApplicationModel_LimitedAccessFeatures) .Get(), IID_ILimitedAccessFeaturesStatics, &limitedAccessFeatures); if (!SUCCEEDED(hr)) { LAF_LOG(LogLevel::Debug, "%s activation error. HRESULT = 0x%lx", lafInfo.debugName.get(), hr); return Err(hr); } hr = limitedAccessFeatures->TryUnlockFeature( HStringReference(lafInfo.feature.get()).Get(), HStringReference(lafInfo.token.get()).Get(), HStringReference(lafInfo.attestation.get()).Get(), &limitedAccessFeaturesResult); if (!SUCCEEDED(hr)) { LAF_LOG(LogLevel::Debug, "%s unlock error. HRESULT = 0x%lx", lafInfo.debugName.get(), hr); return Err(hr); } LimitedAccessFeatureStatus status; hr = limitedAccessFeaturesResult->get_Status(&status); if (!SUCCEEDED(hr)) { LAF_LOG(LogLevel::Debug, "%s get status error. HRESULT = 0x%lx", lafInfo.debugName.get(), hr); return Err(hr); } int state = Unlocked; if ((status != LimitedAccessFeatureStatus_Available) && (status != LimitedAccessFeatureStatus_AvailableWithoutToken)) { LAF_LOG(LogLevel::Debug, "%s not available. HRESULT = 0x%lx", lafInfo.debugName.get(), hr); state = Locked; } return (state == Unlocked); } #else // MINGW32 implementation RefPtr<Win11LimitedAccessFeaturesInterface> CreateWin11LimitedAccessFeaturesInterface() { RefPtr<Win11LimitedAccessFeaturesInterface> result; return result; } static mozilla::Result<LimitedAccessFeatureInfo, nsresult> GenerateLimitedAccessFeatureInfo(const nsCString& debugName, const nsString& lafId) { return mozilla::Err(NS_ERROR_NOT_IMPLEMENTED); } #endif browser/components/shell/Windows11LimitedAccessFeatures.h 0 → 100644 +65 −0 Changes for browser/components/shell/Windows11LimitedAccessFeatures.h: 65 added lines, 0 removed lines. Original line number Diff line number Diff line /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef SHELL_WINDOWS11LIMITEDACCESSFEATURES_H__ #define SHELL_WINDOWS11LIMITEDACCESSFEATURES_H__ #include "nsISupportsImpl.h" #include "nsString.h" #include "mozilla/Result.h" #include "mozilla/ResultVariant.h" #include <winerror.h> #include <windows.h> // for HRESULT #include "mozilla/DefineEnum.h" #include <winerror.h> MOZ_DEFINE_ENUM_CLASS(Win11LimitedAccessFeatureType, (Taskbar)); /** * Class to manage unlocking limited access features on Windows 11. * Unless stubbing for testing purposes, create objects of this * class with CreateWin11LimitedAccessFeaturesInterface. * * Windows 11 requires certain features to be unlocked in order to work * (for instance, the Win11 Taskbar pinning APIs). Call Unlock() * to unlock them. Generally, results will be cached in atomic variables * and future calls to Unlock will be as long as it takes * to fetch an atomic variable. */ class Win11LimitedAccessFeaturesInterface { public: /** * Unlocks the limited access features, if possible. * * Returns an error code on error, true on successful unlock, * false on unlock failed (but with no error). */ virtual mozilla::Result<bool, HRESULT> Unlock( Win11LimitedAccessFeatureType feature) = 0; /** * Reference counting and cycle collection. */ NS_INLINE_DECL_REFCOUNTING(Win11LimitedAccessFeaturesInterface) protected: virtual ~Win11LimitedAccessFeaturesInterface() {} }; struct LimitedAccessFeatureInfo { const nsCString debugName; const nsString feature; const nsString token; const nsString attestation; }; RefPtr<Win11LimitedAccessFeaturesInterface> CreateWin11LimitedAccessFeaturesInterface(); mozilla::Result<LimitedAccessFeatureInfo, nsresult> GenerateLimitedAccessFeatureInfo(const nsCString& debugName, const nsString& lafId); #endif // SHELL_WINDOWS11LIMITEDACCESSFEATURES_H__ browser/components/shell/Windows11TaskbarPinning.cpp +16 −29 Changes for browser/components/shell/Windows11TaskbarPinning.cpp: 16 added lines, 29 removed lines. Original line number Diff line number Diff line Loading @@ -4,8 +4,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "Windows11TaskbarPinning.h" #include "Windows11LimitedAccessFeatures.h" #include "nsILimitedAccessFeature.h" #include "nsWindowsHelpers.h" #include "MainThreadUtils.h" #include "nsThreadUtils.h" Loading Loading @@ -70,39 +70,24 @@ using namespace ABI::Windows::UI::Shell; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::ApplicationModel; static Win11PinToTaskBarResult UnlockTaskbarPinFeature() { nsCOMPtr<nsILimitedAccessFeatureService> lafService = do_GetService("@mozilla.org/limited-access-feature-service;1"); nsAutoCString pinFeatureId; nsresult rv = lafService->GetTaskbarPinFeatureId(pinFeatureId); if (NS_WARN_IF(NS_FAILED(rv))) { return {E_FAIL, Win11PinToTaskBarResultStatus::NotSupported}; } nsCOMPtr<nsILimitedAccessFeature> feature; rv = lafService->GenerateLimitedAccessFeature(pinFeatureId, getter_AddRefs(feature)); if (NS_WARN_IF(NS_FAILED(rv))) { return {E_FAIL, Win11PinToTaskBarResultStatus::NotSupported}; } bool unlocked = false; rv = feature->Unlock(&unlocked); if (NS_WARN_IF(NS_FAILED(rv))) { return {E_FAIL, Win11PinToTaskBarResultStatus::NotSupported}; static Win11PinToTaskBarResult UnlockLimitedAccessFeature( Win11LimitedAccessFeatureType featureType) { RefPtr<Win11LimitedAccessFeaturesInterface> limitedAccessFeatures = CreateWin11LimitedAccessFeaturesInterface(); auto result = limitedAccessFeatures->Unlock(featureType); if (result.isErr()) { auto hr = result.unwrapErr(); TASKBAR_PINNING_LOG(LogLevel::Debug, "Taskbar unlock: Error. HRESULT = 0x%lx", hr); return {hr, Win11PinToTaskBarResultStatus::NotSupported}; } if (!unlocked) { if (result.unwrap() == false) { TASKBAR_PINNING_LOG( LogLevel::Debug, "Taskbar unlock: failed. Not supported on this version of Windows."); return {S_OK, Win11PinToTaskBarResultStatus::NotSupported}; } return {S_OK, Win11PinToTaskBarResultStatus::Success}; } Loading Loading @@ -199,7 +184,8 @@ Win11PinToTaskBarResult PinCurrentAppToTaskbarWin11( "thread only. It blocks, waiting on things to execute " "asynchronously on the main thread."); Win11PinToTaskBarResult unlockStatus = UnlockTaskbarPinFeature(); Win11PinToTaskBarResult unlockStatus = UnlockLimitedAccessFeature(Win11LimitedAccessFeatureType::Taskbar); if (unlockStatus.result != Win11PinToTaskBarResultStatus::Success) { // Limited Access Feature no longer necessary for Windows 11 26200 Build // 7840, and possibly other channels. Loading Loading @@ -428,7 +414,8 @@ Win11PinToTaskBarResult IsCurrentAppPinnedToTaskbarWin11(bool aCheckOnly) { "thread only. It blocks, waiting on things to execute " "asynchronously on the main thread."); Win11PinToTaskBarResult unlockStatus = UnlockTaskbarPinFeature(); Win11PinToTaskBarResult unlockStatus = UnlockLimitedAccessFeature(Win11LimitedAccessFeatureType::Taskbar); if (unlockStatus.result != Win11PinToTaskBarResultStatus::Success) { // Limited Access Feature no longer necessary for Windows 11 26200 Build // 7840, and possibly other channels. Loading Loading
Cargo.lock +0 −14 Changes for Cargo.lock: 0 added lines, 14 removed lines. Original line number Diff line number Diff line Loading @@ -2629,7 +2629,6 @@ dependencies = [ "rure", "rusqlite 0.33.0", "rust_minidump_writer_linux", "shell_windows", "signature_cache", "static_prefs", "storage", Loading Loading @@ -6153,19 +6152,6 @@ dependencies = [ "keccak", ] [[package]] name = "shell_windows" version = "0.1.0" dependencies = [ "base64 0.22.1", "log", "nserror", "nsstring", "sha2", "windows", "xpcom", ] [[package]] name = "shlex" version = "1.3.0" Loading
browser/components/build/components.conf +0 −6 Changes for browser/components/build/components.conf: 0 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -46,10 +46,4 @@ elif buildconfig.substs['OS_ARCH'] == 'WINNT': 'type': 'nsIEHistoryEnumerator', 'headers': ['/browser/components/migration/nsIEHistoryEnumerator.h'], }, { 'cid': '{115f4862-f302-42a2-a58c-d09f636ab601}', 'contract_ids': ['@mozilla.org/limited-access-feature-service;1'], 'headers': ['/browser/components/shell/rust/shell_windows/src/LimitedAccessFeatureService.h'], 'legacy_constructor': 'new_limited_access_feature_service', }, ]
browser/components/shell/Windows11LimitedAccessFeatures.cpp 0 → 100644 +370 −0 Changes for browser/components/shell/Windows11LimitedAccessFeatures.cpp: 370 added lines, 0 removed lines. Original line number Diff line number Diff line /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * This file exists so that LaunchModernSettingsDialogDefaultApps can be called * without linking to libxul. */ #include "Windows11LimitedAccessFeatures.h" #include "mozilla/Logging.h" static mozilla::LazyLogModule sLog("Windows11LimitedAccessFeatures"); #define LAF_LOG(level, msg, ...) MOZ_LOG(sLog, level, (msg, ##__VA_ARGS__)) // MINGW32 is not supported for these features // Fall back function defined in the #else #ifndef __MINGW32__ # include "mozilla/ErrorResult.h" # include "nsString.h" # include "nsCOMPtr.h" # include "nsComponentManagerUtils.h" # include "nsIWindowsRegKey.h" # include "nsWindowsHelpers.h" # include "nsICryptoHash.h" # include "mozilla/Atomics.h" # include "mozilla/Base64.h" # include "mozilla/Char16.h" # include "mozilla/WinHeaderOnlyUtils.h" # include "WinUtils.h" # include <wrl.h> # include <inspectable.h> # include <roapi.h> # include <windows.services.store.h> # include <windows.foundation.h> using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::ApplicationModel; using namespace mozilla; /** * To unlock features, we need: * a feature identifier * a token, * an attestation string * a token * * The token is generated by Microsoft and must * match the publisher id Microsoft thinks we have, for a particular * feature. * * To get a token, find the right microsoft email address by doing * a search on the web for the feature you want unlocked and reach * out to the right people at Microsoft. * * The token is generated from Microsoft. * The jumbled code in the attestation string is a publisher id and * must match the code in the resources / .rc file for the identity, * looking like this for non-MSIX builds: * * Identity LimitedAccessFeature {{ L"MozillaFirefox_pcsmm0jrprpb2" }} * * Broken down: * Identity LimitedAccessFeature {{ L"PRODUCTNAME_PUBLISHERID" }} * * That is injected into our build in create_rc.py and is necessary * to unlock the taskbar pinning feature / APIs from an unpackaged * build. * * In the above, the token is generated from the publisher id (pcsmm0jrprpb2) * and the product name (MozillaFirefox) * * All tokens listed here were provided to us by Microsoft. * * Below and in create_rc.py, we used this set: * * Token: "kRFiWpEK5uS6PMJZKmR7MQ==" * Product Name: "MozillaFirefox" * Publisher ID: "pcsmm0jrprpb2" * * Microsoft also provided these other tokens, which will will * work if accompanied by the matching changes to create_rc.py: * ----- * Token: "RGEhsYgKhmPLKyzkEHnMhQ==" * Product Name: "FirefoxBeta" * Publisher ID: "pcsmm0jrprpb2" * * ----- * * Token: "qbVzns/9kT+t15YbIwT4Jw==" * Product Name: "FirefoxNightly" * Publisher ID: "pcsmm0jrprpb2" * * To use those instead, you have to ensure that the LimitedAccessFeature * generated in create_rc.py has the product name and publisher id * matching the token used in this file. * * For non-packaged (non-MSIX) builds, any of the above sets will work. * Just make sure the right (ProductName_PublisherID) value is in the * generated resource data for the executable, and the matching * (Token) and attestation string * * To get MSIX/packaged builds to work, the product name and publisher in * the final manifest (searchfox.org/mozilla-central/search?q=APPX_PUBLISHER) * should match the token in this file. For that case, the identity value * in the resources does not matter. * * See here for Microsoft examples: https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/TaskbarManager/CppUnpackagedDesktopTaskbarPin */ /** * Unlocks a Windows Limited Access Feature (LAF) by generating a token and * attestation. * * This function first retrieves the LAF key from the registry using * the LAF identifier and then combines the lafId, lafKey, and PFN * into a token. * * Applying Base64(SHA256Encode("<lafId>!<lafKey>!<PFN>")[0..16]) yields the * complete LAF token for unlocking. * * Taking the last 13 characters of the PFN yields the publisher identifier * which is used in the following boilerplate: * "<PFN[-13]> has registered their use of <lafId> with Microsoft and * agrees to the terms of use." * * @return {LimitedAccessFeatureInfo} containing the generated * token and attestation upon success. Contains empty strings for * these fields upon failure. */ static mozilla::Result<LimitedAccessFeatureInfo, nsresult> GenerateLimitedAccessFeatureInfo(const nsCString& debugName, const nsString& lafId) { nsresult rv; // Read registry key for a given Limited Access Feature with ID lafId. nsAutoString keyData; nsCOMPtr<nsIWindowsRegKey> regKey = do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); NS_ENSURE_SUCCESS(rv, Err(rv)); const nsAutoString regPath = u"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModel\\LimitedAccessFeatures\\"_ns + lafId; rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, regPath, nsIWindowsRegKey::ACCESS_READ); NS_ENSURE_SUCCESS(rv, Err(rv)); rv = regKey->ReadStringValue(u""_ns, keyData); NS_ENSURE_SUCCESS(rv, Err(rv)); // Get Package Family Name (PFN) and assemble a string to hash. // First convert this string to SHA256, take the first 16 bytes // only, and then read as base 64. nsAutoCString hashStringResult; nsAutoString encodedToken; // The non-MSIX family name must match whatever value is in create_rc.py // Currently this is MozillaFirefox_pcsmm0jrprpb2 nsAutoString familyName; if (widget::WinUtils::HasPackageIdentity()) { familyName = nsDependentString(mozilla::GetPackageFamilyName().get()); } else { familyName = u"MozillaFirefox_pcsmm0jrprpb2"_ns; } const nsAutoCString hashString = NS_ConvertUTF16toUTF8(lafId + u"!"_ns + keyData + u"!"_ns + familyName); nsCOMPtr<nsICryptoHash> cryptoHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv); NS_ENSURE_SUCCESS(rv, Err(rv)); rv = cryptoHash->Init(nsICryptoHash::SHA256); NS_ENSURE_SUCCESS(rv, Err(rv)); rv = cryptoHash->Update(reinterpret_cast<const uint8_t*>(hashString.get()), hashString.Length()); NS_ENSURE_SUCCESS(rv, Err(rv)); rv = cryptoHash->Finish(false, hashStringResult); NS_ENSURE_SUCCESS(rv, Err(rv)); // Keep only first 16 bytes and encode hashStringResult.Truncate(hashStringResult.Length() - 16); rv = Base64Encode(hashStringResult, encodedToken); NS_ENSURE_SUCCESS(rv, Err(rv)); // The PFN contains a package ID in the last 13 characters. // This ID is based on the value in the publisher field of the // AppManifest. This ID is used to assemble the attestation. familyName.Cut(0, familyName.Length() - 13); nsAutoString attestation = familyName + u" has registered their use of "_ns + lafId + u" with Microsoft and agrees to the terms of use."_ns; LimitedAccessFeatureInfo result = {debugName, lafId, encodedToken, attestation}; return result; } /** Implementation of the Win11LimitedAccessFeaturesInterface. */ class Win11LimitedAccessFeatures : public Win11LimitedAccessFeaturesInterface { public: using AtomicState = Atomic<int, SequentiallyConsistent>; Result<bool, HRESULT> Unlock(Win11LimitedAccessFeatureType feature) override; private: AtomicState& GetState(Win11LimitedAccessFeatureType feature); Result<bool, HRESULT> UnlockImplementation( const LimitedAccessFeatureInfo& lafInfo); /** * Store the state as an atomic so that it can be safely accessed from * different threads. */ static AtomicState mTaskbarState; static AtomicState mDefaultState; enum State { Uninitialized, Locked, Unlocked, }; }; Win11LimitedAccessFeatures::AtomicState Win11LimitedAccessFeatures::mTaskbarState( Win11LimitedAccessFeatures::Uninitialized); Win11LimitedAccessFeatures::AtomicState Win11LimitedAccessFeatures::mDefaultState( Win11LimitedAccessFeatures::Uninitialized); RefPtr<Win11LimitedAccessFeaturesInterface> CreateWin11LimitedAccessFeaturesInterface() { RefPtr<Win11LimitedAccessFeaturesInterface> result( new Win11LimitedAccessFeatures()); return result; } Result<bool, HRESULT> Win11LimitedAccessFeatures::Unlock( Win11LimitedAccessFeatureType feature) { AtomicState& atomicState = GetState(feature); // Win11LimitedAccessFeatureType::Taskbar auto taskbarLafInfo = GenerateLimitedAccessFeatureInfo( "Win11LimitedAccessFeatureType::Taskbar"_ns, u"com.microsoft.windows.taskbar.pin"_ns); if (taskbarLafInfo.isErr()) { LAF_LOG(LogLevel::Debug, "Unlocking taskbar failed with error %d", NS_ERROR_GET_CODE(taskbarLafInfo.unwrapErr())); return Err(E_FAIL); } LimitedAccessFeatureInfo limitedAccessFeatureInfo[] = { taskbarLafInfo.unwrap()}; const auto& lafInfo = limitedAccessFeatureInfo[static_cast<int>(feature)]; LAF_LOG(LogLevel::Debug, "Limited Access Feature Info for %s. Feature %ls, %ls, %ls", lafInfo.debugName.get(), lafInfo.token.getW(), lafInfo.token.getW(), lafInfo.attestation.getW()); int state = atomicState; if (state != Uninitialized) { LAF_LOG(LogLevel::Debug, "%s already initialized! State = %s", lafInfo.debugName.get(), (state == Unlocked) ? "true" : "false"); return (state == Unlocked); } // If multiple threads read the state at the same time, and it's unitialized, // both threads will unlock the feature. This situation is unlikely, but even // if it happens, it's not a problem. auto result = UnlockImplementation(lafInfo); int newState = Locked; if (!result.isErr() && result.unwrap()) { newState = Unlocked; } atomicState = newState; return result; } Win11LimitedAccessFeatures::AtomicState& Win11LimitedAccessFeatures::GetState( Win11LimitedAccessFeatureType feature) { switch (feature) { case Win11LimitedAccessFeatureType::Taskbar: return mTaskbarState; default: LAF_LOG(LogLevel::Debug, "Missing feature type for %d", static_cast<int>(feature)); MOZ_ASSERT(false, "Unhandled feature type! Add a new atomic state variable, add " "that entry to the switch statement above, and add the proper " "entries for the feature and the token."); return mDefaultState; } } Result<bool, HRESULT> Win11LimitedAccessFeatures::UnlockImplementation( const LimitedAccessFeatureInfo& lafInfo) { ComPtr<ILimitedAccessFeaturesStatics> limitedAccessFeatures; ComPtr<ILimitedAccessFeatureRequestResult> limitedAccessFeaturesResult; HRESULT hr = RoGetActivationFactory( HStringReference( RuntimeClass_Windows_ApplicationModel_LimitedAccessFeatures) .Get(), IID_ILimitedAccessFeaturesStatics, &limitedAccessFeatures); if (!SUCCEEDED(hr)) { LAF_LOG(LogLevel::Debug, "%s activation error. HRESULT = 0x%lx", lafInfo.debugName.get(), hr); return Err(hr); } hr = limitedAccessFeatures->TryUnlockFeature( HStringReference(lafInfo.feature.get()).Get(), HStringReference(lafInfo.token.get()).Get(), HStringReference(lafInfo.attestation.get()).Get(), &limitedAccessFeaturesResult); if (!SUCCEEDED(hr)) { LAF_LOG(LogLevel::Debug, "%s unlock error. HRESULT = 0x%lx", lafInfo.debugName.get(), hr); return Err(hr); } LimitedAccessFeatureStatus status; hr = limitedAccessFeaturesResult->get_Status(&status); if (!SUCCEEDED(hr)) { LAF_LOG(LogLevel::Debug, "%s get status error. HRESULT = 0x%lx", lafInfo.debugName.get(), hr); return Err(hr); } int state = Unlocked; if ((status != LimitedAccessFeatureStatus_Available) && (status != LimitedAccessFeatureStatus_AvailableWithoutToken)) { LAF_LOG(LogLevel::Debug, "%s not available. HRESULT = 0x%lx", lafInfo.debugName.get(), hr); state = Locked; } return (state == Unlocked); } #else // MINGW32 implementation RefPtr<Win11LimitedAccessFeaturesInterface> CreateWin11LimitedAccessFeaturesInterface() { RefPtr<Win11LimitedAccessFeaturesInterface> result; return result; } static mozilla::Result<LimitedAccessFeatureInfo, nsresult> GenerateLimitedAccessFeatureInfo(const nsCString& debugName, const nsString& lafId) { return mozilla::Err(NS_ERROR_NOT_IMPLEMENTED); } #endif
browser/components/shell/Windows11LimitedAccessFeatures.h 0 → 100644 +65 −0 Changes for browser/components/shell/Windows11LimitedAccessFeatures.h: 65 added lines, 0 removed lines. Original line number Diff line number Diff line /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef SHELL_WINDOWS11LIMITEDACCESSFEATURES_H__ #define SHELL_WINDOWS11LIMITEDACCESSFEATURES_H__ #include "nsISupportsImpl.h" #include "nsString.h" #include "mozilla/Result.h" #include "mozilla/ResultVariant.h" #include <winerror.h> #include <windows.h> // for HRESULT #include "mozilla/DefineEnum.h" #include <winerror.h> MOZ_DEFINE_ENUM_CLASS(Win11LimitedAccessFeatureType, (Taskbar)); /** * Class to manage unlocking limited access features on Windows 11. * Unless stubbing for testing purposes, create objects of this * class with CreateWin11LimitedAccessFeaturesInterface. * * Windows 11 requires certain features to be unlocked in order to work * (for instance, the Win11 Taskbar pinning APIs). Call Unlock() * to unlock them. Generally, results will be cached in atomic variables * and future calls to Unlock will be as long as it takes * to fetch an atomic variable. */ class Win11LimitedAccessFeaturesInterface { public: /** * Unlocks the limited access features, if possible. * * Returns an error code on error, true on successful unlock, * false on unlock failed (but with no error). */ virtual mozilla::Result<bool, HRESULT> Unlock( Win11LimitedAccessFeatureType feature) = 0; /** * Reference counting and cycle collection. */ NS_INLINE_DECL_REFCOUNTING(Win11LimitedAccessFeaturesInterface) protected: virtual ~Win11LimitedAccessFeaturesInterface() {} }; struct LimitedAccessFeatureInfo { const nsCString debugName; const nsString feature; const nsString token; const nsString attestation; }; RefPtr<Win11LimitedAccessFeaturesInterface> CreateWin11LimitedAccessFeaturesInterface(); mozilla::Result<LimitedAccessFeatureInfo, nsresult> GenerateLimitedAccessFeatureInfo(const nsCString& debugName, const nsString& lafId); #endif // SHELL_WINDOWS11LIMITEDACCESSFEATURES_H__
browser/components/shell/Windows11TaskbarPinning.cpp +16 −29 Changes for browser/components/shell/Windows11TaskbarPinning.cpp: 16 added lines, 29 removed lines. Original line number Diff line number Diff line Loading @@ -4,8 +4,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "Windows11TaskbarPinning.h" #include "Windows11LimitedAccessFeatures.h" #include "nsILimitedAccessFeature.h" #include "nsWindowsHelpers.h" #include "MainThreadUtils.h" #include "nsThreadUtils.h" Loading Loading @@ -70,39 +70,24 @@ using namespace ABI::Windows::UI::Shell; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::ApplicationModel; static Win11PinToTaskBarResult UnlockTaskbarPinFeature() { nsCOMPtr<nsILimitedAccessFeatureService> lafService = do_GetService("@mozilla.org/limited-access-feature-service;1"); nsAutoCString pinFeatureId; nsresult rv = lafService->GetTaskbarPinFeatureId(pinFeatureId); if (NS_WARN_IF(NS_FAILED(rv))) { return {E_FAIL, Win11PinToTaskBarResultStatus::NotSupported}; } nsCOMPtr<nsILimitedAccessFeature> feature; rv = lafService->GenerateLimitedAccessFeature(pinFeatureId, getter_AddRefs(feature)); if (NS_WARN_IF(NS_FAILED(rv))) { return {E_FAIL, Win11PinToTaskBarResultStatus::NotSupported}; } bool unlocked = false; rv = feature->Unlock(&unlocked); if (NS_WARN_IF(NS_FAILED(rv))) { return {E_FAIL, Win11PinToTaskBarResultStatus::NotSupported}; static Win11PinToTaskBarResult UnlockLimitedAccessFeature( Win11LimitedAccessFeatureType featureType) { RefPtr<Win11LimitedAccessFeaturesInterface> limitedAccessFeatures = CreateWin11LimitedAccessFeaturesInterface(); auto result = limitedAccessFeatures->Unlock(featureType); if (result.isErr()) { auto hr = result.unwrapErr(); TASKBAR_PINNING_LOG(LogLevel::Debug, "Taskbar unlock: Error. HRESULT = 0x%lx", hr); return {hr, Win11PinToTaskBarResultStatus::NotSupported}; } if (!unlocked) { if (result.unwrap() == false) { TASKBAR_PINNING_LOG( LogLevel::Debug, "Taskbar unlock: failed. Not supported on this version of Windows."); return {S_OK, Win11PinToTaskBarResultStatus::NotSupported}; } return {S_OK, Win11PinToTaskBarResultStatus::Success}; } Loading Loading @@ -199,7 +184,8 @@ Win11PinToTaskBarResult PinCurrentAppToTaskbarWin11( "thread only. It blocks, waiting on things to execute " "asynchronously on the main thread."); Win11PinToTaskBarResult unlockStatus = UnlockTaskbarPinFeature(); Win11PinToTaskBarResult unlockStatus = UnlockLimitedAccessFeature(Win11LimitedAccessFeatureType::Taskbar); if (unlockStatus.result != Win11PinToTaskBarResultStatus::Success) { // Limited Access Feature no longer necessary for Windows 11 26200 Build // 7840, and possibly other channels. Loading Loading @@ -428,7 +414,8 @@ Win11PinToTaskBarResult IsCurrentAppPinnedToTaskbarWin11(bool aCheckOnly) { "thread only. It blocks, waiting on things to execute " "asynchronously on the main thread."); Win11PinToTaskBarResult unlockStatus = UnlockTaskbarPinFeature(); Win11PinToTaskBarResult unlockStatus = UnlockLimitedAccessFeature(Win11LimitedAccessFeatureType::Taskbar); if (unlockStatus.result != Win11PinToTaskBarResultStatus::Success) { // Limited Access Feature no longer necessary for Windows 11 26200 Build // 7840, and possibly other channels. Loading