Commit da4b5b7d authored by David Parks's avatar David Parks Committed by Pier Angelo Vendrame
Browse files

Bug 2038868: Set Zone.Identifier for dragged URLs r=win-reviewers,gstoll

An (AFAICT undocumented) long-standing feature in Windows is the ability to
set the Zone.Identifier ADS on a file by writing to a file named
"${filename}:Zone.Identifier".  In DND, this patch exports this alongside
the ${filename} that will be used for the shortcut, so explorer writes both.
This causes the UAC to kick in when the shortcut is launched.

Differential Revision: https://phabricator.services.mozilla.com/D301935
parent a6e17cd0
Loading
Loading
Loading
Loading
+80 −8
Original line number Diff line number Diff line
@@ -1230,8 +1230,13 @@ nsDataObj ::GetFileDescriptorInternetShortcutA(FORMATETC& aFE,
  nsAutoString title;
  if (NS_FAILED(ExtractShortcutTitle(title))) return E_OUTOFMEMORY;

  // Allocate space for two FILEDESCRIPTOR entries: the .url file plus a
  // ":Zone.Identifier" ADS so the dropped shortcut is marked Internet-zone
  // (untrusted).
  size_t const allocSize =
      sizeof(FILEGROUPDESCRIPTORA) + sizeof(FILEDESCRIPTORA);
  HGLOBAL fileGroupDescHandle =
      ::GlobalAlloc(GMEM_ZEROINIT | GMEM_SHARE, sizeof(FILEGROUPDESCRIPTORA));
      ::GlobalAlloc(GMEM_ZEROINIT | GMEM_SHARE, allocSize);
  if (!fileGroupDescHandle) return E_OUTOFMEMORY;

  LPFILEGROUPDESCRIPTORA fileGroupDescA =
@@ -1252,11 +1257,24 @@ nsDataObj ::GetFileDescriptorInternetShortcutA(FORMATETC& aFE,
      strcpy(fileGroupDescA->fgd[0].cFileName, "Untitled.url");
    }
  }

  // one file in the file block
  fileGroupDescA->cItems = 1;
  fileGroupDescA->fgd[0].dwFlags = FD_LINKUI;

  // Build the ":Zone.Identifier" ADS entry.
  // If appending the suffix would overflow, refuse the entire descriptor.
  constexpr char kAdsSuffix[] = ":Zone.Identifier";
  constexpr size_t kAdsSuffixSize = sizeof(kAdsSuffix);  // includes terminator
  size_t const mainLen = strnlen(fileGroupDescA->fgd[0].cFileName, MAX_PATH);
  if (mainLen + kAdsSuffixSize > MAX_PATH) {
    ::GlobalUnlock(fileGroupDescHandle);
    ::GlobalFree(fileGroupDescHandle);
    return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  }
  memcpy(fileGroupDescA->fgd[1].cFileName, fileGroupDescA->fgd[0].cFileName,
         mainLen);
  memcpy(fileGroupDescA->fgd[1].cFileName + mainLen, kAdsSuffix,
         kAdsSuffixSize);
  fileGroupDescA->cItems = 2;

  ::GlobalUnlock(fileGroupDescHandle);
  aSTG.hGlobal = fileGroupDescHandle;
  aSTG.tymed = TYMED_HGLOBAL;
