More work on device self-sharing.

This commit is contained in:
Ylian Saint-Hilaire 2021-11-10 13:21:30 -08:00
parent d001699c8c
commit ecd25f3eb2
5 changed files with 35 additions and 23 deletions

View File

@ -1720,13 +1720,13 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if (serverName.split('.') == 1) { url = '/' + xdomain + page + '?c=' + inviteCookie; }
// Create a device sharing database entry
var shareEntry = { _id: 'deviceshare-' + publicid, type: 'deviceshare', nodeid: obj.dbNodeKey, p: flags, domain: domain.id, publicid: publicid, guestName: '*Agent', consent: true, url: url };
var shareEntry = { _id: 'deviceshare-' + publicid, type: 'deviceshare', nodeid: obj.dbNodeKey, p: flags, domain: domain.id, publicid: publicid, guestName: 'Agent', consent: 0x7F, url: url };
if (viewOnly === true) { shareEntry.viewOnly = true; }
parent.db.Set(shareEntry);
// Send out an event that we added a device share
var targets = parent.CreateNodeDispatchTargets(obj.dbMeshKey, obj.dbNodeKey);
var event = { etype: 'node', nodeid: obj.dbNodeKey, action: 'addedDeviceShare', msg: 'Added Device Share', msgid: 101, msgArgs: ['*Agent', 'DATETIME:' + null, 'DATETIME:' + null], domain: domain.id };
var event = { etype: 'node', nodeid: obj.dbNodeKey, action: 'addedDeviceShare', msg: 'Added device share with unlimited time', msgid: 131, msgArgs: ['Agent'], domain: domain.id };
parent.parent.DispatchEvent(targets, obj, event);
// Send device share update
@ -1773,7 +1773,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if (removedExact != null) {
// Send out an event that we removed a device share
var targets = parent.CreateNodeDispatchTargets(obj.dbMeshKey, obj.dbNodeKey, []);
var event = { etype: 'node', nodeid: obj.dbNodeKey, action: 'removedDeviceShare', msg: 'Removed Device Share', msgid: 102, msgArgs: ['*Agent'], domain: domain.id, publicid: publicid };
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);
}

View File

