Migrated to redox_users and some refactoring and docs.
Details - Updated dependencies with `redox_users`. - Migrated all user/group dependent functionality to calls to `redox_users`. - Added top level documentation to the crate. - Added a `spawn_shell` function to reuse a bit of code. - `login`: Handled the case of an invalid login with "Login incorrect" (as in BSD). - `passwd`: Refactored a bit and used `eprintln!` where possible. - `su`: Refactored a bit and used `eprintln!` where possible. - `sudo`: Refactored a bit to simplify logic also used `eprintln!` where possible. - `whoami`: Refactored a bit and used `eprintln!/println!` where possible. - Also added myself on the AUTHORS section :).
This commit is contained in:
Generated
+33
-21
@@ -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)" = "<none>"
|
||||
"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)" = "<none>"
|
||||
"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)" = "<none>"
|
||||
"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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
+21
-21
@@ -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);
|
||||
}
|
||||
|
||||
+40
-65
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-51
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
-55
@@ -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);
|
||||
}
|
||||
|
||||
+87
-106
@@ -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::<Vec<String>>());
|
||||
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::<Vec<String>>());
|
||||
exit(0);
|
||||
}
|
||||
|
||||
fn run_command_as_root(cmd: &str, args: &Vec<String>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-23
@@ -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);
|
||||
}
|
||||
|
||||
+61
-256
@@ -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<Passwd<'a>, ()> {
|
||||
let mut parts = line.split(';');
|
||||
|
||||
let user = parts.next().ok_or(())?;
|
||||
let hash = parts.next().ok_or(())?;
|
||||
let uid = parts.next().ok_or(())?.parse::<u32>().or(Err(()))?;
|
||||
let gid = parts.next().ok_or(())?.parse::<u32>().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<Vec<Passwd<'a>>, ()> {
|
||||
let mut entries: Vec<Passwd<'a>> = 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<Group<'a>, ()> {
|
||||
let mut parts = line.split(';');
|
||||
|
||||
let group = parts.next().ok_or(())?;
|
||||
let gid = parts.next().ok_or(())?.parse::<u32>().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<Vec<Group<'a>>, ()> {
|
||||
let mut entries: Vec<Group<'a>> = 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<String> {
|
||||
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<String> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user