Commit f7cd00e1 authored by Guido Gunther's avatar Guido Gunther

lockscreen: Authenticate via pam

For now compare an N digit PIN
parent 377291c1
/*
* Copyright (C) 2018 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*/
#define G_LOG_DOMAIN "phosh-auth"
#include "config.h"
#include "auth.h"
#include <security/pam_appl.h>
#define PIN_LENGTH 6
typedef struct
{
pam_handle_t *pamh;
} PhoshAuthPrivate;
typedef struct _PhoshAuth
{
GObject parent;
} PhoshAuth;
G_DEFINE_TYPE_WITH_PRIVATE (PhoshAuth, phosh_auth, G_TYPE_OBJECT)
static int
pam_conversation_cb(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *data)
{
const char *pin = data;
int ret = PAM_CONV_ERR;
struct pam_response *pam_resp = calloc(num_msg,
sizeof(struct pam_response));
if (pam_resp == NULL)
return PAM_BUF_ERR;
for (int i = 0; i < num_msg; ++i) {
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
case PAM_PROMPT_ECHO_ON:
pam_resp[i].resp = g_strndup(pin, phosh_auth_get_pin_length ());
ret = PAM_SUCCESS;
break;
case PAM_ERROR_MSG: /* TBD */
case PAM_TEXT_INFO: /* TBD */
default:
break;
}
}
if (ret == PAM_SUCCESS)
*resp = pam_resp;
else
free (pam_resp);
return ret;
}
/* return TRUE if pin is correct, FALSE otherwise */
gboolean
authenticate (PhoshAuth *self, const gchar* number)
{
PhoshAuthPrivate *priv = phosh_auth_get_instance_private (self);
int ret;
gboolean authenticated = FALSE;
const gchar *username;
const struct pam_conv conv = {
.conv = pam_conversation_cb,
.appdata_ptr = (void*)number,
};
if (priv->pamh == NULL) {
username = g_get_user_name ();
ret = pam_start("phosh", username, &conv, &priv->pamh);
if (ret != PAM_SUCCESS) {
g_warning ("PAM start error %s", pam_strerror (priv->pamh, ret));
goto out;
}
}
ret = pam_authenticate(priv->pamh, 0);
if (ret == PAM_SUCCESS) {
authenticated = TRUE;
} else {
if (ret != PAM_AUTH_ERR)
g_warning("pam_authenticate error %s", pam_strerror (priv->pamh, ret));
goto out;
}
ret = pam_end(priv->pamh, ret);
if (ret == PAM_SUCCESS) {
priv->pamh = NULL;
} else {
g_warning("pam_end error %s", pam_strerror (priv->pamh, ret));
}
out:
return authenticated;
}
static void
authenticate_thread (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
PhoshAuth *self = PHOSH_AUTH (source_object);
const char *number = task_data;
gboolean ret;
if (task_data == NULL) {
g_task_return_boolean (task, FALSE);
return;
}
ret = authenticate (self, number);
g_task_return_boolean (task, ret);
}
static void
phosh_auth_finalize (GObject *object)
{
PhoshAuthPrivate *priv = phosh_auth_get_instance_private (PHOSH_AUTH(object));
GObjectClass *parent_class = G_OBJECT_CLASS (phosh_auth_parent_class);
gint ret;
if (priv->pamh) {
ret = pam_end(priv->pamh, ret);
if (ret != PAM_SUCCESS)
g_warning("pam_end error %s", pam_strerror (priv->pamh, ret));
priv->pamh = NULL;
}
if (parent_class->finalize != NULL)
parent_class->finalize (object);
}
static void
phosh_auth_class_init (PhoshAuthClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = phosh_auth_finalize;
}
static void
phosh_auth_init (PhoshAuth *self)
{
}
GObject *
phosh_auth_new (void)
{
return g_object_new (PHOSH_TYPE_AUTH, NULL);
}
void
phosh_auth_authenticate_async_start (PhoshAuth *self,
const gchar *number,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
GTask *task;
task = g_task_new (self, cancellable, callback, callback_data);
g_task_set_task_data (task, (gpointer) number, NULL);
g_task_run_in_thread (task, authenticate_thread);
g_object_unref (task);
}
gboolean
phosh_auth_authenticate_async_finish (PhoshAuth *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
guint
phosh_auth_get_pin_length ()
{
return PIN_LENGTH;
}
/*
* Copyright (C) 2018 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*/
#pragma once
#include <glib-object.h>
#include <gio/gio.h>
#define PHOSH_TYPE_AUTH (phosh_auth_get_type())
G_DECLARE_FINAL_TYPE (PhoshAuth, phosh_auth, PHOSH, AUTH, GObject)
GObject *phosh_auth_new (void);
guint phosh_auth_get_pin_length ();
void phosh_auth_authenticate_async_start (PhoshAuth *self,
const gchar *number,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean phosh_auth_authenticate_async_finish (PhoshAuth *self,
GAsyncResult *result,
GError **error);
......@@ -7,6 +7,7 @@
#define G_LOG_DOMAIN "phosh-lockscreen"
#include "config.h"
#include "auth.h"
#include "lockscreen.h"
#include "wwaninfo.h"
#include "batteryinfo.h"
......@@ -21,7 +22,6 @@
#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <libgnome-desktop/gnome-wall-clock.h>
#define TEST_PIN "1234"
#define LOCKSCREEN_IDLE_SECONDS 5
enum {
......@@ -55,6 +55,7 @@ typedef struct PhoshLockscreen {
GtkWidget *batteryinfo;
guint idle_timer;
gint64 last_input;
PhoshAuth *auth;
GnomeWallClock *wall_clock;
} PhoshLockscreenPrivate;
......@@ -120,45 +121,60 @@ show_unlock_page (PhoshLockscreen *self)
}
static void
keypad_number_notified_cb (PhoshLockscreen *self)
/* callback of async auth task */
void
auth_async_cb (PhoshAuth *auth, GAsyncResult *result, PhoshLockscreen *self)
{
PhoshLockscreenPrivate *priv;
const gchar *number;
g_assert (PHOSH_IS_LOCKSCREEN (self));
GError *error = NULL;
gboolean authenticated;
priv = phosh_lockscreen_get_instance_private (self);
authenticated = phosh_auth_authenticate_async_finish (auth, result, &error);
if (error != NULL) {
g_warning ("Auth failed unexected: %s", error->message);
return;
}
number = hdy_dialer_get_number (HDY_DIALER (priv->dialer_keypad));
/* grab a ref, a listener to "lockscreen-unlock" might decrease the refcount */
g_object_ref (self);
if (strlen (number) == strlen (TEST_PIN)) {
if (!g_strcmp0 (number, TEST_PIN)) {
/* FIXME: handle real PIN
* https://code.puri.sm/Librem5/phosh/issues/25
*/
g_signal_emit(self, signals[LOCKSCREEN_UNLOCK], 0);
} else {
gtk_label_set_label (GTK_LABEL (priv->lbl_unlock_status), _("Wrong PIN"));
hdy_dialer_clear_number (HDY_DIALER (priv->dialer_keypad));
}
if (authenticated) {
g_signal_emit(self, signals[LOCKSCREEN_UNLOCK], 0);
g_clear_object (&priv->auth);
} else {
/* FIXME: give visual feedback */
hdy_dialer_clear_number (HDY_DIALER (priv->dialer_keypad));
gtk_widget_set_sensitive (priv->dialer_keypad, TRUE);
gtk_widget_grab_focus (GTK_WIDGET (priv->dialer_keypad));
}
/* FIXME: must clear out the buffer and use secmem, see secmem branch */
priv->last_input = g_get_monotonic_time ();
g_object_unref (self);
}
static void
keypad_symbol_clicked_cb (PhoshLockscreen *self, gchar symbol, HdyDialer *keypad)
keypad_number_notified_cb (PhoshLockscreen *self)
{
PhoshLockscreenPrivate *priv = phosh_lockscreen_get_instance_private (self);
PhoshLockscreenPrivate *priv;
const gchar *number;
g_assert (PHOSH_IS_LOCKSCREEN (self));
g_assert (HDY_IS_DIALER (keypad));
priv = phosh_lockscreen_get_instance_private (self);
number = hdy_dialer_get_number (HDY_DIALER (priv->dialer_keypad));
keypad_update_labels (self);
priv->last_input = g_get_monotonic_time ();
if (strlen (number) == phosh_auth_get_pin_length()) {
gtk_label_set_label (GTK_LABEL (priv->lbl_unlock_status), _("Checking PIN"));
gtk_widget_set_sensitive (priv->dialer_keypad, FALSE);
if (priv->auth == NULL)
priv->auth = PHOSH_AUTH (phosh_auth_new ());
phosh_auth_authenticate_async_start (priv->auth,
number,
NULL,
(GAsyncReadyCallback)auth_async_cb,
self);
}
}
......@@ -292,9 +308,6 @@ phosh_lockscreen_class_init (PhoshLockscreenClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, PhoshLockscreen, dialer_keypad);
gtk_widget_class_bind_template_child_private (widget_class, PhoshLockscreen, lbl_keypad);
gtk_widget_class_bind_template_child_private (widget_class, PhoshLockscreen, lbl_unlock_status);
gtk_widget_class_bind_template_callback_full (widget_class,
"keypad_symbol_clicked_cb",
G_CALLBACK(keypad_symbol_clicked_cb));
gtk_widget_class_bind_template_callback_full (widget_class,
"keypad_number_notified_cb",
G_CALLBACK(keypad_number_notified_cb));
......
......@@ -34,6 +34,8 @@ foreach proto: wl_protos
endforeach
phosh_sources = [
'auth.c',
'auth.h',
'background.c',
'background.h',
'batteryinfo.c',
......@@ -66,6 +68,7 @@ phosh_deps = [
dependency('wayland-client'),
dependency('libhandy-0.0'),
dependency('upower-glib'),
cc.find_library('pam', required: true),
cc.find_library('m', required: false),
cc.find_library('rt', required: false),
]
......
......@@ -46,7 +46,6 @@
<property name="margin_top">12</property>
<property name="margin_bottom">12</property>
<signal name="notify::number" handler="keypad_number_notified_cb" swapped="yes"/>
<signal name="symbol-clicked" handler="keypad_symbol_clicked_cb" swapped="yes"/>
</object>
<packing>
<property name="left_attach">0</property>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment