diff -urp SimplePAMApps-0.60.orig/common/include/su_indep.h SimplePAMApps-0.60/common/include/su_indep.h --- SimplePAMApps-0.60.orig/common/include/su_indep.h Thu Dec 10 08:46:24 1998 +++ SimplePAMApps-0.60/common/include/su_indep.h Sun Jan 12 19:24:44 2003 @@ -1,5 +1,11 @@ #include +typedef struct +{ + const char *user, *command, *shell; + int login, fast; +} su_context; + extern int is_terminal; void store_terminal_modes(); @@ -12,7 +18,5 @@ void restore_terminal_owner(); int make_process_unkillable(const char **callname , const char **err_descr); void make_process_killable(); -void usage(); -void parse_command_line(int argc, char *argv[] - , int *is_login, const char **user, const char **command); +void parse_command_line(int argc, char *argv[], su_context *ctx); diff -urp SimplePAMApps-0.60.orig/common/lib/shell_args.c SimplePAMApps-0.60/common/lib/shell_args.c --- SimplePAMApps-0.60.orig/common/lib/shell_args.c Tue Dec 8 17:02:48 1998 +++ SimplePAMApps-0.60/common/lib/shell_args.c Sun Jan 12 19:24:44 2003 @@ -13,7 +13,7 @@ * The assumptions used by this code are that a an argument list is * returned in the following cases: * - * 1. login is true [command is always ignored] + * 1. login is true * * - the user has a shell entry which is used * - if the user does not have a shell entry NULL is returned @@ -56,9 +56,6 @@ char * const *build_shell_args(const cha const char **args=NULL; /* array of PATH+ARGS+NULL pointers */ D(("called.")); - if (login) { - command = NULL; /* command always ignored for login */ - } if (pw_shell && *pw_shell != '\0') { char *line; diff -urp SimplePAMApps-0.60.orig/common/lib/su_indep.c SimplePAMApps-0.60/common/lib/su_indep.c --- SimplePAMApps-0.60.orig/common/lib/su_indep.c Mon Feb 1 08:25:05 1999 +++ SimplePAMApps-0.60/common/lib/su_indep.c Tue Apr 15 09:37:55 2003 @@ -15,6 +15,8 @@ #include #include #include +#include +#include "../include/su_indep.h" /* -------------------------------------------- */ /* ------ declarations ------------------------ */ @@ -184,71 +186,98 @@ void make_process_killable() /* ------ command line parser ----------------- */ -void usage() +__attribute__ ((__noreturn__)) +static void usage (int status) { - (void) fprintf(stderr,"usage: su [-] [-c \"command\"] [username]\n"); - exit(1); + fprintf (stderr, "usage: su [-|-l] [-c \"command\"] [-s \"shell\"] [username]\n"); + exit (status); } -void parse_command_line(int argc, char *argv[] - , int *is_login, const char **user, const char **command) +void parse_command_line(int argc, char *argv[], su_context *ctx) { - int username_present, command_present; - - *is_login = 0; - *user = NULL; - *command = NULL; - username_present = command_present = 0; - - while ( --argc > 0 ) { - const char *token; - - token = *++argv; - if (*token == '-') { - switch (*++token) { - case '\0': /* su as a login shell for the user */ - if (*is_login) - usage(); - *is_login = 1; - break; - case 'c': - if (command_present) { - usage(); - } else { /* indicate we are running commands */ - if (*++token != '\0') { - command_present = 1; - *command = token; - } else if (--argc > 0) { - command_present = 1; - *command = *++argv; - } else - usage(); + int optc; + struct option const longopts[] = + { + { "command", required_argument, 0, 'c' }, + { "login", no_argument, 0, 'l' }, + { "shell", required_argument, 0, 's' }, + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + if (argc < 1) + usage (EXIT_FAILURE); + + memset (ctx, 0, sizeof(*ctx)); + + while ((optc = getopt_long (argc, argv, "c:ls:", longopts, 0)) != -1) + { + switch (optc) + { + case 'c': + if (ctx->command) + usage (EXIT_FAILURE); + ctx->command = optarg; + break; + + case 'l': + if (ctx->login) + usage (EXIT_FAILURE); + ctx->login = 1; + break; + + case 's': + if (ctx->shell) + usage (EXIT_FAILURE); + ctx->shell = optarg; + break; + + case 'h': + usage (EXIT_SUCCESS); + + default: + usage (EXIT_FAILURE); } - break; - default: - usage(); - } - } else { /* must be username */ - if (username_present) - usage(); - username_present = 1; - *user = *argv; } - } - if (!username_present) { /* default user is superuser */ - const struct passwd *pw; + if (optind < argc && !strcmp (argv[optind], "-")) + { + if (ctx->login) + usage (EXIT_FAILURE); + ctx->login = 1; + ++optind; + } + + if (optind < argc) + ctx->user = argv[optind++]; + + if (optind < argc) + usage (EXIT_FAILURE); + + if (!ctx->user) + { /* default user is superuser */ + const struct passwd *pw = getpwuid (ROOT_UID); + if (!(pw && pw->pw_name)) /* No ROOT_UID!? */ + { + fprintf (stderr, "su: no access to superuser identity (%d)!\n", ROOT_UID); + exit (EXIT_FAILURE); + } + + ctx->user = strdup (pw->pw_name); + endpwent(); + } - pw = getpwuid(ROOT_UID); - if (pw == NULL) /* No ROOT_UID!? */ + if (ctx->shell) { - printf ("\nsu:no access to superuser identity!? (%d)\n", - ROOT_UID); - exit (1); - } - - *user = NULL; - if (pw->pw_name != NULL) - *user = strdup(pw->pw_name); - } + if (getuid()) + { + fprintf (stderr, "su: setting shell: permission denied\n"); + exit (EXIT_FAILURE); + } + if (access (ctx->shell, X_OK) < 0) + { + fprintf (stderr, "su: setting shell: %s\n", strerror (errno)); + exit (EXIT_FAILURE); + } + } } diff -urp SimplePAMApps-0.60.orig/pamapps/lib/wtmp-gate.c SimplePAMApps-0.60/pamapps/lib/wtmp-gate.c --- SimplePAMApps-0.60.orig/pamapps/lib/wtmp-gate.c Wed Nov 25 12:33:32 1998 +++ SimplePAMApps-0.60/pamapps/lib/wtmp-gate.c Mon Nov 19 04:47:15 2001 @@ -35,6 +35,7 @@ int utmp_open_session(pam_handle_t *pamh *err_descr = pam_strerror(pamh, retval); return -1; } + if (!terminal) terminal = "???"; retval = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost); if (retval != PAM_SUCCESS) rhost = NULL; @@ -55,6 +56,7 @@ int utmp_close_session(pam_handle_t *pam *err_descr = pam_strerror(pamh, retval); return -1; } + if (!terminal) terminal = "???"; return utmp_do_close_session(terminal, callname, err_descr); diff -urp SimplePAMApps-0.60.orig/pamapps/su/su.1 SimplePAMApps-0.60/pamapps/su/su.1 --- SimplePAMApps-0.60.orig/pamapps/su/su.1 Fri Jun 12 17:11:04 1998 +++ SimplePAMApps-0.60/pamapps/su/su.1 Tue Apr 15 12:44:09 2003 @@ -8,7 +8,8 @@ su \- assume a user's identity .SH SYNOPSIS -.B su [-] [-c "command"] [username] +.B su +[\fB-\fR|\fB-l\fR] [\fB-c \fIcommand\fR] [\fB-s \fIshell\fR] [\fIusername\fR] .sp 2 .SH DESCRIPTION .B Su @@ -85,49 +86,28 @@ encountering an accounting failure, a wa .BR su will proceed to invoke the sought user's shell. -.sp -A simple entry in the -.I Linux-PAM -configuration file for this service would be: -.br - -.br - # -.br - # su service, authentication not required for root. -.br - # For other users authentication is provided via -.br - # pwdb. Some account management done with time module -.br - # -.br - su auth sufficient pam_rootok.so -.br - su auth required pam_pwdb.so -.br - su account required pam_pwdb.so - su account required pam_time.so -.br - su session required pam_pwdb.so -.br - # - -.sp -(For the equivalent -.B /etc/pam.d/su -file, you should delete the first field ("su") from the above -example). Note, the -.BR password -module-type is not required for this application to function -correctly. - -.sp -For continuity of service, the administrator might wish to make the -.BR login "(1)" -and -.BR su -configurations similar. +.SH OPTIONS +.TP +.BI "\-c " command ", \-\-command=" command +Pass +.IR command, +a single command line to run, to the shell with a +.B \-c +option instead of starting an interactive shell. +.TP +.B "\-, \-l, \-\-login" +Invoke the shell as a login shell. +.TP +.BI "\-s, \-\-shell " shell +.RI "Superuser may run " shell " instead of " user 's +command-shell, unless the user running +.B su +is not the superuser and +.IR user 's +shell is restricted. +.TP +.B "\-\-help" +Print a usage message on standard output and exit successfully. .SH "EXIT CODE" diff -urp SimplePAMApps-0.60.orig/pamapps/su/su.c SimplePAMApps-0.60/pamapps/su/su.c --- SimplePAMApps-0.60.orig/pamapps/su/su.c Thu Feb 18 12:29:50 1999 +++ SimplePAMApps-0.60/pamapps/su/su.c Tue Apr 15 09:40:54 2003 @@ -18,9 +18,10 @@ #define DEFAULT_SHELL "/bin/sh" #define SLEEP_TO_KILL_CHILDREN 3 /* seconds to wait after SIGTERM before SIGKILL */ -#define SU_FAIL_DELAY 2000000 /* usec on authentication failure */ +#define SU_FAIL_DELAY 0 /* usec on authentication failure */ #include +#include #include #include #include @@ -65,8 +66,8 @@ static int state; #define SU_STATE_TERMINAL_REOWNED 7 #define SU_STATE_UTMP_WRITTEN 8 -static void exit_now(int exit_code, const char *format, ...); -static void exit_child_now(int exit_code, const char *format, ...); +static void exit_now(int exit_code, const char *format, ...) __attribute__ ((__noreturn__)); +static void exit_child_now(int exit_code, const char *format, ...) __attribute__ ((__noreturn__)); static void do_pam_init(const char *user, int is_login); static void su_exec_shell(const char *shell, uid_t uid, int is_login , const char *command, const char *user); @@ -75,12 +76,11 @@ static void su_exec_shell(const char *sh /* ------ the application itself -------------- */ /* -------------------------------------------- */ -void main(int argc, char *argv[]) +int main(int argc, char *argv[]) { - int retcode, is_login, status; + int retcode, status; int retval, final_retval; /* PAM_xxx return values */ - const char *command, *user; - const char *shell; + su_context ctx; pid_t child; uid_t uid; const char *place, *err_descr; @@ -102,7 +102,7 @@ void main(int argc, char *argv[]) /* ------------ parse the argument list ----------- */ - parse_command_line(argc, argv, &is_login, &user, &command); + parse_command_line(argc, argv, &ctx); /* ------ initialize the Linux-PAM interface ------ */ @@ -112,8 +112,8 @@ void main(int argc, char *argv[]) exit_now(1, "su: failed\n"); #endif - do_pam_init(user, is_login); /* call pam_start and set PAM items */ - user = NULL; /* get this info later (it may change) */ + do_pam_init(ctx.user, ctx.login); /* call pam_start and set PAM items */ + ctx.user = NULL; /* get this info later (it may change) */ /* * Note. We have forgotten everything about the user. We will get @@ -166,10 +166,14 @@ void main(int argc, char *argv[]) * Obtain all of the new credentials of the user */ place = "set_user_credentials"; - retval = set_user_credentials(pamh, is_login, &user, &uid, &shell); - if (retval != PAM_SUCCESS) { - (void) pam_close_session(pamh,retval); - break; + { + const char *shell; + retval = set_user_credentials(pamh, ctx.login, &ctx.user, &uid, &shell); + if (retval != PAM_SUCCESS) { + (void) pam_close_session(pamh, retval); + break; + } + if (!ctx.shell) ctx.shell = shell; } state = SU_STATE_CREDENTIALS_GOTTEN; @@ -183,8 +187,7 @@ void main(int argc, char *argv[]) /* * ... setup terminal, ... */ - retcode = change_terminal_owner(uid, is_login - , &place, &err_descr); + retcode = change_terminal_owner(uid, ctx.login, &place, &err_descr); if (retcode > 0) { (void) fprintf(stderr, "su: %s: %s\n", place, err_descr); err_descr = NULL; /* forget about the problem */ @@ -195,7 +198,7 @@ void main(int argc, char *argv[]) /* * ... make [uw]tmp entries. */ - if (is_login) { + if (ctx.login) { /* * Note: we use the parent pid as a session identifier for * the logging. @@ -218,14 +221,14 @@ void main(int argc, char *argv[]) } if (child == 0) { /* child exec's shell */ - su_exec_shell(shell, uid, is_login, command, user); + su_exec_shell(ctx.shell, uid, ctx.login, ctx.command, ctx.user); /* never reached */ } /* wait for child to terminate */ /* job control is off for login sessions */ - prepare_for_job_control(!is_login && command != NULL); + prepare_for_job_control(!ctx.login && ctx.command); status = wait_for_child(child); if (status != 0) D(("shell returned %d", status)); @@ -293,7 +296,7 @@ void main(int argc, char *argv[]) if (reset_terminal_modes() != 0 && !status) status = 1; - exit(status); /* transparent exit */ + return status; } /* -------------------------------------------- */ @@ -380,7 +383,7 @@ static void do_pam_init(const char *user if (terminal) { retval = pam_set_item(pamh, PAM_TTY, (const void *)terminal); } else { - retval = PAM_PERM_DENIED; /* how did we get here? */ + if (getuid() != 0) retval = PAM_PERM_DENIED; } terminal = NULL; } @@ -389,8 +392,6 @@ static void do_pam_init(const char *user const char *ruser = getlogin(); /* Who is running this program? */ if (ruser) { retval = pam_set_item(pamh, PAM_RUSER, (const void *)ruser); - } else { - retval = PAM_PERM_DENIED; /* must be known to system */ } ruser = NULL; } @@ -403,7 +404,7 @@ static void do_pam_init(const char *user exit_now(1, "su: problem establishing environment\n"); } -#ifdef HAVE_PAM_FAIL_DELAY +#if defined(HAVE_PAM_FAIL_DELAY) && SU_FAIL_DELAY /* have to pause on failure. At least this long (doubles..) */ retval = pam_fail_delay(pamh, SU_FAIL_DELAY); if (retval != PAM_SUCCESS) { @@ -472,6 +473,9 @@ static void su_exec_shell(const char *sh if (shell_args == NULL) { exit_child_now(1, "su: could not identify appropriate shell\n"); } + + retval = pam_misc_setenv(pamh, "SHELL", shell_args[0], 0); + D(("pam_misc_setenv: SHELL=%s: retval=%d", shell_args[0], retval)); /* * and now copy the environment for non-PAM use