Added option to save SSH username and key without saving key password.

Ylian Saint-Hilaire committed May 10, 2022 at 17:44 UTC 0d297088c896436af59feceefbc393292e9e1c4b
7 files changed +328 -93
apprelays.js
+102 -23
@@ -447,9 +447,9 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
447 };
448
449 // Save SSH credentials into device
450 - function saveSshCredentials() {
451 - if (domain.allowsavingdevicecredentials == false) return;
452 - parent.parent.db.Get(obj.cookie.nodeid, function (err, nodes) {
450 + function saveSshCredentials(keep) {
451 + if (((keep != 1) && (keep != 2)) || (domain.allowsavingdevicecredentials == false)) return;
452 + parent.parent.db.Get(obj.nodeid, function (err, nodes) {
453 if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
454 const node = nodes[0];
455 const changed = (node.ssh == null);
@@ -461,16 +461,17 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
461 if (obj.password != null) {
462 node.ssh = { u: obj.username, p: obj.password };
463 } else if (obj.privateKey != null) {
464 - node.ssh = { u: obj.username, k: obj.privateKey, kp: obj.privateKeyPass };
464 + node.ssh = { u: obj.username, k: obj.privateKey };
465 + if (keep == 2) { node.ssh.kp = obj.privateKeyPass; }
466 } else return;
467 parent.parent.db.Set(node);
468
469 // Event node change if needed
470 if (changed) {
471 // Event the node change
471 - const event = { etype: 'node', action: 'changenode', nodeid: obj.cookie.nodeid, domain: domain.id, userid: obj.cookie.userid, node: parent.CloneSafeNode(node), msg: "Changed SSH credentials" };
472 + const event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: obj.userid, node: parent.CloneSafeNode(node), msg: "Changed SSH credentials" };
473 if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come.
473 - parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.cookie.nodeid]), obj, event);
474 + parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event);
475 }
476 });
477 }
@@ -498,7 +499,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
499 obj.sshClient = new Client();
500 obj.sshClient.on('ready', function () { // Authentication was successful.
501 // If requested, save the credentials
501 - if (obj.keep === true) saveSshCredentials();
502 + saveSshCredentials(obj.keep);
503 obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64');
504 obj.startTime = Date.now();
505
@@ -590,10 +591,15 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
591 if ((domain.allowsavingdevicecredentials === false) || (node.ssh == null) || (typeof node.ssh != 'object') || (typeof node.ssh.u != 'string') || ((typeof node.ssh.p != 'string') && (typeof node.ssh.k != 'string'))) {
592 // Send a request for SSH authentication
593 try { ws.send(JSON.stringify({ action: 'sshauth' })) } catch (ex) { }
594 + } else if ((domain.allowsavingdevicecredentials !== false) && (node.ssh != null) && (typeof node.ssh.k == 'string') && (node.ssh.kp == null)) {
595 + // Send a request for SSH authentication with option for only the private key password
596 + obj.username = node.ssh.u;
597 + obj.privateKey = node.ssh.k;
598 + try { ws.send(JSON.stringify({ action: 'sshauth', askkeypass: true })) } catch (ex) { }
599 } else {
600 // Use our existing credentials
601 obj.termSize = msg;
596 - obj.keep = false;
602 + delete obj.keep;
603 obj.username = node.ssh.u;
604 if (typeof node.ssh.p == 'string') {
605 obj.password = node.ssh.p;
@@ -610,7 +616,8 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
616 if ((typeof msg.rows != 'number') || (typeof msg.cols != 'number') || (typeof msg.height != 'number') || (typeof msg.width != 'number')) break;
617
618 obj.termSize = msg;
613 - obj.keep = msg.keep; // If true, keep store credentials on the server if the SSH tunnel connected succesfully.
619 + if (msg.keep === true) { msg.keep = 1; } // If true, change to 1. For user/pass, 1 to store user/pass in db. For user/key/pass, 1 to store user/key in db, 2 to store everything in db.
620 + obj.keep = msg.keep; // If set, keep store credentials on the server if the SSH tunnel connected succesfully.
621 obj.username = msg.username;
622 obj.password = msg.password;
623 obj.privateKey = msg.key;
@@ -619,6 +626,24 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
626 }
627 break;
628 }
629 + case 'connectKeyPass': {
630 + // Verify inputs
631 + if (typeof msg.keypass != 'string') break;
632 +
633 + // Check if we have SSH credentials for this device
634 + obj.privateKeyPass = msg.keypass;
635 + obj.termSize = msg;
636 + parent.parent.db.Get(obj.cookie.nodeid, function (err, nodes) {
637 + if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
638 + const node = nodes[0];
639 + if (node.ssh != null) {
640 + obj.username = node.ssh.u;
641 + obj.privateKey = node.ssh.k;
642 + startRelayConnection();
643 + }
644 + });
645 + break;
646 + }
647 case 'resize': {
648 // Verify inputs
649 if ((typeof msg.rows != 'number') || (typeof msg.cols != 'number') || (typeof msg.height != 'number') || (typeof msg.width != 'number')) break;
@@ -752,8 +777,8 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
777 };
778
779 // Save SSH credentials into device
755 - function saveSshCredentials() {
756 - if (domain.allowsavingdevicecredentials == false) return;
780 + function saveSshCredentials(keep) {
781 + if (((keep != 1) && (keep != 2)) || (domain.allowsavingdevicecredentials == false)) return;
782 parent.parent.db.Get(obj.nodeid, function (err, nodes) {
783 if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
784 const node = nodes[0];
@@ -766,8 +791,9 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
791 if (obj.password != null) {
792 node.ssh = { u: obj.username, p: obj.password };
793 } else if (obj.privateKey != null) {
769 - node.ssh = { u: obj.username, k: obj.privateKey, kp: obj.privateKeyPass };
770 - }
794 + node.ssh = { u: obj.username, k: obj.privateKey };
795 + if (keep == 2) { node.ssh.kp = obj.privateKeyPass; }
796 + } else return;
797 parent.parent.db.Set(node);
798
799 // Event node change if needed
@@ -803,7 +829,7 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
829 obj.sshClient = new Client();
830 obj.sshClient.on('ready', function () { // Authentication was successful.
831 // If requested, save the credentials
806 - if (obj.keep === true) saveSshCredentials();
832 + saveSshCredentials(obj.keep);
833 obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64');
834 obj.startTime = Date.now();
835
@@ -848,7 +874,7 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
874 obj.relayActive = false;
875 delete obj.sshClient;
876 delete obj.ser.forwardwrite;
851 - try { ws.send(JSON.stringify({ action: 'sshauth' })) } catch (ex) { }
877 + try { ws.send(JSON.stringify({ action: 'sshauth', askkeypass: ((obj.username != null) && (obj.privateKey != null)) })) } catch (ex) { }
878 }
879
880 // We are all set, start receiving data
@@ -894,7 +920,8 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
920 if ((typeof msg.username != 'string') || ((typeof msg.password != 'string') && (typeof msg.key != 'string'))) break;
921 if ((typeof msg.rows != 'number') || (typeof msg.cols != 'number') || (typeof msg.height != 'number') || (typeof msg.width != 'number')) break;
922
897 - obj.keep = msg.keep; // If true, keep store credentials on the server if the SSH tunnel connected succesfully.
923 + if (msg.keep === true) { msg.keep = 1; } // If true, change to 1. For user/pass, 1 to store user/pass in db. For user/key/pass, 1 to store user/key in db, 2 to store everything in db.
924 + obj.keep = msg.keep; // If set, keep store credentials on the server if the SSH tunnel connected succesfully.
925 obj.termSize = msg;
926 obj.username = msg.username;
927 obj.password = msg.password;
@@ -912,6 +939,26 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
939 startRelayConnection(parent.parent.encodeCookie(cookieContent, parent.parent.loginCookieEncryptionKey));
940 break;
941 }
942 + case 'sshkeyauth': {
943 + // Verify inputs
944 + if (typeof msg.keypass != 'string') break;
945 + if ((typeof msg.rows != 'number') || (typeof msg.cols != 'number') || (typeof msg.height != 'number') || (typeof msg.width != 'number')) break;
946 +
947 + delete obj.keep;
948 + obj.termSize = msg;
949 + obj.privateKeyPass = msg.keypass;
950 +
951 + // Create a mesh relay authentication cookie
952 + const cookieContent = { userid: user._id, domainid: user.domain, nodeid: obj.nodeid, tcpport: obj.tcpport };
953 + if (obj.relaynodeid) {
954 + cookieContent.nodeid = obj.relaynodeid;
955 + cookieContent.tcpaddr = obj.tcpaddr;
956 + } else {
957 + if (obj.mtype == 3) { cookieContent.lc = 1; } // This is a local device
958 + }
959 + startRelayConnection(parent.parent.encodeCookie(cookieContent, parent.parent.loginCookieEncryptionKey));
960 + break;
961 + }
962 case 'sshautoauth': {
963 // Verify inputs
964 if ((typeof msg.rows != 'number') || (typeof msg.cols != 'number') || (typeof msg.height != 'number') || (typeof msg.width != 'number')) break;
@@ -984,6 +1031,11 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
1031 if ((domain.allowsavingdevicecredentials === false) || (node.ssh == null) || (typeof node.ssh != 'object') || (typeof node.ssh.u != 'string') || ((typeof node.ssh.p != 'string') && (typeof node.ssh.k != 'string'))) {
1032 // Send a request for SSH authentication
1033 try { ws.send(JSON.stringify({ action: 'sshauth' })) } catch (ex) { }
1034 + } else if ((typeof node.ssh.k == 'string') && (typeof node.ssh.kp != 'string')) {
1035 + // Send a request for SSH authentication with option for only the private key password
1036 + obj.username = node.ssh.u;
1037 + obj.privateKey = node.ssh.k;
1038 + try { ws.send(JSON.stringify({ action: 'sshauth', askkeypass: true })) } catch (ex) { }
1039 } else {
1040 // Use our existing credentials
1041 obj.username = node.ssh.u;
@@ -1071,8 +1123,8 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
1123 };
1124
1125 // Save SSH credentials into device
1074 - function saveSshCredentials() {
1075 - if (domain.allowsavingdevicecredentials == false) return;
1126 + function saveSshCredentials(keep) {
1127 + if (((keep != 1) && (keep != 2)) || (domain.allowsavingdevicecredentials == false)) return;
1128 parent.parent.db.Get(obj.nodeid, function (err, nodes) {
1129 if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
1130 const node = nodes[0];
@@ -1085,8 +1137,9 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
1137 if (obj.password != null) {
1138 node.ssh = { u: obj.username, p: obj.password };
1139 } else if (obj.privateKey != null) {
1088 - node.ssh = { u: obj.username, k: obj.privateKey, kp: obj.privateKeyPass };
1089 - }
1140 + node.ssh = { u: obj.username, k: obj.privateKey };
1141 + if (keep == 2) { node.ssh.kp = obj.privateKeyPass; }
1142 + } else return;
1143 parent.parent.db.Set(node);
1144
1145 // Event node change if needed
@@ -1122,7 +1175,7 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
1175 obj.sshClient = new Client();
1176 obj.sshClient.on('ready', function () { // Authentication was successful.
1177 // If requested, save the credentials
1125 - if (obj.keep === true) saveSshCredentials();
1178 + saveSshCredentials(obj.keep);
1179 obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64');
1180 obj.startTime = Date.now();
1181
@@ -1161,7 +1214,7 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
1214 obj.relayActive = false;
1215 delete obj.sshClient;
1216 delete obj.ser.forwardwrite;
1164 - try { ws.send(JSON.stringify({ action: 'sshauth' })) } catch (ex) { }
1217 + try { ws.send(JSON.stringify({ action: 'sshauth', askkeypass: ((obj.username != null) && (obj.privateKey != null)) })) } catch (ex) { }
1218 }
1219
1220 // We are all set, start receiving data
@@ -1390,12 +1443,33 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
1443 // Verify inputs
1444 if ((typeof msg.username != 'string') || ((typeof msg.password != 'string') && (typeof msg.key != 'string'))) break;
1445
1393 - obj.keep = (msg.keep === true); // If true, keep store credentials on the server if the SSH tunnel connected succesfully.
1446 + if (msg.keep === true) { msg.keep = 1; } // If true, change to 1. For user/pass, 1 to store user/pass in db. For user/key/pass, 1 to store user/key in db, 2 to store everything in db.
1447 + obj.keep = msg.keep; // If set, keep store credentials on the server if the SSH tunnel connected succesfully.
1448 obj.username = msg.username;
1449 obj.password = msg.password;
1450 obj.privateKey = msg.key;
1451 obj.privateKeyPass = msg.keypass;
1452
1453 + // Create a mesh relay authentication cookie
1454 + const cookieContent = { userid: user._id, domainid: user.domain, nodeid: obj.nodeid, tcpport: obj.tcpport };
1455 + if (obj.relaynodeid) {
1456 + cookieContent.nodeid = obj.relaynodeid;
1457 + cookieContent.tcpaddr = obj.tcpaddr;
1458 + } else {
1459 + if (obj.mtype == 3) { cookieContent.lc = 1; } // This is a local device
1460 + }
1461 + startRelayConnection(parent.parent.encodeCookie(cookieContent, parent.parent.loginCookieEncryptionKey));
1462 + break;
1463 + }
1464 + case 'sshkeyauth': {
1465 + if (obj.sshClient != null) return;
1466 +
1467 + // Verify inputs
1468 + if (typeof msg.keypass != 'string') break;
1469 +
1470 + delete obj.keep;
1471 + obj.privateKeyPass = msg.keypass;
1472 +
1473 // Create a mesh relay authentication cookie
1474 const cookieContent = { userid: user._id, domainid: user.domain, nodeid: obj.nodeid, tcpport: obj.tcpport };
1475 if (obj.relaynodeid) {
@@ -1479,6 +1553,11 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
1553 if ((domain.allowsavingdevicecredentials === false) || (node.ssh == null) || (typeof node.ssh != 'object') || (typeof node.ssh.u != 'string') || ((typeof node.ssh.p != 'string') && (typeof node.ssh.k != 'string'))) {
1554 // Send a request for SSH authentication
1555 try { ws.send(JSON.stringify({ action: 'sshauth' })) } catch (ex) { }
1556 + } else if ((typeof node.ssh.k == 'string') && (typeof node.ssh.kp != 'string')) {
1557 + // Send a request for SSH authentication with option for only the private key password
1558 + obj.username = node.ssh.u;
1559 + obj.privateKey = node.ssh.k;
1560 + try { ws.send(JSON.stringify({ action: 'sshauth', askkeypass: true })) } catch (ex) { }
1561 } else {
1562 // Use our existing credentials
1563 obj.username = node.ssh.u;
meshuser.js
+10 -1
@@ -729,7 +729,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
729 if (docs[i].pmt != null) { docs[i].pmt = 1; }
730
731 // Remove SSH credentials if present
732 - if (docs[i].ssh != null) { docs[i].ssh = (docs[i].ssh.k) ? 2 : 1; }
732 + if (docs[i].ssh != null) {
733 + if (docs[i].ssh.u) {
734 + if (docs[i].ssh.k && docs[i].ssh.kp) { docs[i].ssh = 2; } // Username, key and password
735 + else if (docs[i].ssh.k) { docs[i].ssh = 3; } // Username and key. No password.
736 + else if (docs[i].ssh.p) { docs[i].ssh = 1; } // Username and password
737 + else { delete docs[i].ssh; }
738 + } else {
739 + delete docs[i].ssh;
740 + }
741 + }
742
743 // Remove RDP credentials if present
744 if (docs[i].rdp != null) { docs[i].rdp = 1; }
views/default-mobile.handlebars
+99 -4
@@ -3528,10 +3528,10 @@
3528 if ((node.ssh != null) || (node.rdp != null)) {
3529 var y = [];
3530 if ((meshrights & 4) != 0) {
3531 - if (node.ssh != null) { y.push('<span onclick=showClearSshDialog(3) style=cursor:pointer>' + ((node.ssh == 2) ? "SSH-Key" : "SSH") + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
3531 + if (node.ssh != null) { y.push('<span onclick=showClearSshDialog(3) style=cursor:pointer>' + ((node.ssh == 1) ? "SSH-User+Pass" : ((node.ssh == 2) ? "SSH-User+Key+Pass" : "SSH-User+Key")) + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
3532 if (node.rdp != null) { y.push('<span onclick=showClearRdpDialog(3) style=cursor:pointer>' + "RDP" + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
3533 } else {
3534 - if (node.ssh != null) { y.push(((node.ssh == 2) ? "SSH-Key" : "SSH")); }
3534 + if (node.ssh != null) { y.push(((node.ssh == 1) ? "SSH-User+Pass" : ((node.ssh == 2) ? "SSH-User+Key+Pass" : "SSH-User+Key"))); }
3535 if (node.rdp != null) { y.push("RDP"); }
3536 }
3537 x += addDeviceAttribute("Credentials", y.join(', '));
@@ -4750,12 +4750,46 @@
4750
4751 function tunnelUpdate(data) { if (typeof data == 'string') { xterm.writeUtf8(data); } else { xterm.writeUtf8(new Uint8Array(data)); } }
4752
4753 + function sshTunnelAuthDialog(j, func) {
4754 + var x = '';
4755 + if (j.askkeypass) {
4756 + x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:150px onchange=sshAuthUpdate(event)><option value=3 selected>' + "Stored Key" + '</option><option value=1>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>');
4757 + } else {
4758 + x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:150px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>');
4759 + }
4760 + x += '<div id=d2userauth style=display:none>';
4761 + x += addHtmlValue("Username", '<input id=dp2user style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
4762 + x += '</div>';
4763 + x += '<div id=d2passauth style=display:none>';
4764 + x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
4765 + if ((features2 & 0x00400000) == 0) { x += '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>'; }
4766 + x += '</div><div id=d2keyauth style=display:none>';
4767 + x += addHtmlValue("Key File", '<input type=file id=dp2key style=width:150px maxlength=64 autocomplete=off onchange=sshAuthUpdate(event) />' + '<div id=d2badkey style=font-size:x-small>' + "Key file must be in OpenSSH format." + '</div>');
4768 + x += addHtmlValue("Key Password", '<input type=password id=dp2keypass style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
4769 + if ((features2 & 0x00400000) == 0) {
4770 + x += '<label><input id=dp2keep1 type=checkbox onchange=sshAuthUpdate(event)>' + "Remember user & key" + '</label><br/>';
4771 + x += '<label><input id=dp2keep2 type=checkbox>' + "Remember password" + '</label>';
4772 + }
4773 + x += '</div>';
4774 + if (j.askkeypass) {
4775 + x += '<div id=d2keyauth2 style=display:none>';
4776 + x += addHtmlValue("Password", '<input type=password id=dp2keypass2 style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
4777 + x += '</div>';
4778 + }
4779 + setDialogMode(2, "Authentication", 11, func, x, 'ssh');
4780 + Q('dp2user').focus();
4781 + sshAuthUpdate();
4782 + setTimeout(sshAuthUpdate, 50);
4783 + }
4784 +
4785 function sshTunnelUpdate(data) {
4786 if (typeof data == 'string') {
4787 if (data[0] == '{') {
4788 var j = JSON.parse(data);
4789 switch (j.action) {
4790 case 'sshauth': {
4791 + sshTunnelAuthDialog(j, sshConnectEx);
4792 + /*
4793 var x = '';
4794 x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:150px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>')
4795 x += addHtmlValue("Username", '<input id=dp2user style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
@@ -4769,6 +4803,7 @@
4803 x += '<div id=d2keyauth2 style=font-size:x-small><br />' + "Key file must be in OpenSSH format." + '</div>';
4804 setDialogMode(2, "Authentication", 11, sshConnectEx, x, 'ssh');
4805 setTimeout(sshAuthUpdate, 50);
4806 + */
4807 break;
4808 }
4809 case 'sshautoauth': {
@@ -4783,6 +4818,7 @@
4818 }
4819 }
4820
4821 + /*
4822 function sshAuthUpdate(e) {
4823 QV('d2passauth', Q('dp2authmethod').value == 1);
4824 QV('d2keyauth', Q('dp2authmethod').value == 2);
@@ -4815,6 +4851,57 @@
4851 }
4852 }
4853 }
4854 + */
4855 +
4856 + function sshAuthUpdate(e) {
4857 + QV('d2userauth', Q('dp2authmethod').value != 3);
4858 + QV('d2passauth', Q('dp2authmethod').value == 1);
4859 + QV('d2keyauth', Q('dp2authmethod').value == 2);
4860 + QV('d2keyauth2', Q('dp2authmethod').value == 3);
4861 + if (Q('dp2authmethod').value == 1) {
4862 + QE('idx_dlgOkButton', (Q('dp2user').value.length > 0) && (Q('dp2pass').value.length > 0));
4863 + } else if (Q('dp2authmethod').value == 3) {
4864 + QE('idx_dlgOkButton', Q('dp2keypass2').value.length > 0);
4865 + } else {
4866 + QE('idx_dlgOkButton', false);
4867 + if ((features2 & 0x00400000) == 0) { QE('dp2keep2', Q('dp2keep1').checked); }
4868 + var ok = (Q('dp2user').value.length > 0) && (Q('dp2key').files != null) && (Q('dp2key').files.length == 1) && (Q('dp2key').files[0].size < 8000);
4869 + if (ok == true) {
4870 + var reader = new FileReader();
4871 + reader.onload = function (e) {
4872 + var validkey = ((e.target.result.indexOf('-----BEGIN OPENSSH PRIVATE KEY-----') >= 0) && (e.target.result.indexOf('-----END OPENSSH PRIVATE KEY-----') >= 0));
4873 + QE('idx_dlgOkButton', validkey);
4874 + QS('d2badkey')['color'] = validkey ? '#000' : '#F00';
4875 + }
4876 + reader.readAsText(Q('dp2key').files[0]);
4877 + }
4878 + }
4879 +
4880 + // When the enter key is pressed, move to the next field
4881 + if (e && (e.keyCode == 13) && (e.target) && (Q('dp2authmethod').value == 1)) {
4882 + if (e.target.id == 'dp2user') { Q('dp2pass').focus(); }
4883 + if (e.target.id == 'dp2pass') { dialogclose(1); }
4884 + }
4885 + }
4886 +
4887 + function sshConnectEx(b) {
4888 + if (b == 0) {
4889 + if (terminal != null) { connectTerminal(); } // Disconnect
4890 + } else {
4891 + var keep = 0;
4892 + if (Q('dp2authmethod').value == 1) {
4893 + if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep').checked ? 1 : 0); }
4894 + terminal.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: keep, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
4895 + } else if (Q('dp2authmethod').value == 3) {
4896 + terminal.socket.send(JSON.stringify({ action: 'sshkeyauth', keypass: Q('dp2keypass2').value, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
4897 + } else {
4898 + if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep1').checked ? 1 : 0); if (keep == 1) { keep += (Q('dp2keep2').checked ? 1 : 0); } } // Keep: 1 = user & key, 2 = User, key and password
4899 + var reader = new FileReader(), username = Q('dp2user').value, keypass = Q('dp2keypass').value;
4900 + reader.onload = function (e) { terminal.socket.send(JSON.stringify({ action: 'sshauth', username: username, keypass: keypass, key: e.target.result, keep: keep, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight })); }
4901 + reader.readAsText(Q('dp2key').files[0]);
4902 + }
4903 + }
4904 + }
4905
4906 // Send the new terminal size to the agent
4907 function xTermSendResize() {
@@ -5035,6 +5122,8 @@
5122 // Process any SSH actions
5123 switch (data.action) {
5124 case 'sshauth': {
5125 + sshTunnelAuthDialog(data, p13sshConnectEx);
5126 + /*
5127 var x = '';
5128 x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:150px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>')
5129 x += addHtmlValue("Username", '<input id=dp2user style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
@@ -5048,6 +5137,7 @@
5137 x += '<div id=d2keyauth2 style=font-size:x-small><br />' + "Key file must be in OpenSSH format." + '</div>';
5138 setDialogMode(2, "Authentication", 11, p13sshConnectEx, x, 'ssh');
5139 setTimeout(sshAuthUpdate, 50);
5140 + */
5141 break;
5142 }
5143 case 'autherror': { p13setConsoleMsg("Authentication Error", 5000); return; }
@@ -5088,10 +5178,15 @@
5178 if (b == 0) {
5179 if (files != null) { connectFiles(); } // Disconnect
5180 } else {
5181 + var keep = 0;
5182 if (Q('dp2authmethod').value == 1) {
5092 - files.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: Q('dp2keep').checked }));
5183 + if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep').checked ? 1 : 0); }
5184 + files.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: keep }));
5185 + } else if (Q('dp2authmethod').value == 3) {
5186 + files.socket.send(JSON.stringify({ action: 'sshkeyauth', keypass: Q('dp2keypass2').value }));
5187 } else {
5094 - var reader = new FileReader(), username = Q('dp2user').value, keypass = Q('dp2keypass').value, keep = Q('dp2keep').checked;
5188 + if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep1').checked ? 1 : 0); if (keep == 1) { keep += (Q('dp2keep2').checked ? 1 : 0); } } // Keep: 1 = user & key, 2 = User, key and password
5189 + var reader = new FileReader(), username = Q('dp2user').value, keypass = Q('dp2keypass').value;
5190 reader.onload = function (e) { files.socket.send(JSON.stringify({ action: 'sshauth', username: username, keypass: keypass, key: e.target.result, keep: keep })); }
5191 reader.readAsText(Q('dp2key').files[0]);
5192 }
views/default.handlebars
+51 -36
@@ -7007,10 +7007,10 @@
7007 if ((node.ssh != null) || (node.rdp != null)) {
7008 var y = [];
7009 if ((meshrights & 4) != 0) {
7010 - if (node.ssh != null) { y.push('<span onclick=showClearSshDialog(3) style=cursor:pointer>' + ((node.ssh == 2)?"SSH-Key":"SSH") + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
7010 + if (node.ssh != null) { y.push('<span onclick=showClearSshDialog(3) style=cursor:pointer>' + ((node.ssh == 1)?"SSH-User+Pass":((node.ssh == 2)?"SSH-User+Key+Pass":"SSH-User+Key")) + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
7011 if (node.rdp != null) { y.push('<span onclick=showClearRdpDialog(3) style=cursor:pointer>' + "RDP" + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
7012 } else {
7013 - if (node.ssh != null) { y.push(((node.ssh == 2)?"SSH-Key":"SSH")); }
7013 + if (node.ssh != null) { y.push(((node.ssh == 1)?"SSH-User+Pass":((node.ssh == 2)?"SSH-User+Key+Pass":"SSH-User+Key"))); }
7014 if (node.rdp != null) { y.push("RDP"); }
7015 }
7016 x += addDeviceAttribute("Credentials", y.join(', '));
@@ -9660,25 +9660,45 @@
9660
9661 function tunnelUpdate(data) { if (typeof data == 'string') { xterm.writeUtf8(data); } else { xterm.writeUtf8(new Uint8Array(data)); } }
9662
9663 + function sshTunnelAuthDialog(j, func) {
9664 + var x = '';
9665 + if (j.askkeypass) {
9666 + x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:230px onchange=sshAuthUpdate(event)><option value=3 selected>' + "Stored Key" + '</option><option value=1>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>');
9667 + } else {
9668 + x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:230px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>');
9669 + }
9670 + x += '<div id=d2userauth style=display:none>';
9671 + x += addHtmlValue("Username", '<input id=dp2user style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
9672 + x += '</div>';
9673 + x += '<div id=d2passauth style=display:none>';
9674 + x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
9675 + if ((features2 & 0x00400000) == 0) { x += addHtmlValue('', '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>'); }
9676 + x += '</div><div id=d2keyauth style=display:none>';
9677 + x += addHtmlValue("Key File", '<input type=file id=dp2key style=width:230px maxlength=64 autocomplete=off onchange=sshAuthUpdate(event) />' + '<div id=d2badkey style=font-size:x-small>' + "Key file must be in OpenSSH format." + '</div>');
9678 + x += addHtmlValue("Key Password", '<input type=password id=dp2keypass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
9679 + if ((features2 & 0x00400000) == 0) {
9680 + x += addHtmlValue('', '<label><input id=dp2keep1 type=checkbox onchange=sshAuthUpdate(event)>' + "Remember user & key" + '</label>');
9681 + x += addHtmlValue('', '<label><input id=dp2keep2 type=checkbox>' + "Remember password" + '</label>');
9682 + }
9683 + x += '</div>';
9684 + if (j.askkeypass) {
9685 + x += '<div id=d2keyauth2 style=display:none>';
9686 + x += addHtmlValue("Password", '<input type=password id=dp2keypass2 style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
9687 + x += '</div>';
9688 + }
9689 + setDialogMode(2, "Authentication", 11, func, x, 'ssh');
9690 + Q('dp2user').focus();
9691 + sshAuthUpdate();
9692 + setTimeout(sshAuthUpdate, 50);
9693 + }
9694 +
9695 function sshTunnelUpdate(data) {
9696 if (typeof data == 'string') {
9697 if (data[0] == '{') {
9698 var j = JSON.parse(data);
9699 switch (j.action) {
9700 case 'sshauth': {
9669 - var x = '';
9670 - x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:230px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>')
9671 - x += addHtmlValue("Username", '<input id=dp2user style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
9672 - x += '<div id=d2passauth>';
9673 - x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
9674 - x += '</div><div id=d2keyauth style=display:none>';
9675 - x += addHtmlValue("Key File", '<input type=file id=dp2key style=width:230px maxlength=64 autocomplete=off onchange=sshAuthUpdate(event) />' + '<div id=d2badkey style=font-size:x-small>' + "Key file must be in OpenSSH format." + '</div>');
9676 - x += addHtmlValue("Key Password", '<input type=password id=dp2keypass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
9677 - x += '</div>';
9678 - if ((features2 & 0x00400000) == 0) { x += addHtmlValue('', '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>'); }
9679 - setDialogMode(2, "Authentication", 11, sshConnectEx, x, 'ssh');
9680 - Q('dp2user').focus();
9681 - setTimeout(sshAuthUpdate, 50);
9701 + sshTunnelAuthDialog(j, sshConnectEx);
9702 break;
9703 }
9704 case 'sshautoauth': {
@@ -9695,12 +9715,17 @@
9715 }
9716
9717 function sshAuthUpdate(e) {
9718 + QV('d2userauth', Q('dp2authmethod').value != 3);
9719 QV('d2passauth', Q('dp2authmethod').value == 1);
9720 QV('d2keyauth', Q('dp2authmethod').value == 2);
9721 + QV('d2keyauth2', Q('dp2authmethod').value == 3);
9722 if (Q('dp2authmethod').value == 1) {
9723 QE('idx_dlgOkButton', (Q('dp2user').value.length > 0) && (Q('dp2pass').value.length > 0));
9724 + } else if (Q('dp2authmethod').value == 3) {
9725 + QE('idx_dlgOkButton', Q('dp2keypass2').value.length > 0);
9726 } else {
9727 QE('idx_dlgOkButton', false);
9728 + if ((features2 & 0x00400000) == 0) { QE('dp2keep2', Q('dp2keep1').checked); }
9729 var ok = (Q('dp2user').value.length > 0) && (Q('dp2key').files != null) && (Q('dp2key').files.length == 1) && (Q('dp2key').files[0].size < 8000);
9730 if (ok == true) {
9731 var reader = new FileReader();
@@ -9724,12 +9749,14 @@
9749 if (b == 0) {
9750 if (terminal != null) { connectTerminal(); } // Disconnect
9751 } else {
9727 - var keep = false;
9728 - if ((features2 & 0x00400000) == 0) { keep = Q('dp2keep').checked; }
9729 -
9752 + var keep = 0;
9753 if (Q('dp2authmethod').value == 1) {
9754 + if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep').checked ? 1 : 0); }
9755 terminal.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: keep, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
9756 + } else if (Q('dp2authmethod').value == 3) {
9757 + terminal.socket.send(JSON.stringify({ action: 'sshkeyauth', keypass: Q('dp2keypass2').value, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
9758 } else {
9759 + if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep1').checked ? 1 : 0); if (keep == 1) { keep += (Q('dp2keep2').checked ? 1 : 0); } } // Keep: 1 = user & key, 2 = User, key and password
9760 var reader = new FileReader(), username = Q('dp2user').value, keypass = Q('dp2keypass').value;
9761 reader.onload = function (e) { terminal.socket.send(JSON.stringify({ action: 'sshauth', username: username, keypass: keypass, key: e.target.result, keep: keep, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight })); }
9762 reader.readAsText(Q('dp2key').files[0]);
@@ -10068,22 +10095,7 @@
10095
10096 // Process any SSH actions
10097 switch (data.action) {
10071 - case 'sshauth': {
10072 - var x = '';
10073 - x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:230px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>')
10074 - x += addHtmlValue("Username", '<input id=dp2user style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
10075 - x += '<div id=d2passauth>';
10076 - x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
10077 - x += '</div><div id=d2keyauth style=display:none>';
10078 - x += addHtmlValue("Key File", '<input type=file id=dp2key style=width:230px maxlength=64 autocomplete=off onchange=sshAuthUpdate(event) />');
10079 - x += addHtmlValue("Key Password", '<input type=password id=dp2keypass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
10080 - x += '</div>';
10081 - if ((features2 & 0x00400000) == 0) { x += addHtmlValue('', '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>'); }
10082 - setDialogMode(2, "Authentication", 11, p13sshConnectEx, x, 'ssh');
10083 - Q('dp2user').focus();
10084 - setTimeout(sshAuthUpdate, 50);
10085 - return;
10086 - }
10098 + case 'sshauth': { sshTunnelAuthDialog(data, p13sshConnectEx); return; }
10099 case 'autherror': { p13setConsoleMsg("Authentication Error", 5000); return; }
10100 case 'connectionerror': { p13setConsoleMsg("Connection Error", 5000); return; }
10101 case 'sessionerror': { p13setConsoleMsg("Session expired", 5000); return; }
@@ -10136,11 +10148,14 @@
10148 if (b == 0) {
10149 if (files != null) { connectFiles(); } // Disconnect
10150 } else {
10139 - var keep = false;
10140 - if ((features2 & 0x00400000) == 0) { keep = Q('dp2keep').checked; }
10151 + var keep = 0;
10152 if (Q('dp2authmethod').value == 1) {
10153 + if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep').checked ? 1 : 0); }
10154 files.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: keep }));
10155 + } else if (Q('dp2authmethod').value == 3) {
10156 + files.socket.send(JSON.stringify({ action: 'sshkeyauth', keypass: Q('dp2keypass2').value }));
10157 } else {
10158 + if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep1').checked ? 1 : 0); if (keep == 1) { keep += (Q('dp2keep2').checked ? 1 : 0); } } // Keep: 1 = user & key, 2 = User, key and password
10159 var reader = new FileReader(), username = Q('dp2user').value, keypass = Q('dp2keypass').value;
10160 reader.onload = function (e) { files.socket.send(JSON.stringify({ action: 'sshauth', username: username, keypass: keypass, key: e.target.result, keep: keep })); }
10161 reader.readAsText(Q('dp2key').files[0]);
views/mstsc.handlebars
+2 -2
@@ -80,7 +80,7 @@
80 var urlargs = parseUriArgs();
81 if (urlargs.key && (isAlphaNumeric(urlargs.key) == false)) { delete urlargs.key; }
82 var cookie = '{{{cookie}}}';
83 - var serverCredentials = (decodeURIComponent('{{{serverCredentials}}}') == 'true');
83 + var serverCredentials = parseInt('{{{serverCredentials}}}');
84 var name = decodeURIComponent('{{{name}}}');
85 if (name != '') { document.title = name + ' - ' + document.title; }
86 var features = parseInt('{{{features}}}');
@@ -97,7 +97,7 @@
97 QE('connectButton', false);
98 }
99
100 - if (serverCredentials == true) {
100 + if (serverCredentials == 1) {
101 QV('dropdowndomain', true);
102 Q('d3coreMode').value = 1;
103 } else {
views/ssh.handlebars
+45 -20
@@ -130,12 +130,17 @@
130 }
131
132 function sshAuthUpdate(e) {
133 + QV('d2userauth', Q('dp2authmethod').value != 3);
134 QV('d2passauth', Q('dp2authmethod').value == 1);
135 QV('d2keyauth', Q('dp2authmethod').value == 2);
136 + QV('d2keyauth2', Q('dp2authmethod').value == 3);
137 if (Q('dp2authmethod').value == 1) {
138 QE('idx_dlgOkButton', (Q('dp2user').value.length > 0) && (Q('dp2pass').value.length > 0));
139 + } else if (Q('dp2authmethod').value == 3) {
140 + QE('idx_dlgOkButton', Q('dp2keypass2').value.length > 0);
141 } else {
142 QE('idx_dlgOkButton', false);
143 + if ((features & 1) == 0) { QE('dp2keep2', Q('dp2keep1').checked); }
144 var ok = (Q('dp2user').value.length > 0) && (Q('dp2key').files != null) && (Q('dp2key').files.length == 1) && (Q('dp2key').files[0].size < 8000);
145 if (ok == true) {
146 var reader = new FileReader();
@@ -156,13 +161,18 @@
161 }
162
163 function connectEx() {
159 - var cmd = { action: 'connect', cols: term.cols, rows: term.rows, width: Q('terminal').offsetWidth, height: Q('terminal').offsetHeight, username: Q('dp2user').value, keep: false };
160 - if ((features & 1) == 0) { cmd.keep = Q('dp2keep').checked; }
164 + var cmd = { action: 'connect', cols: term.cols, rows: term.rows, width: Q('terminal').offsetWidth, height: Q('terminal').offsetHeight, username: Q('dp2user').value, keep: 0 };
165
166 if (Q('dp2authmethod').value == 1) {
167 cmd.password = Q('dp2pass').value;
168 + if ((features & 1) == 0) { cmd.keep = Q('dp2keep').checked ? 1 : 0; }
169 + connectEx2(cmd);
170 + } else if (Q('dp2authmethod').value == 3) {
171 + cmd.action = 'connectKeyPass';
172 + cmd.keypass = Q('dp2keypass2').value;
173 connectEx2(cmd);
174 } else {
175 + if ((features & 1) == 0) { cmd.keep = (Q('dp2keep1').checked ? 1 : 0); if (cmd.keep == 1) { cmd.keep += (Q('dp2keep2').checked ? 1 : 0); } } // Keep: 1 = user & key, 2 = User, key and password
176 cmd.keypass = Q('dp2keypass').value;
177 var reader = new FileReader();
178 reader.onload = function (e) { cmd.key = e.target.result; connectEx2(cmd); }
@@ -170,6 +180,38 @@
180 }
181 }
182
183 + function sshTunnelAuthDialog(j, func) {
184 + var x = '';
185 + if (j.askkeypass) {
186 + x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:230px onchange=sshAuthUpdate(event)><option value=3 selected>' + "Stored Key" + '</option><option value=1>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>');
187 + } else {
188 + x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:230px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>');
189 + }
190 + x += '<div id=d2userauth style=display:none>';
191 + x += addHtmlValue("Username", '<input id=dp2user style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
192 + x += '</div>';
193 + x += '<div id=d2passauth style=display:none>';
194 + x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
195 + if ((features & 1) == 0) { x += addHtmlValue('', '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>'); }
196 + x += '</div><div id=d2keyauth style=display:none>';
197 + x += addHtmlValue("Key File", '<input type=file id=dp2key style=width:230px maxlength=64 autocomplete=off onchange=sshAuthUpdate(event) />' + '<div id=d2badkey style=font-size:x-small>' + "Key file must be in OpenSSH format." + '</div>');
198 + x += addHtmlValue("Key Password", '<input type=password id=dp2keypass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
199 + if ((features & 1) == 0) {
200 + x += addHtmlValue('', '<label><input id=dp2keep1 type=checkbox onchange=sshAuthUpdate(event)>' + "Remember user & key" + '</label>');
201 + x += addHtmlValue('', '<label><input id=dp2keep2 type=checkbox>' + "Remember password" + '</label>');
202 + }
203 + x += '</div>';
204 + if (j.askkeypass) {
205 + x += '<div id=d2keyauth2 style=display:none>';
206 + x += addHtmlValue("Password", '<input type=password id=dp2keypass2 style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
207 + x += '</div>';
208 + }
209 + setDialogMode(2, "Authentication", 11, func, x, 'ssh');
210 + Q('dp2user').focus();
211 + sshAuthUpdate();
212 + setTimeout(sshAuthUpdate, 50);
213 + }
214 +
215 function connectEx2(cmd) {
216 state = 1;
217 var url = window.location.protocol.replace('http', 'ws') + '//' + window.location.host + domainurl + 'sshrelay.ashx?auth=' + cookie + (urlargs.key ? ('&key=' + urlargs.key) : '');
@@ -192,24 +234,7 @@
234 if ((json.ctrlChannel == 102938) && (json.type == 'ping')) { socket.send('{"ctrlChannel":"102938","type":"pong"}'); return; }
235 switch (json.action) {
236 case 'connected': { state = 3; updateState(); term.focus(); break; }
195 - case 'sshauth': {
196 - var x = '';
197 - x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:230px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>')
198 - x += addHtmlValue("Username", '<input id=dp2user style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
199 - x += '<div id=d2passauth>';
200 - x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
201 - x += '</div><div id=d2keyauth style=display:none>';
202 - x += addHtmlValue("Key File", '<input type=file id=dp2key style=width:230px maxlength=64 autocomplete=off onchange=sshAuthUpdate(event) />' + '<div id=d2badkey style=font-size:x-small>' + "Key file must be in OpenSSH format." + '</div>');
203 - x += addHtmlValue("Key Password", '<input type=password id=dp2keypass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
204 - x += '</div>';
205 - if ((features & 1) == 0) { x += addHtmlValue('', '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>'); }
206 - setDialogMode(2, "Authentication", 3, connectEx, x);
207 - Q('dp2user').value = user;
208 - Q('dp2pass').value = pass;
209 - if (user == '') { Q('dp2user').focus(); } else { Q('dp2pass').focus(); }
210 - setTimeout(sshAuthUpdate, 50);
211 - break;
212 - }
237 + case 'sshauth': { sshTunnelAuthDialog(json, connectEx); break; }
238 case 'autherror': { setDialogMode(2, "Authentication", 1, null, "Unable to authenticate."); break; }
239 case 'sessionerror': { setDialogMode(2, "Session", 1, null, "Session expired."); break; }
240 case 'sessiontimeout': { setDialogMode(2, "Session", 1, null, "Session timeout."); break; }
webserver.js
+19 -7
@@ -1962,13 +1962,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
1962 if ((err != null) || (nodes.length != 1)) { res.sendStatus(404); return; }
1963 const node = nodes[0];
1964
1965 - // Check if we have RDP credentials for this device
1966 - var serverCredentials = false;
1965 + // Check if we have SSH/RDP credentials for this device
1966 + var serverCredentials = 0;
1967 if (domain.allowsavingdevicecredentials !== false) {
1968 if (page == 'ssh') {
1969 - serverCredentials = ((typeof node.ssh == 'object') && (typeof node.ssh.u == 'string'))
1969 + if ((typeof node.ssh == 'object') && (typeof node.ssh.u == 'string') && (typeof node.ssh.p == 'string')) { serverCredentials = 1; } // Username and password
1970 + else if ((typeof node.ssh == 'object') && (typeof node.ssh.k == 'string') && (typeof node.ssh.kp == 'string')) { serverCredentials = 2; } // Username, key and password
1971 + else if ((typeof node.ssh == 'object') && (typeof node.ssh.k == 'string')) { serverCredentials = 3; } // Username and key. No password.
1972 } else {
1971 - serverCredentials = ((typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string'))
1973 + if ((typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string')) { serverCredentials = 1; } // Username and password
1974 }
1975 }
1976
@@ -2028,14 +2030,20 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
2030 if (typeof node.sshport == 'number') { port = node.sshport; }
2031
2032 // Check if we have SSH credentials for this device
2031 - if (domain.allowsavingdevicecredentials !== false) { serverCredentials = ((typeof node.ssh == 'object') && (typeof node.ssh.u == 'string')); }
2033 + if (domain.allowsavingdevicecredentials !== false) {
2034 + if ((typeof node.ssh == 'object') && (typeof node.ssh.u == 'string') && (typeof node.ssh.p == 'string')) { serverCredentials = 1; } // Username and password
2035 + else if ((typeof node.ssh == 'object') && (typeof node.ssh.k == 'string') && (typeof node.ssh.kp == 'string')) { serverCredentials = 2; } // Username, key and password
2036 + else if ((typeof node.ssh == 'object') && (typeof node.ssh.k == 'string')) { serverCredentials = 3; } // Username and key. No password.
2037 + }
2038 } else {
2039 // RDP port
2040 port = 3389;
2041 if (typeof node.rdpport == 'number') { port = node.rdpport; }
2042
2043 // Check if we have RDP credentials for this device
2038 - if (domain.allowsavingdevicecredentials !== false) { serverCredentials = ((typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string')); }
2044 + if (domain.allowsavingdevicecredentials !== false) {
2045 + if ((typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string')) { serverCredentials = 1; } // Username and password
2046 + }
2047 }
2048 if (req.query.port != null) { var qport = 0; try { qport = parseInt(req.query.port); } catch (ex) { } if ((typeof qport == 'number') && (qport > 0) && (qport < 65536)) { port = qport; } }
2049
@@ -7513,7 +7521,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7521 if ((r.pmt != null) || (r.ssh != null) || (r.rdp != null) || ((r.intelamt != null) && ((r.intelamt.pass != null) || (r.intelamt.mpspass != null)))) {
7522 r = Object.assign({}, r); // Shallow clone
7523 if (r.pmt != null) { r.pmt = 1; }
7516 - if (r.ssh != null) { r.ssh = (r.ssh.k != null) ? 2 : 1; }
7524 + if (r.ssh && r.ssh.u) {
7525 + if (r.ssh.p) { r.ssh = 1; } // Username and password
7526 + else if (r.ssh.k && r.ssh.kp) { r.ssh = 2; } // Username, key and password
7527 + else if (r.ssh.k) { r.ssh = 3; } // Username and key. No password.
7528 + }
7529 if (r.rdp != null) { r.rdp = 1; }
7530 if ((r.intelamt != null) && ((r.intelamt.pass != null) || (r.intelamt.mpspass != null))) {
7531 r.intelamt = Object.assign({}, r.intelamt); // Shallow clone