diff --git a/.gitignore b/.gitignore index 8d73a56..b25c15b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ *~ -pam_wlan.so -pam_wlan_test diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..74cddb8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 2.8.3) +project(PAM_NETWORK_MANAGER C) + +set(CMAKE_MODULE_PATH ${PAM_NETWORK_MANAGER_SOURCE_DIR}) + +find_package(PAM REQUIRED) +find_package(libnm_glib REQUIRED) + +set(PAM_MODULE_DIR "${CMAKE_INSTALL_PREFIX}/lib/security" CACHE STRING "PAM module directory") +set(PAM_NETWORK_MANAGER_HELPER_DIR "${CMAKE_INSTALL_PREFIX}/lib/security/pam_network_manager" CACHE STRING "pam_network_manager_helper install directory") + +set(CONNECTION_TIMEOUT "10" CACHE STRING "Connection timeout") +set(CONNECTION_ID "" CACHE STRING "Connection ID") +set(CONNECTION_UUID "" CACHE STRING "Connection UUID") +set(CONNECTION_SSID "" CACHE STRING "Connection SSID") +set(CONNECTION_AUTH_ALG "open" CACHE STRING "Connection authentication algorithm") +set(CONNECTION_KEY_MGMT "wpa-eap" CACHE STRING "Connection key management method") +set(CONNECTION_EAP "ttls" CACHE STRING "Connection EAP method") +set(CONNECTION_PHASE2_AUTHEAP "mschapv2" CACHE STRING "Connection EAP phase2 authentication method") + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${PAM_INCLUDE_DIR} ${LIBNM_GLIB_INCLUDE_DIRS}) +link_directories(${LIBNM_GLIB_LIBRARY_DIRS}) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +add_executable(pam_network_manager_helper pam_network_manager_helper.c) +set_property(TARGET pam_network_manager_helper PROPERTY COMPILE_DEFINITIONS "_DEFAULT_SOURCE;_BSD_SOURCE") +set_property(TARGET pam_network_manager_helper PROPERTY COMPILE_FLAGS "-std=c99 -Wall ${LIBNM_GLIB_CFLAGS_OTHER}") +set_property(TARGET pam_network_manager_helper PROPERTY LINK_FLAGS "${LIBNM_GLIB_LDFLAGS_OTHER}") +target_link_libraries(pam_network_manager_helper ${LIBNM_GLIB_LIBRARIES}) + +add_executable(pam_network_manager_test pam_network_manager.c) +set_property(TARGET pam_network_manager_test PROPERTY COMPILE_DEFINITIONS "_DEFAULT_SOURCE;_BSD_SOURCE;TEST") +set_property(TARGET pam_network_manager_test PROPERTY COMPILE_FLAGS "-std=c99 -Wall") + +add_library(pam_network_manager MODULE pam_network_manager.c) +set_property(TARGET pam_network_manager PROPERTY COMPILE_DEFINITIONS "_DEFAULT_SOURCE;_BSD_SOURCE") +set_property(TARGET pam_network_manager PROPERTY COMPILE_FLAGS "-std=c99 -Wall") +set_property(TARGET pam_network_manager PROPERTY PREFIX "") + +install(TARGETS pam_network_manager pam_network_manager_helper + RUNTIME DESTINATION "${PAM_NETWORK_MANAGER_HELPER_DIR}" + LIBRARY DESTINATION "${PAM_MODULE_DIR}" +) diff --git a/FindPAM.cmake b/FindPAM.cmake new file mode 100644 index 0000000..bccdf9b --- /dev/null +++ b/FindPAM.cmake @@ -0,0 +1,15 @@ +FIND_PATH(PAM_INCLUDE_DIR security/pam_modules.h) + +IF (PAM_INCLUDE_DIR) + SET(PAM_FOUND TRUE) +ENDIF (PAM_INCLUDE_DIR) + +IF (PAM_FOUND) + IF (NOT PAM_FIND_QUIETLY) + MESSAGE(STATUS "Found PAM (include path: ${PAM_INCLUDE_DIR})") + ENDIF (NOT PAM_FIND_QUIETLY) +ELSE (PAM_FOUND) + IF (PAM_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find PAM") + ENDIF (PAM_FIND_REQUIRED) +ENDIF (PAM_FOUND) diff --git a/Findlibnm_glib.cmake b/Findlibnm_glib.cmake new file mode 100644 index 0000000..3256940 --- /dev/null +++ b/Findlibnm_glib.cmake @@ -0,0 +1,20 @@ +# Defines the following variables: +# LIBNM_GLIB_FOUND +# LIBNM_GLIB_INCLUDE_DIRS +# LIBNM_GLIB_CFLAGS_OTHER +# LIBNM_GLIB_LIBRARIES +# LIBNM_GLIB_LIBRARY_DIRS +# LIBNM_GLIB_LDFLAGS_OTHER + + +find_package(PkgConfig REQUIRED QUIET) +pkg_check_modules(_LIBNM_GLIB libnm-glib) + +set(LIBNM_GLIB_INCLUDE_DIRS "${_LIBNM_GLIB_INCLUDE_DIRS}" CACHE STRING "Include directories for libnm-glib") +set(LIBNM_GLIB_CFLAGS_OTHER "${_LIBNM_GLIB_CFLAGS_OTHER}" CACHE STRING "Additional compiler flags for libnm-glib") +set(LIBNM_GLIB_LIBRARY_DIRS "${_LIBNM_GLIB_LIBRARY_DIRS}" CACHE STRING "Library directories for libnm-glib") +set(LIBNM_GLIB_LIBRARIES "${_LIBNM_GLIB_LIBRARIES}" CACHE STRING "Libraries for libnm-glib") +set(LIBNM_GLIB_LDFLAGS_OTHER "${_LIBNM_GLIB_LDFLAGS_OTHER}" CACHE STRING "Additional linker flags for libnm-glib") + +find_package_handle_standard_args(LIBNM_GLIB REQUIRED_VARS LIBNM_GLIB_LIBRARIES LIBNM_GLIB_INCLUDE_DIRS) +mark_as_advanced(LIBNM_GLIB_INCLUDE_DIRS LIBNM_GLIB_CFLAGS_OTHER LIBNM_GLIB_LIBRARIES LIBNM_GLIB_LIBRARY_DIRS LIBNM_GLIB_LDFLAGS_OTHER) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..28f845a --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Kristof Stahl +Copyright (c) 2015, Matthias Schiffer +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. diff --git a/Makefile b/Makefile deleted file mode 100644 index 0f62ec7..0000000 --- a/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -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/config.h.in b/config.h.in new file mode 100644 index 0000000..a0ab8bc --- /dev/null +++ b/config.h.in @@ -0,0 +1,41 @@ +/* + Copyright (c) 2015, Kristof Stahl + Copyright (c) 2015, Matthias Schiffer + 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. +*/ + + +#pragma once + + +#define PAM_NETWORK_MANAGER_HELPER "@PAM_NETWORK_MANAGER_HELPER_DIR@/pam_network_manager_helper" + + +#define CONNECTION_TIMEOUT @CONNECTION_TIMEOUT@ +#define CONNECTION_ID "@CONNECTION_ID@" +#define CONNECTION_UUID "@CONNECTION_UUID@" +#define CONNECTION_SSID "@CONNECTION_SSID@" +#define CONNECTION_AUTH_ALG "@CONNECTION_AUTH_ALG@" +#define CONNECTION_KEY_MGMT "@CONNECTION_KEY_MGMT@" +#define CONNECTION_EAP "@CONNECTION_EAP@" +#define CONNECTION_PHASE2_AUTHEAP "@CONNECTION_PHASE2_AUTHEAP@" diff --git a/pam_network_manager.c b/pam_network_manager.c new file mode 100644 index 0000000..15a2f3c --- /dev/null +++ b/pam_network_manager.c @@ -0,0 +1,101 @@ +/* + Copyright (c) 2015, Kristof Stahl + Copyright (c) 2015, Matthias Schiffer + 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: + + 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 +*/ + +#define PAM_SM_AUTH + +#include +#include + +#include +#include + +#include + +#include + + +#define UNUSED __attribute__((unused)) + + +static int authenticate(const char *user, const char *pass) { + FILE *stream = popen(PAM_NETWORK_MANAGER_HELPER, "we"); + + fputs(user, stream); + fputc(0, stream); + fputs(pass, stream); + + int status = pclose(stream); + + if (WIFEXITED(status)) + return WEXITSTATUS(status); + else + return PAM_SYSTEM_ERR; +} + +#ifdef TEST + +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: pam_network_manager_test \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, UNUSED int flags, UNUSED int argc, 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 diff --git a/pam_wlan.c b/pam_network_manager_helper.c similarity index 65% rename from pam_wlan.c rename to pam_network_manager_helper.c index 181bb6a..e4153f4 100644 --- a/pam_wlan.c +++ b/pam_network_manager_helper.c @@ -26,31 +26,14 @@ /* - 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 + See: https://developer.gnome.org/libnm-glib/0.9/ https://developer.gnome.org/libnm-util/0.9/ */ -#define PAM_SM_AUTH - -#ifdef TEST - #include - -#endif - -#include #include -#include - -#include -#include #include @@ -63,16 +46,10 @@ #include #include +#include -#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" +#define UNUSED __attribute__((unused)) static GByteArray * string_to_byte_array(const char *str) { @@ -135,7 +112,7 @@ static int setup_connection(NMConnection *con, const char *user, const char *pas /* NMSettingIP6Config */ setting = nm_setting_ip6_config_new(); g_object_set(G_OBJECT(setting), - NM_SETTING_IP6_CONFIG_METHOD, "auto", + NM_SETTING_IP6_CONFIG_METHOD, "link-local", NULL); nm_connection_add_setting(con, setting); @@ -146,6 +123,7 @@ typedef struct { int ret; GMainLoop *loop; NMClient *client; + NMRemoteConnection *con; } cb_arg; @@ -163,7 +141,14 @@ static NMDevice * find_device(NMClient *client) { return NULL; } -static void state_cb(NMDevice *device, GParamSpec *pspec, gpointer user_data) { +static gboolean timeout_cb(gpointer user_data) { + cb_arg *arg = user_data; + g_main_loop_quit(arg->loop); + + return FALSE; +} + +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); @@ -180,20 +165,19 @@ static void state_cb(NMDevice *device, GParamSpec *pspec, gpointer user_data) { } } -static void activate_cb(NMClient *client, NMActiveConnection *active_connection, GError *error, gpointer user_data) { +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(NMRemoteSettings *settings, NMRemoteConnection *con, GError *error, gpointer user_data) { +static void add_cb(UNUSED NMRemoteSettings *settings, NMRemoteConnection *con, GError *error, gpointer user_data) { cb_arg *arg = user_data; if (error) @@ -203,21 +187,86 @@ static void add_cb(NMRemoteSettings *settings, NMRemoteConnection *con, GError * if (!device) goto error; + arg->con = con; + 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); + + g_timeout_add_seconds(CONNECTION_TIMEOUT, timeout_cb, arg); return; error: g_main_loop_quit(arg->loop); } -static int do_authenticate(const char *user, const char *pass) { +static void delete_cb(UNUSED NMRemoteConnection *connection, UNUSED GError *error, gpointer user_data) { + cb_arg *arg = user_data; + g_main_loop_quit(arg->loop); +} + +static void read_cb(UNUSED NMRemoteSettings *settings, gpointer user_data) { + cb_arg *arg = user_data; + g_main_loop_quit(arg->loop); +} + +static gboolean match_connection_user(NMConnection *connection, const char *user) { + NMSetting8021x *setting = nm_connection_get_setting_802_1x(connection); + if (!setting) + return FALSE; + + const char *identity = nm_setting_802_1x_get_identity(setting); + if (!identity) + return FALSE; + + return (strcmp(identity, user) == 0); +} + +static gboolean is_connection_active(NMConnection *connection, cb_arg *arg) { + NMDevice *device = find_device(arg->client); + if (!device) + return FALSE; + + NMActiveConnection *active_connection = nm_device_get_active_connection(device); + if (!active_connection) + return FALSE; + + if (strcmp(nm_connection_get_path(connection), nm_active_connection_get_connection(active_connection)) != 0) + return FALSE; + + NMActiveConnectionState state = nm_active_connection_get_state(active_connection); + + return (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED); +} + +static gboolean handle_old_connection(NMRemoteSettings *settings, const char *user, cb_arg *arg) { + NMRemoteConnection *con = NULL; + + g_signal_connect(settings, NM_REMOTE_SETTINGS_CONNECTIONS_READ, + G_CALLBACK(read_cb), arg); + + g_main_loop_run(arg->loop); + + con = nm_remote_settings_get_connection_by_uuid(settings, CONNECTION_UUID); + if (!con) + goto end; + + if (match_connection_user(NM_CONNECTION(con), user) && is_connection_active(NM_CONNECTION(con), arg)) + return TRUE; + + nm_remote_connection_delete(con, delete_cb, arg); + g_main_loop_run(arg->loop); + + end: + return FALSE; +} + +static int 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.ret = PAM_SERVICE_ERR; arg.loop = g_main_loop_new(NULL, FALSE); arg.client = nm_client_new(); @@ -232,7 +281,13 @@ static int do_authenticate(const char *user, const char *pass) { if (!settings) goto end; + if (handle_old_connection(settings, user, &arg)) { + arg.ret = PAM_IGNORE; + goto end; + } + con = nm_connection_new(); + setup_connection(con, user, pass); if (!nm_remote_settings_add_connection_unsaved(settings, con, add_cb, &arg)) @@ -242,81 +297,47 @@ static int do_authenticate(const char *user, const char *pass) { g_main_loop_run(arg.loop); - end: - g_object_unref(con); + if (arg.ret == PAM_SUCCESS) + goto end; - g_object_unref(settings); - dbus_g_connection_unref(dbus); - g_object_unref(arg.client); + /* Error */ + if (arg.con) { + nm_remote_connection_delete(arg.con, delete_cb, &arg); + g_main_loop_run(arg.loop); + } + + end: + if (con) + g_object_unref(con); + if (settings) + g_object_unref(settings); + if (dbus) + dbus_g_connection_unref(dbus); + if (arg.client) + 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; +int main() { + char buf[1024]; + size_t len = 0; - pid_t pid = fork(); - if (pid < 0) - return PAM_SYSTEM_ERR; + while (len < sizeof(buf)-1) { + size_t r = fread(buf+len, 1, sizeof(buf)-len-1, stdin); + if (!r) + break; - if (pid == 0) { - close(fds[0]); - status = do_authenticate(user, pass); - write(fds[1], &status, sizeof(status)); - close(fds[1]); - _exit(0); + len += r; } - close(fds[1]); + buf[len] = 0; - 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 \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; + const char *user = buf; + const char *pass = user + strlen(user) + 1; + if (pass > buf+len) + return PAM_SYSTEM_ERR; return authenticate(user, pass); } -#endif