Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting to disable DFU and FS access #1891

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/settings/SettingChimes.cpp
displayapp/screens/settings/SettingShakeThreshold.cpp
displayapp/screens/settings/SettingBluetooth.cpp
displayapp/screens/settings/SettingOTA.cpp

## Watch faces
displayapp/screens/WatchFaceAnalog.cpp
Expand Down
14 changes: 14 additions & 0 deletions src/components/ble/DfuService.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "components/ble/DfuService.h"
#include <cstring>
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/settings/Settings.h"
#include "drivers/SpiNorFlash.h"
#include "systemtask/SystemTask.h"
#include <nrf_log.h>
Expand Down Expand Up @@ -78,6 +80,18 @@ void DfuService::Init() {
}

int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
#ifndef PINETIME_IS_RECOVERY
if (systemTask.GetSettings().GetDfuAndFsMode() == Pinetime::Controllers::Settings::DfuAndFsMode::Disabled) {
Pinetime::Controllers::NotificationManager::Notification notif;
memcpy(notif.message.data(), denyAlert, denyAlertLength);
notif.size = denyAlertLength;
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
systemTask.GetNotificationManager().Push(std::move(notif));
systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification);
return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
}
#endif

if (bleController.IsFirmwareUpdating()) {
xTimerStart(timeoutTimer, 0);
}
Expand Down
5 changes: 5 additions & 0 deletions src/components/ble/DfuService.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ namespace Pinetime {

namespace Controllers {
class Ble;
class Settings;
class NotificationManager;

class DfuService {
public:
Expand Down Expand Up @@ -83,6 +85,9 @@ namespace Pinetime {
DfuImage dfuImage;
NotificationManager notificationManager;

static constexpr const char denyAlert[] = "InfiniTime\0Firmware update attempted, but disabled in settings.";
static constexpr const uint8_t denyAlertLength = sizeof(denyAlert); // for this to work denyAlert MUST be array

static constexpr uint16_t dfuServiceId {0x1530};
static constexpr uint16_t packetCharacteristicId {0x1532};
static constexpr uint16_t controlPointCharacteristicId {0x1531};
Expand Down
14 changes: 14 additions & 0 deletions src/components/ble/FSService.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <nrf_log.h>
#include "FSService.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/settings/Settings.h"
#include "systemtask/SystemTask.h"

using namespace Pinetime::Controllers;
Expand Down Expand Up @@ -49,6 +51,18 @@ void FSService::Init() {
}

int FSService::OnFSServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
#ifndef PINETIME_IS_RECOVERY
if (systemTask.GetSettings().GetDfuAndFsMode() == Pinetime::Controllers::Settings::DfuAndFsMode::Disabled) {
Pinetime::Controllers::NotificationManager::Notification notif;
memcpy(notif.message.data(), denyAlert, denyAlertLength);
notif.size = denyAlertLength;
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
systemTask.GetNotificationManager().Push(std::move(notif));
systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification);
return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
}
#endif

if (attributeHandle == versionCharacteristicHandle) {
NRF_LOG_INFO("FS_S : handle = %d", versionCharacteristicHandle);
int res = os_mbuf_append(context->om, &fsVersion, sizeof(fsVersion));
Expand Down
6 changes: 6 additions & 0 deletions src/components/ble/FSService.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace Pinetime {

namespace Controllers {
class Ble;
class Settings;
class NotificationManager;

class FSService {
public:
Expand All @@ -26,6 +28,10 @@ namespace Pinetime {
private:
Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::FS& fs;

static constexpr const char denyAlert[] = "InfiniTime\0File access attempted, but disabled in settings.";
static constexpr const uint8_t denyAlertLength = sizeof(denyAlert); // for this to work denyAlert MUST be array

static constexpr uint16_t FSServiceId {0xFEBB};
static constexpr uint16_t fsVersionId {0x0100};
static constexpr uint16_t fsTransferId {0x0200};
Expand Down
3 changes: 2 additions & 1 deletion src/components/ble/ImmediateAlertService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ int ImmediateAlertService::OnAlertLevelChanged(uint16_t attributeHandle, ble_gat
auto* alertString = ToString(alertLevel);

NotificationManager::Notification notif;
std::memcpy(notif.message.data(), alertString, strlen(alertString));
std::memcpy(notif.message.data(), alertString, strlen(alertString) + 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change related to this patchset? Or is it an unrelated bug fix

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unrelated fix.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you open a separate PR for the fix with a nice explanation what is fixed here please 🙏

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's to do with the fact that we don't use the notification buffer optimally (or even correctly really). I've proposed fixes for this in #1694 and #1695, but I haven't followed up on them.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NeroBurner Sure! I will make a separate PR for this.
@FintasticMan I think when copying a string using memcpy() the size always needs to be strlen() of the string plus 1 for the trailing zero-byte string terminator (unless the destination memory is guaranteed to be prefilled with zero-bytes).

notif.size = strlen(alertString) + 1;
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
notificationManager.Push(std::move(notif));

Expand Down
3 changes: 3 additions & 0 deletions src/components/settings/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ void Settings::LoadSettingsFromFile() {
if (bufferSettings.version == settingsVersion) {
settings = bufferSettings;
}
if (settings.dfuAndFsMode == DfuAndFsMode::EnabledTillReboot) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although it doesn't make too much of a difference, maybe apply this to bufferSettings inside the if clause. That way it's clear that work is being done only when settings are successfully loaded, and it means that settings only gets updated once

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mark9064 I would prefer to leave this as it is now. Currently it will fail-secure (set settings.dfuAndFsMode to DfuAndFsMode::Disabled) if something very unexpected happens.

@NeroBurner This is the reason why DfuAndFsMode::EnabledTillReboot should never get restored from the saved settings on reboot.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if it was unclear, I'm suggesting you do this

  if (bufferSettings.version == settingsVersion) {
    if (bufferSettings.dfuAndFsMode == DfuAndFsMode::EnabledTillReboot) {
      bufferSettings.dfuAndFsMode = DfuAndFsMode::Disabled;
    }
    settings = bufferSettings;
  }

I believe this does not change how the code behaves at all?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exactly the thing I would like to keep as is (unless requested by the maintainers to change it).
If possible I would like to keep this code as it is and keep it at the very end of Settings::LoadSettingsFromFile().

The change you suggest would not change how the code behaves.
However in case of refactoring it would increase the chances of the if (bufferSettings.dfuAndFsMode == DfuAndFsMode::EnabledTillReboot) { to be forgotten or moved to a code path that is not always executed.

Maybe I should add a code comment above if (settings.dfuAndFsMode == DfuAndFsMode::EnabledTillReboot) { telling that this (when needed) switches off DFU and FS access after reboot?
Or, should I make the requested code change + a code comment?

Copy link
Contributor

@mark9064 mark9064 Oct 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd personally prefer the suggested change with a comment explaining that this is needed for firmware+files security to work properly.

Maybe it'd be better to fix this in the data format itself though. What about introducing a new boolean member of Settings.h that's not inside the Settings struct that stores whether DFU+FS is temporarily enabled.
Then GetDfuAndFsMode can compute the correct enum value using the setting+the member, and SetDfuAndFsMode can unpack the state into Disabled,Disabled+member set,Enabled. That way it will be impossible to break when refactoring and we don't need to mess with saving/loading settings

settings.dfuAndFsMode = DfuAndFsMode::Disabled;
}
}

void Settings::SaveSettingsToFile() {
Expand Down
16 changes: 15 additions & 1 deletion src/components/settings/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace Pinetime {
};
enum class PTSGaugeStyle : uint8_t { Full, Half, Numeric };
enum class PTSWeather : uint8_t { On, Off };
enum class DfuAndFsMode : uint8_t { Disabled, Enabled, EnabledTillReboot };

struct PineTimeStyle {
Colors ColorTime = Colors::Teal;
Expand Down Expand Up @@ -283,10 +284,21 @@ namespace Pinetime {
return bleRadioEnabled;
};

void SetDfuAndFsMode(DfuAndFsMode mode) {
if (mode != settings.dfuAndFsMode) {
settingsChanged = true;
}
settings.dfuAndFsMode = mode;
};

DfuAndFsMode GetDfuAndFsMode() const {
return settings.dfuAndFsMode;
};

private:
Pinetime::Controllers::FS& fs;

static constexpr uint32_t settingsVersion = 0x0007;
static constexpr uint32_t settingsVersion = 0x0008;

struct SettingsData {
uint32_t version = settingsVersion;
Expand All @@ -308,6 +320,8 @@ namespace Pinetime {
uint16_t shakeWakeThreshold = 150;

Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;

DfuAndFsMode dfuAndFsMode = DfuAndFsMode::Disabled;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the mode EnabledTillReboot is saved into the settings, and restored on reboot then it is effectively always enabled. Please check my suspicion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NeroBurner The DfuAndFsMode::EnabledTillReboot should never be restored on reboot. Please check the link from @mark9064's comment above.

};

SettingsData settings;
Expand Down
4 changes: 4 additions & 0 deletions src/displayapp/DisplayApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "displayapp/screens/settings/SettingChimes.h"
#include "displayapp/screens/settings/SettingShakeThreshold.h"
#include "displayapp/screens/settings/SettingBluetooth.h"
#include "displayapp/screens/settings/SettingOTA.h"

#include "libs/lv_conf.h"
#include "UserApps.h"
Expand Down Expand Up @@ -524,6 +525,9 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
case Apps::SettingBluetooth:
currentScreen = std::make_unique<Screens::SettingBluetooth>(this, settingsController);
break;
case Apps::SettingOTA:
currentScreen = std::make_unique<Screens::SettingOTA>(this, settingsController);
break;
case Apps::BatteryInfo:
currentScreen = std::make_unique<Screens::BatteryInfo>(batteryController);
break;
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/apps/Apps.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace Pinetime {
SettingChimes,
SettingShakeThreshold,
SettingBluetooth,
SettingOTA,
Error
};

Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/fonts/fonts.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
{
"file": "FontAwesome5-Solid+Brands+Regular.woff",
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743"
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743, 0xf3ed"
}
],
"bpp": 1,
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/screens/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Pinetime {
static constexpr const char* batteryHalf = "\xEF\x89\x82";
static constexpr const char* heartBeat = "\xEF\x88\x9E";
static constexpr const char* bluetooth = "\xEF\x8A\x94";
static constexpr const char* shieldAlt = "\xEF\x8F\xAD";
static constexpr const char* plug = "\xEF\x87\xA6";
static constexpr const char* shoe = "\xEF\x95\x8B";
static constexpr const char* clock = "\xEF\x80\x97";
Expand Down
58 changes: 58 additions & 0 deletions src/displayapp/screens/settings/SettingOTA.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include "displayapp/screens/settings/SettingOTA.h"
#include <lvgl/lvgl.h>
#include "displayapp/DisplayApp.h"
#include "displayapp/Messages.h"
#include "displayapp/screens/Styles.h"
#include "displayapp/screens/Screen.h"
#include "displayapp/screens/Symbols.h"

using namespace Pinetime::Applications::Screens;

namespace {
struct Option {
const char* name;
Pinetime::Controllers::Settings::DfuAndFsMode mode;
};

constexpr std::array<Option, 3> options = {{
{"Enabled", Pinetime::Controllers::Settings::DfuAndFsMode::Enabled},
{"Disabled", Pinetime::Controllers::Settings::DfuAndFsMode::Disabled},
{"Till reboot", Pinetime::Controllers::Settings::DfuAndFsMode::EnabledTillReboot},
}};

std::array<CheckboxList::Item, CheckboxList::MaxItems> CreateOptionArray() {
std::array<Pinetime::Applications::Screens::CheckboxList::Item, CheckboxList::MaxItems> optionArray;
for (size_t i = 0; i < CheckboxList::MaxItems; i++) {
if (i >= options.size()) {
optionArray[i].name = "";
optionArray[i].enabled = false;
} else {
optionArray[i].name = options[i].name;
optionArray[i].enabled = true;
}
}
return optionArray;
};
}

SettingOTA::SettingOTA(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController)
: app {app},
settingsController {settingsController},
checkboxList(
0,
1,
"Firmware & files",
Symbols::shieldAlt,
settingsController.GetDfuAndFsMode() == Pinetime::Controllers::Settings::DfuAndFsMode::Enabled ? 0
: settingsController.GetDfuAndFsMode() == Pinetime::Controllers::Settings::DfuAndFsMode::EnabledTillReboot ? 2
: 1,
[&settings = settingsController](uint32_t index) {
settings.SetDfuAndFsMode(options[index].mode);
},
CreateOptionArray()) {
}

SettingOTA::~SettingOTA() {
lv_obj_clean(lv_scr_act());
settingsController.SaveSettings();
}
28 changes: 28 additions & 0 deletions src/displayapp/screens/settings/SettingOTA.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include <array>
#include <cstdint>
#include <lvgl/lvgl.h>

#include "components/settings/Settings.h"
#include "displayapp/screens/Screen.h"
#include "displayapp/screens/CheckboxList.h"

namespace Pinetime {

namespace Applications {
namespace Screens {

class SettingOTA : public Screen {
public:
SettingOTA(DisplayApp* app, Pinetime::Controllers::Settings& settingsController);
~SettingOTA() override;

private:
DisplayApp* app;
Pinetime::Controllers::Settings& settingsController;
CheckboxList checkboxList;
};
}
}
}
3 changes: 2 additions & 1 deletion src/displayapp/screens/settings/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ namespace Pinetime {
{Symbols::clock, "Chimes", Apps::SettingChimes},
{Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold},
{Symbols::check, "Firmware", Apps::FirmwareValidation},
{Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth},
{Symbols::shieldAlt, "Over-the-air", Apps::SettingOTA},
DavisNT marked this conversation as resolved.
Show resolved Hide resolved

{Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth},
{Symbols::list, "About", Apps::SysInfo},

// {Symbols::none, "None", Apps::None},
Expand Down
8 changes: 8 additions & 0 deletions src/systemtask/SystemTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ namespace Pinetime {
return nimbleController;
};

Pinetime::Controllers::NotificationManager& GetNotificationManager() {
return notificationManager;
};

Pinetime::Controllers::Settings& GetSettings() {
return settingsController;
};

bool IsSleeping() const {
return state == SystemTaskState::Sleeping || state == SystemTaskState::WakingUp;
}
Expand Down
Loading