From 09bfea06288c7c26aa45d327e36a6fd5fde67cc8 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 29 Apr 2021 20:37:28 -0700 Subject: [PATCH] WebSocketProtocol: Add Identify logic (not tested well) --- src/WebSocketProtocol.cpp | 101 ++++++++++++++++++++++++++++++++++++++ src/WebSocketProtocol.h | 2 +- src/WebSocketServer.cpp | 20 ++++---- src/WebSocketServer.h | 12 +++-- 4 files changed, 120 insertions(+), 15 deletions(-) diff --git a/src/WebSocketProtocol.cpp b/src/WebSocketProtocol.cpp index f38bbe28..baef5c6f 100644 --- a/src/WebSocketProtocol.cpp +++ b/src/WebSocketProtocol.cpp @@ -1 +1,102 @@ #include "WebSocketProtocol.h" +#include "obs-websocket.h" +#include "utils/Utils.h" + +#include "plugin-macros.generated.h" + +WebSocketProtocol::ProcessResult SetSessionParameters(WebSocketSession *session, json incomingMessage) +{ + WebSocketProtocol::ProcessResult ret; + + return ret; +} + +WebSocketProtocol::ProcessResult WebSocketProtocol::ProcessMessage(websocketpp::connection_hdl hdl, WebSocketSession *session, json incomingMessage) +{ + WebSocketProtocol::ProcessResult ret; + + if (!incomingMessage.is_object()) { + if (!session->IgnoreInvalidMessages()) { + ret.closeCode = WebSocketServer::WebSocketCloseCode::MessageDecodeError; + ret.closeReason = "You sent a non-object payload."; + } + return ret; + } + + if (!incomingMessage.contains("messageType")) { + if (incomingMessage.contains("request-type")) { + ret.closeCode = WebSocketServer::WebSocketCloseCode::UnsupportedProtocolVersion; + ret.closeReason = "You appear to be attempting to connect with the pre-5.0.0 plugin protocol. Check to make sure your client is updated."; + return ret; + } + if (!session->IgnoreInvalidMessages()) { + ret.closeCode = WebSocketServer::WebSocketCloseCode::UnknownMessageType; + ret.closeReason = "Your request is missing a `messageType`"; + } + return ret; + } + + std::string messageType = incomingMessage["messageType"]; + + if (!session->IsIdentified() && messageType != "Identify") { + ret.closeCode = WebSocketServer::WebSocketCloseCode::NotIdentified; + ret.closeReason = "You attempted to send a non-`Identify` message while not identified."; + return ret; + } + + if (messageType == "Request") { + ; + } else if (messageType == "RequestBatch") { + ; + } else if (messageType == "Identify") { + if (session->IsIdentified()) { + if (!session->IgnoreInvalidMessages()) { + ret.closeCode = WebSocketServer::WebSocketCloseCode::AlreadyIdentified; + ret.closeReason = "You are already Identified with the obs-websocket server."; + } + return ret; + } + + auto webSocketServer = GetWebSocketServer(); + if (!webSocketServer) { + blog(LOG_ERROR, "[WebSocketProtocol::ProcessMessage] Unable to fetch websocket server instance!"); + return ret; + } + + if (webSocketServer->AuthenticationRequired) { + if (!incomingMessage.contains("authentication")) { + ret.closeCode = WebSocketServer::WebSocketCloseCode::InvalidIdentifyParameter; + ret.closeReason = "Your `Identify` payload is missing an `authentication` string, however authentication is required."; + return ret; + } + if (!Utils::Crypto::CheckAuthenticationString(webSocketServer->AuthenticationSecret, session->Challenge(), incomingMessage["authentication"])) { + ret.closeCode = WebSocketServer::WebSocketCloseCode::AuthenticationFailed; + ret.closeReason = "Authentication failed."; + return ret; + } + } + + WebSocketProtocol::ProcessResult parameterResult = SetSessionParameters(session, incomingMessage); + if (ret.closeCode != WebSocketServer::WebSocketCloseCode::DontClose) { + return parameterResult; + } + + blog(LOG_INFO, "Identified!"); + + session->SetIsIdentified(true); + + ret.result["messageType"] = "Identified"; + ret.result["negotiatedRpcVersion"] = session->RpcVersion(); + return ret; + } else if (messageType == "Reidentify") { + ; + } else { + if (!session->IgnoreInvalidMessages()) { + ret.closeCode = WebSocketServer::WebSocketCloseCode::UnknownMessageType; + ret.closeReason = std::string("Unknown message type: %s") + messageType; + } + return ret; + } + + return ret; +} diff --git a/src/WebSocketProtocol.h b/src/WebSocketProtocol.h index 1825381d..0807e14f 100644 --- a/src/WebSocketProtocol.h +++ b/src/WebSocketProtocol.h @@ -9,7 +9,7 @@ namespace WebSocketProtocol { struct ProcessResult { - WebSocketServer::WebSocketCloseCode closeCode; + WebSocketServer::WebSocketCloseCode closeCode = WebSocketServer::WebSocketCloseCode::DontClose; std::string closeReason; json result; }; diff --git a/src/WebSocketServer.cpp b/src/WebSocketServer.cpp index 0b9f3057..ab5ce4df 100644 --- a/src/WebSocketServer.cpp +++ b/src/WebSocketServer.cpp @@ -81,9 +81,9 @@ void WebSocketServer::Start() _serverPort = conf->ServerPort; _serverPassword = conf->ServerPassword; _debugEnabled = conf->DebugEnabled; - _authenticationRequired = conf->AuthRequired; - _authenticationSalt = Utils::Crypto::GenerateSalt(); - _authenticationSecret = Utils::Crypto::GenerateSecret(conf->ServerPassword.toStdString(), _authenticationSalt); + AuthenticationRequired = conf->AuthRequired; + AuthenticationSalt = Utils::Crypto::GenerateSalt(); + AuthenticationSecret = Utils::Crypto::GenerateSecret(conf->ServerPassword.toStdString(), AuthenticationSalt); // Set log levels if debug is enabled if (_debugEnabled) { @@ -191,7 +191,7 @@ std::vector WebSocketServer::GetWebSocke QString WebSocketServer::GetConnectString() { QString ret; - if (_authenticationRequired) + if (AuthenticationRequired) ret = QString("obswebsocket|%1:%2|%3").arg(QString::fromStdString(Utils::Platform::GetLocalAddress())).arg(_serverPort).arg(_serverPassword); else ret = QString("obswebsocket|%1:%2").arg(QString::fromStdString(Utils::Platform::GetLocalAddress())).arg(_serverPort); @@ -275,12 +275,12 @@ void WebSocketServer::onOpen(websocketpp::connection_hdl hdl) helloMessage["obsWebSocketVersion"] = OBS_WEBSOCKET_VERSION; helloMessage["rpcVersion"] = OBS_WEBSOCKET_RPC_VERSION; // todo: Add request and event lists - if (_authenticationRequired) { + if (AuthenticationRequired) { std::string sessionChallenge = Utils::Crypto::GenerateSalt(); session.SetChallenge(sessionChallenge); helloMessage["authentication"] = {}; helloMessage["authentication"]["challenge"] = sessionChallenge; - helloMessage["authentication"]["salt"] = _authenticationSalt; + helloMessage["authentication"]["salt"] = AuthenticationSalt; } // Send object to client @@ -344,7 +344,7 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se if (sessionEncoding == WebSocketEncoding::Json) { if (opcode != websocketpp::frame::opcode::text) { if (!session.IgnoreInvalidMessages()) - _server.close(hdl, WebSocketCloseCode::MessageDecodeError, "The session encoding is set to Json, but the client sent a binary message.", errorCode); + _server.close(hdl, WebSocketCloseCode::MessageDecodeError, "Your session encoding is set to Json, but a binary message was received.", errorCode); return; } @@ -358,7 +358,7 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se } else if (sessionEncoding == WebSocketEncoding::MsgPack) { if (opcode != websocketpp::frame::opcode::binary) { if (!session.IgnoreInvalidMessages()) - _server.close(hdl, WebSocketCloseCode::MessageDecodeError, "The session encoding is set to MsgPack, but the client sent a text message.", errorCode); + _server.close(hdl, WebSocketCloseCode::MessageDecodeError, "Your session encoding is set to MsgPack, but a text message was received.", errorCode); return; } @@ -376,13 +376,13 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se WebSocketProtocol::ProcessResult ret = WebSocketProtocol::ProcessMessage(hdl, &session, incomingMessage); - if (ret.closeCode) { + if (ret.closeCode != WebSocketCloseCode::DontClose) { websocketpp::lib::error_code errorCode; _server.close(hdl, ret.closeCode, ret.closeReason, errorCode); return; } - if (ret.result) { + if (!ret.result.is_null()) { websocketpp::lib::error_code errorCode; if (sessionEncoding == WebSocketEncoding::Json) { std::string helloMessageJson = ret.result.dump(); diff --git a/src/WebSocketServer.h b/src/WebSocketServer.h index 802e4967..0ade9184 100644 --- a/src/WebSocketServer.h +++ b/src/WebSocketServer.h @@ -19,10 +19,13 @@ class WebSocketServer : QObject public: enum WebSocketCloseCode: uint16_t { + // Internal only + DontClose = 0, + // Reserved UnknownReason = 4000, // The server was unable to decode the incoming websocket message MessageDecodeError = 4001, - // The specified `messageType` was invalid + // The specified `messageType` was invalid or missing UnknownMessageType = 4002, // The client sent a websocket message without first sending `Identify` message NotIdentified = 4003, @@ -74,6 +77,10 @@ class WebSocketServer : QObject QString GetConnectString(); + bool AuthenticationRequired; + std::string AuthenticationSecret; + std::string AuthenticationSalt; + public Q_SLOTS: void BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData = nullptr); @@ -97,7 +104,4 @@ class WebSocketServer : QObject uint16_t _serverPort; QString _serverPassword; bool _debugEnabled; - bool _authenticationRequired; - std::string _authenticationSecret; - std::string _authenticationSalt; };