Skip to content

Commit

Permalink
POC: Add pam message management
Browse files Browse the repository at this point in the history
  • Loading branch information
volker-fr committed May 5, 2020
1 parent f3b0612 commit 2ce7800
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 9 deletions.
78 changes: 70 additions & 8 deletions i3lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ static struct ev_timer *clear_indicator_timeout;
static struct ev_timer *discard_passwd_timeout;
extern unlock_state_t unlock_state;
extern auth_state_t auth_state;
//char *pam_msg = NULL;
char *pam_msg;
int pam_msg_count = 0;
int failed_attempts = 0;
bool show_failed_attempts = false;
bool retry_verification = false;
Expand Down Expand Up @@ -225,6 +228,7 @@ static void finish_input(void) {
password[input_position] = '\0';
unlock_state = STATE_KEY_PRESSED;
redraw_screen();
printf("FINISH_INPUT...\n");
input_done();
}

Expand Down Expand Up @@ -309,6 +313,9 @@ static void input_done(void) {
if (debug_mode)
fprintf(stderr, "Authentication failure\n");

// show only pam msg count on each iteration of a failed pam authentication
pam_msg_count = 0;

/* Get state of Caps and Num lock modifiers, to be displayed in
* STATE_AUTH_WRONG state */
xkb_mod_index_t idx, num_mods;
Expand Down Expand Up @@ -429,6 +436,7 @@ static void handle_key_press(xcb_key_press_event_t *event) {
case XKB_KEY_Return:
case XKB_KEY_KP_Enter:
case XKB_KEY_XF86ScreenSaver:
printf("TRIGGER...\n");
if ((ksym == XKB_KEY_j || ksym == XKB_KEY_m) && !ctrl)
break;

Expand All @@ -443,6 +451,7 @@ static void handle_key_press(xcb_key_press_event_t *event) {
}
finish_input();
skip_repeated_empty_password = true;
printf("TRIGGER... END\n");
return;
default:
skip_repeated_empty_password = false;
Expand Down Expand Up @@ -819,34 +828,69 @@ static bool verify_png_image(const char *image_path) {

#ifndef __OpenBSD__
/*
* Callback function for PAM. We only react on password request callbacks.
* Callback function for PAM.
*
*/
static int conv_callback(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr) {
if (num_msg == 0)
return 1;
printf("CONV_CALLBACK\n");
if (num_msg <= 0 || num_msg > 1)
return PAM_CONV_ERR;

/* PAM expects an array of responses, one for each message */
if ((*resp = calloc(num_msg, sizeof(struct pam_response))) == NULL) {
perror("calloc");
return 1;
return PAM_BUF_ERR;
}

for (int c = 0; c < num_msg; c++) {
if (msg[c]->msg_style != PAM_PROMPT_ECHO_OFF &&
msg[c]->msg_style != PAM_PROMPT_ECHO_ON)
continue;
DEBUG("i3lock: PAM message %d, message style: %d, message: %s\n", c, msg[c]->msg_style, msg[c]->msg);

/* return code is currently not used but should be set to zero */
resp[c]->resp_retcode = 0;
resp[c]->resp = NULL;

if (msg[c] != NULL && msg[c]->msg != NULL) {
pam_msg = strdup((msg[c])->msg);
}

switch(msg[c]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
printf("PAM_PROMPT_ECHO_OFF\n");
break;
case PAM_PROMPT_ECHO_ON:
printf("EFF: %s\n", pam_msg);
redraw_screen();
break;

case PAM_ERROR_MSG:
printf("PAM_ERROR_MSG...\n");
break;
case PAM_TEXT_INFO:
printf("TODO: %s\n", pam_msg);
auth_state = STATE_PAM_UNKNOWN;
pam_msg_count += 1;
redraw_screen();
break;

default:
DEBUG("Unsupported PAM message style %d\n", msg[c]->msg_style);
break;
}

// Only submit the password when asked for input
if (msg[c]->msg_style != PAM_PROMPT_ECHO_OFF &&
msg[c]->msg_style != PAM_PROMPT_ECHO_ON)
continue;

if ((resp[c]->resp = strdup(password)) == NULL) {
perror("strdup");
return 1;
}

}

return 0;
return PAM_SUCCESS;
}
#endif

Expand Down Expand Up @@ -997,6 +1041,9 @@ static void raise_loop(xcb_window_t window) {
if (((xcb_destroy_notify_event_t *)event)->window == window)
exit(EXIT_SUCCESS);
break;
case XCB_MAPPING_NOTIFY:
DEBUG("Unhandled event type XCB_MAPPING_NOTIFY\n");
break;
default:
DEBUG("Unhandled event type %d\n", type);
break;
Expand Down Expand Up @@ -1115,8 +1162,23 @@ int main(int argc, char *argv[]) {
if ((ret = pam_start("i3lock", username, &conv, &pam_handle)) != PAM_SUCCESS)
errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret));


if ((ret = pam_set_item(pam_handle, PAM_TTY, getenv("DISPLAY"))) != PAM_SUCCESS)
errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret));

// authenticate so we trigger the call back & get the user prompt
// if ((ret = pam_authenticate(pam_handle, 0) != PAM_SUCCESS) != PAM_SUCCESS)
// errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret));

// get prompt
//const void *promptp = NULL;
//if ((ret = pam_get_item(pam_handle, PAM_USER_PROMPT, &promptp)) != PAM_SUCCESS)
// errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret));
//ret = pam_get_item(pam_handle, PAM_USER_PROMPT, &promptp);
//ret = pam_get_item(pam_handle, PAM_TTY, &promptp);
//printf("DEBUG return: %d\n", ret);

//printf("DEBUG: %s\n", (char*) promptp);
#endif

/* Using mlock() as non-super-user seems only possible in Linux.
Expand Down
58 changes: 57 additions & 1 deletion unlock_indicator.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <ev.h>
#include <cairo.h>
#include <cairo/cairo-xcb.h>
#include <unistd.h>

#include "i3lock.h"
#include "xcb.h"
Expand Down Expand Up @@ -62,6 +63,13 @@ extern bool show_failed_attempts;
/* Number of failed unlock attempts. */
extern int failed_attempts;

/* Number of pam messages received */
extern int pam_msg_count;

/* Message we receive from PAM */
extern char *pam_msg;


/*******************************************************************************
* Variables defined in xcb.c.
******************************************************************************/
Expand Down Expand Up @@ -98,13 +106,17 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
bg_pixmap = create_bg_pixmap(conn, screen, resolution, color);
/* Initialize cairo: Create one in-memory surface to render the unlock
* indicator on, create one XCB surface to actually draw (one or more,
* depending on the amount of screens) unlock indicators on. */
* depending on the amount of screens) unlock indicators on. Create
* a third for potential messages. */
cairo_surface_t *output = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, button_diameter_physical, button_diameter_physical);
cairo_t *ctx = cairo_create(output);

