From 0ad256610c7d04974e24d8ad48f5518be87ee5d1 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Fri, 9 Apr 2021 13:27:21 -0700 Subject: [PATCH] Completed improved device guest sharing. --- agents/meshcore.js | 6 +++- meshrelay.js | 12 ++++++- meshuser.js | 23 +++++++++---- views/default.handlebars | 70 +++++++++++++++++++++++++++++----------- views/sharing.handlebars | 31 ++++++++++++++---- webserver.js | 14 +++++--- 6 files changed, 118 insertions(+), 38 deletions(-) diff --git a/agents/meshcore.js b/agents/meshcore.js index 27e48504..1d759b96 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -1608,6 +1608,10 @@ function onTunnelData(data) { if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; } this.httprequest.protocol = parseInt(data); if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; } + + // See if this protocol request is allowed. + if ((this.httprequest.soptions != null) && (this.httprequest.soptions.usages != null) && (this.httprequest.soptions.usages.indexOf(this.httprequest.protocol) == -1)) { this.httprequest.protocol = 0; } + if (this.httprequest.protocol == 10) { // // Basic file transfer @@ -1882,7 +1886,7 @@ function onTunnelData(data) { } else if (this.httprequest.protocol == 2) { // - // Remote KVM + // Remote Desktop // // Check user access rights for desktop diff --git a/meshrelay.js b/meshrelay.js index 72f997f3..6c94488a 100644 --- a/meshrelay.js +++ b/meshrelay.js @@ -771,7 +771,17 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) { // Send connection request to agent if (obj.id == null) { obj.id = ('' + Math.random()).substring(2); } const rcookie = parent.parent.encodeCookie({ ruserid: user._id, nodeid: node._id }, parent.parent.loginCookieEncryptionKey); - const command = { nodeid: node._id, action: 'msg', type: 'tunnel', userid: user._id, value: '*/meshrelay.ashx?p=' + cookie.p + '&id=' + obj.id + '&rauth=' + rcookie + '&nodeid=' + node._id, soptions: {}, usage: 2, rights: cookie.r, guestname: cookie.gn, consent: cookie.cf, remoteaddr: cleanRemoteAddr(obj.req.clientIp) }; + const command = { nodeid: node._id, action: 'msg', type: 'tunnel', userid: user._id, value: '*/meshrelay.ashx?p=' + cookie.p + '&id=' + obj.id + '&rauth=' + rcookie + '&nodeid=' + node._id, soptions: {}, rights: cookie.r, guestname: cookie.gn, consent: cookie.cf, remoteaddr: cleanRemoteAddr(obj.req.clientIp) }; + + // Limit what this relay connection can do + if (typeof cookie.p == 'number') { + var usages = []; + if (cookie.p & 1) { usages.push(1); usages.push(6); usages.push(8); usages.push(9); } // Terminal + if (cookie.p & 2) { usages.push(2); } // Desktop + if (cookie.p & 4) { usages.push(5); usages.push(10); } // Files + command.soptions.usages = usages; + } + if (typeof domain.consentmessages == 'object') { if (typeof domain.consentmessages.title == 'string') { command.soptions.consentTitle = domain.consentmessages.title; } if (typeof domain.consentmessages.desktop == 'string') { command.soptions.consentMsgDesktop = domain.consentmessages.desktop; } diff --git a/meshuser.js b/meshuser.js index 35f13296..1088b778 100644 --- a/meshuser.js +++ b/meshuser.js @@ -5015,7 +5015,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use else if ((command.start != null) && (typeof command.start != 'number')) { err = 'Invalid start time'; } // Check the start time in seconds else if ((command.end != null) && (typeof command.end != 'number')) { err = 'Invalid end time'; } // Check the end time in seconds else if (common.validateInt(command.consent, 0, 256) == false) { err = 'Invalid flags'; } // Check the flags - else if (common.validateInt(command.p, 1, 2) == false) { err = 'Invalid protocol'; } // Check the protocol, 1 = Terminal, 2 = Desktop + else if (common.validateInt(command.p, 1, 7) == false) { err = 'Invalid protocol'; } // Check the protocol, 1 = Terminal, 2 = Desktop, 4 = Files else if ((command.expire == null) && ((command.start == null) || (command.end == null) || (command.start > command.end))) { err = 'No time specified'; } // Check that a time range is present else { if (command.nodeid.split('/').length == 1) { command.nodeid = 'node/' + domain.id + '/' + command.nodeid; } @@ -5047,13 +5047,25 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } // If we are limited to no terminal, don't allow terminal sharing - if ((command.p == 1) && (rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_NOTERMINAL) != 0)) { + if (((command.p & 1) != 0) && (rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_NOTERMINAL) != 0)) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'deviceShares', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } } - return; + return; + } + + // If we are limited to no desktop, don't allow desktop sharing + if (((command.p & 2) != 0) && (rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_NODESKTOP) != 0)) { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'deviceShares', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } } + return; + } + + // If we are limited to no files, don't allow file sharing + if (((command.p & 4) != 0) && (rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_NOFILES) != 0)) { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'deviceShares', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } } + return; } // If we have view only remote desktop rights, force view-only on the guest share. - if ((rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_REMOTEVIEWONLY) != 0)) { command.viewOnly = true; } + if ((rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_REMOTEVIEWONLY) != 0)) { command.viewOnly = true; command.p = (command.p & 1); } // Create cookie var publicid = getRandomPassword(), startTime, expireTime; @@ -5079,8 +5091,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified var xdomain = (domain.dns == null) ? domain.id : ''; if (xdomain != '') xdomain += '/'; - var page = (command.p == 1) ? 'terminal' : 'desktop'; - var url = 'https://' + serverName + ':' + httpsPort + '/' + xdomain + page + '?c=' + inviteCookie; + var url = 'https://' + serverName + ':' + httpsPort + '/' + xdomain + 'sharing?c=' + inviteCookie; if (serverName.split('.') == 1) { url = '/' + xdomain + page + '?c=' + inviteCookie; } command.url = url; if (command.responseid != null) { command.result = 'OK'; } diff --git a/views/default.handlebars b/views/default.handlebars index 6eab24e6..a03228d3 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -626,7 +626,7 @@ - +
@@ -729,7 +729,7 @@ - +
'; setDialogMode(2, "Share Device", 1, null, x); break; } @@ -6524,12 +6525,13 @@ for (var i = 0; i < deviceShares.length; i++) { var dshare = deviceShares[i]; var trash = ' '; - var details = format("{0}, {1} to {2}", ((dshare.p == 1)?"Terminal":"Desktop"), printFlexDateTime(new Date(dshare.startTime)), printFlexDateTime(new Date(dshare.expireTime))); + var type = ['', "Terminal", "Desktop", "Desktop + Terminal", "Files", "Terminal + Files", "Desktop + Files", "Desktop + Terminal + Files"][dshare.p]; + var details = format("{0}, {1} to {2}", type, printFlexDateTime(new Date(dshare.startTime)), printFlexDateTime(new Date(dshare.expireTime))); if (dshare.viewOnly === true) { details += ", View only"; } if (dshare.consent != null) { if (dshare.consent == 0) { details += ", No Consent"; } else { - if (((dshare.consent & 8) != 0) || ((dshare.consent & 16) != 0)) { details += ", Prompt for consent"; } - if ((dshare.consent & 0x40) != 0) { details += ", Toolbar"; } + if ((dshare.consent & 0x0038) != 0) { details += ", Prompt for consent"; } + if ((dshare.consent & 0x0040) != 0) { details += ", Toolbar"; } } } x += '
 ' + dshare.guestName + '
