Skip to content

Commit 1de8809

Browse files
Victorjdonszelmann
Victor
andcommitted
First working version* (#1)
* hopefully it works now * actually spawn startx * print pwd * print pwd * print pwd * whoami-temp * added suid * added suid * added print user * fixed group ids * inc chvt version * flipped setgid/setuid order * chvt(1) after setuid * change X to vt7 * change X to vt7 * chown * chown * removed chvt * removed startx vt7 * removed chown * restructure * added print xauth * starting X * updated get free display to use path exists * fuck commit messages * yeet * fuck * yeet * yeet * 7 * groups * groups * chown * nochown * print * added xdg envvars * added env * lang * shovel * it works! 🎉 * hi * hey Co-authored-by: Jonathan Donszelmann <[email protected]>
1 parent 6a3ddc5 commit 1de8809

File tree

10 files changed

+517
-111
lines changed

10 files changed

+517
-111
lines changed

Cargo.toml

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "CaDMium"
2+
name = "cadmium"
33
version = "0.1.0"
44
authors = ["Victor Roest <[email protected]>"]
55
edition = "2018"
@@ -9,4 +9,8 @@ logind-dbus="0.1.1"
99
rpassword="4.0.1"
1010
pam="0.7.0"
1111
pam-sys="0.5.6"
12-
nix = "0.15.0"
12+
nix="0.15.0"
13+
users="0.9.1"
14+
chvt="0.2.0"
15+
rand="0.7.2"
16+
xcb="0.9.0"

cadmium.service

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
[Unit]
22
Description=CaDMium
3-
After=systemd-user-sessions.service
4-
5-
After=systemd-user-sessions.service [email protected] plymouth-quit.service systemd-logind.service
6-
3+
After=systemd-user-sessions.service plymouth-quit-wait.service
4+
75

86
[Service]
97
ExecStart=/usr/bin/cadmium
8+
StandardInput=tty
9+
TTYPath=/dev/tty2
10+
TTYReset=yes
11+
TTYVHangup=yes
12+
Type=idle
1013

1114
[Install]
1215
Alias=display-manager.service

res/xsetup.sh

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#! /bin/sh
2+
# Xsession - run as user
3+
# Copyright (C) 2016 Pier Luigi Fiorini <[email protected]>
4+
5+
# This file is extracted from kde-workspace (kdm/kfrontend/genkdmconf.c)
6+
# Copyright (C) 2001-2005 Oswald Buddenhagen <[email protected]>
7+
8+
# Note that the respective logout scripts are not sourced.
9+
case $SHELL in
10+
*/bash)
11+
[ -z "$BASH" ] && exec $SHELL $0 "$@"
12+
set +o posix
13+
[ -f /etc/profile ] && . /etc/profile
14+
if [ -f $HOME/.bash_profile ]; then
15+
. $HOME/.bash_profile
16+
elif [ -f $HOME/.bash_login ]; then
17+
. $HOME/.bash_login
18+
elif [ -f $HOME/.profile ]; then
19+
. $HOME/.profile
20+
fi
21+
;;
22+
*/zsh)
23+
[ -z "$ZSH_NAME" ] && exec $SHELL $0 "$@"
24+
[ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc
25+
zhome=${ZDOTDIR:-$HOME}
26+
# zshenv is always sourced automatically.
27+
[ -f $zdir/zprofile ] && . $zdir/zprofile
28+
[ -f $zhome/.zprofile ] && . $zhome/.zprofile
29+
[ -f $zdir/zlogin ] && . $zdir/zlogin
30+
[ -f $zhome/.zlogin ] && . $zhome/.zlogin
31+
emulate -R sh
32+
;;
33+
*/csh|*/tcsh)
34+
# [t]cshrc is always sourced automatically.
35+
# Note that sourcing csh.login after .cshrc is non-standard.
36+
xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
37+
$SHELL -c "if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c 'export -p' >! $xsess_tmp"
38+
. $xsess_tmp
39+
rm -f $xsess_tmp
40+
;;
41+
*/fish)
42+
[ -f /etc/profile ] && . /etc/profile
43+
xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
44+
$SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp"
45+
. $xsess_tmp
46+
rm -f $xsess_tmp
47+
;;
48+
*) # Plain sh, ksh, and anything we do not know.
49+
[ -f /etc/profile ] && . /etc/profile
50+
[ -f $HOME/.profile ] && . $HOME/.profile
51+
;;
52+
esac
53+
54+
[ -f /etc/xprofile ] && . /etc/xprofile
55+
[ -f $HOME/.xprofile ] && . $HOME/.xprofile
56+
57+
# run all system xinitrc shell scripts.
58+
if [ -d /etc/X11/xinit/xinitrc.d ]; then
59+
for i in /etc/X11/xinit/xinitrc.d/* ; do
60+
if [ -x "$i" ]; then
61+
. "$i"
62+
fi
63+
done
64+
fi
65+
66+
# Load Xsession scripts
67+
# OPTIONFILE, USERXSESSION, USERXSESSIONRC and ALTUSERXSESSION are required
68+
# by the scripts to work
69+
xsessionddir="/etc/X11/Xsession.d"
70+
OPTIONFILE=/etc/X11/Xsession.options
71+
USERXSESSION=$HOME/.xsession
72+
USERXSESSIONRC=$HOME/.xsessionrc
73+
ALTUSERXSESSION=$HOME/.Xsession
74+
75+
if [ -d "$xsessionddir" ]; then
76+
for i in `ls $xsessionddir`; do
77+
script="$xsessionddir/$i"
78+
echo "Loading X session script $script"
79+
if [ -r "$script" -a -f "$script" ] && expr "$i" : '^[[:alnum:]_-]\+$' > /dev/null; then
80+
. "$script"
81+
fi
82+
done
83+
fi
84+
85+
if [ -d /etc/X11/Xresources ]; then
86+
for i in /etc/X11/Xresources/*; do
87+
[ -f $i ] && xrdb -merge $i
88+
done
89+
elif [ -f /etc/X11/Xresources ]; then
90+
xrdb -merge /etc/X11/Xresources
91+
fi
92+
[ -f $HOME/.Xresources ] && xrdb -merge $HOME/.Xresources
93+
94+
if [ -f "$USERXSESSION" ]; then
95+
. "$USERXSESSION"
96+
fi
97+
98+
if [ -z "$*" ]; then
99+
exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session."
100+
else
101+
exec $@
102+
fi

src/askpass/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use std::error::Error;
2+
use core::fmt;
3+
use fmt::Debug;
4+
5+
pub mod simple;
6+
7+
8+
#[derive(Debug)]
9+
pub enum AskPassError {
10+
11+
}
12+
impl Error for AskPassError {}
13+
impl fmt::Display for AskPassError {
14+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15+
<dyn Debug>::fmt(self, f)
16+
}
17+
}
18+
19+
pub struct UserInfo {
20+
pub username: String,
21+
pub password: String,
22+
}
23+

src/askpass/simple.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use crate::askpass::UserInfo;
2+
use std::io;
3+
use std::io::Write;
4+
use rpassword::read_password;
5+
6+
pub fn simple_get_credentials() -> io::Result<UserInfo> {
7+
8+
println!("Login:");
9+
print!("username: ");
10+
io::stdout().flush().ok().expect("Could not flush stdout");
11+
12+
13+
let mut username = String::new();
14+
io::stdin().read_line(&mut username)?;
15+
username.truncate(username.trim_end().len());
16+
17+
print!("password (hidden): ");
18+
io::stdout().flush().ok().expect("Could not flush stdout");
19+
20+
let password = read_password()?;
21+
22+
Ok(UserInfo {
23+
username,
24+
password
25+
})
26+
}

src/dbus.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use std::process::Command;
2+
use std::env;
3+
4+
pub fn start_dbus() {
5+
let dbus_output = Command::new("/usr/bin/dbus-launch").output().expect("Couldn't start DBus");
6+
let results = String::from_utf8_lossy(&dbus_output.stdout);
7+
for variable in results.lines(){
8+
let line: Vec<&str> = variable.splitn(2, "=").collect();
9+
10+
env::set_var(
11+
line.get(0).expect("Couldn't read dbus-launch return value"),
12+
line.get(1).expect("Couldn't read dbus-launch return value")
13+
);
14+
}
15+
16+
}

src/error.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use core::fmt;
2+
use std::error::Error;
3+
use crate::askpass::AskPassError;
4+
use fmt::Debug;
5+
use crate::x::XError;
6+
7+
#[derive(Debug)]
8+
pub enum ErrorKind {
9+
InhibitationError,
10+
IoError,
11+
AuthenticationError,
12+
DBusError,
13+
SessionError,
14+
AskPassError(AskPassError),
15+
XError(XError),
16+
17+
}
18+
impl Error for ErrorKind {}
19+
impl fmt::Display for ErrorKind {
20+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21+
<dyn Debug>::fmt(self, f)
22+
}
23+
}
24+

src/login.rs

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use crate::askpass::UserInfo;
2+
use crate::error::ErrorKind;
3+
use pam_sys::PamReturnCode;
4+
use crate::askpass::simple::simple_get_credentials;
5+
use logind_dbus::LoginManager;
6+
use pam::Authenticator;
7+
use users::get_user_by_name;
8+
use std::env;
9+
10+
fn xdg(tty: u32, _uid: u32) {
11+
// let user = format!("/run/user/{}", uid);
12+
// env::set_var("XDG_RUNTIME_DIR", format!("/run/user/{}", uid));
13+
14+
env::set_var("XDG_SESSION_CLASS", "greeter");
15+
16+
//TODO: should be seat{display}. might need to move to a place where we actually know the display.
17+
env::set_var("XDG_SEAT", "seat0");
18+
19+
env::set_var("XDG_VTNR", format!("{}", tty));
20+
env::set_var("XDG_SESSION_ID", "1");
21+
22+
// temp
23+
// env::set_var("DBUS_SESSION_BUS_ADDRESS", format!("unix:path=/run/user/{}/bus", uid));
24+
25+
env::set_var("XDG_SESSION_TYPE", "tty");
26+
}
27+
28+
pub fn authenticate(tty: u32) -> Result<(UserInfo, LoginManager), ErrorKind>{
29+
let logind_manager = LoginManager::new().expect("Could not get logind-manager");
30+
31+
let mut authenticator = Authenticator::with_password("login")
32+
.expect("Failed to init PAM client.");
33+
34+
// block where we inhibit suspend
35+
let login_info= {
36+
let _suspend_lock = logind_manager.connect()
37+
.inhibit_suspend("Cadmium", "login")
38+
.map_err(|_| ErrorKind::InhibitationError)?;
39+
40+
// TODO: change to generic get credentials
41+
let login_info = simple_get_credentials().map_err(|_| ErrorKind::IoError)?;
42+
43+
let user= get_user_by_name(&login_info.username).expect("Couldn't find username");
44+
xdg(tty as u32, user.uid());
45+
46+
authenticator.get_handler().set_credentials(login_info.username.clone(), login_info.password);
47+
48+
match authenticator.authenticate() {
49+
Err(e)=> {
50+
if e.to_string() == PamReturnCode::PERM_DENIED.to_string() {
51+
println!("Permission denied.");
52+
} else if e.to_string() == PamReturnCode::AUTH_ERR.to_string() {
53+
#[cfg(debug_assertions)]
54+
dbg!("AUTH_ERR");
55+
56+
println!("Authentication error.");
57+
} else if e.to_string() == PamReturnCode::USER_UNKNOWN.to_string() {
58+
#[cfg(debug_assertions)]
59+
dbg!("USER_UNKNOWN");
60+
61+
println!("Authentication error.");
62+
} else if e.to_string() == PamReturnCode::MAXTRIES.to_string() {
63+
println!("Maximum login attempts reached.");
64+
} else if e.to_string() == PamReturnCode::CRED_UNAVAIL.to_string() {
65+
println!("Underlying authentication service can not retrieve user credentials unavailable.");
66+
} else if e.to_string() == PamReturnCode::ACCT_EXPIRED.to_string() {
67+
println!("Account expired");
68+
} else if e.to_string() == PamReturnCode::CRED_EXPIRED.to_string() {
69+
println!("Account expired");
70+
} else if e.to_string() == PamReturnCode::TRY_AGAIN.to_string() {
71+
println!("PAM fucked up, please try again");
72+
} else if e.to_string() == PamReturnCode::ABORT.to_string() {
73+
println!("user's authentication token has expired");
74+
} else if e.to_string() == PamReturnCode::INCOMPLETE.to_string() {
75+
println!("We fucked up, please try again");
76+
} else {
77+
println!("A PAM error occurred: {}", e);
78+
}
79+
80+
return Err(ErrorKind::AuthenticationError)
81+
}
82+
Ok(_) => ()
83+
};
84+
85+
// logind_manager.register().map_err(|_| ErrorKind::DBusError)?;
86+
87+
(
88+
UserInfo{
89+
username: login_info.username,
90+
password: String::new()
91+
},
92+
logind_manager
93+
)
94+
};
95+
96+
authenticator.open_session().map_err(|_| ErrorKind::SessionError)?;
97+
98+
Ok(login_info)
99+
}

0 commit comments

Comments
 (0)