summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile7
-rw-r--r--pam_wlan.c319
3 files changed, 329 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8d73a56
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*~
+pam_wlan.so
+pam_wlan_test
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0f62ec7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+all : pam_wlan.so pam_wlan_test
+
+pam_wlan.so : pam_wlan.c
+ $(CC) -fPIC -shared -o pam_wlan.so pam_wlan.c $(shell pkg-config --cflags --libs libnm-glib) -Wall
+
+pam_wlan_test : pam_wlan.c
+ $(CC) -g -DTEST -o pam_wlan_test pam_wlan.c $(shell pkg-config --cflags --libs libnm-glib) -Wall
diff --git a/pam_wlan.c b/pam_wlan.c
new file mode 100644
index 0000000..514f725
--- /dev/null
+++ b/pam_wlan.c
@@ -0,0 +1,319 @@
+/*
+ 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.
+*/
+
+
+/*
+ Helpful pages:
+
+ http://www.linux-pam.org/Linux-PAM-html/mwg-expected-of-module-auth.html
+ http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
+ http://www.linux-pam.org/Linux-PAM-html/mwg-see-programming-libs.html
+ some (bad) example.. https://github.com/beatgammit/simple-pam/blob/master/src/mypam.c
+
+ https://developer.gnome.org/libnm-glib/0.9/
+ https://developer.gnome.org/libnm-util/0.9/
+*/
+
+#define PAM_SM_AUTH
+
+#ifdef TEST
+
+#include <stdio.h>
+
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/wait.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>
+
+
+#define TIMEOUT 10
+#define CONNECTION_ID "UzL-Wlan"
+#define CONNECTION_UUID "af7b7f0b-3e09-442e-8841-f7d7482fa7b0"
+
+#define CONNECTION_SSID "UzL-Wlan"
+#define CONNECTION_AUTH_ALG "open"
+#define CONNECTION_KEY_MGMT "wpa-eap"
+#define CONNECTION_EAP "ttls"
+#define CONNECTION_PHASE2_AUTHEAP "mschapv2"
+
+
+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(NMActiveConnection *active_connection, GParamSpec *pspec, gpointer user_data) {
+ cb_arg *arg = user_data;
+
+ NMActiveConnectionState state = nm_active_connection_get_state(active_connection);
+
+ if (state != NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
+ if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
+ arg->ret = PAM_SUCCESS;
+
+ g_main_loop_quit(arg->loop);
+ }
+}
+
+static void activate_cb(NMClient *client, NMActiveConnection *active_connection, GError *error, gpointer user_data) {
+ cb_arg *arg = user_data;
+
+ if (error)
+ goto error;
+
+ g_signal_connect(active_connection, "notify::state", G_CALLBACK(state_cb), arg);
+ state_cb(active_connection, NULL, arg);
+
+ return;
+
+ error:
+ g_main_loop_quit(arg->loop);
+}
+
+static void add_cb(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;
+
+ nm_client_activate_connection(arg->client, NM_CONNECTION(con), device, NULL, activate_cb, arg);
+ return;
+
+ error:
+ g_main_loop_quit(arg->loop);
+}
+
+static int do_authenticate(const char *user, const char *pass) {
+ DBusGConnection *dbus = NULL;
+ NMRemoteSettings *settings = NULL;
+ NMConnection *con = NULL;
+ cb_arg arg = {};
+
+ arg.ret = PAM_SYSTEM_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;
+}
+
+static int authenticate(const char *user, const char *pass) {
+ int status;
+ int fds[2];
+ if (pipe(fds))
+ return PAM_SYSTEM_ERR;
+
+ pid_t pid = fork();
+ if (pid < 0)
+ return PAM_SYSTEM_ERR;
+
+ if (pid == 0) {
+ close(fds[0]);
+ status = do_authenticate(user, pass);
+ write(fds[1], &status, sizeof(status));
+ close(fds[1]);
+ _exit(0);
+ }
+
+ close(fds[1]);
+
+ if (waitpid(pid, NULL, 0) < 0)
+ goto error;
+
+ if (read(fds[0], &status, sizeof(status)) != sizeof(status))
+ goto error;
+
+ close(fds[0]);
+ return status;
+
+ error:
+ close(fds[0]);
+ return PAM_SYSTEM_ERR;
+}
+
+#ifdef TEST
+
+int main(int argc, char *argv[]) {
+ if (argc != 2) {
+ fprintf(stderr, "Usage: pam_wlan_test <user>\n");
+ return 1;
+ }
+
+ char *pass = getpass("Password: ");
+
+ fprintf(stderr, "Return: %i\n", authenticate(argv[1], pass));
+
+ return 0;
+}
+
+#else
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, __attribute__((unused)) int flags, __attribute__((unused)) int argc, __attribute__((unused)) const char **argv) {
+ const void *pass;
+ const void *user;
+ int result;
+
+ result = pam_get_item(pamh, PAM_USER, &user);
+ if (result != PAM_SUCCESS)
+ return PAM_INCOMPLETE;
+
+ result = pam_get_item(pamh, PAM_AUTHTOK, &pass);
+ if (result != PAM_SUCCESS)
+ return PAM_INCOMPLETE;
+
+ return authenticate(user, pass);
+}
+#endif