From 545e9e7b2925b369a7c5125a812b6ebc26f59ff2 Mon Sep 17 00:00:00 2001 From: Agoston Szepessy <agoston.the.dev@gmail.com> Date: Tue, 23 Jul 2024 16:06:14 +0200 Subject: [PATCH] Implement getpass using File API Returns a Result for better error handling. --- src/header/unistd/getpass.rs | 81 ++++++++++++++++++++++++++++++++++++ src/header/unistd/mod.rs | 55 ++---------------------- 2 files changed, 85 insertions(+), 51 deletions(-) create mode 100644 src/header/unistd/getpass.rs diff --git a/src/header/unistd/getpass.rs b/src/header/unistd/getpass.rs new file mode 100644 index 00000000..24e7c14c --- /dev/null +++ b/src/header/unistd/getpass.rs @@ -0,0 +1,81 @@ +use core::ptr; + +use alloc::vec::Vec; + +use crate::{ + fs::File, + header::{ + fcntl::{O_CLOEXEC, O_RDWR}, + limits::PASS_MAX, + stdio, termios, + }, + io::{self, Read}, +}; + +use crate::platform::types::*; + +#[derive(Debug)] +enum Error { + Io(io::Error), + CannotConvertFd, +} + +impl From<io::Error> for Error { + fn from(value: io::Error) -> Self { + Error::Io(value) + } +} + +unsafe fn getpass_rs(prompt: *const c_char) -> Result<*mut c_char, Error> { + let mut f = File::open(c_str!("/dev/tty"), O_RDWR | O_CLOEXEC)?; + + let mut term = termios::termios::default(); + termios::tcgetattr(f.fd, &mut term as *mut termios::termios); + let old_term = term.clone(); + + term.c_iflag &= !(termios::IGNCR | termios::INLCR) as u32; + term.c_iflag |= termios::ICRNL as u32; + term.c_lflag &= !(termios::ECHO | termios::ISIG) as u32; + term.c_lflag |= termios::ICANON as u32; + + let cfile = stdio::fdopen(f.fd, c_str!("w+e").as_ptr()); + + if cfile.is_null() { + return Err(Error::CannotConvertFd); + } + + termios::tcsetattr(f.fd, termios::TCSAFLUSH, &term as *const termios::termios); + stdio::fputs(prompt, cfile); + stdio::fflush(cfile); + + let mut buff = Vec::new(); + let mut len = f.read(&mut buff)?; + + static mut PASSBUFF: [c_char; PASS_MAX] = [0; PASS_MAX]; + + for (dst, src) in PASSBUFF.iter_mut().zip(&buff) { + *dst = *src as c_char; + } + + if len > 0 { + if PASSBUFF[len - 1] == b'\n' as c_char || PASSBUFF.len() == len { + len -= 1; + } + } + + PASSBUFF[len] = 0; + + termios::tcsetattr( + f.fd, + termios::TCSAFLUSH, + &old_term as *const termios::termios, + ); + stdio::fputs(c_str!("\n").as_ptr(), cfile); + + Ok(PASSBUFF.as_mut_ptr()) +} + +#[no_mangle] +pub extern "C" fn getpass(prompt: *const c_char) -> *mut c_char { + unsafe { getpass_rs(prompt).unwrap_or(ptr::null_mut()) } +} diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs index 1bc169d9..da7977e9 100644 --- a/src/header/unistd/mod.rs +++ b/src/header/unistd/mod.rs @@ -11,9 +11,7 @@ use crate::{ c_str::CStr, header::{ crypt::{crypt_data, crypt_r}, - errno, fcntl, - limits::{self, PASS_MAX}, - stdio, + errno, fcntl, limits, stdlib::getenv, sys_ioctl, sys_resource, sys_time, sys_utsname, termios, time::timespec, @@ -21,14 +19,16 @@ use crate::{ platform::{self, types::*, Pal, Sys}, pthread::ResultExt, }; + use alloc::collections::LinkedList; -pub use self::{brk::*, getopt::*, pathconf::*, sysconf::*}; +pub use self::{brk::*, getopt::*, getpass::getpass, pathconf::*, sysconf::*}; use super::errno::{E2BIG, ENOMEM}; mod brk; mod getopt; +mod getpass; mod pathconf; mod sysconf; @@ -452,53 +452,6 @@ pub extern "C" fn getpagesize() -> c_int { .expect("page size not representable as type `int`") } -#[no_mangle] -pub unsafe extern "C" fn getpass(prompt: *const c_char) -> *mut c_char { - let tty = stdio::fopen(c_str!("/dev/tty").as_ptr(), c_str!("w+e").as_ptr()); - - if tty.is_null() { - return ptr::null_mut(); - } - - let fd = stdio::fileno(tty); - - let mut term = termios::termios::default(); - termios::tcgetattr(fd, &mut term as *mut termios::termios); - let old_temr = term.clone(); - - term.c_iflag &= !(termios::IGNCR | termios::INLCR) as u32; - term.c_iflag |= termios::ICRNL as u32; - term.c_lflag &= !(termios::ECHO | termios::ISIG) as u32; - term.c_lflag |= termios::ICANON as u32; - - termios::tcsetattr(fd, termios::TCSAFLUSH, &term as *const termios::termios); - stdio::fputs(prompt, tty); - stdio::fflush(tty); - - static mut PASSBUFF: [c_char; PASS_MAX] = [0; PASS_MAX]; - - let len = read(fd, PASSBUFF.as_mut_ptr() as *const c_void, PASSBUFF.len()); - - if len >= 0 { - let mut l = len as usize; - if PASSBUFF[l - 1] == b'\n' as c_char || PASSBUFF.len() == l { - l -= 1; - } - - PASSBUFF[l] = 0; - } - - termios::tcsetattr(fd, termios::TCSAFLUSH, &old_temr as *const termios::termios); - stdio::fputs(c_str!("\n").as_ptr(), tty); - stdio::fclose(tty); - - if len < 0 { - return ptr::null_mut(); - } - - PASSBUFF.as_mut_ptr() -} - #[no_mangle] pub extern "C" fn getpgid(pid: pid_t) -> pid_t { Sys::getpgid(pid) -- GitLab