summaryrefslogtreecommitdiffstats
path: root/modules/pam_selinux/pam_selinux.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:38:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:38:36 +0000
commit26367bfc399cb3862f94ddca8fce87f98f26d67e (patch)
treeba3a4e02ed5ec62fe645dfa810c01d26decf591f /modules/pam_selinux/pam_selinux.c
parentInitial commit. (diff)
downloadpam-26367bfc399cb3862f94ddca8fce87f98f26d67e.tar.xz
pam-26367bfc399cb3862f94ddca8fce87f98f26d67e.zip
Adding upstream version 1.3.1.upstream/1.3.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/pam_selinux/pam_selinux.c')
-rw-r--r--modules/pam_selinux/pam_selinux.c841
1 files changed, 841 insertions, 0 deletions
diff --git a/modules/pam_selinux/pam_selinux.c b/modules/pam_selinux/pam_selinux.c
new file mode 100644
index 0000000..348cdd4
--- /dev/null
+++ b/modules/pam_selinux/pam_selinux.c
@@ -0,0 +1,841 @@
+/******************************************************************************
+ * 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 <dwalsh@redhat.com>
+ * Additional improvements by Tomas Mraz <tmraz@redhat.com>
+ *
+ * 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 <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <syslog.h>
+
+#define PAM_SM_AUTH
+#define PAM_SM_SESSION
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+#include <security/pam_modutil.h>
+#include <security/pam_ext.h>
+
+#include <selinux/selinux.h>
+#include <selinux/get_context_list.h>
+#include <selinux/flask.h>
+#include <selinux/av_permissions.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <selinux/get_default_type.h>
+
+#ifdef HAVE_LIBAUDIT
+#include <libaudit.h>
+#include <sys/select.h>
+#include <errno.h>
+#endif
+
+/* Send audit message */
+static
+
+int send_audit_message(pam_handle_t *pamh, int success, security_context_t default_context,
+ security_context_t selected_context)
+{
+ int rc=0;
+#ifdef HAVE_LIBAUDIT
+ char *msg = NULL;
+ int audit_fd = audit_open();
+ security_context_t default_raw=NULL;
+ security_context_t selected_raw=NULL;
+ const void *tty = NULL, *rhost = NULL;
+ rc = -1;
+ if (audit_fd < 0) {
+ if (errno == EINVAL || errno == EPROTONOSUPPORT ||
+ errno == EAFNOSUPPORT)
+ return 0; /* No audit support in kernel */
+ pam_syslog(pamh, LOG_ERR, "Error connecting to audit system.");
+ return rc;
+ }
+ (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.");
+ default_raw = NULL;
+ }
+ if (selinux_trans_to_raw_context(selected_context, &selected_raw) < 0) {
+ pam_syslog(pamh, LOG_ERR, "Error translating 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) {
+ pam_syslog(pamh, LOG_ERR, "Error allocating memory.");
+ goto out;
+ }
+ 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.");
+ goto out;
+ }
+ rc = 0;
+ out:
+ free(msg);
+ freecon(default_raw);
+ freecon(selected_raw);
+ close(audit_fd);
+#else
+ pam_syslog(pamh, LOG_NOTICE, "pam: default-context=%s selected-context=%s success %d", default_context, selected_context, success);
+#endif
+ return rc;
+}
+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 int mls_range_allowed(pam_handle_t *pamh, security_context_t src, security_context_t dst, int debug)
+{
+ struct av_decision avd;
+ int retval;
+ security_class_t class;
+ access_vector_t bit;
+ context_t src_context;
+ context_t dst_context;
+
+ class = string_to_security_class("context");
+ if (!class) {
+ pam_syslog(pamh, LOG_ERR, "Failed to translate security class context. %m");
+ return 0;
+ }
+
+ bit = string_to_av_perm(class, "contains");
+ if (!bit) {
+ pam_syslog(pamh, LOG_ERR, "Failed to translate av perm contains. %m");
+ return 0;
+ }
+
+ src_context = context_new (src);
+ dst_context = context_new (dst);
+ context_range_set(dst_context, context_range_get(src_context));
+ if (debug)
+ pam_syslog(pamh, LOG_NOTICE, "Checking if %s mls range valid for %s", dst, context_str(dst_context));
+
+ retval = security_compute_av(context_str(dst_context), dst, class, bit, &avd);
+ context_free(src_context);
+ context_free(dst_context);
+ if (retval || ((bit & avd.allowed) != bit))
+ return 0;
+
+ return 1;
+}
+
+static security_context_t
+config_context (pam_handle_t *pamh, security_context_t defaultcon, int use_current_range, int debug)
+{
+ security_context_t 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, _("Default Security Context %s\n"), 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, _("No default type for role %s\n"), 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) {
+ security_context_t 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 && !mls_range_allowed(pamh, defaultcon, newcon, debug)) {
+ 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,_("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 security_context_t
+context_from_env (pam_handle_t *pamh, security_context_t defaultcon, int env_params, int use_current_range, int debug)
+{
+ security_context_t 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) {
+ security_context_t 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 && !mls_range_allowed(pamh, defaultcon, newcon, debug)) {
+ 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 {
+ security_context_t exec_context;
+ security_context_t prev_exec_context;
+ security_context_t default_user_context;
+ security_context_t tty_context;
+ security_context_t 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, security_context_t 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, security_context_t 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;
+ security_context_t *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,
+ _("Unable to get valid context for %s"), 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") || !strncmp(tty, "NODEV", 5)) {
+ 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 (strncmp("/dev/", tty, 5)) {
+ 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,
+ SECCLASS_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 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 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);
+}