@@ -243,7 +243,7 @@ static uint8_t putFloat(uint8_t * dest, float value, uint8_t size, uint32_t mult
243243uint8_t SensorMesh::handleRequest (uint8_t perms, uint32_t sender_timestamp, uint8_t req_type, uint8_t * payload, size_t payload_len) {
244244 memcpy (reply_data, &sender_timestamp, 4 ); // reflect sender_timestamp back in response packet (kind of like a 'tag')
245245
246- if (req_type == REQ_TYPE_GET_TELEMETRY_DATA && (perms & PERM_GET_TELEMETRY) != 0 ) {
246+ if (req_type == REQ_TYPE_GET_TELEMETRY_DATA) { // allow all
247247 telemetry.reset ();
248248 telemetry.addVoltage (TELEM_CHANNEL_SELF, (float )board.getBattMilliVolts () / 1000 .0f );
249249 // query other sensors -- target specific
@@ -254,7 +254,7 @@ uint8_t SensorMesh::handleRequest(uint8_t perms, uint32_t sender_timestamp, uint
254254 memcpy (&reply_data[4 ], telemetry.getBuffer (), tlen);
255255 return 4 + tlen; // reply_len
256256 }
257- if (req_type == REQ_TYPE_GET_AVG_MIN_MAX && (perms & PERM_GET_OTHER_STATS) != 0 ) {
257+ if (req_type == REQ_TYPE_GET_AVG_MIN_MAX && (perms & PERM_ACL_ROLE_MASK) >= PERM_ACL_READ_ONLY ) {
258258 uint32_t start_secs_ago, end_secs_ago;
259259 memcpy (&start_secs_ago, &payload[0 ], 4 );
260260 memcpy (&end_secs_ago, &payload[4 ], 4 );
@@ -288,13 +288,14 @@ uint8_t SensorMesh::handleRequest(uint8_t perms, uint32_t sender_timestamp, uint
288288 }
289289 return ofs;
290290 }
291- if (req_type == REQ_TYPE_GET_ACCESS_LIST && (perms & PERM_ACL_ROLE_MASK) == PERM_ACL_LEVEL3 ) {
291+ if (req_type == REQ_TYPE_GET_ACCESS_LIST && (perms & PERM_ACL_ROLE_MASK) == PERM_ACL_ADMIN ) {
292292 uint8_t res1 = payload[0 ]; // reserved for future (extra query params)
293293 uint8_t res2 = payload[1 ];
294294 if (res1 == 0 && res2 == 0 ) {
295295 uint8_t ofs = 4 ;
296296 for (int i = 0 ; i < num_contacts && ofs + 7 <= sizeof (reply_data) - 4 ; i++) {
297297 auto c = &contacts[i];
298+ if (c->permissions == 0 ) continue ; // skip deleted entries
298299 memcpy (&reply_data[ofs], c->id .pub_key , 6 ); ofs += 6 ; // just 6-byte pub_key prefix
299300 reply_data[ofs++] = c->permissions ;
300301 }
@@ -315,7 +316,14 @@ mesh::Packet* SensorMesh::createSelfAdvert() {
315316 return createAdvert (self_id, app_data, app_data_len);
316317}
317318
318- ContactInfo* SensorMesh::putContact (const mesh::Identity& id) {
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+
326+ ContactInfo* SensorMesh::putContact (const mesh::Identity& id, uint8_t init_perms) {
319327 uint32_t min_time = 0xFFFFFFFF ;
320328 ContactInfo* oldest = &contacts[MAX_CONTACTS - 1 ];
321329 for (int i = 0 ; i < num_contacts; i++) {
@@ -333,22 +341,35 @@ ContactInfo* SensorMesh::putContact(const mesh::Identity& id) {
333341 c = oldest; // evict least active contact
334342 }
335343 memset (c, 0 , sizeof (*c));
344+ c->permissions = init_perms;
336345 c->id = id;
337346 c->out_path_len = -1 ; // initially out_path is unknown
338347 return c;
339348}
340349
341- void SensorMesh::applyContactPermissions (const uint8_t * pubkey, uint8_t perms) {
342- mesh::Identity id (pubkey);
343- auto c = putContact (id);
344-
350+ bool SensorMesh::applyContactPermissions (const uint8_t * pubkey, int key_len, uint8_t perms) {
351+ ContactInfo* c;
345352 if ((perms & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) { // guest role is not persisted in contacts
346- 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+ }
347362 } 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+
348368 c->permissions = perms; // update their permissions
349369 self_id.calcSharedSecret (c->shared_secret , pubkey);
350370 }
351371 dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY); // trigger saveContacts()
372+ return true ;
352373}
353374
354375void SensorMesh::sendAlert (ContactInfo* c, Trigger* t) {
@@ -434,32 +455,43 @@ int SensorMesh::getAGCResetInterval() const {
434455}
435456
436457uint8_t SensorMesh::handleLoginReq (const mesh::Identity& sender, const uint8_t * secret, uint32_t sender_timestamp, const uint8_t * data) {
437- if (strcmp ((char *) data, _prefs.password ) != 0 ) { // check for valid password
438- #if MESH_DEBUG
439- MESH_DEBUG_PRINTLN (" Invalid password: %s" , &data[4 ]);
440- #endif
441- return 0 ;
442- }
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+ }
443474
444- auto client = putContact (sender); // add to contacts (if not already known)
445- if (sender_timestamp <= client->last_timestamp ) {
446- MESH_DEBUG_PRINTLN (" Possible login replay attack!" );
447- return 0 ; // FATAL: client table is full -OR- replay attack
448- }
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+ }
449480
450- MESH_DEBUG_PRINTLN (" Login success!" );
451- client->last_timestamp = sender_timestamp;
452- client->last_activity = getRTCClock ()->getCurrentTime ();
453- client->permissions = PERM_ACL_LEVEL3 | PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO; // initially opt-in to receive alerts (can opt out)
454- 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);
455486
456- dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY);
487+ dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY);
488+ }
457489
458490 uint32_t now = getRTCClock ()->getCurrentTimeUnique ();
459491 memcpy (reply_data, &now, 4 ); // response packets always prefixed with timestamp
460492 reply_data[4 ] = RESP_SERVER_LOGIN_OK;
461493 reply_data[5 ] = 0 ; // NEW: recommended keep-alive interval (secs / 16)
462- reply_data[6 ] = 1 ; // 1 = is admin
494+ reply_data[6 ] = client-> isAdmin () ? 1 : 0 ;
463495 reply_data[7 ] = client->permissions ;
464496 getRNG ()->random (&reply_data[8 ], 4 ); // random blob to help packet-hash uniqueness
465497
@@ -484,16 +516,20 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r
484516 if (memcmp (command, " setperm " , 8 ) == 0 ) { // format: setperm {pubkey-hex} {permissions-int8}
485517 char * hex = &command[8 ];
486518 char * sp = strchr (hex, ' ' ); // look for separator char
487- if (sp == NULL || sp - hex != PUB_KEY_SIZE* 2 ) {
488- strcpy (reply, " Err - bad pubkey len " );
519+ if (sp == NULL ) {
520+ strcpy (reply, " Err - bad params " );
489521 } else {
490522 *sp++ = 0 ; // replace space with null terminator
491523
492524 uint8_t pubkey[PUB_KEY_SIZE];
493- 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)) {
494527 uint8_t perms = atoi (sp);
495- applyContactPermissions (pubkey, perms);
496- strcpy (reply, " OK" );
528+ if (applyContactPermissions (pubkey, hex_len / 2 , perms)) {
529+ strcpy (reply, " OK" );
530+ } else {
531+ strcpy (reply, " Err - invalid params" );
532+ }
497533 } else {
498534 strcpy (reply, " Err - bad pubkey" );
499535 }
@@ -502,6 +538,7 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r
502538 Serial.println (" ACL:" );
503539 for (int i = 0 ; i < num_contacts; i++) {
504540 auto c = &contacts[i];
541+ if (c->permissions == 0 ) continue ; // skip deleted entries
505542
506543 Serial.printf (" %02X " , c->permissions );
507544 mesh::Utils::printHex (Serial, c->id .pub_key , PUB_KEY_SIZE);
@@ -569,7 +606,7 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i
569606 memcpy (×tamp, data, 4 );
570607
571608 if (timestamp > from.last_timestamp ) { // prevent replay attacks
572- uint8_t reply_len = handleRequest (from.isAdmin () ? 0xFFFF : from.permissions , timestamp, data[4 ], &data[5 ], len - 5 );
609+ uint8_t reply_len = handleRequest (from.isAdmin () ? 0xFF : from.permissions , timestamp, data[4 ], &data[5 ], len - 5 );
573610 if (reply_len == 0 ) return ; // invalid command
574611
575612 from.last_timestamp = timestamp;
0 commit comments