' + trash + '
' + details + '
'; @@ -6731,8 +6733,26 @@ if ((rights != 0xFFFFFFFF) && ((rights & 0x100) != 0)) { deskFull = ''; } var fullTerm = ''; if ((rights != 0xFFFFFFFF) && ((rights & 0x200) != 0)) { fullTerm = ''; } - x += addHtmlValue("Type", ''); + var fullFiles = ''; + if ((rights != 0xFFFFFFFF) && ((rights & 0x400) != 0)) { fullFiles = ''; } + var deskFiles = ''; + if ((rights != 0xFFFFFFFF) && ((rights & 0x500) != 0)) { deskFiles = ''; } + var termFiles = ''; + if ((rights != 0xFFFFFFFF) && ((rights & 0x600) != 0)) { termFiles = ''; } + var allFeatures = ''; + if ((rights != 0xFFFFFFFF) && ((rights & 0x700) != 0)) { allFeatures = ''; } + + var y = ''; + if (currentNode.agent.caps & 1) { y += (deskFull + ''); } // Agent is desktop capable + if (currentNode.agent.caps & 2) { y += fullTerm; } // Agent is terminal capable + if (currentNode.agent.caps & 4) { y += fullFiles; } // Agent is files capable + if (currentNode.agent.caps & 5) { y += deskFiles; } // Agent is desktop + files capable + if (currentNode.agent.caps & 6) { y += termFiles; } // Agent is terminal + files capable + if (currentNode.agent.caps & 7) { y += allFeatures; } // Agent is desktop + terminal + files capable + + x += addHtmlValue("Type", ''); var options = { 1 : "1 minute", 5 : "5 minutes", 10 : "10 minutes", 15 : "15 minutes", 30 : "30 minutes", 45 : "45 minutes", 60 : "60 minutes", 120 : "2 hours", 240 : "4 hours", 480 : "8 hours", 720 : "12 hours", 960 : "16 hours", 1440 : "24 hours", 2880 : "2 days", 5760 : "4 days" } + y = ''; for (var i in options) { y += ''; } x += addHtmlValue("Validity", ''); x += '
'; @@ -6758,16 +6778,28 @@ } function showShareDeviceEx(b, tag) { - var consent = 0, p = parseInt(Q('d2shareType').value), viewOnly = false; - if (currentNode.agent.caps & 1) { - if (Q('d2shareType').value == 1) { if (Q('d2userConsent').value == 1) { consent = 18; } else { consent = 2; } } // Terminal Consent: 2 = Notify, 16 = Prompt - if (Q('d2shareType').value > 1) { if (Q('d2userConsent').value == 1) { consent = 73; } else { consent = 65; } } // Desktop Consent: 1 = Notify, 8 = Prompt, 64 = Privacy bar + var consent = 0, p = parseInt(Q('d2shareType').value), viewOnly = false, q = 0; + if (p == 3) { viewOnly = true; } + var q = [0, 1, 2, 2, 4, 6, 5, 7][p]; // Protocol flags: 1 = Terminal, 2 = Desktop, 4 = Files. + + if (q & 1) { + consent |= 0x0002; // Terminal notify + if (Q('d2userConsent').value == 1) { consent |= 0x0010; } // Terminal prompt for user consent } - if (p == 3) { p = 2; viewOnly = true; } + if (q & 2) { + consent |= 0x0001; // Desktop notify + consent |= 0x0040; // Desktop connection toolbar + if (Q('d2userConsent').value == 1) { consent |= 0x0008; } // Desktop prompt for user consent + } + if (q & 4) { + consent |= 0x0004; // Files notify + if (Q('d2userConsent').value == 1) { consent |= 0x0020; } // Files prompt for user consent + } + if (Q('d2timeRange').value == 0) { - meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: p, expire: parseInt(Q('d2inviteExpire').value), consent: consent, viewOnly: viewOnly }); + meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, expire: parseInt(Q('d2inviteExpire').value), consent: consent, viewOnly: viewOnly }); } else { - meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: p, start: Math.floor(tag.selectedDates[0].getTime() / 1000), end: Math.floor(tag.selectedDates[1].getTime() / 1000), consent: consent, viewOnly: viewOnly }); + meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, start: Math.floor(tag.selectedDates[0].getTime() / 1000), end: Math.floor(tag.selectedDates[1].getTime() / 1000), consent: consent, viewOnly: viewOnly }); } } diff --git a/views/sharing.handlebars b/views/sharing.handlebars index b5d070c6..77a3e344 100644 --- a/views/sharing.handlebars +++ b/views/sharing.handlebars @@ -30,7 +30,7 @@ {{{title}}} -
+
@@ -146,7 +146,7 @@
- +