Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ i18n-embed-fl = "0.7"
rust-embed = "8"
futures-util = "0.3.30"
timedate-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings" }
futures-channel = "0.3.31"

[dependencies.greetd_ipc]
version = "0.10.3"
Expand Down
4 changes: 4 additions & 0 deletions cosmic-greeter.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ service = "cosmic-greeter"
[default_session]
command = "cosmic-comp systemd-cat -t cosmic-greeter cosmic-greeter"
user = "cosmic-greeter"

[initial_session]
command = "cosmic-greeter"
user = "cosmic-initial-setup"
42 changes: 41 additions & 1 deletion daemon/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use cosmic_greeter_daemon::UserData;
use std::{env, error::Error, future::pending, io, path::Path};
use std::{env, error::Error, future::pending, io, path::Path, process::Command};
use zbus::{connection::Builder, DBusError};

const INITIAL_SETUP_HOME: &str = "/run/cosmic-initial-setup/";

//IMPORTANT: this function is critical to the security of this proxy. It must ensure that the
// callback is executed with the permissions of the specified user id. A good test is to see if
// the /etc/shadow file can be read with a non-root user, it should fail with EPERM.
Expand Down Expand Up @@ -46,6 +48,44 @@ struct GreeterProxy;

#[zbus::interface(name = "com.system76.CosmicGreeter")]
impl GreeterProxy {
async fn initial_setup_start(&mut self) -> Result<(), GreeterError> {
let home_dir = Path::new(INITIAL_SETUP_HOME);

if let Err(why) = std::fs::create_dir_all(&home_dir.join(".local/share")) {
eprintln!("failed to create local share dir: {why:?}");
}

if let Err(why) = std::fs::create_dir_all(&home_dir.join(".local/state")) {
eprintln!("failed to create local share dir: {why:?}");
}

_ = Command::new("chown")
.args(&["-R", "cosmic-initial-setup:nogroup", INITIAL_SETUP_HOME])
.status();

_ = Command::new("chsh")
.args(&["-s", "/bin/bash", "cosmic-initial-setup"])
.status();

Ok(())
}

async fn initial_setup_end(&mut self, new_user: String) -> Result<(), GreeterError> {
let home_dir = ["/home/", &new_user].concat();
_ = Command::new("rm").args(&["-rf", &home_dir]).status();
_ = Command::new("chown")
.args(&[
"-R",
&[&new_user, ":", &new_user].concat(),
INITIAL_SETUP_HOME,
])
.status();
_ = Command::new("mv")
.args(&[INITIAL_SETUP_HOME, &home_dir])
.status();
Ok(())
}

fn get_user_data(&mut self) -> Result<String, GreeterError> {
// The pwd::Passwd method is unsafe (but not labelled as such) due to using global state (libc pwent functions).
// To prevent issues, this should only be called once in the entire process space at a time
Expand Down
4 changes: 4 additions & 0 deletions dbus/com.system76.CosmicGreeter.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@
<allow send_destination="com.system76.CosmicGreeter"/>
<allow receive_sender="com.system76.CosmicGreeter"/>
</policy>
<policy user="cosmic-initial-setup">
<allow send_destination="com.system76.CosmicGreeter"/>
<allow receive_sender="com.system76.CosmicGreeter"/>
</policy>
</busconfig>
2 changes: 2 additions & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Depends:
bash,
cosmic-comp,
cosmic-greeter-daemon,
cosmic-initial-setup,
cosmic-session,
dbus,
${misc:Depends},
${shlibs:Depends}
Expand Down
7 changes: 2 additions & 5 deletions debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ export VENDOR ?= 1
dh $@

override_dh_auto_clean:
if ! ischroot && test "${VENDOR}" = "1"; then \
just vendor; \
fi
(! ischroot && test "${VENDOR}" = "1") && just vendor || true

override_dh_auto_build:
just build-vendored
test "${VENDOR}" = "1" && just build-vendored || just

override_dh_auto_install:
just rootdir=$(DESTDIR) install-debian
Expand All @@ -24,4 +22,3 @@ execute_after_dh_install:
override_dh_installsystemd:
dh_installsystemd -pcosmic-greeter --no-start -r cosmic-greeter.service
dh_installsystemd -pcosmic-greeter-daemon cosmic-greeter-daemon.service

9 changes: 6 additions & 3 deletions src/greeter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ static USERNAME_ID: LazyLock<iced::id::Id> = LazyLock::new(|| iced::id::Id::new(
default_service = "com.system76.CosmicGreeter",
default_path = "/com/system76/CosmicGreeter"
)]
trait Greeter {
pub trait Greeter {
async fn initial_setup_start(&mut self) -> Result<(), zbus::Error>;

async fn get_user_data(&self) -> Result<String, zbus::Error>;
}

async fn user_data_dbus() -> Result<Vec<UserData>, Box<dyn Error>> {
pub async fn user_data_dbus() -> Result<Vec<UserData>, Box<dyn Error>> {
let connection = Connection::system().await?;

// `dbus_proxy` macro creates `MyGreaterProxy` based on `Notifications` trait.
Expand All @@ -72,7 +74,7 @@ async fn user_data_dbus() -> Result<Vec<UserData>, Box<dyn Error>> {
Ok(user_datas)
}

fn user_data_fallback() -> Vec<UserData> {
pub fn user_data_fallback() -> Vec<UserData> {
// The pwd::Passwd method is unsafe (but not labelled as such) due to using global state (libc pwent functions).
/* unsafe */
{
Expand Down Expand Up @@ -932,6 +934,7 @@ impl cosmic::Application for App {
heartbeat_handle: None,
entering_name: false,
};

(app, common_task)
}

Expand Down
217 changes: 107 additions & 110 deletions src/greeter/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use super::{Message, SocketState};
use cosmic::iced::Subscription;
use futures_channel::mpsc::Sender;
use futures_util::SinkExt;
use greetd_ipc::codec::TokioCodec;
use std::sync::Arc;
Expand All @@ -17,131 +18,127 @@ pub fn subscription() -> Subscription<Message> {
Subscription::run_with_id(
std::any::TypeId::of::<GreetdSubscription>(),
cosmic::iced_futures::stream::channel(1, |mut sender| async move {
let (tx, mut rx) = mpsc::channel::<greetd_ipc::Request>(1);
let (tx, rx) = mpsc::channel::<greetd_ipc::Request>(1);
_ = sender.send(Message::GreetdChannel(tx)).await;
service(sender, rx).await
}),
)
}

let socket_path =
std::env::var_os("GREETD_SOCK").expect("GREETD_SOCK environment not set");
pub async fn service(mut sender: Sender<Message>, mut rx: mpsc::Receiver<greetd_ipc::Request>) {
let socket_path = std::env::var_os("GREETD_SOCK").expect("GREETD_SOCK environment not set");

let mut interval = tokio::time::interval(Duration::from_secs(1));
let mut interval = tokio::time::interval(Duration::from_secs(1));

loop {
_ = sender.send(Message::Reconnect).await;
loop {
_ = sender.send(Message::Reconnect).await;

let mut stream = match UnixStream::connect(&socket_path).await {
Ok(stream) => stream,
Err(why) => {
log::error!("greetd IPC socket connection failed: {why:?}");
_ = sender.send(Message::Socket(SocketState::Error(Arc::new(why))));
let mut stream = match UnixStream::connect(&socket_path).await {
Ok(stream) => stream,
Err(why) => {
log::error!("greetd IPC socket connection failed: {why:?}");
_ = sender.send(Message::Socket(SocketState::Error(Arc::new(why))));

break;
}
};
break;
}
};

_ = sender.send(Message::Socket(SocketState::Open)).await;
_ = sender.send(Message::Socket(SocketState::Open)).await;

while let Some(request) = rx.recv().await {
if let Err(why) = request.write_to(&mut stream).await {
log::error!("error writing to GREETD_SOCK stream: {why:?}");
break;
}
while let Some(request) = rx.recv().await {
if let Err(why) = request.write_to(&mut stream).await {
log::error!("error writing to GREETD_SOCK stream: {why:?}");
break;
}

match greetd_ipc::Response::read_from(&mut stream).await {
Ok(response) => {
match response {
greetd_ipc::Response::AuthMessage {
auth_message_type,
auth_message,
} => match auth_message_type {
greetd_ipc::AuthMessageType::Secret => {
_ = sender
.send(
common::Message::Prompt(
auth_message,
true,
Some(String::new()),
)
.into(),
)
.await;
}
greetd_ipc::AuthMessageType::Visible => {
_ = sender
.send(
common::Message::Prompt(
auth_message,
false,
Some(String::new()),
)
.into(),
)
.await;
}
//TODO: treat error type differently?
greetd_ipc::AuthMessageType::Info
| greetd_ipc::AuthMessageType::Error => {
_ = sender
.send(
common::Message::Prompt(auth_message, false, None)
.into(),
)
.await;
}
},
greetd_ipc::Response::Error {
error_type: _,
description,
} => {
//TODO: use error_type?
match request {
greetd_ipc::Request::CancelSession => {
// Do not send errors for cancel session to gui
log::warn!(
"error while cancelling session: {}",
description
);
match greetd_ipc::Response::read_from(&mut stream).await {
Ok(response) => {
match response {
greetd_ipc::Response::AuthMessage {
auth_message_type,
auth_message,
} => match auth_message_type {
greetd_ipc::AuthMessageType::Secret => {
_ = sender
.send(
common::Message::Prompt(
auth_message,
true,
Some(String::new()),
)
.into(),
)
.await;
}
greetd_ipc::AuthMessageType::Visible => {
_ = sender
.send(
common::Message::Prompt(
auth_message,
false,
Some(String::new()),
)
.into(),
)
.await;
}
//TODO: treat error type differently?
greetd_ipc::AuthMessageType::Info
| greetd_ipc::AuthMessageType::Error => {
_ = sender
.send(common::Message::Prompt(auth_message, false, None).into())
.await;
}
},
greetd_ipc::Response::Error {
error_type: _,
description,
} => {
//TODO: use error_type?
match request {
greetd_ipc::Request::CancelSession => {
// Do not send errors for cancel session to gui
log::warn!("error while cancelling session: {}", description);

// Reconnect to socket
break;
}
_ => {
_ = sender.send(Message::Error(description)).await;
}
}
// Reconnect to socket
break;
}
_ => {
_ = sender.send(Message::Error(description)).await;
}
greetd_ipc::Response::Success => match request {
greetd_ipc::Request::CreateSession { .. } => {
// User has no auth required, proceed to login
_ = sender.send(Message::Login).await;
}
greetd_ipc::Request::PostAuthMessageResponse { .. } => {
// All auth is completed, proceed to login
_ = sender.send(Message::Login).await;
}
greetd_ipc::Request::StartSession { .. } => {
// Session has been started, exit greeter
_ = sender.send(Message::Exit).await;
}
greetd_ipc::Request::CancelSession => {
log::info!("greetd IPC session canceled");
// Reconnect to socket
break;
}
},
}
}
Err(err) => {
log::error!("failed to read socket: {:?}", err);
break;
}
greetd_ipc::Response::Success => match request {
greetd_ipc::Request::CreateSession { .. } => {
// User has no auth required, proceed to login
_ = sender.send(Message::Login).await;
}
greetd_ipc::Request::PostAuthMessageResponse { .. } => {
// All auth is completed, proceed to login
_ = sender.send(Message::Login).await;
}
greetd_ipc::Request::StartSession { .. } => {
// Session has been started, exit greeter
_ = sender.send(Message::Exit).await;
}
greetd_ipc::Request::CancelSession => {
log::info!("greetd IPC session canceled");
// Reconnect to socket
break;
}
},
}
}

log::info!("reconnecting to greetd IPC socket");
interval.tick().await;
Err(err) => {
log::error!("failed to read socket: {:?}", err);
break;
}
}
}

futures_util::future::pending().await
}),
)
log::info!("reconnecting to greetd IPC socket");
interval.tick().await;
}

futures_util::future::pending().await
}
Loading