From 9d14968577ba6e76bf039099ee3d28539637d69b Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 17 Dec 2021 15:10:09 -0600 Subject: [PATCH] implement passwd --status (mostly) Still need to do date printing. 'sudo passwd -S' and 'sudo passwd -S someuser' work. Note passwd has to be setuid root to be able to open shadow file in order to print most of that info as non-root. Signed-off-by: Serge Hallyn --- src/bin/passwd.rs | 120 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 7 deletions(-) diff --git a/src/bin/passwd.rs b/src/bin/passwd.rs index a1a205657..89d234f2c 100644 --- a/src/bin/passwd.rs +++ b/src/bin/passwd.rs @@ -7,9 +7,12 @@ use std::env; use std::fs; use std::path::Path; use std::process::exit; +use std::io::BufReader; +use std::io::prelude::*; +use std::fs::File; use clap::{Clap, ErrorKind, IntoApp}; -use nix::unistd::{chroot, Uid}; +use nix::unistd::{chroot, Uid, User}; const E_SUCCESS: i32 = 0; /* success */ const E_NOPERM: i32 = 1; /* permission denied */ @@ -116,7 +119,8 @@ struct Opts { login: Option, } -/*struct ShadowEntry { +#[allow(dead_code)] +struct ShadowEntry { sp_namp: String, // Login name sp_pwdp: String, // Hashed passphrase sp_lstchg: i64, // Date of last change @@ -126,7 +130,62 @@ struct Opts { sp_inact: i64, // Number of days the account may be inactive sp_expire: i64, // Number of days since 1970-01-01 until account expires sp_flag: u64, // Reserved -}*/ +} + +impl ShadowEntry { + + fn new(line: String) -> Result { + let fields: Vec<&str> = line.split(":").collect(); + if fields.len() != 9 { + // TODO - actually erroring out on a corrupt entry is probably + // bad. + return Err(String::from("Corrupt entry")); + }; + let sp_namp = String::from(fields[0]); + let sp_pwdp = String::from(fields[1]); + let sp_lstchg = fields[2].parse().unwrap_or(-1); + let sp_min = fields[3].parse().unwrap_or(-1); + let sp_max = fields[4].parse().unwrap_or(-1); + let sp_warn = fields[5].parse().unwrap_or(-1); + let sp_inact = fields[6].parse().unwrap_or(-1); + let sp_expire = fields[7].parse().unwrap_or(-1); + let sp_flag = fields[8].parse().unwrap_or(u64::MAX); + Ok(ShadowEntry{ + sp_namp, + sp_pwdp, + sp_lstchg, + sp_min, + sp_max, + sp_warn, + sp_inact, + sp_expire, + sp_flag, + }) + } +} + +#[allow(dead_code)] +struct ShadowFile { + entries: Vec, + dirty: bool, +} + +impl ShadowFile { + fn new(path: &str) -> Result { + let mut list: Vec = Vec::new(); + let f = File::open(&path); + let f = match f { + Ok(f) => f, + Err(e) => return Err(e.to_string()), + }; + let f = BufReader::new(f); + for line in f.lines() { + let e = ShadowEntry::new(line.unwrap())?; + list.push(e); + } + Ok(ShadowFile{entries: list, dirty: false}) + } +} fn do_chroot(newroot: &Path) -> anyhow::Result<()> { if !newroot.is_absolute() { @@ -141,6 +200,42 @@ fn do_chroot(newroot: &Path) -> anyhow::Result<()> { Ok(chroot(newroot)?) } +fn get_my_name() -> String { + // need to implement get_my_pwent(); or really just libc::getlogin(), but it sucks that + // it's unsafe... + // TODO: first try to libc:getlogin() + let myuid = Uid::current(); + let res = User::from_uid(myuid).unwrap().unwrap(); + return res.name; +} + +fn passwd_lock_type(passwd: &str) -> &str { + match passwd { + "*" => "L", + "!" => "L", + "" => "NP", + _ => "P", + } +} + +fn print_status(username: &str) { + let f = ShadowFile::new("/etc/shadow").expect("Failed opening shadow"); + for e in f.entries { + if e.sp_namp == username { + println!("{} {} {} {} {} {} {}", + e.sp_namp, + passwd_lock_type(e.sp_pwdp.as_str()), + e.sp_lstchg, + e.sp_min, + e.sp_max, + e.sp_warn, + e.sp_inact); + return; + } + } + println!("No such user"); +} + fn main() { let opts = Opts::try_parse().unwrap_or_else(|e| { match e.kind { @@ -157,6 +252,10 @@ fn main() { } }); + /*println!("{:?}", opts); + let args: Vec = env::args().collect(); + println!("{:?}", args);*/ + if let Some(newroot) = opts.root { do_chroot(Path::new(&newroot)).unwrap_or_else(|e| { eprintln!("failed to chroot: {}", e); @@ -175,15 +274,22 @@ fn main() { exit(E_NOPERM) } - todo!() + todo!(); + //print_all_statuses(); + //exit(E_SUCCESS); } + let myname = get_my_name(); + let _name = opts.login.unwrap_or_else(|| { - // need to implement get_my_pwent(); or really just libc::getlogin(), but it sucks that - // it's unsafe... - todo!() + myname }); + if opts.status { + print_status(_name.as_str()); + exit(E_SUCCESS); + } + exit(E_SUCCESS) } -- 2.39.5