From 8c068505cfd877fd47efe1631674443ffc59c5a5 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Fri, 8 Feb 2019 09:24:00 -0800 Subject: [PATCH] Added support for both U2F and OTP hardware login keys. --- meshuser.js | 39 ++-- package.json | 2 +- public/images/hardware-key-24.png | Bin 362 -> 0 bytes public/images/hardware-key-32.png | Bin 578 -> 0 bytes public/images/hardware-key-OTP-24.png | Bin 0 -> 779 bytes public/images/hardware-key-U2F-24.png | Bin 0 -> 988 bytes .../images/hardware-keypress-120 - Copy.png | Bin 0 -> 2676 bytes public/images/hardware-keypress-120.png | Bin 2676 -> 8667 bytes views/default-min.handlebars | 2 +- views/default.handlebars | 72 +++---- views/login-min.handlebars | 2 +- views/login-mobile-min.handlebars | 2 +- views/login-mobile.handlebars | 6 +- views/login.handlebars | 6 +- webserver.js | 195 +++++++++--------- 15 files changed, 171 insertions(+), 155 deletions(-) delete mode 100644 public/images/hardware-key-24.png delete mode 100644 public/images/hardware-key-32.png create mode 100644 public/images/hardware-key-OTP-24.png create mode 100644 public/images/hardware-key-U2F-24.png create mode 100644 public/images/hardware-keypress-120 - Copy.png diff --git a/meshuser.js b/meshuser.js index a322d89e..318e9b16 100644 --- a/meshuser.js +++ b/meshuser.js @@ -1503,9 +1503,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Send back the list of keys we have, just send the list of names and index var hkeys = []; - if (user.otphkeys != null) { for (var i = 0; i < user.otphkeys.length; i++) { hkeys.push({ i: user.otphkeys[i].keyIndex, name: user.otphkeys[i].name }); } } - - //hkeys = [{ i: 1234, name: 'My Normal Key' }, { i: 5678, name: 'Backup Key' }, { i: 90122, name: 'Blue Extra Key' }]; + if (user.otphkeys != null) { for (var i = 0; i < user.otphkeys.length; i++) { hkeys.push({ i: user.otphkeys[i].keyIndex, name: user.otphkeys[i].name, type: user.otphkeys[i].type }); } } ws.send(JSON.stringify({ action: 'otp-hkey-get', keys: hkeys })); break; @@ -1539,22 +1537,31 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Check if Yubikey support is present if ((typeof domain.yubikey != 'object') || (typeof domain.yubikey.id != 'string') || (typeof domain.yubikey.secret != 'string')) break; - /* - var yub = require('yubikey-client'); - yub.init(domain.yubikey.id, domain.yubikey.secret); - yub.verify(command.otp, function (err, data) { - console.log(err, data); - }); - */ - + // Query the YubiKey server to validate the OTP var yubikeyotp = require('yubikeyotp'); - //var request = { otp: command.otp, id: domain.yubikey.id, key: domain.yubikey.secret, sl: '100', timestamp: true } var request = { otp: command.otp, id: domain.yubikey.id, key: domain.yubikey.secret, timestamp: true } if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; } - - console.log('YubiKey Request: ' + JSON.stringify(request)); yubikeyotp.verifyOTP(request, function (err, results) { - console.log(err, results); + if (results.status == 'OK') { + var keyIndex = obj.parent.crypto.randomBytes(4).readUInt32BE(0); + var keyId = command.otp.substring(0, 12); + if (user.otphkeys == null) { user.otphkeys = []; } + + // Check if this key was already registered, if so, remove it. + var foundAtIndex = -1; + for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].keyid == keyId) { foundAtIndex = i; } } + if (foundAtIndex != -1) { user.otphkeys.splice(foundAtIndex, 1); } + + // Add the new key and notify + user.otphkeys.push({ name: command.name, type: 2, keyid: keyId, keyIndex: keyIndex }); + obj.parent.db.SetUser(user); + ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: true, name: command.name, index: keyIndex })); + + // Notify change TODO: Should be done on all sessions/servers for this user. + try { ws.send(JSON.stringify({ action: 'userinfo', userinfo: obj.parent.CloneSafeUser(user) })); } catch (ex) { } + } else { + ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: false, name: command.name })); + } }); break; @@ -1587,7 +1594,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: result.successful, name: command.name, index: keyIndex })); if (result.successful) { if (user.otphkeys == null) { user.otphkeys = []; } - user.otphkeys.push({ name: command.name, publicKey: result.publicKey, keyHandle: result.keyHandle, keyIndex: keyIndex }); + user.otphkeys.push({ name: command.name, type: 1, publicKey: result.publicKey, keyHandle: result.keyHandle, keyIndex: keyIndex }); obj.parent.db.SetUser(user); //console.log('KEYS', JSON.stringify(user.otphkeys)); diff --git a/package.json b/package.json index fe6a4ddd..2b957f8d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.2.7-n", + "version": "0.2.7-o", "keywords": [ "Remote Management", "Intel AMT", diff --git a/public/images/hardware-key-24.png b/public/images/hardware-key-24.png deleted file mode 100644 index f2ecfb7f459adafc546e8e6e3ad520727a650d5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 362 zcmeAS@N?(olHy`uVBq!ia0vp^5a~60+7BevL9R^{>fQPqSx8evT`<>3ia}|DKD_c%OHbgqgC*<$YekFS>Yh znP}>r4{IHHoplUcrGG{=haMC?$mTm|!F!3jlLU3HEtqkB7h8O@?3Vfi65cii?(AKn zx0a^`H1SGRJZjn{c4FVdQ&MBb@ E0KUJDt^fc4 diff --git a/public/images/hardware-key-32.png b/public/images/hardware-key-32.png deleted file mode 100644 index 1ad6f6a318781e90e07e395fbab25d276a393808..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 578 zcmV-I0=@l-P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02p*dSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E;(#7eog=Y0kcU&K~z{r?by361yK~n@u6@@#HIHD8ka(; zqovomGzzCth!;TO@)jzglPDFOL=-|nLNp>e1rmiq_^-%L)+}>58Rum7Oy)~|#mLMu zUo!UW5s+_>ogh=9hWVl@rK5+u<1|Ng==-_EL6iJa4@&WFAc_e$_;zg+& zxNAQkH=UL}=-)X67h6j9gR-((_HVH&l@|N>$xb3Eg?Ri*xllwc=^H5Yk}lbTo*}NG zU%rPZ%*$8Oi8JUqX=Ix;3>#Z2*9~niO0}VNk(F%@chEB(`SjR9KjPwDDHkaxJBtOl zk3?A{WH+A{TPpXD#Ad{n$|`hFm1;p+QmT~y%=As8tKXkQ4f91+>Ni2~1L*xsm+b?K Q^#A|>07*qoM6N<$f+%0~EdT%j diff --git a/public/images/hardware-key-OTP-24.png b/public/images/hardware-key-OTP-24.png new file mode 100644 index 0000000000000000000000000000000000000000..2cdedb9024394691652eda5c622ad30faaf03908 GIT binary patch literal 779 zcmV+m1N8ifP)N2bZe?^J zG%heMIczh2P5=M`iAh93R5(wqlubxfQ547DB6FNX9jNUP78I4HsbM;rWk^pGQu>e} z+K4dLFh&JtVLKN&kr36;mtrGmjzyMGjJl|%MU+L6W3*sGQ};-F#OCLOIefY$4Phv8hoHS1wD zM^&#UFV9#64I46bT=pS~E5s$=Jlsvgs5<1q;ktI5_fCcNu$O0)RW^pPa#i}mf{bh( z?S2S4IQGf>>g^e9++1cf3al$^IN2O9`!llS0;9ggv6pn3sXQ98a&+`O0JMdK%L4rMdU6HzmynG#l&wxM>7*urL85a$e zRZdgmo||2xLa#p1AT;)J-PQL+aQD%4m`-TOFVGQu1w2;-UjoCyStQUCtXZ3hCokrt zPMm6zPvAb$CTjMoL4th;U6HzmLWhpmqmaB;k*MA;zXL~cIa)9J;c~aYVzDAQITb@s zC-H`Q3yb8SqT&kbtH7CaUW|~JXPOI&w(A%h2gW{1J}{qvk#{rH82&3tNwJ~5Ga!4( z!(P^fpI|TF6)T<4P_k3U#1G)>cgX~!>d)Wi(cL?Yv&|k{XzRe+(P??FDA^$g)gNm# zaT0cW$a_$_OUKXOz^~~AL~Up)PO03yBiO43#Y00{nho0u9Q5BLYlEfK>^1UO@Ch?S z#&~&XH1b$P3lRfiN2bZe?^J zG%heMIczh2P5=M{T1iAfR5(wq)ca49XB5Ek3Kg;H#zg1D7K(QiX@LgTrWDFh8E$Bl zOmtI>G8;!gaj0A3Q0H{X!eHRMVP>RI%_Nd2!o(Hu$| zJ`~?Y$U9RLrZ6>e3JY9sv$Y{l<}VgZPmE;O*-wN5b~cIFRKOPvE`qf|OfrVEv$24$ znhV+8Tts$3x@^pHIoQ*@iJ2CoI!LlEHuH7Mr~Y*fF-g;?yiiKj#ZoFSm9kIR;VF_A z?AaEP@8bK*KHF;wW!;%$Wq<2#<|NOM`JVbMLJ4I}+d0&>m$}I^2NuLxW2kAb!ria7 zeIGxxmZ2S~#V|br_b*j=Iu78uwx6}1t&okYHs^5S+CfaVx#}Qk;RjBBynYz7Emo~* zNU)n|yy@Xgr-#PQ#FZob)K!ng9xv}C zITECz9WGjKpAtR3x?GU#u+a9$Ioj@?Wl-+7uOd!l+#8u!{*D&61E z{^teZBG%L-S?~DklF&*=&t;(%TUxT(Bi^&qMwjAM-0}*2H@MyVE4M^`tiGBp zrJLDX`6caluF&208{Lm@(*2~9lytisw7zhyunt#0LzI4CfiokOM^Ah3KD|%xvmSay z^gdIbh35~XzUPmGhddTh@#JrXm-P8gbQOh6)I{SLxluUBs5CwDPs$&MJ=N(qL$XQ9Yh*S zk*XzBJGB=BahEg!@;XFLT zoqyAbw;{j#0go~fR@jK2Lx>U4K4Jbm6raH02vo3tgrYu52X#J3FWv#Lo42vN||#bUYHe6u(7v4gdjntVy91Q&dtxCvnaBlMaqjaHggEYX`p2j zICfEqV=Mw4x%nO1@R>W+9`-TM5{*X31qC7ZV}5{RB@$>j508Rrgl4|B_oS49LUyYc zFI1|lbb4W70pYMdIw}LbdiuW|I_iKLU%U{yAe*HpBPSMt=ZT6X_fJp9a2#AmsG zcyjW{bf7W-!%7EfQYjRcfu<%ux94_dri4Mr(OUAH}`V z8sB*E=O6=ZPFGhKo)gtv{A1iLP!%759>eNK|Bu9mhK8DGI2?{mOG`^5mzS3hwhF2# zq}+s}G&E-TnH3d2wx#T_`R<1Kp8THuzD*Zse|PtOI)sl3s(N&)OQ+E8?aa>V0hchApEu?GrEO3X<5rw3azb8FR{kP(c6O46hlYsD%gZD;2KFaYef@jKP0wl* zKa`J6wrdiy?FcdV1D~SPM@RH?bgcb>E*s_hh< zfJsTIF>sBmUq?r=^)JY^5XlQ7(1phy>l?Fg6LRSE?v}G>Wn}U%^J_=7YzSetCKs&z4Hs=z#o`a*b1UwK~Rq z>4YQFb86q4@7^w|kR{msfZr}b|pq;06;k`276l){TCTH4h zeS7(n9$_b5z@Bk@vZkgc+9wd?vY2MxdZ=v$aY>O5TJckc?@8j_-N)8Kc9>o*F;RYg zzln}l`f^`>JZ0T6bqy@~%jn)aKg=|LYs_}#AhFIgQElXtkgOdvH8oZH5-yT4k;nB? zkQJ>Y_^QK{leDM@JKW0!#n?8mo!YQB9wohtJ_%RKp8ul#(24g9rce}mWH5L+VUgL= z(z4d+?1~Ad5PUWMw%qG2)XOI%ZHL3e#FVf|sTI0y;+4@j5^}8m1g#|l&!YS#gV(HUuF4%6#XsW6mXrPaadSblQ|w3#m6ru zf*sQuzHQ<6ebF23a>o81@E}1zZLf&45*Lpm|JHk{?hrzK@Cmw z<{`@xeCiQ4h!<*A}ZLEnM)P!|_G_!1MP zp{37em45>o$}u}DtCg?uka&D_cz2}{@n~sPfvh2MRs@gEP?}@uZiy$BVb-^{A_9Qn z8vFjWe5Zigc@!#`+0d|as-UnTLt6W4$KmRqO`$3^ZEaHz?%#KHb8*@7UddKlV4En_ zaWy?eXM{5Icc+!q#e2U*7<&U_yH;SH${I+YQ9lsF9FGR*#|sv1B-9S061>AhZLgZ; zS(mI8>tsL;O?RWP44d_?q4{@h_jU%Ab*}F5q&p!Cz(g-0jB~_>XW45HgG4{@f!|9M zJwua&%+1YxzSo?Bz`~_RQzFwuE3RR5q(9Q!Fa9e^72=u1Gq9nAEDgD7t(K;FZc4&n zn>Ry4Hzw$Gx_;9D>7nng$ScJ-TplPKie3K&k8%qDB7=L|9cN7T^o*=xUbptZ%@loa zsNL(M8oAOvo1?fS;WXb`Hk;jksK_5S-B*Y{vv2jQjZS_U^Z}7dZK_}}dZ7n_h}%&e zo(@Kou#TgT&;(F__+r25G1Y!MFm_f1eMWdnjU&o1(w!=tUNbJ`JnLkOoAhRwym?(^%BiAz>AfgLlA*uXF2Jxzdt;{^#33_K7SU*P&dx~a)Iy}iIx(6X#JBOF*UfK>@tn*JjUCQYl_9zh;w&LI1oW(|R*kjKW# K!IEh~p#2-#@8LoK literal 0 HcmV?d00001 diff --git a/public/images/hardware-keypress-120.png b/public/images/hardware-keypress-120.png index c1d804b14fd2113a03acec5a3df373103ccbcc63..bec0f39482bd584a507c8e0e41d65026fae71802 100644 GIT binary patch literal 8667 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 K!IEh~p#2-#@8LoK diff --git a/views/default-min.handlebars b/views/default-min.handlebars index 31a6e765..d688c160 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -1 +1 @@ - MeshCentral
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file + MeshCentral
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file diff --git a/views/default.handlebars b/views/default.handlebars index 05bc7abf..76a44d3a 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -1447,38 +1447,44 @@ var end = ""; var x = "Hardware keys are used as secondary login authentication."; x += ""; + var keyType1 = 0; if (message.keys && message.keys.length > 0) { for (var i in message.keys) { var key = message.keys[i]; - x += start + '' + key.name + "" + end; + var type = 'OTP'; + if (key.type == 1) { keyType1++; type = 'U2F'; } + x += start + '' + key.name + "" + end; } } else { x += start + 'No Hardware Keys Configured' + end; } x += "
"; x += "
"; - //x += ""; - - if (u2fSupported()) { - x += ""; - } else { - x += "No hardware key support on this browser."; - } + x += ""; + if ((features & 0x4000) != 0) { x += ""; } x += "

