diff options
author | Steve Langasek <steve.langasek@ubuntu.com> | 2019-01-03 12:48:14 -0800 |
---|---|---|
committer | Steve Langasek <steve.langasek@ubuntu.com> | 2019-01-03 12:48:14 -0800 |
commit | d5b06b67bbeeed7c05c0eb2e05d6a972ad050d1c (patch) | |
tree | ba5654cffacfd2002eefc5bc3764a7971afff1dc /Linux-PAM/modules/pam_selinux/pam_selinux.c | |
parent | 4c51da22e068907adb7857d50f5109a467c94d7c (diff) | |
parent | 7cbfa335c57d068d59508c844f3957165cccfb9b (diff) | |
download | pam-d5b06b67bbeeed7c05c0eb2e05d6a972ad050d1c.tar.gz pam-d5b06b67bbeeed7c05c0eb2e05d6a972ad050d1c.tar.bz2 pam-d5b06b67bbeeed7c05c0eb2e05d6a972ad050d1c.zip |
New upstream version 0.99.7.1
Diffstat (limited to 'Linux-PAM/modules/pam_selinux/pam_selinux.c')
-rw-r--r-- | Linux-PAM/modules/pam_selinux/pam_selinux.c | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/Linux-PAM/modules/pam_selinux/pam_selinux.c b/Linux-PAM/modules/pam_selinux/pam_selinux.c new file mode 100644 index 00000000..5aaec2e7 --- /dev/null +++ b/Linux-PAM/modules/pam_selinux/pam_selinux.c @@ -0,0 +1,482 @@ +/****************************************************************************** + * A module for Linux-PAM that will set the default security context after login + * via PAM. + * + * Copyright (c) 2003 Red Hat, Inc. + * Written by Dan Walsh <dwalsh@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/selinux.h> +#include <selinux/context.h> + +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, + char **responses, int debug) +{ + if (debug) + pam_syslog(pamh, LOG_NOTICE, "%s", text); + + return pam_prompt (pamh, PAM_PROMPT_ECHO_ON, responses, "%s", text); +} + +static security_context_t +select_context (pam_handle_t *pamh, security_context_t* contextlist, + int debug) +{ + char *responses; + char *text=calloc(PATH_MAX,1); + + if (text == NULL) + return (security_context_t) strdup(contextlist[0]); + + snprintf(text, PATH_MAX, + _("Your default context is %s. \n"), contextlist[0]); + send_text(pamh,text,debug); + free(text); + query_response(pamh,_("Do you want to choose a different one? [n]"), + &responses,debug); + if (responses && ((responses[0] == 'y') || + (responses[0] == 'Y'))) + { + int choice=0; + int i; + const char *prompt=_("Enter number of choice: "); + int len=strlen(prompt); + char buf[PATH_MAX]; + + _pam_drop(responses); + for (i = 0; contextlist[i]; i++) { + len+=strlen(contextlist[i]) + 10; + } + text=calloc(len,1); + for (i = 0; contextlist[i]; i++) { + snprintf(buf, PATH_MAX, + "[%d] %s\n", i+1, contextlist[i]); + strncat(text,buf,len); + } + strcat(text,prompt); + while ((choice < 1) || (choice > i)) { + query_response(pamh,text,&responses,debug); + choice = strtol (responses, NULL, 10); + _pam_drop(responses); + } + free(text); + return (security_context_t) strdup(contextlist[choice-1]); + } + else if (responses) + _pam_drop(responses); + + return (security_context_t) strdup(contextlist[0]); +} + +static security_context_t +manual_context (pam_handle_t *pamh, const char *user, int debug) +{ + security_context_t newcon; + context_t new_context; + int mls_enabled = is_selinux_mls_enabled(); + + char *responses; + + while (1) { + query_response(pamh, + _("Would you like to enter a security context? [y] "), + &responses,debug); + if ((responses[0] == 'y') || (responses[0] == 'Y') || + (responses[0] == '\0') ) + { + if (mls_enabled) + new_context = context_new ("user:role:type:level"); + else + new_context = context_new ("user:role:type"); + _pam_drop(responses); + + /* Allow the user to enter each field of the context individually */ + if (context_user_set (new_context, user)) + { + context_free (new_context); + return NULL; + } + query_response(pamh,_("role: "),&responses,debug); + if (context_role_set (new_context, responses)) + { + _pam_drop(responses); + context_free (new_context); + return NULL; + } + _pam_drop(responses); + query_response(pamh,_("type: "),&responses,debug); + if (context_type_set (new_context, responses)) + { + _pam_drop(responses); + context_free (new_context); + return NULL; + } + _pam_drop(responses); + if (mls_enabled) + { + query_response(pamh,_("level: "),&responses,debug); + if (context_range_set (new_context, responses)) + { + _pam_drop(responses); + context_free (new_context); + return NULL; + } + _pam_drop(responses); + } + /* 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)); + context_free (new_context); + return newcon; + } + else + send_text(pamh,_("Not a valid security context"),debug); + } + else { + _pam_drop(responses); + return NULL; + } + } /* end while */ + + return NULL; +} + +static void +security_restorelabel_tty(const pam_handle_t *pamh, + const char *tty, security_context_t context) +{ + char ttybuf[PATH_MAX]; + const char *ptr; + + if (context==NULL) + return; + + if(strncmp("/dev/", tty, 5)) { + snprintf(ttybuf,sizeof(ttybuf),"/dev/%s",tty); + ptr = ttybuf; + } + else + ptr = tty; + + if (setfilecon(ptr, context) && errno != ENOENT) + { + pam_syslog(pamh, LOG_NOTICE, + "Warning! Could not relabel %s with %s, not relabeling: %m", + ptr, context); + } +} + +static security_context_t +security_label_tty(pam_handle_t *pamh, char *tty, + security_context_t usercon) +{ + char ttybuf[PATH_MAX]; + int status=0; + security_context_t newdev_context=NULL; /* The new context of a device */ + security_context_t prev_context=NULL; /* The new context of a device */ + const char *ptr; + + if(strncmp("/dev/", tty, 5)) + { + snprintf(ttybuf,sizeof(ttybuf),"/dev/%s",tty); + ptr = ttybuf; + } + else + ptr = tty; + + if (getfilecon(ptr, &prev_context) < 0) + { + if(errno != ENOENT) + pam_syslog(pamh, LOG_NOTICE, + "Warning! Could not get current context for %s, not relabeling: %m", + ptr); + return NULL; + } + if( security_compute_relabel(usercon,prev_context,SECCLASS_CHR_FILE, + &newdev_context)!=0) + { + pam_syslog(pamh, LOG_NOTICE, + "Warning! Could not get new context for %s, not relabeling: %m", + ptr); + pam_syslog(pamh, LOG_NOTICE, + "usercon=%s, prev_context=%s", usercon, prev_context); + freecon(prev_context); + return NULL; + } + status=setfilecon(ptr,newdev_context); + if (status) + { + pam_syslog(pamh, LOG_NOTICE, + "Warning! Could not relabel %s with %s, not relabeling: %m", + ptr,newdev_context); + freecon(prev_context); + prev_context=NULL; + } + freecon(newdev_context); + return prev_context; +} + +static security_context_t user_context=NULL; +static security_context_t prev_user_context=NULL; +static security_context_t ttyn_context=NULL; /* The current context of ttyn device */ +static int selinux_enabled=0; +static char *ttyn=NULL; + +/* Tell the user that access has been granted. */ +static void +verbose_message(pam_handle_t *pamh, char *msg, int debug) +{ + if (debug) + pam_syslog(pamh, LOG_NOTICE, msg); + + pam_info (pamh, "%s", msg); +} + +PAM_EXTERN 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; +} + +PAM_EXTERN int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +PAM_EXTERN int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int i, debug = 0, ttys=1, has_tty=isatty(0); + int verbose=0, multiple=0, close_session=0; + int ret = 0; + security_context_t* contextlist = NULL; + int num_contexts = 0; + const void *username = NULL; + const void *tty = NULL; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + } + if (strcmp(argv[i], "nottys") == 0) { + ttys = 0; + } + if (strcmp(argv[i], "verbose") == 0) { + verbose = 1; + } + if (strcmp(argv[i], "multiple") == 0) { + multiple = 1; + } + if (strcmp(argv[i], "close") == 0) { + close_session = 1; + } + } + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Open Session"); + + /* this module is only supposed to execute close_session */ + if (close_session) + return PAM_SUCCESS; + + if (!(selinux_enabled = is_selinux_enabled()>0) ) + return PAM_SUCCESS; + + if (pam_get_item(pamh, PAM_USER, &username) != PAM_SUCCESS || + username == NULL) { + return PAM_USER_UNKNOWN; + } + num_contexts = get_ordered_context_list(username, 0, &contextlist); + if (num_contexts > 0) { + if (multiple && (num_contexts > 1) && has_tty) { + user_context = select_context(pamh,contextlist, debug); + freeconary(contextlist); + } else { + user_context = (security_context_t) strdup(contextlist[0]); + freeconary(contextlist); + } + } else { + if (has_tty) { + user_context = manual_context(pamh,username,debug); + if (user_context == NULL) { + pam_syslog (pamh, LOG_ERR, "Unable to get valid context for %s", + (const char *)username); + return PAM_AUTH_ERR; + } + } else { + pam_syslog (pamh, LOG_ERR, + "Unable to get valid context for %s, No valid tty", + (const char *)username); + return PAM_AUTH_ERR; + } + } + if (getexeccon(&prev_user_context)<0) { + prev_user_context=NULL; + } + if (ttys) { + /* Get the name of the terminal. */ + if (pam_get_item(pamh, PAM_TTY, &tty) != PAM_SUCCESS) { + tty = NULL; + } + + if ((tty == NULL) || (strlen(tty) == 0) || + strcmp(tty, "ssh") == 0 || strncmp(tty, "NODEV", 5) == 0) { + tty = ttyname(STDIN_FILENO); + if ((tty == NULL) || (strlen(tty) == 0)) { + tty = ttyname(STDOUT_FILENO); + } + if ((tty == NULL) || (strlen(tty) == 0)) { + tty = ttyname(STDERR_FILENO); + } + } + } + if(ttys && tty ) { + ttyn=strdup(tty); + ttyn_context=security_label_tty(pamh,ttyn,user_context); + } + ret = setexeccon(user_context); + if (ret==0 && verbose) { + char msg[PATH_MAX]; + snprintf(msg, sizeof(msg), + _("Security Context %s Assigned"), user_context); + verbose_message(pamh, msg, debug); + } + if (ret) { + pam_syslog(pamh, LOG_ERR, + "Error! Unable to set %s executable context %s.", + (const char *)username, user_context); + freecon(user_context); + return PAM_AUTH_ERR; + } else { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "set %s security context to %s", + (const char *)username, user_context); + } + freecon(user_context); + + return PAM_SUCCESS; +} + +PAM_EXTERN int +pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int i, debug = 0,status=0, open_session=0; + if (! (selinux_enabled )) + return PAM_SUCCESS; + + /* 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"); + + if (open_session) + return PAM_SUCCESS; + + if (ttyn) { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Restore tty %s -> %s", + ttyn,ttyn_context); + + security_restorelabel_tty(pamh,ttyn,ttyn_context); + freecon(ttyn_context); + free(ttyn); + ttyn=NULL; + } + status=setexeccon(prev_user_context); + freecon(prev_user_context); + if (status) { + pam_syslog(pamh, LOG_ERR, "Error! Unable to set executable context %s.", + prev_user_context); + return PAM_AUTH_ERR; + } + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "setcontext back to orginal"); + + return PAM_SUCCESS; +} |