c935e2689e
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.
218 lines
7.0 KiB
Rust
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);
|
|
}
|
|
}
|
|
}
|