password compare bug fix

This commit is contained in:
liquidraver
2026-06-16 12:36:54 +02:00
parent a0222fe12b
commit 4ec6f27202
5 changed files with 108 additions and 15 deletions
+3 -1
View File
@@ -58,11 +58,13 @@ All commands are sent over USB serial (CDC-ACM). Commands sent remotely over the
| Command | Description |
|---------|-------------|
| `password <new_password>` | Set the admin password |
| `password <new_password>` | Set the admin password (**max 15 characters**) |
| `setperm <perms_hex> <pubkey_hex>` | Set ACL permissions for a node (app format: 2-char hex perms first) |
| `setperm <pubkey_hex> <perms_dec>` | Set ACL permissions for a node (Arduino format: pubkey first, decimal perms) |
| `get acl` | *(USB only)* List all ACL entries with permissions and public keys |
> **Password length:** admin and guest passwords are capped at **15 characters** (16-byte storage incl. NUL; same limit as Arduino MeshCore). The login-send path silently truncates anything longer, so a password >15 chars will never authenticate. Applies to `set guest.password` as well.
---
## Region Filtering
+81 -5
View File
@@ -22,6 +22,8 @@
#include <zephyr/logging/log.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/regulator.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/fs/fs.h>
#include <string.h>
#if defined(CONFIG_SOC_NRF52840)
@@ -547,6 +549,55 @@ static const struct gpio_dt_spec gps_enable_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(gps
#define HAS_GPS_POWER_CONTROL 0
#endif
/* GPS powered by a PMU regulator rail instead of a discrete enable GPIO (e.g.
* LilyGo T-Beam: GPS is on the AXP2101 ALDO3 rail). Selected via the chosen
* `zephcore,gps-power` node pointing at the regulator. Acts as a master power
* switch driven by enable/disable; the duty-cycle standby/wake uses software
* sleep/wake (UART) and leaves the rail up, so the regulator is only toggled on
* the (unguarded) enable/disable/boot paths — never per duty cycle. */
#if DT_NODE_EXISTS(DT_CHOSEN(zephcore_gps_power))
static const struct device *const gps_power_reg =
DEVICE_DT_GET(DT_CHOSEN(zephcore_gps_power));
/* Tracks our intended rail state so enable/disable stay balanced (idempotent).
* Starts true: the rail is `regulator-boot-on`, so it is already up at boot. */
static bool gps_reg_enabled = true;
#define HAS_GPS_POWER_REGULATOR 1
#else
#define HAS_GPS_POWER_REGULATOR 0
#endif
/* AXP2101 backup (button-battery) charger — feeds the GPS receiver's V_BCKP
* domain so ephemeris/RTC survive main-rail (ALDO3) power cuts, giving a
* warm/hot re-fix instead of a cold start each duty cycle. The Zephyr regulator
* driver doesn't expose VBACKUP, so enable it with raw I2C at boot (mirrors
* Arduino enablePowerOutput(XPOWERS_VBACKUP) + setPowerChannelVoltage 3.3V).
* Selected via chosen `zephcore,gps-backup-pmu` pointing at the AXP2101 node. */
#if DT_NODE_EXISTS(DT_CHOSEN(zephcore_gps_backup_pmu))
#define AXP2101_REG_CHG_GAUGE_WDT_CTRL 0x18U /* bit 2 = button-battery charge enable */
#define AXP2101_BTN_CHARGE_ENABLE BIT(2)
#define AXP2101_REG_BTN_BAT_CHG_VOL_SET 0x6AU /* low 3 bits: (mV - 2600) / 100 */
#define AXP2101_BTN_VOL_3V3 0x07U /* (3300 - 2600) / 100 */
static int gps_backup_charger_init(void)
{
static const struct i2c_dt_spec axp = I2C_DT_SPEC_GET(DT_CHOSEN(zephcore_gps_backup_pmu));
if (!device_is_ready(axp.bus)) {
LOG_WRN("GPS backup: AXP2101 I2C bus not ready");
return 0;
}
/* Set the backup-charge target to 3.3V (low 3 bits), then enable the
* charger. Read-modify-write so the fuel-gauge enable (bit 3 of 0x18) and
* the other 0x6A bits are preserved. */
i2c_reg_update_byte_dt(&axp, AXP2101_REG_BTN_BAT_CHG_VOL_SET, 0x07U, AXP2101_BTN_VOL_3V3);
i2c_reg_update_byte_dt(&axp, AXP2101_REG_CHG_GAUGE_WDT_CTRL,
AXP2101_BTN_CHARGE_ENABLE, AXP2101_BTN_CHARGE_ENABLE);
LOG_INF("GPS backup: AXP2101 VBACKUP charger enabled (3.3V)");
return 0;
}
/* After the MFD/I2C is up (POST_KERNEL ~86); APPLICATION is safely later. */
SYS_INIT(gps_backup_charger_init, APPLICATION, 50);
#endif
/* T1000-E specific GPS control pins */
#if DT_NODE_EXISTS(DT_ALIAS(gps_vrtc_enable))
static const struct gpio_dt_spec gps_vrtc_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(gps_vrtc_enable), gpios);
@@ -602,6 +653,20 @@ static bool gps_gpio_configured = false;
* Only relevant on T1000-E (HAS_GPS_VRTC); ignored on other boards. */
static void gps_power_control(bool on, bool keep_vrtc = false)
{
#if HAS_GPS_POWER_REGULATOR
/* Master power rail (PMU regulator). Idempotent enable/disable so the
* refcount stays balanced regardless of how often this is called. */
if (on != gps_reg_enabled && device_is_ready(gps_power_reg)) {
int ret = on ? regulator_enable(gps_power_reg)
: regulator_disable(gps_power_reg);
if (ret == 0) {
gps_reg_enabled = on;
LOG_INF("GPS power %s (regulator)", on ? "ON" : "OFF");
} else {
LOG_WRN("GPS regulator %s failed: %d", on ? "enable" : "disable", ret);
}
}
#endif
#if HAS_GPS_POWER_CONTROL
/* Direct GPIO power control — works on all boards.
* We toggle the GPS power pin ourselves rather than using driver PM
@@ -744,6 +809,12 @@ static void gps_power_control(bool on, bool keep_vrtc = false)
* gps_power_control() was never called (GPIO not yet configured). */
void gps_power_off_for_shutdown(void)
{
#if HAS_GPS_POWER_REGULATOR
if (gps_reg_enabled && device_is_ready(gps_power_reg)) {
regulator_disable(gps_power_reg);
gps_reg_enabled = false;
}
#endif
#if HAS_GPS_POWER_CONTROL
if (gpio_is_ready_dt(&gps_enable_gpio)) {
gpio_pin_configure_dt(&gps_enable_gpio, GPIO_OUTPUT_LOW);
@@ -799,14 +870,14 @@ void gps_power_off_for_shutdown(void)
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gnss), okay) && \
DT_NODE_HAS_STATUS(DT_BUS(DT_NODELABEL(gnss)), okay)
#define HAS_GPS_UART 1
#if !HAS_GPS_POWER_CONTROL
#if !HAS_GPS_POWER_CONTROL && !HAS_GPS_POWER_REGULATOR
static const struct device *gps_uart_dev = DEVICE_DT_GET(DT_BUS(DT_NODELABEL(gnss)));
#endif
#else
#define HAS_GPS_UART 0
#endif
#if HAS_GPS_UART && !HAS_GPS_POWER_CONTROL
#if HAS_GPS_UART && !HAS_GPS_POWER_CONTROL && !HAS_GPS_POWER_REGULATOR
/* Send raw bytes to the GPS UART using blocking poll_out.
* Safe to call even though modem_chat/modem_ubx owns the UART pipe:
* uart_poll_out writes one byte at a time through the TX register,
@@ -877,7 +948,7 @@ static void gps_software_wake(void)
/* Give the module time to boot and start NMEA output */
k_msleep(200);
}
#endif /* HAS_GPS_UART && !HAS_GPS_POWER_CONTROL */
#endif /* HAS_GPS_UART && !HAS_GPS_POWER_CONTROL && !HAS_GPS_POWER_REGULATOR */
/* Go to standby and schedule next wake.
* GPIO power control only — keep VRTC for warm start on T1000-E,
@@ -901,9 +972,14 @@ static void gps_go_to_standby(void)
/* Power down the GPS module.
* GPIO boards: hardware power-off (keep VRTC for warm start on T1000-E).
* Non-GPIO boards: software sleep via UART commands (PMTK + UBX). */
* Regulator boards: cut the main rail entirely (both roles). The AXP2101
* VBACKUP charger keeps the receiver's V_BCKP domain alive, so ephemeris/
* RTC survive the cut and re-acquisition is a warm/hot start, not cold.
* Other non-GPIO boards: software sleep via UART commands (PMTK + UBX). */
#if HAS_GPS_POWER_CONTROL
gps_power_control(false, true);
#elif HAS_GPS_POWER_REGULATOR
gps_power_control(false);
#elif HAS_GPS_UART
gps_software_sleep();
#endif
@@ -932,7 +1008,7 @@ static void gps_start_acquiring(void)
consecutive_good_fixes = 0;
gnss_activity_seen_this_cycle = false;
#if HAS_GPS_POWER_CONTROL
#if HAS_GPS_POWER_CONTROL || HAS_GPS_POWER_REGULATOR
gps_power_control(true);
#elif HAS_GPS_UART
gps_software_wake();
+15 -7
View File
@@ -129,21 +129,29 @@ uint8_t RepeaterMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t
if (client == nullptr) {
uint8_t perms;
/* Constant-time comparison: pad the received password to the full
* 16-byte storage size with zeros, then compare against both
* stored passwords (which are already zero-padded by initNodePrefs).
* Compare both unconditionally so timing is identical for any
* wrong password regardless of which (admin/guest) it most
/* Constant-time comparison: zero-pad BOTH the received password and
* the stored passwords into cleared buffers (copying only up to the
* NUL) before comparing full-width. Don't trust the stored buffer to
* be zero-padded: a password set over a longer previous value via the
* CLI leaves trailing garbage past the NUL (and such garbage may
* already be persisted in flash on upgraded devices). Comparing the
* raw stored buffer full-width would then fail to match a correct
* password. Compare both unconditionally so timing is identical for
* any wrong password regardless of which (admin/guest) it most
* resembles. */
uint8_t received[sizeof(_prefs.password)] = {0};
uint8_t admin_pw[sizeof(_prefs.password)] = {0};
uint8_t guest_pw[sizeof(_prefs.guest_password)] = {0};
size_t r_len = strnlen((const char *)data, sizeof(received) - 1);
memcpy(received, data, r_len);
memcpy(admin_pw, _prefs.password, strnlen(_prefs.password, sizeof(admin_pw) - 1));
memcpy(guest_pw, _prefs.guest_password, strnlen(_prefs.guest_password, sizeof(guest_pw) - 1));
bool admin_match = mesh::Utils::constantTimeEqual(received,
_prefs.password,
admin_pw,
sizeof(received));
bool guest_match = mesh::Utils::constantTimeEqual(received,
_prefs.guest_password,
guest_pw,
sizeof(received));
if (admin_match) {
@@ -79,6 +79,13 @@
/ {
chosen {
zephyr,bt-hci = &esp32_bt_hci;
/* GPS is powered by the AXP2101 ALDO3 rail (no discrete enable GPIO),
* so the GPS manager toggles this regulator to switch GPS on/off. */
zephcore,gps-power = &vdd_gnss;
/* AXP2101 also charges the GPS V_BCKP domain (button-battery output),
* so ephemeris/RTC survive ALDO3 power cuts → warm re-fix in standby.
* Enabled via raw I2C at boot (VBACKUP isn't a Zephyr regulator). */
zephcore,gps-backup-pmu = &axp2101;
};
aliases {
+2 -2
View File
@@ -397,7 +397,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
strcpy(reply, "Error: freq 150-2500, bw 7-500, sf 5-12, cr 5-8, timeout>0");
}
} else if (memcmp(command, "password ", 9) == 0) {
StrHelper::strncpy(_prefs->password, &command[9], sizeof(_prefs->password));
StrHelper::strzcpy(_prefs->password, &command[9], sizeof(_prefs->password));
savePrefs();
snprintf(reply, CLI_REPLY_SIZE, "password now: %s", _prefs->password);
} else if (memcmp(command, "clear stats", 11) == 0) {
@@ -607,7 +607,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
strcpy(reply, "OK");
}
} else if (memcmp(config, "guest.password ", 15) == 0) {
StrHelper::strncpy(_prefs->guest_password, &config[15], sizeof(_prefs->guest_password));
StrHelper::strzcpy(_prefs->guest_password, &config[15], sizeof(_prefs->guest_password));
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "prv.key ", 8) == 0) {