Bug 831614 - Widget code for storing sync credentials. r=jimm
authorBrian R. Bondy <netzen@gmail.com>
Fri, 15 Mar 2013 10:29:59 -0400
changeset 124935 b855b1ac3f1435734f8917c281aa826b396dcd4c
parent 124934 a63b7a4c03f895b4d1772fd051bbf64e396c0a0c
child 124936 19eed0fb290e38b673a9d502fa8082fc909fbc6a
push id24687
push userbbondy@mozilla.com
push dateFri, 15 Mar 2013 14:34:31 +0000
treeherdermozilla-inbound@c75172789a3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs831614
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 831614 - Widget code for storing sync credentials. r=jimm
widget/nsIWinMetroUtils.idl
widget/windows/winrt/nsWinMetroUtils.cpp
--- a/widget/nsIWinMetroUtils.idl
+++ b/widget/nsIWinMetroUtils.idl
@@ -7,17 +7,17 @@
 
 /**
  * Integration with the "Metro"/"Modern" UI environment in Windows 8.
  *
  * Note: browser/metro/base/content/browser-scripts.js contains a stub
  * implementation of this interface for non-Windows systems, for testing and
  * development purposes only.
  */
-[scriptable, uuid(b2452440-71c6-41a7-8eda-48004d725001)]
+[scriptable, uuid(774a4b83-27c3-4F62-8509-efa0dc923a03)]
 interface nsIWinMetroUtils : nsISupports
 {
   /* Fullscreen landscape orientation */
   const long fullScreenLandscape = 0;
   /* Larger snapped state */
   const long filled = 1;
   /* Smaller snapped state */
   const long snapped = 2;
@@ -96,16 +96,39 @@ interface nsIWinMetroUtils : nsISupports
    * Determines if a tile is pinned to the Windows 8 start screen.
    * 
    * @param aTileID An ID which may have been pinned with pinTileAsync
    * @return true if the tile is pinned
    */
   bool isTilePinned(in AString aTileID);
 
   /**
+   * Stores the sync info securely
+   *
+   * @param aEmail The sync account email
+   * @param aPassword The sync account password
+   * @param aKey The sync account key
+   */
+  void storeSyncInfo(in AString aEmail, in AString aPassword, in AString aKey);
+
+  /**
+   * Loads the sync info
+   *
+   * @param aEmail The sync account email
+   * @param aPassword The sync account password
+   * @param aKey The sync account key
+   */
+  void loadSyncInfo(out AString aEmail, out AString aPassword, out AString aKey);
+
+  /**
+   * Clears the stored sync info if any.
+   */
+  void clearSyncInfo();
+
+  /**
    * Soft keyboard attributes. Used in unison with shown/hidden observer
    * events sent via FrameworkView.
    *
    * keyboardVisible - returns true if the soft keyboard is currently
    * displayed, false otherwise.
    * keyboardX, keyboardY, keyboardWidth, keyboardHeight - occlude rect
    * of the keyboard when displayed in device independent pixels.
    */
--- a/widget/windows/winrt/nsWinMetroUtils.cpp
+++ b/widget/windows/winrt/nsWinMetroUtils.cpp
@@ -3,21 +3,23 @@
  * 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/. */
 
 #include "nsWinMetroUtils.h"
 #include "MetroUtils.h"
 #include "nsXULAppAPI.h"
 #include "FrameworkView.h"
 #include "MetroApp.h"
+#include "nsIWindowsRegKey.h"
 
 #include <shldisp.h>
 #include <shellapi.h>
 #include <windows.ui.viewmanagement.h>
 #include <windows.ui.startscreen.h>
+#include <Wincrypt.h>
 
 using namespace ABI::Windows::Foundation;
 using namespace ABI::Windows::UI::StartScreen;
 using namespace ABI::Windows::UI::ViewManagement;
 using namespace Microsoft::WRL;
 using namespace Microsoft::WRL::Wrappers;
 using namespace mozilla::widget::winrt;
 
@@ -26,33 +28,38 @@ namespace widget {
 namespace winrt {
 extern ComPtr<MetroApp> sMetroApp;
 extern nsTArray<nsString>* sSettingsArray;
 } } }
 
 namespace mozilla {
 namespace widget {
 
+static LPCWSTR sSyncEmailField = L"sync-e";
+static LPCWSTR sSyncPasswordField = L"sync-p";
+static LPCWSTR sSyncKeyField = L"sync-k";
+static LPCSTR sRegPath = "Software\\Mozilla\\Firefox";
+
 NS_IMPL_ISUPPORTS1(nsWinMetroUtils, nsIWinMetroUtils)
 
 nsWinMetroUtils::nsWinMetroUtils()
 {
 }
 
 nsWinMetroUtils::~nsWinMetroUtils()
 {
 }
 
 /**
  * Pins a new tile to the Windows 8 start screen.
- * 
+ *
  * @param aTileID         An ID which can later be used to remove the tile
  * @param aShortName      A short name for the tile
  * @param aDiplayName     The name that will be displayed on the tile
- * @param aActivationArgs The arguments to pass to the browser upon 
+ * @param aActivationArgs The arguments to pass to the browser upon
  *                        activation of the tile
  * @param aTileImage An image for the normal tile view
  * @param aSmallTileImage An image for the small tile view
  */
 NS_IMETHODIMP
 nsWinMetroUtils::PinTileAsync(const nsAString &aTileID,
                               const nsAString &aShortName,
                               const nsAString &aDisplayName,
@@ -102,17 +109,17 @@ nsWinMetroUtils::PinTileAsync(const nsAS
   ComPtr<IAsyncOperation<bool>> operation;
   AssertRetHRESULT(secondaryTile->RequestCreateAsync(operation.GetAddressOf()), NS_ERROR_FAILURE);
   operation->put_Completed(callback.Get());
   return NS_OK;
 }
 
 /**
  * Unpins a tile from the Windows 8 start screen.
- * 
+ *
  * @param aTileID An existing ID which was previously pinned
  */
 NS_IMETHODIMP
 nsWinMetroUtils::UnpinTileAsync(const nsAString &aTileID)
 {
   if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
     NS_WARNING("UnpinTileAsync can't be called on the desktop.");
     return NS_ERROR_FAILURE;
@@ -135,17 +142,17 @@ nsWinMetroUtils::UnpinTileAsync(const ns
   ComPtr<IAsyncOperation<bool>> operation;
   AssertRetHRESULT(secondaryTile->RequestDeleteAsync(operation.GetAddressOf()), NS_ERROR_FAILURE);
   operation->put_Completed(callback.Get());
   return NS_OK;
 }
 
 /**
  * Determines if a tile is pinned to the Windows 8 start screen.
- * 
+ *
  * @param aTileID   An ID which may have been pinned with pinTileAsync
  * @param aIsPinned Out parameter for determining if the tile is pinned or not
  */
 NS_IMETHODIMP
 nsWinMetroUtils::IsTilePinned(const nsAString &aTileID, bool *aIsPinned)
 {
   if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
     NS_WARNING("IsTilePinned can't be called on the desktop.");
@@ -163,27 +170,168 @@ nsWinMetroUtils::IsTilePinned(const nsAS
   AssertRetHRESULT(hr, NS_ERROR_FAILURE);
   boolean result = false;
   tileStatics->Exists(tileIdStr.Get(), &result);
   *aIsPinned = result;
   return NS_OK;
 }
 
 /**
+  * Stores the sync info securely in Windows
+  *
+  * @param aEmail The sync account email
+  * @param aPassword The sync account password
+  * @param aKey The sync account key
+  */
+NS_IMETHODIMP
+nsWinMetroUtils::StoreSyncInfo(const nsAString &aEmail,
+                               const nsAString &aPassword,
+                               const nsAString &aKey)
+{
+  DATA_BLOB emailIn = {
+    (aEmail.Length() + 1) * 2,
+    (BYTE *)aEmail.BeginReading()},
+  passwordIn = {
+    (aPassword.Length() + 1) * 2,
+    (BYTE *)aPassword.BeginReading()},
+  keyIn = {
+    (aKey.Length() + 1) * 2,
+    (BYTE *)aKey.BeginReading()};
+  DATA_BLOB emailOut = { 0, nullptr }, passwordOut = {0, nullptr }, keyOut = { 0, nullptr };
+  bool succeeded = CryptProtectData(&emailIn, nullptr, nullptr, nullptr,
+                                    nullptr, 0, &emailOut) &&
+                   CryptProtectData(&passwordIn, nullptr, nullptr, nullptr,
+                                    nullptr, 0, &passwordOut) &&
+                   CryptProtectData(&keyIn, nullptr, nullptr, nullptr,
+                                    nullptr, 0, &keyOut);
+
+  if (succeeded) {
+    nsresult rv;
+    nsCOMPtr<nsIWindowsRegKey> regKey
+      (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
+    NS_ENSURE_SUCCESS(rv, rv);
+    regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+                  NS_ConvertUTF8toUTF16(sRegPath),
+                  nsIWindowsRegKey::ACCESS_SET_VALUE);
+
+    if (NS_FAILED(regKey->WriteBinaryValue(nsDependentString(sSyncEmailField),
+                                           nsAutoCString((const char *)emailOut.pbData,
+                                                         emailOut.cbData)))) {
+      succeeded = false;
+    }
+
+    if (succeeded &&
+        NS_FAILED(regKey->WriteBinaryValue(nsDependentString(sSyncPasswordField),
+                                           nsAutoCString((const char *)passwordOut.pbData,
+                                                         passwordOut.cbData)))) {
+      succeeded = false;
+    }
+
+    if (succeeded &&
+        NS_FAILED(regKey->WriteBinaryValue(nsDependentString(sSyncKeyField),
+                                           nsAutoCString((const char *)keyOut.pbData,
+                                                         keyOut.cbData)))) {
+      succeeded = false;
+    }
+    regKey->Close();
+  }
+
+  LocalFree(emailOut.pbData);
+  LocalFree(passwordOut.pbData);
+  LocalFree(keyOut.pbData);
+
+  return succeeded ? NS_OK : NS_ERROR_FAILURE;
+}
+
+/**
+  * Loads the sync info securely in Windows
+  *
+  * @param aEmail The sync account email
+  * @param aPassword The sync account password
+  * @param aKey The sync account key
+  */
+NS_IMETHODIMP
+nsWinMetroUtils::LoadSyncInfo(nsAString &aEmail, nsAString &aPassword,
+                              nsAString &aKey)
+{
+  nsresult rv;
+  nsCOMPtr<nsIWindowsRegKey> regKey
+    (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+  regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+                 NS_ConvertUTF8toUTF16(sRegPath),
+                 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+
+  nsAutoCString email, password, key;
+  if (NS_FAILED(regKey->ReadBinaryValue(nsDependentString(sSyncEmailField), email)) ||
+      NS_FAILED(regKey->ReadBinaryValue(nsDependentString(sSyncPasswordField), password)) ||
+      NS_FAILED(regKey->ReadBinaryValue(nsDependentString(sSyncKeyField), key))) {
+    return NS_ERROR_FAILURE;
+  }
+  regKey->Close();
+
+  DATA_BLOB emailIn = { email.Length(), (BYTE*)email.BeginReading() },
+            passwordIn = { password.Length(), (BYTE*)password.BeginReading() },
+            keyIn = { key.Length(), (BYTE*)key.BeginReading() };
+  DATA_BLOB emailOut = { 0, nullptr }, passwordOut = { 0, nullptr }, keyOut = { 0, nullptr };
+  bool succeeded = CryptUnprotectData(&emailIn, nullptr, nullptr, nullptr,
+                                      nullptr, 0, &emailOut) &&
+                   CryptUnprotectData(&passwordIn, nullptr, nullptr, nullptr,
+                                      nullptr, 0, &passwordOut) &&
+                   CryptUnprotectData(&keyIn, nullptr, nullptr, nullptr,
+                                      nullptr, 0, &keyOut);
+  if (succeeded) {
+    aEmail = reinterpret_cast<wchar_t*>(emailOut.pbData);
+    aPassword = reinterpret_cast<wchar_t*>(passwordOut.pbData);
+    aKey = reinterpret_cast<wchar_t*>(keyOut.pbData);
+  }
+
+  LocalFree(emailOut.pbData);
+  LocalFree(passwordOut.pbData);
+  LocalFree(keyOut.pbData);
+
+  return succeeded ? NS_OK : NS_ERROR_FAILURE;
+}
+
+/**
+  * Clears the stored sync info if any.
+  */
+NS_IMETHODIMP
+nsWinMetroUtils::ClearSyncInfo()
+{
+  nsresult rv;
+  nsCOMPtr<nsIWindowsRegKey> regKey
+    (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+  regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+                 NS_ConvertUTF8toUTF16(sRegPath),
+                 nsIWindowsRegKey::ACCESS_WRITE);
+  nsresult rv1 = regKey->RemoveValue(nsDependentString(sSyncEmailField));
+  nsresult rv2 = regKey->RemoveValue(nsDependentString(sSyncPasswordField));
+  nsresult rv3 = regKey->RemoveValue(nsDependentString(sSyncKeyField));
+  regKey->Close();
+
+  if (NS_FAILED(rv1) || NS_FAILED(rv2) || NS_FAILED(rv3)) {
+      return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+/**
  * Launches the specified application with the specified arguments and
  * switches to Desktop mode if in metro mode.
 */
 NS_IMETHODIMP
 nsWinMetroUtils::LaunchInDesktop(const nsAString &aPath, const nsAString &aArguments)
 {
   SHELLEXECUTEINFOW sinfo;
   memset(&sinfo, 0, sizeof(SHELLEXECUTEINFOW));
   sinfo.cbSize       = sizeof(SHELLEXECUTEINFOW);
   // Per the Metro style enabled desktop browser, for some reason,
-  // SEE_MASK_FLAG_LOG_USAGE is needed to change from immersive mode 
+  // SEE_MASK_FLAG_LOG_USAGE is needed to change from immersive mode
   // to desktop.
   sinfo.fMask        = SEE_MASK_FLAG_LOG_USAGE;
   sinfo.hwnd         = NULL;
   sinfo.lpFile       = aPath.BeginReading();
   sinfo.lpParameters = aArguments.BeginReading();
   sinfo.lpVerb       = L"open";
   sinfo.nShow        = SW_SHOWNORMAL;
 
@@ -230,17 +378,17 @@ nsWinMetroUtils::ShowSettingsFlyout()
 
   HRESULT hr = MetroUtils::ShowSettingsFlyout();
   return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsWinMetroUtils::GetImmersive(bool *aImersive)
 {
-  *aImersive = 
+  *aImersive =
     XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWinMetroUtils::GetHandPreference(int32_t *aHandPreference)
 {
   if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
@@ -248,17 +396,17 @@ nsWinMetroUtils::GetHandPreference(int32
     return NS_OK;
   }
 
   ComPtr<IUISettings> uiSettings;
   AssertRetHRESULT(ActivateGenericInstance(RuntimeClass_Windows_UI_ViewManagement_UISettings, uiSettings), NS_ERROR_UNEXPECTED);
 
   HandPreference value;
   uiSettings->get_HandPreference(&value);
-  if (value == HandPreference::HandPreference_LeftHanded) 
+  if (value == HandPreference::HandPreference_LeftHanded)
     *aHandPreference = nsIWinMetroUtils::handPreferenceLeft;
   else
     *aHandPreference = nsIWinMetroUtils::handPreferenceRight;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP