Show PAM messages

This commit is contained in:
pjht 2024-11-09 10:05:16 -06:00
parent bd2dfec9ae
commit 532a29816b
Signed by: pjht
GPG Key ID: 7B5F6AFBEC7EE78E
6 changed files with 85 additions and 26 deletions

10
comm.c
View File

@ -38,8 +38,8 @@ ssize_t read_comm_request(char **buf_ptr) {
return size;
}
bool write_comm_reply(bool success) {
if (write(comm[1][1], &success, sizeof(success)) != sizeof(success)) {
bool write_comm_reply(struct comm_reply reply) {
if (write(comm[1][1], &reply, sizeof(reply)) != sizeof(reply)) {
swaylock_log_errno(LOG_ERROR, "failed to write pw check result");
return false;
}
@ -95,11 +95,11 @@ out:
return result;
}
bool read_comm_reply(void) {
bool result = false;
struct comm_reply read_comm_reply(void) {
struct comm_reply result;
if (read(comm[1][0], &result, sizeof(result)) != sizeof(result)) {
swaylock_log_errno(LOG_ERROR, "Failed to read pw result");
result = false;
result.kind = REPLY_AUTH_ERR;
}
return result;
}

View File

@ -5,13 +5,25 @@
struct swaylock_password;
enum conn_reply_kind {
REPLY_SUCCESS,
REPLY_AUTH_ERR,
REPLY_CONTINUE,
REPLY_MSG,
};
struct comm_reply {
enum conn_reply_kind kind;
char pam_msg[256];
};
bool spawn_comm_child(void);
ssize_t read_comm_request(char **buf_ptr);
bool write_comm_reply(bool success);
bool write_comm_reply(struct comm_reply reply);
// Requests the provided password to be checked. The password is always cleared
// when the function returns.
bool write_comm_request(struct swaylock_password *pw);
bool read_comm_reply(void);
struct comm_reply read_comm_reply(void);
// FD to poll for password authentication replies.
int get_comm_reply_fd(void);

View File

@ -17,6 +17,7 @@ enum auth_state {
AUTH_STATE_BACKSPACE,
AUTH_STATE_VALIDATING,
AUTH_STATE_INVALID,
AUTH_STATE_IDLE_MSG,
};
struct swaylock_colorset {
@ -93,6 +94,7 @@ struct swaylock_state {
bool run_display, locked;
struct ext_session_lock_manager_v1 *ext_session_lock_manager_v1;
struct ext_session_lock_v1 *ext_session_lock_v1;
char pam_msg[256];
};
struct swaylock_surface {

18
main.c
View File

@ -1117,14 +1117,28 @@ static void display_in(int fd, short mask, void *data) {
}
static void comm_in(int fd, short mask, void *data) {
if (read_comm_reply()) {
struct comm_reply reply = read_comm_reply();
if (reply.kind == REPLY_SUCCESS) {
// Authentication succeeded
state.run_display = false;
} else {
} else if (reply.kind == REPLY_AUTH_ERR) {
state.auth_state = AUTH_STATE_INVALID;
schedule_indicator_clear(&state);
++state.failed_attempts;
damage_state(&state);
} else if (reply.kind == REPLY_CONTINUE) {
state.auth_state = AUTH_STATE_IDLE;
schedule_indicator_clear(&state);
damage_state(&state);
} else if (reply.kind == REPLY_MSG) {
state.auth_state = AUTH_STATE_IDLE_MSG;
memcpy(&state.pam_msg[0], &reply.pam_msg[0], 256);
schedule_indicator_clear(&state);
damage_state(&state);
}
struct swaylock_surface *surface;
wl_list_for_each(surface, &state.surfaces, link) {
render_frame(surface);
}
}

48
pam.c
View File

@ -1,3 +1,4 @@
#include <security/_pam_types.h>
#define _POSIX_C_SOURCE 200809L
#include <pwd.h>
#include <security/pam_appl.h>
@ -11,6 +12,7 @@
#include "swaylock.h"
static char *pw_buf = NULL;
static bool prompt_send_response = false;
void initialize_pw_backend(int argc, char **argv) {
if (getuid() != geteuid() || getgid() != getegid()) {
@ -38,14 +40,41 @@ static int handle_conversation(int num_msg, const struct pam_message **msg,
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
case PAM_PROMPT_ECHO_ON:
{
if (prompt_send_response) {
prompt_send_response = false;
struct comm_reply reply;
reply.kind = REPLY_CONTINUE;
if (!write_comm_reply(reply)) {
exit(EXIT_FAILURE);
}
}
ssize_t size = read_comm_request(&pw_buf);
if (size < 0) {
exit(EXIT_FAILURE);
} else if (size == 0) {
pam_reply[i].resp = NULL;
} else {
pam_reply[i].resp = strdup(pw_buf); // PAM clears and frees this
password_buffer_destroy(pw_buf, size);
pw_buf = NULL;
if (pam_reply[i].resp == NULL) {
swaylock_log(LOG_ERROR, "Allocation failed");
return PAM_ABORT;
}
}
break;
}
case PAM_ERROR_MSG:
case PAM_TEXT_INFO:
swaylock_log(LOG_DEBUG, "PAM message %s", msg[i]->msg);
prompt_send_response = false;
struct comm_reply reply;
reply.kind = REPLY_MSG;
strncpy(&reply.pam_msg[0], msg[i]->msg, 255);
if (!write_comm_reply(reply)) {
exit(EXIT_FAILURE);
}
break;
}
}
@ -89,16 +118,8 @@ void run_pw_backend_child(void) {
int pam_status = PAM_SUCCESS;
while (1) {
ssize_t size = read_comm_request(&pw_buf);
if (size < 0) {
exit(EXIT_FAILURE);
} else if (size == 0) {
break;
}
int pam_status = pam_authenticate(auth_handle, 0);
password_buffer_destroy(pw_buf, size);
pw_buf = NULL;
prompt_send_response = false;
bool success = pam_status == PAM_SUCCESS;
if (!success) {
@ -106,7 +127,14 @@ void run_pw_backend_child(void) {
get_pam_auth_error(pam_status));
}
if (!write_comm_reply(success)) {
struct comm_reply reply;
if (success) {
reply.kind = REPLY_SUCCESS;
} else {
reply.kind = REPLY_AUTH_ERR;
}
if (!write_comm_reply(reply)) {
exit(EXIT_FAILURE);
}
}

View File

@ -3,6 +3,7 @@
#include <wayland-client.h>
#include "cairo.h"
#include "background-image.h"
#include "log.h"
#include "swaylock.h"
#define M_PI 3.14159265358979323846
@ -94,7 +95,8 @@ void render_frame(struct swaylock_surface *surface) {
char attempts[4]; // like i3lock: count no more than 999
char *text = NULL;
const char *layout_text = NULL;
const char *layout_text = &state->pam_msg[0];
swaylock_log(LOG_DEBUG, "RNDR pm %s", layout_text);
if (state->args.show_indicator) {
switch (state->auth_state) {
@ -135,7 +137,7 @@ void render_frame(struct swaylock_surface *surface) {
++curr_layout;
}
// will handle invalid index if none are active
layout_text = xkb_keymap_layout_get_name(state->xkb.keymap, curr_layout);
// layout_text = xkb_keymap_layout_get_name(state->xkb.keymap, curr_layout);
}
break;
default:
@ -143,6 +145,7 @@ void render_frame(struct swaylock_surface *surface) {
}
}
// Compute the size of the buffer needed
int arc_radius = state->args.radius * surface->scale;
int arc_thickness = state->args.thickness * surface->scale;
@ -219,7 +222,7 @@ void render_frame(struct swaylock_surface *surface) {
float type_indicator_border_thickness =
TYPE_INDICATOR_BORDER_THICKNESS * surface->scale;
if (state->args.show_indicator && (state->auth_state != AUTH_STATE_IDLE ||
if (state->args.show_indicator && ((state->auth_state != AUTH_STATE_IDLE) ||
state->args.indicator_idle_visible)) {
// Fill inner circle
cairo_set_line_width(cairo, 0);