Makefile.am | 1 fuzzer/ClientSession.cpp | 4 - loleaflet/src/map/Clipboard.js | 14 +++--- wsd/ClientSession.cpp | 13 +++--- wsd/ClientSession.hpp | 7 +-- wsd/DocumentBroker.cpp | 7 +-- wsd/DocumentBroker.hpp | 5 +- wsd/FileServer.cpp | 54 +------------------------- wsd/LOOLWSD.cpp | 18 ++------ wsd/ProxyProtocol.cpp | 4 - wsd/ServerURL.hpp | 84 +++++++++++++++++++++++++++++++++++++++++ 11 files changed, 124 insertions(+), 87 deletions(-)
New commits: commit 18c4301a1f26e5630b24db532a532baafb6cd57f Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Wed May 6 17:02:51 2020 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Wed May 6 23:12:12 2020 +0200 Proxy: re-factor proxy handling into ServerURL and cleanup copy/paste. Also adds ServiceRoot handling for clipboard. Change-Id: I7bc6591130fcc7d693e59ab8561fb9e99f4e93d5 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/93578 Tested-by: Michael Meeks <michael.me...@collabora.com> Reviewed-by: Michael Meeks <michael.me...@collabora.com> diff --git a/Makefile.am b/Makefile.am index 42e338913..1b5beb47c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -225,6 +225,7 @@ wsd_headers = wsd/Admin.hpp \ wsd/ProofKey.hpp \ wsd/QueueHandler.hpp \ wsd/SenderQueue.hpp \ + wsd/ServerURL.hpp \ wsd/Storage.hpp \ wsd/TileCache.hpp \ wsd/TileDesc.hpp \ diff --git a/fuzzer/ClientSession.cpp b/fuzzer/ClientSession.cpp index 844fa0c80..a9a6ad657 100644 --- a/fuzzer/ClientSession.cpp +++ b/fuzzer/ClientSession.cpp @@ -21,9 +21,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) std::shared_ptr<ProtocolHandlerInterface> ws; std::string id; bool isReadOnly = false; - const std::string hostNoTrust; + const ServerURL serverURL("", ""); auto session - = std::make_shared<ClientSession>(ws, id, docBroker, uriPublic, isReadOnly, hostNoTrust); + = std::make_shared<ClientSession>(ws, id, docBroker, uriPublic, isReadOnly, serverURL); std::string input(reinterpret_cast<const char*>(data), size); std::stringstream ss(input); diff --git a/loleaflet/src/map/Clipboard.js b/loleaflet/src/map/Clipboard.js index 700369d29..c7b604083 100644 --- a/loleaflet/src/map/Clipboard.js +++ b/loleaflet/src/map/Clipboard.js @@ -99,6 +99,10 @@ L.Clipboard = L.Class.extend({ '&Tag=' + this._accessKey[idx]; }, + getMetaURL: function(idx) { + return this.getMetaBase() + this.getMetaPath(idx); + }, + // Returns the marker used to identify stub messages. _getHtmlStubMarker: function() { return '<title>Stub HTML Message</title>'; @@ -111,7 +115,7 @@ L.Clipboard = L.Class.extend({ // wrap some content with our stub magic _originWrapBody: function(body, isStub) { - var encodedOrigin = encodeURIComponent(this.getMetaPath()); + var encodedOrigin = encodeURIComponent(this.getMetaURL()); var text = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\n' + '<html>\n' + ' <head>\n'; @@ -340,8 +344,7 @@ L.Clipboard = L.Class.extend({ if (meta !== '') { console.log('Transfer between servers\n\t"' + meta + '" vs. \n\t"' + id + '"'); - var destination = this.getMetaBase() + this.getMetaPath(); - this._dataTransferDownloadAndPasteAsync(meta, destination, htmlText); + this._dataTransferDownloadAndPasteAsync(meta, this.getMetaURL(), htmlText); return; } @@ -397,8 +400,7 @@ L.Clipboard = L.Class.extend({ formData.append('file', content); var that = this; - var destination = this.getMetaBase() + this.getMetaPath(); - this._doAsyncDownload('POST', destination, formData, + this._doAsyncDownload('POST', this.getMetaURL(), formData, function() { console.log('Posted ' + content.size + ' bytes successfully'); if (usePasteKeyEvent) { @@ -439,7 +441,7 @@ L.Clipboard = L.Class.extend({ text = this._getStubHtml(); this._onDownloadOnLargeCopyPaste(); this._downloadProgress.setURI( // richer, bigger HTML ... - this.getMetaBase() + this.getMetaPath() + '&MimeType=text/html'); + this.getMetaURL() + '&MimeType=text/html'); } } else if (this._selectionType === null) { console.log('Copy/Cut with no selection!'); diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index c4c6ec7cb..c28961fb6 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -44,7 +44,7 @@ ClientSession::ClientSession( const std::shared_ptr<DocumentBroker>& docBroker, const Poco::URI& uriPublic, const bool readOnly, - const std::string& hostNoTrust) : + const ServerURL &serverURL) : Session(ws, "ToClient-" + id, id, readOnly), _docBroker(docBroker), _uriPublic(uriPublic), @@ -58,7 +58,7 @@ ClientSession::ClientSession( _tileWidthTwips(0), _tileHeightTwips(0), _kitViewId(-1), - _hostNoTrust(hostNoTrust), + _serverURL(serverURL), _isTextDocument(false) { const size_t curConnections = ++LOOLWSD::NumConnections; @@ -207,12 +207,11 @@ std::string ClientSession::getClipboardURI(bool encode) std::string encodeChars = ",/?:@&=+$#"; // match JS encodeURIComponent Poco::URI::encode(wopiSrc.toString(), encodeChars, encodedFrom); - std::string proto = (LOOLWSD::isSSLEnabled() || LOOLWSD::isSSLTermination()) ? "https://" : "http://"; - std::string meta = proto + _hostNoTrust + + std::string meta = _serverURL.getSubURLForEndpoint( "/lool/clipboard?WOPISrc=" + encodedFrom + "&ServerId=" + LOOLWSD::HostIdentifier + "&ViewId=" + std::to_string(getKitViewId()) + - "&Tag=" + _clipboardKeys[0]; + "&Tag=" + _clipboardKeys[0]); if (!encode) return meta; @@ -1030,6 +1029,7 @@ void ClientSession::writeQueuedMessages() LOG_TRC(getName() << " ClientSession: performed write."); } +// NB. also see loleaflet/src/map/Clipboard.js that does this in JS for stubs. void ClientSession::postProcessCopyPayload(std::shared_ptr<Message> payload) { // Insert our meta origin if we can @@ -1043,6 +1043,7 @@ void ClientSession::postProcessCopyPayload(std::shared_ptr<Message> payload) if (pos != std::string::npos) // assume text/html { const std::string meta = getClipboardURI(); + LOG_TRC("Inject clipboard meta origin of '" << meta << "'"); const std::string origin = "<meta name=\"origin\" content=\"" + meta + "\"/>\n"; data.insert(data.begin() + pos, origin.begin(), origin.end()); return true; @@ -1719,7 +1720,7 @@ void ClientSession::dumpState(std::ostream& os) << "\n\t\ttile size Pixel: " << _tileWidthPixel << "x" << _tileHeightPixel << "\n\t\ttile size Twips: " << _tileWidthTwips << "x" << _tileHeightTwips << "\n\t\tkit ViewId: " << _kitViewId - << "\n\t\thost (un-trusted): " << _hostNoTrust + << "\n\t\tour URL (un-trusted): " << _serverURL.getSubURLForEndpoint("") << "\n\t\tisTextDocument: " << _isTextDocument << "\n\t\tclipboardKeys[0]: " << _clipboardKeys[0] << "\n\t\tclipboardKeys[1]: " << _clipboardKeys[1] diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp index f2bd63adc..a99832560 100644 --- a/wsd/ClientSession.hpp +++ b/wsd/ClientSession.hpp @@ -13,6 +13,7 @@ #include "Storage.hpp" #include "MessageQueue.hpp" #include "SenderQueue.hpp" +#include "ServerURL.hpp" #include "DocumentBroker.hpp" #include <Poco/URI.h> #include <Rectangle.hpp> @@ -33,7 +34,7 @@ public: const std::shared_ptr<DocumentBroker>& docBroker, const Poco::URI& uriPublic, const bool isReadOnly, - const std::string& hostNoTrust); + const ServerURL &serverURL); void construct(); virtual ~ClientSession(); @@ -260,8 +261,8 @@ private: /// The integer id of the view in the Kit process int _kitViewId; - /// Un-trusted hostname of our service from the client - const std::string _hostNoTrust; + /// How to find our service from the client. + const ServerURL _serverURL; /// Client is using a text document? bool _isTextDocument; diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp index a81864506..1cbfcac13 100644 --- a/wsd/DocumentBroker.cpp +++ b/wsd/DocumentBroker.cpp @@ -1526,7 +1526,7 @@ std::shared_ptr<ClientSession> DocumentBroker::createNewClientSession( const std::string& id, const Poco::URI& uriPublic, const bool isReadOnly, - const std::string& hostNoTrust) + const ServerURL &serverURL) { try { @@ -1541,7 +1541,7 @@ std::shared_ptr<ClientSession> DocumentBroker::createNewClientSession( // In case of WOPI, if this session is not set as readonly, it might be set so // later after making a call to WOPI host which tells us the permission on files // (UserCanWrite param). - auto session = std::make_shared<ClientSession>(ws, id, shared_from_this(), uriPublic, isReadOnly, hostNoTrust); + auto session = std::make_shared<ClientSession>(ws, id, shared_from_this(), uriPublic, isReadOnly, serverURL); session->construct(); return session; @@ -2269,7 +2269,8 @@ bool ConvertToBroker::startConversion(SocketDisposition &disposition, const std: const bool isReadOnly = true; // FIXME: associate this with moveSocket (?) std::shared_ptr<ProtocolHandlerInterface> nullPtr; - _clientSession = std::make_shared<ClientSession>(nullPtr, id, docBroker, getPublicUri(), isReadOnly, "nocliphost"); + ServerURL serverURL; + _clientSession = std::make_shared<ClientSession>(nullPtr, id, docBroker, getPublicUri(), isReadOnly, serverURL); _clientSession->construct(); if (!_clientSession) diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index f8c2ccea2..f086ddea6 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -97,6 +97,7 @@ private: int _smapsFD; }; +class ServerURL; class ClientSession; /// DocumentBroker is responsible for setting up a document in jail and brokering loading it from @@ -153,7 +154,7 @@ public: const std::string& id, const Poco::URI& uriPublic, const bool isReadOnly, - const std::string& hostNoTrust); + const ServerURL &serverURL); /// Find or create a new client session for the PHP proxy void handleProxyRequest( @@ -161,7 +162,7 @@ public: const std::string& id, const Poco::URI& uriPublic, const bool isReadOnly, - const std::string& hostNoTrust, + const ServerURL &serverURL, const std::shared_ptr<StreamSocket> &socket, bool isWaiting); diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp index 888509591..8809a582a 100644 --- a/wsd/FileServer.cpp +++ b/wsd/FileServer.cpp @@ -43,6 +43,7 @@ #include <Crypto.hpp> #include "FileServer.hpp" #include "LOOLWSD.hpp" +#include "ServerURL.hpp" #include <Log.hpp> #include <Protocol.hpp> #include <Util.hpp> @@ -641,61 +642,12 @@ constexpr char BRANDING_UNSUPPORTED[] = "branding-unsupported"; #endif namespace { - /// Very simple splitting of proxy URLs without fear of escaping or validation. - class ProxyURL { - std::string _schemeAuthority; - std::string _pathPlus; - public: - ProxyURL(const HTTPRequest &request) - { - // The user can override the ServerRoot with a new prefix. - if (_pathPlus.size() <= 0) - _pathPlus = LOOLWSD::ServiceRoot; - - if (_schemeAuthority.size() <= 0) - { - bool ssl = (LOOLWSD::isSSLEnabled() || LOOLWSD::isSSLTermination()); - std::string serverName = LOOLWSD::ServerName.empty() ? request.getHost() : LOOLWSD::ServerName; - _schemeAuthority = (ssl ? "wss://" : "ws://") + serverName; - } - - // A well formed ProxyPrefix will override it. - std::string url = request.get("ProxyPrefix", ""); - if (url.size() <= 0) - return; - - size_t pos = url.find("://"); - if (pos != std::string::npos) { - pos = url.find("/", pos + 3); - if (pos != std::string::npos) - { - _schemeAuthority = url.substr(0, pos); - _pathPlus = url.substr(pos); - return; - } - else - LOG_ERR("Unusual proxy prefix '" << url << "'"); - } else - LOG_ERR("No http[s]:// in unusual proxy prefix '" << url << "'"); - - } - - std::string getResponseRoot() const - { - return _pathPlus; - } - - std::string getWebSocketUrl() const - { - return _schemeAuthority; - } - }; } void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::MemoryInputStream& message, const std::shared_ptr<StreamSocket>& socket) { - ProxyURL cnxDetails(request); + ServerURL cnxDetails(request); const Poco::URI::QueryParameters params = Poco::URI(request.getURI()).getQueryParameters(); @@ -963,7 +915,7 @@ void FileServerRequestHandler::preprocessAdminFile(const HTTPRequest& request,co if (!FileServerRequestHandler::isAdminLoggedIn(request, response)) throw Poco::Net::NotAuthenticatedException("Invalid admin login"); - ProxyURL cnxDetails(request); + ServerURL cnxDetails(request); std::string responseRoot = cnxDetails.getResponseRoot(); static const std::string scriptJS("<script src=\"%s/loleaflet/" LOOLWSD_VERSION_HASH "/%s.js\"></script>"); diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 3b770cf03..18d8469ef 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -2956,7 +2956,7 @@ private: } } - const std::string hostNoTrust = (LOOLWSD::ServerName.empty() ? request.getHost() : LOOLWSD::ServerName); + ServerURL serverURL(request); LOG_INF("URL [" << LOOLWSD::anonymizeUrl(url) << "] is " << (isReadOnly ? "readonly" : "writable") << "."); (void)request; (void)message; (void)disposition; @@ -2973,7 +2973,7 @@ private: { // need to move into the DocumentBroker context before doing session lookup / creation etc. std::string id = _id; - disposition.setMove([docBroker, id, uriPublic, isReadOnly, hostNoTrust, sessionId, isWaiting] + disposition.setMove([docBroker, id, uriPublic, isReadOnly, serverURL, sessionId, isWaiting] (const std::shared_ptr<Socket> &moveSocket) { LOG_TRC("Setting up docbroker thread for " << docBroker->getDocKey()); @@ -2983,7 +2983,7 @@ private: // We no longer own this socket. moveSocket->setThreadOwner(std::thread::id()); - docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, hostNoTrust, + docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, serverURL, sessionId, moveSocket, isWaiting]() { // Now inside the document broker thread ... @@ -2994,7 +2994,7 @@ private: { docBroker->handleProxyRequest( sessionId, id, uriPublic, isReadOnly, - hostNoTrust, streamSocket, isWaiting); + serverURL, streamSocket, isWaiting); return; } catch (const UnauthorizedRequestException& exc) @@ -3084,15 +3084,9 @@ private: DocumentBroker::ChildType::Interactive, url, docKey, _id, uriPublic); if (docBroker) { -#if MOBILEAPP - const std::string hostNoTrust; -#else - // We can send this back to whomever sent it to us though. - const std::string hostNoTrust = (LOOLWSD::ServerName.empty() ? request.getHost() : LOOLWSD::ServerName); -#endif - + ServerURL serverURL(request); std::shared_ptr<ClientSession> clientSession = - docBroker->createNewClientSession(ws, _id, uriPublic, isReadOnly, hostNoTrust); + docBroker->createNewClientSession(ws, _id, uriPublic, isReadOnly, serverURL); if (clientSession) { // Transfer the client socket to the DocumentBroker when we get back to the poll: diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index 973d9f3c5..f11d7d942 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -24,7 +24,7 @@ void DocumentBroker::handleProxyRequest( const std::string& id, const Poco::URI& uriPublic, const bool isReadOnly, - const std::string& hostNoTrust, + const ServerURL &serverURL, const std::shared_ptr<StreamSocket> &socket, bool isWaiting) { @@ -34,7 +34,7 @@ void DocumentBroker::handleProxyRequest( LOG_TRC("proxy: Create session for " << _docKey); clientSession = createNewClientSession( std::make_shared<ProxyProtocolHandler>(), - id, uriPublic, isReadOnly, hostNoTrust); + id, uriPublic, isReadOnly, serverURL); addSession(clientSession); LOOLWSD::checkDiskSpaceAndWarnClients(true); LOOLWSD::checkSessionLimitsAndWarnClients(); diff --git a/wsd/ServerURL.hpp b/wsd/ServerURL.hpp new file mode 100644 index 000000000..3e96fff98 --- /dev/null +++ b/wsd/ServerURL.hpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + */ + +#pragma once + +#include <string> +#include <Poco/Net/HTTPRequest.h> +#include "LOOLWSD.hpp" + +/** This class helps us to build a URL that will reliably point back + * at our service. It does very simple splitting of proxy U + * and handles the proxy prefix feature. + */ +class ServerURL +{ + std::string _schemeProtocol; + std::string _schemeAuthority; + std::string _pathPlus; +public: + ServerURL(const Poco::Net::HTTPRequest &request) + { + init(request.getHost(), request.get("ProxyPrefix", "")); + } + + explicit ServerURL() + { + init("nohostname", ""); + } + + void init(const std::string &host, const std::string &proxyPrefix) + { + // The user can override the ServerRoot with a new prefix. + _pathPlus = LOOLWSD::ServiceRoot; + + bool ssl = (LOOLWSD::isSSLEnabled() || LOOLWSD::isSSLTermination()); + std::string serverName = LOOLWSD::ServerName.empty() ? host : LOOLWSD::ServerName; + _schemeProtocol = (ssl ? "wss://" : "ws://"); + _schemeAuthority = serverName; + + // A well formed ProxyPrefix will override it. + std::string url = proxyPrefix; + if (url.size() <= 0) + return; + + size_t pos = url.find("://"); + if (pos != std::string::npos) { + pos += 3; + auto hostEndPos = url.find("/", pos); + if (hostEndPos != std::string::npos) + { + _schemeProtocol = url.substr(0, pos); + _schemeAuthority = url.substr(pos, hostEndPos - pos); + _pathPlus = url.substr(hostEndPos); + return; + } + else + LOG_ERR("Unusual proxy prefix '" << url << "'"); + } else + LOG_ERR("No http[s]:// in unusual proxy prefix '" << url << "'"); + } + + std::string getResponseRoot() const + { + return _pathPlus; + } + + std::string getWebSocketUrl() const + { + return _schemeProtocol + _schemeAuthority; + } + + std::string getSubURLForEndpoint(const std::string &path) const + { + return _schemeProtocol + _schemeAuthority + _pathPlus + path; + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits