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

Handle password protected BIOS [linux] #36

Merged
merged 8 commits into from
Feb 18, 2024
Merged
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This app is a modern, Flutter based GUI on top of Dell Command | Configure CLI,
* Integrated OTA via Github API
* Detects and handles unsupported modes on supported machines
* Detects non-dell machines, shows error message
* Support protected BIOS (System/Setup/Owner passwords), and secure key saving

Control features:
* Battery status overview (health etc.)
Expand Down Expand Up @@ -83,5 +84,8 @@ Please see [issues](https://github.com/alexVinarskis/dell-powermanager/issues).
* Dell for providing 'Dell Command | Configure CLI'
* Google for creating Flutter :)

## Disclaimer
As per license, this software is provided as-is, without any warranty. It is not affiliated with Dell in any way. Use at your own risk. Me or any other contributors are not responsible for any damage caused by this software, including but not limited to data loss, hardware damage, data breaches etc. Where applicable, integrated solution for secure key saving is used, but it is not guaranteed to be secure in any way. Understand risk and implications before using it. No legal claims can be made against the author or contributors.

## License
This application is licensed under GPLv3. In short, this means you use/copy/modify/distribute it for free, but you must provide source code of your modifications, and keep the same license. You cannot sell it as proprietary software. See [LICENSE](LICENSE) for details.
44 changes: 32 additions & 12 deletions lib/classes/api_cctk.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dell_powermanager/classes/bios_protection_manager.dart';
import 'package:process_run/shell.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';

