[gnome-flashback/wip/muktupavels/screensaver-auth] screensaver: add GfAuth
- From: Alberts Muktupāvels <muktupavels src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-flashback/wip/muktupavels/screensaver-auth] screensaver: add GfAuth
- Date: Thu, 12 Dec 2019 18:09:17 +0000 (UTC)
commit 6f7810d35c59e1c66aa9b1d5f4b8a0ae15ddfed9
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date: Thu Dec 12 19:56:11 2019 +0200
screensaver: add GfAuth
.gitlab-ci.yml | 1 +
configure.ac | 7 +
gnome-flashback/libscreensaver/Makefile.am | 3 +
gnome-flashback/libscreensaver/gf-auth.c | 636 +++++++++++++++++++++++++++++
gnome-flashback/libscreensaver/gf-auth.h | 57 +++
po/POTFILES.in | 1 +
6 files changed, 705 insertions(+)
---
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e8020f9..04c37a8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -25,6 +25,7 @@ build-ubuntu:
libgnome-desktop-3-dev
libgtk-3-dev
libibus-1.0-dev
+ libpam0g-dev
libpango1.0-dev
libpolkit-agent-1-dev
libpolkit-gobject-1-dev
diff --git a/configure.ac b/configure.ac
index 0317836..3710430 100644
--- a/configure.ac
+++ b/configure.ac
@@ -333,6 +333,13 @@ AS_IF([test "x$with_compiz_session" != "xno"], [
AM_CONDITIONAL(WITH_COMPIZ_SESSION, [test "x$found_compiz" = "xyes"])
+dnl **************************************************************************
+dnl PAM
+dnl **************************************************************************
+
+AC_CHECK_HEADERS([security/pam_appl.h])
+AC_SEARCH_LIBS([pam_start], [pam])
+
dnl **************************************************************************
dnl Process .in files
dnl **************************************************************************
diff --git a/gnome-flashback/libscreensaver/Makefile.am b/gnome-flashback/libscreensaver/Makefile.am
index 6b2f51a..52c7baa 100644
--- a/gnome-flashback/libscreensaver/Makefile.am
+++ b/gnome-flashback/libscreensaver/Makefile.am
@@ -18,6 +18,8 @@ libscreensaver_la_CFLAGS = \
$(NULL)
libscreensaver_la_SOURCES = \
+ gf-auth.c \
+ gf-auth.h \
gf-info-bar.c \
gf-info-bar.h \
gf-listener.c \
@@ -45,6 +47,7 @@ libscreensaver_la_LIBADD = \
$(NULL)
ENUM_TYPES = \
+ gf-auth.h \
$(NULL)
gf-screensaver-enum-types.c: gf-screensaver-enum-types.c.in gf-screensaver-enum-types.h $(ENUM_TYPES)
diff --git a/gnome-flashback/libscreensaver/gf-auth.c b/gnome-flashback/libscreensaver/gf-auth.c
new file mode 100644
index 0000000..3a8c2a2
--- /dev/null
+++ b/gnome-flashback/libscreensaver/gf-auth.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2019 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "gf-auth.h"
+
+#include <glib/gi18n.h>
+#include <security/pam_appl.h>
+
+#include "gf-screensaver-enum-types.h"
+
+typedef struct
+{
+ GfAuth *self;
+
+ GfAuthMessageType type;
+ char *message;
+} GfMessageData;
+
+struct _GfAuth
+{
+ GObject parent;
+
+ GCancellable *cancellable;
+
+ GMutex mutex;
+ GCond cond;
+
+ char *username;
+ char *display;
+
+ GTask *task;
+
+ guint message_id;
+
+ gboolean waits_response;
+ char *response;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_USERNAME,
+ PROP_DISPLAY,
+
+ LAST_PROP
+};
+
+static GParamSpec *auth_properties[LAST_PROP] = { NULL };
+
+enum
+{
+ MESSAGE,
+ COMPLETE,
+
+ LAST_SIGNAL
+};
+
+static guint auth_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GfAuth, gf_auth, G_TYPE_OBJECT)
+
+static GQuark
+gf_auth_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("gf-auth-error-quark");
+
+ return quark;
+}
+
+static GfMessageData *
+gf_message_data_new (GfAuth *self,
+ GfAuthMessageType type,
+ const char *message)
+{
+ GfMessageData *msg;
+
+ msg = g_new0 (GfMessageData, 1);
+ msg->self = g_object_ref (self);
+
+ msg->type = type;
+ msg->message = g_strdup (message);
+
+ return msg;
+}
+
+static void
+gf_message_data_free (gpointer data)
+{
+ GfMessageData *msg;
+
+ msg = data;
+
+ g_object_unref (msg->self);
+ g_free (msg->message);
+ g_free (msg);
+}
+
+static GfAuthMessageType
+message_type_from_msg_style (int msg_style)
+{
+ GfAuthMessageType type;
+
+ switch (msg_style)
+ {
+ case PAM_PROMPT_ECHO_OFF:
+ type = GF_AUTH_MESSAGE_PROMPT_ECHO_OFF;
+ break;
+
+ case PAM_PROMPT_ECHO_ON:
+ type = GF_AUTH_MESSAGE_PROMPT_ECHO_ON;
+ break;
+
+ case PAM_ERROR_MSG:
+ type = GF_AUTH_MESSAGE_ERROR_MSG;
+ break;
+
+ case PAM_TEXT_INFO:
+ type = GF_AUTH_MESSAGE_TEXT_INFO;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return type;
+}
+
+static gboolean
+message_cb (gpointer user_data)
+{
+ GfMessageData *data;
+ GfAuth *self;
+
+ data = user_data;
+ self = data->self;
+
+ g_signal_emit (self, auth_signals[MESSAGE], 0, data->type, data->message);
+ self->message_id = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+emit_message_idle (GfAuth *self,
+ int msg_style,
+ const char *msg)
+{
+ GfAuthMessageType message_type;
+ GfMessageData *data;
+
+ message_type = message_type_from_msg_style (msg_style);
+ data = gf_message_data_new (self, message_type, msg);
+
+ g_assert (self->message_id == 0);
+ self->message_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ message_cb,
+ data,
+ gf_message_data_free);
+
+ g_source_set_name_by_id (self->message_id, "[gnome-flashback] message_cb");
+}
+
+static int
+conversation_cb (int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ GTask *task;
+ GCancellable *cancellable;
+ GfAuth *self;
+ struct pam_response *response;
+ int i;
+
+ task = G_TASK (appdata_ptr);
+ cancellable = g_task_get_cancellable (task);
+ self = g_task_get_task_data (task);
+
+ if (g_task_return_error_if_cancelled (task))
+ return PAM_CONV_ERR;
+
+ if (num_msg <= 0)
+ return PAM_CONV_ERR;
+
+ response = calloc (num_msg, sizeof (struct pam_response));
+
+ if (response == NULL)
+ return PAM_CONV_ERR;
+
+ for (i = 0; i < num_msg; i++)
+ {
+ gboolean failed;
+
+ failed = FALSE;
+
+ g_mutex_lock (&self->mutex);
+ emit_message_idle (self, msg[i]->msg_style, msg[i]->msg);
+
+ switch (msg[i]->msg_style)
+ {
+ case PAM_PROMPT_ECHO_ON:
+ case PAM_PROMPT_ECHO_OFF:
+ self->waits_response = TRUE;
+ g_cond_wait (&self->cond, &self->mutex);
+ self->waits_response = FALSE;
+
+ if (self->response != NULL)
+ {
+ response[i].resp = strdup (self->response);
+ g_clear_pointer (&self->response, g_free);
+ }
+ else
+ {
+ failed = TRUE;
+ }
+ break;
+
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ break;
+
+ default:
+ failed = TRUE;
+ break;
+ }
+
+ g_mutex_unlock (&self->mutex);
+
+ if (failed || g_cancellable_is_cancelled (cancellable))
+ {
+ int j;
+
+ for (j = 0; j <= i; j++)
+ {
+ free (response[j].resp);
+ response[j].resp = NULL;
+ }
+
+ free (response);
+ response = NULL;
+
+ return PAM_CONV_ERR;
+ }
+ }
+
+ *resp = response;
+
+ return PAM_SUCCESS;
+}
+
+static void
+terminate_pam_transaction (pam_handle_t *handle,
+ int status)
+{
+ status = pam_end (handle, status);
+
+ if (status == PAM_SUCCESS)
+ return;
+
+ g_debug ("Failed to terminate PAM transaction (%d): %s",
+ status, pam_strerror (NULL, status));
+}
+
+static void
+verify_in_thread_cb (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GfAuth *self;
+ struct pam_conv conv;
+ pam_handle_t *handle;
+ int status;
+
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ self = g_task_get_task_data (task);
+
+ conv.conv = conversation_cb;
+ conv.appdata_ptr = task;
+
+ handle = NULL;
+
+ status = pam_start ("gnome-flashback", self->username, &conv, &handle);
+
+ if (status != PAM_SUCCESS)
+ {
+ g_task_return_new_error (task,
+ gf_auth_error_quark (),
+ status,
+ "%s",
+ pam_strerror (handle, status));
+
+ terminate_pam_transaction (handle, status);
+ return;
+ }
+
+ status = pam_set_item (handle, PAM_TTY, self->display);
+
+ if (status != PAM_SUCCESS)
+ {
+ g_task_return_new_error (task,
+ gf_auth_error_quark (),
+ status,
+ "%s",
+ pam_strerror (handle, status));
+
+ terminate_pam_transaction (handle, status);
+ return;
+ }
+
+ status = pam_authenticate (handle, 0);
+
+ if (g_task_return_error_if_cancelled (task))
+ {
+ terminate_pam_transaction (handle, status);
+ return;
+ }
+
+ if (status != PAM_SUCCESS)
+ {
+ g_task_return_new_error (task,
+ gf_auth_error_quark (),
+ status,
+ "%s",
+ pam_strerror (handle, status));
+
+ terminate_pam_transaction (handle, status);
+ return;
+ }
+
+ status = pam_acct_mgmt (handle, 0);
+
+ if (status != PAM_SUCCESS)
+ {
+ g_task_return_new_error (task,
+ gf_auth_error_quark (),
+ status,
+ "%s",
+ pam_strerror (handle, status));
+
+ terminate_pam_transaction (handle, status);
+ return;
+ }
+
+ status = pam_setcred (handle, PAM_REINITIALIZE_CRED);
+
+ if (status != PAM_SUCCESS)
+ {
+ g_task_return_new_error (task,
+ gf_auth_error_quark (),
+ status,
+ "%s",
+ pam_strerror (handle, status));
+
+ terminate_pam_transaction (handle, status);
+ return;
+ }
+
+ terminate_pam_transaction (handle, status);
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+verify_done_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ gboolean verified;
+ GfAuth *self;
+ const char *message;
+
+ error = NULL;
+ verified = g_task_propagate_boolean (G_TASK (res), &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+
+ self = GF_AUTH (user_data);
+
+ if (!verified)
+ g_clear_object (&self->task);
+
+ message = "";
+ if (error != NULL)
+ {
+ g_debug ("verify_done_cb (%d): %s", error->code, error->message);
+
+ if (error->domain == gf_auth_error_quark ())
+ {
+ switch (error->code)
+ {
+ case PAM_SUCCESS:
+ case PAM_IGNORE:
+ break;
+
+ case PAM_ACCT_EXPIRED:
+ case PAM_AUTHTOK_EXPIRED:
+ message = _("Your account was given a time limit that’s now passed.");
+ break;
+
+ default:
+ message = _("Sorry, that didn’t work. Please try again.");
+ break;
+ }
+ }
+ }
+
+ g_signal_emit (self,
+ auth_signals[COMPLETE],
+ 0,
+ verified,
+ message);
+
+ g_clear_error (&error);
+}
+
+static void
+gf_auth_dispose (GObject *object)
+{
+ GfAuth *self;
+
+ self = GF_AUTH (object);
+
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+
+ g_mutex_lock (&self->mutex);
+
+ if (self->message_id != 0)
+ {
+ g_source_remove (self->message_id);
+ self->message_id = 0;
+ }
+
+ if (self->waits_response)
+ g_cond_signal (&self->cond);
+
+ g_mutex_unlock (&self->mutex);
+
+ g_clear_object (&self->task);
+
+ G_OBJECT_CLASS (gf_auth_parent_class)->dispose (object);
+}
+
+static void
+gf_auth_finalize (GObject *object)
+{
+ GfAuth *self;
+
+ self = GF_AUTH (object);
+
+ g_clear_pointer (&self->username, g_free);
+ g_clear_pointer (&self->display, g_free);
+
+ g_clear_pointer (&self->response, g_free);
+
+ g_mutex_clear (&self->mutex);
+ g_cond_clear (&self->cond);
+
+ G_OBJECT_CLASS (gf_auth_parent_class)->finalize (object);
+}
+
+static void
+gf_auth_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GfAuth *self;
+
+ self = GF_AUTH (object);
+
+ switch (property_id)
+ {
+ case PROP_USERNAME:
+ g_assert (self->username == NULL);
+ self->username = g_value_dup_string (value);
+ break;
+
+ case PROP_DISPLAY:
+ g_assert (self->display == NULL);
+ self->display = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+install_properties (GObjectClass *object_class)
+{
+ auth_properties[PROP_USERNAME] =
+ g_param_spec_string ("username",
+ "username",
+ "username",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ auth_properties[PROP_DISPLAY] =
+ g_param_spec_string ("display",
+ "display",
+ "display",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, auth_properties);
+}
+
+static void
+install_signals (void)
+{
+ auth_signals[MESSAGE] =
+ g_signal_new ("message",
+ GF_TYPE_AUTH,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ GF_TYPE_AUTH_MESSAGE_TYPE,
+ G_TYPE_STRING);
+
+ auth_signals[COMPLETE] =
+ g_signal_new ("complete",
+ GF_TYPE_AUTH,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING);
+}
+
+static void
+gf_auth_class_init (GfAuthClass *self_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (self_class);
+
+ object_class->dispose = gf_auth_dispose;
+ object_class->finalize = gf_auth_finalize;
+ object_class->set_property = gf_auth_set_property;
+
+ install_properties (object_class);
+ install_signals ();
+}
+
+static void
+gf_auth_init (GfAuth *self)
+{
+ self->cancellable = g_cancellable_new ();
+
+ g_mutex_init (&self->mutex);
+ g_cond_init (&self->cond);
+}
+
+GfAuth *
+gf_auth_new (const char *username,
+ const char *display)
+{
+ return g_object_new (GF_TYPE_AUTH,
+ "username", username,
+ "display", display,
+ NULL);
+}
+
+gboolean
+gf_auth_waits_response (GfAuth *self)
+{
+ return self->waits_response;
+}
+
+void
+gf_auth_set_response (GfAuth *self,
+ const char *response)
+{
+ if (!self->waits_response)
+ return;
+
+ g_assert (self->response == NULL);
+ self->response = g_strdup (response);
+
+ g_mutex_lock (&self->mutex);
+ g_cond_signal (&self->cond);
+ g_mutex_unlock (&self->mutex);
+}
+
+void
+gf_auth_verify (GfAuth *self)
+{
+ if (self->task != NULL)
+ return;
+
+ self->task = g_task_new (NULL, self->cancellable, verify_done_cb, self);
+ g_task_set_task_data (self->task, self, NULL);
+
+ g_task_run_in_thread (self->task, verify_in_thread_cb);
+}
diff --git a/gnome-flashback/libscreensaver/gf-auth.h b/gnome-flashback/libscreensaver/gf-auth.h
new file mode 100644
index 0000000..0b3b4c5
--- /dev/null
+++ b/gnome-flashback/libscreensaver/gf-auth.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GF_AUTH_H
+#define GF_AUTH_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GfAuthMessageType:
+ * @GF_AUTH_MESSAGE_PROMPT_ECHO_OFF: obtain a string whilst echoing text.
+ * @GF_AUTH_MESSAGE_PROMPT_ECHO_ON: obtain a string without echoing any text.
+ * @GF_AUTH_MESSAGE_ERROR_MSG: display an error message.
+ * @GF_AUTH_MESSAGE_TEXT_INFO: display some text.
+ *
+ * Message type.
+ */
+typedef enum
+{
+ GF_AUTH_MESSAGE_PROMPT_ECHO_OFF,
+ GF_AUTH_MESSAGE_PROMPT_ECHO_ON,
+ GF_AUTH_MESSAGE_ERROR_MSG,
+ GF_AUTH_MESSAGE_TEXT_INFO
+} GfAuthMessageType;
+
+#define GF_TYPE_AUTH (gf_auth_get_type ())
+G_DECLARE_FINAL_TYPE (GfAuth, gf_auth, GF, AUTH, GObject)
+
+GfAuth *gf_auth_new (const char *username,
+ const char *display);
+
+gboolean gf_auth_waits_response (GfAuth *self);
+
+void gf_auth_set_response (GfAuth *self,
+ const char *response);
+
+void gf_auth_verify (GfAuth *self);
+
+G_END_DECLS
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4f3467a..8d8e8ce 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -39,6 +39,7 @@ gnome-flashback/libpolkit/flashback-listener.c
gnome-flashback/libpolkit/flashback-polkit-dialog.c
gnome-flashback/libpolkit/flashback-polkit-dialog.ui
gnome-flashback/libpower-applet/gf-power-applet.c
+gnome-flashback/libscreensaver/gf-auth.c
gnome-flashback/libsound-applet/gf-sound-applet.c
gnome-flashback/libsound-applet/gvc-channel-bar.c
gnome-flashback/libsound-applet/gvc/gvc-mixer-control.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]