From dfe14123e178f34bcce742f0f5a03532f97c2559 Mon Sep 17 00:00:00 2001 From: fuleyi Date: Wed, 17 Jun 2026 15:49:57 +0800 Subject: [PATCH] fix: optimize power key screen off logic and secure file operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add SafeReadFile and SafeWriteFile utilities to prevent symlink attacks 2. Check DPMS state before turning off screen to avoid redundant operations 3. Change file permissions from 0644 to 0600 for security hardening 4. Delegate power key screen toggle to x-event idle off mechanism Influence: 1. Test power key screen off behavior when screen is already off 2. Verify DPMS state file is correctly read and written 3. Test screen off function does not execute when already in DPMS off state 4. Verify file operations reject symlink-based attacks 5. Test power key behavior with various screen states 6. Verify /tmp/dpms-state file permissions are secure fix: 优化电源键关闭显示器逻辑并加固文件操作安全 1. 新增 SafeReadFile 和 SafeWriteFile 工具函数防止符号链接攻击 2. 关闭屏幕前检查 DPMS 状态以避免重复操作 3. 将文件权限从 0644 改为 0600 以加强安全性 4. 将电源键亮屏功能交由 x-event idle off 机制处理 Influence: 1. 测试屏幕已关闭时电源键的行为 2. 验证 DPMS 状态文件的正确读写 3. 测试 DPMS 已关闭状态下不会重复执行关屏操作 4. 验证文件操作能正确拒绝符号链接攻击 5. 测试不同屏幕状态下的电源键行为 6. 验证 /tmp/dpms-state 文件权限的安全性 PMS: BUG-364835 --- common/fileutil/fileutil.go | 66 +++++++++++++++++++++++++++++++ keybinding1/utils.go | 17 +++++++- session/power1/power_save_plan.go | 8 ++-- session/power1/utils.go | 4 +- 4 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 common/fileutil/fileutil.go diff --git a/common/fileutil/fileutil.go b/common/fileutil/fileutil.go new file mode 100644 index 000000000..f0c45c432 --- /dev/null +++ b/common/fileutil/fileutil.go @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package fileutil + +import ( + "fmt" + "io" + "os" + + "golang.org/x/sys/unix" +) + +// SafeReadFile 安全读取文件,拒绝符号链接 +func SafeReadFile(filename string) ([]byte, error) { + fi, err := os.Lstat(filename) + if err != nil { + return nil, err + } + if fi.Mode()&os.ModeSymlink != 0 { + return nil, fmt.Errorf("file %q is a symlink, refusing to follow", filename) + } + if !fi.Mode().IsRegular() { + return nil, fmt.Errorf("file %q is not a regular file", filename) + } + + f, err := os.OpenFile(filename, unix.O_RDONLY|unix.O_NOFOLLOW, 0) + if err != nil { + return nil, err + } + defer f.Close() + return io.ReadAll(f) +} + +// SafeWriteFile 安全写入文件,拒绝符号链接 +func SafeWriteFile(filename string, content []byte, perm os.FileMode) error { + fi, err := os.Lstat(filename) + if err != nil && !os.IsNotExist(err) { + return err + } + if err == nil { + // 文件已存在,必须是普通文件 + if fi.Mode()&os.ModeSymlink != 0 { + return fmt.Errorf("file %q is a symlink, refusing to write", filename) + } + if !fi.Mode().IsRegular() { + return fmt.Errorf("file %q is not a regular file", filename) + } + f, err := os.OpenFile(filename, unix.O_WRONLY|unix.O_TRUNC|unix.O_NOFOLLOW, perm) + if err != nil { + return err + } + defer f.Close() + _, err = f.Write(content) + return err + } + // 文件不存在,安全创建 + f, err := os.OpenFile(filename, unix.O_WRONLY|unix.O_CREAT|unix.O_EXCL, perm) + if err != nil { + return err + } + defer f.Close() + _, err = f.Write(content) + return err +} diff --git a/keybinding1/utils.go b/keybinding1/utils.go index 4070e4835..1e55cf8d5 100644 --- a/keybinding1/utils.go +++ b/keybinding1/utils.go @@ -20,6 +20,7 @@ import ( dbus "github.com/godbus/dbus/v5" "github.com/linuxdeepin/dde-daemon/keybinding1/constants" "github.com/linuxdeepin/dde-daemon/keybinding1/util" + "github.com/linuxdeepin/dde-daemon/common/fileutil" wm "github.com/linuxdeepin/go-dbus-factory/session/com.deepin.wm" gio "github.com/linuxdeepin/go-gir/gio-2.0" @@ -31,10 +32,10 @@ import ( const ( suspendStateUnknown = iota + 1 suspendStateLidOpen + suspendStateLidClose suspendStateFinish suspendStateWakeup suspendStatePrepare - suspendStateLidClose suspendStateButtonClick ) @@ -287,6 +288,9 @@ func (m *Manager) systemShutdown() { } func (m *Manager) systemTurnOffScreen() { + if isDpmsOff() { + return + } logger.Info("DPMS Off") var err error var useWayland bool @@ -327,7 +331,16 @@ func (m *Manager) systemTurnOffScreen() { m.setWmBlackScreenActive(false) } undoPrepareSuspend() - os.WriteFile("/tmp/dpms-state", []byte("1"), 0644) + fileutil.SafeWriteFile("/tmp/dpms-state", []byte("1"), 0600) +} + +func isDpmsOff() bool { + content, err := fileutil.SafeReadFile("/tmp/dpms-state") + if err != nil { + logger.Debug("read dpms state error:", err) + return false + } + return bytes.Equal(bytes.TrimSpace(content), []byte("1")) } func (m *Manager) systemLogout() { diff --git a/session/power1/power_save_plan.go b/session/power1/power_save_plan.go index 6ef763fce..2ca32a647 100644 --- a/session/power1/power_save_plan.go +++ b/session/power1/power_save_plan.go @@ -13,6 +13,8 @@ import ( "os/exec" "strings" "sync" + + "github.com/linuxdeepin/dde-daemon/common/fileutil" "time" "github.com/godbus/dbus/v5" @@ -1059,15 +1061,15 @@ func (psp *powerSavePlan) startIdleTasksLocked() { } func (ps *powerSavePlan) restoreDpmsStateFile() { - v, err := os.ReadFile("/tmp/dpms-state") + v, err := fileutil.SafeReadFile("/tmp/dpms-state") if err != nil { return } if string(v) == "1" { - err = os.WriteFile("/tmp/dpms-state", []byte("0"), 0644) + err = fileutil.SafeWriteFile("/tmp/dpms-state", []byte("0"), 0600) if err != nil { - logger.Warning("WriteFile /tmp/dpms-state:", err) + logger.Warning("write dpms state:", err) } } } diff --git a/session/power1/utils.go b/session/power1/utils.go index 6d967e050..59f9926f0 100644 --- a/session/power1/utils.go +++ b/session/power1/utils.go @@ -6,13 +6,13 @@ package power import ( "math" - "os" "os/exec" "strings" "time" dbus "github.com/godbus/dbus/v5" "github.com/linuxdeepin/dde-api/soundutils" + "github.com/linuxdeepin/dde-daemon/common/fileutil" . "github.com/linuxdeepin/go-lib/gettext" "github.com/linuxdeepin/go-lib/pulse" "github.com/linuxdeepin/go-x11-client/ext/dpms" @@ -210,7 +210,7 @@ func (m *Manager) setDPMSModeOff() { } else { callSetScreenState(true) } - os.WriteFile("/tmp/dpms-state", []byte("1"), 0644) + fileutil.SafeWriteFile("/tmp/dpms-state", []byte("1"), 0600) } const (