Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for fingerprint login #654

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions res/pam.d/ly
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#%PAM-1.0

auth sufficient pam_unix.so try_first_pass likeauth nullok # empty-password will not pass this, but will not fail causing the next line to get executed
-auth sufficient pam_fprintd.so # We do not want to get errors when pam_fprintd.so is not present
auth include login
-auth optional pam_gnome_keyring.so
-auth optional pam_kwallet5.so
Expand Down
101 changes: 87 additions & 14 deletions src/auth.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,66 @@ const utmp = interop.utmp;
const Utmp = utmp.utmpx;
const SharedError = @import("SharedError.zig");

// When setting the currentLogin you must deallocate the previous currentLogin first
pub var currentLogin: ?[:0]const u8 = null;
pub var asyncPamHandle: ?*interop.pam.pam_handle = null;
pub var current_environment: ?Session.Environment = null;

fn environment_equals(e0: Session.Environment, e1: Session.Environment) bool {
if (!std.mem.eql(u8, e0.cmd, e1.cmd)) {
return false;
}
if (!std.mem.eql(u8, e0.name, e1.name)) {
return false;
}
if (!std.mem.eql(u8, e0.specifier, e1.specifier)) {
return false;
}
if (!(e0.xdg_desktop_names == null and e1.xdg_desktop_names == null) or
(e0.xdg_desktop_names != null and e1.xdg_desktop_names != null and !std.mem.eql(u8, e0.xdg_desktop_names.?, e1.xdg_desktop_names.?)))
{
return false;
}
if (!(e0.xdg_session_desktop == null and e1.xdg_session_desktop == null) or
(e0.xdg_session_desktop != null and e1.xdg_session_desktop != null and !std.mem.eql(u8, e0.xdg_session_desktop.?, e1.xdg_session_desktop.?)))
{
return false;
}
if (e0.display_server != e1.display_server) {
return false;
}
return true;
}

pub fn automaticLogin(config: Config, login: [:0]const u8, environment: Session.Environment, wakesem: *std.Thread.Semaphore) !void {
while (asyncPamHandle == null and currentLogin != null and std.mem.eql(u8, currentLogin.?, login)) {
if (authenticate(config, login, "", environment)) |handle| {
if (currentLogin != null and !std.mem.eql(u8, currentLogin.?, login) and environment_equals(current_environment.?, environment)) {
return;
}
asyncPamHandle = handle;
wakesem.post();
return;
} else |_| {}
}
}

pub fn startAutomaticLogin(allocator: std.mem.Allocator, config: Config, login: Text, environment: Session.Environment, wakesem: *std.Thread.Semaphore) !void {
if (currentLogin) |clogin| {
allocator.free(clogin);
currentLogin = null;
}
const login_text = try allocator.dupeZ(u8, login.text.items);
currentLogin = login_text;
var handle = try std.Thread.spawn(.{}, automaticLogin, .{
config,
login_text,
environment,
wakesem,
});
handle.detach();
}

var xorg_pid: std.posix.pid_t = 0;
pub fn xorgSignalHandler(i: c_int) callconv(.C) void {
if (xorg_pid > 0) _ = std.c.kill(xorg_pid, i);
Expand All @@ -23,16 +83,20 @@ pub fn sessionSignalHandler(i: c_int) callconv(.C) void {
if (child_pid > 0) _ = std.c.kill(child_pid, i);
}

pub fn authenticate(config: Config, current_environment: Session.Environment, login: [:0]const u8, password: [:0]const u8) !void {
var tty_buffer: [3]u8 = undefined;
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});

pub fn authenticate(
config: Config,
login: [:0]const u8,
password: [:0]const u8,
environment: Session.Environment,
) !*interop.pam.pam_handle {
var pam_tty_buffer: [6]u8 = undefined;
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{config.tty});
var tty_buffer: [2]u8 = undefined;
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});

// Set the XDG environment variables
setXdgSessionEnv(current_environment.display_server);
try setXdgEnv(tty_str, current_environment.xdg_session_desktop orelse "", current_environment.xdg_desktop_names orelse "");
setXdgSessionEnv(environment.display_server);
try setXdgEnv(tty_str, environment.xdg_session_desktop orelse "", environment.xdg_desktop_names orelse "");

// Open the PAM session
var credentials = [_:null]?[*:0]const u8{ login, password };
Expand All @@ -43,28 +107,37 @@ pub fn authenticate(config: Config, current_environment: Session.Environment, lo
};
var handle: ?*interop.pam.pam_handle = undefined;

// Do the PAM routine
var status = interop.pam.pam_start(config.service_name, null, &conv, &handle);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer _ = interop.pam.pam_end(handle, status);
errdefer _ = interop.pam.pam_end(handle, status);

// Set PAM_TTY as the current TTY. This is required in case it isn't being set by another PAM module
status = interop.pam.pam_set_item(handle, interop.pam.PAM_TTY, pam_tty_str.ptr);
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, interop.pam.PAM_ESTABLISH_CRED);
errdefer _ = interop.pam.pam_end(handle, status);

if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
return handle.?;
}

pub fn finaliseAuth(config: Config, environment: Session.Environment, handle: ?*interop.pam.pam_handle, login: [:0]const u8) !void {
var status: c_int = undefined;
defer status = interop.pam.pam_end(handle, status);
defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED);

// Open the PAM session
status = interop.pam.pam_open_session(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer status = interop.pam.pam_close_session(handle, 0);
defer status = interop.pam.pam_close_session(handle, status);

var pwd: *interop.pwd.passwd = undefined;
{
Expand All @@ -86,7 +159,7 @@ pub fn authenticate(config: Config, current_environment: Session.Environment, lo

child_pid = try std.posix.fork();
if (child_pid == 0) {
startSession(config, pwd, handle, current_environment) catch |e| {
startSession(config, pwd, handle, environment) catch |e| {
shared_err.writeError(e);
std.process.exit(1);
};
Expand Down Expand Up @@ -124,7 +197,7 @@ fn startSession(
config: Config,
pwd: *interop.pwd.passwd,
handle: ?*interop.pam.pam_handle,
current_environment: Session.Environment,
environment: Session.Environment,
) !void {
if (builtin.os.tag == .freebsd) {
// FreeBSD has initgroups() in unistd
Expand Down Expand Up @@ -156,13 +229,13 @@ fn startSession(
std.posix.chdirZ(pwd.pw_dir.?) catch return error.ChangeDirectoryFailed;

// Execute what the user requested
switch (current_environment.display_server) {
.wayland => try executeWaylandCmd(pwd.pw_shell.?, config, current_environment.cmd),
switch (environment.display_server) {
.wayland => try executeWaylandCmd(pwd.pw_shell.?, config, environment.cmd),
.shell => try executeShellCmd(pwd.pw_shell.?, config),
.xinitrc, .x11 => if (build_options.enable_x11_support) {
var vt_buf: [5]u8 = undefined;
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{config.tty});
try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, config, current_environment.cmd, vt);
try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, config, environment.cmd, vt);
},
}
}
Expand Down
Loading