]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Require WebSocket origins to be whitelisted in order to connect.
authorPeter Powell <petpow@saberuk.com>
Wed, 10 Oct 2018 15:45:35 +0000 (16:45 +0100)
committerPeter Powell <petpow@saberuk.com>
Wed, 10 Oct 2018 15:45:35 +0000 (16:45 +0100)
Fixes #1281.

docs/conf/modules.conf.example
src/modules/m_websocket.cpp

index 8a22a8c719b245920833a962eabecf0590dd7d46..72f295cc54fa76854ac70d97d13e6b4575158892 100644 (file)
 # WebSocket connections. Compatible with SSL/TLS.
 # Requires SHA-1 hash support available in the sha1 module.
 #<module name="websocket">
+#
+# If you use the websocket module you MUST specify one or more origins
+# which are allowed to connect to the server. You should set this as
+# strict as possible to prevent malicious webpages from connecting to
+# your server.
+# <wsorigin allow="https://webchat.example.com/*">
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # XLine database: Stores all *Lines (G/Z/K/R/any added by other modules)
index 12102d2151bd818f392ac82162dbb32dfa117c67..5ac661ccfaefb63927695bf0bbef450ea98424dd 100644 (file)
@@ -21,6 +21,8 @@
 #include "iohook.h"
 #include "modules/hash.h"
 
+typedef std::vector<std::string> OriginList;
+
 static const char MagicGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 static const char whitespace[] = " \t\r\n";
 static dynamic_reference_nocheck<HashProvider>* sha1;
@@ -28,6 +30,8 @@ static dynamic_reference_nocheck<HashProvider>* sha1;
 class WebSocketHookProvider : public IOHookProvider
 {
  public:
+       OriginList allowedorigins;
+
        WebSocketHookProvider(Module* mod)
                : IOHookProvider(mod, "websocket", IOHookProvider::IOH_UNKNOWN, true)
        {
@@ -101,6 +105,7 @@ class WebSocketHook : public IOHookMiddle
 
        State state;
        time_t lastpingpong;
+       OriginList& allowedorigins;
 
        static size_t FillHeader(unsigned char* outbuf, size_t sendlength, OpCode opcode)
        {
@@ -288,6 +293,27 @@ class WebSocketHook : public IOHookMiddle
                if (reqend == std::string::npos)
                        return 0;
 
+               bool allowedorigin = false;
+               HTTPHeaderFinder originheader;
+               if (originheader.Find(recvq, "Origin:", 7, reqend))
+               {
+                       const std::string origin = originheader.ExtractValue(recvq);
+                       for (OriginList::const_iterator iter = allowedorigins.begin(); iter != allowedorigins.end(); ++iter)
+                       {
+                               if (InspIRCd::Match(origin, *iter, ascii_case_insensitive_map))
+                               {
+                                       allowedorigin = true;
+                                       break;
+                               }
+                       }
+               }
+
+               if (!allowedorigin)
+               {
+                       FailHandshake(sock, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n", "WebSocket: Received HTTP request from a non-whitelisted origin");
+                       return -1;
+               }
+
                HTTPHeaderFinder keyheader;
                if (!keyheader.Find(recvq, "Sec-WebSocket-Key:", 18, reqend))
                {
@@ -318,10 +344,11 @@ class WebSocketHook : public IOHookMiddle
        }
 
  public:
-       WebSocketHook(IOHookProvider* Prov, StreamSocket* sock)
+       WebSocketHook(IOHookProvider* Prov, StreamSocket* sock, OriginList& AllowedOrigins)
                : IOHookMiddle(Prov)
                , state(STATE_HTTPREQ)
                , lastpingpong(0)
+               , allowedorigins(AllowedOrigins)
        {
                sock->AddIOHook(this);
        }
@@ -370,7 +397,7 @@ class WebSocketHook : public IOHookMiddle
 
 void WebSocketHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 {
-       new WebSocketHook(this, sock);
+       new WebSocketHook(this, sock, allowedorigins);
 }
 
 class ModuleWebSocket : public Module
@@ -386,6 +413,27 @@ class ModuleWebSocket : public Module
                sha1 = &hash;
        }
 
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTagList tags = ServerInstance->Config->ConfTags("wsorigin");
+               if (tags.first == tags.second)
+                       throw ModuleException("You have loaded the websocket module but not configured any allowed origins!");
+
+               OriginList allowedorigins;
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+
+                       // Ensure that we have the <wsorigin:allow> parameter.
+                       const std::string allow = tag->getString("allow");
+                       if (allow.empty())
+                               throw ModuleException("<wsorigin:allow> is a mandatory field, at " + tag->getTagLocation());
+
+                       allowedorigins.push_back(allow);
+               }
+               hookprov->allowedorigins.swap(allowedorigins);
+       }
+
        void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
        {
                if (type != ExtensionItem::EXT_USER)