Merge branch 'shadow-h' into 'master'

Implement shadow.h and argon2 crypt

See merge request redox-os/relibc!718
This commit is contained in:
Jeremy Soller
2025-09-19 06:45:18 -06:00
8 changed files with 343 additions and 15 deletions
Generated
+22
View File
@@ -11,6 +11,18 @@ dependencies = [
"memchr",
]
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures",
"password-hash",
]
[[package]]
name = "autocfg"
version = "1.4.0"
@@ -40,6 +52,15 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -498,6 +519,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
name = "relibc"
version = "0.2.5"
dependencies = [
"argon2",
"base64ct",
"bcrypt-pbkdf",
"bitflags",
+1
View File
@@ -51,6 +51,7 @@ chrono = {version = "0.4", default-features = false, features = ["alloc"]}
libm = "0.2"
object = { version = "0.36.7", git = "https://gitlab.redox-os.org/andypython/object", default-features = false, features = ["elf", "read_core"] }
spin = "0.9.8"
argon2 = "0.5.3"
[dependencies.dlmalloc]
path = "dlmalloc-rs"
+16
View File
@@ -0,0 +1,16 @@
use alloc::string::{String, ToString};
use argon2::{
password_hash::{PasswordHash, PasswordVerifier},
Argon2,
};
pub fn crypt_argon2(key: &str, setting: &str) -> Option<String> {
let hash = PasswordHash::new(setting).ok()?;
let argon2 = Argon2::default();
if argon2.verify_password(key.as_bytes(), &hash).is_ok() {
Some(setting.to_string())
} else {
None
}
}
+11 -8
View File
@@ -19,6 +19,7 @@ use crate::{
platform::{self, types::*},
};
mod argon2;
mod blowfish;
mod md5;
mod pbkdf2;
@@ -26,6 +27,7 @@ mod scrypt;
mod sha;
use self::{
argon2::crypt_argon2,
blowfish::crypt_blowfish,
md5::crypt_md5,
pbkdf2::crypt_pbkdf2,
@@ -73,21 +75,22 @@ pub unsafe extern "C" fn crypt_r(
let setting = unsafe { CStr::from_ptr(setting) }
.to_str()
.expect("setting must be utf-8");
let setting_bytes = setting.as_bytes();
let encoded = if setting_bytes[0] == b'$' && setting_bytes[1] != 0 && setting_bytes[2] != 0 {
if setting_bytes[1] == b'1' && setting_bytes[2] == b'$' {
let encoded = if setting.starts_with('$') {
if setting.starts_with("$1$") {
crypt_md5(key, setting)
} else if setting_bytes[1] == b'2' && setting_bytes[3] == b'$' {
} else if setting.starts_with("$2") && setting.as_bytes().get(3) == Some(&b'$') {
crypt_blowfish(key, setting)
} else if setting_bytes[1] == b'5' && setting_bytes[2] == b'$' {
} else if setting.starts_with("$5$") {
crypt_sha(key, setting, Sha256)
} else if setting_bytes[1] == b'6' && setting_bytes[2] == b'$' {
} else if setting.starts_with("$6$") {
crypt_sha(key, setting, Sha512)
} else if setting_bytes[1] == b'7' && setting_bytes[2] == b'$' {
} else if setting.starts_with("$7$") {
crypt_scrypt(key, setting)
} else if setting_bytes[1] == b'8' && setting_bytes[2] == b'$' {
} else if setting.starts_with("$8$") {
crypt_pbkdf2(key, setting)
} else if setting.starts_with("$argon2") {
crypt_argon2(key, setting)
} else {
platform::ERRNO.set(EINVAL);
return ptr::null_mut();
+9 -7
View File
@@ -38,6 +38,8 @@ const SEPARATOR: char = ':';
#[cfg(target_os = "redox")]
const SEPARATOR: char = ';';
const GROUP_FILE: &core::ffi::CStr = c"/etc/group";
#[derive(Clone, Copy, Debug)]
struct DestBuffer {
ptr: *mut u8,
@@ -249,7 +251,7 @@ fn parse_grp(line: String, destbuf: Option<DestBuffer>) -> Result<OwnedGrp, Erro
// MT-Unsafe race:grgid locale
#[no_mangle]
pub unsafe extern "C" fn getgrgid(gid: gid_t) -> *mut group {
let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ptr::null_mut();
};
@@ -272,7 +274,7 @@ pub unsafe extern "C" fn getgrgid(gid: gid_t) -> *mut group {
// MT-Unsafe race:grnam locale
#[no_mangle]
pub unsafe extern "C" fn getgrnam(name: *const c_char) -> *mut group {
let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ptr::null_mut();
};
@@ -314,7 +316,7 @@ pub unsafe extern "C" fn getgrgid_r(
*result = ptr::null_mut();
}
let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ENOENT;
};
@@ -368,7 +370,7 @@ pub unsafe extern "C" fn getgrnam_r(
buflen: usize,
result: *mut *mut group,
) -> c_int {
let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ENOENT;
};
@@ -409,7 +411,7 @@ pub unsafe extern "C" fn getgrent() -> *mut group {
let mut line_reader = unsafe { &mut *LINE_READER.get() };
if line_reader.is_none() {
let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ptr::null_mut();
};
*line_reader = Some(BufReader::new(db).lines());
@@ -445,7 +447,7 @@ pub unsafe extern "C" fn endgrent() {
#[no_mangle]
pub unsafe extern "C" fn setgrent() {
let mut line_reader = unsafe { &mut *LINE_READER.get() };
let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return;
};
*line_reader = Some(BufReader::new(db).lines());
@@ -471,7 +473,7 @@ pub unsafe extern "C" fn getgrouplist(
return 0;
};
let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return 0;
};
+1
View File
@@ -61,6 +61,7 @@ pub mod sched;
pub mod semaphore;
pub mod setjmp;
pub mod sgtty;
pub mod shadow;
pub mod signal;
// TODO: spawn.h
// TODO: stdalign.h (likely C implementation)
+12
View File
@@ -0,0 +1,12 @@
sys_includes = ["sys/types.h", "stdint.h", "stdio.h"]
include_guard = "_RELIBC_SHADOW_H"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
[export]
include = ["group"]
+271
View File
@@ -0,0 +1,271 @@
#![deny(unsafe_op_in_unsafe_fn)]
use core::{
cell::SyncUnsafeCell,
convert::TryInto,
mem,
ops::{Deref, DerefMut},
pin::Pin,
ptr,
str::FromStr,
};
use alloc::{boxed::Box, string::String, vec::Vec};
use crate::{
c_str::CStr,
fs::File,
header::{errno, fcntl, string::strlen},
io::{prelude::*, BufReader, Lines},
platform,
platform::types::*,
};
use super::errno::*;
#[cfg(target_os = "linux")]
const SEPARATOR: char = ':';
#[cfg(target_os = "redox")]
const SEPARATOR: char = ';';
const SHADOW_FILE: &core::ffi::CStr = c"/etc/shadow";
#[derive(Clone, Copy, Debug)]
struct DestBuffer {
ptr: *mut u8,
len: usize,
}
#[derive(Debug)]
enum MaybeAllocated {
Owned(Pin<Box<[u8]>>),
Borrowed(DestBuffer),
}
impl Deref for MaybeAllocated {
type Target = [u8];
fn deref(&self) -> &Self::Target {
match self {
MaybeAllocated::Owned(boxed) => boxed,
MaybeAllocated::Borrowed(dst) => unsafe {
core::slice::from_raw_parts(dst.ptr, dst.len)
},
}
}
}
impl DerefMut for MaybeAllocated {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
MaybeAllocated::Owned(boxed) => boxed,
MaybeAllocated::Borrowed(dst) => unsafe {
core::slice::from_raw_parts_mut(dst.ptr, dst.len)
},
}
}
}
static mut SHADOW_BUF: Option<MaybeAllocated> = None;
static mut SHADOW: spwd = spwd {
sp_namp: ptr::null_mut(),
sp_pwdp: ptr::null_mut(),
sp_lstchg: -1,
sp_min: -1,
sp_max: -1,
sp_warn: -1,
sp_inact: -1,
sp_expire: -1,
sp_flag: 0,
};
static LINE_READER: SyncUnsafeCell<Option<Lines<BufReader<File>>>> = SyncUnsafeCell::new(None);
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct spwd {
pub sp_namp: *mut c_char,
pub sp_pwdp: *mut c_char,
pub sp_lstchg: c_long,
pub sp_min: c_long,
pub sp_max: c_long,
pub sp_warn: c_long,
pub sp_inact: c_long,
pub sp_expire: c_long,
pub sp_flag: c_ulong,
}
#[derive(Debug)]
enum Error {
EOF,
BufTooSmall,
Syntax,
}
#[derive(Debug)]
struct OwnedSpwd {
buffer: MaybeAllocated,
reference: spwd,
}
impl OwnedSpwd {
fn into_global(self) -> *mut spwd {
unsafe {
SHADOW_BUF = Some(self.buffer);
SHADOW = self.reference;
&mut SHADOW
}
}
}
fn to_long(s: &str) -> c_long {
c_long::from_str(s).unwrap_or(-1)
}
fn to_ulong(s: &str) -> c_ulong {
c_ulong::from_str(s).unwrap_or(0)
}
fn parse_spwd(line: String, destbuf: Option<DestBuffer>) -> Result<OwnedSpwd, Error> {
let mut parts = line.split(SEPARATOR);
let sp_namp_str = parts.next().ok_or(Error::Syntax)?;
let sp_pwdp_str = parts.next().ok_or(Error::Syntax)?;
//TODO: these are not implemented redox-users crate
let sp_lstchg = to_long(parts.next().unwrap_or(""));
let sp_min = to_long(parts.next().unwrap_or(""));
let sp_max = to_long(parts.next().unwrap_or(""));
let sp_warn = to_long(parts.next().unwrap_or(""));
let sp_inact = to_long(parts.next().unwrap_or(""));
let sp_expire = to_long(parts.next().unwrap_or(""));
let sp_flag = to_ulong(parts.next().unwrap_or(""));
let string_data_len = sp_namp_str.len() + 1 + sp_pwdp_str.len() + 1;
let mut buffer = match destbuf {
Some(buf) => {
if buf.len < string_data_len {
platform::ERRNO.set(ERANGE);
return Err(Error::BufTooSmall);
}
MaybeAllocated::Borrowed(buf)
}
None => {
let mut vec = Vec::with_capacity(string_data_len);
vec.resize(string_data_len, 0);
MaybeAllocated::Owned(Box::into_pin(vec.into_boxed_slice()))
}
};
let (name_slice, rest) = buffer.split_at_mut(sp_namp_str.len() + 1);
name_slice[..sp_namp_str.len()].copy_from_slice(sp_namp_str.as_bytes());
name_slice[sp_namp_str.len()] = 0;
let (pwd_slice, _) = rest.split_at_mut(sp_pwdp_str.len() + 1);
pwd_slice[..sp_pwdp_str.len()].copy_from_slice(sp_pwdp_str.as_bytes());
pwd_slice[sp_pwdp_str.len()] = 0;
let sp_namp = name_slice.as_mut_ptr() as *mut c_char;
let sp_pwdp = pwd_slice.as_mut_ptr() as *mut c_char;
let reference = spwd {
sp_namp,
sp_pwdp,
sp_lstchg,
sp_min,
sp_max,
sp_warn,
sp_inact,
sp_expire,
sp_flag,
};
Ok(OwnedSpwd { buffer, reference })
}
#[no_mangle]
pub unsafe extern "C" fn getspnam(name: *const c_char) -> *mut spwd {
let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) else {
return ptr::null_mut();
};
let c_name = unsafe { CStr::from_ptr(name) };
for line in BufReader::new(db).lines() {
let Ok(line) = line else { continue };
if line.starts_with(c_name.to_str().unwrap_or("\0")) {
if let Ok(pwd) = parse_spwd(line, None) {
return pwd.into_global();
}
}
}
ptr::null_mut()
}
#[no_mangle]
pub unsafe extern "C" fn getspnam_r(
name: *const c_char,
result_buf: *mut spwd,
buffer: *mut c_char,
buflen: size_t,
result: *mut *mut spwd,
) -> c_int {
unsafe { *result = ptr::null_mut() };
let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) else {
return ENOENT;
};
let c_name = unsafe { CStr::from_ptr(name).to_str().unwrap_or("\0") };
for line in BufReader::new(db).lines() {
let Ok(line) = line else { continue };
if line.starts_with(c_name) {
let dest_buf = Some(DestBuffer {
ptr: buffer as *mut u8,
len: buflen,
});
return match parse_spwd(line, dest_buf) {
Ok(sp) => {
unsafe {
*result_buf = sp.reference;
*result = result_buf;
}
0
}
Err(Error::BufTooSmall) => ERANGE,
_ => ENOENT,
};
}
}
ENOENT
}
#[no_mangle]
pub unsafe extern "C" fn setspent() {
let mut line_reader = unsafe { &mut *LINE_READER.get() };
if let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) {
*line_reader = Some(BufReader::new(db).lines());
}
}
#[no_mangle]
pub unsafe extern "C" fn endspent() {
unsafe {
*LINE_READER.get() = None;
}
}
#[no_mangle]
pub unsafe extern "C" fn getspent() -> *mut spwd {
let line_reader = unsafe { &mut *LINE_READER.get() };
if line_reader.is_none() {
unsafe {
setspent();
}
}
if let Some(lines) = line_reader {
if let Some(Ok(line)) = lines.next() {
if let Ok(sp) = parse_spwd(line, None) {
return sp.into_global();
}
}
}
ptr::null_mut()
}