"; setDialogMode(2, "Manage Hardware Login Keys", 8, null, x, 'otpauth-hardware-manage'); - if (u2fSupported() && (message.keys.length > 0)) { QE('d2addkey', false); } + if ((u2fSupported() == false) || (keyType1 > 0)) { QE('d2addkey1', false); } + break; + } + case 'otp-hkey-yubikey-add': { + if (message.result) { + meshserver.send({ action: 'otp-hkey-get' }); // Success, ask for the full list of keys. + } else { + setDialogMode(2, "Add Hardware Login Key", 1, null, '
Error, Unable to add key.

'); + } break; } case 'otp-hkey-setup-request': { if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return; - var x = "Press the key button now.

"; + var x = "Press the key button now.

"; setDialogMode(2, "Add Hardware Login Key", 2, null, x); window.u2f.register(message.request.appId, [message.request], [], function (registrationResponse) { if (registrationResponse.registrationData) { meshserver.send({ action: 'otp-hkey-setup-response', request: message.request, response: registrationResponse, name: Q('dp1keyname').value }); setDialogMode(2, "Add Hardware Login Key", 0, null, '
Checking...


', 'otpauth-hardware-manage'); } else { - setDialogMode(0); + setDialogMode(2, "Add Hardware Login Key", 1, null, '
Error code ' + registrationResponse.errorCode + '

