feature: better login program

This commit is contained in:
Mark Poliakov 2021-12-02 13:02:45 +02:00
parent bd3d4e964d
commit b97db3a0c4
9 changed files with 283 additions and 13 deletions

View File

@ -92,7 +92,9 @@ initrd:
--target=../etc/$(ARCH)-osdev5.json \
-Z build-std=core,alloc,compiler_builtins \
$(CARGO_COMMON_OPTS)
mkdir -p $(O)/rootfs/bin $(O)/rootfs/sbin $(O)/rootfs/dev
mkdir -p $(O)/rootfs/bin $(O)/rootfs/sbin $(O)/rootfs/dev $(O)/rootfs/etc
cp etc/initrd/passwd $(O)/rootfs/etc
cp etc/initrd/shadow $(O)/rootfs/etc
touch $(O)/rootfs/dev/.do_no_remove
cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)/rootfs/init
cp target/$(ARCH)-osdev5/$(PROFILE)/shell $(O)/rootfs/bin

2
etc/initrd/passwd Normal file
View File

@ -0,0 +1,2 @@
root:0:0:root:/:/bin/shell
alnyan:1000:1000:alnyan:/:/bin/shell

2
etc/initrd/shadow Normal file
View File

@ -0,0 +1,2 @@
root:toor
alnyan:

View File

@ -24,6 +24,55 @@ pub struct ProgramArgs {
pub size: usize
}
// TODO utils
use core::fmt;
#[derive(Clone, Copy)]
pub struct FixedStr<const N: usize> {
len: usize,
data: [u8; N],
}
impl<const N: usize> FixedStr<N> {
pub const fn empty() -> Self {
Self {
len: 0,
data: [0; N]
}
}
pub fn copy_from_str(&mut self, src: &str) {
if src.len() > self.data.len() {
panic!("copy_from_str: src len > data len");
}
self.len = src.len();
self.data[..self.len].copy_from_slice(src.as_bytes());
}
pub fn as_str(&self) -> &str {
unsafe {
core::str::from_utf8_unchecked(&self.data[..self.len])
}
}
}
impl<const N: usize> fmt::Debug for FixedStr<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"")?;
fmt::Display::fmt(self, f)?;
write!(f, "\"")
}
}
impl<const N: usize> fmt::Display for FixedStr<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &byte in &self.data[..self.len] {
write!(f, "{}", byte as char)?;
}
Ok(())
}
}
#[cfg(feature = "user")]
pub mod calls;
#[cfg(feature = "user")]

View File

@ -1,6 +1,14 @@
use libsys::{debug::TraceLevel, ProgramArgs};
use alloc::vec::Vec;
use crate::trace;
use alloc::vec::Vec;
use libsys::{
debug::TraceLevel,
ProgramArgs,
};
mod passwd;
pub use passwd::UserInfo;
mod shadow;
pub use shadow::UserShadow;
static mut PROGRAM_ARGS: Vec<&'static str> = Vec::new();

99
libusr/src/env/passwd.rs vendored Normal file
View File

@ -0,0 +1,99 @@
use crate::io::{Read, read_line};
use core::str::FromStr;
use core::fmt;
use crate::trace_debug;
use crate::file::File;
use libsys::{FixedStr, stat::{UserId, GroupId}};
#[derive(Debug, Clone, Copy)]
pub struct UserInfo {
name: FixedStr<32>,
uid: UserId,
gid: GroupId,
home: FixedStr<64>,
shell: FixedStr<64>,
}
impl UserInfo {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn home(&self) -> &str {
self.home.as_str()
}
pub fn shell(&self) -> &str {
self.shell.as_str()
}
pub fn uid(&self) -> UserId {
self.uid
}
pub fn gid(&self) -> GroupId {
self.gid
}
pub fn find<F: Fn(&Self) -> bool>(pred: F) -> Result<Self, ()> {
let mut file = File::open("/etc/passwd").map_err(|_| ())?;
let mut buf = [0; 128];
loop {
let line = read_line(&mut file, &mut buf).map_err(|_| ())?;
if let Some(line) = line {
let ent = UserInfo::from_str(line)?;
if pred(&ent) {
return Ok(ent);
}
} else {
break;
}
}
Err(())
}
pub fn by_name(name: &str) -> Result<Self, ()> {
Self::find(|ent| ent.name() == name)
}
}
impl FromStr for UserInfo {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
let mut iter = s.split(":");
let name = iter.next().ok_or(())?;
let uid = iter
.next()
.ok_or(())
.and_then(|e| u32::from_str(e).map_err(|_| ()))
.map(UserId::from)?;
let gid = iter
.next()
.ok_or(())
.and_then(|e| u32::from_str(e).map_err(|_| ()))
.map(GroupId::from)?;
let comment = iter.next().ok_or(())?;
let home = iter.next().ok_or(())?;
let shell = iter.next().ok_or(())?;
if iter.next().is_some() {
return Err(());
}
let mut res = Self {
uid,
gid,
name: FixedStr::empty(),
home: FixedStr::empty(),
shell: FixedStr::empty(),
};
res.name.copy_from_str(&name);
res.home.copy_from_str(&home);
res.shell.copy_from_str(&shell);
Ok(res)
}
}

