Files
RTLPlayground/sessions.c
Brunoz78andClaude Sonnet 5.5 e1c79ec4cd httpd: reuse the slot of a valid cookie, end sessions on passwd
Review feedback on #560 (DrDoof):

- A login that came with a cookie that is still valid took a second
  slot. The first one looked fresh, so the next login could push out
  someone who is using the switch. The slot of the cookie now gets the
  new id.
- Changing the password leaves no old login valid: session_init() from
  parse_passwd(). With one session the next login pushed a leftover one
  out, with several it would stay valid until the next restart.
- The 32-bit comparison of the last use times moves into a leaf function,
  so the compiler does not give it a 4-byte scratch in internal RAM.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 10:51:23 +01:00

129 lines
3.5 KiB
C

/*
* Login sessions of the web interface.
*
* Up to SESSIONS logins at once, each with its own cookie and its own expiry.
* With a single one, logging in from a second browser (or a phone) ended the
* first browser's session at its next request.
*
* This sits in bank 3: the web server's own bank is full, so httpd.c only
* calls in here. It shares three things with httpd.c: the cookie value it
* parsed (session), the time read for the request (now) and the result
* (authenticated); the reply is written to outbuf at slen.
*/
#pragma codeseg BANK3
#pragma constseg BANK3
#include <stdint.h>
#include "rtl837x_common.h"
#include "sessions.h"
extern volatile __xdata uint8_t sfr_data[4];
extern __xdata uint16_t session_timeout;
extern __xdata uint32_t now;
extern __xdata uint8_t authenticated;
extern __xdata uint8_t * __xdata session;
extern __xdata uint8_t outbuf[];
extern __xdata uint16_t slen;
__xdata char session_id[SESSIONS][SESSION_ID_LENGTH + 1];
__xdata uint32_t last_session_use[SESSIONS];
static __xdata uint8_t ss_slot, ss_i, ss_oldest, ss_c;
/* The slot whose cookie the last request came with; only meaningful while
* authenticated is set, which only session_check() does */
static __xdata uint8_t ss_hit;
static __code const char ss_hex[] = "0123456789abcdef";
/* What may follow a cookie value: ';' separates cookies in a Cookie header */
static __code const char ss_end[] = " \t:?=\n\r;";
/* Leaf function, so the compiler keeps its 32-bit scratch in the overlay
* rather than in internal RAM. */
static uint8_t ss_stale(void)
{
if (now - last_session_use[ss_slot] > session_timeout)
return 1;
return 0;
}
/* Likewise a leaf: the 32-bit comparison needs no stack of its own this way */
static uint8_t ss_older(void)
{
if (last_session_use[ss_slot] < last_session_use[ss_oldest])
return 1;
return 0;
}
/* Whether the cookie value is the id of slot ss_slot */
static uint8_t ss_match(void)
{
for (ss_i = 0; ss_i < SESSION_ID_LENGTH; ss_i++) {
if (session[ss_i] != session_id[ss_slot][ss_i])
return 0;
}
ss_c = session[SESSION_ID_LENGTH];
if (!ss_c)
return 1;
for (ss_i = 0; ss_end[ss_i]; ss_i++) {
if (ss_c == ss_end[ss_i])
return 1;
}
return 0;
}
void session_init(void) __banked
{
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++)
session_id[ss_slot][0] = NUL;
}
void session_check(void) __banked
{
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++) {
if (!session_id[ss_slot][0])
continue;
if (ss_stale()) {
session_id[ss_slot][0] = NUL;
} else if (ss_match()) {
authenticated = 1;
ss_hit = ss_slot;
last_session_use[ss_slot] = now;
return;
}
}
}
void session_new(void) __banked
{
if (authenticated && session_id[ss_hit][0]) {
/* The login came with a cookie that is still valid: its slot gets the
* new id. A second slot for the same browser would leave the old one
* looking fresh and push out someone else at the next login. */
ss_slot = ss_hit;
} else {
/* A free or expired slot, else the least recently used */
ss_oldest = 0;
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++) {
if (!session_id[ss_slot][0] || ss_stale())
break;
if (ss_older())
ss_oldest = ss_slot;
}
if (ss_slot == SESSIONS)
ss_slot = ss_oldest;
}
last_session_use[ss_slot] = now;
for (ss_i = 0; ss_i < SESSION_ID_LENGTH; ss_i++) {
if (!(ss_i & 7))
get_random_32();
ss_c = sfr_data[(ss_i >> 1) & 3];
if (ss_i & 1)
ss_c >>= 4;
session_id[ss_slot][ss_i] = ss_hex[ss_c & 0xf];
outbuf[slen++] = ss_hex[ss_c & 0xf];
}
session_id[ss_slot][SESSION_ID_LENGTH] = NUL;
}