'); } }); break; @@ -5310,38 +5316,32 @@ meshserver.send({ action: 'otp-hkey-get' }); } - function account_addhkey() { - var x = "Type in the name of the key to add.

"; - x += addHtmlValue('Key Name', ''); - setDialogMode(2, "Add Hardware Login Key", 3, account_addhkeyEx, x); + function account_addhkey(type) { + if (type == 1) { + var x = "Type in the name of the key to add.

"; + x += addHtmlValue('Key Name', ''); + } else if (type == 2) { + var x = "Type in a key name, select the OTP box and press the USB key button

"; + x += addHtmlValue('Key Name', ''); + x += addHtmlValue('OTP from key', ''); + } + setDialogMode(2, "Add Hardware Login Key", 3, account_addhkeyEx, x, type); Q('dp1keyname').focus(); } - function account_addhkeyValidate(e) { - if ((e != null) && (e.keyCode == 13)) { dialogclose(1); } + function account_addhkeyValidate(e,action) { + if ((e != null) && (e.keyCode == 13)) { if (action == 2) { dialogclose(1); } else { Q('dp1key').focus(); } } } - function account_addhkeyEx() { + function account_addhkeyEx(button, type) { var name = Q('dp1keyname').value; if (name == '') { name = 'MyKey'; } - meshserver.send({ action: 'otp-hkey-setup-request', name: name }); - } - - function account_addYubiKey() { - if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return; - var x = "Type in a name for the key and press button on the key to register the new hardware key.

