Require absolute path and safe ownership

This commit is contained in:
Dain Nilsson 2024-04-16 09:53:49 +02:00
parent c74738dd87
commit 3c2710c4db
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
2 changed files with 40 additions and 7 deletions

View File

@ -75,7 +75,9 @@ https://developers.yubico.com/yubikey-manager/Device_Permissions.html[here].
For some configurations running Wayland, copying an OTP to clipboard only works
when the app has focus. If you are unable to reliably copy to clipboard from
the systray icon, you can use a separate binary which take the payload to stdin
by defining the environment variable `_YA_TRAY_CLIPBOARD`.
by defining the environment variable `_YA_TRAY_CLIPBOARD`. Note that this must
be an absolute path to a binary owned by root:root, and should not be
world-writable.
For example: `_YA_TRAY_CLIPBOARD=/usr/bin/wl-copy`.
NOTE: Only use a trusted binary, OTPs will be sent to this when copied to clipboard from the systray!

View File

@ -108,7 +108,7 @@ String _getIcon() {
class _Systray extends TrayListener {
final Ref _ref;
late final String? _cliboardBinary;
String? _clipboardBinary;
int _lastClick = 0;
AppLocalizations _l10n;
DevicePath _devicePath = DevicePath([]);
@ -116,12 +116,11 @@ class _Systray extends TrayListener {
bool _isHidden = false;
_Systray(this._ref) : _l10n = _ref.read(l10nProvider) {
_cliboardBinary =
Platform.isLinux ? Platform.environment['_YA_TRAY_CLIPBOARD'] : null;
_init();
}
Future<void> _init() async {
unawaited(_initClipboardBinary());
await trayManager.setIcon(_getIcon(), isTemplate: true);
await _updateContextMenu();
@ -129,6 +128,37 @@ class _Systray extends TrayListener {
trayManager.addListener(this);
}
Future<void> _initClipboardBinary() async {
final clipboardPath = Platform.environment['_YA_TRAY_CLIPBOARD'];
if (clipboardPath != null && Platform.isLinux) {
final file = File(clipboardPath);
if (!(await file.exists())) {
_log.warning(
'Not using custom binary for clipboard: $clipboardPath. File not found.');
return;
}
final resolved = await file.resolveSymbolicLinks();
final result = await Process.run('ls', ['-nd', '--', resolved],
environment: {'LC_ALL': 'C'});
if (result.exitCode == 0) {
final output = result.stdout as String;
//Eg. "-rwxr-xr-x 1 0 0 52384 Oct 7 2019 /usr/bin/wl-copy"
final isFile = output[0] == '-';
final noWorldWrite = output[8] == '-';
final parts = output.split(RegExp(r'\s+'));
final rootOwner = parts[2] == '0';
final rootGroup = parts[3] == '0';
//Ensure file, owned by root:root, not world writable
if (isFile && noWorldWrite && rootOwner && rootGroup) {
_clipboardBinary = resolved;
} else {
_log.warning('Not using custom binary for clipboard: $clipboardPath');
_log.debug('Refusing to use custom clipboard binary: $output');
}
}
}
}
void dispose() {
trayManager.removeListener(this);
trayManager.destroy();
@ -195,11 +225,12 @@ class _Systray extends TrayListener {
onClick: (_) async {
final code = await _calculateCode(_devicePath, e, _ref);
if (code != null) {
if (_cliboardBinary != null) {
if (_clipboardBinary != null) {
// Copy to clipboard via another executable, which can be needed for Wayland
_log.debug(
'Using custom binary to copy to clipboard: $_cliboardBinary');
final process = await Process.start(_cliboardBinary!, []);
'Using custom binary to copy to clipboard: $_clipboardBinary');
final process =
await Process.start(_clipboardBinary!, []);
process.stdin.writeln(code.value);
await process.stdin.close();
} else {