commit cd19cb7115328663d3807c607a12d9bfcb701042 Author: Dietrich Date: Mon Sep 21 09:05:38 2020 +0200 Parsing the passwd file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7fd59b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +.vscode +launch.json diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..d3b6965 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "adduser" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4882398 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "adduser" +version = "0.1.0" +authors = ["Dietrich "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/bin/main.rs b/src/bin/main.rs new file mode 100644 index 0000000..d37ed23 --- /dev/null +++ b/src/bin/main.rs @@ -0,0 +1,21 @@ +extern crate adduser; + +use adduser::passwd::Passwd; +use std::fs::File; +use std::io::{self, prelude::*, BufReader}; + +fn main() { + let mut file = File::open("/etc/passwd").unwrap(); + let reader = BufReader::new(file); + + for line in reader.lines() { + println!("{}", Passwd::new_from_string(&line.unwrap()).unwrap()); + } + + // let pwd = Passwd::default(); + // let pwd2 = + // Passwd::new_from_string("howdy:notencrypted:1001:1001:not done:/home/test:/bin/bash"); + // println!("Test struct: {}", pwd); + + // assert_eq!(pwd, pwd2.unwrap()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..08dd158 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod passwd; + +pub use passwd::{Password, Username}; diff --git a/src/passwd.rs b/src/passwd.rs new file mode 100644 index 0000000..8c2e457 --- /dev/null +++ b/src/passwd.rs @@ -0,0 +1,201 @@ +use std::cmp::Eq; +use std::fmt::{self, Display}; + +#[derive(Debug, PartialEq, Eq)] +pub struct Username<'a> { + pw_name: &'a str, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Password<'a> { + pw_passwd: &'a str, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Uid { + pw_uid: u32, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Gid { + pw_gid: u32, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Gecos<'a> { + Detail { + full_name: &'a str, + room: &'a str, + phone_work: &'a str, + phone_home: &'a str, + other: &'a str, + }, + Simple { + comment: &'a str, + }, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct HomeDir<'a> { + pw_dir: &'a str, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct ShellDir<'a> { + pw_shell: &'a str, +} + +/// A record in the user database `/etc/passwd`. +#[derive(Debug, PartialEq, Eq)] +pub struct Passwd<'a> { + pw_name: Username<'a>, /* Username. */ + pw_passwd: Password<'a>, /* Hashed passphrase, if shadow database not in use (see shadow.h). */ + pw_uid: Uid, /* User ID. */ + pw_gid: Gid, /* Group ID. */ + pw_gecos: Gecos<'a>, /* Real name. */ + pw_dir: HomeDir<'a>, /* Home directory. */ + pw_shell: ShellDir<'a>, /* Shell program. */ +} + +impl<'a> Passwd<'a> { + pub fn new_from_string(line: &'a str) -> Result { + let elements: Vec<&str> = line.split(":").collect(); + if elements.len() != 7 { + return Err("Failed to parse: not enough elements"); + } else { + Ok(Passwd { + pw_name: Username { + pw_name: elements.get(0).unwrap(), + }, + pw_passwd: Password { + pw_passwd: elements.get(1).unwrap(), + }, + pw_uid: Uid { + pw_uid: elements.get(2).unwrap().parse::().unwrap(), + }, + pw_gid: Gid { + pw_gid: elements.get(3).unwrap().parse::().unwrap(), + }, + pw_gecos: parse_gecos(elements.get(4).unwrap()).unwrap(), + pw_dir: HomeDir { + pw_dir: elements.get(5).unwrap(), + }, + pw_shell: ShellDir { + pw_shell: elements.get(6).unwrap(), + }, + }) + } + } +} + +fn parse_gecos(source: &str) -> Result { + let vals: Vec<&str> = source.split(',').collect(); + if vals.len() == 5 { + Ok(Gecos::Detail { + full_name: vals.get(0).unwrap(), + room: vals.get(1).unwrap(), + phone_work: vals.get(2).unwrap(), + phone_home: vals.get(3).unwrap(), + other: vals.get(4).unwrap(), + }) + } else if vals.len() == 1 { + Ok(Gecos::Simple { + comment: vals.get(0).unwrap(), + }) + } else { + panic!(format!("Could not parse this string: {}", source)) + } +} + +impl Default for Passwd<'_> { + fn default() -> Self { + Passwd { + pw_name: Username { pw_name: "howdy" }, + pw_passwd: Password { + pw_passwd: "notencrypted", + }, + pw_uid: Uid { pw_uid: 1001 }, + pw_gid: Gid { pw_gid: 1001 }, + pw_gecos: Gecos::Simple { + comment: "not done", + }, + pw_dir: HomeDir { + pw_dir: "/home/test", + }, + pw_shell: ShellDir { + pw_shell: "/bin/bash", + }, + } + } +} + +impl Display for Passwd<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}:{}:{}:{}:{}:{}:{}", + self.pw_name, + self.pw_passwd, + self.pw_uid, + self.pw_gid, + self.pw_gecos, + self.pw_dir, + self.pw_shell + ) + } +} + +impl Display for Username<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.pw_name,) + } +} + +impl Display for Password<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.pw_passwd,) + } +} + +impl Display for Uid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.pw_uid,) + } +} + +impl Display for Gid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.pw_gid,) + } +} + +impl Display for Gecos<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self { + Gecos::Simple { comment } => write!(f, "{}", comment), + Gecos::Detail { + full_name, + room, + phone_work, + phone_home, + other, + } => write!( + f, + "{},{},{},{},{}", + full_name, room, phone_work, phone_home, other + ), + } + } +} + +impl Display for HomeDir<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.pw_dir,) + } +} + +impl Display for ShellDir<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.pw_shell,) + } +}