"; - x += addHtmlValue('Key Name', ''); - x += addHtmlValue('Key Token', ''); - setDialogMode(2, "Add Yubikey", 3, account_addYubiKeyEx, x); - account_addYubiKeyValidate(); - } - - function account_addYubiKeyValidate() { - QE('idx_dlgOkButton', (Q('dp1keyname').value.length > 0) && (Q('dp1keytoken').value.length > 0)); - } - - function account_addYubiKeyEx() { - meshserver.send({ action: 'otp-hkey-yubikey-add', name: Q('dp1keyname').value, otp: Q('dp1keytoken').value }); + if (type == 1) { + meshserver.send({ action: 'otp-hkey-setup-request', name: name }); + } else if (type == 2) { + meshserver.send({ action: 'otp-hkey-yubikey-add', name: name, otp: Q('dp1key').value }); + setDialogMode(2, "Add Hardware Login Key", 0, null, "
Checking...


", 'otpauth-hardware-manage'); + } } function account_removehkey(index) { diff --git a/views/login-min.handlebars b/views/login-min.handlebars index 4a09c04c..2e45fd30 100644 --- a/views/login-min.handlebars +++ b/views/login-min.handlebars @@ -1 +1 @@ - MeshCentral - Login
{{{title}}}
{{{title2}}}

