Ensure initial session is only run once.
Security concerns were raised regarding the initial session being executed whenever greetd was restarted (when signing out of one's DE, when greetd or a greeter restarted or crashed, ...). This creates a runfile (by default at /run/greetd.run) either when the initial session is executed or when a greeter is started. Whenever this file exists, the initial session is ignored (and the configured greeter is always run).
This commit is contained in:

committed by
Kenny Levinsen

parent
41c6b5f1d4
commit
5201469e39
@@ -5,6 +5,8 @@ use getopts::Options;
|
|||||||
|
|
||||||
use super::error::Error;
|
use super::error::Error;
|
||||||
|
|
||||||
|
const RUNFILE: &str = "/run/greetd.run";
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum VtSelection {
|
pub enum VtSelection {
|
||||||
Next,
|
Next,
|
||||||
@@ -38,12 +40,14 @@ pub struct ConfigTerminal {
|
|||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct ConfigGeneral {
|
pub struct ConfigGeneral {
|
||||||
pub source_profile: bool,
|
pub source_profile: bool,
|
||||||
|
pub runfile: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ConfigGeneral {
|
impl Default for ConfigGeneral {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ConfigGeneral {
|
ConfigGeneral {
|
||||||
source_profile: true,
|
source_profile: true,
|
||||||
|
runfile: RUNFILE.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,13 +170,21 @@ fn parse_new_config(config: &HashMap<&str, HashMap<&str, &str>>) -> Result<Confi
|
|||||||
}?;
|
}?;
|
||||||
|
|
||||||
let general = match config.get("general") {
|
let general = match config.get("general") {
|
||||||
Some(section) => ConfigGeneral {
|
Some(section) => {
|
||||||
|
let runfilestr = section.get("runfile").unwrap_or(&RUNFILE);
|
||||||
|
let runfile = maybe_unquote(runfilestr)
|
||||||
|
.map_err(|e| format!("unable to read general.runfile: {}", e))?;
|
||||||
|
|
||||||
|
ConfigGeneral {
|
||||||
source_profile: section
|
source_profile: section
|
||||||
.get("source_profile")
|
.get("source_profile")
|
||||||
.unwrap_or(&"true")
|
.unwrap_or(&"true")
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|e| format!("could not parse source_profile: {}", e))?,
|
.map_err(|e| format!("could not parse source_profile: {}", e))?,
|
||||||
},
|
runfile,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
None => Default::default(),
|
None => Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -386,6 +398,7 @@ user = \"john\"
|
|||||||
[terminal]\nvt = 1\n[default_session]\ncommand = \"agreety\"
|
[terminal]\nvt = 1\n[default_session]\ncommand = \"agreety\"
|
||||||
[general]
|
[general]
|
||||||
source_profile = false
|
source_profile = false
|
||||||
|
runfile = \"/path/to/greetd.state\"
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.expect("config didn't parse");
|
.expect("config didn't parse");
|
||||||
@@ -401,6 +414,7 @@ source_profile = false
|
|||||||
},
|
},
|
||||||
general: ConfigGeneral {
|
general: ConfigGeneral {
|
||||||
source_profile: false,
|
source_profile: false,
|
||||||
|
runfile: "/path/to/greetd.state".to_string(),
|
||||||
},
|
},
|
||||||
initial_session: None,
|
initial_session: None,
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
use std::time::{Duration, Instant};
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
path::Path,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use nix::{
|
use nix::{
|
||||||
sys::wait::{waitpid, WaitPidFlag, WaitStatus},
|
sys::wait::{waitpid, WaitPidFlag, WaitStatus},
|
||||||
@@ -41,6 +45,7 @@ pub struct Context {
|
|||||||
pam_service: String,
|
pam_service: String,
|
||||||
term_mode: TerminalMode,
|
term_mode: TerminalMode,
|
||||||
source_profile: bool,
|
source_profile: bool,
|
||||||
|
runfile: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
@@ -51,6 +56,7 @@ impl Context {
|
|||||||
pam_service: String,
|
pam_service: String,
|
||||||
term_mode: TerminalMode,
|
term_mode: TerminalMode,
|
||||||
source_profile: bool,
|
source_profile: bool,
|
||||||
|
runfile: String,
|
||||||
) -> Context {
|
) -> Context {
|
||||||
Context {
|
Context {
|
||||||
inner: RwLock::new(ContextInner {
|
inner: RwLock::new(ContextInner {
|
||||||
@@ -64,6 +70,7 @@ impl Context {
|
|||||||
pam_service,
|
pam_service,
|
||||||
term_mode,
|
term_mode,
|
||||||
source_profile,
|
source_profile,
|
||||||
|
runfile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +138,18 @@ impl Context {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if this is the first time greetd starts since boot, or if it restarted for any reason
|
||||||
|
pub fn is_first_run(&self) -> bool {
|
||||||
|
!Path::new(&self.runfile).exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create runfile used to check if greetd was already started since boot
|
||||||
|
pub fn create_runfile(&self) {
|
||||||
|
if let Err(err) = File::create(&self.runfile) {
|
||||||
|
eprintln!("could not create runfile: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Directly start an initial session, bypassing the normal scheduling.
|
/// Directly start an initial session, bypassing the normal scheduling.
|
||||||
pub async fn start_user_session(&self, user: &str, cmd: Vec<String>) -> Result<(), Error> {
|
pub async fn start_user_session(&self, user: &str, cmd: Vec<String>) -> Result<(), Error> {
|
||||||
{
|
{
|
||||||
|
@@ -222,9 +222,10 @@ pub async fn main(config: Config) -> Result<(), Error> {
|
|||||||
service.to_string(),
|
service.to_string(),
|
||||||
term_mode.clone(),
|
term_mode.clone(),
|
||||||
config.file.general.source_profile,
|
config.file.general.source_profile,
|
||||||
|
config.file.general.runfile,
|
||||||
));
|
));
|
||||||
|
|
||||||
if let Some(s) = config.file.initial_session {
|
if let (Some(s), true) = (config.file.initial_session, ctx.is_first_run()) {
|
||||||
if let Err(e) = ctx.start_user_session(&s.user, vec![s.command]).await {
|
if let Err(e) = ctx.start_user_session(&s.user, vec![s.command]).await {
|
||||||
eprintln!("unable to start greeter: {}", e);
|
eprintln!("unable to start greeter: {}", e);
|
||||||
reset_vt(&term_mode).map_err(|e| format!("unable to reset VT: {}", e))?;
|
reset_vt(&term_mode).map_err(|e| format!("unable to reset VT: {}", e))?;
|
||||||
@@ -238,6 +239,8 @@ pub async fn main(config: Config) -> Result<(), Error> {
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.create_runfile();
|
||||||
|
|
||||||
let mut alarm = signal(SignalKind::alarm()).expect("unable to listen for SIGALRM");
|
let mut alarm = signal(SignalKind::alarm()).expect("unable to listen for SIGALRM");
|
||||||
let mut child = signal(SignalKind::child()).expect("unable to listen for SIGCHLD");
|
let mut child = signal(SignalKind::child()).expect("unable to listen for SIGCHLD");
|
||||||
let mut term = signal(SignalKind::terminate()).expect("unable to listen for SIGTERM");
|
let mut term = signal(SignalKind::terminate()).expect("unable to listen for SIGTERM");
|
||||||
|
@@ -46,6 +46,13 @@ nor deserved its own section.
|
|||||||
Whether or not to source ~/.profile and /etc/profile if present when running
|
Whether or not to source ~/.profile and /etc/profile if present when running
|
||||||
commands. Defaults to true.
|
commands. Defaults to true.
|
||||||
|
|
||||||
|
*runfile* = path-to-runfile
|
||||||
|
Location of greetd's runfile that is created during the first run to prevent
|
||||||
|
the initial session from being run again on session termination or on greetd
|
||||||
|
restart.
|
||||||
|
|
||||||
|
This file should be in a location that is cleared during a reboot.
|
||||||
|
|
||||||
## default_session
|
## default_session
|
||||||
|
|
||||||
This section describes the default session, also referred to as the *greeter*.
|
This section describes the default session, also referred to as the *greeter*.
|
||||||
@@ -66,6 +73,11 @@ This section describes the default session, also referred to as the *greeter*.
|
|||||||
This optional section describes the initial session, commonly referred to as
|
This optional section describes the initial session, commonly referred to as
|
||||||
"auto-login".
|
"auto-login".
|
||||||
|
|
||||||
|
The initial session will only be executed during the first run of greetd since
|
||||||
|
boot in order to ensure signing out works properly and to prevent security
|
||||||
|
issues whenever greetd or the greeter exit. This is checked through the
|
||||||
|
presence of the runfile.
|
||||||
|
|
||||||
*command* = command-line
|
*command* = command-line
|
||||||
The command-line to run to start the initial session, e.g. "sway". The
|
The command-line to run to start the initial session, e.g. "sway". The
|
||||||
initial session will be run when exactly once when greetd is initially
|
initial session will be run when exactly once when greetd is initially
|
||||||
|
Reference in New Issue
Block a user