Ticket #26540: 0001-Bug-26540-Enabling-pdfjs-disableRange-option-prevent(tor-browser).patch

File 0001-Bug-26540-Enabling-pdfjs-disableRange-option-prevent(tor-browser).patch, 16.1 KB (added by pospeselr, 12 months ago)
  • browser/extensions/pdfjs/content/PdfStreamConverter.jsm

    From faa5a43ceb2e28c1e34b254666f498c1234f4bf8 Mon Sep 17 00:00:00 2001
    From: Richard Pospesel <richard@torproject.org>
    Date: Thu, 19 Jul 2018 12:08:29 -0700
    Subject: [PATCH] Bug 26540: Enabling pdfjs disableRange option prevents pdfs
     from loading
    
    The temporary solution for #15599 was to disable range-based requests in
    pdfjs.  The range-based requests involved XMLHttpRequests individual PDF
    pages in chunks as needed for viewing, rather than downloading the
    entire pdf file to before viewing.  This created worse user experience,
    but first party isolation guarantees were respected.
    
    In ESR60, disabling range-based requests seems to prevent PDFs from ever
    loading, so an actual solution is required.
    
    This patch smuggles in the first-party domain on the
    nsIPrivateBrowsingChannel interface.  If the first-party domain normally
    read from the Channel->LoadInfo->OriginAttributes is blank, than
    torbutton will try to read it off of the Channel itself.  In pdfjs, the
    XMLHttpRequest's Channel gets a copy of the first-party domain string
    provided by the 'node principal'.
    
    We cannot update the Channel's LoadInfo->OriginAttributes directly,
    because internal logic performs checks ensuring that the LoadInfo
    matches that of the XMLHttpRequest's security principal.  In the case of
    XMLHttpRequest created in pdfjs,the security principal is the
    'System Principal' since it is technically created inside of firefox
    chrome code
    ---
     .../pdfjs/content/PdfStreamConverter.jsm      | 13 +++++++++++++
     netwerk/base/PrivateBrowsingChannel.h         | 14 ++++++++++++++
     netwerk/base/nsBaseChannel.cpp                | 12 +++++++++---
     netwerk/base/nsIPrivateBrowsingChannel.idl    | 19 +++++++++++++++++++
     netwerk/ipc/NeckoChannelParams.ipdlh          |  1 +
     netwerk/protocol/http/HttpChannelChild.cpp    |  1 +
     netwerk/protocol/http/HttpChannelParent.cpp   |  8 ++++++--
     netwerk/protocol/http/HttpChannelParent.h     |  3 ++-
     8 files changed, 65 insertions(+), 6 deletions(-)
    
    diff --git a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
    index a2ebec9450d4..7b2ffa6df689 100644
    a b class ChromeActions { 
    228228      startAt: Date.now(),
    229229    };
    230230  }
    231231
    232232  isInPrivateBrowsing() {
    233233    return PrivateBrowsingUtils.isContentWindowPrivate(this.domWindow);
    234234  }
    235235
     236  getFirstPartyDomain() {
     237    try {
     238      return this.domWindow.document.nodePrincipal.originAttributes.firstPartyDomain;
     239    } catch(err) {
     240      return "";
     241    }
     242  }
     243
    236244  download(data, sendResponse) {
    237245    var self = this;
    238246    var originalUrl = data.originalUrl;
    239247    var blobUrl = data.blobUrl || originalUrl;
    240248    // The data may not be downloaded so we need just retry getting the pdf with
    241249    // the original url.
    242250    var originalUri = NetUtil.newURI(originalUrl);
    243251    var filename = data.filename;
    class ChromeActions { 
    246254      filename = "document.pdf";
    247255    }
    248256    var blobUri = NetUtil.newURI(blobUrl);
    249257    var extHelperAppSvc =
    250258          Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
    251259             getService(Ci.nsIExternalHelperAppService);
    252260
    253261    var docIsPrivate = this.isInPrivateBrowsing();
     262    var firstPartyDomain = this.getFirstPartyDomain();
    254263    var netChannel = NetUtil.newChannel({
    255264      uri: blobUri,
    256265      loadUsingSystemPrincipal: true,
    257266    });
    258267    if ("nsIPrivateBrowsingChannel" in Ci &&
    259268        netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
    260269      netChannel.setPrivate(docIsPrivate);
     270      netChannel.setFirstPartyDomain(firstPartyDomain);
    261271    }
    262272    NetUtil.asyncFetch(netChannel, function(aInputStream, aResult) {
    263273      if (!Components.isSuccessCode(aResult)) {
    264274        if (sendResponse) {
    265275          sendResponse(true);
    266276        }
    267277        return;
    268278      }
    class ChromeActions { 
    281291        }
    282292      } catch (e) {}
    283293      channel.setURI(originalUri);
    284294      channel.loadInfo = netChannel.loadInfo;
    285295      channel.contentStream = aInputStream;
    286296      if ("nsIPrivateBrowsingChannel" in Ci &&
    287297          channel instanceof Ci.nsIPrivateBrowsingChannel) {
    288298        channel.setPrivate(docIsPrivate);
     299        channel.setFirstPartyDomain(firstPartyDomain);
    289300      }
    290301
    291302      var listener = {
    292303        extListener: null,
    293304        onStartRequest(aRequest, aContext) {
    294305          var loadContext = self.domWindow
    295306                                .QueryInterface(Ci.nsIInterfaceRequestor)
    296307                                .getInterface(Ci.nsIWebNavigation)
    class RangedChromeActions extends ChromeActions { 
    591602    var self = this;
    592603    var xhr_onreadystatechange = function xhr_onreadystatechange() {
    593604      if (this.readyState === 1) { // LOADING
    594605        var netChannel = this.channel;
    595606        if ("nsIPrivateBrowsingChannel" in Ci &&
    596607            netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
    597608          var docIsPrivate = self.isInPrivateBrowsing();
    598609          netChannel.setPrivate(docIsPrivate);
     610          var firstPartyDomain = self.getFirstPartyDomain();
     611          netChannel.setFirstPartyDomain(firstPartyDomain);
    599612        }
    600613      }
    601614    };
    602615    var getXhr = function getXhr() {
    603616      var xhr = new XMLHttpRequest();
    604617      xhr.addEventListener("readystatechange", xhr_onreadystatechange);
    605618      return xhr;
    606619    };
  • netwerk/base/PrivateBrowsingChannel.h

    diff --git a/netwerk/base/PrivateBrowsingChannel.h b/netwerk/base/PrivateBrowsingChannel.h
    index cf97aff638bc..1fb8b7604521 100644
    a b public: 
    5959      NS_ENSURE_ARG_POINTER(aResult);
    6060      *aResult = mPrivateBrowsingOverriden;
    6161      if (mPrivateBrowsingOverriden) {
    6262          *aValue = mPrivateBrowsing;
    6363      }
    6464      return NS_OK;
    6565  }
    6666
     67  NS_IMETHOD SetFirstPartyDomain(const nsAString & aFirstPartyDomain) override
     68  {
     69    mFirstPartyDomain.Assign(aFirstPartyDomain);
     70    return NS_OK;
     71  }
     72
     73  NS_IMETHOD GetFirstPartyDomain(nsAString & aFirstPartyDomain) override
     74  {
     75    aFirstPartyDomain.Assign(mFirstPartyDomain);
     76    return NS_OK;
     77  }
     78
     79
    6780  // Must be called every time the channel's callbacks or loadGroup is updated
    6881  void UpdatePrivateBrowsing()
    6982  {
    7083      // once marked as private we never go un-private
    7184      if (mPrivateBrowsing) {
    7285          return;
    7386      }
    7487
    public: 
    118131      // From this point on, we just hand off the work to CanSetCallbacks,
    119132      // because the logic is exactly the same.
    120133      return CanSetCallbacks(callbacks);
    121134  }
    122135
    123136protected:
    124137  bool mPrivateBrowsingOverriden;
    125138  bool mPrivateBrowsing;
     139  nsString mFirstPartyDomain;
    126140};
    127141
    128142} // namespace net
    129143} // namespace mozilla
    130144
    131145#endif
    132146
  • netwerk/base/nsBaseChannel.cpp

    diff --git a/netwerk/base/nsBaseChannel.cpp b/netwerk/base/nsBaseChannel.cpp
    index 30b7ec756056..a32f82e13af2 100644
    a b nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags, 
    131131    newChannel->SetLoadInfo(newLoadInfo);
    132132  }
    133133  else {
    134134    // the newChannel was created with a dummy loadInfo, we should clear
    135135    // it in case the original channel does not have a loadInfo
    136136    newChannel->SetLoadInfo(nullptr);
    137137  }
    138138
    139   // Preserve the privacy bit if it has been overridden
    140   if (mPrivateBrowsingOverriden) {
     139
     140  // Copy over nsIPrivateBrowsingChannel data
     141  {
    141142    nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel =
    142143      do_QueryInterface(newChannel);
    143144    if (newPBChannel) {
    144       newPBChannel->SetPrivate(mPrivateBrowsing);
     145      // Always smuggle the first party domain
     146      newPBChannel->SetFirstPartyDomain(mFirstPartyDomain);
     147      // Preserve the privacy bit if it has been overridden
     148      if (mPrivateBrowsingOverriden) {
     149        newPBChannel->SetPrivate(mPrivateBrowsing);
     150      }
    145151    }
    146152  }
    147153
    148154  nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel);
    149155  if (bag) {
    150156    for (auto iter = mPropertyHash.Iter(); !iter.Done(); iter.Next()) {
    151157      bag->SetProperty(iter.Key(), iter.UserData());
    152158    }
  • netwerk/base/nsIPrivateBrowsingChannel.idl

    diff --git a/netwerk/base/nsIPrivateBrowsingChannel.idl b/netwerk/base/nsIPrivateBrowsingChannel.idl
    index 3bc822f01857..db508f5cb4cb 100644
    a b interface nsIPrivateBrowsingChannel : nsISupports 
    5050     * This function is used to determine whether the channel's private mode
    5151     * has been overridden by a call to setPrivate.  It is intended to be used
    5252     * by NS_UsePrivateBrowsing(), and you should not call it directly.
    5353     *
    5454     * @param aValue the overridden value.  This will only be set if the function
    5555     *               returns true.
    5656     */
    5757    [noscript] boolean isPrivateModeOverriden(out boolean aValue);
     58
     59    /**
     60     * Setter for a smuggled copy of the channel's first-party domain.
     61     *
     62     * Channels created for XMLHttpRequests created in a System Context (ie,
     63     * from Firefox chrome) will not have a proper first-party domain in its
     64     * OriginAttributes, even when the first-party domain is known at creation
     65     * time.
     66     *
     67     * The first use case for this 'smuggling' is for pdfjs range-based
     68     * XMLHttpRequests which are created in a System Context.
     69     */
     70    void setFirstPartyDomain(in DOMString aFirstPartyDomain);
     71
     72    /**
     73     * Getter for the first-party domain used by torbutton for first-party
     74     * circuit isolation.
     75     */
     76    readonly attribute DOMString firstPartyDomain;
    5877};
  • netwerk/ipc/NeckoChannelParams.ipdlh

    diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh
    index 064bfcb795f1..7223a5c5cf14 100644
    a b struct HttpChannelOpenArgs 
    203203  uint64_t                    topLevelOuterContentWindowId;
    204204  TimeStamp                   launchServiceWorkerStart;
    205205  TimeStamp                   launchServiceWorkerEnd;
    206206  TimeStamp                   dispatchFetchEventStart;
    207207  TimeStamp                   dispatchFetchEventEnd;
    208208  TimeStamp                   handleFetchEventStart;
    209209  TimeStamp                   handleFetchEventEnd;
    210210  bool                        forceMainDocumentChannel;
     211  nsCString                   firstPartyDomain;
    211212};
    212213
    213214struct HttpChannelConnectArgs
    214215{
    215216  uint32_t registrarId;
    216217  bool shouldIntercept;
    217218};
    218219
  • netwerk/protocol/http/HttpChannelChild.cpp

    diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp
    index eddbee29a848..ac4d4a1ec219 100644
    a b HttpChannelChild::ContinueAsyncOpen() 
    27932793  openArgs.launchServiceWorkerStart() = mLaunchServiceWorkerStart;
    27942794  openArgs.launchServiceWorkerEnd()   = mLaunchServiceWorkerEnd;
    27952795  openArgs.dispatchFetchEventStart()  = mDispatchFetchEventStart;
    27962796  openArgs.dispatchFetchEventEnd()    = mDispatchFetchEventEnd;
    27972797  openArgs.handleFetchEventStart()    = mHandleFetchEventStart;
    27982798  openArgs.handleFetchEventEnd()      = mHandleFetchEventEnd;
    27992799
    28002800  openArgs.forceMainDocumentChannel() = mForceMainDocumentChannel;
     2801  openArgs.firstPartyDomain()         = NS_ConvertUTF16toUTF8(mFirstPartyDomain);
    28012802
    28022803  // This must happen before the constructor message is sent. Otherwise messages
    28032804  // from the parent could arrive quickly and be delivered to the wrong event
    28042805  // target.
    28052806  SetEventTarget();
    28062807
    28072808  // The socket transport in the chrome process now holds a logical ref to us
    28082809  // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
  • netwerk/protocol/http/HttpChannelParent.cpp

    diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp
    index 339b40fca8b7..e84b8a3c7e11 100644
    a b HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs) 
    147147                       a.channelId(), a.contentWindowId(), a.preferredAlternativeType(),
    148148                       a.topLevelOuterContentWindowId(),
    149149                       a.launchServiceWorkerStart(),
    150150                       a.launchServiceWorkerEnd(),
    151151                       a.dispatchFetchEventStart(),
    152152                       a.dispatchFetchEventEnd(),
    153153                       a.handleFetchEventStart(),
    154154                       a.handleFetchEventEnd(),
    155                        a.forceMainDocumentChannel());
     155                       a.forceMainDocumentChannel(),
     156                       a.firstPartyDomain());
    156157  }
    157158  case HttpChannelCreationArgs::THttpChannelConnectArgs:
    158159  {
    159160    const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
    160161    return ConnectChannel(cArgs.registrarId(), cArgs.shouldIntercept());
    161162  }
    162163  default:
    163164    NS_NOTREACHED("unknown open type");
    HttpChannelParent::DoAsyncOpen( const URIParams& aURI, 
    489490                                 const nsCString&           aPreferredAlternativeType,
    490491                                 const uint64_t&            aTopLevelOuterContentWindowId,
    491492                                 const TimeStamp&           aLaunchServiceWorkerStart,
    492493                                 const TimeStamp&           aLaunchServiceWorkerEnd,
    493494                                 const TimeStamp&           aDispatchFetchEventStart,
    494495                                 const TimeStamp&           aDispatchFetchEventEnd,
    495496                                 const TimeStamp&           aHandleFetchEventStart,
    496497                                 const TimeStamp&           aHandleFetchEventEnd,
    497                                  const bool&                aForceMainDocumentChannel)
     498                                 const bool&                aForceMainDocumentChannel,
     499                                 const nsCString&           aFirstPartyDomain)
    498500{
    499501  nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
    500502  if (!uri) {
    501503    // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
    502504    // null deref here.
    503505    return false;
    504506  }
    505507  nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
    HttpChannelParent::DoAsyncOpen( const URIParams& aURI, 
    577579  }
    578580  if (aLoadFlags != nsIRequest::LOAD_NORMAL)
    579581    httpChannel->SetLoadFlags(aLoadFlags);
    580582
    581583  if (aForceMainDocumentChannel) {
    582584    httpChannel->SetIsMainDocumentChannel(true);
    583585  }
    584586
     587  httpChannel->SetFirstPartyDomain(NS_ConvertUTF8toUTF16(aFirstPartyDomain));
     588
    585589  for (uint32_t i = 0; i < requestHeaders.Length(); i++) {
    586590    if (requestHeaders[i].mEmpty) {
    587591      httpChannel->SetEmptyRequestHeader(requestHeaders[i].mHeader);
    588592    } else {
    589593      httpChannel->SetRequestHeader(requestHeaders[i].mHeader,
    590594                                    requestHeaders[i].mValue,
    591595                                    requestHeaders[i].mMerge);
    592596    }
  • netwerk/protocol/http/HttpChannelParent.h

    diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h
    index d35e28033a45..6261f83d4b7a 100644
    a b protected: 
    173173              const nsCString&           aPreferredAlternativeType,
    174174              const uint64_t&            aTopLevelOuterContentWindowId,
    175175              const TimeStamp&           aLaunchServiceWorkerStart,
    176176              const TimeStamp&           aLaunchServiceWorkerEnd,
    177177              const TimeStamp&           aDispatchFetchEventStart,
    178178              const TimeStamp&           aDispatchFetchEventEnd,
    179179              const TimeStamp&           aHandleFetchEventStart,
    180180              const TimeStamp&           aHandleFetchEventEnd,
    181               const bool&                aForceMainDocumentChannel);
     181              const bool&                aForceMainDocumentChannel,
     182              const nsCString&           aFirstPartyDomain);
    182183
    183184  virtual mozilla::ipc::IPCResult RecvSetPriority(const int16_t& priority) override;
    184185  virtual mozilla::ipc::IPCResult RecvSetClassOfService(const uint32_t& cos) override;
    185186  virtual mozilla::ipc::IPCResult RecvSetCacheTokenCachedCharset(const nsCString& charset) override;
    186187  virtual mozilla::ipc::IPCResult RecvSuspend() override;
    187188  virtual mozilla::ipc::IPCResult RecvResume() override;
    188189  virtual mozilla::ipc::IPCResult RecvCancel(const nsresult& status) override;
    189190  virtual mozilla::ipc::IPCResult RecvRedirect2Verify(const nsresult& result,