From 873500a0af65c3a51837ab187ebad854738be009 Mon Sep 17 00:00:00 2001 From: Youngsoo Choi Date: Tue, 3 Aug 2021 01:49:22 -0700 Subject: [PATCH] [DeviceHome] Upgrade to v1.0.6 - Support message port communication between device home and web app instead of web socket to prevent smack security vulnerabilities - Support emulator connection with device home Change-Id: Ia23f954a8b2c5a6020490a3b8c47ae2120b56bb4 Signed-off-by: Youngsoo Choi --- device_home/client/js/myApps.js | 1 - device_home/pincode/js/pincode.js | 18 +-- device_home/service/relay-server.js | 285 ++++++++++++++++++------------------ device_home/service/service.js | 31 +++- 4 files changed, 173 insertions(+), 162 deletions(-) diff --git a/device_home/client/js/myApps.js b/device_home/client/js/myApps.js index 9ea6114..9e2b7e5 100755 --- a/device_home/client/js/myApps.js +++ b/device_home/client/js/myApps.js @@ -143,4 +143,3 @@ const myappsmodule = {}; export function openAppWindow(response) { myappsmodule.openAppWindow(response); }; - diff --git a/device_home/pincode/js/pincode.js b/device_home/pincode/js/pincode.js index fd03484..e284054 100755 --- a/device_home/pincode/js/pincode.js +++ b/device_home/pincode/js/pincode.js @@ -24,20 +24,20 @@ function init() { numbers = Array.prototype.slice.call(numbers); numbers.forEach(function(number, index) { number.addEventListener('click', function() { - number.className += ' grow'; + number.classList.add('grow'); if (number.innerHTML === '0') { input += 0; } else { input += index + 1; } - dots[input.length - 1].className += ' active'; + dots[input.length - 1].classList.add('active'); if (input.length >= 4) { console.log(`${TAG} input: ${input}`); sendPinCode(input); setTimeout(function() { dots.forEach(function(dot, index) { - dot.className = 'dot'; + dot.classList.add('dot'); }); input = ''; }, 900); @@ -46,7 +46,7 @@ function init() { }, 1000); } setTimeout(function() { - number.className = 'number'; + number.classList.add('number'); }, 1000); }); }); @@ -63,7 +63,7 @@ async function sendPinCode(data) { dots.forEach(function(dot, index) { dot.classList.add('wrong'); }); - document.body.className += ' wrong'; + document.body.classList.add('wrong'); setTimeout(function() { alert('Failed to input 5 times. A new Pincode has been generated, so check the TV notification.'); }, 1000); @@ -84,17 +84,17 @@ async function sendPinCode(data) { function chkPinCode(returnVal) { if (returnVal) { dots.forEach(function(dot, index) { - dot.className += ' correct'; + dot.classList.add('correct'); }); - document.body.className += ' correct'; + document.body.classList.add('correct'); setTimeout(function() { loginForm.submit(); }, 1000); } else { dots.forEach(function(dot, index) { - dot.className += ' wrong'; + dot.classList.add('wrong'); }); - document.body.className += ' wrong'; + document.body.classList.add('wrong'); } } diff --git a/device_home/service/relay-server.js b/device_home/service/relay-server.js index e912c4f..0063afc 100755 --- a/device_home/service/relay-server.js +++ b/device_home/service/relay-server.js @@ -1,4 +1,4 @@ -"use strict"; +'use strict'; const io = require('socket.io'); const JSEncryptLib = require('./jsencrypt'); const localClientIps = [ @@ -6,14 +6,18 @@ const localClientIps = [ '127.0.0.1' ]; const TAG = '[DeviceHome][relay-server.js]' +const TAG_HOST = `${TAG}[Host]`; +const TAG_CLIENT = `${TAG}[Client]`; const TO_ALL = 100; +let receiver = {}; +let sender = {}; let remoteClients = {}; -let localClients = {}; let localClientMessageQueues = {}; +let globalClientPublicKeys = {}; var getIp = function(rawIp) { - return rawIp.slice(rawIp.lastIndexOf(":") + 1); + return rawIp.slice(rawIp.lastIndexOf(':') + 1); } var decrypt = function(msg, ws) { const crypto = new JSEncryptLib(); @@ -31,143 +35,130 @@ var encrypt = function(msg, ws, pkgId, clientPublicKeys) { console.log(`${TAG}[encrypt] plain : ${msg}`); return serverCrypto.encrypt(msg); } +var OnReceived = function (msg) { + console.log(`${TAG_HOST}[OnReceived] msg[0].key : ${msg[0].key}`); + console.log(`${TAG_HOST}[OnReceived] msg[0].value : ${msg[0].value}`); + const pkgId = msg[0].key; + msg = JSON.parse(msg[0].value); + if (msg.type === 'AppId') { + const appId = msg.data; + console.log(`${TAG_HOST}[OnReceived] Initialize msg port sender : ${appId}`); + if (!sender[pkgId]) + sender[pkgId] = {}; + sender[pkgId].messagePort = tizen.messageport.requestRemoteMessagePort(appId, `${pkgId}.Companion`); + if (localClientMessageQueues[pkgId] !== undefined) { + while (localClientMessageQueues[pkgId].length) { + const msg = localClientMessageQueues[pkgId].shift(); + console.log(`${TAG_HOST} #################################`); + console.log(`${TAG_HOST} send pending msg : ${msg}`); + console.log(`${TAG_HOST} #################################`); + sender[pkgId].messagePort.sendMessage([{ + key: pkgId, + value: msg, + }]); + } + } + } else { + console.log(`${TAG_HOST}[OnReceived] Send message from msg port to web socket`); + if (!remoteClients || !remoteClients[pkgId].length) { + console.log(`${TAG_HOST}[OnReceived] There's no available web socket remote clients`); + return; + } + const id = msg.id; + if (id === -1) { + console.log(`${TAG_HOST}[OnReceived] Handle message only on relay server`); + // Do what need to handle in relay server only + return; + } + console.log(`${TAG_HOST}[OnReceived] id : ${id}`); + if (id === TO_ALL) { + console.log(`${TAG_HOST}[OnReceived] Send message to all web sockets`); + console.log(`${TAG_HOST}[OnReceived] remoteClients length : ${remoteClients[pkgId].length}`); + for (let client of remoteClients[pkgId]) { + const encrypted = encrypt(JSON.stringify(msg), client, pkgId, globalClientPublicKeys); + if (encrypted === false || encrypted === null) { + console.log(`${TAG_HOST}[OnReceived] Failed to encrypt message!`); + return; + } + console.log(`${TAG_HOST}[OnReceived] encrypted : ${encrypted}`); + client.emit('d2d_message', encrypted); + } + } else { + console.log(`${TAG_HOST}[OnReceived] Send message to a web socket #${id}`); + if (id < remoteClients[pkgId].length) { + const encrypted = encrypt(JSON.stringify(msg), remoteClients[pkgId][id], pkgId, globalClientPublicKeys); + if (encrypted === false || encrypted === null) { + console.log(`${TAG_HOST}[OnReceived] Failed to encrypt message!`); + return; + } + console.log(`${TAG_HOST}[OnReceived] encrypted : ${encrypted}`); + remoteClients[pkgId][id].emit('d2d_message', encrypted); + } + } + } +}; var addConnection = function(wsServer, pkgId, sessionMiddleware, clientPublicKeys) { + globalClientPublicKeys = clientPublicKeys; + // Host + console.log(`${TAG_HOST} Initialize msg port recevier : ${pkgId}.Companion`); + if (!receiver[pkgId]) + receiver[pkgId] = {}; + receiver[pkgId].messagePort = tizen.messageport.requestLocalMessagePort(`${pkgId}.Companion`); + receiver[pkgId].messagePortListener = receiver[pkgId].messagePort.addMessagePortListener(OnReceived.bind(this)); + + // Remote Client + console.log(`${TAG_CLIENT} Initialize client web socket`); const namespace = wsServer.of(`/${pkgId}`); namespace.use((socket, next) => { sessionMiddleware(socket.request, {}, next); }); namespace.on('connection', ws => { - console.log(`${TAG} connection event : ${pkgId}`); + console.log(`${TAG_CLIENT} connection event : ${pkgId}`); // In case of local client, remoteAddress will be ::1 // In case of remote client, remoteAddress will be ::ffff:ipaddress // e.g.) ::ffff:192.168.0.21 - console.log(`${TAG} address : ${ws.handshake.address}`); - console.log(`${TAG} url : ${ws.handshake.url}`); - console.log(`${TAG} session id : ${ws.request.session.id}`); + console.log(`${TAG_CLIENT} address : ${ws.handshake.address}`); + console.log(`${TAG_CLIENT} url : ${ws.handshake.url}`); + console.log(`${TAG_CLIENT} session id : ${ws.request.session.id}`); if (remoteClients[pkgId] === undefined) remoteClients[pkgId] = []; - if (localClientIps.includes(getIp(ws.handshake.address))) { - const TAG_LOCAL = `${TAG}[Local]`; - console.log(`${TAG_LOCAL} connected from local`); - localClients[pkgId] = ws; - console.log(`${TAG_LOCAL} remote length : ${remoteClients[pkgId].length}`); - - if (!remoteClients[pkgId].length) { - console.log(`${TAG_LOCAL} connected : no remote-clients`); - } - if (localClientMessageQueues[pkgId] !== undefined) { - while (localClientMessageQueues[pkgId].length) { - const msg = localClientMessageQueues[pkgId].shift(); - console.log(`${TAG_LOCAL} #################################`); - console.log(`${TAG_LOCAL} send pending msg : ${msg}`); - console.log(`${TAG_LOCAL} #################################`); - ws.emit('d2d_message', msg); - } - } - ws.on('d2d_message', function(msg) { - console.log(`${TAG_LOCAL} #################################`); - console.log(`${TAG_LOCAL} on d2d message : ${msg}`); - console.log(`${TAG_LOCAL} #################################`); - - let myPkgId = null; - for (let key in localClients) { - if (localClients[key] === ws) { - myPkgId = key; - break; - } - } - - let id = JSON.parse(msg).id; - console.log(`${TAG_LOCAL} id : ${id}`); - if (id == -1) { - // Do what need to handle in relay server only - return; - } - if (!remoteClients[myPkgId].length) - return; - if (id == TO_ALL) { - console.log(`${TAG_LOCAL} remoteClients length : ${remoteClients[myPkgId].length}`); - for (let client of remoteClients[myPkgId]) { - const encrypted = encrypt(msg, client, myPkgId, clientPublicKeys); - if (encrypted === false || encrypted === null) { - console.log("Error: Failed to encrypt message"); - return; - } - console.log(`${TAG_LOCAL} encrypted : ${encrypted}`); - client.emit('d2d_message', encrypted); - } - } else { - console.log(`${TAG_LOCAL} id : ${id}`); - if (id < remoteClients[myPkgId].length) { - msg = encrypt(msg, remoteClients[myPkgId][id], myPkgId, clientPublicKeys); - if (msg === false || msg === null) { - console.log("Error: Failed to encrypt message"); - return; - } - console.log(`${TAG_LOCAL} encrypted : ${msg}`); - remoteClients[myPkgId][id].emit('d2d_message', msg); - } - } - }); - ws.on('disconnect', function(msg) { - console.log(`${TAG_LOCAL}[close] #################################`); - console.log(`${TAG_LOCAL}[close] on disconnect event : ${msg}`); - console.log(`${TAG_LOCAL}[close] #################################`); - let myPkgId = null; - for (let key in localClients) { - if (localClients[key] === ws) { - myPkgId = key; - break; - } - } - localClients[myPkgId] = undefined; - console.log(`${TAG_LOCAL}[close] remoteClients[${myPkgId}] : ${remoteClients[myPkgId]}`); - for (let remoteClient of remoteClients[myPkgId]) { - console.log(`${TAG}[close local client] sent local_client_disconnect`); - let disconnection = JSON.stringify({ - type: "local_client_disconnect", - data: { - totalcount: localClients.length, - id: 0 - }, - id: -1 - }); - disconnection = encrypt(disconnection, remoteClient, myPkgId, clientPublicKeys); - if (disconnection === false || disconnection === null) { - console.log("Error: Failed to encrypt message"); - return; - } - console.log(`${TAG_LOCAL} encrypted : ${disconnection}`); - remoteClient.emit('d2d_message', disconnection); - } - }); - } else { - const TAG_REMOTE = `${TAG}[Remote]`; + if (!localClientIps.includes(getIp(ws.handshake.address))) { const ip = getIp(ws.handshake.address); - console.log(`${TAG_REMOTE} connected from ${ip}`); - if (remoteClients[pkgId].indexOf(ws) == -1) { + console.log(`${TAG_CLIENT} connected from ${ip}`); + if (remoteClients[pkgId].indexOf(ws) === -1) { remoteClients[pkgId].push(ws); const index = remoteClients[pkgId].length - 1; - console.log(`${TAG_REMOTE} pkgId : ${pkgId}`); - console.log(`${TAG_REMOTE} remote length : ${remoteClients[pkgId].length}`); - console.log(`${TAG_REMOTE} local length : ${Object.keys(localClients).length}`); + console.log(`${TAG_CLIENT} pkgId : ${pkgId}`); + console.log(`${TAG_CLIENT} client length : ${remoteClients[pkgId].length}`); + console.log(`${TAG_CLIENT} host length : ${Object.keys(sender).length}`); - let res = JSON.stringify({type: "id", data: index, id: -1}); - res = encrypt(res, ws, pkgId, clientPublicKeys); - console.log(`${TAG_REMOTE} encrypted : ${res}`); - if (res === false || res === null) { - console.log("Error: Failed to encrypt message"); + const res = JSON.stringify({type: 'id', data: index, id: -1}); + const encrypted = encrypt(res, ws, pkgId, clientPublicKeys); + console.log(`${TAG_CLIENT} encrypted : ${encrypted}`); + if (encrypted === false || encrypted === null) { + console.log('Error: Failed to encrypt message'); return; } - ws.emit('d2d_message', res); + ws.emit('d2d_message', encrypted); + if (sender[pkgId]) { + sender[pkgId].messagePort.sendMessage([{ + key: pkgId, + value: res, + }]); + } - console.log(`${TAG_REMOTE}[connection] localClients.length : ${localClients.length}`); - if (localClients[pkgId] === undefined || Object.keys(localClients).length === 0) { - console.log(`${TAG_REMOTE} connected : no local-client`); + console.log(`${TAG_CLIENT}[connection] sender.length : ${sender.length}`); + if (sender[pkgId] === undefined || Object.keys(sender).length === 0) { + console.log(`${TAG_CLIENT} connected : waiting for host`); } else { - localClients[pkgId].emit('d2d_message', JSON.stringify({type: "new_client", data: index, id: -1})); + if (sender[pkgId]) { + sender[pkgId].messagePort.sendMessage([{ + key: pkgId, + value: JSON.stringify({type: 'new_client', data: index, id: -1}), + }]); + } } } ws.on('d2d_message', function(msg) { @@ -178,32 +169,35 @@ var addConnection = function(wsServer, pkgId, sessionMiddleware, clientPublicKey break; } } - console.log(`${TAG_REMOTE} #################################`); - console.log(`${TAG_REMOTE} on d2d message : ${msg}`); + console.log(`${TAG_CLIENT} #################################`); + console.log(`${TAG_CLIENT} on d2d message : ${msg}`); msg = decrypt(msg, ws); - console.log(`${TAG_REMOTE} #################################`); + console.log(`${TAG_CLIENT} #################################`); if (msg === false || msg === null) { - console.log("Error: Failed to decrypt message"); + console.log('Error: Failed to decrypt message'); return; } - if (localClients[myPkgId]) { - console.log(`${TAG_REMOTE} decrypted : ${msg}`); - localClients[myPkgId].emit('d2d_message', msg); + if (sender[myPkgId]) { + console.log(`${TAG_CLIENT} decrypted : ${msg}`); + sender[myPkgId].messagePort.sendMessage([{ + key: myPkgId, + value: msg, + }]); } else { - console.log(`${TAG_REMOTE} #################################`); - console.log(`${TAG_REMOTE} localClient is not ready`); - console.log(`${TAG_REMOTE} #################################`); + console.log(`${TAG_CLIENT} #################################`); + console.log(`${TAG_CLIENT} localClient is not ready`); + console.log(`${TAG_CLIENT} #################################`); if (localClientMessageQueues[myPkgId] === undefined) localClientMessageQueues[myPkgId] = []; localClientMessageQueues[myPkgId].push(msg); - console.log(`${TAG_REMOTE} localClient is not ready : ${localClientMessageQueues[myPkgId]}`); + console.log(`${TAG_CLIENT} localClient is not ready : ${localClientMessageQueues[myPkgId]}`); } }); ws.on('disconnect', function(msg) { - console.log(`${TAG_REMOTE}[close] #################################`); - console.log(`${TAG_REMOTE}[close] on disconnect event : ${msg}`); - console.log(`${TAG_REMOTE}[close] #################################`); + console.log(`${TAG_CLIENT}[close] #################################`); + console.log(`${TAG_CLIENT}[close] on disconnect event : ${msg}`); + console.log(`${TAG_CLIENT}[close] #################################`); let myPkgId = null; for (let key in remoteClients) { if (remoteClients[key].indexOf(ws) != -1) { @@ -215,17 +209,20 @@ var addConnection = function(wsServer, pkgId, sessionMiddleware, clientPublicKey let index = remoteClients[myPkgId].indexOf(ws); remoteClients[myPkgId][index] = undefined; remoteClients[myPkgId].splice(index, 1); - console.log(`${TAG_REMOTE}[close] localClients[${myPkgId}] : ${localClients[myPkgId]}`); - if (localClients[myPkgId] !== undefined) { - console.log(`${TAG_REMOTE}[close] sent remote_client_disconnect`); - localClients[myPkgId].emit('d2d_message', JSON.stringify({ - type: "remote_client_disconnect", - data: { - totalcount: remoteClients[myPkgId].length, - id: index - }, - id: -1 - })); + console.log(`${TAG_CLIENT}[close] sender[${myPkgId}] : ${sender[myPkgId]}`); + if (sender[myPkgId] !== undefined) { + console.log(`${TAG_CLIENT}[close] sent remote_client_disconnect to host`); + sender[myPkgId].messagePort.sendMessage([{ + key: pkgId, + value: JSON.stringify({ + type: 'remote_client_disconnect', + data: { + totalcount: remoteClients[myPkgId].length, + id: index + }, + id: -1 + }), + }]); } }); } diff --git a/device_home/service/service.js b/device_home/service/service.js index c7206f6..8e2882d 100755 --- a/device_home/service/service.js +++ b/device_home/service/service.js @@ -26,11 +26,14 @@ const TIZEN_WEB_APP_SHARED_RESOURCES = 'shared/res/'; const WEBCLIP_DIRECTORY = 'webclip'; const WEBCLIP_MANIFEST = 'manifest.json'; const is_tv = webapis.cachedProperty !== undefined; +const emulator_ip = '10.0.2.15'; +const local_ip = '127.0.0.1'; const non_ip_list = [ '1', - '127.0.0.1', - '192.168.250.250' + '192.168.250.250', + local_ip ] + const security = new Security(); var apps = []; @@ -194,8 +197,18 @@ function getWebClipsList() { } function sendLoginIdAndDeviceName(login_id, device_ip) { - const device_name = webapis.mde.getDeviceName(); - console.log(`${TAG} login_id = ${login_id}, device_name = ${device_name}`); + let device_name = 'Tizen'; + if (device_ip === emulator_ip) { + device_ip = `${local_ip}:${g.port}`; + device_name = 'Tizen Emulator'; + // Skip pin-code for emulator + DEMO_MODE = true; + } else if (typeof webapis.mde !== 'undefined') { + device_name = webapis.mde.getDeviceName(); + } + console.log(`${TAG} login_id : ${login_id}`); + console.log(`${TAG} device_ip : ${device_ip}`); + console.log(`${TAG} device_name : ${device_name}`); const xhr = new XMLHttpRequest(); const keyVal = { @@ -223,7 +236,7 @@ function sendLoginIdAndDeviceName(login_id, device_ip) { function updateDNSresolver(device_ip) { console.log(`${TAG} Server is listening on ${device_ip}:${g.port}`); let login_id = 'stester81@gmail.com'; - if (is_tv) + if (is_tv && typeof webapis.mde !== 'undefined') login_id = webapis.mde.getCurrentLoginId(); sendLoginIdAndDeviceName(login_id, device_ip); } @@ -368,7 +381,7 @@ var HTTPserverStart = function() { res.sendFile(file, options, function(err) { if (err) { console.log(`${TAG} err: ${err}`); - res.send("err", err); + res.status('err').send(err); } else { console.log(`${TAG} res.sendFile() done: ${file}`); } @@ -410,7 +423,8 @@ var HTTPserverStart = function() { // FIXME: Remove app logic here res.render("client/invited.html"); } else if (req.query.pageUrl !== undefined) { - webapis.mde.launchBrowserFromUrl(req.query.pageUrl); + if (typeof webapis.mde !== 'undefined') + webapis.mde.launchBrowserFromUrl(req.query.pageUrl); } else { // Device home res.render("client/client.html"); @@ -485,7 +499,8 @@ var HTTPserverStart = function() { }); app.post('/url', (req, res) => { - webapis.mde.launchBrowserFromUrl(req.body.url); + if (typeof webapis.mde !== 'undefined') + webapis.mde.launchBrowserFromUrl(req.body.url); }); httpserver = http.createServer(app); -- 2.7.4