From 40394dddad06c19bab9925cf48e0f846fccc032a Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 28 Sep 2016 16:01:14 -0600 Subject: [PATCH] Validate user login against /etc/passwd --- filesystem/etc/passwd | 1 + initfs/etc/init.rc | 2 +- programs/login/src/main.rs | 136 +++++++++++++++++++++++++++---------- 3 files changed, 101 insertions(+), 38 deletions(-) create mode 100644 filesystem/etc/passwd diff --git a/filesystem/etc/passwd b/filesystem/etc/passwd new file mode 100644 index 0000000..7090814 --- /dev/null +++ b/filesystem/etc/passwd @@ -0,0 +1 @@ +root;A69F73CCA23A9AC5C8B567DC185A756E97C982164FE25859E0D1DCC1475C80A615B2123AF1F5F94C11E3E9402C3AC558F50199D95B6D3E31758586281DCD26;0;0;root;file:home;file:bin/ion diff --git a/initfs/etc/init.rc b/initfs/etc/init.rc index a24336f..22ae8e4 100644 --- a/initfs/etc/init.rc +++ b/initfs/etc/init.rc @@ -2,4 +2,4 @@ initfs:bin/pcid initfs:etc/pcid.toml initfs:bin/redoxfs disk:0 file:bin/vesad file:bin/ps2d -file:bin/login display: file:bin/ion +file:bin/login display: diff --git a/programs/login/src/main.rs b/programs/login/src/main.rs index b38dd7d..b512961 100644 --- a/programs/login/src/main.rs +++ b/programs/login/src/main.rs @@ -1,20 +1,55 @@ +#![feature(question_mark)] + extern crate octavo; extern crate syscall; extern crate termion; use octavo::octavo_digest::Digest; use octavo::octavo_digest::sha3::Sha512; +use std::fs::File; use std::io::{Read, Write}; use std::process::Command; use std::{env, io, thread}; use termion::input::TermRead; +pub struct Passwd<'a> { + user: &'a str, + hash: &'a str, + uid: usize, + gid: usize, + name: &'a str, + home: &'a str, + 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 main() { let mut args = env::args().skip(1); let tty = args.next().expect("login: no tty provided"); - let sh = args.next().expect("login: no sh provided"); - let sh_args: Vec = args.collect(); let _ = syscall::close(2); let _ = syscall::close(1); @@ -24,6 +59,8 @@ pub fn main() { let _ = syscall::open(&tty, syscall::flag::O_RDWR); let _ = syscall::open(&tty, syscall::flag::O_RDWR); + env::set_current_dir("file:").unwrap(); + env::set_var("COLUMNS", "80"); env::set_var("LINES", "30"); env::set_var("TTY", &tty); @@ -35,51 +72,76 @@ pub fn main() { let mut stdout = stdout.lock(); loop { - stdout.write_all(b"\x1B[1mredox login:\x1B[0m ").expect("login: failed to write user prompt"); + stdout.write_all(b"\x1B[1mredox login:\x1B[0m ").unwrap(); let _ = stdout.flush(); - let user = (&mut stdin as &mut Read).read_line().expect("login: failed to read user").unwrap_or(String::new()); + let user = (&mut stdin as &mut Read).read_line().unwrap().unwrap_or(String::new()); if ! user.is_empty() { - stdout.write_all(b"\x1B[1mpassword:\x1B[0m ").expect("login: failed to write password prompt"); + stdout.write_all(b"\x1B[1mpassword:\x1B[0m ").unwrap(); let _ = stdout.flush(); - if let Some(password) = stdin.read_passwd(&mut stdout).expect("login: failed to read password") { - let mut output = vec![0; Sha512::output_bytes()]; - let mut hash = Sha512::default(); - hash.update(&password.as_bytes()); - hash.result(&mut output); + if let Some(password) = stdin.read_passwd(&mut stdout).unwrap() { + let password_hash = { + let mut output = vec![0; Sha512::output_bytes()]; + let mut hash = Sha512::default(); + hash.update(&password.as_bytes()); + hash.result(&mut output); + let mut encoded = String::new(); + for b in output.iter() { + encoded.push_str(&format!("{:X}", b)); + } + encoded + }; - println!(""); - - print!("hash: {}: '{}' ", user, password); - for b in output.iter() { - print!("{:X} ", b); - } - println!(""); - - let home = "file:home"; - - env::set_current_dir(home).expect("login: failed to cd to home"); - - let mut command = Command::new(&sh); - for arg in sh_args.iter() { - command.arg(arg); + { + let mut debug = File::open("debug:").unwrap(); + write!(debug, "hash: {}: '{}': {}\n", user, password, password_hash); } - command.env("USER", &user); - command.env("HOME", home); - command.env("PATH", "file:bin"); - - match command.spawn() { - Ok(mut child) => match child.wait() { - Ok(_status) => (), //println!("login: waited for {}: {:?}", sh, status.code()), - Err(err) => panic!("login: failed to wait for '{}': {}", sh, err) - }, - Err(err) => panic!("login: failed to execute '{}': {}", sh, err) + let mut passwd_string = String::new(); + { + let mut passwd_file = File::open("file:etc/passwd").unwrap(); + passwd_file.read_to_string(&mut passwd_string).unwrap(); } - stdout.write(b"\x1Bc").expect("login: failed to reset screen"); - let _ = stdout.flush(); + let mut passwd_option = None; + for line in passwd_string.lines() { + if let Ok(passwd) = Passwd::parse(line) { + if password_hash == passwd.hash { + passwd_option = Some(passwd); + break; + } + } + } + + if let Some(passwd) = passwd_option { + stdout.write(b"\n").unwrap(); + let _ = stdout.flush(); + + let mut command = Command::new(passwd.shell); + + env::set_current_dir(passwd.home).unwrap(); + + command.env("USER", &user); + command.env("HOME", passwd.home); + command.env("PATH", "file:bin"); + + match command.spawn() { + Ok(mut child) => match child.wait() { + Ok(_status) => (), //println!("login: waited for {}: {:?}", sh, status.code()), + Err(err) => panic!("login: failed to wait for '{}': {}", passwd.shell, err) + }, + Err(err) => panic!("login: failed to execute '{}': {}", passwd.shell, err) + } + + env::set_current_dir("file:").unwrap(); + + stdout.write(b"\x1Bc").unwrap(); + let _ = stdout.flush(); + } else { + stdout.write(b"\nLogin failed\n").unwrap(); + let _ = stdout.flush(); + } } } }