mirror of https://github.com/fairyglade/ly.git
Implement finger-print authentication
Many thanks to Kerigan for providing the original C code. This commit mainly posts his code to zig. Additional features provided by me are: - automatically checking every 100ms - request new login info for new usernames Co-authored-by: Kerigan <kerigancreighton@gmail.com>
This commit is contained in:
parent
87ceba4de8
commit
6bf9b928f2
96
src/auth.zig
96
src/auth.zig
|
@ -120,6 +120,102 @@ pub fn authenticate(config: Config, current_environment: Session.Environment, lo
|
|||
if (shared_err.readError()) |err| return err;
|
||||
}
|
||||
|
||||
pub fn fingerprintAuth(config: Config, login: [:0]const u8) !*interop.pam.pam_handle {
|
||||
// Open the PAM session
|
||||
var credentials = [_:null]?[*:0]const u8{ login, "" };
|
||||
|
||||
const conv = interop.pam.pam_conv{
|
||||
.conv = loginConv,
|
||||
.appdata_ptr = @ptrCast(&credentials),
|
||||
};
|
||||
var handle: ?*interop.pam.pam_handle = undefined;
|
||||
|
||||
var status = interop.pam.pam_start(config.service_name.ptr, null, &conv, &handle);
|
||||
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);
|
||||
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);
|
||||
|
||||
return handle.?;
|
||||
}
|
||||
|
||||
pub fn postFingerprintAuth(config: Config, desktop: Desktop, handle: *interop.pam.pam_handle, login: [:0]const u8) !void {
|
||||
var tty_buffer: [2]u8 = undefined;
|
||||
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
|
||||
const current_environment = desktop.environments.items[desktop.current];
|
||||
|
||||
// Set the XDG environment variables
|
||||
setXdgSessionEnv(current_environment.display_server);
|
||||
try setXdgEnv(tty_str, current_environment.xdg_session_desktop, current_environment.xdg_desktop_names orelse "");
|
||||
|
||||
var pwd: *interop.passwd = undefined;
|
||||
{
|
||||
defer interop.endpwent();
|
||||
|
||||
// Get password structure from username
|
||||
pwd = interop.getpwnam(login.ptr) orelse return error.GetPasswordNameFailed;
|
||||
}
|
||||
|
||||
// Set user shell if it hasn't already been set
|
||||
if (pwd.pw_shell[0] == 0) {
|
||||
interop.setusershell();
|
||||
pwd.pw_shell = interop.getusershell();
|
||||
interop.endusershell();
|
||||
}
|
||||
|
||||
var shared_err = try SharedError.init();
|
||||
defer shared_err.deinit();
|
||||
|
||||
child_pid = try std.posix.fork();
|
||||
if (child_pid == 0) {
|
||||
startSession(config, pwd, handle, current_environment) catch |e| {
|
||||
shared_err.writeError(e);
|
||||
std.process.exit(1);
|
||||
};
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
var entry: Utmp = std.mem.zeroes(Utmp);
|
||||
addUtmpEntry(&entry, pwd.pw_name, child_pid) catch {};
|
||||
|
||||
// If we receive SIGTERM, forward it to child_pid
|
||||
const act = std.posix.Sigaction{
|
||||
.handler = .{ .handler = &sessionSignalHandler },
|
||||
.mask = std.posix.empty_sigset,
|
||||
.flags = 0,
|
||||
};
|
||||
try std.posix.sigaction(std.posix.SIG.TERM, &act, null);
|
||||
|
||||
// Wait for the session to stop
|
||||
_ = std.posix.waitpid(child_pid, 0);
|
||||
|
||||
removeUtmpEntry(&entry);
|
||||
|
||||
try resetTerminal(pwd.pw_shell, config.term_reset_cmd);
|
||||
|
||||
// Close the PAM session
|
||||
var status = interop.pam.pam_close_session(handle, 0);
|
||||
if (status != 0) return pamDiagnose(status);
|
||||
|
||||
status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED);
|
||||
if (status != 0) return pamDiagnose(status);
|
||||
|
||||
status = interop.pam.pam_end(handle, status);
|
||||
if (status != 0) return pamDiagnose(status);
|
||||
|
||||
if (shared_err.readError()) |err| return err;
|
||||
}
|
||||
|
||||
fn startSession(
|
||||
config: Config,
|
||||
pwd: *interop.pwd.passwd,
|
||||
|
|
67
src/main.zig
67
src/main.zig
|
@ -39,6 +39,33 @@ pub fn signalHandler(i: c_int) callconv(.C) void {
|
|||
std.c.exit(i);
|
||||
}
|
||||
|
||||
// When setting the currentLogin you must deallocate the previous currentLogin first
|
||||
var currentLogin: ?[:0]const u8 = null;
|
||||
var asyncPamHandle: ?*interop.pam.pam_handle = null;
|
||||
|
||||
fn fingerprintLogin(config: Config, login: [:0]const u8) !void {
|
||||
// TODO: loop if there was an error
|
||||
const pamHandle = try auth.fingerprintAuth(config, login);
|
||||
if (currentLogin != null and !std.mem.eql(u8, std.mem.span(@as([*:0]const u8, currentLogin.?)), login)) {
|
||||
return;
|
||||
}
|
||||
asyncPamHandle = pamHandle;
|
||||
}
|
||||
|
||||
fn startFingerPrintLogin(allocator: std.mem.Allocator, config: Config, login: Text) !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(.{}, fingerprintLogin, .{
|
||||
config,
|
||||
login_text,
|
||||
});
|
||||
handle.detach();
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var shutdown = false;
|
||||
var restart = false;
|
||||
|
@ -278,6 +305,9 @@ pub fn main() !void {
|
|||
}
|
||||
}
|
||||
|
||||
try startFingerPrintLogin(allocator, config, login);
|
||||
defer allocator.free(currentLogin.?);
|
||||
|
||||
// Place components on the screen
|
||||
{
|
||||
buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
|
||||
|
@ -526,9 +556,9 @@ pub fn main() !void {
|
|||
_ = termbox.tb_present();
|
||||
}
|
||||
|
||||
var timeout: i32 = -1;
|
||||
var timeout: i32 = 100;
|
||||
|
||||
// Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for the event indefinitely instead
|
||||
// Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for 100ms instead
|
||||
if (animate and !animation_timed_out) {
|
||||
timeout = config.min_refresh_delta;
|
||||
|
||||
|
@ -556,9 +586,26 @@ pub fn main() !void {
|
|||
timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1);
|
||||
}
|
||||
|
||||
timeout = @min(timeout, 100);
|
||||
|
||||
const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout);
|
||||
|
||||
update = timeout != -1;
|
||||
if (asyncPamHandle) |handle| {
|
||||
if (auth.postFingerprintAuth(config, session, handle, currentLogin.?)) |_| {
|
||||
try info_line.setText(lang.logout);
|
||||
} else |err| {
|
||||
active_input = .password;
|
||||
try info_line.setText(getAuthErrorMsg(err, lang));
|
||||
try startFingerPrintLogin(allocator, config, login);
|
||||
}
|
||||
|
||||
asyncPamHandle = null;
|
||||
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios);
|
||||
termbox.tb_clear();
|
||||
termbox.tb_present();
|
||||
std.time.sleep(1000000000);
|
||||
}
|
||||
|
||||
if (event_error < 0 or event.type != termbox.TB_EVENT_KEY) continue;
|
||||
|
||||
|
@ -745,8 +792,20 @@ pub fn main() !void {
|
|||
switch (active_input) {
|
||||
.info_line => info_line.label.handle(&event, insert_mode),
|
||||
.session => session.label.handle(&event, insert_mode),
|
||||
.login => login.handle(&event, insert_mode) catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
.login => {
|
||||
const shouldFingerprint = switch (event.key) {
|
||||
termbox.TB_KEY_DELETE => true,
|
||||
termbox.TB_KEY_BACKSPACE, termbox.TB_KEY_BACKSPACE2 => true,
|
||||
else => event.ch > 31 and event.ch < 127,
|
||||
};
|
||||
|
||||
if (shouldFingerprint) {
|
||||
try startFingerPrintLogin(allocator, config, login);
|
||||
}
|
||||
|
||||
login.handle(&event, insert_mode) catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
};
|
||||
},
|
||||
.password => password.handle(&event, insert_mode) catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
|
|
Loading…
Reference in New Issue