diff options
-rw-r--r-- | examples/full_example.c | 67 | ||||
-rw-r--r-- | examples/meson.build | 24 | ||||
-rw-r--r-- | examples/simple_example.c | 36 | ||||
-rw-r--r-- | include/meson.build | 4 | ||||
-rw-r--r-- | include/pam.h | 102 | ||||
-rw-r--r-- | meson.build | 28 | ||||
-rw-r--r-- | meson_options.txt | 3 | ||||
-rw-r--r-- | pam/astal-auth | 5 | ||||
-rw-r--r-- | src/meson.build | 53 | ||||
-rw-r--r-- | src/pam.c | 438 |
10 files changed, 760 insertions, 0 deletions
diff --git a/examples/full_example.c b/examples/full_example.c new file mode 100644 index 0000000..5174edd --- /dev/null +++ b/examples/full_example.c @@ -0,0 +1,67 @@ +#include "pam.h" +#include <bsd/readpassphrase.h> + +GMainLoop *loop; + +static void authenticate(AstalAuthPam *pam) { + if(!astal_auth_pam_start_authenticate(pam)) { + g_print("could not start authentication process\n"); + g_object_unref(pam); + g_main_loop_quit(loop); + } +} + +static void on_visible(AstalAuthPam *pam, const gchar *data) { + gchar passbuf[1024]; + readpassphrase(data, passbuf, sizeof(passbuf), RPP_ECHO_ON); + astal_auth_pam_supply_secret(pam, passbuf); +} + +static void on_hidden(AstalAuthPam *pam, const gchar *data) { + gchar passbuf[1024]; + readpassphrase(data, passbuf, sizeof(passbuf), RPP_ECHO_OFF); + astal_auth_pam_supply_secret(pam, passbuf); +} + +static void on_info(AstalAuthPam *pam, const gchar *data) { + g_print("info: %s\n", data); + astal_auth_pam_supply_secret(pam, NULL); +} + +static void on_error(AstalAuthPam *pam, const gchar *data) { + g_print("error: %s\n", data); + astal_auth_pam_supply_secret(pam, NULL); +} + +static void on_success(AstalAuthPam *pam) { + g_print("success\n"); + g_object_unref(pam); + g_main_loop_quit(loop); +} + +static void on_fail(AstalAuthPam *pam, const gchar *data) { + g_print("fail: %s\n", data); + authenticate(pam); +} + + +int main(void) { + + GMainContext *loopctx = NULL; + + loop = g_main_loop_new(loopctx, FALSE); + + AstalAuthPam *pam = g_object_new(ASTAL_AUTH_TYPE_PAM, NULL); + + g_signal_connect(pam, "auth-prompt-visible", G_CALLBACK(on_visible), NULL); + g_signal_connect(pam, "auth-prompt-hidden", G_CALLBACK(on_hidden), NULL); + g_signal_connect(pam, "auth-info", G_CALLBACK(on_info), NULL); + g_signal_connect(pam, "auth-error", G_CALLBACK(on_error), NULL); + + g_signal_connect(pam, "success", G_CALLBACK(on_success), NULL); + g_signal_connect(pam, "fail", G_CALLBACK(on_fail), NULL); + + authenticate(pam); + + g_main_loop_run(loop); +} diff --git a/examples/meson.build b/examples/meson.build new file mode 100644 index 0000000..b02ed55 --- /dev/null +++ b/examples/meson.build @@ -0,0 +1,24 @@ +astal_auth = declare_dependency( + link_with : astal_auth_lib, + include_directories : astal_auth_inc) + + +deps_example = [ + dependency('gobject-2.0'), + dependency('libbsd'), + astal_auth +] + +astal_auth_full_exmple = executable( + 'astal_auth_full_example', + files('full_example.c'), + build_by_default : get_option('examples'), + dependencies : deps_example, + install : false) + +astal_auth_simple_example = executable( + 'astal_auth_simple_example', + files('simple_example.c'), + build_by_default : get_option('examples'), + dependencies : deps_example, + install : false) diff --git a/examples/simple_example.c b/examples/simple_example.c new file mode 100644 index 0000000..e48bc78 --- /dev/null +++ b/examples/simple_example.c @@ -0,0 +1,36 @@ +#include <bsd/readpassphrase.h> +#include "pam.h" + +GMainLoop *loop; + +void ready_callback(AstalAuthPam *pam, + GAsyncResult *res, + gpointer user_data) { + GError *error = NULL; + astal_auth_pam_authenticate_finish(res, &error); + if (error == NULL) { + g_print("success\n"); + } else { + g_print("failure: %s\n", error->message); + g_error_free(error); + } + + g_main_loop_quit(loop); +} + +int main(void) { + + GMainContext *loopctx = NULL; + loop = g_main_loop_new(loopctx, FALSE); + + gchar *passbuf = calloc(1024, sizeof(gchar)); + readpassphrase("Password: ", passbuf, 1024, RPP_ECHO_OFF); + astal_auth_pam_authenticate(passbuf, + (GAsyncReadyCallback) ready_callback, + NULL + ); + g_free(passbuf); + + g_main_loop_run(loop); + exit(EXIT_SUCCESS); +} diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 0000000..59db862 --- /dev/null +++ b/include/meson.build @@ -0,0 +1,4 @@ +astal_auth_inc = include_directories('.') +astal_auth_headers = files('pam.h') + +install_headers('pam.h', subdir : 'astal') diff --git a/include/pam.h b/include/pam.h new file mode 100644 index 0000000..bc1c28c --- /dev/null +++ b/include/pam.h @@ -0,0 +1,102 @@ +#ifndef ASTAL_AUTH_PAM_H +#define ASTAL_AUTH_PAM_H + +#include <glib-object.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define ASTAL_AUTH_TYPE_PAM (astal_auth_pam_get_type()) + +G_DECLARE_FINAL_TYPE (AstalAuthPam, astal_auth_pam, ASTAL_AUTH, PAM, GObject) + +/** + * astal_auth_pam_set_username + * @self: a AstalAuthPam object + * @username: the new username + * + * Sets the username to be used for authentication. This must be set to + * before calling start_authenticate. + * Changing it afterwards has no effect on the authentication process. + * + * Defaults to the owner of the process. + * + */ +void astal_auth_pam_set_username(AstalAuthPam *self, const gchar *username); + +/** + * astal_auth_pam_get_username + * @self: a AstalAuthPam object + * + * Fetches the username from AsalAuthPam object. + * + * Returns: the username of the AsalAuthPam object. This string is + * owned by the object and must not be modified or freed. + */ +const gchar *astal_auth_pam_get_username(AstalAuthPam *self); + +/** + * astal_auth_pam_set_service + * @self: a AstalAuthPam object + * @service: the pam service used for authentication + * + * Sets the service to be used for authentication. This must be set to + * before calling start_authenticate. + * Changing it afterwards has no effect on the authentication process. + * + * Defaults to `astal-auth`. + * + */ +void astal_auth_pam_set_service(AstalAuthPam *self, const gchar *service); + +/** + * astal_auth_pam_get_service + * @self: a AstalAuthPam + * + * Fetches the service from AsalAuthPam object. + * + * Returns: the service of the AsalAuthPam object. This string is + * owned by the object and must not be modified or freed. + */ +const gchar *astal_auth_pam_get_service(AstalAuthPam *self); + + +/** + * astal_auth_pam_start_authentication: + * @self: a AstalAuthPam Object + * + * starts a new authentication process using the PAM (Pluggable Authentication Modules) system. + * Note that this will cancel an already running authentication process + * associated with this AstalAuthPam object. + */ +gboolean astal_auth_pam_start_authenticate(AstalAuthPam *self); + +/** + * astal_auth_pam_supply_secret + * @self: a AstalAuthPam Object + * @secret: (nullable) the secret to be provided to pam. Can be NULL. + * + * provides pam with a secret. This method must be called exactly once after a + * auth-* signal is emitted. + */ +void astal_auth_pam_supply_secret(AstalAuthPam *self, const gchar *secret); + +/** + * astal_auth_pam_authenticate: + * @password: the password to be authenticated + * @result_callback: (scope async) (closure user_data): a GAsyncReadyCallback + * to call when the request is satisfied + * @user_data: the data to pass to callback function + * + * Requests authentication of the provided password using the PAM (Pluggable Authentication Modules) system. + */ +gboolean astal_auth_pam_authenticate(const gchar *password, + GAsyncReadyCallback result_callback, + gpointer user_data); + +gssize astal_auth_pam_authenticate_finish(GAsyncResult *res, + GError **error); + +G_END_DECLS + +#endif // !ASTAL_AUTH_PAM_H diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..34ab7c8 --- /dev/null +++ b/meson.build @@ -0,0 +1,28 @@ +project('astal_auth', + 'c', + version : '0.1.0', + default_options : [ + 'c_std=gnu11', + 'warning_level=3', + 'prefix=/usr' + ] +) + +add_project_arguments( + ['-Wno-pedantic'], + language : 'c') + +lib_so_version = 0 + +pkg_config = import('pkgconfig') +gnome = import('gnome') + +subdir('include') +subdir('src') +subdir('examples') + + +install_data( + 'pam/astal-auth', + install_dir : get_option('sysconfdir') / 'pam.d' +)
\ No newline at end of file diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..e28447e --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,3 @@ +option('examples', type : 'boolean', value : false, description : 'Build example applications') +option('introspection', type : 'boolean', value : true, description : 'Build gobject-introspection data') +option('vapi', type : 'boolean', value : true, description : 'Generate vapi data (needs vapigen & introspection option)') diff --git a/pam/astal-auth b/pam/astal-auth new file mode 100644 index 0000000..41f79d7 --- /dev/null +++ b/pam/astal-auth @@ -0,0 +1,5 @@ +# PAM configuration file for the astal-auth library. +# By default, it only includes the 'login' +# configuration file (see /etc/pam.d/login) + +auth include login diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..4c14897 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,53 @@ +srcs = files( + 'pam.c', +) + +deps = [ + dependency('gobject-2.0'), + dependency('gio-2.0'), + dependency('pam') +] + +astal_auth_lib = library( + 'astal-auth', + sources : srcs, + include_directories : astal_auth_inc, + dependencies : deps, + version : meson.project_version(), + soversion : lib_so_version, + install : true +) + +pkg_config_name = 'astal-auth-' + lib_so_version.to_string() + +if get_option('introspection') + gir = gnome.generate_gir( + astal_auth_lib, + sources : srcs + astal_auth_headers, + nsversion : '1.0', + namespace : 'AstalAuth', + symbol_prefix : 'astal_auth', + identifier_prefix : 'AstalAuth', + includes : ['GObject-2.0', 'Gio-2.0'], + header : 'astal/astal-auth.h', + export_packages : pkg_config_name, + install : true + ) + + if get_option('vapi') + gnome.generate_vapi( + pkg_config_name, + sources : [gir[0]], + packages : ['gobject-2.0', 'gio-2.0'], + install : true) + endif +endif + +pkg_config.generate( + name : 'astal-auth', + version : meson.project_version(), + libraries : [astal_auth_lib], + filebase : pkg_config_name, + subdirs : 'astal', + description : 'astal authentication module', + url : 'https://github.com/kotontrion/astal-auth') diff --git a/src/pam.c b/src/pam.c new file mode 100644 index 0000000..de32531 --- /dev/null +++ b/src/pam.c @@ -0,0 +1,438 @@ +#include "pam.h" +#include <pwd.h> +#include <security/_pam_types.h> +#include <security/pam_appl.h> + + +struct _AstalAuthPam { + GObject parent_instance; + + gchar *username; + gchar *service; +}; + +typedef struct { + GTask *task; + GMainContext *context; + GMutex data_mutex; + GCond data_cond; + + gchar *secret; + gboolean secret_set; +} AstalAuthPamPrivate; + +typedef struct { + AstalAuthPam *pam; + guint signal_id; + gchar *msg; +} AstalAuthPamSignalEmitData; + +static void astal_auth_pam_signal_emit_data_free(AstalAuthPamSignalEmitData *data) { + g_free(data->msg); + g_free(data); +} + +typedef enum { + ASTAL_AUTH_PAM_SIGNAL_PROMPT_VISIBLE, + ASTAL_AUTH_PAM_SIGNAL_PROMPT_HIDDEN, + ASTAL_AUTH_PAM_SIGNAL_INFO, + ASTAL_AUTH_PAM_SIGNAL_ERROR, + ASTAL_AUTH_PAM_SIGNAL_SUCCESS, + ASTAL_AUTH_PAM_SIGNAL_FAIL, + ASTAL_AUTH_PAM_N_SIGNALS +} AstalAuthPamSignals; + +typedef enum { + ASTAL_AUTH_PAM_PROP_USERNAME = 1, + ASTAL_AUTH_PAM_PROP_SERVICE, + ASTAL_AUTH_PAM_N_PROPERTIES +} AstalAuthPamProperties; + + +static guint astal_auth_pam_signals[ASTAL_AUTH_PAM_N_SIGNALS] = {0,}; +static GParamSpec *astal_auth_pam_properties[ASTAL_AUTH_PAM_N_PROPERTIES] = {NULL,}; + +G_DEFINE_TYPE_WITH_PRIVATE(AstalAuthPam, astal_auth_pam, G_TYPE_OBJECT); + +void astal_auth_pam_set_username(AstalAuthPam *self, const gchar *username) { + + g_return_if_fail(ASTAL_AUTH_IS_PAM(self)); + g_return_if_fail(username != NULL); + + g_free(self->username); + self->username = g_strdup(username); + g_object_notify(G_OBJECT(self), "username"); +} + +void astal_auth_pam_supply_secret(AstalAuthPam *self, const gchar *secret) { + + g_return_if_fail(ASTAL_AUTH_IS_PAM(self)); + AstalAuthPamPrivate *priv = astal_auth_pam_get_instance_private(self); + + g_mutex_lock(&priv->data_mutex); + g_free(priv->secret); + priv->secret = g_strdup(secret); + priv->secret_set = TRUE; + g_cond_signal(&priv->data_cond); + g_mutex_unlock(&priv->data_mutex); +} + +void astal_auth_pam_set_service(AstalAuthPam *self, const gchar *service) { + + g_return_if_fail(ASTAL_AUTH_IS_PAM(self)); + g_return_if_fail(service != NULL); + + g_free(self->service); + self->service = g_strdup(service); + g_object_notify(G_OBJECT(self), "service"); +} + +const gchar *astal_auth_pam_get_username(AstalAuthPam *self) { + + g_return_val_if_fail (ASTAL_AUTH_IS_PAM(self), NULL); + return self->username; +} + +const gchar *astal_auth_pam_get_service(AstalAuthPam *self) { + + g_return_val_if_fail(ASTAL_AUTH_IS_PAM(self), NULL); + return self->service; +} + +static void astal_auth_pam_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { + AstalAuthPam *self = ASTAL_AUTH_PAM(object); + + switch (property_id) { + case ASTAL_AUTH_PAM_PROP_USERNAME: + astal_auth_pam_set_username(self, g_value_get_string(value)); + break; + case ASTAL_AUTH_PAM_PROP_SERVICE: + astal_auth_pam_set_service(self, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void astal_auth_pam_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { + AstalAuthPam *self = ASTAL_AUTH_PAM(object); + + switch (property_id) { + case ASTAL_AUTH_PAM_PROP_USERNAME: + g_value_set_string(value, self->username); + break; + case ASTAL_AUTH_PAM_PROP_SERVICE: + g_value_set_string(value, self->service); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void astal_auth_pam_callback(GObject *object, GAsyncResult *res, gpointer user_data) { + + AstalAuthPam *self = ASTAL_AUTH_PAM(object); + AstalAuthPamPrivate *priv = astal_auth_pam_get_instance_private(self); + + GTask *task = g_steal_pointer(&priv->task); + + GError *error = NULL; + g_task_propagate_int(task, &error); + + if (error == NULL) { + g_signal_emit(self, astal_auth_pam_signals[ASTAL_AUTH_PAM_SIGNAL_SUCCESS], 0); + } else { + g_signal_emit(self, astal_auth_pam_signals[ASTAL_AUTH_PAM_SIGNAL_FAIL], 0, error->message); + g_error_free(error); + } + g_object_unref(task); +} + +static gboolean astal_auth_pam_emit_signal_in_context(gpointer user_data) { + AstalAuthPamSignalEmitData *data = user_data; + g_signal_emit(data->pam, data->signal_id, 0, data->msg); + return G_SOURCE_REMOVE; +} + +static void astal_auth_pam_emit_signal(AstalAuthPam *pam, guint signal, const gchar *msg) { + GSource *emit_source; + AstalAuthPamSignalEmitData *data; + + data = g_new0 (AstalAuthPamSignalEmitData, 1); + data->pam = pam; + data->signal_id = astal_auth_pam_signals[signal]; + data->msg = g_strdup(msg); + + emit_source = g_idle_source_new(); + g_source_set_callback(emit_source, astal_auth_pam_emit_signal_in_context, data, + (GDestroyNotify) astal_auth_pam_signal_emit_data_free); + g_source_set_priority(emit_source, G_PRIORITY_DEFAULT); + g_source_attach(emit_source, ((AstalAuthPamPrivate *) astal_auth_pam_get_instance_private(pam))->context); + g_source_unref(emit_source); + +} + + +int astal_auth_pam_handle_conversation(int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *appdata_ptr) { + AstalAuthPam *self = appdata_ptr; + AstalAuthPamPrivate *priv = astal_auth_pam_get_instance_private(self); + + struct pam_response *replies = NULL; + if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG) { + return PAM_CONV_ERR; + } + replies = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response)); + if (replies == NULL) { + return PAM_BUF_ERR; + } + for (int i = 0; i < num_msg; ++i) { + guint signal; + switch (msg[i]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + signal = ASTAL_AUTH_PAM_SIGNAL_PROMPT_HIDDEN; + break; + case PAM_PROMPT_ECHO_ON: + signal = ASTAL_AUTH_PAM_SIGNAL_PROMPT_VISIBLE; + break; + case PAM_ERROR_MSG: + signal = ASTAL_AUTH_PAM_SIGNAL_ERROR;; + break; + case PAM_TEXT_INFO: + signal = ASTAL_AUTH_PAM_SIGNAL_INFO; + break; + default: + g_free(replies); + return PAM_CONV_ERR; + break; + } + guint signal_id = astal_auth_pam_signals[signal]; + if (g_signal_has_handler_pending( + self, + signal_id, + 0, + FALSE) + ) { + astal_auth_pam_emit_signal(self, signal, msg[i]->msg); + g_mutex_lock(&priv->data_mutex); + while (!priv->secret_set) { + g_cond_wait(&priv->data_cond, &priv->data_mutex); + } + replies[i].resp_retcode = 0; + replies[i].resp = g_strdup(priv->secret); + g_free(priv->secret); + priv->secret = NULL; + priv->secret_set = FALSE; + g_mutex_unlock(&priv->data_mutex); + } + } + *resp = replies; + return PAM_SUCCESS; +} + +static void astal_auth_pam_thread(GTask *task, + gpointer object, + gpointer task_data, + GCancellable *cancellable) { + + AstalAuthPam *self = g_task_get_source_object(task); + + pam_handle_t *pamh = NULL; + const struct pam_conv conv = { + .conv = astal_auth_pam_handle_conversation, + .appdata_ptr = self, + }; + + int retval; + retval = pam_start(self->service, self->username, &conv, &pamh); + if (retval == PAM_SUCCESS) { + retval = pam_authenticate(pamh, 0); + pam_end(pamh, retval); + } + if (retval != PAM_SUCCESS) { + g_task_return_new_error(task, G_IO_ERROR, G_IO_ERROR_FAILED, "%s", pam_strerror(pamh, retval)); + } else { + g_task_return_int(task, retval); + } +} + +gboolean astal_auth_pam_start_authenticate_with_callback(AstalAuthPam *self, + GAsyncReadyCallback result_callback, + gpointer user_data) { + + g_return_val_if_fail(ASTAL_AUTH_IS_PAM(self), FALSE); + AstalAuthPamPrivate *priv = astal_auth_pam_get_instance_private(self); + g_return_val_if_fail(priv->task == NULL, FALSE); + + priv->task = g_task_new(self, NULL, result_callback, user_data); + g_task_set_priority(priv->task, 0); + g_task_set_name(priv->task, "[AstalAuth] authenticate"); + g_task_run_in_thread(priv->task, astal_auth_pam_thread); + + return TRUE; +} + +gboolean astal_auth_pam_start_authenticate(AstalAuthPam *self) { + return astal_auth_pam_start_authenticate_with_callback(self, (GAsyncReadyCallback) astal_auth_pam_callback, NULL); +} + +static void astal_auth_pam_on_hidden(AstalAuthPam *pam, const gchar *msg, gchar *password) { + astal_auth_pam_supply_secret(pam, password); + g_free(password); +} + +gboolean astal_auth_pam_authenticate(const gchar *password, + GAsyncReadyCallback result_callback, + gpointer user_data) { + + AstalAuthPam *pam = g_object_new(ASTAL_AUTH_TYPE_PAM, NULL); + g_signal_connect(pam, "auth-prompt-hidden", G_CALLBACK(astal_auth_pam_on_hidden), (void *) g_strdup(password)); + + gboolean started = astal_auth_pam_start_authenticate_with_callback(pam, result_callback, user_data); + g_object_unref(pam); + return started; +} + +gssize astal_auth_pam_authenticate_finish(GAsyncResult *res, + GError **error) { + return g_task_propagate_int(G_TASK(res), error); +} + +static void astal_auth_pam_init(AstalAuthPam *self) { + AstalAuthPamPrivate *priv = astal_auth_pam_get_instance_private(self); + + priv->secret = NULL; + + g_cond_init(&priv->data_cond); + g_mutex_init(&priv->data_mutex); + + priv->context = g_main_context_get_thread_default(); + +} + +static void astal_auth_pam_finalize(GObject *gobject) { + + AstalAuthPam *self = ASTAL_AUTH_PAM(gobject); + AstalAuthPamPrivate *priv = astal_auth_pam_get_instance_private(self); + + g_free(self->username); + g_free(self->service); + + g_free(priv->secret); + + g_cond_clear(&priv->data_cond); + g_mutex_clear(&priv->data_mutex); + + G_OBJECT_CLASS (astal_auth_pam_parent_class)->finalize(gobject); +} + +static void astal_auth_pam_class_init(AstalAuthPamClass *class) { + + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->get_property = astal_auth_pam_get_property; + object_class->set_property = astal_auth_pam_set_property; + + object_class->finalize = astal_auth_pam_finalize; + + struct passwd *passwd = getpwuid(getuid()); + + astal_auth_pam_properties[ASTAL_AUTH_PAM_PROP_USERNAME] = + g_param_spec_string("username", + "username", + "username used for authentication", + passwd->pw_name, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE); + + astal_auth_pam_properties[ASTAL_AUTH_PAM_PROP_SERVICE] = + g_param_spec_string("service", + "service", + "the pam service to use", + "astal-auth", + G_PARAM_CONSTRUCT | G_PARAM_READWRITE); + + g_object_class_install_properties(object_class, + ASTAL_AUTH_PAM_N_PROPERTIES, astal_auth_pam_properties); + + astal_auth_pam_signals[ASTAL_AUTH_PAM_SIGNAL_PROMPT_VISIBLE] = g_signal_new( + "auth-prompt-visible", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING + ); + + astal_auth_pam_signals[ASTAL_AUTH_PAM_SIGNAL_PROMPT_HIDDEN] = g_signal_new( + "auth-prompt-hidden", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING + ); + + astal_auth_pam_signals[ASTAL_AUTH_PAM_SIGNAL_INFO] = g_signal_new( + "auth-info", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING + ); + + astal_auth_pam_signals[ASTAL_AUTH_PAM_SIGNAL_ERROR] = g_signal_new( + "auth-error", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING + ); + astal_auth_pam_signals[ASTAL_AUTH_PAM_SIGNAL_SUCCESS] = g_signal_new( + "success", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0 + ); + astal_auth_pam_signals[ASTAL_AUTH_PAM_SIGNAL_FAIL] = g_signal_new( + "fail", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING + ); +} + + + |