Server hardening, user alerts and user permission checking.

Ylian Saint-Hilaire committed Apr 5, 2018 at 16:45 UTC 3c1797a01688fb9da28a56fd84ca637fb5eaf536
5 files changed +216 -129
common.js
+8 -2
@@ -20,7 +20,7 @@ module.exports.IntToStrX = function(v) { return String.fromCharCode(v & 0xFF, (v
20 module.exports.MakeToArray = function(v) { if (!v || v == null || typeof v == 'object') return v; return [v]; }
21 module.exports.SplitArray = function(v) { return v.split(','); }
22 module.exports.Clone = function(v) { return JSON.parse(JSON.stringify(v)); }
23 -module.exports.IsFilenameValid = (function () { var x1 = /^[^\\/:\*\?"<>\|]+$/, x2 = /^\./, x3 = /^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname) { return x1.test(fname) && !x2.test(fname) && !x3.test(fname) && (fname[0] != '.'); } })();
23 +module.exports.IsFilenameValid = (function () { var x1 = /^[^\\/:\*\?"<>\|]+$/, x2 = /^\./, x3 = /^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname) { return module.exports.validateString(fname, 1, 4096) && x1.test(fname) && !x2.test(fname) && !x3.test(fname) && (fname[0] != '.'); } })();
24
25 // Move an element from one position in an array to a new position
26 module.exports.ArrayElementMove = function(arr, from, to) { arr.splice(to, 0, arr.splice(from, 1)[0]); };
@@ -125,4 +125,10 @@ module.exports.objKeysToLower = function (obj) {
125 if (typeof obj[i] == 'object') { module.exports.objKeysToLower(obj[i]); } // LowerCase all key names in the child object
126 }
127 return obj;
128 -}
\ No newline at end of file
128 +}
129 +
130 +// Validation methods
131 +module.exports.validateString = function(str, minlen, maxlen) { return ((str != null) && (typeof str == 'string') && ((minlen == null) || (str.length >= minlen)) && ((maxlen == null) || (str.length <= maxlen))); }
132 +module.exports.validateInt = function(int, minval, maxval) { return ((int != null) && (typeof int == 'number') && ((minval == null) || (int >= minval)) && ((maxval == null) || (int <= maxval))); }
133 +module.exports.validateArray = function(array, minlen, maxlen) { return ((array != null) && Array.isArray(array) && ((minlen == null) || (array.length >= minlen)) && ((maxlen == null) || (array.length <= maxlen))); }
134 +module.exports.validateObject = function(obj) { return ((obj != null) && (typeof obj == 'object')); }
meshuser.js
+169 -107
@@ -28,7 +28,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
28 }
29
30 // Convert a mesh path array into a real path on the server side
31 - function meshPathToRealPath(meshpath) {
31 + function meshPathToRealPath(meshpath, user) {
32 + if (obj.common.validateArray(meshpath, 1) == false) return null;
33 + var splitid = meshpath[0].split('/');
34 + if (splitid[0] == 'user') {
35 + // Check user access
36 + if (meshpath[0] != user._id) return null; // Only allow own user folder
37 + } else if (splitid[0] == 'mesh') {
38 + // Check mesh access
39 + var meshrights = user.links[meshpath[0]];
40 + if ((meshrights == null) || ((meshrights & 32) == 0)) return null; // This user must have mesh rights to "server files"
41 + } else return null;
42 var rootfolder = meshpath[0], rootfoldersplit = rootfolder.split('/'), domainx = 'domain';
43 if (rootfoldersplit[1].length > 0) domainx = 'domain-' + rootfoldersplit[1];
44 var path = obj.parent.path.join(obj.parent.filespath, domainx, rootfoldersplit[0] + "-" + rootfoldersplit[2]);
@@ -94,8 +104,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
104
105 // When data is received from the web socket
106 ws.on('message', function (msg) {
97 - var user = obj.parent.users[req.session.userid];
98 - var command = JSON.parse(msg.toString('utf8'))
107 + var command, user = obj.parent.users[req.session.userid];
108 + try { command = JSON.parse(msg.toString('utf8')); } catch (e) { return; }
109 + if ((user == null) || (obj.common.validateString(command.action, 3, 32) == false)) return; // User must be set and action must be a string between 3 and 32 chars
110 +
111 switch (command.action) {
112 case 'meshes':
113 {
@@ -114,6 +126,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
126 } else {
127 // Request list of all nodes for one specific meshid
128 var meshid = command.meshid;
129 + if (obj.common.validateString(meshid, 0, 128) == false) return;
130 if (meshid.split('/').length == 0) { meshid = 'mesh/' + domain.id + '/' + command.meshid; }
131 if (user.links[meshid] != null) { links.push(meshid); }
132 }
@@ -149,6 +162,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
162 {
163 // Query the database for the power timeline for a given node
164 // The result is a compacted array: [ startPowerState, startTimeUTC, powerState ] + many[ deltaTime, powerState ]
165 + if (obj.common.validateString(command.nodeid, 0, 128) == false) return;
166 obj.db.getPowerTimeline(command.nodeid, function (err, docs) {
167 if (err == null && docs.length > 0) {
168 var timeline = [], time = null, previousPower;
@@ -204,27 +218,26 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
218 // Check permissions
219 if ((user.siteadmin & 8) != 0) {
220 // Perform a file operation (Create Folder, Delete Folder, Delete File...)
207 - if ((command.path != null) && (typeof command.path == 'object') && command.path.length > 0) {
208 - var sendUpdate = true;
209 - var path = meshPathToRealPath(command.path); // TODO: Check mesh rights!!!!!
210 - if (path == null) break;
211 -
212 - if ((command.fileop == 'createfolder') && (obj.common.IsFilenameValid(command.newfolder) == true)) { try { obj.fs.mkdirSync(path + "/" + command.newfolder); } catch (e) { } } // Create a new folder
213 - else if (command.fileop == 'delete') { for (var i in command.delfiles) { if (obj.common.IsFilenameValid(command.delfiles[i]) == true) { var fullpath = path + "/" + command.delfiles[i]; try { obj.fs.rmdirSync(fullpath); } catch (e) { try { obj.fs.unlinkSync(fullpath); } catch (e) { } } } } } // Delete
214 - else if ((command.fileop == 'rename') && (obj.common.IsFilenameValid(command.oldname) == true) && (obj.common.IsFilenameValid(command.newname) == true)) { try { obj.fs.renameSync(path + "/" + command.oldname, path + "/" + command.newname); } catch (e) { } } // Rename
215 - else if ((command.fileop == 'copy') || (command.fileop == 'move')) {
216 - var scpath = meshPathToRealPath(command.scpath); // TODO: Check mesh rights!!!!!
217 - if (scpath == null) break;
218 - // TODO: Check quota if this is a copy!!!!!!!!!!!!!!!!
219 - for (var i in command.names) {
220 - var s = obj.path.join(scpath, command.names[i]), d = obj.path.join(path, command.names[i]);
221 - sendUpdate = false;
222 - copyFile(s, d, function (op) { if (op != null) { obj.fs.unlink(op, function () { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); }); } else { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); } }, ((command.fileop == 'move') ? s : null));
223 - }
221 + if (obj.common.validateString(command.fileop, 4, 16) == false) return;
222 + var sendUpdate = true, path = meshPathToRealPath(command.path, user); // This will also check access rights
223 + if (path == null) break;
224 +
225 + if ((command.fileop == 'createfolder') && (obj.common.IsFilenameValid(command.newfolder) == true)) { try { obj.fs.mkdirSync(path + "/" + command.newfolder); } catch (e) { } } // Create a new folder
226 + else if (command.fileop == 'delete') { if (obj.common.validateArray(command.delfiles, 1) == false) return; for (var i in command.delfiles) { if (obj.common.IsFilenameValid(command.delfiles[i]) == true) { var fullpath = path + "/" + command.delfiles[i]; try { obj.fs.rmdirSync(fullpath); } catch (e) { try { obj.fs.unlinkSync(fullpath); } catch (e) { } } } } } // Delete
227 + else if ((command.fileop == 'rename') && (obj.common.IsFilenameValid(command.oldname) == true) && (obj.common.IsFilenameValid(command.newname) == true)) { try { obj.fs.renameSync(path + "/" + command.oldname, path + "/" + command.newname); } catch (e) { } } // Rename
228 + else if ((command.fileop == 'copy') || (command.fileop == 'move')) {
229 + if (obj.common.validateArray(command.names, 1) == false) return;
230 + var scpath = meshPathToRealPath(command.scpath, user); // This will also check access rights
231 + if (scpath == null) break;
232 + // TODO: Check quota if this is a copy!!!!!!!!!!!!!!!!
233 + for (var i in command.names) {
234 + var s = obj.path.join(scpath, command.names[i]), d = obj.path.join(path, command.names[i]);
235 + sendUpdate = false;
236 + copyFile(s, d, function (op) { if (op != null) { obj.fs.unlink(op, function () { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); }); } else { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); } }, ((command.fileop == 'move') ? s : null));
237 }
225 -
226 - if (sendUpdate == true) { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); } // Fire an event causing this user to update this files
238 }
239 +
240 + if (sendUpdate == true) { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); } // Fire an event causing this user to update this files
241 }
242 break;
243 }
@@ -232,32 +245,31 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
245 {
246 // Route a message.
247 // This this command has a nodeid, that is the target.
235 - if (command.nodeid != null) {
236 - var splitnodeid = command.nodeid.split('/');
237 - // Check that we are in the same domain and the user has rights over this node.
238 - if ((splitnodeid[0] == 'node') && (splitnodeid[1] == domain.id)) {
239 - // See if the node is connected
240 - var agent = obj.parent.wsagents[command.nodeid];
241 - if (agent != null) {
248 + if (obj.common.validateString(command.nodeid, 8, 128) == false) return;
249 + var splitnodeid = command.nodeid.split('/');
250 + // Check that we are in the same domain and the user has rights over this node.
251 + if ((splitnodeid[0] == 'node') && (splitnodeid[1] == domain.id)) {
252 + // See if the node is connected
253 + var agent = obj.parent.wsagents[command.nodeid];
254 + if (agent != null) {
255 + // Check if we have permission to send a message to that node
256 + var rights = user.links[agent.dbMeshKey];
257 + if ((rights != null) && ((rights.rights & 8) != 0)) { // 8 is remote control permission
258 + command.sessionid = ws.sessionId; // Set the session id, required for responses.
259 + command.rights = rights.rights; // Add user rights flags to the message
260 + delete command.nodeid; // Remove the nodeid since it's implyed.
261 + agent.send(JSON.stringify(command));
262 + }
263 + } else {
264 + // Check if a peer server is connected to this agent
265 + var routing = obj.parent.parent.GetRoutingServerId(command.nodeid, 1); // 1 = MeshAgent routing type
266 + if (routing != null) {
267 // Check if we have permission to send a message to that node
243 - var rights = user.links[agent.dbMeshKey];
268 + var rights = user.links[routing.meshid];
269 if ((rights != null) && ((rights.rights & 8) != 0)) { // 8 is remote control permission
245 - command.sessionid = ws.sessionId; // Set the session id, required for responses.
246 - command.rights = rights.rights; // Add user rights flags to the message
247 - delete command.nodeid; // Remove the nodeid since it's implyed.
248 - agent.send(JSON.stringify(command));
249 - }
250 - } else {
251 - // Check if a peer server is connected to this agent
252 - var routing = obj.parent.parent.GetRoutingServerId(command.nodeid, 1); // 1 = MeshAgent routing type
253 - if (routing != null) {
254 - // Check if we have permission to send a message to that node
255 - var rights = user.links[routing.meshid];
256 - if ((rights != null) && ((rights.rights & 8) != 0)) { // 8 is remote control permission
257 - command.fromSessionid = ws.sessionId; // Set the session id, required for responses.
258 - command.rights = rights.rights; // Add user rights flags to the message
259 - obj.parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
260 - }
270 + command.fromSessionid = ws.sessionId; // Set the session id, required for responses.
271 + command.rights = rights.rights; // Add user rights flags to the message
272 + obj.parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
273 }
274 }
275 }
@@ -307,35 +319,34 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
319 case 'changeemail':
320 {
321 // Change the email address
310 - if ((command.email != null) && (typeof command.email == 'string') && (command.email.length < 1024)) {
311 - var x = command.email.split('@');
312 - if ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2)) {
313 - if (obj.parent.users[req.session.userid].email != command.email) {
314 - // Check if this email is already validated on a different account
315 - obj.db.GetUserWithVerifiedEmail(domain.id, command.email, function (err, docs) {
316 - if (docs.length > 0) {
317 - // Notify the duplicate email error
318 - ws.send(JSON.stringify({ action: 'msg', type: 'notify', value: 'Failed to change email address, another account already using: <b>' + EscapeHtml(command.email) + '</b>.' }));
319 - } else {
320 - // Update the user's email
321 - var oldemail = user.email;
322 - user.email = command.email;
323 - user.emailVerified = false;
324 - obj.parent.db.SetUser(user);
325 -
326 - // Event the change
327 - var userinfo = obj.common.Clone(user);
328 - delete userinfo.hash;
329 - delete userinfo.passhint;
330 - delete userinfo.salt;
331 - delete userinfo.type;
332 - delete userinfo.domain;
333 - delete userinfo.subscriptions;
334 - delete userinfo.passtype;
335 - obj.parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Changed email of user ' + userinfo.name + ' from ' + oldemail + ' to ' + user.email, domain: domain.id })
336 - }
337 - });
338 - }
322 + if (obj.common.validateString(command.email, 3, 1024) == false) return;
323 + var x = command.email.split('@');
324 + if ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2)) {
325 + if (obj.parent.users[req.session.userid].email != command.email) {
326 + // Check if this email is already validated on a different account
327 + obj.db.GetUserWithVerifiedEmail(domain.id, command.email, function (err, docs) {
328 + if (docs.length > 0) {
329 + // Notify the duplicate email error
330 + ws.send(JSON.stringify({ action: 'msg', type: 'notify', value: 'Failed to change email address, another account already using: <b>' + EscapeHtml(command.email) + '</b>.' }));
331 + } else {
332 + // Update the user's email
333 + var oldemail = user.email;
334 + user.email = command.email;
335 + user.emailVerified = false;
336 + obj.parent.db.SetUser(user);
337 +
338 + // Event the change
339 + var userinfo = obj.common.Clone(user);
340 + delete userinfo.hash;
341 + delete userinfo.passhint;
342 + delete userinfo.salt;
343 + delete userinfo.type;
344 + delete userinfo.domain;
345 + delete userinfo.subscriptions;
346 + delete userinfo.passtype;
347 + obj.parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Changed email of user ' + userinfo.name + ' from ' + oldemail + ' to ' + user.email, domain: domain.id })
348 + }
349 + });
350 }
351 }
352 break;
@@ -343,14 +354,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
354 case 'verifyemail':
355 {
356 // Send a account email verification email
346 - if ((command.email != null) && (typeof command.email == 'string') && (command.email.length < 1024)) {
347 - var x = command.email.split('@');
348 - if ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2)) {
349 - if (obj.parent.users[req.session.userid].email == command.email) {
350 - // Send the verification email
351 - if (obj.parent.parent.mailserver != null) {
352 - obj.parent.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email);
353 - }
357 + if (obj.common.validateString(command.email, 3, 1024) == false) return;
358 + var x = command.email.split('@');
359 + if ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2)) {
360 + if (obj.parent.users[req.session.userid].email == command.email) {
361 + // Send the verification email
362 + if (obj.parent.parent.mailserver != null) {
363 + obj.parent.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email);
364 }
365 }
366 }
@@ -363,10 +373,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
373 if ((user.siteadmin & 2) == 0) break;
374 if (obj.parent.parent.multiServer == null) {
375 // No peering, use simple session counting
366 - for (var i in obj.wssessions) { if (obj.wssessions[i][0].domainid == domain.id) { wssessions[i] = obj.wssessions[i].length; } }
376 + for (var i in obj.parent.wssessions) { if (obj.parent.wssessions[i][0].domainid == domain.id) { wssessions[i] = obj.parent.wssessions[i].length; } }
377 } else {
378 // We have peer servers, use more complex session counting
369 - for (var userid in obj.sessionsCount) { if (userid.split('/')[1] == domain.id) { wssessions[userid] = obj.sessionsCount[userid]; } }
379 + for (var userid in obj.parent.sessionsCount) { if (userid.split('/')[1] == domain.id) { wssessions[userid] = obj.parent.sessionsCount[userid]; } }
380 }
381 ws.send(JSON.stringify({ action: 'wssessioncount', wssessions: wssessions, tag: command.tag })); // wssessions is: userid --> count
382 break;
@@ -375,9 +385,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
385 {
386 // Delete a user account
387 if ((user.siteadmin & 2) == 0) break;
378 - var delusername = command.username, deluserid = command.userid, deluser = obj.parent.users[deluserid];
388 + if (obj.common.validateString(command.userid, 1, 2048) == false) break;
389 + var delusersplit = command.userid.split('/'), deluserid = command.userid, deluser = obj.parent.users[deluserid];
390 + if ((deluser == null) || (delusersplit.length != 3) || (delusersplit[1] != domain.id)) break; // Invalid domain, operation only valid for current domain
391 if ((deluser.siteadmin != null) && (deluser.siteadmin > 0) && (user.siteadmin != 0xFFFFFFFF)) break; // Need full admin to remote another administrator
380 - if ((deluserid.split('/').length != 3) || (deluserid.split('/')[1] != domain.id)) break; // Invalid domain, operation only valid for current domain
392
393 // Delete all files on the server for this account
394 try {
@@ -387,7 +398,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
398
399 obj.db.Remove(deluserid);
400 delete obj.parent.users[deluserid];
390 - obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: deluserid, username: delusername, action: 'accountremove', msg: 'Account removed', domain: domain.id })
401 + obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: deluserid, username: deluser.name, action: 'accountremove', msg: 'Account removed', domain: domain.id })
402 obj.parent.parent.DispatchEvent([deluserid], obj, 'close');
403
404 break;
@@ -396,10 +407,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
407 {
408 // Add a new user account
409 if ((user.siteadmin & 2) == 0) break;
410 + if (obj.common.validateString(command.username, 1, 64) == false) break; // Username is between 1 and 64 characters
411 + if (obj.common.validateString(command.pass, 1, 256) == false) break; // Password is between 1 and 256 characters
412 var newusername = command.username, newuserid = 'user/' + domain.id + '/' + command.username.toLowerCase();
413 if (newusername == '~') break; // This is a reserved user name
414 if (!obj.parent.users[newuserid]) {
402 - var newuser = { type: 'user', _id: newuserid, name: newusername, email: command.email, creation: Date.now(), domain: domain.id };
415 + var newuser = { type: 'user', _id: newuserid, name: newusername, creation: Date.now(), domain: domain.id };
416 + if (obj.common.validateString(command.email, 1, 256) == true) { newuser.email = command.email; } // Email is between 1 and 256 characters
417 obj.parent.users[newuserid] = newuser;
418 // Create a user, generate a salt and hash the password
419 require('./pass').hash(command.pass, function (err, salt, hash) {
@@ -422,9 +436,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
436 if (((user.siteadmin & 2) != 0) || (user.name == command.name)) {
437 var chguserid = 'user/' + domain.id + '/' + command.name.toLowerCase(), chguser = obj.parent.users[chguserid], change = 0;
438 if (chguser) {
425 - if (command.email && chguser.email != command.email) { chguser.email = command.email; change = 1; }
426 - if (command.quota != chguser.quota) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; }
427 - if ((user.siteadmin == 0xFFFFFFFF) && (command.siteadmin != null) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1 }
439 + if (obj.common.validateString(command.email, 1, 256) && (chguser.email != command.email)) { chguser.email = command.email; change = 1; }
440 + if (obj.common.validateInt(command.quota, 0) && (command.quota != chguser.quota)) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; }
441 + if ((user.siteadmin == 0xFFFFFFFF) && obj.common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1 }
442 if (change == 1) {
443 obj.db.SetUser(chguser);
444 obj.parent.parent.DispatchEvent([chguser._id], obj, 'resubscribe');
@@ -442,6 +456,24 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
456 }
457 break;
458 }
459 + case 'notifyuser':
460 + {
461 + // Send a notification message to a user
462 + if ((user.siteadmin & 2) == 0) break;
463 + if (obj.common.validateString(command.userid, 1, 64) == false) break; // Meshname is between 1 and 64 characters
464 + if (obj.common.validateString(command.msg, 1, 4096) == false) break;
465 +
466 + // Create the notification message
467 + var notification = { "action": "msg", "type": "notify", "value": "<b>" + user.name + "</b>: " + EscapeHtml(command.msg), "userid": user._id, "username": user.name };
468 +
469 + // Get the list of sessions for this user
470 + var sessions = obj.parent.wssessions[command.userid];
471 + if (sessions != null) { for (var i in sessions) { sessions[i].send(JSON.stringify(notification)); } }
472 +
473 + if (obj.parent.parent.multiServer != null) {
474 + // TODO: Add multi-server support
475 + }
476 + }
477 case 'serverversion':
478 {
479 // Check the server version
@@ -459,7 +491,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
491 case 'createmesh':
492 {
493 // Create mesh
462 - // TODO: Right now, we only create type 1 Agent-less Intel AMT mesh, or type 2 Agent mesh
494 + if (obj.common.validateString(command.meshname, 1, 64) == false) break; // Meshname is between 1 and 64 characters
495 + if (obj.common.validateString(command.desc, 0, 1024) == false) break; // Mesh description is between 0 and 1024 characters
496 +
497 + // We only create Agent-less Intel AMT mesh (Type1), or Agent mesh (Type2)
498 if ((command.meshtype == 1) || (command.meshtype == 2)) {
499 // Create a type 1 agent-less Intel AMT mesh.
500 obj.parent.crypto.randomBytes(48, function (err, buf) {
@@ -482,6 +517,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
517 case 'deletemesh':
518 {
519 // Delete a mesh and all computers within it
520 + if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid
521 obj.db.Get(command.meshid, function (err, meshes) {
522 if (meshes.length != 1) return;
523 var mesh = meshes[0];
@@ -519,20 +555,25 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
555 case 'editmesh':
556 {
557 // Change the name or description of a mesh
558 + if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid
559 var mesh = obj.parent.meshes[command.meshid], change = '';
560 if (mesh) {
561 // Check if this user has rights to do this
562 if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 1) == 0)) return;
563 if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
564
528 - if (command.meshname && command.meshname != '' && command.meshname != mesh.name) { change = 'Mesh name changed from "' + mesh.name + '" to "' + command.meshname + '"'; mesh.name = command.meshname; }
529 - if (command.desc != null && command.desc != mesh.desc) { if (change != '') change += ' and description changed'; else change += 'Mesh "' + mesh.name + '" description changed'; mesh.desc = command.desc; }
565 + if ((obj.common.validateString(command.meshname, 1, 64) == true) && (command.meshname != mesh.name)) { change = 'Mesh name changed from "' + mesh.name + '" to "' + command.meshname + '"'; mesh.name = command.meshname; }
566 + if ((obj.common.validateString(command.desc, 1, 1024) == true) && (command.desc != mesh.desc)) { if (change != '') change += ' and description changed'; else change += 'Mesh "' + mesh.name + '" description changed'; mesh.desc = command.desc; }
567 if (change != '') { obj.db.Set(mesh); obj.parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, { etype: 'mesh', username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }) }
568 }
569 break;
570 }
571 case 'addmeshuser':
572 {
573 + if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid
574 + if (obj.common.validateString(command.username, 1, 64) == false) break; // Username is between 1 and 64 characters
575 + if (obj.common.validateInt(command.meshadmin) == false) break; // Mesh rights must be an integer
576 +
577 // Check if the user exists
578 var newuserid = 'user/' + domain.id + '/' + command.username.toLowerCase(), newuser = obj.parent.users[newuserid];
579 if (newuser == null) {
@@ -554,17 +595,19 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
595 obj.parent.parent.DispatchEvent([newuser._id], obj, 'resubscribe');
596
597 // Add a user to the mesh
557 - mesh.links[newuserid] = { name: command.username, rights: command.meshadmin };
598 + mesh.links[newuserid] = { name: newuser.name, rights: command.meshadmin };
599 obj.db.Set(mesh);
600
601 // Notify mesh change
561 - var change = 'Added user ' + command.username + ' to mesh ' + mesh.name;
562 - obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, newuserid], obj, { etype: 'mesh', username: user.name, userid: command.userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id })
602 + var change = 'Added user ' + newuser.name + ' to mesh ' + mesh.name;
603 + obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, newuserid], obj, { etype: 'mesh', username: newuser.name, userid: command.userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id })
604 }
605 break;
606 }
607 case 'removemeshuser':
608 {
609 + if (obj.common.validateString(command.userid, 1, 1024) == false) break; // Check userid
610 + if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check meshid
611 if ((command.userid.split('/').length != 3) || (command.userid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
612
613 // Check if the user exists
@@ -597,15 +640,20 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
640
641 // Notify mesh change
642 var change = 'Removed user ' + deluser.name + ' from mesh ' + mesh.name;
600 - obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, command.userid], obj, { etype: 'mesh', username: user.name, userid: command.userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id })
643 + obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, command.userid], obj, { etype: 'mesh', username: user.name, userid: deluser.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id })
644 }
645 break;
646 }
647 case 'addamtdevice':
648 {
649 if (obj.args.wanonly == true) return; // This is a WAN-only server, local Intel AMT computers can't be added
607 -
650 + if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check meshid
651 if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
652 + if (obj.common.validateString(command.devicename, 1, 256) == false) break; // Check device name
653 + if (obj.common.validateString(command.hostname, 1, 256) == false) break; // Check hostname
654 + if (obj.common.validateString(command.amtusername, 1, 16) == false) break; // Check username
655 + if (obj.common.validateString(command.amtpassword, 1, 16) == false) break; // Check password
656 + if (obj.common.validateInt(command.amttls, 0, 1) == false) break; // Check TLS flag
657
658 // Get the mesh
659 var mesh = obj.parent.meshes[command.meshid];
@@ -634,6 +682,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
682 case 'scanamtdevice':
683 {
684 if (obj.args.wanonly == true) return; // This is a WAN-only server, this type of scanning is not allowed.
685 + if (obj.common.validateString(command.range, 1, 256) == false) break; // Check range string
686
687 // Ask the RMCP scanning to scan a range of IP addresses
688 if (obj.parent.parent.amtScanner) {
@@ -645,8 +694,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
694 }
695 case 'removedevices':
696 {
697 + if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
698 +
699 for (var i in command.nodeids) {
700 var nodeid = command.nodeids[i];
701 + if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
702 if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
703
704 // Get the device
@@ -669,10 +721,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
721 obj.parent.parent.DispatchEvent(['*', node.meshid], obj, { etype: 'node', username: user.name, action: 'removenode', nodeid: node._id, msg: change, domain: domain.id })
722
723 // Disconnect all connections if needed
672 - var state = obj.parent.parent.GetConnectivityState(command.nodeid);
724 + var state = obj.parent.parent.GetConnectivityState(nodeid);
725 if ((state != null) && (state.connectivity != null)) {
674 - if ((state.connectivity & 1) != 0) { obj.parent.wsagents[command.nodeid].close(); } // Disconnect mesh agent
675 - if ((state.connectivity & 2) != 0) { obj.parent.parent.mpsserver.close(obj.parent.parent.mpsserver.ciraConnections[command.nodeid]); } // Disconnect CIRA connection
726 + if ((state.connectivity & 1) != 0) { obj.parent.wsagents[nodeid].close(); } // Disconnect mesh agent
727 + if ((state.connectivity & 2) != 0) { obj.parent.parent.mpsserver.close(obj.parent.parent.mpsserver.ciraConnections[nodeid]); } // Disconnect CIRA connection
728 }
729 }
730 });
@@ -682,12 +734,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
734 }
735 case 'wakedevices':
736 {
685 - // TODO: INPUT VALIDATION!!!
737 + if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
738 // TODO: We can optimize this a lot.
739 // - We should get a full list of all MAC's to wake first.
740 // - We should try to only have one agent per subnet (using Gateway MAC) send a wake-on-lan.
741 for (var i in command.nodeids) {
742 var nodeid = command.nodeids[i], wakeActions = 0;
743 + if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
744 if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
745 // Get the device
746 obj.db.Get(nodeid, function (err, nodes) {
@@ -739,9 +792,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
792 }
793 case 'poweraction':
794 {
742 - // TODO: INPUT VALIDATION!!!
795 + if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
796 for (var i in command.nodeids) {
797 var nodeid = command.nodeids[i], powerActions = 0;
798 + if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
799 if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
800 // Get the device
801 obj.db.Get(nodeid, function (err, nodes) {
@@ -774,7 +828,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
828 case 'getnetworkinfo':
829 {
830 // Argument validation
777 - if ((command.nodeid == null) || (typeof command.nodeid != 'string') || (command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
831 + if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
832 + if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
833
834 // Get the device
835 obj.db.Get(command.nodeid, function (err, nodes) {
@@ -800,7 +855,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
855 case 'changedevice':
856 {
857 // Argument validation
803 - if ((command.nodeid == null) || (typeof command.nodeid != 'string') || (command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
858 + if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
859 + if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
860 if ((command.userloc) && (command.userloc.length != 2) && (command.userloc.length != 0)) return;
861
862 // Change the device
@@ -857,7 +913,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
913 case 'uploadagentcore':
914 {
915 if (user.siteadmin != 0xFFFFFFFF) break;
916 + if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
917 if (command.path) {
918 + if (obj.common.validateString(command.path, 1, 4096) == false) break; // Check path
919 if (command.path == '*') {
920 // Update the server default core and send a core hash request
921 // Load default mesh agent core if present, then perform a core update
@@ -883,6 +941,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
941 case 'agentdisconnect':
942 {
943 // Force mesh agent disconnection
944 + if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
945 + if (obj.common.validateInt(command.disconnectMode) == false) break; // Check disconnect mode
946 obj.parent.forceMeshAgentDisconnect(user, domain, command.nodeid, command.disconnectMode);
947 break;
948 }
@@ -896,6 +956,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
956 case 'getcookie':
957 {
958 // Check if this user has rights on this nodeid
959 + if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
960 obj.db.Get(command.nodeid, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???)
961 if (nodes.length == 1) {
962 var meshlinks = user.links[nodes[0].meshid];
@@ -916,6 +977,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
977 {
978 if ((obj.parent.parent.mailserver == null) || (obj.args.lanonly == true)) return; // This operation requires the email server
979 if ((obj.parent.parent.certificates.CommonName == null) || (obj.parent.parent.certificates.CommonName == 'un-configured')) return; // Server name must be configured
980 + if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check meshid
981 if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
982
983 // Get the mesh
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.1.5-w",
3 + "version": "0.1.5-y",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
views/default.handlebars
+29 -10
@@ -4197,7 +4197,7 @@
4197 function account_createMesh() {
4198 if (xxdialogMode) return;
4199 var x = "Create a new mesh computer group using the options below.<br /><br />";
4200 - x += addHtmlValue('Mesh Name', '<input id=dp2meshname style=width:230px maxlength=32 onchange=account_validateMeshCreate() onkeyup=account_validateMeshCreate() />');
4200 + x += addHtmlValue('Mesh Name', '<input id=dp2meshname style=width:230px maxlength=64 onchange=account_validateMeshCreate() onkeyup=account_validateMeshCreate() />');
4201 x += addHtmlValue('Mesh Type', '<div style=width:230px;margin:0;padding:0><select id=dp2meshtype style=width:100% onchange=account_validateMeshCreate() ><option value=2>Mesh Agent Policy</option><option value=1>Intel&reg; AMT Agent-less Policy</option></select></div>');
4202 x += addHtmlValue('Description', '<div style=width:230px;margin:0;padding:0><textarea id=dp2meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>');
4203 setDialogMode(2, "Create Mesh", 3, account_createMeshEx, x);
@@ -4747,15 +4747,24 @@
4747 function updateUsers() {
4748 QV('MainMenuMyUsers', (users != null) && ((features & 4) == 0));
4749 if ((users == null) || ((features & 4) != 0)) { QH('p3users', ''); return; }
4750 +
4751 + // Sort the list of user id's
4752 + var sortedUserIds = [];
4753 + for (var i in users) { sortedUserIds.push(i); }
4754 + sortedUserIds.sort();
4755 +
4756 + // Display the users using the sorted list
4757 var x = '<table style=width:100% cellpadding=0 cellspacing=0>';
4751 - for (var i in users) {
4752 - var user = users[i], icon = 'm2', msg = '';
4758 + for (var i in sortedUserIds) {
4759 + var user = users[sortedUserIds[i]], icon = 'm2', msg = '', self = (user.name != userinfo.name);
4760 if (wssessions != null && wssessions[user._id]) {
4761 + if (self) { msg += "<a onclick=showUserAlertDialog(event,\"" + user._id + "\")>"; }
4762 var sessions = wssessions[user._id];
4755 - if (sessions == 1) { msg = '1 active session'; } else { msg = sessions + ' active sessions'; }
4763 + if (sessions == 1) { msg += '1 active session'; } else { msg += sessions + ' active sessions'; }
4764 + if (self) { msg += "</a>"; }
4765 }
4766 if (msg != '') msg += ', ';
4758 - if (user.name != userinfo.name) { msg += "<a onclick=showUserAdminDialog(event,\"" + user._id + "\")>"; }
4767 + if (self) { msg += "<a onclick=showUserAdminDialog(event,\"" + user._id + "\")>"; }
4768 if ((user.siteadmin == null) || (user.siteadmin == 0)) {
4769 msg += "User";
4770 } else if (user.siteadmin == 8) {
@@ -4766,7 +4775,7 @@
4775 msg += "Partial Admin";
4776 }
4777 if ((user.quota != null) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
4769 - if (user.name != userinfo.name) { msg += "</a>"; }
4778 + if (self) { msg += "</a>"; }
4779 var username = EscapeHtml(user.name);
4780 if (user.email != null) { username += ', <a onclick=doemail(event,\"' + user.email + '\")>' + user.email + '</a>' + (((serverinfo.emailcheck == true) && (user.emailVerified != true))?' (unverified)':''); }
4781 x += '<tr><td style=cursor:pointer onclick=showUserInfoDialog(\"' + user._id + '\")>';
@@ -4779,6 +4788,16 @@
4788 QH('p3users', x);
4789 }
4790
4791 + function showUserAlertDialog(e, userid) {
4792 + if (xxdialogMode) return;
4793 + haltEvent(e);
4794 + setDialogMode(2, "Notify " + EscapeHtml(users[userid].name), 3, showUserAlertDialogEx, 'Send a text notification to this user.<textarea id=d2notifyText maxlength=2048 style="width:100%;height:184px;resize:none"></textarea>', userid);
4795 + Q('d2notifyText').focus();
4796 + return false;
4797 + }
4798 +
4799 + function showUserAlertDialogEx(button, userid) { meshserver.send({ action: 'notifyuser', userid: userid, msg: Q('d2notifyText').value }); }
4800 +
4801 function doemail(e, addr) {
4802 if (xxdialogMode) return;
4803 haltEvent(e);
@@ -4807,10 +4826,10 @@
4826 function showCreateNewAccountDialog() {
4827 if (xxdialogMode) return;
4828 var x = '';
4810 - x += addHtmlValue('Name', '<input id=p4name style=width:230px maxlength=32 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
4811 - x += addHtmlValue('Email', '<input id=p4email style=width:230px maxlength=64 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
4812 - x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=64 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
4813 - x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=64 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
4829 + x += addHtmlValue('Name', '<input id=p4name style=width:230px maxlength=64 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
4830 + x += addHtmlValue('Email', '<input id=p4email style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
4831 + x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
4832 + x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
4833 setDialogMode(2, "Create Account", 3, showCreateNewAccountDialogEx, x);
4834 showCreateNewAccountDialogValidate();
4835 Q('p4name').focus();
views/login.handlebars
+9 -9
@@ -42,11 +42,11 @@
42 <table>
43 <tr>
44 <td align=right width=100>Username:</td>
45 - <td><input id=username type=text name=username onchange=validateLogin(1) onkeyup=validateLogin(1,event) /></td>
45 + <td><input id=username type=text maxlength=64 name=username onchange=validateLogin(1) onkeyup=validateLogin(1,event) /></td>
46 </tr>
47 <tr>
48 <td align=right>Password:</td>
49 - <td><input id=password type=password name=password autocomplete=off onchange=validateLogin(2) onkeyup=validateLogin(2,event) /></td>
49 + <td><input id=password type=password maxlength=256 name=password autocomplete=off onchange=validateLogin(2) onkeyup=validateLogin(2,event) /></td>
50 </tr>
51 <tr>
52 <td><div id=showPassHintLink style=display:none><a onclick=showPassHint() style="cursor:pointer">Show Hint</a></div></td>
@@ -73,27 +73,27 @@
73 <table>
74 <tr>
75 <td align=right width=100>Username:</td>
76 - <td><input id=ausername type=text name=username onchange=validateCreate(1) onkeydown=haltReturn(event) onkeyup=validateCreate(1,event) /></td>
76 + <td><input id=ausername type=text name=username onchange=validateCreate(1) maxlength=64 onkeydown=haltReturn(event) onkeyup=validateCreate(1,event) /></td>
77 </tr>
78 <tr>
79 <td align=right width=100>Email:</td>
80 - <td><input id=aemail type=text name=email onchange=validateCreate(2) onkeydown=haltReturn(event) onkeyup=validateCreate(2,event) /></td>
80 + <td><input id=aemail type=text name=email onchange=validateCreate(2) maxlength=256 onkeydown=haltReturn(event) onkeyup=validateCreate(2,event) /></td>
81 </tr>
82 <tr>
83 <td align=right>Password:</td>
84 - <td><input id=apassword1 type=password name=password1 autocomplete=off onkeydown=haltReturn(event) onchange=validateCreate(3) onkeyup=validateCreate(3,event) /></td>
84 + <td><input id=apassword1 type=password name=password1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(3) onkeyup=validateCreate(3,event) /></td>
85 </tr>
86 <tr>
87 <td align=right>Password:</td>
88 - <td><input id=apassword2 type=password name=password2 autocomplete=off onkeydown=haltReturn(event) onchange=validateCreate(4) onkeyup=validateCreate(4,event) /></td>
88 + <td><input id=apassword2 type=password name=password2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(4) onkeyup=validateCreate(4,event) /></td>
89 </tr>
90 <tr>
91 <td align=right>Password Hint:</td>
92 - <td><input id=apasswordhint type=text name=apasswordhint autocomplete=off maxlength=250 onkeydown=haltReturn(event) onchange=validateCreate(5) onkeyup=validateCreate(5,event) /></td>
92 + <td><input id=apasswordhint type=text name=apasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(5) onkeyup=validateCreate(5,event) /></td>
93 </tr>
94 <tr id=newAccountPass title="Enter the account creation token">
95 <td align=right>Creation Token:</td>
96 - <td><input id=anewaccountpass type=password name=anewaccountpass autocomplete=off maxlength=250 onkeydown=haltReturn(event) onchange=validateCreate(6) onkeyup=validateCreate(6,event) /></td>
96 + <td><input id=anewaccountpass type=password name=anewaccountpass autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(6) onkeyup=validateCreate(6,event) /></td>
97 </tr>
98 <tr>
99 <td colspan=2>
@@ -116,7 +116,7 @@
116 <table>
117 <tr>
118 <td align=right width=100>Email:</td>
119 - <td><input id=remail type=text name=email onchange=validateReset() onkeyup=validateReset(event) /></td>
119 + <td><input id=remail type=text name=email maxlength=256 onchange=validateReset() onkeyup=validateReset(event) /></td>
120 </tr>
121 <tr>
122 <td colspan=2>