aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Magahern <james@magahern.com>2019-01-19 19:39:38 -0800
committerJames Magahern <james@magahern.com>2019-01-19 19:39:54 -0800
commit89bd1cb350d03bbe312f6a1c94984e45540c5826 (patch)
tree833e03f40343aa5143daa1475cfe5905db428b7c
parentStart working on accept anim (diff)
Authentication now works
-rw-r--r--meson.build6
-rw-r--r--src/auth.c130
-rw-r--r--src/auth.h35
-rw-r--r--src/main.c78
-rw-r--r--src/render.c3
-rw-r--r--src/render.h7
6 files changed, 255 insertions, 4 deletions
diff --git a/meson.build b/meson.build
index 3b5a780..fab674f 100644
--- a/meson.build
+++ b/meson.build
@@ -2,7 +2,10 @@ project('buzzlocker', 'c')
gnome = import('gnome') # for gresources
+cc = meson.get_compiler('c')
+
sources = [
+ 'src/auth.c',
'src/main.c',
'src/render.c',
'src/x11_support.c',
@@ -14,6 +17,8 @@ dependencies = [
dependency('librsvg-2.0'),
dependency('pangocairo'),
dependency('gio-2.0'),
+ cc.find_library('pam', required: true),
+ cc.find_library('pthread', required: true),
]
# Resources
@@ -27,6 +32,5 @@ resources = gnome.compile_resources(
executable('buzzlocker',
sources: sources + resources,
dependencies: dependencies,
- c_std: 'c11',
install: true
) \ No newline at end of file
diff --git a/src/auth.c b/src/auth.c
new file mode 100644
index 0000000..97e934f
--- /dev/null
+++ b/src/auth.c
@@ -0,0 +1,130 @@
+/*
+ * auth.c
+ *
+ * Created by buzzert <buzzert@buzzert.net> 2019-01-19
+ */
+
+#include "auth.h"
+
+#include <pthread.h>
+#include <pwd.h>
+#include <security/pam_appl.h>
+#include <semaphore.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+struct auth_handle_t {
+ void *context;
+ auth_callbacks_t callbacks;
+
+ sem_t prompt_semaphore;
+ auth_prompt_response_t *prompt_response;
+};
+
+int process_message(const struct pam_message *msg, struct pam_response *resp, struct auth_handle_t *handle)
+{
+ switch (msg->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ case PAM_PROMPT_ECHO_OFF: {
+ handle->callbacks.prompt_handler(msg->msg, handle->context);
+
+ sem_wait(&handle->prompt_semaphore);
+
+ auth_prompt_response_t *response = handle->prompt_response;
+ resp->resp = response->response_buffer;
+ resp->resp_retcode = response->response_code;
+ break;
+ }
+ case PAM_ERROR_MSG:
+ handle->callbacks.error_handler(msg->msg, handle->context);
+ break;
+ case PAM_TEXT_INFO:
+ handle->callbacks.info_handler(msg->msg, handle->context);
+ break;
+ }
+
+ return PAM_SUCCESS;
+}
+
+int perform_conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *data)
+{
+ *resp = calloc(num_msg, sizeof(struct pam_response));
+
+ struct auth_handle_t *handle = (struct auth_handle_t *)data;
+ for (unsigned i = 0; i < num_msg; ++i) {
+ process_message(msg[i], resp[i], handle);
+ }
+
+ return PAM_SUCCESS;
+}
+
+static void* auth_thread_main(void *arg)
+{
+ struct pam_conv conv;
+ conv.conv = perform_conversation;
+ conv.appdata_ptr = arg;
+
+ // Get current username
+ struct passwd *pwd = getpwuid(getuid());
+ const char *username = pwd->pw_name;
+ if (username == NULL || strlen(username) == 0) {
+ fprintf(stderr, "Couldn't get name for the current user\n");
+ // todo: report to callback
+ }
+
+ // Start PAM authentication
+ pam_handle_t *pam = NULL;
+ pam_start(
+ "login",
+ username,
+ &conv,
+ &pam
+ );
+
+ bool authenticating = true;
+ struct auth_handle_t *handle = (struct auth_handle_t *)arg;
+ while (authenticating) {
+ int status = pam_authenticate(pam, 0);
+ handle->callbacks.result_handler(status, handle->context);
+
+ if (status == PAM_SUCCESS) {
+ authenticating = false;
+ }
+ }
+
+ pam_end(pam, 0);
+
+ return NULL;
+}
+
+struct auth_handle_t* auth_begin_authentication(auth_callbacks_t callbacks, void *context)
+{
+ struct auth_handle_t *handle = malloc(sizeof(struct auth_handle_t));
+ handle->callbacks = callbacks;
+ handle->context = context;
+ handle->prompt_response = NULL;
+ sem_init(&handle->prompt_semaphore, 0, 0);
+
+ pthread_t auth_thread;
+ if (pthread_create(&auth_thread, NULL, auth_thread_main, handle)) {
+ fprintf(stderr, "Error creating auth thread\n");
+ }
+
+ return handle;
+}
+
+
+void auth_attempt_authentication(struct auth_handle_t *handle, auth_prompt_response_t response)
+{
+ if (handle->prompt_response == NULL) {
+ handle->prompt_response = malloc(sizeof(auth_prompt_response_t));
+ }
+ handle->prompt_response = memcpy(handle->prompt_response, &response, sizeof(auth_prompt_response_t));
+
+ sem_post(&handle->prompt_semaphore);
+}
+
+
diff --git a/src/auth.h b/src/auth.h
new file mode 100644
index 0000000..462e77b
--- /dev/null
+++ b/src/auth.h
@@ -0,0 +1,35 @@
+/*
+ * auth.h
+ *
+ * Handles all authentication events via PAM
+ * Created by buzzert <buzzert@buzzert.net> 2019-01-19
+ */
+
+#pragma once
+
+typedef struct {
+ char *response_buffer;
+ int response_code;
+} auth_prompt_response_t;
+
+// NOTE: These callbacks are called on a separate thread
+typedef void(*ShowInfo)(const char *info_msg, void *context);
+typedef void(*ShowError)(const char *error_msg, void *context);
+typedef void(*PromptUser)(const char *prompt, void *context);
+typedef void(*AuthenticationResult)(int result, void *context);
+
+typedef struct {
+ ShowInfo info_handler;
+ ShowError error_handler;
+ PromptUser prompt_handler;
+ AuthenticationResult result_handler;
+} auth_callbacks_t;
+
+struct auth_handle_t;
+
+// Starts an authentication thread and returns immediately
+struct auth_handle_t* auth_begin_authentication(auth_callbacks_t callbacks, void *context);
+
+// Perform an authentication attempt
+void auth_attempt_authentication(struct auth_handle_t *handle, auth_prompt_response_t response);
+
diff --git a/src/main.c b/src/main.c
index ffbc098..5edb881 100644
--- a/src/main.c
+++ b/src/main.c
@@ -4,6 +4,7 @@
* Created 2019-01-16 by James Magahern <james@magahern.com>
*/
+#include "auth.h"
#include "render.h"
#include "x11_support.h"
@@ -15,6 +16,11 @@
static const size_t kMaxPasswordLength = 128;
+static inline saver_state_t* saver_state(void *c)
+{
+ return (saver_state_t *)c;
+}
+
static void accept_password(saver_state_t *state);
/*
@@ -31,6 +37,8 @@ static void window_changed_size(saver_state_t *state, XConfigureEvent *event)
static void handle_key_event(saver_state_t *state, XKeyEvent *event)
{
+ if (!state->input_allowed) return;
+
KeySym key;
char keybuf[8];
XLookupString(event, keybuf, sizeof(keybuf), &key, NULL);
@@ -47,7 +55,7 @@ static void handle_key_event(saver_state_t *state, XKeyEvent *event)
} else if (strlen(keybuf) > 0) {
size_t add_len = strlen(keybuf);
if ( (length + add_len) < state->password_buffer_len - 1 ) {
- strncpy(password_buf + length, keybuf, add_len);
+ strncpy(password_buf + length, keybuf, add_len + 1);
}
}
}
@@ -91,6 +99,55 @@ static int poll_events(saver_state_t *state)
static void accept_password(saver_state_t *state)
{
state->cursor_animating = false;
+
+ size_t pw_length = strlen(state->password_buffer);
+ char *password_buf = malloc(pw_length);
+ strncpy(password_buf, state->password_buffer, pw_length + 1);
+
+ auth_prompt_response_t response;
+ response.response_buffer = password_buf;
+ response.response_code = 0;
+ auth_attempt_authentication(state->auth_handle, response);
+
+ // Block input until we hear back from the auth thread
+ state->input_allowed = false;
+}
+
+/*
+ * Auth callbacks
+ */
+
+void callback_show_info(const char *info_msg, void *context)
+{
+ saver_state(context)->password_prompt = info_msg;
+}
+
+void callback_show_error(const char *error_msg, void *context)
+{
+ saver_state(context)->password_prompt = error_msg;
+}
+
+void callback_prompt_user(const char *prompt, void *context)
+{
+ size_t prompt_len = strlen(prompt);
+ char *new_prompt = malloc(prompt_len);
+ strncpy(new_prompt, prompt, prompt_len + 1);
+
+ saver_state_t *state = saver_state(context);
+ state->password_prompt = new_prompt;
+ state->cursor_animating = true;
+ state->input_allowed = true;
+}
+
+void callback_authentication_result(int result, void *context)
+{
+ saver_state_t *state = saver_state(context);
+ if (result == 0) {
+ state->is_authenticated = true;
+ } else {
+ // Try again
+ state->password_buffer[0] = '\0';
+ }
}
/*
@@ -147,7 +204,19 @@ static int runloop(cairo_surface_t *surface)
state.status_font = status_font;
state.password_buffer = calloc(1, kMaxPasswordLength);
state.password_buffer_len = kMaxPasswordLength;
- state.cursor_animating = true;
+ state.cursor_animating = false;
+ state.input_allowed = false;
+ state.password_prompt = "";
+ state.is_authenticated = false;
+
+ auth_callbacks_t callbacks = {
+ .info_handler = callback_show_info,
+ .error_handler = callback_show_error,
+ .prompt_handler = callback_prompt_user,
+ .result_handler = callback_authentication_result
+ };
+
+ state.auth_handle = auth_begin_authentication(callbacks, &state);
// Main run loop
struct timespec sleep_time = { 0, 5000000 };
@@ -164,6 +233,11 @@ static int runloop(cairo_surface_t *surface)
cairo_surface_flush(surface);
nanosleep(&sleep_time, NULL);
+
+ if (state.is_authenticated) {
+ // We're done here
+ break;
+ }
}
// Cleanup
diff --git a/src/render.c b/src/render.c
index 214af8b..7860189 100644
--- a/src/render.c
+++ b/src/render.c
@@ -82,9 +82,10 @@ void draw_password_field(saver_state_t *state)
cairo_t *cr = state->ctx;
// Draw status text
+ const char *prompt = (state->password_prompt ?: "???");
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
pango_layout_set_font_description(state->pango_layout, state->status_font);
- pango_layout_set_text(state->pango_layout, "Password: ", -1);
+ pango_layout_set_text(state->pango_layout, prompt, -1);
int t_width, t_height;
pango_layout_get_size(state->pango_layout, &t_width, &t_height);
diff --git a/src/render.h b/src/render.h
index 5d387e6..902763d 100644
--- a/src/render.h
+++ b/src/render.h
@@ -6,6 +6,8 @@
#pragma once
+#include "auth.h"
+
#include <cairo/cairo.h>
#include <cairo-xlib.h>
#include <librsvg/rsvg.h>
@@ -25,12 +27,17 @@ typedef struct {
int canvas_width;
int canvas_height;
+ bool input_allowed;
bool cursor_animating;
double cursor_opacity;
double cursor_fade_direction;
+ bool is_authenticated;
+ const char *password_prompt;
char *password_buffer;
size_t password_buffer_len;
+
+ struct auth_handle_t *auth_handle;
} saver_state_t;
// The purple sidebar