From bb21560c6ab6217c06533ea85dd7db5a40379e5b Mon Sep 17 00:00:00 2001
From: renoseven <dev@renoseven.net>
Date: Sat, 11 May 2024 10:21:58 +0800
Subject: [PATCH 17/21] 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 1d13df6..3e920ab 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -511,6 +511,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
 dependencies = [
  "autocfg",
  "hashbrown",
+ "serde",
 ]
 
 [[package]]
@@ -1184,6 +1185,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.41.0

