From 1b4f1c2d8524058644333a4cb4d182914fc0c600 Mon Sep 17 00:00:00 2001 From: Anton Golubev Date: Mon, 7 Aug 2023 18:11:20 +0300 Subject: [PATCH 1/3] Change the display order of PAM messages - do not show a message if the face model is not found - show a message if the user could not be recognized - show prompt if face model found (and enabled option) - enable the "detection_notice" option by default as this will only be shown to users who created the face model --- howdy/src/compare.py | 5 +++++ howdy/src/config.ini | 2 +- howdy/src/pam/main.cc | 40 ++++++++++++++++++++++++++++++++-------- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/howdy/src/compare.py b/howdy/src/compare.py index 99f5285b..7efb41c9 100644 --- a/howdy/src/compare.py +++ b/howdy/src/compare.py @@ -140,6 +140,11 @@ def send_to_ui(type, message): if len(models) < 1: exit(10) +# notify the PAM module so that it issues a message +sys.stdout.flush() +print("HAS_MODEL") +sys.stdout.flush() + # Read config from disk config = configparser.ConfigParser() config.read(PATH + "/config.ini") diff --git a/howdy/src/config.ini b/howdy/src/config.ini index 27ab8231..0514f3ed 100644 --- a/howdy/src/config.ini +++ b/howdy/src/config.ini @@ -3,7 +3,7 @@ [core] # Print that face detection is being attempted -detection_notice = false +detection_notice = true # Print that face detection has timed out timeout_notice = true diff --git a/howdy/src/pam/main.cc b/howdy/src/pam/main.cc index 290a549f..e60a9153 100644 --- a/howdy/src/pam/main.cc +++ b/howdy/src/pam/main.cc @@ -66,10 +66,10 @@ auto howdy_error(int status, switch (status) { case CompareError::NO_FACE_MODEL: - conv_function(PAM_ERROR_MSG, S("There is no face model known")); syslog(LOG_NOTICE, "Failure, no face model known"); break; case CompareError::TIMEOUT_REACHED: + conv_function(PAM_ERROR_MSG, S("Failure, timeout reached")); syslog(LOG_ERR, "Failure, timeout reached"); break; case CompareError::ABORT: @@ -244,12 +244,7 @@ auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv, textdomain(GETTEXT_PACKAGE); // If enabled, send a notice to the user that facial login is being attempted - if (config.GetBoolean("core", "detection_notice", false)) { - if ((conv_function(PAM_TEXT_INFO, S("Attempting facial authentication"))) != - PAM_SUCCESS) { - syslog(LOG_ERR, "Failed to send detection notice"); - } - } + bool detection_notice = config.GetBoolean("core", "detection_notice", true); // Get the username from PAM, needed to match correct face model char *username = nullptr; @@ -259,18 +254,46 @@ auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv, return pam_res; } + int conv_pipe[2]; + + if (pipe (conv_pipe)) { + syslog(LOG_ERR, "Pipe failed."); + return PAM_SYSTEM_ERR; + } + + posix_spawn_file_actions_t action; + posix_spawn_file_actions_init(&action); + posix_spawn_file_actions_addclose(&action, conv_pipe[0]); + posix_spawn_file_actions_adddup2(&action, conv_pipe[1], 1); + posix_spawn_file_actions_addclose(&action, conv_pipe[1]); + const char *const args[] = {PYTHON_EXECUTABLE, // NOLINT COMPARE_PROCESS_PATH, username, nullptr}; pid_t child_pid; // Start the python subprocess - if (posix_spawnp(&child_pid, PYTHON_EXECUTABLE, nullptr, nullptr, + if (posix_spawnp(&child_pid, PYTHON_EXECUTABLE, &action, nullptr, const_cast(args), nullptr) != 0) { syslog(LOG_ERR, "Can't spawn the howdy process: %s (%d)", strerror(errno), errno); return PAM_SYSTEM_ERR; } + // show the PAM message from the compare script + optional_task child_conv([&] { + char buffer[100]; + while(read(conv_pipe[0], buffer, 100)) { + if (!strncmp(buffer, "HAS_MODEL", 9) && detection_notice) { + if ((conv_function(PAM_TEXT_INFO, + S("Attempting facial authentication"))) != + PAM_SUCCESS) { + syslog(LOG_ERR, "Failed to send detection notice"); + } + } + } + }); + child_conv.activate(); + // NOTE: We should replace mutex and condition_variable by atomic wait, but // it's too recent (C++20) std::mutex mutx; @@ -347,6 +370,7 @@ auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv, // The compare process has finished its execution child_task.stop(false); + child_conv.stop(true); // Get python process status code int status = child_task.get(); From a5184dc663969960dd1cd7a543ff2df646e22392 Mon Sep 17 00:00:00 2001 From: Anton Golubev Date: Fri, 11 Aug 2023 10:10:45 +0300 Subject: [PATCH 2/3] Fixup: check if the user has a model directry in the PAM module Revert the use of pipe to signal from the script that the face model is not found and do it directly in the module. --- howdy/src/compare.py | 5 ----- howdy/src/pam/main.cc | 42 +++++++++++------------------------------- 2 files changed, 11 insertions(+), 36 deletions(-) diff --git a/howdy/src/compare.py b/howdy/src/compare.py index 7efb41c9..99f5285b 100644 --- a/howdy/src/compare.py +++ b/howdy/src/compare.py @@ -140,11 +140,6 @@ def send_to_ui(type, message): if len(models) < 1: exit(10) -# notify the PAM module so that it issues a message -sys.stdout.flush() -print("HAS_MODEL") -sys.stdout.flush() - # Read config from disk config = configparser.ConfigParser() config.read(PATH + "/config.ini") diff --git a/howdy/src/pam/main.cc b/howdy/src/pam/main.cc index e60a9153..89c7e985 100644 --- a/howdy/src/pam/main.cc +++ b/howdy/src/pam/main.cc @@ -243,9 +243,6 @@ auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv, bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); textdomain(GETTEXT_PACKAGE); - // If enabled, send a notice to the user that facial login is being attempted - bool detection_notice = config.GetBoolean("core", "detection_notice", true); - // Get the username from PAM, needed to match correct face model char *username = nullptr; if ((pam_res = pam_get_user(pamh, const_cast(&username), @@ -254,46 +251,30 @@ auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv, return pam_res; } - int conv_pipe[2]; - - if (pipe (conv_pipe)) { - syslog(LOG_ERR, "Pipe failed."); - return PAM_SYSTEM_ERR; + // pre-check if this user has face model file + auto model_path = std::string("/etc/howdy/models/") + username + ".dat"; + if (!std::ifstream(model_path)) { + return howdy_status(username, CompareError::NO_FACE_MODEL, config, + conv_function); + } else if (config.GetBoolean("core", "detection_notice", true)) { + if ((conv_function(PAM_TEXT_INFO, S("Attempting facial authentication"))) != + PAM_SUCCESS) { + syslog(LOG_ERR, "Failed to send detection notice"); + } } - posix_spawn_file_actions_t action; - posix_spawn_file_actions_init(&action); - posix_spawn_file_actions_addclose(&action, conv_pipe[0]); - posix_spawn_file_actions_adddup2(&action, conv_pipe[1], 1); - posix_spawn_file_actions_addclose(&action, conv_pipe[1]); - const char *const args[] = {PYTHON_EXECUTABLE, // NOLINT COMPARE_PROCESS_PATH, username, nullptr}; pid_t child_pid; // Start the python subprocess - if (posix_spawnp(&child_pid, PYTHON_EXECUTABLE, &action, nullptr, + if (posix_spawnp(&child_pid, PYTHON_EXECUTABLE, nullptr, nullptr, const_cast(args), nullptr) != 0) { syslog(LOG_ERR, "Can't spawn the howdy process: %s (%d)", strerror(errno), errno); return PAM_SYSTEM_ERR; } - // show the PAM message from the compare script - optional_task child_conv([&] { - char buffer[100]; - while(read(conv_pipe[0], buffer, 100)) { - if (!strncmp(buffer, "HAS_MODEL", 9) && detection_notice) { - if ((conv_function(PAM_TEXT_INFO, - S("Attempting facial authentication"))) != - PAM_SUCCESS) { - syslog(LOG_ERR, "Failed to send detection notice"); - } - } - } - }); - child_conv.activate(); - // NOTE: We should replace mutex and condition_variable by atomic wait, but // it's too recent (C++20) std::mutex mutx; @@ -370,7 +351,6 @@ auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv, // The compare process has finished its execution child_task.stop(false); - child_conv.stop(true); // Get python process status code int status = child_task.get(); From 40720bbf87a7d650b629d4c86f8c4ea2c83ea3a1 Mon Sep 17 00:00:00 2001 From: Anton Golubev Date: Wed, 6 Sep 2023 11:49:43 +0300 Subject: [PATCH 3/3] Fixup: don't else after return and no det. notice --- howdy/src/config.ini | 2 +- howdy/src/pam/main.cc | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/howdy/src/config.ini b/howdy/src/config.ini index 0514f3ed..27ab8231 100644 --- a/howdy/src/config.ini +++ b/howdy/src/config.ini @@ -3,7 +3,7 @@ [core] # Print that face detection is being attempted -detection_notice = true +detection_notice = false # Print that face detection has timed out timeout_notice = true diff --git a/howdy/src/pam/main.cc b/howdy/src/pam/main.cc index 89c7e985..69d9d535 100644 --- a/howdy/src/pam/main.cc +++ b/howdy/src/pam/main.cc @@ -256,7 +256,9 @@ auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv, if (!std::ifstream(model_path)) { return howdy_status(username, CompareError::NO_FACE_MODEL, config, conv_function); - } else if (config.GetBoolean("core", "detection_notice", true)) { + } + + if (config.GetBoolean("core", "detection_notice", true)) { if ((conv_function(PAM_TEXT_INFO, S("Attempting facial authentication"))) != PAM_SUCCESS) { syslog(LOG_ERR, "Failed to send detection notice");