cairo_surface_t *xcb_output = cairo_xcb_surface_create(conn, bg_pixmap, vistype, resolution[0], resolution[1]);
cairo_t *xcb_ctx = cairo_create(xcb_output);

cairo_surface_t *pam_output = cairo_xcb_surface_create(conn, bg_pixmap, vistype, resolution[0], resolution[1]);
cairo_t *pam_ctx = cairo_create(pam_output);

if (img) {
if (!tile) {
cairo_set_source_surface(xcb_ctx, img, 0, 0);
Expand All @@ -131,6 +143,45 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
cairo_fill(xcb_ctx);
}

//////////////////
cairo_text_extents_t extents;
double x,y;
x = resolution[0] / 2 - ((extents.width / 2) + extents.x_bearing);
y = resolution[1] * 2/3 - ((extents.height / 2) + extents.y_bearing) + 28.0;

if (pam_msg_count && pam_msg) {
char *message = malloc(log10(pam_msg_count)+1 + strlen(": ") + strlen(pam_msg)+2);
sprintf(message, "%d: %s", pam_msg_count, pam_msg);

cairo_select_font_face(pam_ctx, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(pam_ctx, 28.0);
cairo_text_extents (pam_ctx, pam_msg, &extents);

cairo_move_to (pam_ctx, x, y);
char *text;
text = "TEST";
// FIX... doesn't print...
//cairo_show_text (pam_ctx, message);
cairo_show_text (pam_ctx, text);
cairo_show_text (pam_ctx, pam_msg);
DEBUG("PRINTED %s\n", message);

free(message);
}
pam_msg = NULL;

/* draw helping lines */

cairo_set_source_rgba (pam_ctx, 1, 0.2, 0.2, 0.6);
cairo_set_line_width (pam_ctx, 6.0);
cairo_arc (pam_ctx, x, y, 10.0, 0, 2*M_PI);
cairo_fill (pam_ctx);
cairo_move_to (pam_ctx, x, 0);
cairo_rel_line_to (pam_ctx, 0, x);
cairo_stroke (pam_ctx);


//////////////////
if (unlock_indicator &&
(unlock_state >= STATE_KEY_PRESSED || auth_state > STATE_AUTH_IDLE)) {
cairo_scale(ctx, scaling_factor, scaling_factor);
Expand All @@ -152,6 +203,7 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
break;
case STATE_AUTH_WRONG:
case STATE_I3LOCK_LOCK_FAILED:
case STATE_PAM_UNKNOWN:
cairo_set_source_rgba(ctx, 250.0 / 255, 0, 0, 0.75);
break;
default:
Expand All @@ -171,6 +223,7 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
break;
case STATE_AUTH_WRONG:
case STATE_I3LOCK_LOCK_FAILED:
case STATE_PAM_UNKNOWN:
cairo_set_source_rgb(ctx, 125.0 / 255, 51.0 / 255, 0);
break;
case STATE_AUTH_IDLE:
Expand Down Expand Up @@ -218,6 +271,9 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
case STATE_I3LOCK_LOCK_FAILED:
text = "Lock failed!";
break;
case STATE_PAM_UNKNOWN:
text = "PAM MSG!";
break;
default:
if (unlock_state == STATE_NOTHING_TO_DELETE) {
text = "No input";
Expand Down
1 change: 1 addition & 0 deletions unlock_indicator.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ typedef enum {
STATE_AUTH_LOCK = 2, /* currently locking the screen */
STATE_AUTH_WRONG = 3, /* the password was wrong */
STATE_I3LOCK_LOCK_FAILED = 4, /* i3lock failed to load */
STATE_PAM_UNKNOWN = 5, /* not yet handled PAM errors */
} auth_state_t;

xcb_pixmap_t draw_image(uint32_t* resolution);
Expand Down

0 comments on commit 2ce7800

Please sign in to comment.