@@ -1,10 +1,6 @@
|
|||||||
use crate::{client, util::uuid_by_one_user_filter};
|
use crate::{client, util::uuid_by_one_user_filter};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use semios_account::{
|
|
||||||
auth::{AuthCli, AuthenticateArgs, UserAuthMethodFlags},
|
|
||||||
protocol::{GetAuthMethodPathArgs, GetUserAuthMethodsArgs},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Parser)]
|
#[derive(Debug, Clone, Parser)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
use crate::client;
|
||||||
|
use semios_account::{protocol::GetUserInfoArgs, wellknown::CLI_LOGIN_SHELL};
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Cli {
|
||||||
|
preserve_environment: bool,
|
||||||
|
user: Option<String>,
|
||||||
|
host: Option<String>,
|
||||||
|
force: bool,
|
||||||
|
}
|
||||||
|
impl Cli {
|
||||||
|
const USAGE: &str = "usage: login [-p] [-h host] [-f] [name]";
|
||||||
|
|
||||||
|
fn parse() -> Self {
|
||||||
|
let mut args = std::env::args().skip(1);
|
||||||
|
let mut this = Self {
|
||||||
|
preserve_environment: false,
|
||||||
|
user: None,
|
||||||
|
host: None,
|
||||||
|
force: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
while let Some(i) = args.next() {
|
||||||
|
if i == "-f" {
|
||||||
|
this.force = true;
|
||||||
|
} else if i == "-p" {
|
||||||
|
this.preserve_environment = true;
|
||||||
|
} else if i == "-h" {
|
||||||
|
this.host = Some(args.next().unwrap_or_else(|| Self::parse_error()));
|
||||||
|
} else if !i.starts_with("-") {
|
||||||
|
this.user = Some(i);
|
||||||
|
} else {
|
||||||
|
Self::parse_error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_error() -> ! {
|
||||||
|
eprintln!("{}", Self::USAGE);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
let mut client = match client() {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("login: Service error: {err}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let user = cli.user.clone().unwrap_or_else(ask_login);
|
||||||
|
let Ok(user_info) = client.get_user_info(GetUserInfoArgs::Username(user.clone())) else {
|
||||||
|
eprintln!("\nLogin incorrect");
|
||||||
|
std::process::exit(1);
|
||||||
|
};
|
||||||
|
let auth_result =
|
||||||
|
crate::util::authenticate(&mut client, user_info.uuid, Some("password".into()), "CLI");
|
||||||
|
match auth_result {
|
||||||
|
Ok(true) => (),
|
||||||
|
Ok(false) => std::process::exit(1),
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!("\nLogin incorrect");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let Some(uid) = user_info.host_uid else {
|
||||||
|
eprintln!("\nThis user is not configured to login on this host.");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(shell) = user_info.defaults.get(CLI_LOGIN_SHELL) else {
|
||||||
|
eprintln!("\nThis user has no login shell.");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let home_dir = user_info.home_directory.unwrap_or_else(|| "/".into());
|
||||||
|
|
||||||
|
if !crate::has_root_privs() {
|
||||||
|
eprintln!("login: the `login` binary is only available with root privileges");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set environment variables
|
||||||
|
if !cli.preserve_environment {
|
||||||
|
// SAFETY: The program is single-threaded, so no data race is possible here.
|
||||||
|
unsafe {
|
||||||
|
std::env::set_var("HOME", &home_dir);
|
||||||
|
std::env::set_var("USER", &user);
|
||||||
|
std::env::set_var("SHELL", &shell);
|
||||||
|
std::env::set_var("LOGNAME", &user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let err = std::process::Command::new(shell).uid(uid).exec();
|
||||||
|
eprintln!("login: exec error: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ask_login() -> String {
|
||||||
|
eprint!("login: ");
|
||||||
|
let mut login = String::with_capacity(128);
|
||||||
|
_ = std::io::stdin().read_line(&mut login);
|
||||||
|
login.trim().into()
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ mod authenticate;
|
|||||||
mod create_group;
|
mod create_group;
|
||||||
mod create_user;
|
mod create_user;
|
||||||
mod info;
|
mod info;
|
||||||
|
mod login;
|
||||||
mod su;
|
mod su;
|
||||||
mod update_auth;
|
mod update_auth;
|
||||||
mod util;
|
mod util;
|
||||||
@@ -31,7 +32,10 @@ fn main() {
|
|||||||
// Invoke non-default utility if required
|
// Invoke non-default utility if required
|
||||||
if progname() == "su" {
|
if progname() == "su" {
|
||||||
su::main();
|
su::main();
|
||||||
std::process::exit(0);
|
return;
|
||||||
|
} else if progname() == "login" {
|
||||||
|
login::main();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke the default `account` utility
|
// Invoke the default `account` utility
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use semios_account::{protocol::GetUserInfoArgs, wellknown::CLI_LOGIN_SHELL};
|
|
||||||
|
|
||||||
use crate::client;
|
use crate::client;
|
||||||
|
use semios_account::{protocol::GetUserInfoArgs, wellknown::CLI_LOGIN_SHELL};
|
||||||
use std::{ffi::OsString, os::unix::process::CommandExt};
|
use std::{ffi::OsString, os::unix::process::CommandExt};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ anyhow = "1"
|
|||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
bitflags = "2"
|
bitflags = "2"
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
rusqlite = "0.39"
|
rusqlite = { version = "0.39", features = ["bundled"] }
|
||||||
rustc-hash = "2"
|
rustc-hash = "2"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
// Sandbox rules for running an authentication method.
|
|
||||||
@@ -154,7 +154,7 @@ impl Session {
|
|||||||
|
|
||||||
async fn list_user(
|
async fn list_user(
|
||||||
state: Arc<AppState>,
|
state: Arc<AppState>,
|
||||||
caller: Caller,
|
_caller: Caller,
|
||||||
args: ListUserArgs,
|
args: ListUserArgs,
|
||||||
) -> Result<Vec<Uuid>, Error> {
|
) -> Result<Vec<Uuid>, Error> {
|
||||||
Ok(state.local_db.list_user(args.start, args.len))
|
Ok(state.local_db.list_user(args.start, args.len))
|
||||||
@@ -162,7 +162,7 @@ async fn list_user(
|
|||||||
|
|
||||||
async fn list_group(
|
async fn list_group(
|
||||||
state: Arc<AppState>,
|
state: Arc<AppState>,
|
||||||
caller: Caller,
|
_caller: Caller,
|
||||||
args: ListGroupArgs,
|
args: ListGroupArgs,
|
||||||
) -> Result<Vec<Uuid>, Error> {
|
) -> Result<Vec<Uuid>, Error> {
|
||||||
Ok(state.local_db.list_group(args.start, args.len))
|
Ok(state.local_db.list_group(args.start, args.len))
|
||||||
@@ -170,7 +170,7 @@ async fn list_group(
|
|||||||
|
|
||||||
async fn get_user_info(
|
async fn get_user_info(
|
||||||
state: Arc<AppState>,
|
state: Arc<AppState>,
|
||||||
caller: Caller,
|
_caller: Caller,
|
||||||
args: GetUserInfoArgs,
|
args: GetUserInfoArgs,
|
||||||
) -> Result<UserInfo, Error> {
|
) -> Result<UserInfo, Error> {
|
||||||
let uuid = match args {
|
let uuid = match args {
|
||||||
@@ -189,7 +189,7 @@ async fn get_user_info(
|
|||||||
|
|
||||||
async fn get_group_info(
|
async fn get_group_info(
|
||||||
state: Arc<AppState>,
|
state: Arc<AppState>,
|
||||||
caller: Caller,
|
_caller: Caller,
|
||||||
args: GetGroupInfoArgs,
|
args: GetGroupInfoArgs,
|
||||||
) -> Result<GroupInfo, Error> {
|
) -> Result<GroupInfo, Error> {
|
||||||
let uuid = match args {
|
let uuid = match args {
|
||||||
@@ -279,7 +279,7 @@ async fn create_group(
|
|||||||
async fn remove_user(
|
async fn remove_user(
|
||||||
state: Arc<AppState>,
|
state: Arc<AppState>,
|
||||||
caller: Caller,
|
caller: Caller,
|
||||||
args: RemoveUserArgs,
|
_args: RemoveUserArgs,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
require_secure_tags(&state, caller, &[SecureTag::RemoveUser]).await?;
|
require_secure_tags(&state, caller, &[SecureTag::RemoveUser]).await?;
|
||||||
todo!();
|
todo!();
|
||||||
@@ -321,7 +321,7 @@ async fn set_secret(
|
|||||||
|
|
||||||
async fn get_user_auth_methods(
|
async fn get_user_auth_methods(
|
||||||
state: Arc<AppState>,
|
state: Arc<AppState>,
|
||||||
caller: Caller,
|
_caller: Caller,
|
||||||
args: GetUserAuthMethodsArgs,
|
args: GetUserAuthMethodsArgs,
|
||||||
) -> Result<Vec<UserAuthMethodRecord>, Error> {
|
) -> Result<Vec<UserAuthMethodRecord>, Error> {
|
||||||
require_secure_tags(&state, Caller::User(args.user), &[SecureTag::Login]).await?;
|
require_secure_tags(&state, Caller::User(args.user), &[SecureTag::Login]).await?;
|
||||||
@@ -330,7 +330,7 @@ async fn get_user_auth_methods(
|
|||||||
|
|
||||||
async fn user_add_auth_method(
|
async fn user_add_auth_method(
|
||||||
state: Arc<AppState>,
|
state: Arc<AppState>,
|
||||||
caller: Caller,
|
_caller: Caller,
|
||||||
args: UserAddAuthMethodArgs,
|
args: UserAddAuthMethodArgs,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
require_secure_tags(&state, Caller::User(args.user), &[SecureTag::Login]).await?;
|
require_secure_tags(&state, Caller::User(args.user), &[SecureTag::Login]).await?;
|
||||||
@@ -355,7 +355,7 @@ async fn user_add_auth_method(
|
|||||||
|
|
||||||
async fn get_auth_method_path(
|
async fn get_auth_method_path(
|
||||||
state: Arc<AppState>,
|
state: Arc<AppState>,
|
||||||
caller: Caller,
|
_caller: Caller,
|
||||||
args: GetAuthMethodPathArgs,
|
args: GetAuthMethodPathArgs,
|
||||||
) -> Result<PathBuf, Error> {
|
) -> Result<PathBuf, Error> {
|
||||||
Ok(state
|
Ok(state
|
||||||
@@ -368,7 +368,7 @@ async fn get_auth_method_path(
|
|||||||
// ==- Helpers -==
|
// ==- Helpers -==
|
||||||
|
|
||||||
async fn require_same_user(
|
async fn require_same_user(
|
||||||
state: &AppState,
|
_state: &AppState,
|
||||||
caller: Caller,
|
caller: Caller,
|
||||||
requested_user: Uuid,
|
requested_user: Uuid,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
record::{GroupInfo, HostGid, HostUid, SecureTag},
|
record::{HostGid, HostUid, SecureTag},
|
||||||
secret::{MasterKeyProvision, Secret},
|
secret::{MasterKeyProvision, Secret},
|
||||||
};
|
};
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"on_failure": "terminate",
|
||||||
|
"request": {
|
||||||
|
"method": "CreateUser",
|
||||||
|
"params": {
|
||||||
|
"username": "root",
|
||||||
|
"fullname": "System Administrator",
|
||||||
|
"host_uid": { "manual": 0 },
|
||||||
|
"description": "UNIX system administrator",
|
||||||
|
"secure_tags": ["Login", "CreateUser", "RemoveUser", "ReadSecret", "WriteSecret"],
|
||||||
|
"extra_records": {},
|
||||||
|
"defaults": {
|
||||||
|
"CliLoginShell": "/bin/sh"
|
||||||
|
},
|
||||||
|
"home_directory": "/home/root",
|
||||||
|
"groups": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user