diff -urN pam-pld-0.77.3.org/configure.in pam-pld-0.77.3/configure.in --- pam-pld-0.77.3.org/configure.in 2003-12-27 01:14:06.000000000 +0100 +++ pam-pld-0.77.3/configure.in 2003-12-27 03:18:31.602208496 +0100 @@ -282,6 +282,8 @@ AC_MSG_WARN(pam_cap module will not be built!)) AC_CHECK_LIB(opie, opieverify, opielib_present=true, AC_MSG_WARN(pam_opie modules will not be built!)) +AC_CHECK_LIB(selinux, getfilecon, libselinux_present=true, + AC_MSG_WARN(SELinux support will not be enabled. pam_selinux and pam_selinux_check module will not be built!)) AC_MSG_CHECKING(for hosts_access in -lwrap) oldLIBS=$LIBS LIBS="$LIBS -lwrap" @@ -301,6 +303,7 @@ AM_CONDITIONAL(CAPLIB_PRESENT, test x$caplib_present = xtrue) AM_CONDITIONAL(WRAPLIB_PRESENT, test x$wraplib_present = xtrue) AM_CONDITIONAL(OPIELIB_PRESENT, test x$opielib_present = xtrue) +AM_CONDITIONAL(LIBSELINUX_PRESENT, test x$libselinux_present = xtrue) AC_DEFINE(HAVE_CRACKLIB) AC_MSG_CHECKING([where cracklib_dict is located]) diff -urN pam-pld-0.77.3.org/modules/pam_selinux/Makefile.am pam-pld-0.77.3/modules/pam_selinux/Makefile.am --- pam-pld-0.77.3.org/modules/pam_selinux/Makefile.am 1970-01-01 01:00:00.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_selinux/Makefile.am 2003-12-27 03:17:34.973817320 +0100 @@ -0,0 +1,61 @@ +## Process this file with automake to produce Makefile.in + +@SET_MAKE@ + +if LIBSELINUX_PRESENT + +if DYNAMIC_MODULES +modules_LTLIBRARIES = pam_selinux.la pam_selinux_check.la +endif + +pam_selinux_la_SOURCES = pam_selinux.c +pam_seelinux_check_la_SOURCES = pam_selinux_check.c + +else + +EXTRA_pam_selinux_la_SOURCES = pam_selinux.c +EXTRA_pam_selinux_check_la_SOURCES = pam_selinux_check.c + +man_MANS = pam_selinux.8 pam_selinux_check.8 + +endif + +TITLE = pam_selinux + +pam_selinux_la_LIBADD = -lselinux +pam_selinux_check_la_LIBADD = -lselinux + +modulesdir = /lib/security + +pam_selinux_la_CFLAGS = -I$(top_srcdir)/include $(DYNAMIC) +pam_selinuc_check_la_CFLAGS = -I$(top_srcdir)/include $(DYNAMIC) + +pam_selinux_la_LDFLAGS = -avoid-version -module +pam_selinux_check_la_LDFLAGS= -avoid-version -module + +EXTRA_DIST = README + +CLEANFILES = *~ $(TITLE).so $(TITLE)_check.so + +clean-local: + rm -rf static + +if STATIC_MODULES + +if LIBSELINX_PRESENT +noinst_LTLIBRARIES = libpam_selinux.la libpam_selinux_check.la +libpam_selinux_la_SOURCES = pam_selinux.c +libpam_selinux_check_la_SOURCES = pam_selinux_check.c +libpam_selinux_la_DEPENDENCIES = register_static_stamp +libpam_selinux_check_la_DEPENDENCIES = register_static_stamp +libpam_selinux_la_CFLAGS = -I. -I../.. -I$(top_srcdir)/include $(STATIC) +libpam_selinux_check_la_CFLAGS = -I. -I../.. -I$(top_srcdir)/include $(STATIC) +endif + +endif + +register_static_stamp: + ( cd .. ; ./register_static $(TITLE) $(TITLE)/lib$(TITLE).la ) + ( cd .. ; ./register_static $(TITLE)_check $(TITLE)/lib$(TITLE)_check.la ) + touch register_static_stamp + diff -urN pam-pld-0.77.3.org/modules/pam_selinux/pam_selinux.8 pam-pld-0.77.3/modules/pam_selinux/pam_selinux.8 --- pam-pld-0.77.3.org/modules/pam_selinux/pam_selinux.8 1970-01-01 01:00:00.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_selinux/pam_selinux.8 2003-12-27 01:14:20.000000000 +0100 @@ -0,0 +1,49 @@ +.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. + +.SH ARGUMENTS +.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 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 diff -urN pam-pld-0.77.3.org/modules/pam_selinux/pam_selinux.c pam-pld-0.77.3/modules/pam_selinux/pam_selinux.c --- pam-pld-0.77.3.org/modules/pam_selinux/pam_selinux.c 1970-01-01 01:00:00.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_selinux/pam_selinux.c 2003-12-27 01:14:20.000000000 +0100 @@ -0,0 +1,542 @@ +/****************************************************************************** + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "../../_pam_aconf.h" +#include +#include +#include + +#include +#define _(x) gettext(x) + +#ifndef PAM_SELINUX_MAIN +#define MODULE "pam_selinux" + +#include +#include +#include +#include +#include + +/* Validate a tty pathname as actually belonging to a tty, and return its base + * name if it's valid. */ +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; + memset(&message, 0, sizeof(message)); + message.msg_style = PAM_TEXT_INFO; + message.msg = text; + if (debug) + syslog(LOG_DEBUG, MODULE ": %s", message.msg); + return conv->conv(1, messages, &responses, conv->appdata_ptr); +} +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_DEBUG, MODULE ": %s", message.msg); + return conv->conv(1, messages, responses, conv->appdata_ptr); +} +static const 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) { + if (conv->conv != NULL) { + struct pam_response *responses; + char *text=calloc(PATH_MAX,1); + 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[0].resp[0] == 'y') || (responses[0].resp[0] == 'Y')) + { + int choice=0; + int i; + char *prompt=_("Enter number of choice: "); + int len=strlen(prompt); + for (i = 0; contextlist[i]; i++) { + len+=strlen(contextlist[i]) + 10; + } + text=calloc(len,1); + for (i = 0; contextlist[i]; i++) { + sprintf(text, + "%s[%d] %s\n", text,i+1, contextlist[i]); + } + strcat(text,prompt); + while ((choice < 1) || (choice > i)) { + query_response(conv,text,&responses,debug); + choice = strtol (responses[0].resp, NULL, 10); + } + free(text); + return (security_context_t) strdup(contextlist[choice-1]); + } + } else { + if (debug) + syslog(LOG_DEBUG, _("%s: bogus conversation function"),MODULE); + } + } else { + if (debug) + syslog(LOG_DEBUG, _("%s: no conversation function"),MODULE); + } + return (security_context_t) strdup(contextlist[0]); +} +static const security_context_t +manual_context( pam_handle_t *pamh, char *user, int debug) +{ + struct pam_conv *conv; + security_context_t newcon; + if (pam_get_item(pamh, PAM_CONV, (const void**) &conv) == PAM_SUCCESS) { + if (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') + { + context_t new_context = context_new ("user:role:type"); + /* 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)) + { + context_free (new_context); + return NULL; + } + query_response(conv,_("type: "),&responses,debug); + if (context_type_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 { + return NULL; + } + } + } else { + if (debug) + syslog(LOG_DEBUG, _("%s: bogus conversation function"),MODULE); + } + } else { + if (debug) + syslog(LOG_DEBUG, _("%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(!strncmp(tty, "/dev/pts", 8)) + return; + + if (setfilecon(ptr, context)) + { + syslog(LOG_NOTICE, _("Warning! Could not relabel %s with %s, not relabeling.\n"), ttybuf,context); + } + return; +} + +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); + freecon(newdev_context); + if (status) + { + syslog(LOG_NOTICE, _("Warning! Could not relabel %s with %s, not relabeling."), ptr,newdev_context); + freecon(prev_context); + return NULL; + } + 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 != 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_DEBUG, MODULE ": %s", message.msg); + conv->conv(1, messages, &responses, conv->appdata_ptr); + } else { + if (debug) + syslog(LOG_DEBUG, _("%s: bogus conversation function"),MODULE); + } + } else { + if (debug) + syslog(LOG_DEBUG,_("%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,verbose=0,multiple=0; + int ret=0; + security_context_t* contextlist=NULL; + int num_contexts = 0; + char *username=NULL; + const char *tty=NULL; + /* Parse arguments. */ + if (!(selinux_enabled = is_selinux_enabled()) ) + { + return PAM_SUCCESS; + } + 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 (pam_get_item(pamh, PAM_USER, (const void**)&username) != PAM_SUCCESS) { + return PAM_AUTH_ERR; + } + num_contexts = get_ordered_context_list(username, 0, &contextlist); + if (num_contexts > 0) { + if (multiple) { + user_context = select_context(pamh,contextlist, debug); + freeconary(contextlist); + if (ret < 0) { + syslog (LOG_ERR, _("%s: query_user_context failed"), argv[0]); + return PAM_AUTH_ERR; + } + } else { + user_context = (security_context_t) strdup(contextlist[0]); + freeconary(contextlist); + } + } else { + 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; + } + } + 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)) { + 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 && strncmp(tty, "NODEV", 5) ) { + 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_DEBUG, _("%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; + if (! (selinux_enabled )) + { + return PAM_SUCCESS; + } + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + } + } + + if (ttyn) { + 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_DEBUG, _("%s: setcontext back to orginal"),MODULE); + + return PAM_SUCCESS; +} + +#else /* PAM_SELINUX_MAIN */ + +/************************************************************************ + * + * All PAM code goes in this section. + * + ************************************************************************/ + +#include /* for getuid(), exit(), getopt() */ +#include +#include /* for wait() */ + +#include /* for PAM functions */ +#include /* 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) ) { + result = 1; /* user authenticated OK! */ + } + pam_open_session(*pamh, 0); + 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; + execv("/bin/sh",argv); + fprintf(stderr,"Failure\n"); + return 0; +} +#endif diff -urN pam-pld-0.77.3.org/modules/pam_selinux/pam_selinux_check.8 pam-pld-0.77.3/modules/pam_selinux/pam_selinux_check.8 --- pam-pld-0.77.3.org/modules/pam_selinux/pam_selinux_check.8 1970-01-01 01:00:00.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_selinux/pam_selinux_check.8 2003-12-27 01:14:20.000000000 +0100 @@ -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 diff -urN pam-pld-0.77.3.org/modules/pam_selinux/pam_selinux_check.c pam-pld-0.77.3/modules/pam_selinux/pam_selinux_check.c --- pam-pld-0.77.3.org/modules/pam_selinux/pam_selinux_check.c 1970-01-01 01:00:00.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_selinux/pam_selinux_check.c 2003-12-27 01:14:20.000000000 +0100 @@ -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 + * + * 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" + diff -urN pam-pld-0.77.3.org/modules/pam_selinux/README pam-pld-0.77.3/modules/pam_selinux/README --- pam-pld-0.77.3.org/modules/pam_selinux/README 1970-01-01 01:00:00.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_selinux/README 2003-12-27 01:14:20.000000000 +0100 @@ -0,0 +1,15 @@ +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: + 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. + +Dan Walsh + + diff -urN pam-pld-0.77.3.org/modules/pam_unix/lckpwdf.-c pam-pld-0.77.3/modules/pam_unix/lckpwdf.-c --- pam-pld-0.77.3.org/modules/pam_unix/lckpwdf.-c 2003-12-27 01:14:07.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_unix/lckpwdf.-c 2003-12-27 01:14:20.000000000 +0100 @@ -26,6 +26,9 @@ #include #include +#ifdef WITH_SELINUX +#include +#endif #define LOCKFILE "/etc/.pwd.lock" #define TIMEOUT 15 @@ -64,6 +67,28 @@ if (lockfd != -1) return -1; +#ifdef WITH_SELINUX + if(is_selinux_enabled()) + { + lockfd = open(LOCKFILE, O_WRONLY); + if(lockfd == -1 && errno == ENOENT) + { + security_context_t create_context; + int rc; + + if(getfilecon("/etc/passwd", &create_context)) + return -1; + rc = setfscreatecon(create_context); + freecon(create_context); + if(rc) + return -1; + lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600); + if(setfscreatecon(NULL)) + return -1; + } + } + else +#endif lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600); if (lockfd == -1) return -1; diff -urN pam-pld-0.77.3.org/modules/pam_unix/Makefile.am pam-pld-0.77.3/modules/pam_unix/Makefile.am --- pam-pld-0.77.3.org/modules/pam_unix/Makefile.am 2003-12-27 01:14:07.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_unix/Makefile.am 2003-12-27 03:20:17.576098016 +0100 @@ -9,6 +9,14 @@ modulesdir = /lib/security sbindir = /sbin +if LIBSELINUX_PRESENT +LIBSELINUX = -lselinux +DEFS = -DWITH_SELINUX=1 +else +LIBSELINUX = +DEFS = +endif + AM_CPPFLAGS = -DCHKPWD_HELPER=\"$(sbindir)/$(sbin_PROGRAMS)\" \ -I$(top_srcdir)/include \ -I$(top_srcdir)/pamcrypt/include @@ -25,15 +33,15 @@ if NSLLIB_PRESENT if CRACKLIB_PRESENT -pam_unix_la_LIBADD = -lnsl -lcrack ../../pamcrypt/libpamcrypt.la +pam_unix_la_LIBADD = -lnsl -lcrack $(LIBSELINUX) ../../pamcrypt/libpamcrypt.la else -pam_unix_la_LIBADD = -lnsl ../../pamcrypt/libpamcrypt.la +pam_unix_la_LIBADD = -lnsl $(LIBSELINUX) ../../pamcrypt/libpamcrypt.la endif else if CRACKLIB_PRESENT -pam_unix_la_LIBADD = -lcrack ../../pamcrypt/libpamcrypt.la +pam_unix_la_LIBADD = -lcrack $(LIBSELINUX) ../../pamcrypt/libpamcrypt.la else -pam_unix_la_LIBADD = ../../pamcrypt/libpamcrypt.la +pam_unix_la_LIBADD = $(LIBSELINUX) ../../pamcrypt/libpamcrypt.la endif endif @@ -42,7 +50,7 @@ sbin_PROGRAMS = unix_chkpwd unix_chkpwd_SOURCES = unix_chkpwd.c -unix_chkpwd_LDADD = ../../pamcrypt/libpamcrypt.la +unix_chkpwd_LDADD = $(LIBSELINUX) ../../pamcrypt/libpamcrypt.la EXTRA_DIST = README CHANGELOG diff -urN pam-pld-0.77.3.org/modules/pam_unix/pam_unix_acct.c pam-pld-0.77.3/modules/pam_unix/pam_unix_acct.c --- pam-pld-0.77.3.org/modules/pam_unix/pam_unix_acct.c 2003-12-27 01:14:07.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_unix/pam_unix_acct.c 2003-12-27 01:14:20.000000000 +0100 @@ -44,6 +44,15 @@ #include #include #include /* for time() */ +#include +#include +#include +#ifdef WITH_SELINUX +#include +#define SELINUX_ENABLED is_selinux_enabled() +#else +#define SELINUX_ENABLED 0 +#endif #include "security/_pam_macros.h" @@ -59,7 +68,110 @@ #endif /* LINUX_PAM */ #include "support.h" +struct spwd spwd; +struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user) +{ + int retval=0, child, fds[2]; + void (*sighandler)(int) = NULL; + D(("running verify_binary")); + + /* create a pipe for the messages */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + _log_err(LOG_ERR, pamh, "Could not make pipe %s",strerror(errno)); + return NULL; + } + D(("called.")); + + if (off(UNIX_NOREAP, ctrl)) { + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + * + * The "noreap" module argument is provided so that the admin can + * override this behavior. + */ + sighandler = signal(SIGCHLD, SIG_DFL); + } + + /* fork */ + child = fork(); + if (child == 0) { + int i=0; + struct rlimit rlim; + static char *envp[] = { NULL }; + char *args[] = { NULL, NULL, NULL, NULL }; + + close(0); close(1); + /* reopen stdin as pipe */ + close(fds[0]); + dup2(fds[1], STDOUT_FILENO); + + /* XXX - should really tidy up PAM here too */ + + if (getrlimit(RLIMIT_NOFILE,&rlim)==0) { + for (i=2; i < rlim.rlim_max; i++) { + if (fds[1] != i) { + close(i); + } + } + } + /* exec binary helper */ + args[0] = x_strdup(CHKPWD_HELPER); + args[1] = x_strdup(user); + args[2] = x_strdup("verify"); + + execve(CHKPWD_HELPER, args, envp); + + _log_err(LOG_ERR, pamh, "execve failed: %s",strerror(errno)); + /* should not get here: exit with error */ + D(("helper binary is not available")); + exit(PAM_AUTHINFO_UNAVAIL); + } else { + if (child > 0) { + char buf[1024]; + int rc=0; + rc = read(fds[0], buf, sizeof(buf) - 1); + if(rc > 0) + { + buf[rc] = '\0'; + sscanf(buf,"%ld:%ld:%ld:%ld:%ld:%ld", + &spwd.sp_lstchg, /* last password change */ + &spwd.sp_min, /* days until change allowed. */ + &spwd.sp_max, /* days before change required */ + &spwd.sp_warn, /* days warning for expiration */ + &spwd.sp_inact, /* days before account inactive */ + &spwd.sp_expire); /* date when account expires */ + } + else { + _log_err(LOG_ERR, pamh, " ERROR %d:%s \n",rc, strerror(errno)); + } + close(fds[1]); + close(fds[0]); + rc=waitpid(child, &retval, 0); /* wait for helper to complete */ + if (rc<0) { + _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno)); + retval = PAM_AUTH_ERR; + } else { + retval = WEXITSTATUS(retval); + } + } else { + D(("fork failed")); + close(fds[0]);close(fds[1]); + retval = PAM_AUTH_ERR; + } + } + if (sighandler != NULL) { + (void) signal(SIGCHLD, sighandler); /* restore old signal handler */ + } + D(("Returning %d",retval)); + if (retval != PAM_SUCCESS) { + return NULL; + } + return &spwd; +} /* * PAM framework looks for this entry-point to pass control to the * account management module. @@ -128,6 +240,9 @@ return PAM_SUCCESS; } + if (!spent && SELINUX_ENABLED ) + spent = _unix_run_verify_binary(pamh, ctrl, uname); + if (!spent) return PAM_AUTHINFO_UNAVAIL; /* Couldn't get username from shadow */ diff -urN pam-pld-0.77.3.org/modules/pam_unix/pam_unix_passwd.c pam-pld-0.77.3/modules/pam_unix/pam_unix_passwd.c --- pam-pld-0.77.3.org/modules/pam_unix/pam_unix_passwd.c 2003-12-27 01:14:07.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_unix/pam_unix_passwd.c 2003-12-27 03:09:39.984026744 +0100 @@ -56,7 +56,18 @@ #include #include #include - +#include +#include +#include +static int selinux_enabled=-1; +#ifdef WITH_SELINUX +#include +static security_context_t prev_context=NULL; +#define SELINUX_ENABLED (selinux_enabled != -1 ? selinux_enabled : selinux_enabled=is_selinux_enabled()) +#else +#define SELINUX_ENABLED 0 +#endif + #ifdef HAVE_CRACK_H #include #endif @@ -260,6 +271,97 @@ return master; } +static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, const char *fromwhat, const char *towhat) +{ + int retval, child, fds[2]; + void (*sighandler)(int) = NULL; + + D(("called.")); + /* create a pipe for the password */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + return PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + * + * The "noreap" module argument is provided so that the admin can + * override this behavior. + */ + sighandler = signal(SIGCHLD, SIG_DFL); + } + + /* fork */ + child = fork(); + if (child == 0) { + int i=0; + struct rlimit rlim; + static char *envp[] = { NULL }; + char *args[] = { NULL, NULL, NULL, NULL }; + + /* XXX - should really tidy up PAM here too */ + + close(0); close(1); + /* reopen stdin as pipe */ + close(fds[1]); + dup2(fds[0], STDIN_FILENO); + + if (getrlimit(RLIMIT_NOFILE,&rlim)==0) { + for (i=2; i < rlim.rlim_max; i++) { + if (fds[0] != i) + close(i); + } + } + /* exec binary helper */ + args[0] = x_strdup(CHKPWD_HELPER); + args[1] = x_strdup(user); + args[2] = x_strdup("shadow"); + + execve(CHKPWD_HELPER, args, envp); + + /* should not get here: exit with error */ + D(("helper binary is not available")); + exit(PAM_AUTHINFO_UNAVAIL); + } else if (child > 0) { + /* wait for child */ + /* if the stored password is NULL */ + int rc=0; + if (fromwhat) + write(fds[1], fromwhat, strlen(fromwhat)+1); + else + write(fds[1], "", 1); + if (towhat) { + write(fds[1], towhat, strlen(towhat)+1); + } + else + write(fds[1], "", 1); + + close(fds[0]); /* close here to avoid possible SIGPIPE above */ + close(fds[1]); + rc=waitpid(child, &retval, 0); /* wait for helper to complete */ + if (rc<0) { + _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno)); + retval = PAM_AUTH_ERR; + } else { + retval = WEXITSTATUS(retval); + } + } else { + D(("fork failed")); + close(fds[0]); + close(fds[1]); + retval = PAM_AUTH_ERR; + } + + if (sighandler != NULL) { + (void) signal(SIGCHLD, sighandler); /* restore old signal handler */ + } + + return retval; +} static int check_old_password(const char *forwho, const char *newpass) { static char buf[16384]; @@ -318,16 +420,41 @@ } oldmask = umask(077); + + if (SELINUX_ENABLED) { + security_context_t passwd_context = NULL; + + if (getfilecon("/etc/passwd",&passwd_context) < 0) { + return PAM_AUTHTOK_ERR; + } + + if (getfscreatecon(&prev_context)<0) { + freecon(passwd_context); + return PAM_AUTHTOK_ERR; + } + + if (setfscreatecon(passwd_context)) { + freecon(passwd_context); + freecon(prev_context); + return PAM_AUTHTOK_ERR; + } + + freecon(passwd_context); + } + pwfile = fopen(OPW_TMPFILE, "w"); umask(oldmask); - if (pwfile == NULL) { - return PAM_AUTHTOK_ERR; - } + + if (pwfile == NULL) { + err = 1; + goto done; + } opwfile = fopen(OLD_PASSWORDS_FILE, "r"); if (opwfile == NULL) { - fclose(pwfile); - return PAM_AUTHTOK_ERR; + fclose(pwfile); + err=1; + goto done; } chown(OPW_TMPFILE, 0, 0); @@ -388,8 +515,20 @@ err = 1; } - if (!err) { +done: + if (!err) rename(OPW_TMPFILE, OLD_PASSWORDS_FILE); +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + if (setfscreatecon(prev_context)) { + err = 1; + } + if (prev_context) + freecon(prev_context); + prev_context=NULL; + } +#endif + if (!err) { return PAM_SUCCESS; } else { unlink(OPW_TMPFILE); @@ -406,16 +545,36 @@ int oldmask; oldmask = umask(077); +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + security_context_t passwd_context=NULL; + if (getfilecon("/etc/passwd",&passwd_context)<0) { + return PAM_AUTHTOK_ERR; + }; + if (getfscreatecon(&prev_context)<0) { + freecon(passwd_context); + return PAM_AUTHTOK_ERR; + } + if (setfscreatecon(passwd_context)) { + freecon(passwd_context); + freecon(prev_context); + return PAM_AUTHTOK_ERR; + } + freecon(passwd_context); + } +#endif pwfile = fopen(PW_TMPFILE, "w"); umask(oldmask); - if (pwfile == NULL) { - return PAM_AUTHTOK_ERR; - } + if (pwfile == NULL) { + err=1; + goto done; + } opwfile = fopen("/etc/passwd", "r"); if (opwfile == NULL) { - fclose(pwfile); - return PAM_AUTHTOK_ERR; + fclose(pwfile); + err = 1; + goto done; } chown(PW_TMPFILE, 0, 0); @@ -447,9 +606,22 @@ err = 1; } +done: if (!err) { rename(PW_TMPFILE, "/etc/passwd"); _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho); + } +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + if (setfscreatecon(prev_context)) { + err = 1; + } + if (prev_context) + freecon(prev_context); + prev_context=NULL; + } +#endif + if (!err) { return PAM_SUCCESS; } else { unlink(PW_TMPFILE); @@ -465,20 +637,43 @@ int oldmask; spwdent = getspnam(forwho); - if (spwdent == NULL) { - return PAM_USER_UNKNOWN; - } + + if (spwdent == NULL) { + return PAM_USER_UNKNOWN; + } + oldmask = umask(077); +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + security_context_t shadow_context=NULL; + if (getfilecon("/etc/shadow",&shadow_context)<0) { + return PAM_AUTHTOK_ERR; + }; + if (getfscreatecon(&prev_context)<0) { + freecon(shadow_context); + return PAM_AUTHTOK_ERR; + } + if (setfscreatecon(shadow_context)) { + freecon(shadow_context); + freecon(prev_context); + return PAM_AUTHTOK_ERR; + } + freecon(shadow_context); + } +#endif pwfile = fopen(SH_TMPFILE, "w"); umask(oldmask); - if (pwfile == NULL) { - return PAM_AUTHTOK_ERR; - } + + if (pwfile == NULL) { + err = 1; + goto done; + } opwfile = fopen("/etc/shadow", "r"); if (opwfile == NULL) { fclose(pwfile); - return PAM_AUTHTOK_ERR; + err = 1; + goto done; } chown(SH_TMPFILE, 0, 0); @@ -508,8 +703,22 @@ err = 1; } +done: if (!err) { rename(SH_TMPFILE, "/etc/shadow"); + } +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + if (setfscreatecon(prev_context)) { + err = 1; + } + if (prev_context) + freecon(prev_context); + prev_context=NULL; + } +#endif + + if (!err) { return PAM_SUCCESS; } else { unlink(SH_TMPFILE); @@ -600,6 +809,8 @@ } if (on(UNIX_SHADOW, ctrl) || (strcmp(pwd->pw_passwd, "x") == 0)) { retval = _update_shadow(forwho, towhat); + if (retval != PAM_SUCCESS && SELINUX_ENABLED) + retval = _unix_run_shadow_binary(pamh, ctrl, forwho, fromwhat, towhat); if (retval == PAM_SUCCESS) retval = _update_passwd(pamh, forwho, "x"); } else { @@ -609,7 +820,7 @@ return retval; } -static int _unix_verify_shadow(const char *user, unsigned int ctrl) +static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl) { struct passwd *pwd = NULL; /* Password and shadow password */ struct spwd *spwdent = NULL; /* file entries for the user */ @@ -630,6 +841,8 @@ spwdent = getspnam(user); endspent(); + if (spwdent == NULL && SELINUX_ENABLED ) + spwdent = _unix_run_verify_binary(pamh, ctrl, user); if (spwdent == NULL) return PAM_AUTHINFO_UNAVAIL; } else { @@ -877,7 +1090,7 @@ _log_err(LOG_CRIT, pamh, "failed to set PAM_OLDAUTHTOK"); } - retval = _unix_verify_shadow(user, ctrl); + retval = _unix_verify_shadow(pamh,user, ctrl); if (retval == PAM_AUTHTOK_ERR) { if (off(UNIX__IAMROOT, ctrl)) _make_remark(pamh, ctrl, PAM_ERROR_MSG, @@ -926,7 +1139,7 @@ #endif return retval; } - retval = _unix_verify_shadow(user, ctrl); + retval = _unix_verify_shadow(pamh, user, ctrl); if (retval != PAM_SUCCESS) { _log_err(LOG_NOTICE, pamh, "user not authenticated 2"); #ifdef USE_LCKPWDF diff -urN pam-pld-0.77.3.org/modules/pam_unix/support.c pam-pld-0.77.3/modules/pam_unix/support.c --- pam-pld-0.77.3.org/modules/pam_unix/support.c 2003-12-27 01:14:07.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_unix/support.c 2003-12-27 03:10:49.073523544 +0100 @@ -13,11 +13,18 @@ #include #include #include +#include #include #include #include #include - +#ifdef WITH_SELINUX +#include +#define SELINUX_ENABLED is_selinux_enabled() +#else +#define SELINUX_ENABLED 0 +#endif + #include "security/_pam_macros.h" #include "security/pam_modules.h" @@ -464,18 +471,32 @@ /* fork */ child = fork(); if (child == 0) { + int i=0; + struct rlimit rlim; static char *envp[] = { NULL }; - char *args[] = { NULL, NULL, NULL }; + char *args[] = { NULL, NULL, NULL, NULL }; /* XXX - should really tidy up PAM here too */ + close(0); close(1); /* reopen stdin as pipe */ close(fds[1]); dup2(fds[0], STDIN_FILENO); + if (getrlimit(RLIMIT_NOFILE,&rlim)==0) { + for (i=2; i < rlim.rlim_max; i++) { + if (fds[0] != i) + close(i); + } + } /* exec binary helper */ args[0] = x_strdup(CHKPWD_HELPER); args[1] = x_strdup(user); + if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */ + args[2]=x_strdup("nullok"); + } else { + args[2]=x_strdup("nonull"); + } execve(CHKPWD_HELPER, args, envp); @@ -485,11 +506,7 @@ } else if (child > 0) { /* wait for child */ /* if the stored password is NULL */ - if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */ - write(fds[1], "nullok\0\0", 8); - } else { - write(fds[1], "nonull\0\0", 8); - } + int rc=0; if (passwd != NULL) { /* send the password to the child */ write(fds[1], passwd, strlen(passwd)+1); passwd = NULL; @@ -498,10 +515,17 @@ } close(fds[0]); /* close here to avoid possible SIGPIPE above */ close(fds[1]); - (void) waitpid(child, &retval, 0); /* wait for helper to complete */ - retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR; + rc=waitpid(child, &retval, 0); /* wait for helper to complete */ + if (rc<0) { + _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno)); + retval = PAM_AUTH_ERR; + } else { + retval = WEXITSTATUS(retval); + } } else { D(("fork failed")); + close(fds[0]); + close(fds[1]); retval = PAM_AUTH_ERR; } @@ -523,6 +547,7 @@ char *data_name; int retval; + D(("called")); #ifdef HAVE_PAM_FAIL_DELAY @@ -589,7 +614,7 @@ retval = PAM_SUCCESS; if (pwd == NULL || salt == NULL || !strcmp(salt, "x")) { - if (geteuid()) { + if (geteuid() || SELINUX_ENABLED) { /* we are not root perhaps this is the reason? Run helper */ D(("running helper binary")); retval = _unix_run_helper_binary(pamh, p, ctrl, name); diff -urN pam-pld-0.77.3.org/modules/pam_unix/support.h pam-pld-0.77.3/modules/pam_unix/support.h --- pam-pld-0.77.3.org/modules/pam_unix/support.h 2003-12-27 01:14:07.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_unix/support.h 2003-12-27 01:14:20.000000000 +0100 @@ -147,5 +147,6 @@ ,const char *data_name ,const char **pass); +extern struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user); #endif /* _PAM_UNIX_SUPPORT_H */ diff -urN pam-pld-0.77.3.org/modules/pam_unix/unix_chkpwd.c pam-pld-0.77.3/modules/pam_unix/unix_chkpwd.c --- pam-pld-0.77.3.org/modules/pam_unix/unix_chkpwd.c 2003-12-27 01:14:07.000000000 +0100 +++ pam-pld-0.77.3/modules/pam_unix/unix_chkpwd.c 2003-12-27 02:58:53.004382600 +0100 @@ -27,13 +27,24 @@ #include #include #include +#include #include #include #include #include - +#include +static int selinux_enabled=-1; +#ifdef WITH_SELINUX +#include +#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : selinux_enabled=is_selinux_enabled()) +static security_context_t prev_context=NULL; +#else +#define SELINUX_ENABLED 0 +#endif + #define MAXPASS 200 /* the maximum length of a password */ +#include #include "security/_pam_macros.h" #include "pam_crypt.h" @@ -86,6 +97,33 @@ (void) sigaction(SIGQUIT, &action, NULL); } +static int _verify_account(const char * const uname) +{ + struct spwd *spent; + struct passwd *pwent; + + pwent = getpwnam(uname); + if (!pwent) { + _log_err(LOG_ALERT, "could not identify user (from getpwnam(%s))", uname); + return PAM_USER_UNKNOWN; + } + + spent = getspnam( uname ); + if (!spent) { + _log_err(LOG_ALERT, "could not get username from shadow (%s))", uname); + return PAM_AUTHINFO_UNAVAIL; /* Couldn't get username from shadow */ + } + printf("%ld:%ld:%ld:%ld:%ld:%ld", + spent->sp_lstchg, /* last password change */ + spent->sp_min, /* days until change allowed. */ + spent->sp_max, /* days before change required */ + spent->sp_warn, /* days warning for expiration */ + spent->sp_inact, /* days before account inactive */ + spent->sp_expire); /* date when account expires */ + + return PAM_SUCCESS; +} + static int _unix_verify_password(const char *name, const char *p, int opt) { struct passwd *pwd = NULL; @@ -223,11 +261,171 @@ return username; } + #define SH_TMPFILE "/etc/nshadow" + static int _update_shadow(const char *forwho) + { + struct spwd *spwdent = NULL, *stmpent = NULL; + FILE *pwfile, *opwfile; + int err = 1; + int oldmask; + struct stat st; + char pass[MAXPASS + 1]; + char towhat[MAXPASS + 1]; + int npass=0; + + /* read the password from stdin (a pipe from the pam_unix module) */ + + npass = read(STDIN_FILENO, pass, MAXPASS); + + if (npass < 0) { /* is it a valid password? */ + + _log_err(LOG_DEBUG, "no password supplied"); + return PAM_AUTHTOK_ERR; + + } else if (npass >= MAXPASS) { + + _log_err(LOG_DEBUG, "password too long"); + return PAM_AUTHTOK_ERR; + + } else { + /* does pass agree with the official one? */ + int retval=0; + pass[npass] = '\0'; /* NUL terminate */ + retval = _unix_verify_password(forwho, pass, 0); + if (retval != PAM_SUCCESS) { + return retval; + } + } + + /* read the password from stdin (a pipe from the pam_unix module) */ + + npass = read(STDIN_FILENO, towhat, MAXPASS); + + if (npass < 0) { /* is it a valid password? */ + + _log_err(LOG_DEBUG, "no new password supplied"); + return PAM_AUTHTOK_ERR; + + } else if (npass >= MAXPASS) { + + _log_err(LOG_DEBUG, "new password too long"); + return PAM_AUTHTOK_ERR; + + } + + towhat[npass] = '\0'; /* NUL terminate */ + spwdent = getspnam(forwho); + if (spwdent == NULL) { + return PAM_USER_UNKNOWN; + } + oldmask = umask(077); + + #ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + security_context_t shadow_context=NULL; + if (getfilecon("/etc/shadow",&shadow_context)<0) { + return PAM_AUTHTOK_ERR; + }; + if (getfscreatecon(&prev_context)<0) { + freecon(shadow_context); + return PAM_AUTHTOK_ERR; + } + if (setfscreatecon(shadow_context)) { + freecon(shadow_context); + freecon(prev_context); + return PAM_AUTHTOK_ERR; + } + freecon(shadow_context); + } + #endif + pwfile = fopen(SH_TMPFILE, "w"); + umask(oldmask); + if (pwfile == NULL) { + err = 1; + goto done; + } + + opwfile = fopen("/etc/shadow", "r"); + if (opwfile == NULL) { + fclose(pwfile); + err = 1; + goto done; + } + + if (fstat(fileno(opwfile), &st) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + if (fchmod(fileno(pwfile), st.st_mode) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + stmpent = fgetspent(opwfile); + while (stmpent) { + + if (!strcmp(stmpent->sp_namp, forwho)) { + stmpent->sp_pwdp = towhat; + stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24); + err = 0; + D(("Set password %s for %s", stmpent->sp_pwdp, forwho)); + } + + if (putspent(stmpent, pwfile)) { + D(("error writing entry to shadow file: %s\n", strerror(errno))); + err = 1; + break; + } + + stmpent = fgetspent(opwfile); + } + fclose(opwfile); + + if (fclose(pwfile)) { + D(("error writing entries to shadow file: %s\n", strerror(errno))); + err = 1; + } + + done: + if (!err) { + rename(SH_TMPFILE, "/etc/shadow"); + } + + #ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + if (setfscreatecon(prev_context)) { + err = 1; + } + if (prev_context) + freecon(prev_context); + prev_context=NULL; + } + #endif + + if (!err) { + return PAM_SUCCESS; + } else { + unlink(SH_TMPFILE); + return PAM_AUTHTOK_ERR; + } + } + + int main(int argc, char *argv[]) { char pass[MAXPASS + 1]; - char option[8]; int npass, opt; + char *option; int force_failure = 0; int retval = UNIX_FAILED; char *user; @@ -246,8 +444,7 @@ * account). */ - if (isatty(STDIN_FILENO)) { - + if (isatty(STDIN_FILENO) || argc != 3 ) { _log_err(LOG_NOTICE ,"inappropriate use of Unix helper binary [UID=%d]" ,getuid()); @@ -258,19 +455,43 @@ return UNIX_FAILED; } - /* - * determine the current user's name is - */ - user = getuidname(getuid()); - if (argc == 2) { - /* if the caller specifies the username, verify that user - matches it */ - if (strcmp(user, argv[1])) { - force_failure = 1; - } - } + /* + * determine the current user's name is. + * On a SELinux enabled system, policy will prevent third parties from using + * unix_chkpwd as a password guesser. Leaving the existing check prevents + * su from working, Since the current uid is the users and the password is + * for root. + */ + if (SELINUX_ENABLED) { + user=argv[1]; + } + else { + user = getuidname(getuid()); + /* if the caller specifies the username, verify that user + matches it */ + if (strcmp(user, argv[1])) { + return UNIX_FAILED; + } + } + + option=argv[2]; + + if (strncmp(argv[2], "verify", 8) == 0) { + /* Get the account information from the shadow file */ + return _verify_account(argv[1]); + } + + if (strncmp(option, "shadow", 8) == 0) { + /* Attempting to change the password */ + return _update_shadow(argv[1]); + } + /* read the nullok/nonull option */ + if (strncmp(option, "nullok", 8) == 0) + nullok = 1; + else + nullok = 0; - /* read the nullok/nonull option */ + /* read the password from stdin (a pipe from the pam_unix module) */ npass = read(STDIN_FILENO, option, 8);