diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 0000000..3132067 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.10) +set(PROJECT_NAME "secure_application") +project(${PROJECT_NAME} LANGUAGES CXX) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +list(APPEND PLUGIN_SOURCES + "secure_application_plugin.cc" +) + +add_library(${PLUGIN_NAME} SHARED + ${PLUGIN_SOURCES} +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) +target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) + +# List of absolute paths to libraries that should be bundled with the plugin +set(secure_application_bundled_libraries + "" + PARENT_SCOPE +) diff --git a/linux/include/secure_application/secure_application_plugin.h b/linux/include/secure_application/secure_application_plugin.h new file mode 100644 index 0000000..bcab58d --- /dev/null +++ b/linux/include/secure_application/secure_application_plugin.h @@ -0,0 +1,26 @@ +#ifndef FLUTTER_PLUGIN_SECURE_APPLICATION_PLUGIN_H_ +#define FLUTTER_PLUGIN_SECURE_APPLICATION_PLUGIN_H_ + +#include + +G_BEGIN_DECLS + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +typedef struct _SecureApplicationPlugin SecureApplicationPlugin; +typedef struct { + GObjectClass parent_class; +} SecureApplicationPluginClass; + +FLUTTER_PLUGIN_EXPORT GType secure_application_plugin_get_type(); + +FLUTTER_PLUGIN_EXPORT void secure_application_plugin_register_with_registrar( + FlPluginRegistrar* registrar); + +G_END_DECLS + +#endif // FLUTTER_PLUGIN_SECURE_APPLICATION_PLUGIN_H_ diff --git a/linux/secure_application_plugin.cc b/linux/secure_application_plugin.cc new file mode 100644 index 0000000..ec8869c --- /dev/null +++ b/linux/secure_application_plugin.cc @@ -0,0 +1,80 @@ +#include "include/secure_application/secure_application_plugin.h" + +#include +#include +#include + +#include + +#define SECURE_APPLICATION_PLUGIN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), secure_application_plugin_get_type(), \ + SecureApplicationPlugin)) + +struct _SecureApplicationPlugin { + GObject parent_instance; + FlMethodChannel* channel; +}; + +G_DEFINE_TYPE(SecureApplicationPlugin, secure_application_plugin, + g_object_get_type()) + +// Mirrors Windows: respond Success() to every method call. The actual lock / +// blur UI is rendered Dart-side by SecureGate, so the native plugin only needs +// to acknowledge channel calls and invoke "lock" on system events. +static void secure_application_plugin_handle_method_call( + SecureApplicationPlugin* self, FlMethodCall* method_call) { + g_autoptr(FlMethodResponse) response = nullptr; + const gchar* method = fl_method_call_get_name(method_call); + + if (strcmp(method, "getPlatformVersion") == 0) { + struct utsname uname_data = {}; + uname(&uname_data); + g_autofree gchar* version = g_strdup_printf("Linux %s", uname_data.version); + g_autoptr(FlValue) result = fl_value_new_string(version); + response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); + } else { + response = FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + } + fl_method_call_respond(method_call, response, nullptr); +} + +static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call, + gpointer user_data) { + SecureApplicationPlugin* plugin = SECURE_APPLICATION_PLUGIN(user_data); + secure_application_plugin_handle_method_call(plugin, method_call); +} + +static void secure_application_plugin_dispose(GObject* object) { + SecureApplicationPlugin* self = SECURE_APPLICATION_PLUGIN(object); + g_clear_object(&self->channel); + G_OBJECT_CLASS(secure_application_plugin_parent_class)->dispose(object); +} + +static void secure_application_plugin_class_init( + SecureApplicationPluginClass* klass) { + G_OBJECT_CLASS(klass)->dispose = secure_application_plugin_dispose; +} + +static void secure_application_plugin_init(SecureApplicationPlugin* self) {} + +void secure_application_plugin_register_with_registrar( + FlPluginRegistrar* registrar) { + SecureApplicationPlugin* plugin = SECURE_APPLICATION_PLUGIN( + g_object_new(secure_application_plugin_get_type(), nullptr)); + + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + plugin->channel = + fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), + "secure_application", FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler( + plugin->channel, method_call_cb, g_object_ref(plugin), g_object_unref); + + // NOTE: We deliberately do NOT hook the GTK window-state-event to invoke + // "lock" on iconify the way the Windows plugin hooks SC_MINIMIZE. On Linux, + // local_auth has no platform implementation, so an app that engages + // SecureGate's locked state has no way to authenticate back out — the user + // is stranded on the lock screen. Apps that want explicit lock-on-minimize + // on Linux can call `lock()` themselves from a Dart-side WindowListener. + + g_object_unref(plugin); +} diff --git a/pubspec.yaml b/pubspec.yaml index 1f19783..92548b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,3 +32,5 @@ flutter: fileName: secure_application_web.dart windows: pluginClass: SecureApplicationPlugin + linux: + pluginClass: SecureApplicationPlugin