Files
RedBear-OS/src/bin/login.rs
T
vasilito c935e2689e login: DIAGNOSTIC — spawn login shell without namespace restriction
Getty forwards console input to the pty master correctly (verified), and
login reads the username fine, but after apply_login_schemes() restricts
the namespace the interactive shell's pty-slave read never returns the
forwarded input. Temporarily skip the restriction to confirm it is the
cause. Also removes the getty forward diagnostic.
2026-07-18 12:52:51 +09:00

218 lines
7.0 KiB
Rust

#[macro_use]
extern crate clap;
use libredox::error::Result;
use std::fs::File;
use std::io::{self, Write};
use std::str;
use extra::option::OptionalExt;
use redox_users::{All, AllUsers, Config, User};
use termion::input::TermRead;
use userutils::spawn_shell;
const _MAN_PAGE: &'static str = /* @MANSTART{login} */
r#"
NAME
login - log into the computer
SYNOPSIS
login
DESCRIPTION
The login utility logs users (and pseudo-users) into the computer system.
OPTIONS
-h --help
Display help info and exit.
AUTHOR
Written by Jeremy Soller, Jose Narvaez.
"#; /* @MANEND */
const ISSUE_FILE: &'static str = "/etc/issue";
const MOTD_FILE: &'static str = "/etc/motd";
// TODO: Move to redox_users once the definition solidifies.
const DEFAULT_SCHEMES: [&'static str; 26] = [
// Kernel schemes
"debug",
"event",
"memory",
"pipe",
"serio",
"irq",
"time",
"sys",
// Base schemes
"rand",
"null",
"zero",
"log",
// Network schemes
"ip",
"icmp",
"tcp",
"udp",
// IPC schemes
"shm",
"chan",
"uds_stream",
"uds_dgram",
// File schemes
"file",
// Display schemes
"display.vesa",
"display*",
// Other schemes
"pty",
"sudo",
"audio",
];
pub fn apply_login_schemes(
user: &User<redox_users::auth::Full>,
default_schemes: &[&str],
) -> Result<libredox::Fd> {
let schemes = match load_config_schemes(user) {
Some(s) => s,
_ => default_schemes.iter().map(|s| s.to_string()).collect(),
};
let mut names: Vec<ioslice::IoSlice> = Vec::with_capacity(schemes.len());
for scheme in schemes.iter() {
names.push(ioslice::IoSlice::new(scheme.as_bytes()));
}
let ns_fd = libredox::call::mkns(&names)?;
let before_ns_fd = libredox::Fd::new(libredox::call::setns(ns_fd)?);
Ok(before_ns_fd)
}
fn load_config_schemes(user: &User<redox_users::auth::Full>) -> Option<Vec<String>> {
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fs;
const LOGIN_SCHEMES_FILE: &'static str = "/etc/login_schemes.toml";
#[derive(Debug, Clone, Serialize, Deserialize)]
struct UserSchemeConfig {
pub schemes: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct LoginConfig {
#[serde(rename = "user_schemes")]
pub user_schemes: BTreeMap<String, UserSchemeConfig>,
}
let config_str = fs::read_to_string(LOGIN_SCHEMES_FILE).ok()?;
let config: LoginConfig = toml::from_str(&config_str).ok()?;
config
.user_schemes
.get(&user.user)
.map(|cfg| cfg.schemes.clone())
}
pub fn main() {
let mut stdout = io::stdout();
let mut stderr = io::stderr();
let _args = clap_app!(login =>
(author: "Jeremy Soller, Jose Narvaez")
(about: "Login as a user")
)
.get_matches();
if let Ok(mut issue) = File::open(ISSUE_FILE) {
io::copy(&mut issue, &mut stdout).r#try(&mut stderr);
stdout.flush().r#try(&mut stderr);
}
loop {
let user = liner::Context::new()
.read_line(
liner::Prompt::from("\x1B[1mRed Bear login:\x1B[0m "),
None,
&mut liner::BasicCompleter::new(Vec::<String>::new()),
)
.r#try(&mut stderr);
if !user.is_empty() {
let stdin = io::stdin();
let mut stdin = stdin.lock();
let sys_users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1);
match sys_users.get_by_name(user) {
None => {
stdout.write(b"\nLogin incorrect\n").r#try(&mut stderr);
stdout.write(b"\n").r#try(&mut stderr);
stdout.flush().r#try(&mut stderr);
continue;
}
Some(user) => {
// Establish a working directory on the `file` scheme (the
// user's home) BEFORE any namespace restriction. login runs
// with the CWD inherited from init, which on the live image
// lives on the `initfs` scheme. apply_login_schemes() below
// switches to a namespace containing only DEFAULT_SCHEMES
// (which excludes `initfs`), after which that inherited CWD
// fd is unresolvable — and because relibc resolves paths via
// the CWD fd (AT_REDOX_CWD_FD), the login shell's spawn
// (Command child chdir + execve) then fails or hangs. Doing
// this chdir here, while still in the full namespace, points
// the CWD at `file:` (home, fallback `/`), which remains
// valid inside the restricted login namespace.
if std::env::set_current_dir(&user.home).is_err() {
let _ = std::env::set_current_dir("/");
}
if user.is_passwd_blank() {
if let Ok(mut motd) = File::open(MOTD_FILE) {
io::copy(&mut motd, &mut stdout).r#try(&mut stderr);
stdout.flush().r#try(&mut stderr);
}
// DIAGNOSTIC: temporarily skip the login namespace
// restriction (apply_login_schemes). After its setns(),
// the interactive shell's pty-slave stdin stops
// receiving input forwarded to the pty master (getty
// forwards it, but the slave read never returns it), so
// the shell can never read a command. Spawn the shell in
// the inherited namespace to test whether the
// restriction is the cause.
let _ = apply_login_schemes; // keep import used
spawn_shell(user).unwrap_or_exit(1);
break;
}
stdout
.write_all(b"\x1B[1mpassword:\x1B[0m ")
.r#try(&mut stderr);
stdout.flush().r#try(&mut stderr);
if let Some(password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) {
stdout.write(b"\n").r#try(&mut stderr);
stdout.flush().r#try(&mut stderr);
if user.verify_passwd(&password) {
if let Ok(mut motd) = File::open(MOTD_FILE) {
io::copy(&mut motd, &mut stdout).r#try(&mut stderr);
stdout.flush().r#try(&mut stderr);
}
spawn_shell(user).unwrap_or_exit(1);
break;
}
}
}
}
} else {
stdout.write(b"\n").r#try(&mut stderr);
stdout.flush().r#try(&mut stderr);
}
}
}