Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a22b3d5
Showing
3 changed files
with
339 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
/target/ | ||
**/*.rs.bk | ||
Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "redox_users" | ||
version = "0.1.0" | ||
authors = ["Jose Narvaez <goyox86@gmail.com>"] | ||
description = "A Rust library to access Redox users and groups functionality" | ||
license = "MIT" | ||
repository = "https://github.com/redox-os/users" | ||
documentation = "https://docs.rs/redox_users" | ||
|
||
[dependencies] | ||
argon2rs = { version = "0.2", default-features = false } | ||
extra = { git = "https://github.com/redox-os/libextra.git" } | ||
redox_syscall = "0.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,322 @@ | ||
extern crate argon2rs; | ||
extern crate extra; | ||
extern crate syscall; | ||
|
||
use std::io::{self, Read}; | ||
use std::fs::File; | ||
use std::process::exit; | ||
|
||
use argon2rs::verifier::Encoded; | ||
use argon2rs::{Argon2, Variant}; | ||
use extra::option::OptionalExt; | ||
|
||
const PASSWD_FILE: &'static str = "/etc/passwd"; | ||
const GROUP_FILE: &'static str = "/etc/group"; | ||
|
||
/// A struct representing a Redox user | ||
/// Currently maps to an entry in the '/etc/passwd' file | ||
#[derive(Clone)] | ||
pub struct User { | ||
pub user: String, // UNIX username | ||
pub hash: String, // Hashed password | ||
pub uid: u32, // User id | ||
pub gid: u32, // Group id | ||
pub name: String, // Real username | ||
pub home: String, // Home directory path | ||
pub shell: String // Shell path | ||
} | ||
|
||
impl User { | ||
pub fn parse(line: &str) -> Result<User, ()> { | ||
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(User { | ||
user: user.into(), | ||
hash: hash.into(), | ||
uid: uid, | ||
gid: gid, | ||
name: name.into(), | ||
home: home.into(), | ||
shell: shell.into() | ||
}) | ||
} | ||
|
||
pub fn parse_file(file_data: &str) -> Result<Vec<User>, ()> { | ||
let mut entries: Vec<User> = Vec::new(); | ||
|
||
for line in file_data.lines() { | ||
if let Ok(user) = User::parse(line) { | ||
entries.push(user); | ||
} | ||
} | ||
|
||
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 Redox users group | ||
/// Currently maps to an '/etc/group' file entry | ||
#[derive(Clone)] | ||
pub struct Group { | ||
pub group: String, // UNIX group name | ||
pub gid: u32, // UNIX unique group id | ||
pub users: String, // Comma separated list of group members | ||
} | ||
|
||
impl Group { | ||
pub fn parse(line: &str) -> Result<Group, ()> { | ||
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.into(), | ||
gid: gid, | ||
users: users.into() | ||
}) | ||
} | ||
|
||
pub fn parse_file(file_data: &str) -> Result<Vec<Group>, ()> { | ||
let mut entries: Vec<Group> = 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. | ||
/// | ||
/// 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. | ||
/// | ||
/// # Examples | ||
/// | ||
/// Basic usage: | ||
/// | ||
/// ``` | ||
/// let euid = get_euid(&mut stderr); | ||
/// | ||
/// ``` | ||
pub fn get_euid() -> usize { | ||
match syscall::geteuid() { | ||
Ok(euid) => euid, | ||
Err(_) => { | ||
eprintln!("failed to get effective UID"); | ||
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() -> usize { | ||
match syscall::getuid() { | ||
Ok(euid) => euid, | ||
Err(_) => { | ||
eprintln!("failed to get real UID"); | ||
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() -> usize { | ||
match syscall::getegid() { | ||
Ok(euid) => euid, | ||
Err(_) => { | ||
eprintln!("failed to get effective GID"); | ||
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() -> usize { | ||
match syscall::getgid() { | ||
Ok(euid) => euid, | ||
Err(_) => { | ||
eprintln!("failed to get real GID"); | ||
exit(1) | ||
} | ||
} | ||
} | ||
|
||
/// Gets the UNIX User file entry representing a user for a given user id. | ||
/// | ||
/// This function will read '/etc/passwd' looking for an entry for the provided user ID, | ||
/// returning a `User` struct representing that user if found and `None` otherwise. | ||
/// 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_by_id(1, &mut stderr).unwrap(); | ||
/// | ||
/// ``` | ||
pub fn get_user_by_uid(uid: usize) -> Option<User> { | ||
let mut stderr = io::stderr(); | ||
|
||
let mut user_string = String::new(); | ||
let mut file = File::open(PASSWD_FILE).try(&mut stderr); | ||
file.read_to_string(&mut user_string).try(&mut stderr); | ||
|
||
let passwd_file_entries = User::parse_file(&user_string).unwrap(); | ||
|
||
passwd_file_entries.iter() | ||
.find(|user| user.uid as usize == uid) | ||
.cloned() | ||
} | ||
|
||
/// Gets the UNIX User file entry representing a user for a given user id. | ||
/// | ||
/// This function will read '/etc/passwd' looking for an entry for the provided user ID, | ||
/// returning a `User` struct representing that user if found and `None` otherwise. | ||
/// 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_by_id(1, &mut stderr).unwrap(); | ||
/// | ||
/// ``` | ||
pub fn get_user_by_name<T: AsRef<str>>(username: T) -> Option<User> { | ||
let mut stderr = io::stderr(); | ||
|
||
let mut user_string = String::new(); | ||
let mut file = File::open(PASSWD_FILE).try(&mut stderr); | ||
file.read_to_string(&mut user_string).try(&mut stderr); | ||
|
||
let passwd_file_entries = User::parse_file(&user_string).unwrap(); | ||
|
||
passwd_file_entries.iter() | ||
.find(|user| user.user == username.as_ref()) | ||
.cloned() | ||
} | ||
|
||
|
||
/// Gets the group for a given group id. | ||
/// | ||
/// This function will read `/etc/group` file looking for an entry for the provided | ||
/// returning a `Group` struct representing the group if found and `None` otherwise. | ||
/// 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_by_id(1, &mut stderr).unwrap(); | ||
/// | ||
/// ``` | ||
pub fn get_group_by_gid(gid: usize) -> Option<Group> { | ||
let mut stderr = io::stderr(); | ||
|
||
let mut group_string = String::new(); | ||
let mut file = File::open(GROUP_FILE).try(&mut stderr); | ||
file.read_to_string(&mut group_string).try(&mut stderr); | ||
|
||
let group_file_entries = Group::parse_file(&group_string).unwrap(); | ||
group_file_entries.iter() | ||
.find(|group| group.gid as usize == gid) | ||
.cloned() | ||
} | ||
|
||
/// Gets the group for a given group name. | ||
/// | ||
/// This function will read `/etc/group` file looking for an entry for the provided | ||
/// returning a `Group` struct representing the group if found and `None` otherwise. | ||
/// 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_by_id(1, &mut stderr).unwrap(); | ||
/// | ||
/// ``` | ||
pub fn get_group_by_name<T: AsRef<str>>(groupname: T) -> Option<Group> { | ||
let mut stderr = io::stderr(); | ||
|
||
let mut group_string = String::new(); | ||
let mut file = File::open(GROUP_FILE).try(&mut stderr); | ||
file.read_to_string(&mut group_string).try(&mut stderr); | ||
|
||
let group_file_entries = Group::parse_file(&group_string).unwrap(); | ||
group_file_entries.iter() | ||
.find(|group| group.group == groupname.as_ref()) | ||
.cloned() | ||
} |