diff --git a/Cargo.lock b/Cargo.lock index c257444566..efb0c144be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,21 +1,7 @@ -[root] -name = "userutils" -version = "0.1.0" -dependencies = [ - "arg_parser 0.1.0 (git+https://github.com/redox-os/arg-parser.git)", - "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "extra 0.1.0 (git+https://github.com/redox-os/libextra.git)", - "liner 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "arg_parser" version = "0.1.0" -source = "git+https://github.com/redox-os/arg-parser.git#288d2fd9ae27ed2c7a3aaf5a77cf07e2b2bd356c" +source = "git+https://github.com/redox-os/arg-parser.git#d16e2d02e87996bbe2bce4a201c66c0df4a1e866" [[package]] name = "argon2rs" @@ -67,7 +53,7 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.32" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -81,11 +67,11 @@ dependencies = [ [[package]] name = "rand" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -101,6 +87,16 @@ dependencies = [ "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "redox_users" +version = "0.1.0" +source = "git+https://github.com/redox-os/users.git#77ce04ba3c2a1e4731e75eb58984598fa8937880" +dependencies = [ + "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "extra 0.1.0 (git+https://github.com/redox-os/libextra.git)", + "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scoped_threadpool" version = "0.1.8" @@ -111,7 +107,7 @@ name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -121,6 +117,21 @@ name = "unicode-width" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "userutils" +version = "0.1.0" +dependencies = [ + "arg_parser 0.1.0 (git+https://github.com/redox-os/arg-parser.git)", + "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "extra 0.1.0 (git+https://github.com/redox-os/libextra.git)", + "liner 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_users 0.1.0 (git+https://github.com/redox-os/users.git)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "checksum arg_parser 0.1.0 (git+https://github.com/redox-os/arg-parser.git)" = "" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" @@ -130,11 +141,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum extra 0.1.0 (git+https://github.com/redox-os/libextra.git)" = "" "checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159" "checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82" -"checksum libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "56cce3130fd040c28df6f495c8492e5ec5808fb4c9093c310df02b0c8f030148" +"checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2" "checksum liner 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f9e406164c25b420480023985bdf65cef366855666ad4cb12cd3eaee82dcb399" -"checksum rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "61efcbcd9fa8d8fbb07c84e34a8af18a1ff177b449689ad38a6e9457ecc7b2ae" +"checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd" "checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum redox_users 0.1.0 (git+https://github.com/redox-os/users.git)" = "" "checksum scoped_threadpool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4ea459fe3ceff01e09534847c49860891d3ff1c12b4eb7731b67f2778fb60190" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" diff --git a/Cargo.toml b/Cargo.toml index e3903112a8..2b1bea0415 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,4 +38,5 @@ liner = "0.1" rand = "0.3" redox_syscall = "0.1" redox_termios = "0.1" +redox_users = { git = "https://github.com/redox-os/users.git" } termion = "1.5.1" diff --git a/src/bin/id.rs b/src/bin/id.rs index 21fa6ff269..a64a6ad9e0 100644 --- a/src/bin/id.rs +++ b/src/bin/id.rs @@ -1,6 +1,6 @@ extern crate arg_parser; extern crate extra; -extern crate userutils; +extern crate redox_users; use std::borrow::Borrow; use std::hash::Hash; @@ -11,7 +11,7 @@ use std::process::exit; use extra::io::fail; use extra::option::OptionalExt; use arg_parser::{ArgParser, Param}; -use userutils::{get_egid, get_gid, get_euid, get_uid, get_user, get_group}; +use redox_users::{get_egid, get_gid, get_euid, get_uid, get_user_by_id, get_group_by_id}; const HELP_INFO: &'static str = "Try ‘id --help’ for more information.\n"; const MAN_PAGE: &'static str = /* @MANSTART{id} */ r#" @@ -97,8 +97,8 @@ pub fn main() { exit(1); } - let egid = get_egid(&mut stderr); - let gid = get_gid(&mut stderr); + let egid = get_egid(); + let gid = get_gid(); print_msg(&format!("{} {}\n", egid, gid), &mut stdout, &mut stderr); exit(0); } @@ -115,13 +115,13 @@ pub fn main() { if parser.found(&'u') && parser.found(&'n') { // Did they pass -r? F so, we show the real let uid = if parser.found(&'r') { - get_uid(&mut stderr) + get_uid() } else { - get_euid(&mut stderr) + get_euid() }; - get_user(uid, &mut stderr).map(|user| { - print_msg(&format!("{}\n", user), &mut stdout, &mut stderr); + get_user_by_id(uid).map(|user| { + print_msg(&format!("{}\n", user.user), &mut stdout, &mut stderr); exit(0); }).or_else(|| { fail(&format!("id: no user found for uid: {}", uid), &mut stderr) @@ -130,14 +130,14 @@ pub fn main() { // Display real user ID if parser.found(&'u') && parser.found(&'r') { - let uid = get_uid(&mut stderr); + let uid = get_uid(); print_msg(&format!("{}\n", uid), &mut stdout, &mut stderr); exit(0); } // Display effective user ID if parser.found(&'u') { - let euid = get_euid(&mut stderr); + let euid = get_euid(); print_msg(&format!("{}\n", euid), &mut stdout, &mut stderr); exit(0); } @@ -146,13 +146,13 @@ pub fn main() { if parser.found(&'g') && parser.found(&'n') { // Did they pass -r? If so we show the real one let gid = if parser.found(&'r') { - get_gid(&mut stderr) + get_gid() } else { - get_egid(&mut stderr) + get_egid() }; - get_group(gid, &mut stderr).map(|group| { - print_msg(&format!("{}\n", group), &mut stdout, &mut stderr); + get_group_by_id(gid).map(|group| { + print_msg(&format!("{}\n", group.group), &mut stdout, &mut stderr); exit(0); }).or_else(|| { fail(&format!("id: no group found for gid: {}", gid), &mut stderr) @@ -161,14 +161,14 @@ pub fn main() { // Display the real group ID if parser.found(&'g') && parser.found(&'r') { - let gid = get_gid(&mut stderr); + let gid = get_gid(); print_msg(&format!("{}\n", gid), &mut stdout, &mut stderr); exit(0); } // Display effective group ID if parser.found(&'g') { - let egid = get_egid(&mut stderr); + let egid = get_egid(); print_msg(&format!("{}\n", egid), &mut stdout, &mut stderr); exit(0); } @@ -186,17 +186,17 @@ pub fn main() { } // We get everything we can and show that - let euid = get_euid(&mut stderr); - let egid = get_egid(&mut stderr); - let user = get_user(euid, &mut stderr).unwrap_or_else(|| { + let euid = get_euid(); + let egid = get_egid(); + let user = get_user_by_id(euid).unwrap_or_else(|| { fail(&format!("id: no user found for uid: {}", euid), &mut stderr); }); - let group = get_group(egid, &mut stderr).unwrap_or_else(|| { + let group = get_group_by_id(egid).unwrap_or_else(|| { fail(&format!("id: no group found for gid: {}", euid), &mut stderr); }); - let msg = format!("uid={}({}) gid={}({})\n", euid, user, egid, group); + let msg = format!("uid={}({}) gid={}({})\n", euid, user.user, egid, group.group); print_msg(&msg, &mut stdout, &mut stderr); exit(0); } diff --git a/src/bin/login.rs b/src/bin/login.rs index 9f7eb27f28..84f06fd34b 100644 --- a/src/bin/login.rs +++ b/src/bin/login.rs @@ -4,20 +4,20 @@ extern crate arg_parser; extern crate extra; extern crate liner; extern crate termion; +extern crate redox_users; extern crate userutils; use std::fs::File; -use std::io::{self, Read, Write}; -use std::os::unix::process::CommandExt; -use std::process::{exit, Command}; +use std::io::{self, Write}; +use std::process::exit; use std::env; use std::str; -use extra::io::fail; use extra::option::OptionalExt; use arg_parser::ArgParser; use termion::input::TermRead; -use userutils::Passwd; +use redox_users::get_user_by_name; +use userutils::spawn_shell; const MAN_PAGE: &'static str = /* @MANSTART{login} */ r#" NAME @@ -36,12 +36,11 @@ OPTIONS Display this help and exit. AUTHOR - Written by Jeremy Soller. + Written by Jeremy Soller, Jose Narvaez. "#; /* @MANEND */ const ISSUE_FILE: &'static str = "/etc/issue"; const MOTD_FILE: &'static str = "/etc/motd"; -const PASSWD_FILE: &'static str = "/etc/passwd"; pub fn main() { let mut stdout = io::stdout(); @@ -72,67 +71,42 @@ pub fn main() { let stdin = io::stdin(); let mut stdin = stdin.lock(); - let mut passwd_string = String::new(); - match File::open(PASSWD_FILE) { - Ok(mut file) => file.read_to_string(&mut passwd_string).try(&mut stderr), - Err(err) => { - let msg = &format!("login: failed to open passwd file: {}", err); - fail(msg, &mut stderr); - } - }; - - let passwd_file_entries = match Passwd::parse_file(&passwd_string) { - Ok(entries) => entries, - Err(_) => fail("login: error parsing passwd file", &mut stderr) - }; - - let mut passwd_option = passwd_file_entries.iter() - .find(|passwd| user == passwd.user && "" == passwd.hash); - - if passwd_option.is_none() { - stdout.write_all(b"\x1B[1mpassword:\x1B[0m ").try(&mut stderr); - stdout.flush().try(&mut stderr); - - if let Some(password) = stdin.read_passwd(&mut stdout).try(&mut stderr) { + let user_option = get_user_by_name(user); + match user_option { + None => { + stdout.write(b"\nLogin incorrect\n").try(&mut stderr); stdout.write(b"\n").try(&mut stderr); - stdout.flush().try(&mut stderr);; - - passwd_option = passwd_file_entries.iter() - .find(|passwd| user == passwd.user && passwd.verify(&password)); - } - } - - if let Some(passwd) = passwd_option { - if let Ok(mut motd) = File::open(MOTD_FILE) { - io::copy(&mut motd, &mut stdout).try(&mut stderr); stdout.flush().try(&mut stderr); + continue; + }, + Some(user) => { + if user.hash == "" { + if let Ok(mut motd) = File::open(MOTD_FILE) { + io::copy(&mut motd, &mut stdout).try(&mut stderr); + stdout.flush().try(&mut stderr); + } + + spawn_shell(user); + break; + } + + stdout.write_all(b"\x1B[1mpassword:\x1B[0m ").try(&mut stderr); + stdout.flush().try(&mut stderr); + if let Some(password) = stdin.read_passwd(&mut stdout).try(&mut stderr) { + stdout.write(b"\n").try(&mut stderr); + stdout.flush().try(&mut stderr); + + if user.verify_passwd(&password) { + if let Ok(mut motd) = File::open(MOTD_FILE) { + io::copy(&mut motd, &mut stdout).try(&mut stderr); + stdout.flush().try(&mut stderr); + } + + spawn_shell(user); + break; + } + } } - - let mut command = Command::new(passwd.shell); - - command.uid(passwd.uid); - command.gid(passwd.gid); - - command.current_dir(passwd.home); - - command.env("USER", &user); - command.env("UID", format!("{}", passwd.uid)); - command.env("GROUPS", format!("{}", passwd.gid)); - command.env("HOME", passwd.home); - command.env("SHELL", passwd.shell); - - match command.spawn() { - Ok(mut child) => match child.wait() { - Ok(_status) => (), - Err(err) => panic!("login: failed to wait for '{}': {}", passwd.shell, err) - }, - Err(err) => panic!("login: failed to execute '{}': {}", passwd.shell, err) - } - - break; - } else { - stdout.write(b"\nLogin failed\n").try(&mut stderr); - stdout.flush().try(&mut stderr);; } } else { stdout.write(b"\n").try(&mut stderr); @@ -140,3 +114,4 @@ pub fn main() { } } } + diff --git a/src/bin/passwd.rs b/src/bin/passwd.rs index 7fcdbeef0b..c644811be6 100644 --- a/src/bin/passwd.rs +++ b/src/bin/passwd.rs @@ -4,19 +4,17 @@ extern crate arg_parser; extern crate extra; extern crate rand; extern crate termion; -extern crate userutils; +extern crate redox_users; use rand::{Rng, OsRng}; use std::{env, io}; -use std::fs::File; -use std::io::{Read, Write}; +use std::io::Write; use std::process::exit; use arg_parser::ArgParser; -use termion::input::TermRead; -use userutils::Passwd; use extra::option::OptionalExt; -use extra::io::fail; +use termion::input::TermRead; +use redox_users::{User, get_uid, get_user_by_id, get_user_by_name}; const MAN_PAGE: &'static str = /* @MANSTART{passwd} */ r#" NAME @@ -38,11 +36,9 @@ OPTIONS Display this help and exit. AUTHOR - Written by Jeremy Soller. + Written by Jeremy Soller, Jose Narvaez. "#; /* @MANEND */ -const PASSWD_FILE: &'static str = "/etc/passwd"; - fn main() { let stdin = io::stdin(); let mut stdin = stdin.lock(); @@ -61,46 +57,31 @@ fn main() { exit(0); } - let uid = userutils::get_uid(&mut stderr) as u32; - - let mut passwd_string = String::new(); - let mut passwd_file = File::open(PASSWD_FILE).try(&mut stderr); - passwd_file.read_to_string(&mut passwd_string).try(&mut stderr); - let passwd_file_entries = match Passwd::parse_file(&passwd_string) { - Ok(entries) => entries, - Err(_) => fail("passwd: error parsing passwd file", &mut stderr) - }; - - let passwd = if parser.args.is_empty() { - let passwd_option = passwd_file_entries.iter() - .find(|passwd| passwd.uid == uid); - - if let Some(passwd) = passwd_option { - passwd - } else { - fail(&format!("passwd: current user id {} does not exist", uid), &mut stderr); - } + let uid = get_uid(); + + let user = if parser.args.is_empty() { + get_user_by_id(uid).unwrap_or_else(|| { + eprintln!("passwd: current user id {} does not exist", uid); + exit(1); + }) } else { - let user = &parser.args[0]; - let passwd_option = passwd_file_entries.iter() - .find(|passwd| passwd.user == user); - - if let Some(passwd) = passwd_option { - passwd - } else { - fail(&format!("passwd: user '{}' does not exist", user), &mut stderr); - } + let username = &parser.args[0]; + get_user_by_name(username).unwrap_or_else(|| { + eprintln!("passwd: user '{}' does not exist", username); + exit(1); + }) }; - if passwd.uid == uid || uid == 0 { - let msg = format!("changing password for '{}' \n", passwd.user); + let uid = uid as u32; + if user.uid == uid || uid == 0 { + let msg = format!("changing password for '{}' \n", user.user); stdout.write_all(&msg.as_bytes()).try(&mut stderr); stdout.flush().try(&mut stderr); let mut verified = false; - if passwd.hash == "" { + if user.hash == "" { verified = true; - } else if passwd.uid == uid || uid != 0 { + } else if user.uid == uid || uid != 0 { stdout.write_all(b"current password: ").try(&mut stderr); stdout.flush().try(&mut stderr); @@ -108,7 +89,7 @@ fn main() { stdout.write(b"\n").try(&mut stderr); stdout.flush().try(&mut stderr); - if passwd.verify(&password) { + if user.verify_passwd(&password) { verified = true; } } @@ -130,25 +111,29 @@ fn main() { if new_password == confirm_password { let salt = format!("{:X}", OsRng::new().try(&mut stderr).next_u64()); - let encoded_passwd = userutils::Passwd::encode(&new_password, &salt); - //TODO: Actually persist the new password to PASSWD_FILE + let encoded_passwd = User::encode_passwd(&new_password, &salt); + // TODO: Actually persist the new password to PASSWD_FILE let msg = format!("{}\n", encoded_passwd); stdout.write_all(&msg.as_bytes()).try(&mut stderr); stdout.flush().try(&mut stderr); } else { - fail("passwd: new password does not match confirm password", &mut stderr); + eprintln!("passwd: new password does not match confirm password"); + exit(1); } } else { - fail("passwd: no confirm password provided", &mut stderr); + eprintln!("passwd: no confirm password provided"); + exit(1); } } else { - fail("passwd: no new password provided", &mut stderr); + eprintln!("passwd: no new password provided"); + exit(1); } } else { - fail("passwd: incorrect current password", &mut stderr); + eprintln!("passwd: incorrect current password"); + exit(1); } } else { - let msg = &format!("passwd: you do not have permission to set the password of '{}'", passwd.user); - fail(msg, &mut stderr); + eprintln!("passwd: you do not have permission to set the password of '{}'", user.user); + exit(1); } -} +} \ No newline at end of file diff --git a/src/bin/su.rs b/src/bin/su.rs index 2bd610af33..d32cb45fbc 100644 --- a/src/bin/su.rs +++ b/src/bin/su.rs @@ -3,20 +3,19 @@ extern crate arg_parser; extern crate extra; extern crate termion; +extern crate redox_users; extern crate userutils; use std::env; -use std::fs::File; -use std::io::{self, Read, Write}; -use std::os::unix::process::CommandExt; -use std::process::{exit, Command}; +use std::io::{self, Write}; +use std::process::exit; use std::str; use arg_parser::ArgParser; -use extra::io::fail; use extra::option::OptionalExt; use termion::input::TermRead; -use userutils::Passwd; +use redox_users::{get_uid, get_user_by_name}; +use userutils::spawn_shell; const MAN_PAGE: &'static str = /* @MANSTART{su} */ r#" NAME @@ -37,11 +36,9 @@ OPTIONS Display this help and exit. AUTHOR - Written by Jeremy Soller. + Written by Jeremy Soller, Jose Narvaez. "#; /* @MANEND */ -const PASSWD_FILE: &'static str = "/etc/passwd"; - pub fn main() { let stdin = io::stdin(); let mut stdin = stdin.lock(); @@ -60,64 +57,34 @@ pub fn main() { exit(0); } - let user = if parser.args.is_empty() { + let target_user = if parser.args.is_empty() { String::from("root") } else { parser.args[0].to_string() }; - let uid = userutils::get_uid(&mut stderr); + let uid = get_uid(); + let user = get_user_by_name(&target_user).unwrap_or_else(|| { + eprintln!("su: user {} not found", target_user); + exit(1); + }); - let mut passwd_string = String::new(); - let mut passwd_file = match File::open(PASSWD_FILE) { - Ok(file) => file, - Err(err) => fail(&format!("su: there was an error opening the passwd file: {}", err), &mut stderr) - }; - - passwd_file.read_to_string(&mut passwd_string).try(&mut stderr); - - let passwd_file_entries = match Passwd::parse_file(&passwd_string) { - Ok(entries) => entries, - Err(_) => fail(&format!("su: there was an error parsing the passwd file."), &mut stderr) - }; - - let mut passwd_option = passwd_file_entries.iter() - .find(|passwd| { user == passwd.user && ("" == passwd.hash || uid == 0) }); - - if passwd_option.is_none() { + if uid > 0 || user.hash != "" { stdout.write_all(b"password: ").try(&mut stderr); stdout.flush().try(&mut stderr); if let Some(password) = stdin.read_passwd(&mut stdout).try(&mut stderr) { - stdout.write(b"\n").try(&mut stderr); - stdout.flush().try(&mut stderr); - passwd_option = passwd_file_entries.iter() - .find(|passwd| user == passwd.user && passwd.verify(&password)); + if user.verify_passwd(&password) { + spawn_shell(user); + exit(0); + } else { + stdout.write(b"su: authentication failed\n").try(&mut stderr); + stdout.flush().try(&mut stderr); + exit(1); + } } } - if let Some(passwd) = passwd_option { - let mut command = Command::new(passwd.shell); - - command.uid(passwd.uid); - command.gid(passwd.gid); - - command.env("USER", &user); - command.env("UID", format!("{}", passwd.uid)); - command.env("GROUPS", format!("{}", passwd.gid)); - command.env("HOME", passwd.home); - command.env("SHELL", passwd.shell); - - match command.spawn() { - Ok(mut child) => match child.wait() { - Ok(_status) => (), - Err(err) => fail(&format!("su: failed to wait for '{}': {}", passwd.shell, err), &mut stderr) - }, - Err(err) => fail(&format!("su: failed to execute '{}': {}", passwd.shell, err), &mut stderr) - } - } else { - stdout.write(b"su: authentication failed\n").try(&mut stderr); - stdout.flush().try(&mut stderr); - } + spawn_shell(user); } diff --git a/src/bin/sudo.rs b/src/bin/sudo.rs index e27fce2ae2..0801a1965f 100644 --- a/src/bin/sudo.rs +++ b/src/bin/sudo.rs @@ -1,18 +1,20 @@ +#![deny(warnings)] + extern crate arg_parser; extern crate syscall; extern crate termion; -extern crate userutils; +extern crate redox_users; use std::env; -use std::fs::File; -use std::io::{self, Read, Write}; +use std::io::{self, Write}; use std::os::unix::process::CommandExt; -use std::process::{self, Command}; +use std::process::{Command, exit}; use arg_parser::ArgParser; use termion::input::TermRead; -use userutils::{Passwd, Group}; +use redox_users::{get_uid, get_user_by_id, get_group_by_name}; +const MAX_ATTEMPTS: u16 = 3; const MAN_PAGE: &'static str = /* @MANSTART{sudo} */ r#" NAME sudo - execute a command as another user @@ -37,7 +39,7 @@ EXIT STATUS the exit status will be >0. AUTHOR - Written by Jeremy Soller. + Written by Jeremy Soller, Jose Narvaez. "#; /* @MANEND */ pub fn main() { @@ -45,8 +47,6 @@ pub fn main() { let mut stdin = stdin.lock(); let stdout = io::stdout(); let mut stdout = stdout.lock(); - let stderr = io::stderr(); - let mut stderr = stderr.lock(); let mut parser = ArgParser::new(1) .add_flag(&["h", "help"]); @@ -56,117 +56,98 @@ pub fn main() { if parser.found("help") { let _ = stdout.write_all(MAN_PAGE.as_bytes()); let _ = stdout.flush(); - process::exit(0); + exit(0); } let mut args = env::args().skip(1); - match args.next() { - None => { - writeln!(stderr, "sudo: no command provided").unwrap(); - process::exit(1); - }, - Some(cmd) => { - let uid = syscall::getuid().unwrap() as u32; + let cmd = args.next().unwrap_or_else(|| { + eprintln!("sudo: no command provided"); + exit(1); + }); - if uid != 0 { - let mut passwd_string = String::new(); - if let Ok(mut file) = File::open("/etc/passwd") { - let _ = file.read_to_string(&mut passwd_string); - } + let uid = get_uid(); + let user = get_user_by_id(uid).unwrap_or_else(|| { + eprintln!("sudo: user not found"); + exit(1); + }); - let mut passwd_option = None; - for line in passwd_string.lines() { - if let Ok(passwd) = Passwd::parse(line) { - if uid == passwd.uid { - passwd_option = Some(passwd); - break; - } - } - } + if uid != 0 { + let sudo_group = get_group_by_name("sudo").unwrap_or_else(|| { + eprintln!("sudo: sudo group not found"); + exit(1); + }); - match passwd_option { - None => { - writeln!(stderr, "sudo: user not found in passwd").unwrap(); - process::exit(1); - }, - Some(passwd) => { - let mut group_string = String::new(); - if let Ok(mut file) = File::open("/etc/group") { - let _ = file.read_to_string(&mut group_string); - } + if sudo_group.users.iter().any(|name| name == &user.user) { + if ! user.hash.is_empty() { + let max_attempts = MAX_ATTEMPTS; + let mut attempts = 0; - let mut group_option = None; - for line in group_string.lines() { - if let Ok(group) = Group::parse(line) { - if group.group == "sudo" && group.users.split(',').any(|name| name == passwd.user) { - group_option = Some(group); - break; - } - } - } - - if group_option.is_none() { - writeln!(stderr, "sudo: '{}' not in sudo group", passwd.user).unwrap(); - process::exit(1); - } - - if ! passwd.hash.is_empty() { - let max_attempts = 3; - let mut attempts = 0; - loop { - write!(stdout, "[sudo] password for {}: ", passwd.user).unwrap(); - let _ = stdout.flush(); - - match stdin.read_passwd(&mut stdout).unwrap() { - Some(password) => { - write!(stdout, "\n").unwrap(); - let _ = stdout.flush(); - - if passwd.verify(&password) { - break; - } else { - attempts += 1; - writeln!(stderr, "sudo: incorrect password ({}/{})", attempts, max_attempts).unwrap(); - if attempts >= max_attempts { - process::exit(1); - } - } - }, - None => { - write!(stdout, "\n").unwrap(); - process::exit(1); - } + loop { + print!("[sudo] password for {}: ", user.user); + let _ = stdout.flush(); + + match stdin.read_passwd(&mut stdout).unwrap() { + Some(password) => { + write!(stdout, "\n").unwrap(); + let _ = stdout.flush(); + + if user.verify_passwd(&password) { + break; + } else { + attempts += 1; + eprintln!("sudo: incorrect password ({}/{})", attempts, max_attempts); + if attempts >= max_attempts { + exit(1); } } + }, + None => { + write!(stdout, "\n").unwrap(); + exit(1); } } } + } else { + // FIXME: We should not be doing this as provides access to any + // user w/o auth to run stuff as root. We should be doing something like: + // eprintln!("sudo: '{}' is in sudo group but does not have a password set", user.user); + // exit(1); + run_command_as_root(&cmd, &args.collect::>()); + exit(0); } - - let mut command = Command::new(&cmd); - for arg in args { - command.arg(&arg); - } - - command.uid(0); - command.gid(0); - command.env("USER", "root"); - command.env("UID", "0"); - command.env("GROUPS", "0"); - - match command.spawn() { - Ok(mut child) => match child.wait() { - Ok(status) => process::exit(status.code().unwrap_or(0)), - Err(err) => { - writeln!(stderr, "sudo: failed to wait for {}: {}", cmd, err).unwrap(); - process::exit(1); - } - }, - Err(err) => { - writeln!(stderr, "sudo: failed to execute {}: {}", cmd, err).unwrap(); - process::exit(1); - } - } + } else { + eprintln!("sudo: '{}' not in sudo group", user.user); + exit(1); } } + + run_command_as_root(&cmd, &args.collect::>()); + exit(0); } + +fn run_command_as_root(cmd: &str, args: &Vec) { + let mut command = Command::new(&cmd); + for arg in args { + command.arg(&arg); + } + + command.uid(0); + command.gid(0); + command.env("USER", "root"); + command.env("UID", "0"); + command.env("GROUPS", "0"); + + match command.spawn() { + Ok(mut child) => match child.wait() { + Ok(status) => exit(status.code().unwrap_or(0)), + Err(err) => { + eprintln!("sudo: failed to wait for {}: {}", cmd, err); + exit(1); + } + }, + Err(err) => { + eprintln!("sudo: failed to execute {}: {}", cmd, err); + exit(1); + } + } +} \ No newline at end of file diff --git a/src/bin/whoami.rs b/src/bin/whoami.rs index d19a297747..1274d1da9e 100644 --- a/src/bin/whoami.rs +++ b/src/bin/whoami.rs @@ -2,24 +2,24 @@ extern crate arg_parser; extern crate extra; -extern crate userutils; +extern crate redox_users; use std::io::{self, Write}; use std::process::exit; use std::env; use arg_parser::ArgParser; use extra::option::OptionalExt; -use userutils::{get_euid, get_user}; +use redox_users::{get_euid, get_user_by_id}; const MAN_PAGE: &'static str = /* @MANSTART{whoami} */ r#" NAME - whoami - display effective user id + whoami - display effective user id SYNOPSIS - whoami [ -h | --help ] + whoami [ -h | --help ] DESCRIPTION - The whoami utility displays your effective user ID as a name. + The whoami utility displays your effective user ID as a name. OPTIONS -h @@ -27,20 +27,20 @@ OPTIONS Display this help and exit. EXIT STATUS - The whoami utility exits 0 on success, and >0 if an error occurs. + The whoami utility exits 0 on success, and >0 if an error occurs. AUTHOR Written by Jose Narvaez. "#; /* @MANEND */ fn main() { - let stdout = io::stdout(); - let mut stdout = stdout.lock(); - let mut stderr = io::stderr(); + let stdout = io::stdout(); + let mut stdout = stdout.lock(); + let mut stderr = io::stderr(); - let mut parser = ArgParser::new(1) + let mut parser = ArgParser::new(1) .add_flag(&["h", "help"]); - parser.parse(env::args()); + parser.parse(env::args()); if parser.found("help") { stdout.write_all(MAN_PAGE.as_bytes()).try(&mut stderr); @@ -48,18 +48,12 @@ fn main() { exit(0); } - let euid = get_euid(&mut stderr); - let user = match get_user(euid, &mut stderr) { - Some(user) => user, - None => { - let msg = format!("whoami: no user found for uid: {}", euid); - stdout.write_all(msg.as_bytes()).try(&mut stderr); - stdout.flush().try(&mut stderr); - exit(1); - } - }; + let euid = get_euid(); + let user = get_user_by_id(euid).unwrap_or_else(|| { + println!("whoami: no user found for uid: {}", euid); + exit(1); + }); - stdout.write_all(format!("{}\n", user).as_bytes()).try(&mut stderr); - stdout.flush().try(&mut stderr); + println!("{}", user.user); exit(0); } diff --git a/src/lib.rs b/src/lib.rs index 856eebe345..f7f8a98799 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,269 +1,74 @@ -extern crate argon2rs; -extern crate extra; -extern crate syscall; +//! Redox OS user and group utilities. +//! +//! The `userutils` crate is contains the utilities for dealing with users and groups in Redox OS. +//! They are heavily influenced by UNIX and are, when nedeed, tailored to specific Redox use cases. +//! +//! These implementations strive to be as simple as possible drawing particular +//! inspiration by BSD systems. They are indeed small, by choice. +//! +//! The included utilities are: +//! +//! - `getty`: Used by `init(8)` to open and initialize the TTY line, read a login name, +//! and invoke `login(1)`. +//! - `id`: Displays user identity. +//! - `login`: Allows users to into the system +//! - `passwd`: Allows users to modify their passwords. +//! - `su`: Allows users to sustitute identity. +//! - `sudo`: Enables users to execute a command as another user. +//! - `whoami`: Display effective user ID. -use std::io::{Read, Stderr, Write}; -use std::fs::File; -use std::process::exit; +extern crate redox_users; -use argon2rs::verifier::Encoded; -use argon2rs::{Argon2, Variant}; -use extra::option::OptionalExt; +use std::process::Command; +use std::os::unix::process::CommandExt; -const PASSWD_FILE: &'static str = "/etc/passwd"; -const GROUP_FILE: &'static str = "/etc/group"; +use redox_users::User; -/// A struct representing a UNIX /etc/passwd file entry -pub struct Passwd<'a> { - pub user: &'a str, - pub hash: &'a str, - pub uid: u32, - pub gid: u32, - pub name: &'a str, - pub home: &'a str, - pub shell: &'a str -} - -impl<'a> Passwd<'a> { - pub fn parse(line: &'a str) -> Result, ()> { - let mut parts = line.split(';'); - - let user = parts.next().ok_or(())?; - let hash = parts.next().ok_or(())?; - let uid = parts.next().ok_or(())?.parse::().or(Err(()))?; - let gid = parts.next().ok_or(())?.parse::().or(Err(()))?; - let name = parts.next().ok_or(())?; - let home = parts.next().ok_or(())?; - let shell = parts.next().ok_or(())?; - - Ok(Passwd { - user: user, - hash: hash, - uid: uid, - gid: gid, - name: name, - home: home, - shell: shell - }) - } - - pub fn parse_file(file_data: &'a str) -> Result>, ()> { - let mut entries: Vec> = Vec::new(); - - for line in file_data.lines() { - if let Ok(passwd) = Passwd::parse(line) { - entries.push(passwd); - } - } - - Ok(entries) - } - - pub fn encode(password: &str, salt: &str) -> String { - let a2 = Argon2::new(10, 1, 4096, Variant::Argon2i).unwrap(); - let e = Encoded::new(a2, password.as_bytes(), salt.as_bytes(), &[], &[]); - String::from_utf8(e.to_u8()).unwrap() - } - - pub fn verify(&self, password: &str) -> bool { - let e = Encoded::from_u8(self.hash.as_bytes()).unwrap(); - e.verify(password.as_bytes()) - } -} - -/// A struct representing a UNIX /etc/group file entry -pub struct Group<'a> { - pub group: &'a str, - pub gid: u32, - pub users: &'a str, -} - -impl<'a> Group<'a> { - pub fn parse(line: &'a str) -> Result, ()> { - let mut parts = line.split(';'); - - let group = parts.next().ok_or(())?; - let gid = parts.next().ok_or(())?.parse::().or(Err(()))?; - let users = parts.next().ok_or(())?; - - Ok(Group { - group: group, - gid: gid, - users: users - }) - } - - pub fn parse_file(file_data: &'a str) -> Result>, ()> { - let mut entries: Vec> = Vec::new(); - - for line in file_data.lines() { - if let Ok(group) = Group::parse(line) { - entries.push(group); - } - } - - Ok(entries) - } -} - -/// Gets the current process effective user id aborting the caller on error. +/// Spawns a shell for the given `User`. /// -/// This function issues the `geteuid` system call returning the process effective -/// user id. In case of an error it will log message to `stderr` and then abort -/// the caller process with an non-zero exit code. +/// The new the shell process will have set the users UID and GID, its CWD will be +/// set to the users's home directory and the follwing enviroment variables will +/// be populated like so: +/// +/// - `USER` set to the user's `user` field. +/// - `UID` set to the user's `uid` field. +/// - `GROUPS` set the user's `gid` field. +/// - `HOME` set to the user's `home` field. +/// - `SHELL` set to the user's `shell` field. /// /// # Examples /// -/// Basic usage: -/// /// ``` -/// let euid = get_euid(&mut stderr); +/// use redox_users::get_user_by_name; /// +/// let user = get_user_by_name("goyox86"); +/// spawn_shell(user); /// ``` -pub fn get_euid(stderr: &mut Stderr) -> usize { - match syscall::geteuid() { - Ok(euid) => euid, - Err(_) => { - let mut stderr = stderr.lock(); - let _ = stderr.write_all(b"failed to get effective UID\n"); - let _ = stderr.flush(); - exit(1) - } - } -} - -/// Gets the current process real user id aborting the caller on error. -/// -/// This function issues the `getuid` system call returning the process real -/// user id. In case of an error it will log message to `stderr` and then abort -/// the caller process with an non-zero exit code. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let uid = get_uid(&mut stderr); -/// -/// ``` -pub fn get_uid(stderr: &mut Stderr) -> usize { - match syscall::getuid() { - Ok(euid) => euid, - Err(_) => { - let mut stderr = stderr.lock(); - let _ = stderr.write_all(b"failed to get real UID\n"); - let _ = stderr.flush(); - exit(1) - } - } -} - -/// Gets the current process effective group id aborting the caller on error. -/// -/// This function issues the `getegid` system call returning the process effective -/// group id. In case of an error it will log message to `stderr` and then abort -/// the caller process with an non-zero exit code. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let egid = get_egid(&mut stderr); -/// -/// ``` -pub fn get_egid(stderr: &mut Stderr) -> usize { - match syscall::getegid() { - Ok(euid) => euid, - Err(_) => { - let mut stderr = stderr.lock(); - let _ = stderr.write_all(b"failed to get effective GID\n"); - let _ = stderr.flush(); - exit(1) - } - } -} - -/// Gets the current process real group id aborting the caller on error. -/// -/// This function issues the `getegid` system call returning the process real -/// group id. In case of an error it will log message to `stderr` and then abort -/// the caller process with an non-zero exit code. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let gid = get_gid(&mut stderr); -/// -/// ``` -pub fn get_gid(stderr: &mut Stderr) -> usize { - match syscall::getgid() { - Ok(euid) => euid, - Err(_) => { - let mut stderr = stderr.lock(); - let _ = stderr.write_all(b"failed to get real GID\n"); - let _ = stderr.flush(); - exit(1) - } - } -} - -/// Gets the user name for a given user id. -/// -/// This function will read `/etc/passwd` looking for an entry for the provided -/// user ID, returning its UNIX username. In case of an error it will log message -/// to `stderr` and then will the caller process with an non-zero exit code. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let user = get_user(1, &mut stderr); -/// -/// ``` -pub fn get_user(uid: usize, stderr: &mut Stderr) -> Option { - let mut passwd_string = String::new(); - let mut file = File::open(PASSWD_FILE).try(stderr); - file.read_to_string(&mut passwd_string).try(stderr); - - let passwd_file_entries = Passwd::parse_file(&passwd_string).unwrap(); - let passwd = passwd_file_entries.iter() - .find(|passwd| passwd.uid as usize == uid); - - match passwd { - Some(passwd) => Some(String::from(passwd.user)), - None => None - } -} - -/// Gets the UNIX group name for a given group ID. -/// -/// This function will read `/etc/group` file looking for an entry for the provided -/// group ID, returning its UNIX group name. In case of an error it will log message -/// to `stderr` and then will the caller process with an non-zero exit code. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let group = get_group(1, &mut stderr); -/// -/// ``` -pub fn get_group(gid: usize, stderr: &mut Stderr) -> Option { - let mut group_string = String::new(); - let mut file = File::open(GROUP_FILE).try(stderr); - file.read_to_string(&mut group_string).try(stderr); - - let group_file_entries = Group::parse_file(&group_string).unwrap(); - let group = group_file_entries.iter() - .find(|group| group.gid as usize == gid); - - match group { - Some(group) => Some(String::from(group.group)), - None => None +/// +/// # Panics +/// +/// This function can panic under two scenarios. The first, when an error occurs while +/// spawning the new process containig the shell and the second, when after a succesful +/// spawn, an error happens while trying to wait for the newly created process. +pub fn spawn_shell(user: User) { + let mut command = Command::new(&user.shell); + + command.uid(user.uid); + command.gid(user.gid); + + command.current_dir(&user.home); + + command.env("USER", &user.user); + command.env("UID", format!("{}", user.uid)); + command.env("GROUPS", format!("{}", user.gid)); + command.env("HOME", &user.home); + command.env("SHELL", &user.shell); + + match command.spawn() { + Ok(mut child) => match child.wait() { + Ok(_status) => (), + Err(err) => panic!("userutils: failed to wait for '{}': {}", user.shell, err) + }, + Err(err) => panic!("userutils: failed to execute '{}': {}", user.shell, err) } }