Improved MeshCmd error messages.

Ylian Saint-Hilaire committed May 25, 2021 at 22:30 UTC f73c5d2a123351626ee87cb144db96e5fc866053
4 files changed +100 -81
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/meshcmd.js
+7 -1
@@ -2034,7 +2034,13 @@ function OnServerWebSocket(msg, s, head) {
2034 } else {
2035 console.log("Login token required, use --token [token].");
2036 }
2037 - } else { console.log("Invalid username or password."); }
2037 + } else if (command.msg == 'badtlscert') {
2038 + console.log("Invalid TLS certificate detected.");
2039 + } else if (command.msg == 'badargs') {
2040 + console.log("Invalid protocol arguments.");
2041 + } else {
2042 + console.log("Invalid username/password.");
2043 + }
2044 } else { console.log("Server disconnected: " + command.msg); }
2045 process.exit(1);
2046 return;
webserver.js
+93 -80
@@ -6048,12 +6048,20 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
6048
6049 switch (command.action) {
6050 case 'serverAuth': { // This command is used to perform server "inner" authentication.
6051 - if (obj.common.validateString(command.cnonce, 1, 256) == false) break; // Check the client nonce
6052 - if (obj.common.validateString(command.tlshash, 1, 512) == false) break; // Check the TLS hash
6051 + // Check the client nonce and TLS hash
6052 + if ((obj.common.validateString(command.cnonce, 1, 256) == false) || (obj.common.validateString(command.tlshash, 1, 512) == false)) {
6053 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'badargs' })); } catch (ex) { }
6054 + try { ws.close(); } catch (ex) { }
6055 + break;
6056 + }
6057
6058 // Check that the TLS hash is an acceptable one.
6059 var h = Buffer.from(command.tlshash, 'hex').toString('binary');
6056 - if ((obj.webCertificateHashs[domain.id] != h) && (obj.webCertificateFullHashs[domain.id] != h) && (obj.defaultWebCertificateHash != h) && (obj.defaultWebCertificateFullHash != h)) { try { ws.close(); } catch (ex) { } return; }
6060 + if ((obj.webCertificateHashs[domain.id] != h) && (obj.webCertificateFullHashs[domain.id] != h) && (obj.defaultWebCertificateHash != h) && (obj.defaultWebCertificateFullHash != h)) {
6061 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'badtlscert' })); } catch (ex) { }
6062 + try { ws.close(); } catch (ex) { }
6063 + return;
6064 + }
6065
6066 // TLS hash check is a success, sign the request.
6067 // Perform the hash signature using the server agent certificate
@@ -6069,89 +6077,94 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
6077 // Check username and password authentication
6078 if ((typeof command.username == 'string') && (typeof command.password == 'string')) {
6079 obj.authenticate(Buffer.from(command.username, 'base64').toString(), Buffer.from(command.password, 'base64').toString(), domain, function (err, userid, passhint, loginOptions) {
6072 - var user = obj.users[userid];
6073 - if ((err == null) && (user)) {
6074 - // Check if a 2nd factor is needed
6075 - var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap'))
6076 -
6077 - // See if we support two-factor trusted cookies
6078 - var twoFactorCookieDays = 30;
6079 - if (typeof domain.twofactorcookiedurationdays == 'number') { twoFactorCookieDays = domain.twofactorcookiedurationdays; }
6080 -
6081 - if (checkUserOneTimePasswordRequired(domain, user, req, loginOptions) == true) {
6082 - // Figure out if email 2FA is allowed
6083 - var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
6084 - var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
6085 - //var push2fa = ((parent.firebase != null) && (user.otpdev != null));
6086 - if ((typeof command.token != 'string') || (command.token == '**email**') || (command.token == '**sms**')/* || (command.token == '**push**')*/) {
6087 - if ((command.token == '**email**') && (email2fa == true)) {
6088 - // Cause a token to be sent to the user's registered email
6089 - user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
6090 - obj.db.SetUser(user);
6091 - parent.debug('web', 'Sending 2FA email to: ' + user.email);
6092 - domain.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key);
6093 - // Ask for a login token & confirm email was sent
6094 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
6095 - } else if ((command.token == '**sms**') && (sms2fa == true)) {
6096 - // Cause a token to be sent to the user's phone number
6097 - user.otpsms = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() };
6098 - obj.db.SetUser(user);
6099 - parent.debug('web', 'Sending 2FA SMS to: ' + user.phone);
6100 - parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req));
6101 - // Ask for a login token & confirm sms was sent
6102 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
6103 - /*
6104 - } else if ((command.token == '**push**') && (push2fa == true)) {
6105 - // Cause push notification to device
6106 - const code = Buffer.from(obj.common.zeroPad(getRandomSixDigitInteger(), 6)).toString('base64');
6107 - const authCookie = parent.encodeCookie({ a: 'checkAuth', c: code, u: user._id, n: user.otpdev });
6108 - var payload = { notification: { title: "MeshCentral", body: user.name + " authentication" }, data: { url: '2fa://auth?code=' + code + '&c=' + authCookie } };
6109 - var options = { priority: 'High', timeToLive: 60 }; // TTL: 1 minute
6110 - parent.firebase.sendToDevice(user.otpdev, payload, options, function (id, err, errdesc) {
6111 - if (err == null) { parent.debug('email', 'Successfully auth check send push message to device'); } else { parent.debug('email', 'Failed auth check push message to device, error: ' + errdesc); }
6112 - });
6113 - */
6114 - } else {
6115 - // Ask for a login token
6116 - parent.debug('web', 'Asking for login token');
6117 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (ex) { console.log(ex); }
6118 - }
6119 - } else {
6120 - checkUserOneTimePassword(req, domain, user, command.token, null, function (result) {
6121 - if (result == false) {
6122 - // Failed, ask for a login token again
6123 - parent.debug('web', 'Invalid login token, asking again');
6124 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
6080 + if ((err != null) || (userid == null)) {
6081 + // Invalid authentication
6082 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'noauth-2c' })); } catch (ex) { }
6083 + try { ws.close(); } catch (ex) { }
6084 + } else {
6085 + var user = obj.users[userid];
6086 + if ((err == null) && (user)) {
6087 + // Check if a 2nd factor is needed
6088 + var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap'))
6089 +
6090 + // See if we support two-factor trusted cookies
6091 + var twoFactorCookieDays = 30;
6092 + if (typeof domain.twofactorcookiedurationdays == 'number') { twoFactorCookieDays = domain.twofactorcookiedurationdays; }
6093 +
6094 + if (checkUserOneTimePasswordRequired(domain, user, req, loginOptions) == true) {
6095 + // Figure out if email 2FA is allowed
6096 + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
6097 + var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
6098 + //var push2fa = ((parent.firebase != null) && (user.otpdev != null));
6099 + if ((typeof command.token != 'string') || (command.token == '**email**') || (command.token == '**sms**')/* || (command.token == '**push**')*/) {
6100 + if ((command.token == '**email**') && (email2fa == true)) {
6101 + // Cause a token to be sent to the user's registered email
6102 + user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
6103 + obj.db.SetUser(user);
6104 + parent.debug('web', 'Sending 2FA email to: ' + user.email);
6105 + domain.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key);
6106 + // Ask for a login token & confirm email was sent
6107 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
6108 + } else if ((command.token == '**sms**') && (sms2fa == true)) {
6109 + // Cause a token to be sent to the user's phone number
6110 + user.otpsms = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() };
6111 + obj.db.SetUser(user);
6112 + parent.debug('web', 'Sending 2FA SMS to: ' + user.phone);
6113 + parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req));
6114 + // Ask for a login token & confirm sms was sent
6115 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
6116 + /*
6117 + } else if ((command.token == '**push**') && (push2fa == true)) {
6118 + // Cause push notification to device
6119 + const code = Buffer.from(obj.common.zeroPad(getRandomSixDigitInteger(), 6)).toString('base64');
6120 + const authCookie = parent.encodeCookie({ a: 'checkAuth', c: code, u: user._id, n: user.otpdev });
6121 + var payload = { notification: { title: "MeshCentral", body: user.name + " authentication" }, data: { url: '2fa://auth?code=' + code + '&c=' + authCookie } };
6122 + var options = { priority: 'High', timeToLive: 60 }; // TTL: 1 minute
6123 + parent.firebase.sendToDevice(user.otpdev, payload, options, function (id, err, errdesc) {
6124 + if (err == null) { parent.debug('email', 'Successfully auth check send push message to device'); } else { parent.debug('email', 'Failed auth check push message to device, error: ' + errdesc); }
6125 + });
6126 + */
6127 } else {
6126 - // We are authenticated with 2nd factor.
6127 - // Check email verification
6128 - if (emailcheck && (user.email != null) && (user.emailVerified !== true)) {
6129 - parent.debug('web', 'Invalid login, asking for email validation');
6130 - try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true })); ws.close(); } catch (e) { }
6128 + // Ask for a login token
6129 + parent.debug('web', 'Asking for login token');
6130 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (ex) { console.log(ex); }
6131 + }
6132 + } else {
6133 + checkUserOneTimePassword(req, domain, user, command.token, null, function (result) {
6134 + if (result == false) {
6135 + // Failed, ask for a login token again
6136 + parent.debug('web', 'Invalid login token, asking again');
6137 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
6138 } else {
6132 - // We are authenticated
6133 - ws._socket.pause();
6134 - ws.removeAllListeners(['message', 'close', 'error']);
6135 - func(ws, req, domain, user);
6139 + // We are authenticated with 2nd factor.
6140 + // Check email verification
6141 + if (emailcheck && (user.email != null) && (user.emailVerified !== true)) {
6142 + parent.debug('web', 'Invalid login, asking for email validation');
6143 + try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true })); ws.close(); } catch (e) { }
6144 + } else {
6145 + // We are authenticated
6146 + ws._socket.pause();
6147 + ws.removeAllListeners(['message', 'close', 'error']);
6148 + func(ws, req, domain, user);
6149 + }
6150 }
6137 - }
6138 - });
6139 - }
6140 - } else {
6141 - // Check email verification
6142 - if (emailcheck && (user.email != null) && (user.emailVerified !== true)) {
6143 - parent.debug('web', 'Invalid login, asking for email validation');
6144 - var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
6145 - var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
6146 - try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true })); ws.close(); } catch (e) { }
6151 + });
6152 + }
6153 } else {
6148 - // We are authenticated
6149 - ws._socket.pause();
6150 - ws.removeAllListeners(['message', 'close', 'error']);
6151 - func(ws, req, domain, user);
6154 + // Check email verification
6155 + if (emailcheck && (user.email != null) && (user.emailVerified !== true)) {
6156 + parent.debug('web', 'Invalid login, asking for email validation');
6157 + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
6158 + var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
6159 + try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true })); ws.close(); } catch (e) { }
6160 + } else {
6161 + // We are authenticated
6162 + ws._socket.pause();
6163 + ws.removeAllListeners(['message', 'close', 'error']);
6164 + func(ws, req, domain, user);
6165 + }
6166 }
6167 }
6154 -
6168 }
6169 });
6170 } else {