@ -84,6 +84,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
obj.user = user;
obj.ruserid = null;
obj.req = req; // Used in multi-server.js
if ((cookie != null) && (cookie.nouser == 1)) { obj.nouser = true; } // This is a relay without user authentication
// Setup subscription for desktop sharing public identifier
// If the identifier is removed, drop the connection
@ -96,11 +97,11 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
// Check relay authentication
if ((user == null) && (obj.req.query != null) && (obj.req.query.rauth != null)) {
const rcookie = parent.parent.decodeCookie(obj.req.query.rauth, parent.parent.loginCookieEncryptionKey, 240); // Cookie with 4 hour timeout
if (rcookie.ruserid != null) { obj.ruserid = rcookie.ruserid; }
if (rcookie.ruserid != null) { obj.ruserid = rcookie.ruserid; } else if (rcookie.nouser === 1) { obj.rnouser = true; }
}
// If there is no authentication, drop this connection
if ((obj.id != null) && (obj.id.startsWith('meshmessenger/') == false) && (obj.user == null) && (obj.ruserid == null)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Connection with no authentication (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } return; }
if ((obj.id != null) && (obj.id.startsWith('meshmessenger/') == false) && (obj.user == null) && (obj.ruserid == null) && (obj.nouser !== true) && (obj.rnouser !== true)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Connection with no authentication (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } return; }
// Relay session count (we may remove this in the future)
obj.relaySessionCounted = true;
@ -153,8 +154,8 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
obj.sendAgentMessage = function (command, userid, domainid) {
var rights, mesh;
if (command.nodeid == null) return false;
var user = parent.users[userid];
if (user == null) return false;
var user = null;
if (userid != null) { user = parent.users[userid]; if (user == null) return false; }
var splitnodeid = command.nodeid.split('/');
// Check that we are in the same domain and the user has rights over this node.
if ((splitnodeid[0] == 'node') && (splitnodeid[1] == domainid)) {
@ -163,15 +164,17 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
var agent = parent.wsagents[command.nodeid];
if (agent != null) {
// Check if we have permission to send a message to that node
rights = parent.GetNodeRights(user, agent.dbMeshKey, agent.dbNodeKey);
if (userid == null) { rights = MESHRIGHT_REMOTECONTROL; } else { rights = parent.GetNodeRights(user, agent.dbMeshKey, agent.dbNodeKey); }
mesh = parent.meshes[agent.dbMeshKey];
if ((rights != null) && (mesh != null) || ((rights & MESHRIGHT_REMOTECONTROL) != 0)) {
if (ws.sessionId) { command.sessionid = ws.sessionId; } // Set the session id, required for responses.
command.rights = rights; // Add user rights flags to the message
if (typeof command.consent == 'number') { command.consent = command.consent | mesh.consent; } else { command.consent = mesh.consent; } // Add user consent
if (typeof domain.userconsentflags == 'number') { command.consent |= domain.userconsentflags; } // Add server required consent flags
command.username = user.name; // Add user name
command.realname = user.realname; // Add real name
if (user != null) {
command.username = user.name; // Add user name
command.realname = user.realname; // Add real name
}
if (typeof domain.desktopprivacybartext == 'string') { command.privacybartext = domain.desktopprivacybartext; } // Privacy bar text
delete command.nodeid; // Remove the nodeid since it's implyed.
agent.send(JSON.stringify(command));
@ -182,15 +185,17 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
var routing = parent.parent.GetRoutingServerIdNotSelf(command.nodeid, 1); // 1 = MeshAgent routing type
if (routing != null) {
// Check if we have permission to send a message to that node
rights = parent.GetNodeRights(user, routing.meshid, command.nodeid);
if (userid == null) { rights = MESHRIGHT_REMOTECONTROL; } else { rights = parent.GetNodeRights(user, routing.meshid, command.nodeid); }
mesh = parent.meshes[routing.meshid];
if (rights != null || ((rights & MESHRIGHT_REMOTECONTROL) != 0)) {
if (ws.sessionId) { command.fromSessionid = ws.sessionId; } // Set the session id, required for responses.
command.rights = rights; // Add user rights flags to the message
if (typeof command.consent == 'number') { command.consent = command.consent | mesh.consent; } else { command.consent = mesh.consent; } // Add user consent
if (typeof domain.userconsentflags == 'number') { command.consent |= domain.userconsentflags; } // Add server required consent flags
command.username = user.name; // Add user name
command.realname = user.realname; // Add real name
if (user != null) {
command.username = user.name; // Add user name
command.realname = user.realname; // Add real name
}
if (typeof domain.desktopprivacybartext == 'string') { command.privacybartext = domain.desktopprivacybartext; } // Privacy bar text
parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
return true;
@ -285,7 +290,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
if (u1 != null) { u1 = 'user/' + domain.id + '/' + parent.args.user.toLowerCase(); }
if (u2 != null) { u2 = 'user/' + domain.id + '/' + parent.args.user.toLowerCase(); }
}
if (u1 != u2) {
if ((u1 != u2) && (obj.nouser !== true) && (relayinfo.peer1.nouser !== true)) {
ws.close();
parent.parent.debug('relay', 'Relay auth mismatch (' + u1 + ' != ' + u2 + '): ' + obj.id + ' (' + obj.req.clientIp + ')');
delete obj.id;
@ -829,7 +834,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
setExpireTimer();
// Mark this relay session as authenticated if this is the user end.
obj.authenticated = (user != null);
obj.authenticated = ((user != null) || (obj.nouser === true));
if (obj.authenticated) {
// To build the connection URL, if we are using a sub-domain or one with a DNS, we need to craft the URL correctly.
var xdomain = (domain.dns == null) ? domain.id : '';
@ -931,7 +936,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
const node = docs[0];
// Check if this user has permission to manage this computer
if ((parent.GetNodeRights(user, node.meshid, node._id) & MESHRIGHT_REMOTECONTROL) == 0) { console.log('ERR: Access denied (2)'); try { obj.close(); } catch (e) { } return; }
if ((obj.nouser !== true) && ((parent.GetNodeRights(user, node.meshid, node._id) & MESHRIGHT_REMOTECONTROL) == 0)) { console.log('ERR: Access denied (2)'); try { obj.close(); } catch (e) { } return; }
// Set nodeid and meshid
obj.nodeid = node._id;
@ -939,8 +944,10 @@ 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: '*/' + xdomain + 'meshrelay.ashx?p=' + obj.req.query.p + '&id=' + obj.id + '&rauth=' + rcookie + '&nodeid=' + node._id, soptions: {}, rights: cookie.r, guestname: cookie.gn, consent: cookie.cf, remoteaddr: cleanRemoteAddr(obj.req.clientIp) };
const rcookieData = { nodeid: node._id };
if (user != null) { rcookieData.ruserid = user._id; } else if (obj.nouser === true) { rcookieData.nouser = 1; }
const rcookie = parent.parent.encodeCookie(rcookieData, parent.parent.loginCookieEncryptionKey);
const command = { nodeid: node._id, action: 'msg', type: 'tunnel', value: '*/' + xdomain + 'meshrelay.ashx?p=' + obj.req.query.p + '&id=' + obj.id + '&rauth=' + rcookie + '&nodeid=' + node._id, soptions: {}, rights: cookie.r, guestname: cookie.gn, consent: cookie.cf, remoteaddr: cleanRemoteAddr(obj.req.clientIp) };
obj.guestname = cookie.gn;
// Limit what this relay connection can do
@ -966,7 +973,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
if (typeof domain.notificationmessages.files == 'string') { command.soptions.notifyMsgFiles = domain.notificationmessages.files; }
}
parent.parent.debug('relay', 'Relay: Sending agent tunnel command: ' + JSON.stringify(command));
if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
if (obj.sendAgentMessage(command, user?user._id:null, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
performRelay(0);
});

View File

@ -403,7 +403,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
else if (event == 'updatefiles') { updateUserFiles(user, ws, domain); }
else {
// If updating guest device shares, if we are updating a user that is not creator of the share, remove the URL.
if (event.action == 'deviceShareUpdate') {
if ((event.action == 'deviceShareUpdate') && (Array.isArray(event.deviceShares))) {
event = common.Clone(event);
for (var i in event.deviceShares) { if (event.deviceShares[i].userid != user._id) { delete event.deviceShares[i].url; } }
}

View File

@ -76,7 +76,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au
obj.xxOnControlCommand = function (msg) {
var controlMsg;
try { controlMsg = JSON.parse(msg); } catch (e) { return; }
if (controlMsg.ctrlChannel != '102938') { obj.m.ProcessData(msg); return; }
if (controlMsg.ctrlChannel != '102938') { if (obj.m.ProcessData) { obj.m.ProcessData(msg); } else { console.log(msg); } return; }
if ((typeof args != 'undefined') && args.redirtrace) { console.log('RedirRecv', controlMsg); }
if (controlMsg.type == 'console') {
obj.setConsoleMessage(controlMsg.msg, controlMsg.msgid, controlMsg.msgargs, controlMsg.timeout);

View File

@ -3584,6 +3584,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
const doc = docs[0];
// Generate an old style cookie from the information in the database
var cookie = { a: 5, p: doc.p, uid: doc.userid, gn: doc.guestName, nid: doc.nodeid, cf: doc.consent, pid: doc.publicid };
if ((cookie.userid == null) && (cookie.pid.startsWith('AS:node/'))) { cookie.nouser = 1; }
if ((doc.startTime != null) && (doc.expireTime != null)) { cookie.start = doc.startTime; cookie.expire = doc.expireTime; }
if (doc.viewOnly === true) { cookie.vo = 1; }
handleSharingRequestEx(req, res, domain, cookie);
@ -3618,7 +3619,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Looks good, let's create the outbound session cookies.
// Consent flags are 1 = Notify, 8 = Prompt, 64 = Privacy Bar.
const authCookie = obj.parent.encodeCookie({ userid: c.uid, domainid: domain.id, nid: c.nid, ip: req.clientIp, p: c.p, gn: c.gn, cf: c.cf, r: 8, expire: c.expire, pid: c.pid, vo: c.vo }, obj.parent.loginCookieEncryptionKey);
const authCookieData = { userid: c.uid, domainid: domain.id, nid: c.nid, ip: req.clientIp, p: c.p, gn: c.gn, cf: c.cf, r: 8, expire: c.expire, pid: c.pid, vo: c.vo };
if ((authCookieData.userid == null) && (authCookieData.pid.startsWith('AS:node/'))) { authCookieData.nouser = 1; }
const authCookie = obj.parent.encodeCookie(authCookieData, obj.parent.loginCookieEncryptionKey);
// Server features
var features2 = 0;
@ -3633,7 +3636,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
});
}
// Handle domain redirection
obj.handleDomainRedirect = function (req, res) {
const domain = checkUserIpAddress(req, res);
@ -6595,6 +6597,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} else if ((cookie != null) && (cookie.a === 3) && (typeof cookie.u == 'string') && (obj.users[cookie.u]) && (cookie.u.split('/')[1] == domain.id)) {
// Valid cookie, we are authenticated. Cookie of format { u: 'user//name', a: 3 }
func(ws, req, domain, obj.users[cookie.u], cookie);
} else if ((cookie != null) && (cookie.nouser === 1)) {
// This is a valid cookie, but no user. This is used for agent self-sharing.
func(ws, req, domain, null, cookie);
} else {
// This is a bad cookie, keep going anyway, maybe we have a active session that will save us.
if ((cookie != null) && (cookie.domainid != domain.id)) { parent.debug('web', 'ERR: Invalid domain, got \"' + cookie.domainid + '\", expected \"' + domain.id + '\".'); }