1 diff -urNp coreutils-8.4-orig/configure.ac coreutils-8.4/configure.ac
2 --- coreutils-8.4-orig/configure.ac 2010-01-11 18:20:42.000000000 +0100
3 +++ coreutils-8.4/configure.ac 2010-02-12 10:17:46.000000000 +0100
4 @@ -126,6 +126,13 @@ if test "$gl_gcc_warnings" = yes; then
5 AC_SUBST([GNULIB_WARN_CFLAGS])
8 +dnl Give the chance to enable PAM
9 +AC_ARG_ENABLE(pam, dnl
10 +[ --enable-pam Enable use of the PAM libraries],
11 +[AC_DEFINE(USE_PAM, 1, [Define if you want to use PAM])
12 +LIB_PAM="-ldl -lpam -lpam_misc"
18 diff -urNp coreutils-8.4-orig/doc/coreutils.texi coreutils-8.4/doc/coreutils.texi
19 --- coreutils-8.4-orig/doc/coreutils.texi 2010-01-03 18:06:20.000000000 +0100
20 +++ coreutils-8.4/doc/coreutils.texi 2010-02-12 10:17:46.000000000 +0100
21 @@ -15081,8 +15081,11 @@ to certain shells, etc.).
23 @command{su} can optionally be compiled to use @code{syslog} to report
24 failed, and optionally successful, @command{su} attempts. (If the system
25 -supports @code{syslog}.) However, GNU @command{su} does not check if the
26 -user is a member of the @code{wheel} group; see below.
27 +supports @code{syslog}.)
29 +This version of @command{su} has support for using PAM for
30 +authentication. You can edit @file{/etc/pam.d/su} to customize its
33 The program accepts the following options. Also see @ref{Common options}.
35 @@ -15124,6 +15127,8 @@ environment variables except @env{TERM},
36 @env{PATH} to a compiled-in default value. Change to @var{user}'s home
37 directory. Prepend @samp{-} to the shell's name, intended to make it
38 read its login startup file(s).
39 +Additionaly @env{DISPLAY} and @env{XAUTHORITY} environment variables
40 +are preserved as well for PAM functionality.
44 @@ -15163,33 +15168,6 @@ Exit status:
45 the exit status of the subshell otherwise
48 -@cindex wheel group, not supported
49 -@cindex group wheel, not supported
51 -@subsection Why GNU @command{su} does not support the @samp{wheel} group
53 -(This section is by Richard Stallman.)
57 -Sometimes a few of the users try to hold total power over all the
58 -rest. For example, in 1984, a few users at the MIT AI lab decided to
59 -seize power by changing the operator password on the Twenex system and
60 -keeping it secret from everyone else. (I was able to thwart this coup
61 -and give power back to the users by patching the kernel, but I
62 -wouldn't know how to do that in Unix.)
64 -However, occasionally the rulers do tell someone. Under the usual
65 -@command{su} mechanism, once someone learns the root password who
66 -sympathizes with the ordinary users, he or she can tell the rest. The
67 -``wheel group'' feature would make this impossible, and thus cement the
70 -I'm on the side of the masses, not that of the rulers. If you are
71 -used to supporting the bosses and sysadmins in whatever they do, you
72 -might find this idea strange at first.
75 @node timeout invocation
76 @section @command{timeout}: Run a command with a time limit
78 diff -urNp coreutils-8.4-orig/src/Makefile.am coreutils-8.4/src/Makefile.am
79 --- coreutils-8.4-orig/src/Makefile.am 2010-01-03 18:06:20.000000000 +0100
80 +++ coreutils-8.4/src/Makefile.am 2010-02-12 10:17:46.000000000 +0100
81 @@ -361,7 +361,7 @@ factor_LDADD += $(LIB_GMP)
82 uptime_LDADD += $(GETLOADAVG_LIBS)
85 -su_LDADD += $(LIB_CRYPT)
86 +su_LDADD += $(LIB_CRYPT) @LIB_PAM@
88 # for various ACL functions
89 copy_LDADD += $(LIB_ACL)
90 diff -urNp coreutils-8.4-orig/src/su.c coreutils-8.4/src/su.c
91 --- coreutils-8.4-orig/src/su.c 2010-02-12 10:15:15.000000000 +0100
92 +++ coreutils-8.4/src/su.c 2010-02-12 10:24:29.000000000 +0100
94 restricts who can su to UID 0 accounts. RMS considers that to
99 + Actually, with PAM, su has nothing to do with whether or not a
100 + wheel group is enforced by su. RMS tries to restrict your access
101 + to a su which implements the wheel group, but PAM considers that
102 + to be fascist, and gives the user/sysadmin the opportunity to
103 + enforce a wheel group by proper editing of /etc/pam.conf
107 Compile-time options:
108 -DSYSLOG_SUCCESS Log successful su's (by default, to root) with syslog.
109 -DSYSLOG_FAILURE Log failed su's (by default, to root) with syslog.
115 +# include <signal.h>
116 +# include <sys/wait.h>
117 +# include <sys/fsuid.h>
118 +# include <unistd.h>
119 +# include <security/pam_appl.h>
120 +# include <security/pam_misc.h>
121 +#endif /* USE_PAM */
126 @@ -120,10 +139,17 @@
127 /* The user to become if none is specified. */
128 #define DEFAULT_USER "root"
131 char *crypt (char const *key, char const *salt);
134 -static void run_shell (char const *, char const *, char **, size_t)
135 +static void run_shell (char const *, char const *, char **, size_t,
136 + const struct passwd *)
143 /* If true, pass the `-f' option to the subshell. */
144 static bool fast_startup;
145 @@ -209,7 +235,26 @@ log_su (struct passwd const *pw, bool su
150 +static pam_handle_t *pamh = NULL;
152 +static struct pam_conv conv = {
157 +#define PAM_BAIL_P if (retval) { \
158 + pam_end(pamh, PAM_SUCCESS); \
161 +#define PAM_BAIL_P_VOID if (retval) { \
162 + pam_end(pamh, PAM_SUCCESS); \
167 /* Ask the user for a password.
168 + If PAM is in use, let PAM ask for the password if necessary.
169 Return true if the user gives the correct password for entry PW,
170 false if not. Return true without asking for a password if run by UID 0
171 or if PW has an empty password. */
172 @@ -217,6 +262,44 @@ log_su (struct passwd const *pw, bool su
174 correct_password (const struct passwd *pw)
177 + struct passwd *caller;
178 + char *tty_name, *ttyn;
179 + retval = pam_start(PROGRAM_NAME, pw->pw_name, &conv, &pamh);
182 + if (getuid() != 0 && !isatty(0)) {
183 + fprintf(stderr, "standard in must be a tty\n");
187 + caller = getpwuid(getuid());
188 + if(caller != NULL && caller->pw_name != NULL) {
189 + retval = pam_set_item(pamh, PAM_RUSER, caller->pw_name);
195 + if (strncmp(ttyn, "/dev/", 5) == 0)
199 + retval = pam_set_item(pamh, PAM_TTY, tty_name);
202 + retval = pam_authenticate(pamh, 0);
204 + retval = pam_acct_mgmt(pamh, 0);
205 + if (retval == PAM_NEW_AUTHTOK_REQD) {
206 + /* password has expired. Offer option to change it. */
207 + retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
211 + /* must be authenticated if this point was reached */
213 +#else /* !USE_PAM */
214 char *unencrypted, *encrypted, *correct;
215 #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
216 /* Shadow passwd stuff for SVR3 and maybe other systems. */
217 @@ -241,6 +324,7 @@ correct_password (const struct passwd *p
218 encrypted = crypt (unencrypted, correct);
219 memset (unencrypted, 0, strlen (unencrypted));
220 return STREQ (encrypted, correct);
221 +#endif /* !USE_PAM */
224 /* Update `environ' for the new shell based on PW, with SHELL being
225 @@ -254,12 +338,18 @@ modify_environment (const struct passwd
226 /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
227 Unset all other environment variables. */
228 char const *term = getenv ("TERM");
229 + char const *display = getenv ("DISPLAY");
230 + char const *xauthority = getenv ("XAUTHORITY");
232 term = xstrdup (term);
233 environ = xmalloc ((6 + !!term) * sizeof (char *));
236 xsetenv ("TERM", term);
238 + xsetenv ("DISPLAY", display);
240 + xsetenv ("XAUTHORITY", xauthority);
241 xsetenv ("HOME", pw->pw_dir);
242 xsetenv ("SHELL", shell);
243 xsetenv ("USER", pw->pw_name);
244 @@ -292,8 +382,13 @@ change_identity (const struct passwd *pw
246 #ifdef HAVE_INITGROUPS
248 - if (initgroups (pw->pw_name, pw->pw_gid) == -1)
249 + if (initgroups (pw->pw_name, pw->pw_gid) == -1) {
251 + pam_close_session(pamh, 0);
252 + pam_end(pamh, PAM_ABORT);
254 error (EXIT_CANCELED, errno, _("cannot set groups"));
258 if (setgid (pw->pw_gid))
259 @@ -302,6 +397,31 @@ change_identity (const struct passwd *pw
260 error (EXIT_CANCELED, errno, _("cannot set user id"));
264 +static int caught=0;
265 +/* Signal handler for parent process later */
266 +static void su_catch_sig(int sig)
272 +pam_copyenv (pam_handle_t *pamh)
276 + env = pam_getenvlist(pamh);
288 /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
289 If COMMAND is nonzero, pass it to the shell with the -c option.
290 Pass ADDITIONAL_ARGS to the shell as more arguments; there
291 @@ -309,17 +429,49 @@ change_identity (const struct passwd *pw
294 run_shell (char const *shell, char const *command, char **additional_args,
295 - size_t n_additional_args)
296 + size_t n_additional_args, const struct passwd *pw)
298 size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
299 char const **args = xnmalloc (n_args, sizeof *args);
306 + retval = pam_open_session(pamh,0);
307 + if (retval != PAM_SUCCESS) {
308 + fprintf (stderr, "could not open session\n");
312 +/* do this at the last possible moment, because environment variables may
313 + be passed even in the session phase
315 + if(pam_copyenv(pamh) != PAM_SUCCESS)
316 + fprintf (stderr, "error copying PAM environment\n");
318 + /* Credentials should be set in the parent */
319 + if (pam_setcred(pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS) {
320 + pam_close_session(pamh, 0);
321 + fprintf(stderr, "could not set PAM credentials\n");
326 + if (child == 0) { /* child shell */
327 + change_identity (pw);
334 char *shell_basename;
336 + if(chdir(pw->pw_dir))
337 + error(0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
339 shell_basename = last_component (shell);
340 arg0 = xmalloc (strlen (shell_basename) + 2);
342 @@ -344,6 +496,67 @@ run_shell (char const *shell, char const
343 error (0, errno, "%s", shell);
347 + } else if (child == -1) {
348 + fprintf(stderr, "can not fork user shell: %s", strerror(errno));
349 + pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT);
350 + pam_close_session(pamh, 0);
351 + pam_end(pamh, PAM_ABORT);
355 + sigfillset(&ourset);
356 + if (sigprocmask(SIG_BLOCK, &ourset, NULL)) {
357 + fprintf(stderr, "%s: signal malfunction\n", PROGRAM_NAME);
361 + struct sigaction action;
362 + action.sa_handler = su_catch_sig;
363 + sigemptyset(&action.sa_mask);
364 + action.sa_flags = 0;
365 + sigemptyset(&ourset);
366 + if (sigaddset(&ourset, SIGTERM)
367 + || sigaddset(&ourset, SIGALRM)
368 + || sigaction(SIGTERM, &action, NULL)
369 + || sigprocmask(SIG_UNBLOCK, &ourset, NULL)) {
370 + fprintf(stderr, "%s: signal masking malfunction\n", PROGRAM_NAME);
378 + pid = waitpid(-1, &status, WUNTRACED);
380 + if (((pid_t)-1 != pid) && (0 != WIFSTOPPED (status))) {
381 + kill(getpid(), WSTOPSIG(status));
382 + /* once we get here, we must have resumed */
383 + kill(pid, SIGCONT);
385 + } while (0 != WIFSTOPPED(status));
389 + fprintf(stderr, "\nSession terminated, killing shell...");
390 + kill (child, SIGTERM);
392 + /* Not checking retval on this because we need to call close session */
393 + pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT);
394 + retval = pam_close_session(pamh, 0);
396 + retval = pam_end(pamh, PAM_SUCCESS);
400 + kill(child, SIGKILL);
401 + fprintf(stderr, " ...killed.\n");
404 + exit ((0 != WIFEXITED (status)) ? WEXITSTATUS (status)
405 + : WTERMSIG (status) + 128);
406 +#endif /* USE_PAM */
409 /* Return true if SHELL is a restricted shell (one not returned by
410 @@ -511,9 +724,9 @@ main (int argc, char **argv)
411 shell = xstrdup (shell ? shell : pw->pw_shell);
412 modify_environment (pw, shell);
415 change_identity (pw);
416 - if (simulate_login && chdir (pw->pw_dir) != 0)
417 - error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
420 /* error() flushes stderr, but does not check for write failure.
421 Normally, we would catch this via our atexit() hook of
422 @@ -523,5 +736,5 @@ main (int argc, char **argv)
424 exit (EXIT_CANCELED);
426 - run_shell (shell, command, argv + optind, MAX (0, argc - optind));
427 + run_shell (shell, command, argv + optind, MAX (0, argc - optind), pw);