diff --git a/README.md b/README.md index 48c0071..32780a2 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,20 @@ The following section briefly describes how to start the stuff for testing purposes * `./src/nm-openvpn-service --bus-name org.freedesktop.NetworkManager.wireguard` to start the plugin -* `examples/dbus/dbus.py` to send Disconnect() to the plugin \ No newline at end of file +* `examples/dbus/dbus.py` to send Disconnect() to the plugin + +## Files +The following is a list of files that I created over the course of the project and is mainly for myself to keep track of them. + +* `nm-wireguard-service.conf` +* `includes2strings.py` + +## Knowledge +* The wireguard plugin basically handles incoming DBUS requests for the *NM VPN Plugin Interface* (can be looked at via `examples/dbus/dbus.py`) +* `auth-dialog/nm-openvpn-auth-dialog` reads the secrets from STDIN until the string "DONE" occurs and then proceeds to handle them + +NM VPN Plugin: +https://developer.gnome.org/libnm-glib/stable/libnm-glib-NMVPNPlugin.html + +Settings VPN (sent via DBus on Connect(a{sa{sv}}) method): +https://developer.gnome.org/libnm/stable/NMSettingVpn.html#nm-setting-vpn-get-data-item \ No newline at end of file diff --git a/auth-dialog/main.c b/auth-dialog/main.c index 7f320a3..fe7ce37 100644 --- a/auth-dialog/main.c +++ b/auth-dialog/main.c @@ -457,6 +457,8 @@ main (int argc, char *argv[]) AskUserFunc ask_user_func = NULL; FinishFunc finish_func = NULL; + // this describes how the arguments are supposed to be parsed + // e.g. "-u UUID" GOptionContext *context; GOptionEntry entries[] = { { "reprompt", 'r', 0, G_OPTION_ARG_NONE, &retry, "Reprompt for passwords", NULL}, @@ -485,16 +487,22 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } + // the parameters are supplied via arguments + printf("UUID: %s, Name: %s, Service: %s\n", vpn_uuid, vpn_name, vpn_service); + if (strcmp (vpn_service, NM_VPN_SERVICE_TYPE_OPENVPN) != 0) { fprintf (stderr, "This dialog only works with the '%s' service\n", NM_VPN_SERVICE_TYPE_OPENVPN); return EXIT_FAILURE; } + printf("Reading stdin details\n"); + // reads secrets/data from STDIN until "DONE" is read if (!nm_vpn_service_plugin_read_vpn_details (0, &data, &secrets)) { fprintf (stderr, "Failed to read '%s' (%s) data and secrets from stdin.\n", vpn_name, vpn_uuid); - return 1; + //return 1; // TODO re-add } + printf("Done reading\n"); if (external_ui_mode) { no_secrets_required_func = eui_no_secrets_required; diff --git a/examples/dbus/dbus.py b/examples/dbus/dbus.py index 1893aeb..2fe4f9b 100755 --- a/examples/dbus/dbus.py +++ b/examples/dbus/dbus.py @@ -24,12 +24,14 @@ show_introspect = True def send_desktop_notification(title="Hello World", msg="pydbus works!"): """Send a notification to the desktop environment to display""" + bus = SessionBus() notifications = bus.get('.Notifications') notifications.Notify('test', 0, 'dialog-information', title, msg, [], {}, 5000) def list_systemd_units(): """Fetch all systemd units and print them""" + bus = SystemBus() # systemd is now a proxy for the .systemd1 remote object @@ -42,6 +44,7 @@ def list_systemd_units(): def stop_start_systemd_unit(name="ssh.service"): """Stop and restart a systemd unit""" + bus = SystemBus() # systemd is now a proxy for the .systemd1 remote object @@ -51,6 +54,7 @@ def stop_start_systemd_unit(name="ssh.service"): def watch_for_new_systemd_jobs(): """Wait for new systemd units and when they are created, print them out""" + bus = SystemBus() # systemd is now a proxy for the .systemd1 remote object @@ -76,8 +80,9 @@ def hibernate(): else: print("Cannot hibernate") - except GLib.Error as e: + except Exception as ex: print("Could not get PowerManager from DBUS") + print(str(ex)) def get_wg_plugin(bus_name="org.freedesktop.NetworkManager.wireguard", @@ -98,15 +103,39 @@ def get_wg_plugin(bus_name="org.freedesktop.NetworkManager.wireguard", def wg_disconnect(wg_plugin): """Disconnect the WG VPN plugin""" + wg_plugin.Disconnect() +def wg_connect(wg_plugin): + """Send the Connect Command to the Wireguard Plugin""" + + # these are the settings that are expected by Connect(a{sa{sv}}) for a VPN plugin + service_type = GLib.Variant("s", "service") + user_name = GLib.Variant("s", "wireguard") + persistent = GLib.Variant("b", False) + data = GLib.Variant("a{ss}", {"maxi": "cool"}) + secrets = GLib.Variant("a{ss}", {"name": "maxi moser"}) + timeout = GLib.Variant("u", 1337) + + # The DBus type: a{sa{sv}} + # is a Dictionary with... + # Key: Type ("wireless", "wired", "vpn", ...) -- we want VPN + # Value: Dictionary with Key: Setting Name, Value: Setting Value + settings = {"vpn": + {"service-type": service_type, + "user-name": user_name, + "persistent": persistent, + "data": data, + "secrets": secrets, + "timeout": timeout} + } + wg_plugin.Connect(settings) + show_introspect = False if __name__ == "__main__": # send_desktop_notification("Guten Tag", "pydbus funktioniert, mein Herr!") - # list_systemd_units() - # hibernate() try: wg = get_wg_plugin() @@ -114,7 +143,7 @@ if __name__ == "__main__": print(wg.Introspect()) help(wg) - wg_disconnect(wg) + wg_connect(wg) except Exception as ex: - print(str(ex)) \ No newline at end of file + print(str(ex)) diff --git a/src/nm-openvpn-service.c b/src/nm-openvpn-service.c index 7732910..db55a42 100644 --- a/src/nm-openvpn-service.c +++ b/src/nm-openvpn-service.c @@ -236,6 +236,28 @@ _LOGD_enabled (void) /*****************************************************************************/ +static const char * +wg_quick_find_exepath (void) +{ + static const char *paths[] = { + "/usr/sbin/wg-quick", + "/usr/bin/wg-quick", + "/sbin/wg-quick", + "/bin/wg-quick", + "/usr/local/sbin/wg-quick", + "/usr/local/bin/wg-quick", + }; + int i; + + for (i = 0; i < G_N_ELEMENTS(paths); i++) { + if (g_file_test (paths[i], G_FILE_TEST_EXISTS)) { + return paths[i]; + } + } + + return NULL; +} + static const char * openvpn_binary_find_exepath (void) { @@ -393,6 +415,59 @@ pids_pending_add (GPid pid, NMWireguardPlugin *plugin) gl.pids_pending_list = g_slist_prepend (gl.pids_pending_list, pid_data); } +// TODO implement me right +wg_quick_closed(GPid pid, gint status, gpointer user_data){ + PidsPendingData *pid_data = user_data; + NMWireguardPlugin *plugin; + + if (WIFEXITED (status)) { + int exit_status; + + exit_status = WEXITSTATUS (status); + if (exit_status != 0) + _LOGW ("wg-quick[%ld] exited with error code %d", (long) pid, exit_status); + else + _LOGI ("wg-quick[%ld] exited with success", (long) pid); + } + else if (WIFSTOPPED (status)) + _LOGW ("wg-quick[%ld] stopped unexpectedly with signal %d", (long) pid, WSTOPSIG (status)); + else if (WIFSIGNALED (status)) + _LOGW ("wg-quick[%ld] died with signal %d", (long) pid, WTERMSIG (status)); + else + _LOGW ("wg-quick[%ld] died from an unnatural cause", (long) pid); + + g_return_if_fail (pid_data); + g_return_if_fail (pid_data->pid == pid); + g_return_if_fail (g_slist_find (gl.pids_pending_list, pid_data)); + + plugin = pid_data->plugin; + + pid_data->watch_id = 0; + gl.pids_pending_list = g_slist_remove (gl.pids_pending_list , pid_data); + pids_pending_data_free (pid_data); +} + +// TODO implement me right +static void +pids_pending_add_wg (GPid pid, NMWireguardPlugin *plugin) +{ + PidsPendingData *pid_data; + + g_return_if_fail (NM_IS_WIREGUARD_PLUGIN (plugin)); + g_return_if_fail (pid > 0); + + _LOGI ("wg-quick[%ld] started", (long) pid); + + pid_data = g_slice_new (PidsPendingData); + pid_data->pid = pid; + pid_data->kill_id = 0; + pid_data->watch_id = g_child_watch_add (pid, wg_quick_closed, pid_data); + pid_data->plugin = plugin; + g_object_add_weak_pointer ((GObject *) plugin, (gpointer *) &pid_data->plugin); + + gl.pids_pending_list = g_slist_prepend (gl.pids_pending_list, pid_data); +} + static gboolean pids_pending_ensure_killed (gpointer user_data) { @@ -2038,23 +2113,47 @@ real_disconnect (NMVpnServicePlugin *plugin, static gboolean test_connect (NMVpnServicePlugin *plugin, NMConnection *connection, - GError **error) + GError **err) { - _LOGI("Did a dummy connect"); - /* - printf("I Know It!\n"); - // FIXME find something useful - char **cmd = {"touch", "/home/maxmanski/iknowhwatyoudidlastsummer", NULL}; - GPid pid = 0; - GSpawnFlags spawn_flags = G_SPAWN_DO_NOT_REAP_CHILD; - spawn_flags = G_SPAWN_DEFAULT; + NMSettingVpn *setting = nm_connection_get_setting_vpn(connection); + char *secret = nm_setting_vpn_get_secret(setting, "name"); + char *str_setting = nm_setting_to_string(setting); + char *wg_quick_path = wg_quick_find_exepath(); + //char * - if (!g_spawn_async (NULL, cmd, NULL, spawn_flags, NULL, NULL, &pid, error)){ + if(wg_quick_path == NULL){ + _LOGW("Error: Could not find wg-quick!"); return FALSE; } - */ + printf("Setting to String: %s\n", str_setting); + printf("Secret: %s\n", secret); + + //char **cmd = {wg_quick_path, "up", "mullvad", NULL}; + char **cmd = {"/usr/bin/touch", "/home/maxmanski/uwotm8", NULL}; + GPid pid = 0; + GSpawnFlags spawn_flags = G_SPAWN_DO_NOT_REAP_CHILD; + spawn_flags = G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL; + spawn_flags = G_SPAWN_DEFAULT | G_SPAWN_DO_NOT_REAP_CHILD; + +// if (!g_spawn_async (NULL, cmd, NULL, spawn_flags, NULL, NULL, &pid, err)){ + int exit_code = 0; + *err = NULL; // TODO remove? + + if (!g_spawn_async(NULL, cmd, NULL, spawn_flags, NULL, NULL, &pid, err)){ + _LOGW("An error occured while spawning wg-quick! (Error: %s)", (*err)->message); + return FALSE; + } + + pids_pending_add_wg(pid, plugin); + printf("PID of spawned command: %d\n", pid); + // note: exit code 139 means SIGSEV (program died because of segfault or so) + { + printf("WG set up and ready to go!\n"); + } + + _LOGI("Did a dummy connect"); return TRUE; }