summaryrefslogtreecommitdiffstats
path: root/pam_network_manager_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'pam_network_manager_helper.c')
-rw-r--r--pam_network_manager_helper.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/pam_network_manager_helper.c b/pam_network_manager_helper.c
new file mode 100644
index 0000000..7043ade
--- /dev/null
+++ b/pam_network_manager_helper.c
@@ -0,0 +1,252 @@
+/*
+ Copyright (c) 2015, Kristof Stahl <stahl@itsc.uni-luebeck.de>
+ Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+/*
+ See:
+
+ https://developer.gnome.org/libnm-glib/0.9/
+ https://developer.gnome.org/libnm-util/0.9/
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <security/pam_modules.h>
+
+#include <nm-client.h>
+#include <nm-remote-settings.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-wireless-security.h>
+#include <nm-setting-8021x.h>
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
+
+#include <config.h>
+
+
+#define UNUSED __attribute__((unused))
+
+
+static GByteArray * string_to_byte_array(const char *str) {
+ size_t len = strlen(str);
+ GByteArray *ba = g_byte_array_sized_new(len);
+
+ g_byte_array_append(ba, (const unsigned char *)str, len);
+
+ return ba;
+}
+
+static int setup_connection(NMConnection *con, const char *user, const char *pass) {
+ NMSetting *setting;
+
+ /* NMSettingConnection */
+ setting = nm_setting_connection_new();
+ g_object_set(G_OBJECT(setting),
+ NM_SETTING_CONNECTION_UUID, CONNECTION_UUID,
+ NM_SETTING_CONNECTION_ID, CONNECTION_ID,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+ NULL);
+ nm_connection_add_setting(con, setting);
+
+ /* NMSettingWireless */
+ setting = nm_setting_wireless_new();
+ GByteArray *ssid = string_to_byte_array(CONNECTION_SSID);
+ g_object_set(G_OBJECT(setting),
+ NM_SETTING_WIRELESS_SSID, ssid,
+ NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
+ NULL);
+ g_byte_array_unref(ssid);
+ nm_connection_add_setting(con, setting);
+
+ /* NMSettingWirelessSecurity */
+ setting = nm_setting_wireless_security_new();
+ g_object_set(G_OBJECT(setting),
+ NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, CONNECTION_AUTH_ALG,
+ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, CONNECTION_KEY_MGMT,
+ NULL);
+ nm_connection_add_setting(con, setting);
+
+ /* NMSetting8021x */
+ setting = nm_setting_802_1x_new();
+ /* TODO: Add certificate and subject check */
+ nm_setting_802_1x_add_eap_method(NM_SETTING_802_1X(setting), CONNECTION_EAP);
+ g_object_set(setting,
+ NM_SETTING_802_1X_IDENTITY, user,
+ NM_SETTING_802_1X_PASSWORD, pass,
+ NM_SETTING_802_1X_PHASE2_AUTHEAP, CONNECTION_PHASE2_AUTHEAP,
+ NULL);
+ nm_connection_add_setting(con, setting);
+
+ /* NMSettingIP4Config */
+ setting = nm_setting_ip4_config_new();
+ g_object_set(G_OBJECT(setting),
+ NM_SETTING_IP4_CONFIG_METHOD, "auto",
+ NULL);
+ nm_connection_add_setting(con, setting);
+
+ /* NMSettingIP6Config */
+ setting = nm_setting_ip6_config_new();
+ g_object_set(G_OBJECT(setting),
+ NM_SETTING_IP6_CONFIG_METHOD, "auto",
+ NULL);
+ nm_connection_add_setting(con, setting);
+
+ return PAM_SUCCESS;
+}
+
+typedef struct {
+ int ret;
+ GMainLoop *loop;
+ NMClient *client;
+} cb_arg;
+
+
+static NMDevice * find_device(NMClient *client) {
+ size_t i;
+ const GPtrArray *devices = nm_client_get_devices(client);
+
+ for (i = 0; i < devices->len; i++) {
+ NMDevice *device = g_ptr_array_index(devices, i);
+
+ if (nm_device_get_device_type(device) == NM_DEVICE_TYPE_WIFI)
+ return device;
+ }
+
+ return NULL;
+}
+
+static void state_cb(NMDevice *device, UNUSED GParamSpec *pspec, gpointer user_data) {
+ cb_arg *arg = user_data;
+
+ NMActiveConnection *active_connection = nm_device_get_active_connection(device);
+ if (!active_connection)
+ return;
+
+ NMActiveConnectionState state = nm_active_connection_get_state(active_connection);
+
+ if (state != NM_ACTIVE_CONNECTION_STATE_ACTIVATING && state != NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) {
+ if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
+ arg->ret = PAM_SUCCESS;
+
+ g_main_loop_quit(arg->loop);
+ }
+}
+
+static void activate_cb(UNUSED NMClient *client, UNUSED NMActiveConnection *active_connection, GError *error, gpointer user_data) {
+ cb_arg *arg = user_data;
+
+ if (error)
+ goto error;
+
+ return;
+
+ error:
+ g_main_loop_quit(arg->loop);
+}
+
+static void add_cb(UNUSED NMRemoteSettings *settings, NMRemoteConnection *con, GError *error, gpointer user_data) {
+ cb_arg *arg = user_data;
+
+ if (error)
+ goto error;
+
+ NMDevice *device = find_device(arg->client);
+ if (!device)
+ goto error;
+
+ g_signal_connect(device, "notify::state", G_CALLBACK(state_cb), arg);
+ nm_client_activate_connection(arg->client, NM_CONNECTION(con), device, NULL, activate_cb, arg);
+ return;
+
+ error:
+ g_main_loop_quit(arg->loop);
+}
+
+static int authenticate(const char *user, const char *pass) {
+ DBusGConnection *dbus = NULL;
+ NMRemoteSettings *settings = NULL;
+ NMConnection *con = NULL;
+ cb_arg arg = {};
+
+ arg.ret = PAM_SERVICE_ERR;
+ arg.loop = g_main_loop_new(NULL, FALSE);
+
+ arg.client = nm_client_new();
+ if (!arg.client)
+ goto end;
+
+ dbus = dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!dbus)
+ goto end;
+
+ settings = nm_remote_settings_new(dbus);
+ if (!settings)
+ goto end;
+
+ con = nm_connection_new();
+ setup_connection(con, user, pass);
+
+ if (!nm_remote_settings_add_connection_unsaved(settings, con, add_cb, &arg))
+ goto end;
+
+ arg.ret = PAM_AUTH_ERR;
+
+ g_main_loop_run(arg.loop);
+
+ end:
+ g_object_unref(con);
+
+ g_object_unref(settings);
+ dbus_g_connection_unref(dbus);
+ g_object_unref(arg.client);
+ g_main_loop_unref(arg.loop);
+
+ return arg.ret;
+}
+
+int main() {
+ char buf[1024];
+ size_t len = 0;
+
+ while (len < sizeof(buf)-1) {
+ size_t r = fread(buf+len, 1, sizeof(buf)-len-1, stdin);
+ if (!r)
+ break;
+
+ len += r;
+ }
+
+ buf[len] = 0;
+
+ const char *user = buf;
+ const char *pass = user + strlen(user) + 1;
+ if (pass > buf+len)
+ return PAM_SYSTEM_ERR;
+
+ return authenticate(user, pass);
+}