@@ -316,6 +316,13 @@ mesh::Packet* SensorMesh::createSelfAdvert() {
316316 return createAdvert (self_id, app_data, app_data_len);
317317}
318318
319+ ContactInfo* SensorMesh::getContact (const uint8_t * pubkey, int key_len) {
320+ for (int i = 0 ; i < num_contacts; i++) {
321+ if (memcmp (pubkey, contacts[i].id .pub_key , key_len) == 0 ) return &contacts[i]; // already known
322+ }
323+ return NULL ; // not found
324+ }
325+
319326ContactInfo* SensorMesh::putContact (const mesh::Identity& id, uint8_t init_perms) {
320327 uint32_t min_time = 0xFFFFFFFF ;
321328 ContactInfo* oldest = &contacts[MAX_CONTACTS - 1 ];
@@ -340,17 +347,29 @@ ContactInfo* SensorMesh::putContact(const mesh::Identity& id, uint8_t init_perms
340347 return c;
341348}
342349
343- void SensorMesh::applyContactPermissions (const uint8_t * pubkey, uint8_t perms) {
344- mesh::Identity id (pubkey);
345- auto c = putContact (id, 0 );
346-
350+ bool SensorMesh::applyContactPermissions (const uint8_t * pubkey, int key_len, uint8_t perms) {
351+ ContactInfo* c;
347352 if ((perms & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) { // guest role is not persisted in contacts
348- memset (c, 0 , sizeof (*c));
353+ c = getContact (pubkey, key_len);
354+ if (c == NULL ) return false ; // partial pubkey not found
355+
356+ num_contacts--; // delete from contacts[]
357+ int i = c - contacts;
358+ while (i < num_contacts) {
359+ contacts[i] = contacts[i + 1 ];
360+ i++;
361+ }
349362 } else {
363+ if (key_len < PUB_KEY_SIZE) return false ; // need complete pubkey when adding/modifying
364+
365+ mesh::Identity id (pubkey);
366+ c = putContact (id, 0 );
367+
350368 c->permissions = perms; // update their permissions
351369 self_id.calcSharedSecret (c->shared_secret , pubkey);
352370 }
353371 dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY); // trigger saveContacts()
372+ return true ;
354373}
355374
356375void SensorMesh::sendAlert (ContactInfo* c, Trigger* t) {
@@ -436,32 +455,43 @@ int SensorMesh::getAGCResetInterval() const {
436455}
437456
438457uint8_t SensorMesh::handleLoginReq (const mesh::Identity& sender, const uint8_t * secret, uint32_t sender_timestamp, const uint8_t * data) {
439- if (strcmp ((char *) data, _prefs.password ) != 0 ) { // check for valid password
440- #if MESH_DEBUG
441- MESH_DEBUG_PRINTLN (" Invalid password: %s" , &data[4 ]);
442- #endif
443- return 0 ;
444- }
458+ ContactInfo* client;
459+ if (data[0 ] == 0 ) { // blank password, just check if sender is in ACL
460+ client = getContact (sender.pub_key , PUB_KEY_SIZE);
461+ if (client == NULL ) {
462+ #if MESH_DEBUG
463+ MESH_DEBUG_PRINTLN (" Login, sender not in ACL" );
464+ #endif
465+ return 0 ;
466+ }
467+ } else {
468+ if (strcmp ((char *) data, _prefs.password ) != 0 ) { // check for valid admin password
469+ #if MESH_DEBUG
470+ MESH_DEBUG_PRINTLN (" Invalid password: %s" , &data[4 ]);
471+ #endif
472+ return 0 ;
473+ }
445474
446- auto client = putContact (sender, PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO); // add to contacts (if not already known)
447- if (sender_timestamp <= client->last_timestamp ) {
448- MESH_DEBUG_PRINTLN (" Possible login replay attack!" );
449- return 0 ; // FATAL: client table is full -OR- replay attack
450- }
475+ client = putContact (sender, PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO); // add to contacts (if not already known)
476+ if (sender_timestamp <= client->last_timestamp ) {
477+ MESH_DEBUG_PRINTLN (" Possible login replay attack!" );
478+ return 0 ; // FATAL: client table is full -OR- replay attack
479+ }
451480
452- MESH_DEBUG_PRINTLN (" Login success!" );
453- client->last_timestamp = sender_timestamp;
454- client->last_activity = getRTCClock ()->getCurrentTime ();
455- client->permissions |= PERM_ACL_ADMIN;
456- memcpy (client->shared_secret , secret, PUB_KEY_SIZE);
481+ MESH_DEBUG_PRINTLN (" Login success!" );
482+ client->last_timestamp = sender_timestamp;
483+ client->last_activity = getRTCClock ()->getCurrentTime ();
484+ client->permissions |= PERM_ACL_ADMIN;
485+ memcpy (client->shared_secret , secret, PUB_KEY_SIZE);
457486
458- dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY);
487+ dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY);
488+ }
459489
460490 uint32_t now = getRTCClock ()->getCurrentTimeUnique ();
461491 memcpy (reply_data, &now, 4 ); // response packets always prefixed with timestamp
462492 reply_data[4 ] = RESP_SERVER_LOGIN_OK;
463493 reply_data[5 ] = 0 ; // NEW: recommended keep-alive interval (secs / 16)
464- reply_data[6 ] = 1 ; // 1 = is admin
494+ reply_data[6 ] = client-> isAdmin () ? 1 : 0 ;
465495 reply_data[7 ] = client->permissions ;
466496 getRNG ()->random (&reply_data[8 ], 4 ); // random blob to help packet-hash uniqueness
467497
@@ -486,16 +516,20 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r
486516 if (memcmp (command, " setperm " , 8 ) == 0 ) { // format: setperm {pubkey-hex} {permissions-int8}
487517 char * hex = &command[8 ];
488518 char * sp = strchr (hex, ' ' ); // look for separator char
489- if (sp == NULL || sp - hex != PUB_KEY_SIZE* 2 ) {
490- strcpy (reply, " Err - bad pubkey len " );
519+ if (sp == NULL ) {
520+ strcpy (reply, " Err - bad params " );
491521 } else {
492522 *sp++ = 0 ; // replace space with null terminator
493523
494524 uint8_t pubkey[PUB_KEY_SIZE];
495- if (mesh::Utils::fromHex (pubkey, PUB_KEY_SIZE, hex)) {
525+ int hex_len = min (sp - hex, PUB_KEY_SIZE*2 );
526+ if (mesh::Utils::fromHex (pubkey, hex_len / 2 , hex)) {
496527 uint8_t perms = atoi (sp);
497- applyContactPermissions (pubkey, perms);
498- strcpy (reply, " OK" );
528+ if (applyContactPermissions (pubkey, hex_len / 2 , perms)) {
529+ strcpy (reply, " OK" );
530+ } else {
531+ strcpy (reply, " Err - invalid params" );
532+ }
499533 } else {
500534 strcpy (reply, " Err - bad pubkey" );
501535 }
0 commit comments