From 1752dabe3f779286735a652e7ad4b6d0ea77e68d Mon Sep 17 00:00:00 2001 From: llenotre Date: Thu, 4 Jul 2024 00:04:26 +0200 Subject: [PATCH] refactor: move `login` and `su` to the common executable --- login/Cargo.toml | 10 ----- src/insmod.rs | 2 +- login/src/main.rs => src/login.rs | 7 +-- src/lsmod.rs | 2 +- src/main-suid.rs | 22 +++++++++- src/main.rs | 10 +---- src/rmmod.rs | 2 +- su/src/main.rs => src/su.rs | 72 +++++++++++++------------------ src/utils/lib.rs | 9 ++++ su/Cargo.toml | 9 ---- 10 files changed, 68 insertions(+), 77 deletions(-) delete mode 100644 login/Cargo.toml rename login/src/main.rs => src/login.rs (97%) rename su/src/main.rs => src/su.rs (50%) delete mode 100644 su/Cargo.toml diff --git a/login/Cargo.toml b/login/Cargo.toml deleted file mode 100644 index 863b9a3..0000000 --- a/login/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "login" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -utils = { path = "../utils" } -libc = "*" diff --git a/src/insmod.rs b/src/insmod.rs index 0575520..f5f212d 100644 --- a/src/insmod.rs +++ b/src/insmod.rs @@ -1,6 +1,5 @@ //! The `insmod` command loads a module from a file. -use crate::error; use std::env::ArgsOs; use std::ffi::c_long; use std::fs::File; @@ -8,6 +7,7 @@ use std::io::Error; use std::os::fd::AsRawFd; use std::process::exit; use std::ptr::null; +use utils::error; /// The ID of the `finit_module` system call. const FINIT_MODULE_ID: c_long = 0x15e; diff --git a/login/src/main.rs b/src/login.rs similarity index 97% rename from login/src/main.rs rename to src/login.rs index a2bacd4..2292e4f 100644 --- a/login/src/main.rs +++ b/src/login.rs @@ -1,8 +1,6 @@ //! `login` prompts a username/password to authenticate on a new session. -#![feature(never_type)] -#![feature(os_str_display)] - +use std::env::ArgsOs; use std::ffi::{CString, OsString}; use std::fmt::Formatter; use std::os::unix::ffi::OsStrExt; @@ -100,8 +98,7 @@ impl fmt::Display for LoginPrompt { } } -fn main() { - let _args = env::args(); // TODO Parse and use +pub fn main(_args: ArgsOs) { let login_prompt = LoginPrompt(get_hostname()); loop { println!(); diff --git a/src/lsmod.rs b/src/lsmod.rs index 1413aa8..9723e87 100644 --- a/src/lsmod.rs +++ b/src/lsmod.rs @@ -1,9 +1,9 @@ //! The `lsmod` command allows to list loaded kernel modules. -use crate::error; use std::fs::File; use std::io::BufRead; use std::io::BufReader; +use utils::error; /// The path to the modules file. const MODULES_PATH: &str = "/proc/modules"; diff --git a/src/main-suid.rs b/src/main-suid.rs index 0568193..3ab1ccd 100644 --- a/src/main-suid.rs +++ b/src/main-suid.rs @@ -1,5 +1,25 @@ //! Main of all commands that **require** the SUID flag. +#![feature(never_type)] +#![feature(os_str_display)] + +mod login; +mod su; + +use std::env; +use utils::error; + fn main() { - // TODO + let mut args = env::args_os(); + let bin = args + .next() + .and_then(|s| s.into_string().ok()) + .unwrap_or_else(|| { + error("mutils", "missing binary name"); + }); + match bin.as_str() { + "login" => login::main(args), + "su" => su::main(args), + _ => error("mutils", "invalid binary name"), + } } diff --git a/src/main.rs b/src/main.rs index 6c5ddf9..28dcff1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,14 +11,8 @@ mod nologin; mod ps; mod rmmod; -use std::process::exit; -use std::{env, fmt}; - -/// Writes an error to stderr, then exits. -fn error(bin: &str, msg: M) -> ! { - eprintln!("{bin}: error: {msg}"); - exit(1); -} +use std::env; +use utils::error; fn main() { let mut args = env::args_os(); diff --git a/src/rmmod.rs b/src/rmmod.rs index 158af34..f182563 100644 --- a/src/rmmod.rs +++ b/src/rmmod.rs @@ -1,12 +1,12 @@ //! The `rmmod` command unloads a module. -use crate::error; use std::env::ArgsOs; use std::ffi::c_long; use std::ffi::CString; use std::io::Error; use std::os::unix::ffi::OsStrExt; use std::process::exit; +use utils::error; /// The ID of the `delete_module` system call. const DELETE_MODULE_ID: c_long = 0x81; diff --git a/su/src/main.rs b/src/su.rs similarity index 50% rename from su/src/main.rs rename to src/su.rs index 50966da..84db2f6 100644 --- a/su/src/main.rs +++ b/src/su.rs @@ -1,86 +1,76 @@ //! `su` is a command allowing to run another command with a substitute user and group ID. -use std::env; +use std::env::ArgsOs; +use std::ffi::{OsStr, OsString}; +use std::os::unix::ffi::OsStrExt; use std::process::exit; use std::process::Command; +use utils::error; use utils::prompt::prompt; /// The command's arguments. #[derive(Default)] -struct Args<'s> { +struct Args { /// The user which executes the command. If None, using root. - user: Option<&'s str>, + user: Option, /// The group which executes the command. If None, using root. - group: Option<&'s str>, - + group: Option, /// The shell to execute. If None, using the default. - shell: Option<&'s str>, - + shell: Option, /// Arguments for the command to execute. - args: Vec<&'s str>, + args: Vec, } /// Parses the given CLI arguments `args` and returns their representation in the `Args` structure. -fn parse_args(args: &Vec) -> Args<'_> { +fn parse_args(args: ArgsOs) -> Args { + let mut args = args.peekable(); let mut result = Args::default(); - // Iterating on arguments, skipping binary's name - let mut iter = args.iter().skip(1).peekable(); - // Tells whether arguments contain initial options - let has_options = { - iter.peek() - .map(|first_arg| { - first_arg - .chars() - .peekable() - .peek() - .map(|first_char| *first_char == '-') - .unwrap_or(false) - }) - .unwrap_or(false) - }; - - // Parsing options if present + let has_options = args + .peek() + .map(|first_arg| first_arg.as_bytes().first().cloned() == Some(b'-')) + .unwrap_or(false); + // Parse options if present if has_options { - for arg in iter.by_ref() { + for arg in args.by_ref() { if arg == "-" { break; } // TODO } } - - result.user = iter.next().map(String::as_str); - result.args = iter.map(String::as_str).collect(); + result.user = args.next(); + result.args = args.collect(); result } -fn main() { - let args: Vec = env::args().collect(); - let args = parse_args(&args); +pub fn main(args: ArgsOs) { + let args = parse_args(args); - let _user = args.user.unwrap_or("root"); + let _user = args.user.as_deref().unwrap_or(OsStr::new("root")); // TODO Read user's entry - let shell = args.shell.unwrap_or("TODO"); + let shell = args.shell.as_deref().unwrap_or(OsStr::new("TODO")); let _pass = prompt("Password: ", true); let correct = false; // TODO Check password against user's if correct { // TODO Change user - - // Running the shell + // TODO use `execve` instead + // Run the shell let status = Command::new(shell) .args(args.args) // TODO Set env .status() - .unwrap_or_else(|_| { - eprintln!("su: Failed to run shell `{shell}`"); - exit(1); + .unwrap_or_else(|e| { + error( + "su", + format_args!("Failed to run shell `{}`: {e}", shell.display()), + ); }); - // Exiting with the shell's status + // Exit with the shell's status exit(status.code().unwrap()); } else { eprintln!("su: Authentication failure"); diff --git a/src/utils/lib.rs b/src/utils/lib.rs index 1d92416..2b6f581 100644 --- a/src/utils/lib.rs +++ b/src/utils/lib.rs @@ -1,6 +1,15 @@ //! Features utils to several commands. +use std::fmt; +use std::process::exit; + pub mod disk; pub mod prompt; pub mod user; pub mod util; + +/// Writes an error to stderr, then exits. +pub fn error(bin: &str, msg: M) -> ! { + eprintln!("{bin}: error: {msg}"); + exit(1); +} diff --git a/su/Cargo.toml b/su/Cargo.toml deleted file mode 100644 index 752413d..0000000 --- a/su/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "su" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -utils = { path = "../utils" }