From 8052db073ac931bbd5568f893b1a46bcd943b1d7 Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Sun, 28 Apr 2024 23:11:21 -0700 Subject: [PATCH] feat(linux): adding pam support --- linux/CMakeLists.txt | 5 ++- linux/application-priv.h | 1 + linux/application.cc | 16 +++++-- linux/channels/auth.cc | 94 ++++++++++++++++++++++++++++++++++++++++ linux/channels/auth.h | 7 +++ 5 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 linux/channels/auth.cc create mode 100644 linux/channels/auth.h diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index c369c015..0a18e7ba 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -1,6 +1,6 @@ # Project-level configuration. cmake_minimum_required(VERSION 3.10) -project(runner LANGUAGES CXX) +project(runner LANGUAGES CXX C) # The name of the executable created for the application. Change this to change # the on-disk name of your application. @@ -64,6 +64,7 @@ add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") add_executable(${BINARY_NAME} "main.cc" "application.cc" + "channels/auth.cc" "channels/outputs.cc" "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" ) @@ -74,7 +75,7 @@ apply_standard_settings(${BINARY_NAME}) # Add dependency libraries. Add any application-specific dependencies here. target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK PkgConfig::PAM) # Run the Flutter tool portions of the build. This must not be removed. add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/linux/application-priv.h b/linux/application-priv.h index 3c58a760..ac3db1a4 100644 --- a/linux/application-priv.h +++ b/linux/application-priv.h @@ -4,5 +4,6 @@ struct _GenesisShellApplication { GtkApplication parent_instance; char** dart_entrypoint_arguments; FlMethodChannel* outputs; + FlMethodChannel* auth; GtkWindow* win; }; diff --git a/linux/application.cc b/linux/application.cc index b808311a..f7aa72ae 100644 --- a/linux/application.cc +++ b/linux/application.cc @@ -2,6 +2,7 @@ #include "application.h" #include "application-priv.h" +#include "channels/auth.h" #include "channels/outputs.h" #include @@ -32,9 +33,17 @@ static void genesis_shell_application_activate(GApplication* application) { fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); - self->outputs = fl_method_channel_new(fl_engine_get_binary_messenger(fl_view_get_engine(view)), "com.expidusos.genesis.shell/outputs", FL_METHOD_CODEC(codec)); - fl_method_channel_set_method_call_handler(self->outputs, outputs_method_call_handler, self, nullptr); + { + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + self->outputs = fl_method_channel_new(fl_engine_get_binary_messenger(fl_view_get_engine(view)), "com.expidusos.genesis.shell/outputs", FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler(self->outputs, outputs_method_call_handler, self, nullptr); + } + + { + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + self->auth = fl_method_channel_new(fl_engine_get_binary_messenger(fl_view_get_engine(view)), "com.expidusos.genesis.shell/auth", FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler(self->auth, auth_method_call_handler, self, nullptr); + } gtk_widget_grab_focus(GTK_WIDGET(view)); } @@ -81,6 +90,7 @@ static void genesis_shell_application_dispose(GObject* object) { GenesisShellApplication* self = GENESIS_SHELL_APPLICATION(object); g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); g_clear_object(&self->outputs); + g_clear_object(&self->auth); G_OBJECT_CLASS(genesis_shell_application_parent_class)->dispose(object); } diff --git a/linux/channels/auth.cc b/linux/channels/auth.cc new file mode 100644 index 00000000..549d9fc2 --- /dev/null +++ b/linux/channels/auth.cc @@ -0,0 +1,94 @@ +#include + +#include +#include + +#include "outputs.h" +#include "../application-priv.h" + +static void message_callback(GObject* obj, GAsyncResult* result, gpointer user_data) { + GArray* array_resp = (GArray*)user_data; + + g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(FL_METHOD_CHANNEL(obj), result, nullptr); + g_autoptr(FlValue) resp_value = fl_method_response_get_result(response, nullptr); + + struct pam_response resp; + resp.resp_retcode = 0; + resp.resp = g_strdup(fl_value_get_string(resp_value)); + g_array_append_val(array_resp, resp); +} + +static int conversation(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr) { + GenesisShellApplication* self = GENESIS_SHELL_APPLICATION(appdata_ptr); + + GArray* array_resp = g_array_new(false, true, sizeof (struct pam_response)); + + for (int i = 0; i < num_msg; i++) { + const char* msg_content = msg[i]->msg; + + fl_method_channel_invoke_method(self->auth, "ask", fl_value_new_string(msg_content), nullptr, message_callback, array_resp); + } + + while (true) { + if ((array_resp->len / sizeof (struct pam_response)) == num_msg) { + break; + } + } + + *resp = static_cast(g_array_steal(array_resp, nullptr)); + return PAM_SUCCESS; +} + +void auth_method_call_handler(FlMethodChannel* channel, FlMethodCall* method_call, gpointer user_data) { + g_autoptr(FlMethodResponse) response = nullptr; + if (strcmp(fl_method_call_get_name(method_call), "start") == 0) { + FlValue* args = fl_method_call_get_args(method_call); + + gchar* username = nullptr; + if (fl_value_lookup_string(args, "username") != nullptr) { + username = (gchar*)fl_value_get_string(fl_value_lookup_string(args, "username")); + } + + struct pam_conv* conv = (struct pam_conv*)malloc(sizeof(struct pam_conv)); + conv->conv = conversation; + conv->appdata_ptr = user_data; + + pam_handle_t* handle = NULL; + int r = pam_start("genesis-shell", username, conv, &handle); + if (r != PAM_SUCCESS) { + fl_method_call_respond_error(method_call, "PAM", "pam_start failed", fl_value_new_string(pam_strerror(handle, r)), NULL); + pam_end(handle, r); + free(conv); + return; + } + + r = pam_authenticate(handle, PAM_SILENT); + if (r != PAM_SUCCESS) { + fl_method_call_respond_error(method_call, "PAM", "pam_authenticate failed", fl_value_new_string(pam_strerror(handle, r)), NULL); + pam_end(handle, r); + free(conv); + return; + } + + r = pam_acct_mgmt(handle, PAM_SILENT); + if (r != PAM_SUCCESS) { + fl_method_call_respond_error(method_call, "PAM", "pam_acct_mgmt failed", fl_value_new_string(pam_strerror(handle, r)), NULL); + pam_end(handle, r); + free(conv); + return; + } + + pam_get_item(handle, PAM_USER, (const void**)&username); + pam_end(handle, r); + free(conv); + + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_string(username))); + } else { + response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); + } + + g_autoptr(GError) error = nullptr; + if (!fl_method_call_respond(method_call, response, &error)) { + g_warning("Failed to send response: %s", error->message); + } +} diff --git a/linux/channels/auth.h b/linux/channels/auth.h new file mode 100644 index 00000000..89935b6d --- /dev/null +++ b/linux/channels/auth.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "../application.h" + +void auth_method_call_handler(FlMethodChannel* channel, FlMethodCall* method_call, gpointer user_data);