827 lines
26 KiB
Diff
827 lines
26 KiB
Diff
From bbbcb0c08f4a6a63230288485d88492465e2a593 Mon Sep 17 00:00:00 2001
|
|
From: renoseven <dev@renoseven.net>
|
|
Date: Sat, 11 May 2024 10:21:58 +0800
|
|
Subject: [PATCH] security: change daemon socket permission
|
|
|
|
1. add socket uid & gid to config file
|
|
default uid: 0
|
|
default gid: 0
|
|
|
|
2. set socket permission by its uid & gid
|
|
uid == gid: 0600
|
|
uid != gid: 0660
|
|
|
|
Signed-off-by: renoseven <dev@renoseven.net>
|
|
---
|
|
Cargo.lock | 2 +
|
|
syscared/Cargo.toml | 1 +
|
|
syscared/src/args.rs | 8 ++-
|
|
syscared/src/config.rs | 88 ++++++++++++++++++++++++++++++
|
|
syscared/src/main.rs | 70 ++++++++++++++++++------
|
|
upatchd/Cargo.toml | 2 +-
|
|
upatchd/src/config.rs | 91 ++++++++++++++++++++++++++++++++
|
|
upatchd/src/hijacker/config.rs | 85 +++++------------------------
|
|
upatchd/src/hijacker/mod.rs | 47 +++--------------
|
|
upatchd/src/main.rs | 55 ++++++++++++++-----
|
|
upatchd/src/rpc/skeleton_impl.rs | 8 +--
|
|
11 files changed, 310 insertions(+), 147 deletions(-)
|
|
create mode 100644 syscared/src/config.rs
|
|
create mode 100644 upatchd/src/config.rs
|
|
|
|
diff --git a/Cargo.lock b/Cargo.lock
|
|
index e6d830b..e3074a6 100644
|
|
--- a/Cargo.lock
|
|
+++ b/Cargo.lock
|
|
@@ -511,6 +511,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
|
dependencies = [
|
|
"autocfg",
|
|
"hashbrown",
|
|
+ "serde",
|
|
]
|
|
|
|
[[package]]
|
|
@@ -1185,6 +1186,7 @@ dependencies = [
|
|
"object",
|
|
"parking_lot",
|
|
"serde",
|
|
+ "serde_yaml",
|
|
"signal-hook",
|
|
"syscare-abi",
|
|
"syscare-common",
|
|
diff --git a/syscared/Cargo.toml b/syscared/Cargo.toml
|
|
index 2b148e7..27f27e2 100644
|
|
--- a/syscared/Cargo.toml
|
|
+++ b/syscared/Cargo.toml
|
|
@@ -26,5 +26,6 @@ nix = { version = "0.26" }
|
|
object = { version = "0.29" }
|
|
parking_lot = { version = "0.11" }
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
+serde_yaml = { version = "0.8" }
|
|
signal-hook = { version = "0.3" }
|
|
uuid = { version = "0.8", features = ["v4", "serde"] }
|
|
diff --git a/syscared/src/args.rs b/syscared/src/args.rs
|
|
index 71cdf95..4c28dff 100644
|
|
--- a/syscared/src/args.rs
|
|
+++ b/syscared/src/args.rs
|
|
@@ -22,6 +22,7 @@ use syscare_common::fs;
|
|
|
|
use super::{DAEMON_ABOUT, DAEMON_NAME, DAEMON_VERSION};
|
|
|
|
+const DEFAULT_CONFIG_DIR: &str = "/etc/syscare";
|
|
const DEFAULT_DATA_ROOT: &str = "/usr/lib/syscare";
|
|
const DEFAULT_WORK_DIR: &str = "/var/run/syscare";
|
|
const DEFAULT_LOG_DIR: &str = "/var/log/syscare";
|
|
@@ -42,6 +43,10 @@ pub struct Arguments {
|
|
#[clap(short, long)]
|
|
pub daemon: bool,
|
|
|
|
+ /// Daemon config directory
|
|
+ #[clap(long, default_value=DEFAULT_CONFIG_DIR)]
|
|
+ pub config_dir: PathBuf,
|
|
+
|
|
/// Daemon data directory
|
|
#[clap(long, default_value = DEFAULT_DATA_ROOT)]
|
|
pub data_dir: PathBuf,
|
|
@@ -65,8 +70,9 @@ impl Arguments {
|
|
}
|
|
|
|
fn normalize_path(mut self) -> Result<Self> {
|
|
- self.work_dir = fs::normalize(&self.work_dir)?;
|
|
+ self.config_dir = fs::normalize(&self.config_dir)?;
|
|
self.data_dir = fs::normalize(&self.data_dir)?;
|
|
+ self.work_dir = fs::normalize(&self.work_dir)?;
|
|
self.log_dir = fs::normalize(&self.log_dir)?;
|
|
|
|
Ok(self)
|
|
diff --git a/syscared/src/config.rs b/syscared/src/config.rs
|
|
new file mode 100644
|
|
index 0000000..af98a51
|
|
--- /dev/null
|
|
+++ b/syscared/src/config.rs
|
|
@@ -0,0 +1,88 @@
|
|
+// SPDX-License-Identifier: Mulan PSL v2
|
|
+/*
|
|
+ * Copyright (c) 2024 Huawei Technologies Co., Ltd.
|
|
+ * upatchd is licensed under Mulan PSL v2.
|
|
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
+ * You may obtain a copy of Mulan PSL v2 at:
|
|
+ * http://license.coscl.org.cn/MulanPSL2
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
+ * See the Mulan PSL v2 for more details.
|
|
+ */
|
|
+
|
|
+use std::path::Path;
|
|
+
|
|
+use anyhow::{anyhow, Result};
|
|
+use serde::{Deserialize, Serialize};
|
|
+use syscare_common::fs;
|
|
+
|
|
+const DEFAULT_SOCKET_UID: u32 = 0;
|
|
+const DEFAULT_SOCKET_GID: u32 = 0;
|
|
+
|
|
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
+pub struct SocketConfig {
|
|
+ pub uid: u32,
|
|
+ pub gid: u32,
|
|
+}
|
|
+
|
|
+impl Default for SocketConfig {
|
|
+ fn default() -> Self {
|
|
+ Self {
|
|
+ uid: DEFAULT_SOCKET_UID,
|
|
+ gid: DEFAULT_SOCKET_GID,
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
+pub struct DaemonConfig {
|
|
+ pub socket: SocketConfig,
|
|
+}
|
|
+
|
|
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
+pub struct Config {
|
|
+ pub daemon: DaemonConfig,
|
|
+}
|
|
+
|
|
+impl Config {
|
|
+ pub fn parse<P: AsRef<Path>>(path: P) -> Result<Self> {
|
|
+ let config_path = path.as_ref();
|
|
+ let instance = serde_yaml::from_reader(fs::open_file(config_path)?)
|
|
+ .map_err(|_| anyhow!("Failed to parse config {}", config_path.display()))?;
|
|
+
|
|
+ Ok(instance)
|
|
+ }
|
|
+
|
|
+ pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
|
+ let config_path = path.as_ref();
|
|
+ let config_file = fs::create_file(config_path)?;
|
|
+ serde_yaml::to_writer(config_file, self)
|
|
+ .map_err(|_| anyhow!("Failed to write config {}", config_path.display()))?;
|
|
+
|
|
+ Ok(())
|
|
+ }
|
|
+}
|
|
+
|
|
+#[test]
|
|
+fn test() -> Result<()> {
|
|
+ use anyhow::{ensure, Context};
|
|
+ use std::path::PathBuf;
|
|
+
|
|
+ let tmp_file = PathBuf::from("/tmp/syscared.yaml");
|
|
+
|
|
+ let orig_cfg = Config::default();
|
|
+ println!("{:#?}", orig_cfg);
|
|
+
|
|
+ orig_cfg
|
|
+ .write(&tmp_file)
|
|
+ .context("Failed to write config")?;
|
|
+
|
|
+ let new_cfg = Config::parse(tmp_file).context("Failed to read config")?;
|
|
+ println!("{:#?}", new_cfg);
|
|
+
|
|
+ ensure!(orig_cfg == new_cfg, "Config does not match");
|
|
+
|
|
+ Ok(())
|
|
+}
|
|
diff --git a/syscared/src/main.rs b/syscared/src/main.rs
|
|
index 22f01df..b840abf 100644
|
|
--- a/syscared/src/main.rs
|
|
+++ b/syscared/src/main.rs
|
|
@@ -22,7 +22,8 @@ use flexi_logger::{
|
|
};
|
|
use jsonrpc_core::IoHandler;
|
|
use jsonrpc_ipc_server::{Server, ServerBuilder};
|
|
-use log::{error, info, LevelFilter, Record};
|
|
+use log::{debug, error, info, warn, LevelFilter, Record};
|
|
+use nix::unistd::{chown, Gid, Uid};
|
|
use parking_lot::RwLock;
|
|
use patch::manager::PatchManager;
|
|
use signal_hook::{consts::TERM_SIGNALS, iterator::Signals, low_level::signal_name};
|
|
@@ -30,38 +31,45 @@ use signal_hook::{consts::TERM_SIGNALS, iterator::Signals, low_level::signal_nam
|
|
use syscare_common::{fs, os};
|
|
|
|
mod args;
|
|
+mod config;
|
|
mod fast_reboot;
|
|
mod patch;
|
|
mod rpc;
|
|
|
|
use args::Arguments;
|
|
+use config::Config;
|
|
+use patch::monitor::PatchMonitor;
|
|
use rpc::{
|
|
skeleton::{FastRebootSkeleton, PatchSkeleton},
|
|
skeleton_impl::{FastRebootSkeletonImpl, PatchSkeletonImpl},
|
|
};
|
|
|
|
-use crate::patch::monitor::PatchMonitor;
|
|
-
|
|
const DAEMON_NAME: &str = env!("CARGO_PKG_NAME");
|
|
const DAEMON_VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
const DAEMON_ABOUT: &str = env!("CARGO_PKG_DESCRIPTION");
|
|
const DAEMON_UMASK: u32 = 0o077;
|
|
|
|
+const CONFIG_FILE_NAME: &str = "syscared.yaml";
|
|
+const PID_FILE_NAME: &str = "syscared.pid";
|
|
+const SOCKET_FILE_NAME: &str = "syscared.sock";
|
|
+
|
|
+const CONFIG_DIR_PERM: u32 = 0o700;
|
|
const DATA_DIR_PERM: u32 = 0o700;
|
|
const WORK_DIR_PERM: u32 = 0o755;
|
|
const LOG_DIR_PERM: u32 = 0o700;
|
|
-const PID_FILE_NAME: &str = "syscared.pid";
|
|
-const SOCKET_FILE_NAME: &str = "syscared.sock";
|
|
+const SOCKET_FILE_PERM: u32 = 0o660;
|
|
+const SOCKET_FILE_PERM_STRICT: u32 = 0o600;
|
|
|
|
const MAIN_THREAD_NAME: &str = "main";
|
|
const UNNAMED_THREAD_NAME: &str = "<unnamed>";
|
|
const LOG_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.6f";
|
|
|
|
-struct SyscareDaemon {
|
|
+struct Daemon {
|
|
args: Arguments,
|
|
+ config: Config,
|
|
}
|
|
|
|
-impl SyscareDaemon {
|
|
+impl Daemon {
|
|
fn format_log(
|
|
w: &mut dyn std::io::Write,
|
|
now: &mut DeferredNow,
|
|
@@ -101,9 +109,11 @@ impl SyscareDaemon {
|
|
os::umask::set_umask(DAEMON_UMASK);
|
|
|
|
let args = Arguments::new()?;
|
|
+ fs::create_dir_all(&args.config_dir)?;
|
|
fs::create_dir_all(&args.data_dir)?;
|
|
fs::create_dir_all(&args.work_dir)?;
|
|
fs::create_dir_all(&args.log_dir)?;
|
|
+ fs::set_permissions(&args.config_dir, Permissions::from_mode(CONFIG_DIR_PERM))?;
|
|
fs::set_permissions(&args.data_dir, Permissions::from_mode(DATA_DIR_PERM))?;
|
|
fs::set_permissions(&args.work_dir, Permissions::from_mode(WORK_DIR_PERM))?;
|
|
fs::set_permissions(&args.log_dir, Permissions::from_mode(LOG_DIR_PERM))?;
|
|
@@ -138,14 +148,29 @@ impl SyscareDaemon {
|
|
.start()
|
|
.context("Failed to initialize logger")?;
|
|
|
|
+ // Initialize config
|
|
+ debug!("Initializing configuation...");
|
|
+ let config_file = args.config_dir.join(CONFIG_FILE_NAME);
|
|
+ let config = match Config::parse(&config_file) {
|
|
+ Ok(config) => config,
|
|
+ Err(e) => {
|
|
+ warn!("{:?}", e);
|
|
+ info!("Using default configuration...");
|
|
+ let config = Config::default();
|
|
+ config.write(&config_file)?;
|
|
+
|
|
+ config
|
|
+ }
|
|
+ };
|
|
+
|
|
// Print panic to log incase it really happens
|
|
panic::set_hook(Box::new(|info| error!("{}", info)));
|
|
|
|
- Ok(Self { args })
|
|
+ Ok(Self { args, config })
|
|
}
|
|
}
|
|
|
|
-impl SyscareDaemon {
|
|
+impl Daemon {
|
|
fn daemonize(&self) -> Result<()> {
|
|
if !self.args.daemon {
|
|
return Ok(());
|
|
@@ -171,13 +196,24 @@ impl SyscareDaemon {
|
|
|
|
fn start_rpc_server(&self, io_handler: IoHandler) -> Result<Server> {
|
|
let socket_file = self.args.work_dir.join(SOCKET_FILE_NAME);
|
|
- let server = ServerBuilder::new(io_handler)
|
|
- .set_client_buffer_size(1)
|
|
- .start(
|
|
- socket_file
|
|
- .to_str()
|
|
- .context("Failed to convert socket path to string")?,
|
|
- )?;
|
|
+ let builder = ServerBuilder::new(io_handler).set_client_buffer_size(1);
|
|
+ let server = builder.start(
|
|
+ socket_file
|
|
+ .to_str()
|
|
+ .context("Failed to convert socket path to string")?,
|
|
+ )?;
|
|
+
|
|
+ let socket_owner = Uid::from_raw(self.config.daemon.socket.uid);
|
|
+ let socket_group = Gid::from_raw(self.config.daemon.socket.gid);
|
|
+ chown(&socket_file, Some(socket_owner), Some(socket_group))?;
|
|
+
|
|
+ fs::set_permissions(
|
|
+ &socket_file,
|
|
+ match socket_owner.as_raw() == socket_group.as_raw() {
|
|
+ true => Permissions::from_mode(SOCKET_FILE_PERM_STRICT),
|
|
+ false => Permissions::from_mode(SOCKET_FILE_PERM),
|
|
+ },
|
|
+ )?;
|
|
|
|
Ok(server)
|
|
}
|
|
@@ -227,7 +263,7 @@ impl SyscareDaemon {
|
|
}
|
|
|
|
fn main() {
|
|
- let daemon = match SyscareDaemon::new() {
|
|
+ let daemon = match Daemon::new() {
|
|
Ok(instance) => instance,
|
|
Err(e) => {
|
|
eprintln!("Error: {:?}", e);
|
|
diff --git a/upatchd/Cargo.toml b/upatchd/Cargo.toml
|
|
index dd9f5ca..fea0859 100644
|
|
--- a/upatchd/Cargo.toml
|
|
+++ b/upatchd/Cargo.toml
|
|
@@ -14,7 +14,7 @@ anyhow = { version = "1.0" }
|
|
clap = { version = "3.2", features = ["cargo", "derive"] }
|
|
daemonize = { version = "0.5" }
|
|
flexi_logger = { version = "0.24", features = ["compress"] }
|
|
-indexmap = { version = "1.9" }
|
|
+indexmap = { version = "1.9", features = ["serde"] }
|
|
jsonrpc-core = { version = "18.0" }
|
|
jsonrpc-derive = { version = "18.0" }
|
|
jsonrpc-ipc-server = { version = "18.0" }
|
|
diff --git a/upatchd/src/config.rs b/upatchd/src/config.rs
|
|
new file mode 100644
|
|
index 0000000..125770d
|
|
--- /dev/null
|
|
+++ b/upatchd/src/config.rs
|
|
@@ -0,0 +1,91 @@
|
|
+// SPDX-License-Identifier: Mulan PSL v2
|
|
+/*
|
|
+ * Copyright (c) 2024 Huawei Technologies Co., Ltd.
|
|
+ * upatchd is licensed under Mulan PSL v2.
|
|
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
+ * You may obtain a copy of Mulan PSL v2 at:
|
|
+ * http://license.coscl.org.cn/MulanPSL2
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
+ * See the Mulan PSL v2 for more details.
|
|
+ */
|
|
+
|
|
+use std::path::Path;
|
|
+
|
|
+use anyhow::{anyhow, Result};
|
|
+use serde::{Deserialize, Serialize};
|
|
+use syscare_common::fs;
|
|
+
|
|
+use crate::hijacker::HijackerConfig;
|
|
+
|
|
+const DEFAULT_SOCKET_UID: u32 = 0;
|
|
+const DEFAULT_SOCKET_GID: u32 = 0;
|
|
+
|
|
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
+pub struct SocketConfig {
|
|
+ pub uid: u32,
|
|
+ pub gid: u32,
|
|
+}
|
|
+
|
|
+impl Default for SocketConfig {
|
|
+ fn default() -> Self {
|
|
+ Self {
|
|
+ uid: DEFAULT_SOCKET_UID,
|
|
+ gid: DEFAULT_SOCKET_GID,
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
+pub struct DaemonConfig {
|
|
+ pub socket: SocketConfig,
|
|
+}
|
|
+
|
|
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
+pub struct Config {
|
|
+ pub daemon: DaemonConfig,
|
|
+ pub hijacker: HijackerConfig,
|
|
+}
|
|
+
|
|
+impl Config {
|
|
+ pub fn parse<P: AsRef<Path>>(path: P) -> Result<Self> {
|
|
+ let config_path = path.as_ref();
|
|
+ let instance = serde_yaml::from_reader(fs::open_file(config_path)?)
|
|
+ .map_err(|_| anyhow!("Failed to parse config {}", config_path.display()))?;
|
|
+
|
|
+ Ok(instance)
|
|
+ }
|
|
+
|
|
+ pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
|
+ let config_path = path.as_ref();
|
|
+ let config_file = fs::create_file(config_path)?;
|
|
+ serde_yaml::to_writer(config_file, self)
|
|
+ .map_err(|_| anyhow!("Failed to write config {}", config_path.display()))?;
|
|
+
|
|
+ Ok(())
|
|
+ }
|
|
+}
|
|
+
|
|
+#[test]
|
|
+fn test() -> Result<()> {
|
|
+ use anyhow::{ensure, Context};
|
|
+ use std::path::PathBuf;
|
|
+
|
|
+ let tmp_file = PathBuf::from("/tmp/upatchd.yaml");
|
|
+
|
|
+ let orig_cfg = Config::default();
|
|
+ println!("{:#?}", orig_cfg);
|
|
+
|
|
+ orig_cfg
|
|
+ .write(&tmp_file)
|
|
+ .context("Failed to write config")?;
|
|
+
|
|
+ let new_cfg = Config::parse(tmp_file).context("Failed to read config")?;
|
|
+ println!("{:#?}", new_cfg);
|
|
+
|
|
+ ensure!(orig_cfg == new_cfg, "Config does not match");
|
|
+
|
|
+ Ok(())
|
|
+}
|
|
diff --git a/upatchd/src/hijacker/config.rs b/upatchd/src/hijacker/config.rs
|
|
index f96cc05..5f97fb1 100644
|
|
--- a/upatchd/src/hijacker/config.rs
|
|
+++ b/upatchd/src/hijacker/config.rs
|
|
@@ -12,15 +12,10 @@
|
|
* See the Mulan PSL v2 for more details.
|
|
*/
|
|
|
|
-use std::{
|
|
- collections::HashMap,
|
|
- ops::Deref,
|
|
- path::{Path, PathBuf},
|
|
-};
|
|
+use std::path::PathBuf;
|
|
|
|
-use anyhow::{anyhow, Result};
|
|
+use indexmap::{indexmap, IndexMap};
|
|
use serde::{Deserialize, Serialize};
|
|
-use syscare_common::fs;
|
|
|
|
const CC_BINARY: &str = "/usr/bin/cc";
|
|
const CXX_BINARY: &str = "/usr/bin/c++";
|
|
@@ -34,73 +29,21 @@ const GCC_HIJACKER: &str = "/usr/libexec/syscare/gcc-hijacker";
|
|
const GXX_HIJACKER: &str = "/usr/libexec/syscare/g++-hijacker";
|
|
const AS_HIJACKER: &str = "/usr/libexec/syscare/as-hijacker";
|
|
|
|
-#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
-pub struct HijackerConfig(HashMap<PathBuf, PathBuf>);
|
|
-
|
|
-impl HijackerConfig {
|
|
- pub fn parse_from<P: AsRef<Path>>(path: P) -> Result<Self> {
|
|
- let config_path = path.as_ref();
|
|
- let config_file = fs::open_file(config_path)?;
|
|
- let instance: Self = serde_yaml::from_reader(config_file)
|
|
- .map_err(|_| anyhow!("Failed to parse config {}", config_path.display()))?;
|
|
-
|
|
- Ok(instance)
|
|
- }
|
|
-
|
|
- pub fn write_to<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
|
- let config_path = path.as_ref();
|
|
- let config_file = fs::create_file(config_path)?;
|
|
- serde_yaml::to_writer(config_file, self)
|
|
- .map_err(|_| anyhow!("Failed to write config {}", config_path.display()))?;
|
|
-
|
|
- Ok(())
|
|
- }
|
|
-}
|
|
-
|
|
-impl Deref for HijackerConfig {
|
|
- type Target = HashMap<PathBuf, PathBuf>;
|
|
-
|
|
- fn deref(&self) -> &Self::Target {
|
|
- &self.0
|
|
- }
|
|
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
+pub struct HijackerConfig {
|
|
+ pub mapping: IndexMap<PathBuf, PathBuf>,
|
|
}
|
|
|
|
impl Default for HijackerConfig {
|
|
fn default() -> Self {
|
|
- let mut map = HashMap::new();
|
|
- map.insert(PathBuf::from(CC_BINARY), PathBuf::from(CC_HIJACKER));
|
|
- map.insert(PathBuf::from(CXX_BINARY), PathBuf::from(CXX_HIJACKER));
|
|
- map.insert(PathBuf::from(GCC_BINARY), PathBuf::from(GCC_HIJACKER));
|
|
- map.insert(PathBuf::from(GXX_BINARY), PathBuf::from(GXX_HIJACKER));
|
|
- map.insert(PathBuf::from(AS_BINARY), PathBuf::from(AS_HIJACKER));
|
|
-
|
|
- Self(map)
|
|
+ Self {
|
|
+ mapping: indexmap! {
|
|
+ PathBuf::from(CC_BINARY) => PathBuf::from(CC_HIJACKER),
|
|
+ PathBuf::from(CXX_BINARY) => PathBuf::from(CXX_HIJACKER),
|
|
+ PathBuf::from(GCC_BINARY) => PathBuf::from(GCC_HIJACKER),
|
|
+ PathBuf::from(GXX_BINARY) => PathBuf::from(GXX_HIJACKER),
|
|
+ PathBuf::from(AS_BINARY) => PathBuf::from(AS_HIJACKER),
|
|
+ },
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
-impl std::fmt::Display for HijackerConfig {
|
|
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
- f.write_fmt(format_args!("{:#?}", &self.0))
|
|
- }
|
|
-}
|
|
-
|
|
-#[test]
|
|
-fn test() -> Result<()> {
|
|
- use anyhow::{ensure, Context};
|
|
-
|
|
- let tmp_file = PathBuf::from("/tmp/upatch_hijacker_config.yaml");
|
|
-
|
|
- let orig_cfg = HijackerConfig::default();
|
|
- println!("{}", orig_cfg);
|
|
-
|
|
- orig_cfg
|
|
- .write_to(&tmp_file)
|
|
- .context("Failed to write config")?;
|
|
-
|
|
- let new_cfg = HijackerConfig::parse_from(tmp_file).context("Failed to read config")?;
|
|
- println!("{}", new_cfg);
|
|
-
|
|
- ensure!(orig_cfg == new_cfg, "Config does not match");
|
|
-
|
|
- Ok(())
|
|
-}
|
|
diff --git a/upatchd/src/hijacker/mod.rs b/upatchd/src/hijacker/mod.rs
|
|
index 8ac12e7..d0f2c4d 100644
|
|
--- a/upatchd/src/hijacker/mod.rs
|
|
+++ b/upatchd/src/hijacker/mod.rs
|
|
@@ -12,7 +12,6 @@
|
|
* See the Mulan PSL v2 for more details.
|
|
*/
|
|
|
|
-use std::os::unix::prelude::MetadataExt;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use anyhow::{bail, Context, Result};
|
|
@@ -25,14 +24,14 @@ mod elf_resolver;
|
|
mod ioctl;
|
|
mod kmod;
|
|
|
|
-use config::HijackerConfig;
|
|
+pub use config::HijackerConfig;
|
|
use elf_resolver::ElfResolver;
|
|
use ioctl::HijackerIoctl;
|
|
use kmod::HijackerKmodGuard;
|
|
|
|
const KMOD_NAME: &str = "upatch_hijacker";
|
|
const KMOD_DEV_PATH: &str = "/dev/upatch-hijacker";
|
|
-const KMOD_FILE_PATH: &str = "/usr/libexec/syscare/upatch_hijacker.ko";
|
|
+const KMOD_PATH: &str = "/usr/libexec/syscare/upatch_hijacker.ko";
|
|
|
|
const HIJACK_SYMBOL_NAME: &str = "execve";
|
|
|
|
@@ -43,36 +42,6 @@ pub struct Hijacker {
|
|
}
|
|
|
|
impl Hijacker {
|
|
- fn initialize_config<P: AsRef<Path>>(config_path: P) -> Result<HijackerConfig> {
|
|
- const MODE_EXEC_MASK: u32 = 0o111;
|
|
-
|
|
- let config = match config_path.as_ref().exists() {
|
|
- true => HijackerConfig::parse_from(config_path)?,
|
|
- false => {
|
|
- info!("Generating default configuration...");
|
|
- let config = HijackerConfig::default();
|
|
- config.write_to(config_path)?;
|
|
-
|
|
- config
|
|
- }
|
|
- };
|
|
-
|
|
- for hijacker in config.values() {
|
|
- let is_executable_file = hijacker
|
|
- .symlink_metadata()
|
|
- .map(|m| m.is_file() && (m.mode() & MODE_EXEC_MASK != 0))
|
|
- .with_context(|| format!("Failed to read {} metadata", hijacker.display()))?;
|
|
- if !is_executable_file {
|
|
- bail!(
|
|
- "Hijack program {} is not an executable file",
|
|
- hijacker.display()
|
|
- );
|
|
- }
|
|
- }
|
|
-
|
|
- Ok(config)
|
|
- }
|
|
-
|
|
fn find_symbol_addr(symbol_name: &str) -> Result<(PathBuf, u64)> {
|
|
let exec_file = MappedFile::open(os::process::path())?;
|
|
let exec_resolver = ElfResolver::new(exec_file.as_bytes())?;
|
|
@@ -91,14 +60,9 @@ impl Hijacker {
|
|
}
|
|
|
|
impl Hijacker {
|
|
- pub fn new<P: AsRef<Path>>(config_path: P) -> Result<Self> {
|
|
- debug!("Initializing hijacker configuation...");
|
|
- let config = Self::initialize_config(config_path)
|
|
- .context("Failed to initialize hijacker configuration")?;
|
|
- info!("Using elf mapping: {}", config);
|
|
-
|
|
+ pub fn new(config: HijackerConfig) -> Result<Self> {
|
|
debug!("Initializing hijacker kernel module...");
|
|
- let kmod = HijackerKmodGuard::new(KMOD_NAME, KMOD_FILE_PATH)?;
|
|
+ let kmod = HijackerKmodGuard::new(KMOD_NAME, KMOD_PATH)?;
|
|
|
|
debug!("Initializing hijacker ioctl channel...");
|
|
let ioctl = HijackerIoctl::new(KMOD_DEV_PATH)?;
|
|
@@ -113,9 +77,9 @@ impl Hijacker {
|
|
ioctl.enable_hijacker(lib_path, offset)?;
|
|
|
|
Ok(Self {
|
|
+ config,
|
|
_kmod: kmod,
|
|
ioctl,
|
|
- config,
|
|
})
|
|
}
|
|
}
|
|
@@ -124,6 +88,7 @@ impl Hijacker {
|
|
fn get_hijacker<P: AsRef<Path>>(&self, exec_path: P) -> Result<&Path> {
|
|
let hijacker = self
|
|
.config
|
|
+ .mapping
|
|
.get(exec_path.as_ref())
|
|
.with_context(|| format!("Cannot find hijacker for {}", exec_path.as_ref().display()))?
|
|
.as_path();
|
|
diff --git a/upatchd/src/main.rs b/upatchd/src/main.rs
|
|
index 1007ebb..066e53e 100644
|
|
--- a/upatchd/src/main.rs
|
|
+++ b/upatchd/src/main.rs
|
|
@@ -22,16 +22,19 @@ use flexi_logger::{
|
|
};
|
|
use jsonrpc_core::IoHandler;
|
|
use jsonrpc_ipc_server::{Server, ServerBuilder};
|
|
-use log::{error, info, LevelFilter, Record};
|
|
+use log::{debug, error, info, warn, LevelFilter, Record};
|
|
+use nix::unistd::{chown, Gid, Uid};
|
|
use signal_hook::{consts::TERM_SIGNALS, iterator::Signals, low_level::signal_name};
|
|
|
|
use syscare_common::{fs, os};
|
|
|
|
mod args;
|
|
+mod config;
|
|
mod hijacker;
|
|
mod rpc;
|
|
|
|
use args::Arguments;
|
|
+use config::Config;
|
|
use rpc::{Skeleton, SkeletonImpl};
|
|
|
|
const DAEMON_NAME: &str = env!("CARGO_PKG_NAME");
|
|
@@ -46,17 +49,19 @@ const SOCKET_FILE_NAME: &str = "upatchd.sock";
|
|
const CONFIG_DIR_PERM: u32 = 0o700;
|
|
const WORK_DIR_PERM: u32 = 0o755;
|
|
const LOG_DIR_PERM: u32 = 0o700;
|
|
-const SOCKET_FILE_PERM: u32 = 0o666;
|
|
+const SOCKET_FILE_PERM: u32 = 0o660;
|
|
+const SOCKET_FILE_PERM_STRICT: u32 = 0o600;
|
|
|
|
const MAIN_THREAD_NAME: &str = "main";
|
|
const UNNAMED_THREAD_NAME: &str = "<unnamed>";
|
|
const LOG_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.6f";
|
|
|
|
-struct UpatchDaemon {
|
|
+struct Daemon {
|
|
args: Arguments,
|
|
+ config: Config,
|
|
}
|
|
|
|
-impl UpatchDaemon {
|
|
+impl Daemon {
|
|
fn format_log(
|
|
w: &mut dyn std::io::Write,
|
|
now: &mut DeferredNow,
|
|
@@ -133,14 +138,28 @@ impl UpatchDaemon {
|
|
.start()
|
|
.context("Failed to initialize logger")?;
|
|
|
|
+ // Initialize config
|
|
+ debug!("Initializing configuation...");
|
|
+ let config_file = args.config_dir.join(CONFIG_FILE_NAME);
|
|
+ let config = match Config::parse(&config_file) {
|
|
+ Ok(config) => config,
|
|
+ Err(e) => {
|
|
+ warn!("{:?}", e);
|
|
+ info!("Using default configuration...");
|
|
+ let config = Config::default();
|
|
+ config.write(&config_file)?;
|
|
+
|
|
+ config
|
|
+ }
|
|
+ };
|
|
+
|
|
// Print panic to log incase it really happens
|
|
panic::set_hook(Box::new(|info| error!("{}", info)));
|
|
-
|
|
- Ok(Self { args })
|
|
+ Ok(Self { args, config })
|
|
}
|
|
}
|
|
|
|
-impl UpatchDaemon {
|
|
+impl Daemon {
|
|
fn daemonize(&self) -> Result<()> {
|
|
if !self.args.daemon {
|
|
return Ok(());
|
|
@@ -156,10 +175,11 @@ impl UpatchDaemon {
|
|
}
|
|
|
|
fn initialize_skeleton(&self) -> Result<IoHandler> {
|
|
- let mut io_handler = IoHandler::new();
|
|
+ let config = self.config.hijacker.clone();
|
|
+ let methods = SkeletonImpl::new(config)?.to_delegate();
|
|
|
|
- let config_file = self.args.config_dir.join(CONFIG_FILE_NAME);
|
|
- io_handler.extend_with(SkeletonImpl::new(config_file)?.to_delegate());
|
|
+ let mut io_handler = IoHandler::new();
|
|
+ io_handler.extend_with(methods);
|
|
|
|
Ok(io_handler)
|
|
}
|
|
@@ -173,7 +193,17 @@ impl UpatchDaemon {
|
|
.context("Failed to convert socket path to string")?,
|
|
)?;
|
|
|
|
- fs::set_permissions(&socket_file, Permissions::from_mode(SOCKET_FILE_PERM))?;
|
|
+ let socket_owner = Uid::from_raw(self.config.daemon.socket.uid);
|
|
+ let socket_group = Gid::from_raw(self.config.daemon.socket.gid);
|
|
+ chown(&socket_file, Some(socket_owner), Some(socket_group))?;
|
|
+
|
|
+ fs::set_permissions(
|
|
+ &socket_file,
|
|
+ match socket_owner.as_raw() == socket_group.as_raw() {
|
|
+ true => Permissions::from_mode(SOCKET_FILE_PERM_STRICT),
|
|
+ false => Permissions::from_mode(SOCKET_FILE_PERM),
|
|
+ },
|
|
+ )?;
|
|
|
|
Ok(server)
|
|
}
|
|
@@ -183,6 +213,7 @@ impl UpatchDaemon {
|
|
info!("Upatch Daemon - {}", DAEMON_VERSION);
|
|
info!("================================");
|
|
info!("Start with {:#?}", self.args);
|
|
+ info!("Using {:#?}", self.config);
|
|
self.daemonize()?;
|
|
|
|
info!("Initializing skeleton...");
|
|
@@ -213,7 +244,7 @@ impl UpatchDaemon {
|
|
}
|
|
|
|
fn main() {
|
|
- let daemon = match UpatchDaemon::new() {
|
|
+ let daemon = match Daemon::new() {
|
|
Ok(instance) => instance,
|
|
Err(e) => {
|
|
eprintln!("Error: {:?}", e);
|
|
diff --git a/upatchd/src/rpc/skeleton_impl.rs b/upatchd/src/rpc/skeleton_impl.rs
|
|
index a334120..d725166 100644
|
|
--- a/upatchd/src/rpc/skeleton_impl.rs
|
|
+++ b/upatchd/src/rpc/skeleton_impl.rs
|
|
@@ -12,12 +12,12 @@
|
|
* See the Mulan PSL v2 for more details.
|
|
*/
|
|
|
|
-use std::path::{Path, PathBuf};
|
|
+use std::path::PathBuf;
|
|
|
|
use anyhow::{Context, Result};
|
|
use log::{debug, info};
|
|
|
|
-use crate::hijacker::Hijacker;
|
|
+use crate::hijacker::{Hijacker, HijackerConfig};
|
|
|
|
use super::{
|
|
function::{RpcFunction, RpcResult},
|
|
@@ -29,10 +29,10 @@ pub struct SkeletonImpl {
|
|
}
|
|
|
|
impl SkeletonImpl {
|
|
- pub fn new<P: AsRef<Path>>(config_path: P) -> Result<Self> {
|
|
+ pub fn new(config: HijackerConfig) -> Result<Self> {
|
|
debug!("Initializing hijacker...");
|
|
Ok(Self {
|
|
- hijacker: Hijacker::new(config_path).context("Failed to initialize hijacker")?,
|
|
+ hijacker: Hijacker::new(config).context("Failed to initialize hijacker")?,
|
|
})
|
|
}
|
|
}
|
|
--
|
|
2.34.1
|
|
|