mirror of
https://github.com/javalsai/lidm.git
synced 2026-01-13 00:50:00 +01:00
feat: add support for fido yubikeys (#89)
* add support for fido keybind * add to themes * fix clang format * Update ui.c * docs: add misc stuff about the yubikey --------- Co-authored-by: javalsai <jvssxxi@gmail.com>
This commit is contained in:
@@ -27,10 +27,11 @@ kmscon -l --vt /dev/tty7 --font-name "Cascadia Code" -- /usr/bin/lidm
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Simple as C, you only need a C compiler and standard unix libraries to build this.
|
- Simple as C, meant to depend only on standard libc and basic unix system headers.
|
||||||
- Fully customizable, from strings, including action keys, to colors (I hope you know ansi escape codes)
|
- Fully customizable: ALL strings, colors (with its ANSI keys) and most behavior.
|
||||||
- Automatically detects xorg and wayland sessions, plus allowing to launch the default user shell (if enabled in config)
|
- Xorg[\*](https://github.com/javalsai/lidm/issues/79) and wayland sessions, while supporting the default user shell (if enabled in config)
|
||||||
- Starts with many init systems (systemd, dinit, runit, openrc and s6).
|
- Init agnostinc (systemd, dinit, runit, openrc and s6).
|
||||||
|
- Supports [fido yubikeys](./docs/yubikey.md) (via pam_u2f).
|
||||||
|
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
|
|
||||||
|
|||||||
@@ -69,13 +69,13 @@ Characters for the corners of the box (ctl = corner top left, cbr = corner botto
|
|||||||
.SS functions
|
.SS functions
|
||||||
All these are of type \fBKEY\fP.
|
All these are of type \fBKEY\fP.
|
||||||
.TP
|
.TP
|
||||||
\fBpoweroff, reboot, refresh\fP
|
\fBpoweroff, reboot, fido, refresh\fP
|
||||||
Function key to use for such action.
|
Function key to use for such action.
|
||||||
|
|
||||||
.SS strings
|
.SS strings
|
||||||
Display strings to use for some elements.
|
Display strings to use for some elements.
|
||||||
.TP
|
.TP
|
||||||
\fBf_poweroff, f_reboot, f_refresh\fP
|
\fBf_poweroff, f_reboot, f_fido, f_refresh\fP
|
||||||
Text displayed to name such functions at the bottom of the screen.
|
Text displayed to name such functions at the bottom of the screen.
|
||||||
.TP
|
.TP
|
||||||
\fBe_user, e_passwd\fP
|
\fBe_user, e_passwd\fP
|
||||||
|
|||||||
30
docs/yubikey.md
Normal file
30
docs/yubikey.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Yubikeys
|
||||||
|
|
||||||
|
Quick reference explaining how yubikeys work for now.
|
||||||
|
|
||||||
|
# Enable
|
||||||
|
|
||||||
|
Yubikeys are disabled by default, to enable them activate a keybinding for it (`[functions] fido`) in the config file.
|
||||||
|
|
||||||
|
Note that pressing this configured keybinding has no difference from trying to log in with an empty password, there's virtually no difference.
|
||||||
|
|
||||||
|
`pam_u2f` must be configured with a registered key (`pamu2fcfg`).
|
||||||
|
|
||||||
|
# Extra
|
||||||
|
|
||||||
|
All my yubikey knowledge comes from the [pr that implemented this](https://github.com/javalsai/lidm/pull/89), please refer to it for extra details. Contributions to this documentation are welcome (explaining more in detail, potential issues... really anything that improves this).
|
||||||
|
|
||||||
|
Allegedly this pam module configuration should work:
|
||||||
|
|
||||||
|
```pam
|
||||||
|
#%PAM-1.0
|
||||||
|
|
||||||
|
auth sufficient pam_u2f.so cue
|
||||||
|
auth requisite pam_nologin.so
|
||||||
|
auth include system-local-login
|
||||||
|
account include system-local-login
|
||||||
|
session include system-local-login
|
||||||
|
password include system-local-login
|
||||||
|
```
|
||||||
|
|
||||||
|
Also, I recommend giving the [arch wiki](https://wiki.archlinux.org/title/YubiKey) a read anyways.
|
||||||
@@ -92,6 +92,7 @@ BUILD(chars, CHARS, TABLE_CHARS);
|
|||||||
#define TABLE_FUNCTIONS(F, name) \
|
#define TABLE_FUNCTIONS(F, name) \
|
||||||
F(enum keys, poweroff, KEY, F1, name) \
|
F(enum keys, poweroff, KEY, F1, name) \
|
||||||
F(enum keys, reboot, KEY, F2, name) \
|
F(enum keys, reboot, KEY, F2, name) \
|
||||||
|
F(enum keys, fido, KEY, NONE, name) \
|
||||||
F(enum keys, refresh, KEY, F5, name)
|
F(enum keys, refresh, KEY, F5, name)
|
||||||
|
|
||||||
BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS);
|
BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS);
|
||||||
@@ -99,6 +100,7 @@ BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS);
|
|||||||
#define TABLE_STRINGS(F, name) \
|
#define TABLE_STRINGS(F, name) \
|
||||||
F(char* NNULLABLE, f_poweroff, STRING, "poweroff", name) \
|
F(char* NNULLABLE, f_poweroff, STRING, "poweroff", name) \
|
||||||
F(char* NNULLABLE, f_reboot, STRING, "reboot", name) \
|
F(char* NNULLABLE, f_reboot, STRING, "reboot", name) \
|
||||||
|
F(char* NNULLABLE, f_fido, STRING, "fido", name) \
|
||||||
F(char* NNULLABLE, f_refresh, STRING, "refresh", name) \
|
F(char* NNULLABLE, f_refresh, STRING, "refresh", name) \
|
||||||
F(char* NNULLABLE, e_user, STRING, "user", name) \
|
F(char* NNULLABLE, e_user, STRING, "user", name) \
|
||||||
F(char* NNULLABLE, e_passwd, STRING, "password", name) \
|
F(char* NNULLABLE, e_passwd, STRING, "password", name) \
|
||||||
|
|||||||
@@ -32,9 +32,11 @@ enum keys {
|
|||||||
END,
|
END,
|
||||||
PAGE_UP,
|
PAGE_UP,
|
||||||
PAGE_DOWN,
|
PAGE_DOWN,
|
||||||
|
NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* const KEY_NAMES[] = {
|
static const char* const KEY_NAMES[] = {
|
||||||
|
[NONE] = "NONE",
|
||||||
[ESC] = "ESC",
|
[ESC] = "ESC",
|
||||||
[F1] = "F1",
|
[F1] = "F1",
|
||||||
[F2] = "F2",
|
[F2] = "F2",
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ void setup(struct config* config);
|
|||||||
int load(struct Vector* users, struct Vector* sessions);
|
int load(struct Vector* users, struct Vector* sessions);
|
||||||
void print_err(const char* /*msg*/);
|
void print_err(const char* /*msg*/);
|
||||||
void print_errno(const char* /*descr*/);
|
void print_errno(const char* /*descr*/);
|
||||||
|
void print_pam_msg(const char* msg, int msg_style);
|
||||||
|
void clear_pam_msg(void);
|
||||||
|
|
||||||
void ui_update_field(enum input focused_input);
|
void ui_update_field(enum input focused_input);
|
||||||
void ui_update_ffield();
|
void ui_update_ffield();
|
||||||
|
|||||||
41
src/auth.c
41
src/auth.c
@@ -17,6 +17,11 @@
|
|||||||
#include "unistd.h"
|
#include "unistd.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
struct pam_conv_data {
|
||||||
|
char* password;
|
||||||
|
void (*display_pam_msg)(const char* msg, int msg_style);
|
||||||
|
};
|
||||||
|
|
||||||
int pam_conversation(int num_msg, const struct pam_message** msg,
|
int pam_conversation(int num_msg, const struct pam_message** msg,
|
||||||
struct pam_response** resp, void* appdata_ptr) {
|
struct pam_response** resp, void* appdata_ptr) {
|
||||||
struct pam_response* reply =
|
struct pam_response* reply =
|
||||||
@@ -24,13 +29,34 @@ int pam_conversation(int num_msg, const struct pam_message** msg,
|
|||||||
if (!reply) {
|
if (!reply) {
|
||||||
return PAM_BUF_ERR;
|
return PAM_BUF_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct pam_conv_data* conv_data = (struct pam_conv_data*)appdata_ptr;
|
||||||
|
|
||||||
for (size_t i = 0; i < num_msg; i++) {
|
for (size_t i = 0; i < num_msg; i++) {
|
||||||
reply[i].resp = NULL;
|
reply[i].resp = NULL;
|
||||||
reply[i].resp_retcode = 0;
|
reply[i].resp_retcode = 0;
|
||||||
if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
|
|
||||||
msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
|
switch (msg[i]->msg_style) {
|
||||||
char* input = (char*)appdata_ptr;
|
case PAM_PROMPT_ECHO_OFF:
|
||||||
reply[i].resp = strdup(input);
|
case PAM_PROMPT_ECHO_ON:
|
||||||
|
reply[i].resp = strdup(conv_data->password);
|
||||||
|
if (!reply[i].resp) {
|
||||||
|
for (size_t j = 0; j < i; j++)
|
||||||
|
free(reply[j].resp);
|
||||||
|
free(reply);
|
||||||
|
return PAM_BUF_ERR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_TEXT_INFO:
|
||||||
|
case PAM_ERROR_MSG:
|
||||||
|
if (conv_data->display_pam_msg && msg[i]->msg) {
|
||||||
|
conv_data->display_pam_msg(msg[i]->msg, msg[i]->msg_style);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*resp = reply;
|
*resp = reply;
|
||||||
@@ -54,7 +80,9 @@ void clear_screen() {
|
|||||||
|
|
||||||
pam_handle_t* get_pamh(char* user, char* passwd) {
|
pam_handle_t* get_pamh(char* user, char* passwd) {
|
||||||
pam_handle_t* pamh = NULL;
|
pam_handle_t* pamh = NULL;
|
||||||
struct pam_conv pamc = {pam_conversation, (void*)passwd};
|
struct pam_conv_data conv_data = {.password = passwd,
|
||||||
|
.display_pam_msg = print_pam_msg};
|
||||||
|
struct pam_conv pamc = {pam_conversation, (void*)&conv_data};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
char* pam_service_override = getenv("LIDM_PAM_SERVICE");
|
char* pam_service_override = getenv("LIDM_PAM_SERVICE");
|
||||||
@@ -192,9 +220,10 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
|
|||||||
|
|
||||||
pam_handle_t* pamh = get_pamh(user, passwd);
|
pam_handle_t* pamh = get_pamh(user, passwd);
|
||||||
if (pamh == NULL) {
|
if (pamh == NULL) {
|
||||||
print_err("error on pam authentication");
|
print_pam_msg("authentication failed", PAM_ERROR_MSG);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
clear_pam_msg();
|
||||||
|
|
||||||
bool* reach_session = shmalloc(sizeof(bool));
|
bool* reach_session = shmalloc(sizeof(bool));
|
||||||
if (reach_session == NULL) {
|
if (reach_session == NULL) {
|
||||||
|
|||||||
65
src/ui.c
65
src/ui.c
@@ -5,6 +5,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
#include <security/pam_appl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
@@ -295,6 +296,21 @@ int load(struct Vector* users, struct Vector* sessions) {
|
|||||||
restore_all();
|
restore_all();
|
||||||
reboot(RB_POWER_OFF);
|
reboot(RB_POWER_OFF);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
} else if (g_config->functions.fido != NONE &&
|
||||||
|
ansi_code == g_config->functions.fido) {
|
||||||
|
bool successful_write = write_launch_state((struct LaunchState){
|
||||||
|
.username = st_user().username,
|
||||||
|
.session_opt =
|
||||||
|
st_session(g_config->behavior.include_defshell).name,
|
||||||
|
});
|
||||||
|
if (!successful_write) log_puts("[E] failed to write launch state");
|
||||||
|
|
||||||
|
if (!launch(st_user().username, "",
|
||||||
|
st_session(g_config->behavior.include_defshell),
|
||||||
|
&restore_all, g_config)) {
|
||||||
|
print_passwd(utf8len(of_passwd.efield.content), true);
|
||||||
|
ui_update_cursor_focus();
|
||||||
|
}
|
||||||
} else if (ansi_code == A_UP || ansi_code == A_DOWN) {
|
} else if (ansi_code == A_UP || ansi_code == A_DOWN) {
|
||||||
st_ch_focus(ansi_code == A_DOWN ? 1 : -1);
|
st_ch_focus(ansi_code == A_DOWN ? 1 : -1);
|
||||||
} else if (ansi_code == A_RIGHT || ansi_code == A_LEFT) {
|
} else if (ansi_code == A_RIGHT || ansi_code == A_LEFT) {
|
||||||
@@ -509,6 +525,8 @@ static void print_box() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void print_footer() {
|
static void print_footer() {
|
||||||
|
bool fido_enabled = g_config->functions.fido != NONE;
|
||||||
|
|
||||||
size_t bsize = utf8len(g_config->strings.f_poweroff) +
|
size_t bsize = utf8len(g_config->strings.f_poweroff) +
|
||||||
utf8len(KEY_NAMES[g_config->functions.poweroff]) +
|
utf8len(KEY_NAMES[g_config->functions.poweroff]) +
|
||||||
utf8len(g_config->strings.f_reboot) +
|
utf8len(g_config->strings.f_reboot) +
|
||||||
@@ -516,19 +534,31 @@ static void print_footer() {
|
|||||||
utf8len(g_config->strings.f_refresh) +
|
utf8len(g_config->strings.f_refresh) +
|
||||||
utf8len(KEY_NAMES[g_config->functions.refresh]);
|
utf8len(KEY_NAMES[g_config->functions.refresh]);
|
||||||
|
|
||||||
bsize += 2 * 2 + // 2 wide separators between 3 items
|
bsize += 2 * 2 + 3 * 1;
|
||||||
3 * 1; // 3 thin separators inside every item
|
|
||||||
|
if (fido_enabled) {
|
||||||
|
bsize += utf8len(g_config->strings.f_fido) +
|
||||||
|
utf8len(KEY_NAMES[g_config->functions.fido]) + 2 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
uint row = window.ws_row - 1;
|
uint row = window.ws_row - 1;
|
||||||
uint col = window.ws_col - 2 - bsize;
|
uint col = window.ws_col - 2 - bsize;
|
||||||
printf(
|
|
||||||
"\x1b[%3$d;%4$dH%8$s \x1b[%1$sm%5$s\x1b[%2$sm %9$s "
|
printf("\x1b[%d;%dH%s \x1b[%sm%s\x1b[%sm %s \x1b[%sm%s\x1b[%sm ", row, col,
|
||||||
"\x1b[%1$sm%6$s\x1b[%2$sm %10$s \x1b[%1$sm%7$s\x1b[%2$sm",
|
g_config->strings.f_poweroff, g_config->colors.e_key,
|
||||||
g_config->colors.e_key, g_config->colors.fg, row, col,
|
KEY_NAMES[g_config->functions.poweroff], g_config->colors.fg,
|
||||||
KEY_NAMES[g_config->functions.poweroff],
|
g_config->strings.f_reboot, g_config->colors.e_key,
|
||||||
KEY_NAMES[g_config->functions.reboot],
|
KEY_NAMES[g_config->functions.reboot], g_config->colors.fg);
|
||||||
KEY_NAMES[g_config->functions.refresh], g_config->strings.f_poweroff,
|
|
||||||
g_config->strings.f_reboot, g_config->strings.f_refresh);
|
if (fido_enabled) {
|
||||||
|
printf("%s \x1b[%sm%s\x1b[%sm ", g_config->strings.f_fido,
|
||||||
|
g_config->colors.e_key, KEY_NAMES[g_config->functions.fido],
|
||||||
|
g_config->colors.fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s \x1b[%sm%s\x1b[%sm", g_config->strings.f_refresh,
|
||||||
|
g_config->colors.e_key, KEY_NAMES[g_config->functions.refresh],
|
||||||
|
g_config->colors.fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_err(const char* msg) {
|
void print_err(const char* msg) {
|
||||||
@@ -536,6 +566,21 @@ void print_err(const char* msg) {
|
|||||||
msg, errno, strerror(errno));
|
msg, errno, strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void print_pam_msg(const char* msg, int msg_style) {
|
||||||
|
uint row = box_start.y + BOX_HEIGHT + 1;
|
||||||
|
const char* color =
|
||||||
|
(msg_style == PAM_ERROR_MSG) ? g_config->colors.err : g_config->colors.fg;
|
||||||
|
printf("\x1b[%d;%dH\x1b[K\x1b[%sm%.*s\x1b[%sm", row, box_start.x, color,
|
||||||
|
BOX_WIDTH, msg, g_config->colors.fg);
|
||||||
|
(void)fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_pam_msg(void) {
|
||||||
|
uint row = box_start.y + BOX_HEIGHT + 1;
|
||||||
|
printf("\x1b[%d;%dH\x1b[K", row, box_start.x);
|
||||||
|
(void)fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
void print_errno(const char* descr) {
|
void print_errno(const char* descr) {
|
||||||
if (descr == NULL)
|
if (descr == NULL)
|
||||||
(void)fprintf(stderr, "\x1b[%d;%dH\x1b[%smunknown error(%d): %s",
|
(void)fprintf(stderr, "\x1b[%d;%dH\x1b[%smunknown error(%d): %s",
|
||||||
|
|||||||
@@ -25,11 +25,13 @@
|
|||||||
[functions]
|
[functions]
|
||||||
# poweroff = F1
|
# poweroff = F1
|
||||||
# reboot = F2
|
# reboot = F2
|
||||||
|
# fido = NONE
|
||||||
# refresh = F5
|
# refresh = F5
|
||||||
|
|
||||||
[strings]
|
[strings]
|
||||||
# f_poweroff = "poweroff"
|
# f_poweroff = "poweroff"
|
||||||
# f_reboot = "reboot"
|
# f_reboot = "reboot"
|
||||||
|
# f_fido = "fido"
|
||||||
# f_refresh = "refresh"
|
# f_refresh = "refresh"
|
||||||
# e_user = "user"
|
# e_user = "user"
|
||||||
# e_passwd = "password"
|
# e_passwd = "password"
|
||||||
|
|||||||
Reference in New Issue
Block a user