67
libusr/src/env/shadow.rs vendored Normal file
View File

@ -0,0 +1,67 @@
use crate::file::File;
use crate::io::{Read, read_line};
use core::str::FromStr;
use libsys::FixedStr;
#[derive(Debug, Clone, Copy)]
pub struct UserShadow {
name: FixedStr<32>,
password: FixedStr<64>,
}
impl UserShadow {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn password(&self) -> &str {
self.password.as_str()
}
pub fn find<F: Fn(&Self) -> bool>(pred: F) -> Result<Self, ()> {
let mut file = File::open("/etc/shadow").map_err(|_| ())?;
let mut buf = [0; 128];
loop {
let line = read_line(&mut file, &mut buf).map_err(|_| ())?;
if let Some(line) = line {
let ent = UserShadow::from_str(line)?;
if pred(&ent) {
return Ok(ent);
}
} else {
break;
}
}
Err(())
}
pub fn by_name(name: &str) -> Result<Self, ()> {
Self::find(|ent| ent.name() == name)
}
}
impl FromStr for UserShadow {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
let mut iter = s.split(':');
let name = iter.next().ok_or(())?;
let password = iter.next().ok_or(())?;
if iter.next().is_some() {
return Err(());
}
let mut res = Self {
name: FixedStr::empty(),
password: FixedStr::empty(),
};
res.name.copy_from_str(name);
res.password.copy_from_str(password);
Ok(res)
}
}

View File

@ -42,3 +42,27 @@ pub fn stat(pathname: &str) -> Result<Stat, Error> {
sys_fstatat(None, pathname, &mut buf, 0).unwrap();
Ok(buf)
}
// TODO use BufRead instead once it's implemented
pub(crate) fn read_line<'a, F: Read>(f: &mut F, buf: &'a mut [u8]) -> Result<Option<&'a str>, ()> {
let mut pos = 0;
loop {
if pos == buf.len() {
return Err(());
}
let count = f.read(&mut buf[pos..=pos]).map_err(|_| ())?;
if count == 0 {
if pos == 0 {
return Ok(None);
}
break;
}
if buf[pos] == b'\n' {
break;
}
pos += 1;
}
core::str::from_utf8(&buf[..pos]).map_err(|_| ()).map(Some)
}

View File

@ -14,7 +14,8 @@ use libsys::{
stat::{FileDescriptor, FileMode, GroupId, OpenFlags, UserId},
termios::{Termios, TermiosLflag},
};
use libusr::{env, io};
use libusr::{env::{self, UserInfo, UserShadow}, io};
use core::str::FromStr;
struct HiddenInput {
fd: FileDescriptor,
@ -73,7 +74,7 @@ fn readline(fd: FileDescriptor, buf: &mut [u8]) -> Result<&str, Errno> {
}
}
fn login_as(uid: UserId, gid: GroupId, shell: &str) -> Result<(), Errno> {
fn login(uid: UserId, gid: GroupId, shell: &str) -> Result<(), Errno> {
if let Some(pid) = unsafe { sys_fork() }? {
let mut status = 0;
sys_waitpid(pid, &mut status).ok();
@ -90,6 +91,11 @@ fn login_as(uid: UserId, gid: GroupId, shell: &str) -> Result<(), Errno> {
}
}
fn login_as(name: &str) -> Result<(), Errno> {
let ent = UserInfo::by_name(name).map_err(|_| Errno::DoesNotExist)?;
login(ent.uid(), ent.gid(), ent.shell())
}
// TODO baud rate and misc port settings
#[no_mangle]
fn main() -> i32 {
@ -132,15 +138,26 @@ fn main() -> i32 {
loop {
print!("login: ");
let username = readline(FileDescriptor::STDIN, &mut user_buf).expect("Login read failed");
print!("password: ");
let password = {
let mut input = HiddenInput::open(FileDescriptor::STDIN).unwrap();
input.readline(&mut password_buf)
}
.expect("Password read failed");
if username == "root" && password == "toor" {
login_as(UserId::from(0), GroupId::from(0), "/bin/shell").unwrap();
let shadow = match UserShadow::by_name(username) {
Ok(e) => e,
Err(_) => continue
};
if !shadow.password().is_empty() {
print!("password: ");
let password = {
let mut input = HiddenInput::open(FileDescriptor::STDIN).unwrap();
input.readline(&mut password_buf)
}
.expect("Password read failed");
if password != shadow.password() {
eprintln!("Incorrect password");
continue;
}
}
login_as(username);
}
}