Antoine POPINEAU: 1 Ensure initial session is only run once. 4 files changed, 57 insertions(+), 9 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~kennylevinsen/greetd-devel/patches/23843/mbox | git am -3Learn more about email & git
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 relates to the issue raised in https://todo.sr.ht/~kennylevinsen/greetd/10 and on tuigreet's GitHub repository. greetd/src/config/mod.rs | 28 +++++++++++++++++++++------- greetd/src/context.rs | 21 ++++++++++++++++++++- greetd/src/server.rs | 5 ++++- man/greetd-5.scd | 12 ++++++++++++ 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/greetd/src/config/mod.rs b/greetd/src/config/mod.rs index 3dd5985..5f2d537 100644 --- a/greetd/src/config/mod.rs +++ b/greetd/src/config/mod.rs @@ -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, } diff --git a/greetd/src/context.rs b/greetd/src/context.rs index 0d7fe97..cff0a44 100644 --- a/greetd/src/context.rs +++ b/greetd/src/context.rs @@ -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> { { diff --git a/greetd/src/server.rs b/greetd/src/server.rs index 9ad1a4f..0a1994b 100644 --- a/greetd/src/server.rs +++ b/greetd/src/server.rs @@ -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"); diff --git a/man/greetd-5.scd b/man/greetd-5.scd index 3cd20d4..18048ba 100644 --- a/man/greetd-5.scd +++ b/man/greetd-5.scd @@ -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 -- 2.32.0
builds.sr.ht <builds@sr.ht>greetd/patches: SUCCESS in 2m41s [Ensure initial session is only run once.][0] from [Antoine POPINEAU][1] [0]: https://lists.sr.ht/~kennylevinsen/greetd-devel/patches/23843 [1]: mailto:antoine@popineau.eu ✓ #544726 SUCCESS greetd/patches/archlinux.yml https://builds.sr.ht/~kennylevinsen/job/544726 ✓ #544725 SUCCESS greetd/patches/alpine.yml https://builds.sr.ht/~kennylevinsen/job/544725
Applied, thanks!