819 lines
23 KiB
C
819 lines
23 KiB
C
/******************************************************************************
|
|
* 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>
|
|
|
|
#include <security/pam_modules.h>
|
|
#include <security/_pam_macros.h>
|
|
#include <security/pam_modutil.h>
|
|
#include <security/pam_ext.h>
|
|
#include "pam_inline.h"
|
|
#include "pam_i18n.h"
|
|
|
|
#include <selinux/selinux.h>
|
|
#include <selinux/get_context_list.h>
|
|
#include <selinux/context.h>
|
|
#include <selinux/get_default_type.h>
|
|
|
|
#ifdef HAVE_LIBAUDIT
|
|
#include <libaudit.h>
|
|
#include <sys/select.h>
|
|
#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, "op=pam_selinux 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 ? default_context : "(null)",
|
|
selected_context ? selected_context : "(null)", 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);
|
|
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);
|
|
security_class_t tclass;
|
|
|
|
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() != 0) ? PAM_SESSION_ERR : PAM_SUCCESS;
|
|
}
|
|
|
|
tclass = string_to_security_class("chr_file");
|
|
if (tclass == 0) {
|
|
pam_syslog(pamh, LOG_ERR, "Failed to get chr_file security class");
|
|
freecon(data->prev_tty_context);
|
|
data->prev_tty_context = NULL;
|
|
free(data->tty_path);
|
|
data->tty_path = NULL;
|
|
return (security_getenforce() != 0) ? PAM_SESSION_ERR : PAM_SUCCESS;
|
|
}
|
|
|
|
if (security_compute_relabel(data->exec_context, data->prev_tty_context,
|
|
tclass, &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() != 0) ? 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() != 0)
|
|
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() != 0)
|
|
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() != 0) ? 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);
|
|
}
|