From de848d9e9146434817c65d74d1d0313e9d729462 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 14:01:37 +0200 Subject: Adding upstream version 1.4.0. Signed-off-by: Daniel Baumann --- modules/pam_selinux/pam_selinux.c | 807 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 807 insertions(+) create mode 100644 modules/pam_selinux/pam_selinux.c (limited to 'modules/pam_selinux/pam_selinux.c') diff --git a/modules/pam_selinux/pam_selinux.c b/modules/pam_selinux/pam_selinux.c new file mode 100644 index 0000000..06c3ce6 --- /dev/null +++ b/modules/pam_selinux/pam_selinux.c @@ -0,0 +1,807 @@ +/****************************************************************************** + * A module for Linux-PAM that will set the default security context after login + * via PAM. + * + * Copyright (c) 2003-2008 Red Hat, Inc. + * Written by Dan Walsh + * Additional improvements by Tomas Mraz + * + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "pam_inline.h" + +#include +#include +#include +#include + +#ifdef HAVE_LIBAUDIT +#include +#include +#endif + +/* Send audit message */ +static void +send_audit_message(const pam_handle_t *pamh, int success, const char *default_context, + const char *selected_context) +{ +#ifdef HAVE_LIBAUDIT + char *msg = NULL; + int audit_fd = audit_open(); + char *default_raw = NULL; + char *selected_raw = NULL; + const void *tty = NULL, *rhost = NULL; + if (audit_fd < 0) { + if (errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT) { + goto fallback; /* No audit support in kernel */ + } + pam_syslog(pamh, LOG_ERR, "Error connecting to audit system: %m"); + goto fallback; + } + (void)pam_get_item(pamh, PAM_TTY, &tty); + (void)pam_get_item(pamh, PAM_RHOST, &rhost); + if (selinux_trans_to_raw_context(default_context, &default_raw) < 0) { + pam_syslog(pamh, LOG_ERR, "Error translating default context '%s'.", default_context); + default_raw = NULL; + } + if (selinux_trans_to_raw_context(selected_context, &selected_raw) < 0) { + pam_syslog(pamh, LOG_ERR, "Error translating selected context '%s'.", selected_context); + selected_raw = NULL; + } + if (asprintf(&msg, "pam: default-context=%s selected-context=%s", + default_raw ? default_raw : (default_context ? default_context : "?"), + selected_raw ? selected_raw : (selected_context ? selected_context : "?")) < 0) { + msg = NULL; /* asprintf leaves msg in undefined state on failure */ + pam_syslog(pamh, LOG_ERR, "Error allocating memory."); + goto fallback; + } + if (audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE, + msg, rhost, NULL, tty, success) <= 0) { + pam_syslog(pamh, LOG_ERR, "Error sending audit message: %m"); + goto fallback; + } + goto cleanup; + + fallback: +#endif /* HAVE_LIBAUDIT */ + pam_syslog(pamh, LOG_NOTICE, "pam: default-context=%s selected-context=%s success %d", + default_context, selected_context, success); + +#ifdef HAVE_LIBAUDIT + cleanup: + free(msg); + freecon(default_raw); + freecon(selected_raw); + if (audit_fd >= 0) + close(audit_fd); +#endif /* HAVE_LIBAUDIT */ +} + +static int +send_text (pam_handle_t *pamh, const char *text, int debug) +{ + if (debug) + pam_syslog(pamh, LOG_NOTICE, "%s", text); + return pam_info (pamh, "%s", text); +} + +/* + * This function sends a message to the user and gets the response. The caller + * is responsible for freeing the responses. + */ +static int +query_response (pam_handle_t *pamh, const char *text, const char *def, + char **response, int debug) +{ + int rc; + if (def) + rc = pam_prompt (pamh, PAM_PROMPT_ECHO_ON, response, "%s [%s] ", text, def); + else + rc = pam_prompt (pamh, PAM_PROMPT_ECHO_ON, response, "%s ", text); + + if (*response == NULL) { + rc = PAM_CONV_ERR; + } + + if (rc != PAM_SUCCESS) { + pam_syslog(pamh, LOG_WARNING, "No response to query: %s", text); + } else if (debug) + pam_syslog(pamh, LOG_NOTICE, "%s %s", text, *response); + return rc; +} + +static char * +config_context (pam_handle_t *pamh, const char *defaultcon, int use_current_range, int debug) +{ + char *newcon = NULL; + context_t new_context; + int mls_enabled = is_selinux_mls_enabled(); + char *response=NULL; + char *type=NULL; + char resp_val = 0; + + pam_prompt (pamh, PAM_TEXT_INFO, NULL, _("The default security context is %s."), defaultcon); + + while (1) { + if (query_response(pamh, + _("Would you like to enter a different role or level?"), "n", + &response, debug) == PAM_SUCCESS) { + resp_val = response[0]; + _pam_drop(response); + } else { + resp_val = 'N'; + } + if ((resp_val == 'y') || (resp_val == 'Y')) + { + if ((new_context = context_new(defaultcon)) == NULL) + goto fail_set; + + /* Allow the user to enter role and level individually */ + if (query_response(pamh, _("role:"), context_role_get(new_context), + &response, debug) == PAM_SUCCESS && response[0]) { + if (get_default_type(response, &type)) { + pam_prompt(pamh, PAM_ERROR_MSG, NULL, + _("There is no default type for role %s."), response); + _pam_drop(response); + continue; + } else { + if (context_role_set(new_context, response)) + goto fail_set; + if (context_type_set (new_context, type)) + goto fail_set; + _pam_drop(type); + } + } + _pam_drop(response); + + if (mls_enabled) + { + if (use_current_range) { + char *mycon = NULL; + context_t my_context; + + if (getcon(&mycon) != 0) + goto fail_set; + my_context = context_new(mycon); + if (my_context == NULL) { + freecon(mycon); + goto fail_set; + } + freecon(mycon); + if (context_range_set(new_context, context_range_get(my_context))) { + context_free(my_context); + goto fail_set; + } + context_free(my_context); + } else if (query_response(pamh, _("level:"), context_range_get(new_context), + &response, debug) == PAM_SUCCESS && response[0]) { + if (context_range_set(new_context, response)) + goto fail_set; + } + _pam_drop(response); + } + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Selected Security Context %s", context_str(new_context)); + + /* Get the string value of the context and see if it is valid. */ + if (!security_check_context(context_str(new_context))) { + newcon = strdup(context_str(new_context)); + if (newcon == NULL) + goto fail_set; + context_free(new_context); + + /* we have to check that this user is allowed to go into the + range they have specified ... role is tied to an seuser, so that'll + be checked at setexeccon time */ + if (mls_enabled && + selinux_check_access(defaultcon, newcon, "context", "contains", NULL) != 0) { + pam_syslog(pamh, LOG_NOTICE, "Security context %s is not allowed for %s", defaultcon, newcon); + + send_audit_message(pamh, 0, defaultcon, newcon); + + free(newcon); + goto fail_range; + } + return newcon; + } + else { + send_audit_message(pamh, 0, defaultcon, context_str(new_context)); + send_text(pamh,_("This is not a valid security context."),debug); + } + context_free(new_context); /* next time around allocates another */ + } + else + return strdup(defaultcon); + } /* end while */ + + return NULL; + + fail_set: + free(type); + _pam_drop(response); + context_free (new_context); + send_audit_message(pamh, 0, defaultcon, NULL); + fail_range: + return NULL; +} + +static char * +context_from_env (pam_handle_t *pamh, const char *defaultcon, int env_params, int use_current_range, int debug) +{ + char *newcon = NULL; + context_t new_context; + context_t my_context = NULL; + int mls_enabled = is_selinux_mls_enabled(); + const char *env = NULL; + char *type = NULL; + int fail = 1; + + if ((new_context = context_new(defaultcon)) == NULL) + goto fail_set; + + if (env_params && (env = pam_getenv(pamh, "SELINUX_ROLE_REQUESTED")) != NULL && env[0] != '\0') { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Requested role: %s", env); + + if (get_default_type(env, &type)) { + pam_syslog(pamh, LOG_NOTICE, "No default type for role %s", env); + goto fail_set; + } else { + if (context_role_set(new_context, env)) + goto fail_set; + if (context_type_set(new_context, type)) + goto fail_set; + } + } + + if (mls_enabled) { + if ((env = pam_getenv(pamh, "SELINUX_USE_CURRENT_RANGE")) != NULL && env[0] == '1') { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "SELINUX_USE_CURRENT_RANGE is set"); + use_current_range = 1; + } + + if (use_current_range) { + char *mycon = NULL; + + if (getcon(&mycon) != 0) + goto fail_set; + my_context = context_new(mycon); + if (my_context == NULL) { + freecon(mycon); + goto fail_set; + } + freecon(mycon); + env = context_range_get(my_context); + } else { + env = pam_getenv(pamh, "SELINUX_LEVEL_REQUESTED"); + } + + if (env != NULL && env[0] != '\0') { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Requested level: %s", env); + if (context_range_set(new_context, env)) + goto fail_set; + } + } + + newcon = strdup(context_str(new_context)); + if (newcon == NULL) + goto fail_set; + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Selected Security Context %s", newcon); + + /* Get the string value of the context and see if it is valid. */ + if (security_check_context(newcon)) { + pam_syslog(pamh, LOG_NOTICE, "Not a valid security context %s", newcon); + + goto fail_set; + } + + /* we have to check that this user is allowed to go into the + range they have specified ... role is tied to an seuser, so that'll + be checked at setexeccon time */ + if (mls_enabled && + selinux_check_access(defaultcon, newcon, "context", "contains", NULL) != 0) { + pam_syslog(pamh, LOG_NOTICE, "Security context %s is not allowed for %s", defaultcon, newcon); + + goto fail_set; + } + + fail = 0; + + fail_set: + free(type); + context_free(my_context); + context_free(new_context); + if (fail) { + send_audit_message(pamh, 0, defaultcon, newcon); + freecon(newcon); + newcon = NULL; + } + return newcon; +} + +#define DATANAME "pam_selinux_context" +typedef struct { + char *exec_context; + char *prev_exec_context; + char *default_user_context; + char *tty_context; + char *prev_tty_context; + char *tty_path; +} module_data_t; + +static void +free_module_data(module_data_t *data) +{ + free(data->tty_path); + freecon(data->prev_tty_context); + freecon(data->tty_context); + freecon(data->default_user_context); + freecon(data->prev_exec_context); + if (data->exec_context != data->default_user_context) + freecon(data->exec_context); + memset(data, 0, sizeof(*data)); + free(data); +} + +static void +cleanup(pam_handle_t *pamh UNUSED, void *data, int err UNUSED) +{ + free_module_data(data); +} + +static const module_data_t * +get_module_data(const pam_handle_t *pamh) +{ + const void *data; + + return (pam_get_data(pamh, DATANAME, &data) == PAM_SUCCESS) ? data : NULL; +} + +static const char * +get_item(const pam_handle_t *pamh, int item_type) +{ + const void *item; + + return (pam_get_item(pamh, item_type, &item) == PAM_SUCCESS) ? item : NULL; +} + +static int +set_exec_context(const pam_handle_t *pamh, const char *context) +{ + if (setexeccon(context) == 0) + return 0; + pam_syslog(pamh, LOG_ERR, "Setting executable context \"%s\" failed: %m", + context ? context : ""); + return -1; +} + +static int +set_file_context(const pam_handle_t *pamh, const char *context, + const char *file) +{ + if (!file) + return 0; + if (setfilecon(file, context) == 0 || errno == ENOENT) + return 0; + pam_syslog(pamh, LOG_ERR, "Setting file context \"%s\" failed for %s: %m", + context ? context : "", file); + return -1; +} + +static int +compute_exec_context(pam_handle_t *pamh, module_data_t *data, + int select_context, int use_current_range, + int env_params, int debug) +{ + const char *username; + +#ifdef HAVE_GETSEUSER + const char *service; +#endif + char *seuser = NULL; + char *level = NULL; + char **contextlist = NULL; + int num_contexts = 0; + const struct passwd *pwd; + + if (!(username = get_item(pamh, PAM_USER))) { + pam_syslog(pamh, LOG_ERR, "Cannot obtain the user name"); + return PAM_USER_UNKNOWN; + } + + if ((pwd = pam_modutil_getpwnam(pamh, username)) != NULL) { + username = pwd->pw_name; + } /* ignore error and keep using original username */ + + /* compute execute context */ +#ifdef HAVE_GETSEUSER + if (!(service = get_item(pamh, PAM_SERVICE))) { + pam_syslog(pamh, LOG_ERR, "Cannot obtain the service name"); + return PAM_SESSION_ERR; + } + if (getseuser(username, service, &seuser, &level) == 0) { +#else + if (getseuserbyname(username, &seuser, &level) == 0) { +#endif + num_contexts = get_ordered_context_list_with_level(seuser, level, NULL, + &contextlist); + if (debug) + pam_syslog(pamh, LOG_DEBUG, "Username= %s SELinux User= %s Level= %s", + username, seuser, level); + free(level); + } + if (num_contexts > 0) { + free(seuser); + data->default_user_context = strdup(contextlist[0]); + freeconary(contextlist); + if (!data->default_user_context) { + pam_syslog(pamh, LOG_CRIT, "Out of memory"); + return PAM_BUF_ERR; + } + + data->exec_context = data->default_user_context; + if (select_context) + data->exec_context = config_context(pamh, data->default_user_context, + use_current_range, debug); + else if (env_params || use_current_range) + data->exec_context = context_from_env(pamh, data->default_user_context, + env_params, use_current_range, + debug); + } + + if (!data->exec_context) { + pam_syslog(pamh, LOG_ERR, "Unable to get valid context for %s", username); + pam_prompt(pamh, PAM_ERROR_MSG, NULL, + _("A valid context for %s could not be obtained."), username); + } + + if (getexeccon(&data->prev_exec_context) < 0) + data->prev_exec_context = NULL; + + return PAM_SUCCESS; +} + +static int +compute_tty_context(const pam_handle_t *pamh, module_data_t *data) +{ + const char *tty = get_item(pamh, PAM_TTY); + + if (!tty || !*tty || !strcmp(tty, "ssh") + || pam_str_skip_prefix(tty, "NODEV") != NULL) { + tty = ttyname(STDIN_FILENO); + if (!tty || !*tty) + tty = ttyname(STDOUT_FILENO); + if (!tty || !*tty) + tty = ttyname(STDERR_FILENO); + if (!tty || !*tty) + return PAM_SUCCESS; + } + + if (pam_str_skip_prefix(tty, "/dev/") == NULL) { + if (asprintf(&data->tty_path, "%s%s", "/dev/", tty) < 0) + data->tty_path = NULL; + } else { + data->tty_path = strdup(tty); + } + + if (!data->tty_path) { + pam_syslog(pamh, LOG_CRIT, "Out of memory"); + return PAM_BUF_ERR; + } + + if (getfilecon(data->tty_path, &data->prev_tty_context) < 0) { + data->prev_tty_context = NULL; + if (errno == ENOENT) { + free(data->tty_path); + data->tty_path = NULL; + return PAM_SUCCESS; + } + pam_syslog(pamh, LOG_ERR, "Failed to get current context for %s: %m", + data->tty_path); + return (security_getenforce() == 1) ? PAM_SESSION_ERR : PAM_SUCCESS; + } + + if (security_compute_relabel(data->exec_context, data->prev_tty_context, + string_to_security_class("chr_file"), &data->tty_context)) { + data->tty_context = NULL; + pam_syslog(pamh, LOG_ERR, "Failed to compute new context for %s: %m", + data->tty_path); + freecon(data->prev_tty_context); + data->prev_tty_context = NULL; + free(data->tty_path); + data->tty_path = NULL; + return (security_getenforce() == 1) ? PAM_SESSION_ERR : PAM_SUCCESS; + } + + return PAM_SUCCESS; +} + +static int +restore_context(const pam_handle_t *pamh, const module_data_t *data, int debug) +{ + int err; + + if (!data) { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "No context to restore"); + return PAM_SUCCESS; + } + + if (debug && data->tty_path) + pam_syslog(pamh, LOG_NOTICE, + "Restore file context of tty %s: [%s] -> [%s]", + data->tty_path, + data->tty_context ? data->tty_context : "", + data->prev_tty_context ? data->prev_tty_context : ""); + err = set_file_context(pamh, data->prev_tty_context, data->tty_path); + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Restore executable context: [%s] -> [%s]", + data->exec_context, + data->prev_exec_context ? data->prev_exec_context : ""); + err |= set_exec_context(pamh, data->prev_exec_context); + + if (err && security_getenforce() == 1) + return PAM_SESSION_ERR; + + return PAM_SUCCESS; +} + +static int +set_context(pam_handle_t *pamh, const module_data_t *data, + int debug, int verbose) +{ + int rc, err; + + if (debug && data->tty_path) + pam_syslog(pamh, LOG_NOTICE, "Set file context of tty %s: [%s] -> [%s]", + data->tty_path, + data->prev_tty_context ? data->prev_tty_context : "", + data->tty_context ? data->tty_context : ""); + err = set_file_context(pamh, data->tty_context, data->tty_path); + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Set executable context: [%s] -> [%s]", + data->prev_exec_context ? data->prev_exec_context : "", + data->exec_context); + rc = set_exec_context(pamh, data->exec_context); + err |= rc; + + send_audit_message(pamh, !rc, data->default_user_context, data->exec_context); + if (verbose && !rc) { + char msg[PATH_MAX]; + + snprintf(msg, sizeof(msg), + _("Security context %s has been assigned."), data->exec_context); + send_text(pamh, msg, debug); + } +#ifdef HAVE_SETKEYCREATECON + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Set key creation context to %s", + data->exec_context ? data->exec_context : ""); + rc = setkeycreatecon(data->exec_context); + err |= rc; + if (rc) + pam_syslog(pamh, LOG_ERR, "Setting key creation context %s failed: %m", + data->exec_context ? data->exec_context : ""); + if (verbose && !rc) { + char msg[PATH_MAX]; + + snprintf(msg, sizeof(msg), + _("Key creation context %s has been assigned."), data->exec_context); + send_text(pamh, msg, debug); + } +#endif + + if (err && security_getenforce() == 1) + return PAM_SESSION_ERR; + + return PAM_SUCCESS; +} + +static int +create_context(pam_handle_t *pamh, int argc, const char **argv, + int debug, int verbose) +{ + int i; + int ttys = 1; + int select_context = 0; + int use_current_range = 0; + int env_params = 0; + module_data_t *data; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "nottys") == 0) { + ttys = 0; + } + if (strcmp(argv[i], "select_context") == 0) { + select_context = 1; + } + if (strcmp(argv[i], "use_current_range") == 0) { + use_current_range = 1; + } + if (strcmp(argv[i], "env_params") == 0) { + env_params = 1; + } + } + + if (is_selinux_enabled() <= 0) { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "SELinux is not enabled"); + return PAM_SUCCESS; + } + + if (select_context && env_params) { + pam_syslog(pamh, LOG_ERR, + "select_context cannot be used with env_params"); + select_context = 0; + } + + if (!(data = calloc(1, sizeof(*data)))) { + pam_syslog(pamh, LOG_CRIT, "Out of memory"); + return PAM_BUF_ERR; + } + + i = compute_exec_context(pamh, data, select_context, use_current_range, + env_params, debug); + if (i != PAM_SUCCESS) { + free_module_data(data); + return i; + } + + if (!data->exec_context) { + free_module_data(data); + return (security_getenforce() == 1) ? PAM_SESSION_ERR : PAM_SUCCESS; + } + + if (ttys && (i = compute_tty_context(pamh, data)) != PAM_SUCCESS) { + free_module_data(data); + return i; + } + + if ((i = pam_set_data(pamh, DATANAME, data, cleanup)) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "Error saving context: %m"); + free_module_data(data); + return i; + } + + return set_context(pamh, data, debug, verbose); +} + +int +pam_sm_authenticate(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + /* Fail by default. */ + return PAM_AUTH_ERR; +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + const module_data_t *data; + int i, debug = 0, verbose = 0, close_session = 0, restore = 0; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + } + if (strcmp(argv[i], "verbose") == 0) { + verbose = 1; + } + if (strcmp(argv[i], "close") == 0) { + close_session = 1; + } + if (strcmp(argv[i], "restore") == 0) { + restore = 1; + } + } + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Open Session"); + + /* Is this module supposed to execute close_session only? */ + if (close_session) + return PAM_SUCCESS; + + data = get_module_data(pamh); + + /* Is this module supposed only to restore original context? */ + if (restore) + return restore_context(pamh, data, debug); + + /* If there is a saved context, this module is supposed to set it again. */ + return data ? set_context(pamh, data, debug, verbose) : + create_context(pamh, argc, argv, debug, verbose); +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int i, debug = 0, open_session = 0; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + } + if (strcmp(argv[i], "open") == 0) { + open_session = 1; + } + } + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Close Session"); + + /* Is this module supposed to execute open_session only? */ + if (open_session) + return PAM_SUCCESS; + + /* Restore original context. */ + return restore_context(pamh, get_module_data(pamh), debug); +} -- cgit v1.2.3