From 41fb7d4f42f78b5c28709d38f2a741d5723257c7 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Tue, 25 Oct 2022 11:14:26 -0700 Subject: [PATCH] Fixed guest web relay session revocation (#4667) --- meshagent.js | 2 +- meshuser.js | 2 +- webrelayserver.js | 17 +++++++++++++++-- webserver.js | 17 +++++++++++++++-- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/meshagent.js b/meshagent.js index 025decbc..88c0b01f 100644 --- a/meshagent.js +++ b/meshagent.js @@ -1842,7 +1842,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Event device share removal if (removedExact != null) { // Send out an event that we removed a device share - var targets = parent.CreateNodeDispatchTargets(obj.dbMeshKey, obj.dbNodeKey, []); + var targets = parent.CreateNodeDispatchTargets(obj.dbMeshKey, obj.dbNodeKey, ['server-shareremove']); var event = { etype: 'node', nodeid: obj.dbNodeKey, action: 'removedDeviceShare', msg: 'Removed Device Share', msgid: 102, msgArgs: ['Agent'], domain: domain.id, publicid: publicid }; parent.parent.DispatchEvent(targets, obj, event); } diff --git a/meshuser.js b/meshuser.js index 8e379926..086128ec 100644 --- a/meshuser.js +++ b/meshuser.js @@ -4160,7 +4160,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Event device share removal if (removedExact != null) { // Send out an event that we removed a device share - var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['server-users', user._id]); + var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['server-users', 'server-shareremove', user._id]); var event = { etype: 'node', userid: user._id, username: user.name, nodeid: node._id, action: 'removedDeviceShare', msg: 'Removed Device Share', msgid: 102, msgArgs: [removedExact.guestName], domain: domain.id, publicid: command.publicid }; parent.parent.DispatchEvent(targets, obj, event); diff --git a/webrelayserver.js b/webrelayserver.js index 34f12bfd..2c6a31f3 100644 --- a/webrelayserver.js +++ b/webrelayserver.js @@ -65,6 +65,17 @@ module.exports.CreateWebRelayServer = function (parent, db, args, certificates, // If args.sessionkey is a string, use it as a single key, but args.sessionkey can also be used as an array of keys. const keygrip = require('keygrip')((typeof args.sessionkey == 'string') ? [args.sessionkey] : args.sessionkey, 'sha384', 'base64'); + // Watch for device share removal + parent.AddEventDispatch(['server-shareremove'], obj); + obj.HandleEvent = function (source, event, ids, id) { + if (event.action == 'removedDeviceShare') { + for (var relaySessionId in relaySessions) { + // A share was removed that matches an active session, close the session. + if (relaySessions[relaySessionId].xpublicid === event.publicid) { relaySessions[relaySessionId].close(); } + } + } + } + // Setup cookie session const sessionOptions = { name: 'xid', // Recommended security practice to not use the default cookie name @@ -187,11 +198,11 @@ module.exports.CreateWebRelayServer = function (parent, db, args, certificates, if (req.query.c == null) { res.sendStatus(404); return; } // Decode and check if this relay cookie is valid - var userid, domainid, domain, nodeid, addr, port, appid, webSessionId, expire; + var userid, domainid, domain, nodeid, addr, port, appid, webSessionId, expire, publicid; const urlCookie = obj.parent.decodeCookie(req.query.c, parent.loginCookieEncryptionKey, 32); // Allow cookies up to 32 minutes old. The web page will renew this cookie every 30 minutes. if (urlCookie == null) { res.sendStatus(404); return; } - // Decode the incomign cookie + // Decode the incoming cookie if ((urlCookie.ruserid != null) && (urlCookie.x != null)) { if (parent.webserver.destroyedSessions[urlCookie.ruserid + '/' + urlCookie.x] != null) { res.sendStatus(404); return; } @@ -220,6 +231,7 @@ module.exports.CreateWebRelayServer = function (parent, db, args, certificates, port = urlCookie.port; appid = (urlCookie.p == 16) ? 2 : 1; // appid: 1 = HTTP, 2 = HTTPS webSessionId = userid + '/' + urlCookie.pid; + publicid = urlCookie.pid; if (req.session.x) { delete req.session.x; } // Clear the web relay sessionid if (req.session.userid) { delete req.session.userid; } // Clear the web relay userid if (req.session.z != webSessionId) { req.session.z = webSessionId; } // Set the web relay guest session @@ -248,6 +260,7 @@ module.exports.CreateWebRelayServer = function (parent, db, args, certificates, // Create a web relay session const relaySession = require('./apprelays.js').CreateWebRelaySession(obj, db, req, args, domain, userid, nodeid, addr, port, appid, webSessionId, expire); + relaySession.xpublicid = publicid; relaySession.onclose = function (sessionId) { // Remove the relay session delete relaySessions[sessionId]; diff --git a/webserver.js b/webserver.js index 793f9be4..46a542e9 100644 --- a/webserver.js +++ b/webserver.js @@ -87,9 +87,20 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF // Web relay sessions var webRelayNextSessionId = 1; - var webRelaySessions = {} // UserId/SessionId/Host --> Web Relay Session + var webRelaySessions = {} // UserId/SessionId/Host --> Web Relay Session var webRelayCleanupTimer = null; + // Monitor web relay session removals + parent.AddEventDispatch(['server-shareremove'], obj); + obj.HandleEvent = function (source, event, ids, id) { + if (event.action == 'removedDeviceShare') { + for (var relaySessionId in webRelaySessions) { + // A share was removed that matches an active session, close the web relay session. + if (webRelaySessions[relaySessionId].xpublicid === event.publicid) { webRelaySessions[relaySessionId].close(); } + } + } + } + // Mesh Rights const MESHRIGHT_EDITMESH = 0x00000001; const MESHRIGHT_MANAGEUSERS = 0x00000002; @@ -6749,7 +6760,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF if (req.query.c == null) { res.sendStatus(404); return; } // Decode and check if this relay cookie is valid - var userid, domainid, domain, nodeid, addr, port, appid, webSessionId, expire; + var userid, domainid, domain, nodeid, addr, port, appid, webSessionId, expire, publicid; const urlCookie = obj.parent.decodeCookie(req.query.c, parent.loginCookieEncryptionKey, 32); // Allow cookies up to 32 minutes old. The web page will renew this cookie every 30 minutes. if (urlCookie == null) { res.sendStatus(404); return; } @@ -6782,6 +6793,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF port = urlCookie.port; appid = (urlCookie.p == 16) ? 2 : 1; // appid: 1 = HTTP, 2 = HTTPS webSessionId = userid + '/' + urlCookie.pid; + publicid = urlCookie.pid; if (req.session.x) { delete req.session.x; } // Clear the web relay sessionid if (req.session.userid) { delete req.session.userid; } // Clear the web relay userid if (req.session.z != webSessionId) { req.session.z = webSessionId; } // Set the web relay guest session @@ -6854,6 +6866,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF // Create a web relay session const relaySession = require('./apprelays.js').CreateWebRelaySession(obj, db, req, args, domain, userid, nodeid, addr, port, appid, xrelaySessionId, expire); + relaySession.xpublicid = publicid; relaySession.onclose = function (sessionId) { // Remove the relay session delete webRelaySessions[sessionId];