Compare commits

...

4 Commits

Author SHA1 Message Date
AnErrupTion d6fc781192
Oops, forgot to allocate hehe
Signed-off-by: AnErrupTion <anerruption@disroot.org>
5 months ago
AnErrupTion 55ef58ad7e
Try to fix some more bugs
Signed-off-by: AnErrupTion <anerruption@disroot.org>
5 months ago
AnErrupTion 74556479db
Fix some bugs (hopefully)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
5 months ago
AnErrupTion c03e2e6b87
Implement more (untested) authentication code
Signed-off-by: AnErrupTion <anerruption@disroot.org>
5 months ago

@ -6,9 +6,20 @@ const Desktop = @import("tui/components/Desktop.zig");
const Text = @import("tui/components/Text.zig");
const Allocator = std.mem.Allocator;
// TODO
pub fn authenticate(allocator: Allocator, tty: u8, buffer: TerminalBuffer, desktop: Desktop, login: Text, password: Text) !void {
_ = buffer;
var login_conv_allocator: Allocator = undefined;
pub fn authenticate(
allocator: Allocator,
tty: u8,
desktop: Desktop,
login: Text,
password: *Text,
service_name: []const u8,
path: []const u8,
term_reset_cmd: []const u8,
wayland_cmd: []const u8,
) !void {
login_conv_allocator = allocator;
const uid = interop.getuid();
@ -19,17 +30,171 @@ pub fn authenticate(allocator: Allocator, tty: u8, buffer: TerminalBuffer, deskt
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
const current_environment = desktop.environments.items[desktop.current];
// Add XDG environment variables
// Set the XDG environment variables
setXdgSessionEnv(current_environment.display_server);
try setXdgEnv(allocator, tty_str, uid_str, current_environment.xdg_name);
// Open the PAM session
var credentials = [_][]const u8{ login.text.items, password.text.items };
const login_text_z = try allocator.dupeZ(u8, login.text.items);
defer allocator.free(login_text_z);
const password_text_z = try allocator.dupeZ(u8, password.text.items);
defer allocator.free(password_text_z);
var credentials = try allocator.allocSentinel([*c]const u8, 2, 0);
defer allocator.free(credentials);
credentials[0] = login_text_z.ptr;
credentials[1] = password_text_z.ptr;
const conv = interop.pam.pam_conv{
.conv = loginConv,
.appdata_ptr = @ptrCast(&credentials),
.appdata_ptr = @ptrCast(credentials.ptr),
};
_ = conv;
var handle: ?*interop.pam.pam_handle = undefined;
const service_name_z = try allocator.dupeZ(u8, service_name);
defer allocator.free(service_name_z);
var status = interop.pam.pam_start(service_name_z.ptr, null, &conv, &handle);
defer status = interop.pam.pam_end(handle, status);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
// Do the PAM routine
status = interop.pam.pam_authenticate(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
status = interop.pam.pam_acct_mgmt(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
status = interop.pam.pam_setcred(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
status = interop.pam.pam_open_session(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
// Clear the password
password.clear();
// Get password structure from username
const maybe_pwd = interop.getpwnam(login_text_z.ptr);
interop.endpwent();
if (maybe_pwd == null) return error.GetPasswordNameFailed;
const pwd = maybe_pwd.?;
// Set user shell if it hasn't already been set
if (pwd.pw_shell[0] == 0) {
interop.setusershell();
defer interop.endusershell();
const shell = interop.getusershell();
if (shell[0] != 0) {
var index: usize = 0;
while (true) : (index += 1) {
const char = shell[index];
pwd.pw_shell[index] = char;
if (char == 0) break;
}
}
}
// Restore the previous terminal mode
interop.termbox.tb_clear();
interop.termbox.tb_present();
interop.termbox.tb_shutdown();
const pid = std.c.fork();
if (pid == 0) {
// Set the user information
status = interop.initgroups(pwd.pw_name, pwd.pw_gid);
if (status != 0) return error.GroupInitializationFailed;
status = std.c.setgid(pwd.pw_gid);
if (status != 0) return error.SetUserGidFailed;
status = std.c.setuid(pwd.pw_uid);
if (status != 0) return error.SetUserUidFailed;
// Set up the environment (this clears the currently set one)
try initEnv(allocator, pwd, path);
// Reset the XDG environment variables from before
setXdgSessionEnv(current_environment.display_server);
try setXdgEnv(allocator, tty_str, uid_str, current_environment.xdg_name);
// Set the PAM variables
const pam_env_vars = interop.pam.pam_getenvlist(handle);
var index: usize = 0;
while (true) : (index += 1) {
const pam_env_var = pam_env_vars[index];
if (pam_env_var == null) break;
_ = interop.putenv(pam_env_var);
}
// Execute what the user requested
status = interop.chdir(pwd.pw_dir);
if (status != 0) return error.ChangeDirectoryFailed;
try resetTerminal(allocator, pwd.pw_shell, term_reset_cmd);
switch (current_environment.display_server) {
.wayland => try executeWaylandCmd(pwd.pw_shell, wayland_cmd, current_environment.cmd),
.shell => executeShellCmd(pwd.pw_shell),
.xinitrc, .x11 => {
// TODO
},
}
std.os.exit(0);
}
// TODO: Add UTMP entry
// Wait for the session to stop
_ = std.c.waitpid(pid, &status, 0);
// TODO: Remove UTMP entry
try resetTerminal(allocator, pwd.pw_shell, term_reset_cmd);
// Re-initialize termbox
_ = interop.termbox.tb_init();
_ = interop.termbox.tb_select_output_mode(interop.termbox.TB_OUTPUT_NORMAL);
// TODO: Reload the DE list on log out
// Close the PAM session
status = interop.pam.pam_close_session(handle, 0);
if (status != 0) return pamDiagnose(status);
status = interop.pam.pam_setcred(handle, 0);
if (status != 0) return pamDiagnose(status);
}
fn initEnv(allocator: Allocator, pwd: *interop.passwd, path: []const u8) !void {
const term = interop.getenv("TERM");
const lang = interop.getenv("LANG");
if (term[0] == 0) _ = interop.setenv("TERM", "linux", 1);
if (lang[0] == 0) _ = interop.setenv("LANG", "C", 1);
_ = interop.setenv("HOME", pwd.pw_dir, 1);
_ = interop.setenv("PWD", pwd.pw_dir, 1);
_ = interop.setenv("SHELL", pwd.pw_shell, 1);
_ = interop.setenv("USER", pwd.pw_name, 1);
_ = interop.setenv("LOGNAME", pwd.pw_name, 1);
const path_z = try allocator.dupeZ(u8, path);
defer allocator.free(path_z);
const status = interop.setenv("PATH", path_z, 1);
if (status != 0) return error.SetPathFailed;
}
fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
@ -44,22 +209,101 @@ fn setXdgEnv(allocator: Allocator, tty_str: [:0]u8, uid_str: [:0]u8, desktop_nam
const desktop_name_z = try allocator.dupeZ(u8, desktop_name);
defer allocator.free(desktop_name_z);
_ = interop.setenv("XDG_RUNTIME_DIR", uid_str, 0);
_ = interop.setenv("XDG_RUNTIME_DIR", uid_str.ptr, 0);
_ = interop.setenv("XDG_SESSION_CLASS", "user", 0);
_ = interop.setenv("XDG_SESSION_ID", "1", 0);
_ = interop.setenv("XDG_SESSION_DESKTOP", desktop_name_z, 0);
_ = interop.setenv("XDG_SESSION_DESKTOP", desktop_name_z.ptr, 0);
_ = interop.setenv("XDG_SEAT", "seat0", 0);
_ = interop.setenv("XDG_VTNR", tty_str, 0);
_ = interop.setenv("XDG_VTNR", tty_str.ptr, 0);
}
fn loginConv(
num_msg: c_int,
msg: [*][*]const interop.pam.pam_message,
resp: [*][*]const interop.pam.pam_response,
msg: ?[*]?*const interop.pam.pam_message,
resp: ?*?[*]interop.pam.pam_response,
appdata_ptr: ?*anyopaque,
) c_int {
_ = num_msg;
_ = msg;
_ = resp;
_ = appdata_ptr;
) callconv(.C) c_int {
const message_count: u32 = @intCast(num_msg);
const messages = msg.?;
const response = login_conv_allocator.alloc(interop.pam.pam_response, message_count) catch return interop.pam.PAM_BUF_ERR;
defer login_conv_allocator.free(response);
var status: c_int = undefined;
for (0..message_count) |i| set_credentials: {
switch (messages[i].?.msg_style) {
// TODO: Potentially cast appdata pointer before so we only do it once
// TODO: Verify if we need to do string duplication here
interop.pam.PAM_PROMPT_ECHO_ON => {
const appdata: ?*align(8) anyopaque = @alignCast(appdata_ptr);
const data: [*][:0]u8 = @ptrCast(appdata.?);
const username = data[0];
response[i].resp = username;
},
interop.pam.PAM_PROMPT_ECHO_OFF => {
const appdata: ?*align(8) anyopaque = @alignCast(appdata_ptr);
const data: [*][:0]u8 = @ptrCast(appdata.?);
const password = data[1];
response[i].resp = password;
},
interop.pam.PAM_ERROR_MSG => {
status = interop.pam.PAM_CONV_ERR;
break :set_credentials;
},
else => {},
}
}
if (status == interop.pam.PAM_SUCCESS) resp.?.* = response.ptr;
return status;
}
fn resetTerminal(allocator: Allocator, shell: [*:0]const u8, term_reset_cmd: []const u8) !void {
const term_reset_cmd_z = try allocator.dupeZ(u8, term_reset_cmd);
defer allocator.free(term_reset_cmd_z);
const pid = std.c.fork();
if (pid == 0) {
_ = interop.execl(shell, shell, "-c\x00".ptr, term_reset_cmd_z.ptr, @as([*c]const u8, 0));
std.os.exit(0);
}
var status: c_int = undefined;
_ = std.c.waitpid(pid, &status, 0);
}
fn executeWaylandCmd(shell: [*:0]const u8, wayland_cmd: []const u8, desktop_cmd: []const u8) !void {
var cmd_buffer = std.mem.zeroes([1024]u8);
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ wayland_cmd, desktop_cmd });
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0));
}
fn executeShellCmd(shell: [*:0]const u8) void {
_ = interop.execl(shell, shell, @as([*c]const u8, 0));
}
fn pamDiagnose(status: c_int) anyerror {
return switch (status) {
interop.pam.PAM_ACCT_EXPIRED => return error.PamAccountExpired,
interop.pam.PAM_AUTH_ERR => return error.PamAuthError,
interop.pam.PAM_AUTHINFO_UNAVAIL => return error.PamAuthInfoUnavailable,
interop.pam.PAM_BUF_ERR => return error.PamBufferError,
interop.pam.PAM_CRED_ERR => return error.PamCredentialsError,
interop.pam.PAM_CRED_EXPIRED => return error.PamCredentialsExpired,
interop.pam.PAM_CRED_INSUFFICIENT => return error.PamCredentialsInsufficient,
interop.pam.PAM_CRED_UNAVAIL => return error.PamCredentialsUnavailable,
interop.pam.PAM_MAXTRIES => return error.PamMaximumTries,
interop.pam.PAM_NEW_AUTHTOK_REQD => return error.PamNewAuthTokenRequired,
interop.pam.PAM_PERM_DENIED => return error.PamPermissionDenied,
interop.pam.PAM_SESSION_ERR => return error.PamSessionError,
interop.pam.PAM_SYSTEM_ERR => return error.PamSystemError,
interop.pam.PAM_USER_UNKNOWN => return error.PamUserUnknown,
else => return error.PamAbort,
};
}

@ -12,6 +12,7 @@ pub const pam = @cImport({
pub const c_size = u64;
pub const c_uid = u32;
pub const c_gid = u32;
pub const c_time = c_long;
pub const tm = extern struct {
tm_sec: c_int,
@ -24,6 +25,16 @@ pub const tm = extern struct {
tm_yday: c_int,
tm_isdst: c_int,
};
pub const passwd = extern struct {
pw_name: [*:0]u8,
pw_passwd: [*:0]u8,
pw_uid: c_uid,
pw_gid: c_gid,
pw_gecos: [*:0]u8,
pw_dir: [*:0]u8,
pw_shell: [*:0]u8,
};
pub const _POSIX_HOST_NAME_MAX: c_int = 0xFF;
pub const _SC_HOST_NAME_MAX: c_int = 0xB4;
@ -50,7 +61,17 @@ pub extern "c" fn time(second: ?*c_time) c_time;
pub extern "c" fn localtime(timer: *const c_time) *tm;
pub extern "c" fn strftime(str: [*:0]u8, maxsize: c_size, format: [*:0]const u8, timeptr: *const tm) c_size;
pub extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int;
pub extern "c" fn getenv(name: [*:0]const u8) [*:0]u8;
pub extern "c" fn putenv(name: [*:0]u8) c_int;
pub extern "c" fn getuid() c_uid;
pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
pub extern "c" fn endpwent() void;
pub extern "c" fn setusershell() void;
pub extern "c" fn getusershell() [*:0]u8;
pub extern "c" fn endusershell() void;
pub extern "c" fn initgroups(user: [*:0]const u8, group: c_gid) c_int;
pub extern "c" fn chdir(path: [*:0]const u8) c_int;
pub extern "c" fn execl(path: [*:0]const u8, arg: [*:0]const u8, ...) c_int;
pub fn getHostName(allocator: Allocator) !struct {
buffer: []u8,

@ -80,6 +80,9 @@ pub fn main() !void {
}
// Initialize information line with host name
var got_host_name = false;
var host_name_buffer: []u8 = undefined;
get_host_name: {
const host_name_struct = interop.getHostName(allocator) catch |err| {
if (err == error.CannotGetHostName) {
@ -89,8 +92,9 @@ pub fn main() !void {
}
break :get_host_name;
};
defer allocator.free(host_name_struct.buffer);
got_host_name = true;
host_name_buffer = host_name_struct.buffer;
info_line = host_name_struct.slice;
}
@ -134,16 +138,13 @@ pub fn main() !void {
const reader = file.reader();
const username_length = try reader.readIntLittle(u64);
const username_buffer = try file.readToEndAlloc(allocator, username_length);
const username_buffer = try allocator.alloc(u8, username_length);
defer allocator.free(username_buffer);
_ = try reader.read(username_buffer);
const current_desktop = try reader.readIntLittle(u64);
const load_buffer = try file.readToEndAlloc(allocator, config.ly.max_login_len + 5);
defer allocator.free(load_buffer);
if (username_buffer.len > 0) {
try login.text.appendSlice(username_buffer);
login.end = username_buffer.len;
@ -470,7 +471,17 @@ pub fn main() !void {
var has_error = false;
auth.authenticate(allocator, config.ly.tty, buffer, desktop, login, password) catch {
auth.authenticate(
allocator,
config.ly.tty,
desktop,
login,
&password,
config.ly.service_name,
config.ly.path,
config.ly.term_reset_cmd,
config.ly.wayland_cmd,
) catch {
has_error = true;
auth_fails += 1;
active_input = .password;
@ -499,6 +510,8 @@ pub fn main() !void {
}
}
if (got_host_name) allocator.free(host_name_buffer);
if (shutdown) {
return std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.ly.shutdown_cmd });
} else if (restart) {

@ -103,7 +103,7 @@ pub fn addEnvironmentWithBuffer(self: *Desktop, entry_buffer: []u8, name: []cons
}
pub fn crawl(self: *Desktop, path: []const u8, display_server: DisplayServer) !void {
var directory = try std.fs.openDirAbsolute(path, .{});
var directory = std.fs.openDirAbsolute(path, .{}) catch return;
defer directory.close();
var iterable_directory = try std.fs.openIterableDirAbsolute(path, .{});

Loading…
Cancel
Save