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