summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThorsten Kukuk <kukuk@thkukuk.de>2005-05-16 11:04:31 +0000
committerThorsten Kukuk <kukuk@thkukuk.de>2005-05-16 11:04:31 +0000
commit72188ec3483744ef54c20245ac4125eb1c67e92e (patch)
tree4c347b8078c1d26c605a52a175ba5d0fce5877a5
parent67aab1ff5515054341a438cf9804e9c9b3a88033 (diff)
Relevant BUGIDs: none
Purpose of commit: new feature Commit summary: --------------- Add pam_selinux module
-rw-r--r--modules/pam_selinux/Makefile26
-rw-r--r--modules/pam_selinux/README17
-rw-r--r--modules/pam_selinux/pam_selinux.863
-rw-r--r--modules/pam_selinux/pam_selinux.c645
-rw-r--r--modules/pam_selinux/pam_selinux_check.835
-rw-r--r--modules/pam_selinux/pam_selinux_check.c43
6 files changed, 829 insertions, 0 deletions
diff --git a/modules/pam_selinux/Makefile b/modules/pam_selinux/Makefile
new file mode 100644
index 00000000..a11a7c57
--- /dev/null
+++ b/modules/pam_selinux/Makefile
@@ -0,0 +1,26 @@
+#
+# $Id$
+#
+
+include ../../Make.Rules
+
+ifeq ($(HAVE_LIBSELINUX),yes)
+
+TITLE=pam_selinux
+APPLICATION=$(TITLE)_check
+APPMODE=04511
+MAN8=$(TITLE).8 $(TITLE)_check.8
+MODULE_SIMPLE_EXTRALIBS=-lselinux
+
+#MODULE_SIMPLE_INSTALL=$(MAKE) $(APPLICATION); $(INSTALL) -m $(APPMODE) $(APPLICATION) $(FAKEROOT)$(SUPLEMENTED)
+
+include ../Simple.Rules
+
+$(APPLICATION): $(APPLICATION).c
+ $(CC) $(CFLAGS) $(INCLUDE_PAMMODUTILS) -o $@ $^ $(LINK_PAMMODUTILS) -lpam -lpam_misc
+
+else
+
+include ../dont_makefile
+
+endif \ No newline at end of file
diff --git a/modules/pam_selinux/README b/modules/pam_selinux/README
new file mode 100644
index 00000000..174c4bda
--- /dev/null
+++ b/modules/pam_selinux/README
@@ -0,0 +1,17 @@
+This is pam_selinux, a module for setting the default security context after
+login via PAM.
+
+Background: SELinux provides a mechanism for allowing people to login with
+different security contexts.
+
+The module takes these arguments:
+The module takes these arguments:
+ close Only execute the close_session portion of the module.
+ debug Log debug messages (with priority DEBUG) to syslog.
+ nottys Do not set security context on controlling tty
+ verbose Attempt to tell the user when security context is set.
+ open Only execute the open_session portion of the module.
+
+Dan Walsh <dwalsh@redhat.com>
+
+
diff --git a/modules/pam_selinux/pam_selinux.8 b/modules/pam_selinux/pam_selinux.8
new file mode 100644
index 00000000..4ccec58c
--- /dev/null
+++ b/modules/pam_selinux/pam_selinux.8
@@ -0,0 +1,63 @@
+.TH pam_selinux 8 2003/08/26 "Red Hat Linux" "System Administrator's Manual"
+.SH NAME
+pam_selinux \- set the default security context after login via PAM.
+.SH SYNOPSIS
+.B session optional /lib/security/pam_selinux.so
+.br
+
+.SH DESCRIPTION
+In a nutshell, pam_selinux sets up the default security context for the next execed
+shell.
+
+When an application opens a session using pam_selinux, the shell that gets
+executed will be run in the default security context, or if the user chooses
+and the pam file allows the selected security context. Also the controlling
+tty will have it's security context modified to match the users.
+
+Adding pam_selinux into a pam file could cause other pam modules to change
+their behavior if the exec another application. The close and open option help
+mitigate this problem. close option will only cause the close portion of the
+pam_selinux to execute, and open will only cause the open portion to run. You
+can add pam_selinux to the config file twice. Add the pam_selinux close as the
+'first' session entry and open as the 'last' session entry. This way when pam
+executes the open pass through the modules, pam_selinux open_session will
+happen last. When pam executes the close pass through the modules pam_selinux
+close_session will happen first.
+
+.SH ARGUMENTS
+.IP close
+Only execute the close_session portion of the module.
+.IP debug
+turns on debugging via \fBsyslog(3)\fR.
+.IP multiple
+tells pam_selinux.so to allow the user to select the security context they will
+login with, if the user has more than one role.
+.IP open
+Only execute the open_session portion of the module.
+.IP nottys
+Do not try to setup the ttys security context.
+.IP verbose
+attempt to inform the user when security context is set.
+
+.SH EXAMPLE
+\fB/etc/pam.d/some-login-program\fP:
+.br
+auth required /lib/security/pam_unix.so
+.br
+session required /lib/security/pam_permit.so
+session optional /lib/security/pam_selinux.so
+.br
+
+.SH CAVEATS
+Setting the following line will cause the login to fail
+auth sufficient /lib/security/pam_selinux.so verbose
+
+
+.SH SEE ALSO
+pam_selinux_check(8)
+
+.SH BUGS
+Let's hope not, but if you find any, please email the author.
+
+.SH AUTHOR
+Dan Walsh <dwalsh@redhat.com>
diff --git a/modules/pam_selinux/pam_selinux.c b/modules/pam_selinux/pam_selinux.c
new file mode 100644
index 00000000..0c05d18b
--- /dev/null
+++ b/modules/pam_selinux/pam_selinux.c
@@ -0,0 +1,645 @@
+/******************************************************************************
+ * 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.
+ *
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_SESSION
+
+#include "../../_pam_aconf.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 <linux/limits.h>
+
+#include "../../_pam_aconf.h"
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+#include <security/_pam_modutil.h>
+
+#include <libintl.h>
+#define _(x) gettext(x)
+
+#ifndef PAM_SELINUX_MAIN
+#define MODULE "pam_selinux"
+
+#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( struct pam_conv *conv, const char *text, int debug) {
+ struct pam_message message;
+ const struct pam_message *messages[] = {&message};
+ struct pam_response *responses;
+ int retval;
+
+ memset(&message, 0, sizeof(message));
+ message.msg_style = PAM_TEXT_INFO;
+ message.msg = text;
+ if (debug)
+ syslog(LOG_NOTICE, MODULE ": %s", message.msg);
+ retval = conv->conv(1, messages, &responses, conv->appdata_ptr);
+ if (responses)
+ _pam_drop_reply(responses, 1);
+ return retval;
+}
+
+/*
+ * This function sends a message to the user and gets the response. The caller
+ * is responsible for freeing the responses.
+ */
+static int query_response( struct pam_conv *conv, const char *text,
+ struct pam_response **responses, int debug) {
+ struct pam_message message;
+ const struct pam_message *messages[] = {&message};
+
+ memset(&message, 0, sizeof(message));
+ message.msg_style = PAM_PROMPT_ECHO_ON;
+ message.msg = text;
+
+ if (debug)
+ syslog(LOG_NOTICE, MODULE ": %s", message.msg);
+
+ return conv->conv(1, messages, responses, conv->appdata_ptr);
+}
+
+static security_context_t
+select_context (pam_handle_t *pamh, security_context_t* contextlist,
+ int debug)
+{
+ struct pam_conv *conv;
+
+ if (pam_get_item(pamh, PAM_CONV, (const void**) &conv) == PAM_SUCCESS &&
+ conv) {
+ if (conv->conv != NULL) {
+ struct pam_response *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(conv,text,debug);
+ free(text);
+ query_response(conv,_("Do you want to choose a different one? [n]"),
+ &responses,debug);
+ if (responses && ((responses[0].resp[0] == 'y') ||
+ (responses[0].resp[0] == 'Y')))
+ {
+ int choice=0;
+ int i;
+ char *prompt=_("Enter number of choice: ");
+ int len=strlen(prompt);
+ char buf[PATH_MAX];
+
+ _pam_drop_reply(responses, 1);
+ 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(conv,text,&responses,debug);
+ choice = strtol (responses[0].resp, NULL, 10);
+ _pam_drop_reply(responses, 1);
+ }
+ free(text);
+ return (security_context_t) strdup(contextlist[choice-1]);
+ }
+ else if (responses)
+ _pam_drop_reply(responses, 1);
+ } else {
+ if (debug)
+ syslog(LOG_NOTICE, _("%s: bogus conversation function"),MODULE);
+ }
+ } else {
+ if (debug)
+ syslog(LOG_NOTICE, _("%s: no conversation function"),MODULE);
+ }
+ return (security_context_t) strdup(contextlist[0]);
+}
+
+static security_context_t
+manual_context (pam_handle_t *pamh, char *user, int debug)
+{
+ struct pam_conv *conv;
+ security_context_t newcon;
+ context_t new_context;
+ int mls_enabled = is_selinux_mls_enabled();
+
+ if (pam_get_item(pamh, PAM_CONV, (const void**) &conv) == PAM_SUCCESS) {
+ if (conv && conv->conv != NULL) {
+ struct pam_response *responses;
+
+ while (1) {
+ query_response(conv,
+ _("Would you like to enter a security context? [y] "),
+ &responses,debug);
+ if ((responses[0].resp[0] == 'y') || (responses[0].resp[0] == 'Y') ||
+ (responses[0].resp[0] == '\0') )
+ {
+ if (mls_enabled)
+ new_context = context_new ("user:role:type:level");
+ else
+ new_context = context_new ("user:role:type");
+ _pam_drop_reply(responses, 1);
+
+ /* 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(conv,_("role: "),&responses,debug);
+ if (context_role_set (new_context, responses[0].resp))
+ {
+ _pam_drop_reply(responses, 1);
+ context_free (new_context);
+ return NULL;
+ }
+ _pam_drop_reply(responses, 1);
+ query_response(conv,_("type: "),&responses,debug);
+ if (context_type_set (new_context, responses[0].resp))
+ {
+ _pam_drop_reply(responses, 1);
+ context_free (new_context);
+ return NULL;
+ }
+ _pam_drop_reply(responses, 1);
+ if (mls_enabled)
+ {
+ query_response(conv,_("level: "),&responses,debug);
+ if (context_range_set (new_context, responses[0].resp))
+ {
+ context_free (new_context);
+ return NULL;
+ }
+ }
+ /* 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(conv,_("Not a valid security context"),debug);
+ }
+ else {
+ _pam_drop_reply(responses, 1);
+ return NULL;
+ }
+ } /* end while */
+ } else {
+ if (debug)
+ syslog(LOG_NOTICE, _("%s: bogus conversation function"),MODULE);
+ }
+ } else {
+ if (debug)
+ syslog(LOG_NOTICE, _("%s: no conversation function"),MODULE);
+ }
+ return NULL;
+}
+
+static void security_restorelabel_tty(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)
+ {
+ syslog(LOG_NOTICE,
+ _("Warning! Could not relabel %s with %s, not relabeling.\n"),
+ ptr, context);
+ }
+}
+
+static security_context_t security_label_tty(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)
+ {
+ syslog(LOG_NOTICE,
+ _("Warning! Could not get current context for %s, not relabeling."), ptr);
+ return NULL;
+ }
+ if( security_compute_relabel(usercon,prev_context,SECCLASS_CHR_FILE,
+ &newdev_context)!=0)
+ {
+ syslog(LOG_NOTICE,
+ _("Warning! Could not get new context for %s, not relabeling."),
+ ptr);
+ syslog(LOG_NOTICE, "usercon=%s, prev_context=%s\n", usercon, prev_context);
+ freecon(prev_context);
+ return NULL;
+ }
+ status=setfilecon(ptr,newdev_context);
+ if (status)
+ {
+ syslog(LOG_NOTICE,
+ _("Warning! Could not relabel %s with %s, not relabeling.%s"),
+ ptr,newdev_context,strerror(errno));
+ 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)
+{
+ struct pam_conv *conv;
+ struct pam_message message;
+ const struct pam_message *messages[] = {&message};
+ struct pam_response *responses;
+ if (pam_get_item(pamh, PAM_CONV, (const void**) &conv) == PAM_SUCCESS) {
+ if (conv && conv->conv != NULL) {
+ char text[PATH_MAX];
+
+ memset(&message, 0, sizeof(message));
+ message.msg_style = PAM_TEXT_INFO;
+ snprintf(text, sizeof(text), msg);
+
+ message.msg = text;
+ if (debug)
+ syslog(LOG_NOTICE, MODULE ": %s", message.msg);
+ conv->conv(1, messages, &responses, conv->appdata_ptr);
+ if (responses)
+ _pam_drop_reply(responses, 1);
+ } else {
+ if (debug)
+ syslog(LOG_NOTICE, _("%s: bogus conversation function"),MODULE);
+ }
+ } else {
+ if (debug)
+ syslog(LOG_NOTICE,_("%s: no conversation function"),MODULE);
+ }
+}
+
+PAM_EXTERN int
+pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ /* Fail by default. */
+ return PAM_AUTH_ERR;
+}
+
+PAM_EXTERN int
+pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN int
+pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ int i, debug = 0, ttys=1, has_tty=isatty(0), verbose=0, multiple=0, close_session=0;
+ int ret=0;
+ security_context_t* contextlist=NULL;
+ int num_contexts = 0;
+ char *username=NULL;
+ const char *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)
+ syslog(LOG_NOTICE, MODULE ": %s", "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, (const void**)&username) != PAM_SUCCESS ||
+ username == NULL) {
+ return PAM_AUTH_ERR;
+ }
+ 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) {
+ syslog (LOG_ERR, _("Unable to get valid context for %s"), username);
+ return PAM_AUTH_ERR;
+ }
+ } else {
+ syslog (LOG_ERR, _("Unable to get valid context for %s, No valid tty"), 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, (const void**)&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(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) {
+ syslog(LOG_ERR, _("Error! Unable to set %s executable context %s."),
+ username, user_context);
+ freecon(user_context);
+ return PAM_AUTH_ERR;
+ } else {
+ if (debug)
+ syslog(LOG_NOTICE, _("%s: set %s security context to %s"),MODULE,
+ username, user_context);
+ }
+ freecon(user_context);
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN int
+pam_sm_close_session(pam_handle_t *pamh, int flags, 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)
+ syslog(LOG_NOTICE, MODULE ": %s", "Close Session");
+
+ if (open_session)
+ return PAM_SUCCESS;
+
+ if (ttyn) {
+ if (debug)
+ syslog(LOG_NOTICE, MODULE ":Restore tty %s -> %s", ttyn,ttyn_context);
+
+ security_restorelabel_tty(ttyn,ttyn_context);
+ freecon(ttyn_context);
+ free(ttyn);
+ ttyn=NULL;
+ }
+ status=setexeccon(prev_user_context);
+ freecon(prev_user_context);
+ if (status) {
+ syslog(LOG_ERR, _("Error! Unable to set executable context %s."),
+ prev_user_context);
+ return PAM_AUTH_ERR;
+ }
+
+ if (debug)
+ syslog(LOG_NOTICE, _("%s: setcontext back to orginal"),MODULE);
+
+ return PAM_SUCCESS;
+}
+
+#else /* PAM_SELINUX_MAIN */
+
+/************************************************************************
+ *
+ * All PAM code goes in this section.
+ *
+ ************************************************************************/
+
+#include <unistd.h> /* for getuid(), exit(), getopt() */
+#include <signal.h>
+#include <sys/wait.h> /* for wait() */
+
+#include <security/pam_appl.h> /* for PAM functions */
+#include <security/pam_misc.h> /* for misc_conv PAM utility function */
+
+#define SERVICE_NAME "pam_selinux_check" /* the name of this program for PAM */
+ /* The file containing the context to run
+ * the scripts under. */
+int authenticate_via_pam( const char *user , pam_handle_t **pamh);
+
+/* authenticate_via_pam()
+ *
+ * in: user
+ * out: nothing
+ * return: value condition
+ * ----- ---------
+ * 1 pam thinks that the user authenticated themselves properly
+ * 0 otherwise
+ *
+ * this function uses pam to authenticate the user running this
+ * program. this is the only function in this program that makes pam
+ * calls.
+ *
+ */
+
+int authenticate_via_pam( const char *user , pam_handle_t **pamh) {
+
+ struct pam_conv *conv;
+ int result = 0; /* our result, set to 0 (not authenticated) by default */
+
+ /* this is a jump table of functions for pam to use when it wants to *
+ * communicate with the user. we'll be using misc_conv(), which is *
+ * provided for us via pam_misc.h. */
+ struct pam_conv pam_conversation = {
+ misc_conv,
+ NULL
+ };
+ conv = &pam_conversation;
+
+
+ /* make `p_pam_handle' a valid pam handle so we can use it when *
+ * calling pam functions. */
+ if( PAM_SUCCESS != pam_start( SERVICE_NAME,
+ user,
+ conv,
+ pamh ) ) {
+ fprintf( stderr, _("failed to initialize PAM\n") );
+ exit( -1 );
+ }
+
+ if( PAM_SUCCESS != pam_set_item(*pamh, PAM_RUSER, user))
+ {
+ fprintf( stderr, _("failed to pam_set_item()\n") );
+ exit( -1 );
+ }
+
+ /* Ask PAM to authenticate the user running this program */
+ if( PAM_SUCCESS == pam_authenticate(*pamh,0) ) {
+ if ( PAM_SUCCESS == pam_open_session(*pamh, 0) )
+ result = 1; /* user authenticated OK! */
+ }
+ return( result );
+
+} /* authenticate_via_pam() */
+
+int main(int argc, char **argv) {
+ pam_handle_t *pamh;
+ int childPid;
+
+ if (!authenticate_via_pam(argv[1],&pamh))
+ exit(-1);
+
+ childPid = fork();
+ if (childPid < 0) {
+ int errsv = errno;
+
+ /* error in fork() */
+ fprintf(stderr, _("login: failure forking: %s"), strerror(errsv));
+ pam_close_session(pamh, 0);
+ /* We're done with PAM. Free `pam_handle'. */
+ pam_end( pamh, PAM_SUCCESS );
+ exit(0);
+ }
+ if (childPid) {
+ close(0); close(1); close(2);
+ struct sigaction sa;
+ memset(&sa,0,sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ while(wait(NULL) == -1 && errno == EINTR) /**/ ;
+ openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
+ pam_close_session(pamh, 0);
+ /* We're done with PAM. Free `pam_handle'. */
+ pam_end( pamh, PAM_SUCCESS );
+ exit(0);
+ }
+ argv[0]="/bin/sh";
+ argv[1]=NULL;
+
+ /* NOTE: The environment has not been sanitized. LD_PRELOAD and other fun
+ * things could be set. */
+ execv("/bin/sh",argv);
+ fprintf(stderr,"Failure\n");
+ return 0;
+}
+#endif
diff --git a/modules/pam_selinux/pam_selinux_check.8 b/modules/pam_selinux/pam_selinux_check.8
new file mode 100644
index 00000000..5151be7d
--- /dev/null
+++ b/modules/pam_selinux/pam_selinux_check.8
@@ -0,0 +1,35 @@
+.TH pam_selinux_check 8 2002/05/23 "Red Hat Linux" "System Administrator's Manual"
+.SH NAME
+pam_selinux_check \- login program to test pam_selinux_check
+.SH SYNOPSIS
+.B pam_selinux_check [user]
+.br
+
+.SH DESCRIPTION
+With no arguments,
+.B pam_selinux_check
+will prompt for user
+
+.SH OPTIONS
+.IP target_user
+The user to login as.
+
+.SH DIAGNOSTICS
+You must setup a /etc/pam.d/pam_selinux_check file, in order for the check to work.
+
+When checking if a selinux is valid,
+.B pam_selinux_check
+returns an exit code of 0 for success and > 0 on error:
+
+.nf
+1: Authentication failure
+.fi
+
+.SH SEE ALSO
+pam_selinux(8)
+
+.SH BUGS
+Let's hope not, but if you find any, please email the author.
+
+.SH AUTHOR
+Dan Walsh <dwalsh@redhat.com>
diff --git a/modules/pam_selinux/pam_selinux_check.c b/modules/pam_selinux/pam_selinux_check.c
new file mode 100644
index 00000000..f2aa795e
--- /dev/null
+++ b/modules/pam_selinux/pam_selinux_check.c
@@ -0,0 +1,43 @@
+/******************************************************************************
+ * 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.
+ *
+ */
+
+#define PAM_SELINUX_MAIN 1
+#include "pam_selinux.c"
+