Welcome

Connect to your home or office devices from anywhere in the world using MeshCentral, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.


\ No newline at end of file + MeshCentral - Login
{{{title}}}
{{{title2}}}

Welcome

Connect to your home or office devices from anywhere in the world using MeshCentral, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.


\ No newline at end of file diff --git a/views/login-mobile-min.handlebars b/views/login-mobile-min.handlebars index 16a50e47..baeadf9c 100644 --- a/views/login-mobile-min.handlebars +++ b/views/login-mobile-min.handlebars @@ -1 +1 @@ - MeshCentral - Login
{{{title}}}
{{{title2}}}
\ No newline at end of file + MeshCentral - Login
{{{title}}}
{{{title2}}}
\ No newline at end of file diff --git a/views/login-mobile.handlebars b/views/login-mobile.handlebars index 9edf2d0d..c8aef775 100644 --- a/views/login-mobile.handlebars +++ b/views/login-mobile.handlebars @@ -150,7 +150,7 @@ Login token: - + @@ -351,9 +351,9 @@ function checkToken() { var t1 = Q('tokenInput').value; - var t2 = t1.replace(/\D/g, ''); + var t2 = t1.split(' ').join(''); if (t1 != t2) { Q('tokenInput').value = t2; } - QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8)); + QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8) || (Q('tokenInput').value.length == 44)); } // diff --git a/views/login.handlebars b/views/login.handlebars index c833f28f..3457af8a 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -223,7 +223,7 @@ Login token: - + @@ -446,9 +446,9 @@ function checkToken() { var t1 = Q('tokenInput').value; - var t2 = t1.replace(/\D/g, ''); + var t2 = t1.split(' ').join(''); if (t1 != t2) { Q('tokenInput').value = t2; } - QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8)); + QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8) || (Q('tokenInput').value.length == 44)); } // diff --git a/webserver.js b/webserver.js index 7687abe5..f8addf87 100644 --- a/webserver.js +++ b/webserver.js @@ -342,43 +342,76 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } // Check the 2-step auth token - function checkUserOneTimePassword(domain, user, token, hwtoken1, hwtoken2) { + function checkUserOneTimePassword(domain, user, token, hwtoken1, hwtoken2, func) { const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName != 'un-configured') && (obj.args.lanonly !== true) && (obj.args.nousers !== true)); - if (twoStepLoginSupported == false) return true; + if (twoStepLoginSupported == false) { func(true); return; }; - // Check hardware key + // Check U2F hardware key if (user.otphkeys && (user.otphkeys.length > 0) && (typeof (hwtoken1) == 'string') && (typeof (hwtoken2) == 'string')) { - // Check hardware token - var authRequest = null, authResponse = null; - try { authRequest = JSON.parse(hwtoken1); } catch (ex) { } - try { authResponse = JSON.parse(hwtoken2); } catch (ex) { } - if ((authRequest != null) && (authResponse != null)) { - const u2f = require('u2f'); - const result = u2f.checkSignature(authRequest[0], authResponse, user.otphkeys[0].publicKey); - if (result.successful === true) return true; + var u2fpublicKey = null; + + // Find a U2F key + for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fpublicKey = user.otphkeys[i].publicKey; } } + + if (u2fpublicKey != null) { + // Check hardware token + var authRequest = null, authResponse = null; + try { authRequest = JSON.parse(hwtoken1); } catch (ex) { } + try { authResponse = JSON.parse(hwtoken2); } catch (ex) { } + if ((authRequest != null) && (authResponse != null)) { + const u2f = require('u2f'); + const result = u2f.checkSignature(authRequest[0], authResponse, u2fpublicKey); + if (result.successful === true) { func(true); return; }; + } } } // Check Google Authenticator const otplib = require('otplib') - if (user.otpsecret && (typeof (token) == 'string') && (otplib.authenticator.check(token, user.otpsecret) == true)) return true; + if (user.otpsecret && (typeof (token) == 'string') && (token.length == 6) && (otplib.authenticator.check(token, user.otpsecret) == true)) { func(true); return; }; // Check written down keys - if ((user.otpkeys != null) && (user.otpkeys.keys != null)) { + if ((user.otpkeys != null) && (user.otpkeys.keys != null) && (typeof (token) == 'string') && (token.length == 8)) { var tokenNumber = parseInt(token); - for (var i = 0; i < user.otpkeys.keys.length; i++) { if ((tokenNumber === user.otpkeys.keys[i].p) && (user.otpkeys.keys[i].u === true)) { user.otpkeys.keys[i].u = false; return true; } } + for (var i = 0; i < user.otpkeys.keys.length; i++) { if ((tokenNumber === user.otpkeys.keys[i].p) && (user.otpkeys.keys[i].u === true)) { user.otpkeys.keys[i].u = false; func(true); return; } } } - return false; + // Check OTP hardware key + if (domain.yubikey.id && domain.yubikey.secret && user.otphkeys && (user.otphkeys.length > 0) && (typeof (token) == 'string') && (token.length == 44)) { + var keyId = token.substring(0, 12); + + // Find a matching OPT key + var match = false; + for (var i = 0; i < user.otphkeys.length; i++) { if ((user.otphkeys[i].type === 2) && (user.otphkeys[i].keyid === keyId)) { match = true; } } + + // If we have a match, check the OTP + if (match === true) { + var yubikeyotp = require('yubikeyotp'); + var request = { otp: token, id: domain.yubikey.id, key: domain.yubikey.secret, timestamp: true } + if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; } + yubikeyotp.verifyOTP(request, function (err, results) { func(results.status == 'OK'); }); + return; + } + } + + func(false); } - // Return a hardware key challenge + // Return a U2F hardware key challenge + // TODO: Figure out how to support many U2F keys at the same time. function getHardwareKeyChallenge(domain, user) { if (user.otphkeys && (user.otphkeys.length > 0)) { - var requests = []; - const u2f = require('u2f'); - for (var i in user.otphkeys) { requests.push(u2f.request('https://' + obj.parent.certificates.CommonName, user.otphkeys[i].keyHandle)); } - return JSON.stringify(requests); + // Find a U2F key + var u2fKeyHandle = null; + for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeyHandle = user.otphkeys[i].keyHandle; } } + + // Generate a U2F challenge + if (u2fKeyHandle != null) { + var requests = []; + const u2f = require('u2f'); + for (var i in user.otphkeys) { requests.push(u2f.request('https://' + obj.parent.certificates.CommonName, u2fKeyHandle)); } + return JSON.stringify(requests); + } } return ''; } @@ -398,89 +431,24 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Check if this user has 2-step login active if (checkUserOneTimePasswordRequired(domain, user)) { - if (checkUserOneTimePassword(domain, user, req.body.token, req.body.hwtoken1, req.body.hwtoken2) == false) { - // 2-step auth is required, but the token is not present or not valid. - if (user.otpsecret != null) { req.session.error = 'Invalid token, try again.'; } - req.session.loginmode = '4'; - req.session.tokenusername = xusername; - req.session.tokenpassword = xpassword; - res.redirect(domain.url); - return; - } - } - - /* - // Check if this user has 2-step login active - var tokenValid = 0; - const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName != 'un-configured') && (obj.args.lanonly !== true) && (obj.args.nousers !== true)); - const otplib = require('otplib') - otplib.authenticator.options = { window: 6 }; // Set +/- 3 minute window - if (twoStepLoginSupported && user.otpsecret && ((typeof (req.body.token) != 'string') || ((tokenValid = otplib.authenticator.check(req.body.token, user.otpsecret)) !== true))) { - // Failed OTP, check user's one time passwords - console.log(user); - if ((req.body.token != null) && ((user.otpkeys != null) && (user.otpkeys.keys != null)) || (user.otphkeys && user.otphkeys.length > 0)) { - var found = null; - var tokenNumber = parseInt(req.body.token); - for (var i = 0; i < user.otpkeys.keys.length; i++) { if ((tokenNumber === user.otpkeys.keys[i].p) && (user.otpkeys.keys[i].u === true)) { user.otpkeys.keys[i].u = false; found = i; } } - if (found == null) { + checkUserOneTimePassword(domain, user, req.body.token, req.body.hwtoken1, req.body.hwtoken2, function (result) { + if (result == false) { // 2-step auth is required, but the token is not present or not valid. if (user.otpsecret != null) { req.session.error = 'Invalid token, try again.'; } req.session.loginmode = '4'; req.session.tokenusername = xusername; req.session.tokenpassword = xpassword; res.redirect(domain.url); - return; + } else { + // Login succesful + completeLoginRequest(req, res, domain, user, userid); } - } else { - // 2-step auth is required, but the token is not present or not valid. - if (user.otpsecret != null) { req.session.error = 'Invalid token, try again.'; } - req.session.loginmode = '4'; - req.session.tokenusername = xusername; - req.session.tokenpassword = xpassword; - res.redirect(domain.url); - return; - } - } - */ - - // Save login time - user.login = Math.floor(Date.now() / 1000); - obj.db.SetUser(user); - - // Regenerate session when signing in to prevent fixation - //req.session.regenerate(function () { - // Store the user's primary key in the session store to be retrieved, or in this case the entire user object - // req.session.success = 'Authenticated as ' + user.name + 'click to logout. You may now access /restricted.'; - delete req.session.loginmode; - delete req.session.tokenusername; - delete req.session.tokenpassword; - req.session.userid = userid; - req.session.domainid = domain.id; - req.session.currentNode = ''; - if (req.session.passhint) { delete req.session.passhint; } - if (req.body.viewmode) { req.session.viewmode = req.body.viewmode; } - if (req.body.host) { - // TODO: This is a terrible search!!! FIX THIS. - /* - obj.db.GetAllType('node', function (err, docs) { - for (var i = 0; i < docs.length; i++) { - if (docs[i].name == req.body.host) { - req.session.currentNode = docs[i]._id; - break; - } - } - console.log("CurrentNode: " + req.session.currentNode); - // This redirect happens after finding node is completed - res.redirect(domain.url); }); - */ - res.redirect(domain.url); // Temporary - } else { - res.redirect(domain.url); + return; } - //}); - obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id }); + // Login succesful + completeLoginRequest(req, res, domain, user, userid); } else { delete req.session.loginmode; if (err == 'locked') { req.session.error = 'Account locked.'; } else { req.session.error = 'Login failed, check username and password.'; } @@ -494,6 +462,47 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { }); } + function completeLoginRequest(req, res, domain, user, userid) { + // Save login time + user.login = Math.floor(Date.now() / 1000); + obj.db.SetUser(user); + + // Regenerate session when signing in to prevent fixation + //req.session.regenerate(function () { + // Store the user's primary key in the session store to be retrieved, or in this case the entire user object + // req.session.success = 'Authenticated as ' + user.name + 'click to logout. You may now access /restricted.'; + delete req.session.loginmode; + delete req.session.tokenusername; + delete req.session.tokenpassword; + req.session.userid = userid; + req.session.domainid = domain.id; + req.session.currentNode = ''; + if (req.session.passhint) { delete req.session.passhint; } + if (req.body.viewmode) { req.session.viewmode = req.body.viewmode; } + if (req.body.host) { + // TODO: This is a terrible search!!! FIX THIS. + /* + obj.db.GetAllType('node', function (err, docs) { + for (var i = 0; i < docs.length; i++) { + if (docs[i].name == req.body.host) { + req.session.currentNode = docs[i]._id; + break; + } + } + console.log("CurrentNode: " + req.session.currentNode); + // This redirect happens after finding node is completed + res.redirect(domain.url); + }); + */ + res.redirect(domain.url); // Temporary + } else { + res.redirect(domain.url); + } + //}); + + obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id }); + } + function handleCreateAccountRequest(req, res) { const domain = checkUserIpAddress(req, res); if ((domain == null) || (domain.auth == 'sspi')) return;