import '../configs/constants.dart';
import '../classes/dependencies_manager.dart';
Expand Down Expand Up @@ -35,13 +37,16 @@ class ApiCCTK {
static late Timer _timer;
static bool? _apiReady;
static bool _cctkMutexLocked = false;
static final _shell = Shell(verbose: Environment.runningDebug, throwOnError: false);
static Shell _shell = Shell();

static SharedPreferences? _prefs;
static const _uuid = Uuid();

static final CCTKState cctkState = CCTKState();

ApiCCTK(Duration refreshInternal) {
sourceEnvironment();
BiosProtectionManager.secureReadPassword();
_refreshInternal = refreshInternal;
_query();
_timer = Timer.periodic(_refreshInternal, (Timer t) => _query());
Expand All @@ -54,6 +59,14 @@ class ApiCCTK {
static void stop() {
_timer.cancel();
}
static void sourceEnvironment() {
/* (re)create shell with optional environment variables overwrite */
_shell = Shell(
verbose: Environment.runningDebug,
throwOnError: false,
environment: Environment.biosPwd == null ? null : (ShellEnvironment()..vars[Constants.varnameBiosPwd] = Environment.biosPwd!),
);
}

static void _callDepsChanged(bool apiReady) {
var dubList = List.from(_callbacksDepsChanged);
Expand Down Expand Up @@ -124,18 +137,17 @@ class ApiCCTK {
// get & process response
bool success = _processResponse(await _runCctk(arg));
_cctkRelease();
if (!success) {
return false;
}
// notify listeners
_callStateChanged(cctkState);
return true;
return success;
}

static bool _processResponse(ProcessResult pr) {
cctkState.exitCodeRead = pr.exitCode;
if (pr.exitCode != 0) {
_apiReady ??= false;
return false;
} else {
_apiReady ??= true;
}
for (String output in pr.stdout.toString().split("\n")) {
List<String> argAndValue = output.trim().replaceAll("\r", "").split("=");
Expand All @@ -150,6 +162,7 @@ class ApiCCTK {
}

static bool _processSupported(ProcessResult pr, var param) {
cctkState.exitCodeRead = pr.exitCode;
String output = (pr.stderr.toString() + pr.stdout.toString()).replaceAll("\n", "");
if ((output.isEmpty && pr.exitCode == 0) || output.contains("WMI-ACPI")) {
cctkState.cctkCompatible = false;
Expand All @@ -158,6 +171,8 @@ class ApiCCTK {
if (pr.exitCode != 0) {
_apiReady ??= false;
return false;
} else {
_apiReady ??= true;
}
Map<String, bool> supportedModes = {};
for (String output in pr.stdout.toString().replaceAll("\r", "").split("\n")) {
Expand All @@ -173,14 +188,19 @@ class ApiCCTK {
return true;
}

static Future<bool> request(String cctkType, String mode) async {
// get & process response
if (!_processResponse(await _runCctk('--$cctkType=$mode'))) {
return false;
static Future<bool> request(String cctkType, String mode, {String? requestCode}) async {
/* Query, process, and respond */
late String cmd;
if (Platform.isLinux) {
cmd = '--$cctkType=$mode${Environment.biosPwd == null ? "" : " --ValSetupPwd=\$${Constants.varnameBiosPwd} --ValSysPwd=\$${Constants.varnameBiosPwd}"}';
} else {
cmd = '--$cctkType=$mode${Environment.biosPwd == null ? "" : " --ValSetupPwd=%${Constants.varnameBiosPwd}% --ValSysPwd=%${Constants.varnameBiosPwd}%"}';
}
// notify listeners
ProcessResult pr = await _runCctk(cmd);
bool success = _processResponse(pr);
cctkState.exitStateWrite = ExitState(pr.exitCode, cctkType, mode, requestCode?? _uuid.v4());
_callStateChanged(cctkState);
return true;
return success;
}

static Future<ProcessResult> _runCctk(String arg) async {
Expand Down
36 changes: 36 additions & 0 deletions lib/classes/bios_protection_manager.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'dart:convert';

import 'package:flutter_secure_storage/flutter_secure_storage.dart';

import '../classes/api_cctk.dart';
import '../configs/constants.dart';
import '../configs/environment.dart';

class BiosProtectionManager {
static const storage = FlutterSecureStorage();

static Future<void> secureReadPassword() async {
String? pwdB64 = await storage.read(key: Constants.varnameBiosPwd);
if (pwdB64 == null) {
return;
}
String pwd = utf8.decode(base64.decode(pwdB64));
if (pwd.isEmpty) {
return;
}
loadPassword(pwd);
}

static Future<void> secureWritePassword(String password) async {
await storage.write(
key: Constants.varnameBiosPwd,
value: base64.encode(utf8.encode(password)),
);
loadPassword(password);
}

static void loadPassword(String password) {
Environment.biosPwd = password;
ApiCCTK.sourceEnvironment();
}
}
14 changes: 14 additions & 0 deletions lib/classes/cctk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class CCTK {
/*
* Exit codes of the CLI
* Used premarily for BIOS password requests handling
*
*/
static const exitCodes = (
ok: 0,
sysPwdRequired: 66,
sysPwdInvalid: 67,
setupPwdRequired: 65,
setupPwdInvalid: 58,
ownerPwdSet: 112,
);

/*
* Thermal Management
*
Expand Down
11 changes: 11 additions & 0 deletions lib/classes/cctk_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,19 @@ class ParameterState {
ParameterState({this.mode = "", this.supported});
}

class ExitState {
int exitCode;
String cctkType;
String mode;
String requestCode;

ExitState(this.exitCode, this.cctkType, this.mode, this.requestCode);
}

class CCTKState {
bool? cctkCompatible;
int? exitCodeRead;
ExitState? exitStateWrite;
Map <dynamic, ParameterState> parameters = {
CCTK.thermalManagement: ParameterState(),
CCTK.primaryBattChargeCfg: ParameterState(),
Expand Down
103 changes: 0 additions & 103 deletions lib/components/menu_compatibility.dart

This file was deleted.

13 changes: 11 additions & 2 deletions lib/components/mode_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ enum OptionState {
}

class ModeItem extends StatefulWidget {
const ModeItem(this.title, {super.key, this.description = "", this.paddingH = 0, this.paddingV = 0, this.onPress, this.isSelected = false, this.isSupported = true, this.backgroundColor = Colors.transparent, this.isLoading = false, this.bottomItem, this.isDataMissing = false});
const ModeItem(this.title, {super.key, this.description = "", this.paddingH = 0, this.paddingV = 0, this.onPress, this.isSelected = false, this.isSupported = true, this.backgroundColor = Colors.transparent, this.isLoading = false, this.failedToSwitch = false, this.bottomItem, this.isDataMissing = false});

final String title;
final String description;
Expand All @@ -22,6 +22,7 @@ class ModeItem extends StatefulWidget {
final bool isSupported;
final bool isLoading;
final bool isDataMissing;
final bool failedToSwitch;
final Color backgroundColor;
final Widget? bottomItem;
final onPress;
Expand All @@ -38,6 +39,8 @@ class _ModeItemState extends State<ModeItem> {
switch (state) {
case OptionState.selectSucceeded:
return const LinearProgressIndicator(backgroundColor: Colors.transparent, value: 1,);
case OptionState.selectFailed:
return LinearProgressIndicator(backgroundColor: Colors.transparent, color: Theme.of(context).colorScheme.error, value: 1,);
case OptionState.selecting:
return const LinearProgressIndicator(backgroundColor: Colors.transparent);
default:
Expand Down Expand Up @@ -76,7 +79,13 @@ class _ModeItemState extends State<ModeItem> {
)
:
const SizedBox(height: 20,),
_getProgressBar(widget.isSelected ? widget.isLoading ? OptionState.selecting : OptionState.selectSucceeded : OptionState.deselected, context),
_getProgressBar(
!widget.isSelected ? OptionState.deselected :
widget.isLoading ? OptionState.selecting :
widget.failedToSwitch ? OptionState.selectFailed :
OptionState.selectSucceeded,
context,
),
],
);
}
Expand Down
Loading
Loading