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;
|
||||
|
||||
const RUNFILE: &str = "/run/greetd.run";
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum VtSelection {
|
||||
Next,
|
||||
@@ -38,12 +40,14 @@ pub struct ConfigTerminal {
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct ConfigGeneral {
|
||||
pub source_profile: bool,
|
||||
pub runfile: String,
|
||||
}
|
||||
|
||||
impl Default for ConfigGeneral {
|
||||
fn default() -> Self {
|
||||
ConfigGeneral {
|
||||
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") {
|
||||
Some(section) => ConfigGeneral {
|
||||
source_profile: section
|
||||
.get("source_profile")
|
||||
.unwrap_or(&"true")
|
||||
.parse()
|
||||
.map_err(|e| format!("could not parse source_profile: {}", e))?,
|
||||
},
|
||||
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
|
||||
.get("source_profile")
|
||||
.unwrap_or(&"true")
|
||||
.parse()
|
||||
.map_err(|e| format!("could not parse source_profile: {}", e))?,
|
||||
runfile,
|
||||
}
|
||||
}
|
||||
|
||||
None => Default::default(),
|
||||
};
|
||||
|
||||
@@ -386,6 +398,7 @@ user = \"john\"
|
||||
[terminal]\nvt = 1\n[default_session]\ncommand = \"agreety\"
|
||||
[general]
|
||||
source_profile = false
|
||||
runfile = \"/path/to/greetd.state\"
|
||||
",
|
||||
)
|
||||
.expect("config didn't parse");
|
||||
@@ -401,6 +414,7 @@ source_profile = false
|
||||
},
|
||||
general: ConfigGeneral {
|
||||
source_profile: false,
|
||||
runfile: "/path/to/greetd.state".to_string(),
|
||||
},
|
||||
initial_session: None,
|
||||
}
|
||||
|
@@ -1,4 +1,8 @@
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{
|
||||
fs::File,
|
||||
path::Path,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use nix::{
|
||||
sys::wait::{waitpid, WaitPidFlag, WaitStatus},
|
||||
@@ -41,6 +45,7 @@ pub struct Context {
|
||||
pam_service: String,
|
||||
term_mode: TerminalMode,
|
||||
source_profile: bool,
|
||||
runfile: String,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
@@ -51,6 +56,7 @@ impl Context {
|
||||
pam_service: String,
|
||||
term_mode: TerminalMode,
|
||||
source_profile: bool,
|
||||
runfile: String,
|
||||
) -> Context {
|
||||
Context {
|
||||
inner: RwLock::new(ContextInner {
|
||||
@@ -64,6 +70,7 @@ impl Context {
|
||||
pam_service,
|
||||
term_mode,
|
||||
source_profile,
|
||||
runfile,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +138,18 @@ impl Context {
|
||||
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.
|
||||
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(),
|
||||
term_mode.clone(),
|
||||
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 {
|
||||
eprintln!("unable to start greeter: {}", 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);
|
||||
}
|
||||
|
||||
ctx.create_runfile();
|
||||
|
||||
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 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
|
||||
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
|
||||
|
||||
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
|
||||
"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
|
||||
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
|
||||
|
Reference in New Issue
Block a user