diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | pam_wlan.c | 319 |
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 |