@@ -1271,8 +1289,13 @@ nsDataObj ::GetFileDescriptorInternetShortcutW(FORMATETC& aFE,
  nsAutoString title;
  if (NS_FAILED(ExtractShortcutTitle(title))) return E_OUTOFMEMORY;

  // Allocate space for two FILEDESCRIPTOR entries: the .url file plus a
  // ":Zone.Identifier" ADS so the dropped shortcut is marked Internet-zone
  // (untrusted).
  size_t const allocSize =
      sizeof(FILEGROUPDESCRIPTORW) + sizeof(FILEDESCRIPTORW);
  HGLOBAL fileGroupDescHandle =
      ::GlobalAlloc(GMEM_ZEROINIT | GMEM_SHARE, sizeof(FILEGROUPDESCRIPTORW));
      ::GlobalAlloc(GMEM_ZEROINIT | GMEM_SHARE, allocSize);
  if (!fileGroupDescHandle) return E_OUTOFMEMORY;

  LPFILEGROUPDESCRIPTORW fileGroupDescW =
@@ -1293,11 +1316,25 @@ nsDataObj ::GetFileDescriptorInternetShortcutW(FORMATETC& aFE,
      wcscpy(fileGroupDescW->fgd[0].cFileName, L"Untitled.url");
    }
  }

  // one file in the file block
  fileGroupDescW->cItems = 1;
  fileGroupDescW->fgd[0].dwFlags = FD_LINKUI;

  // Build the ":Zone.Identifier" ADS entry.
  // If appending the suffix would overflow, refuse the entire descriptor.
  constexpr WCHAR kAdsSuffix[] = L":Zone.Identifier";
  constexpr size_t kAdsSuffixLen =
      (sizeof(kAdsSuffix) / sizeof(WCHAR));  // includes terminator
  size_t const mainLen = wcsnlen(fileGroupDescW->fgd[0].cFileName, MAX_PATH);
  if (mainLen + kAdsSuffixLen > MAX_PATH) {
    ::GlobalUnlock(fileGroupDescHandle);
    ::GlobalFree(fileGroupDescHandle);
    return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  }
  wmemcpy(fileGroupDescW->fgd[1].cFileName, fileGroupDescW->fgd[0].cFileName,
          mainLen);
  wmemcpy(fileGroupDescW->fgd[1].cFileName + mainLen, kAdsSuffix,
          kAdsSuffixLen);
  fileGroupDescW->cItems = 2;

  ::GlobalUnlock(fileGroupDescHandle);
  aSTG.hGlobal = fileGroupDescHandle;
  aSTG.tymed = TYMED_HGLOBAL;
@@ -1313,6 +1350,41 @@ nsDataObj ::GetFileDescriptorInternetShortcutW(FORMATETC& aFE,
//
HRESULT
nsDataObj ::GetFileContentsInternetShortcut(FORMATETC& aFE, STGMEDIUM& aSTG) {
  // The descriptor advertises two entries: the .url content (lindex 0) and
  // the ":Zone.Identifier" ADS that marks it as Internet-zone (lindex 1).
  if (aFE.lindex == 1) {
    constexpr char kZoneIdContent[] = "[ZoneTransfer]\r\nZoneId=3\r\n";
    constexpr size_t kZoneIdLen = sizeof(kZoneIdContent) - 1;

    nsAutoGlobalMem globalMem(nsHGLOBAL(::GlobalAlloc(GMEM_SHARE, kZoneIdLen)));
    if (!globalMem) {
      return E_OUTOFMEMORY;
    }
    char* contents = reinterpret_cast<char*>(::GlobalLock(globalMem.get()));
    if (!contents) {
      return E_OUTOFMEMORY;
    }
    memcpy(contents, kZoneIdContent, kZoneIdLen);
    ::GlobalUnlock(globalMem.get());

    if (aFE.tymed & TYMED_ISTREAM) {
      RefPtr<IStream> stream = new CMemStream(
          globalMem.disown(), kZoneIdLen, already_AddRefed<AutoCloseEvent>());
      stream.forget(&aSTG.pstm);
      aSTG.tymed = TYMED_ISTREAM;
    } else {
      aSTG.hGlobal = globalMem.disown();
      aSTG.tymed = TYMED_HGLOBAL;
    }
    return S_OK;
  }

  // Treat aFE.lindex = 0 or -1 as requests for the URL file.  Anything else is
  // invalid.
  if (aFE.lindex != 0 && aFE.lindex != -1) {
    return DV_E_LINDEX;
  }

  static const char* kShellIconPref = "browser.shell.shortcutFavicons";
  nsAutoString url;
  if (NS_FAILED(ExtractShortcutURL(url))) return E_OUTOFMEMORY;