feature: better login program
This commit is contained in:
parent
bd3d4e964d
commit
b97db3a0c4
4
Makefile
4
Makefile
@ -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
2
etc/initrd/passwd
Normal file
@ -0,0 +1,2 @@
|
||||
root:0:0:root:/:/bin/shell
|
||||
alnyan:1000:1000:alnyan:/:/bin/shell
|
2
etc/initrd/shadow
Normal file
2
etc/initrd/shadow
Normal file
@ -0,0 +1,2 @@
|
||||
root:toor
|
||||
alnyan:
|
@ -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")]
|
||||
|
12
libusr/src/env.rs → libusr/src/env/mod.rs
vendored
12
libusr/src/env.rs → libusr/src/env/mod.rs
vendored
@ -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
99
libusr/src/env/passwd.rs
vendored
Normal 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
67
libusr/src/env/shadow.rs
vendored
Normal 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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user