]> git.ipfire.org Git - people/arne_f/ipfire-3.x.git/blame - coreutils/patches/coreutils-8.5-pam.patch
coreutils: Update to 8.16.
[people/arne_f/ipfire-3.x.git] / coreutils / patches / coreutils-8.5-pam.patch
CommitLineData
6987acf5
MT
1diff -urNp coreutils-8.16-orig/configure.ac coreutils-8.16/configure.ac
2--- coreutils-8.16-orig/configure.ac 2012-03-24 19:22:13.000000000 +0100
3+++ coreutils-8.16/configure.ac 2012-03-26 17:59:07.900139497 +0200
4@@ -185,6 +185,20 @@ fi
1555d43c
SS
5
6 AC_FUNC_FORK
7
8+AC_ARG_ENABLE(pam, AS_HELP_STRING([--disable-pam],
9+ [Disable PAM support in su (default=auto)]), , [enable_pam=yes])
10+if test "x$enable_pam" != xno; then
11+ AC_CHECK_LIB([pam], [pam_start], [enable_pam=yes], [enable_pam=no])
12+ AC_CHECK_LIB([pam_misc], [misc_conv], [:], [enable_pam=no])
13+ if test "x$enable_pam" != xno; then
14+ AC_DEFINE(USE_PAM, 1, [Define if you want to use PAM])
15+ PAM_LIBS="-lpam -lpam_misc"
16+ AC_SUBST(PAM_LIBS)
17+ fi
18+fi
19+AC_MSG_CHECKING([whether to enable PAM support in su])
20+AC_MSG_RESULT([$enable_pam])
21+
22 optional_bin_progs=
23 AC_CHECK_FUNCS([chroot],
24 gl_ADD_PROG([optional_bin_progs], [chroot]))
6987acf5
MT
25diff -urNp coreutils-8.16-orig/doc/coreutils.texi coreutils-8.16/doc/coreutils.texi
26--- coreutils-8.16-orig/doc/coreutils.texi 2012-03-26 17:58:27.624763998 +0200
27+++ coreutils-8.16/doc/coreutils.texi 2012-03-26 17:59:07.907138599 +0200
28@@ -15804,7 +15804,9 @@ the exit status of @var{command} otherwi
29
30 @command{su} allows one user to temporarily become another user. It runs a
31 command (often an interactive shell) with the real and effective user
32-ID, group ID, and supplemental groups of a given @var{user}. Synopsis:
33+ID, group ID, and supplemental groups of a given @var{user}. When the -l
34+option is given, the su-l PAM file is used instead of the default su PAM file.
35+Synopsis:
36
37 @example
38 su [@var{option}]@dots{} [@var{user} [@var{arg}]@dots{}]
39@@ -15883,7 +15885,8 @@ environment variables except @env{TERM},
40 (which are set, even for the super-user, as described above), and set
41 @env{PATH} to a compiled-in default value. Change to @var{user}'s home
42 directory. Prepend @samp{-} to the shell's name, intended to make it
43-read its login startup file(s).
44+read its login startup file(s). When this option is given, /etc/pam.d/su-l
45+PAM file is used instead of the default one.
46
47 @item -m
48 @itemx -p
49diff -urNp coreutils-8.16-orig/src/Makefile.am coreutils-8.16/src/Makefile.am
50--- coreutils-8.16-orig/src/Makefile.am 2012-03-24 19:22:13.000000000 +0100
51+++ coreutils-8.16/src/Makefile.am 2012-03-26 17:59:07.928142551 +0200
52@@ -357,8 +357,8 @@ factor_LDADD += $(LIB_GMP)
1555d43c
SS
53 # for getloadavg
54 uptime_LDADD += $(GETLOADAVG_LIBS)
55
56-# for crypt
57-su_LDADD += $(LIB_CRYPT)
58+# for crypt and pam
59+su_LDADD += $(LIB_CRYPT) $(PAM_LIBS)
60
61 # for various ACL functions
62 copy_LDADD += $(LIB_ACL)
6987acf5
MT
63diff -urNp coreutils-8.16-orig/src/su.c coreutils-8.16/src/su.c
64--- coreutils-8.16-orig/src/su.c 2012-03-26 17:58:27.629764055 +0200
65+++ coreutils-8.16/src/su.c 2012-03-26 17:59:07.931138998 +0200
1555d43c
SS
66@@ -37,6 +37,16 @@
67 restricts who can su to UID 0 accounts. RMS considers that to
68 be fascist.
69
70+#ifdef USE_PAM
71+
72+ Actually, with PAM, su has nothing to do with whether or not a
73+ wheel group is enforced by su. RMS tries to restrict your access
74+ to a su which implements the wheel group, but PAM considers that
75+ to be fascist, and gives the user/sysadmin the opportunity to
76+ enforce a wheel group by proper editing of /etc/pam.d/su
77+
78+#endif
79+
80 Compile-time options:
81 -DSYSLOG_SUCCESS Log successful su's (by default, to root) with syslog.
82 -DSYSLOG_FAILURE Log failed su's (by default, to root) with syslog.
83@@ -52,6 +62,13 @@
84 #include <sys/types.h>
85 #include <pwd.h>
86 #include <grp.h>
87+#ifdef USE_PAM
88+#include <security/pam_appl.h>
89+#include <security/pam_misc.h>
90+#include <signal.h>
91+#include <sys/wait.h>
92+#include <sys/fsuid.h>
93+#endif
94
95 #include "system.h"
96 #include "getpass.h"
6987acf5 97@@ -120,7 +137,9 @@
1555d43c
SS
98 /* The user to become if none is specified. */
99 #define DEFAULT_USER "root"
100
101+#ifndef USE_PAM
102 char *crypt (char const *key, char const *salt);
103+#endif
104
105 static void run_shell (char const *, char const *, char **, size_t)
106 ATTRIBUTE_NORETURN;
6987acf5 107@@ -134,6 +153,11 @@ static bool simulate_login;
1555d43c
SS
108 /* If true, change some environment vars to indicate the user su'd to. */
109 static bool change_environment;
110
111+#ifdef USE_PAM
112+static bool _pam_session_opened;
113+static bool _pam_cred_established;
114+#endif
115+
116 static struct option const longopts[] =
117 {
118 {"command", required_argument, NULL, 'c'},
6987acf5 119@@ -212,7 +236,174 @@ log_su (struct passwd const *pw, bool su
1555d43c
SS
120 }
121 #endif
122
123+#ifdef USE_PAM
124+#define PAM_SERVICE_NAME PROGRAM_NAME
125+#define PAM_SERVICE_NAME_L PROGRAM_NAME "-l"
126+static sig_atomic_t volatile caught_signal = false;
127+static pam_handle_t *pamh = NULL;
128+static int retval;
129+static struct pam_conv conv =
130+{
131+ misc_conv,
132+ NULL
133+};
134+
135+#define PAM_BAIL_P(a) \
136+ if (retval) \
137+ { \
138+ pam_end (pamh, retval); \
139+ a; \
140+ }
141+
142+static void
143+cleanup_pam (int retcode)
144+{
145+ if (_pam_session_opened)
146+ pam_close_session (pamh, 0);
147+
148+ if (_pam_cred_established)
149+ pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT);
150+
151+ pam_end(pamh, retcode);
152+}
153+
154+/* Signal handler for parent process. */
155+static void
156+su_catch_sig (int sig)
157+{
158+ caught_signal = true;
159+}
160+
161+/* Export env variables declared by PAM modules. */
162+static void
163+export_pamenv (void)
164+{
165+ char **env;
166+
167+ /* This is a copy but don't care to free as we exec later anyways. */
168+ env = pam_getenvlist (pamh);
169+ while (env && *env)
170+ {
171+ if (putenv (*env) != 0)
172+ xalloc_die ();
173+ env++;
174+ }
175+}
176+
177+static void
178+create_watching_parent (void)
179+{
180+ pid_t child;
6987acf5 181+ sigset_t ourset, blockset;
1555d43c
SS
182+ int status = 0;
183+
184+ retval = pam_open_session (pamh, 0);
185+ if (retval != PAM_SUCCESS)
186+ {
187+ cleanup_pam (retval);
188+ error (EXIT_FAILURE, 0, _("cannot not open session: %s"),
189+ pam_strerror (pamh, retval));
190+ }
191+ else
192+ _pam_session_opened = 1;
193+
194+ child = fork ();
195+ if (child == (pid_t) -1)
196+ {
197+ cleanup_pam (PAM_ABORT);
198+ error (EXIT_FAILURE, errno, _("cannot create child process"));
199+ }
200+
201+ /* the child proceeds to run the shell */
202+ if (child == 0)
203+ return;
204+
205+ /* In the parent watch the child. */
206+
207+ /* su without pam support does not have a helper that keeps
208+ sitting on any directory so let's go to /. */
209+ if (chdir ("/") != 0)
210+ error (0, errno, _("warning: cannot change directory to %s"), "/");
211+
212+ sigfillset (&ourset);
213+ if (sigprocmask (SIG_BLOCK, &ourset, NULL))
214+ {
215+ error (0, errno, _("cannot block signals"));
216+ caught_signal = true;
217+ }
218+ if (!caught_signal)
219+ {
220+ struct sigaction action;
221+ action.sa_handler = su_catch_sig;
222+ sigemptyset (&action.sa_mask);
223+ action.sa_flags = 0;
224+ sigemptyset (&ourset);
225+ if (sigaddset (&ourset, SIGTERM)
226+ || sigaddset (&ourset, SIGALRM)
227+ || sigaction (SIGTERM, &action, NULL)
228+ || sigprocmask (SIG_UNBLOCK, &ourset, NULL))
229+ {
230+ error (0, errno, _("cannot set signal handler"));
231+ caught_signal = true;
232+ }
233+ }
234+ if (!caught_signal)
235+ {
236+ pid_t pid;
237+ for (;;)
238+ {
239+ pid = waitpid (child, &status, WUNTRACED);
240+
241+ if (pid != (pid_t)-1 && WIFSTOPPED (status))
242+ {
6987acf5 243+ /* tcsh sends SIGTSTP to the process group, and so is already pending */
1555d43c 244+ kill (getpid (), SIGSTOP);
6987acf5
MT
245+ if (WSTOPSIG(status) != SIGSTOP) {
246+ sigemptyset(&blockset);
247+ if (sigaddset(&blockset, WSTOPSIG(status)) ||
248+ sigprocmask(SIG_UNBLOCK, &blockset, &ourset) ||
249+ sigprocmask(SIG_SETMASK, &ourset, NULL))
250+ {
251+ error (0, errno, _("cannot set signal handler"));
252+ }
253+ }
1555d43c
SS
254+ /* once we get here, we must have resumed */
255+ kill (pid, SIGCONT);
256+ }
257+ else
258+ break;
259+ }
260+ if (pid != (pid_t)-1)
261+ if (WIFSIGNALED (status))
262+ status = WTERMSIG (status) + 128;
263+ else
264+ status = WEXITSTATUS (status);
265+ else
266+ status = 1;
267+ }
268+ else
269+ status = 1;
270+
271+ if (caught_signal)
272+ {
273+ fprintf (stderr, _("\nSession terminated, killing shell..."));
274+ kill (child, SIGTERM);
275+ }
276+
277+ cleanup_pam (PAM_SUCCESS);
278+
279+ if (caught_signal)
280+ {
281+ sleep (2);
282+ kill (child, SIGKILL);
283+ fprintf (stderr, _(" ...killed.\n"));
284+ }
285+ exit (status);
286+}
287+#endif
288+
289 /* Ask the user for a password.
290+ If PAM is in use, let PAM ask for the password if necessary.
291 Return true if the user gives the correct password for entry PW,
292 false if not. Return true without asking for a password if run by UID 0
293 or if PW has an empty password. */
6987acf5 294@@ -220,10 +411,52 @@ log_su (struct passwd const *pw, bool su
1555d43c
SS
295 static bool
296 correct_password (const struct passwd *pw)
297 {
298+#ifdef USE_PAM
299+ const struct passwd *lpw;
300+ const char *cp;
301+
302+ retval = pam_start (simulate_login ? PAM_SERVICE_NAME_L : PAM_SERVICE_NAME,
303+ pw->pw_name, &conv, &pamh);
304+ PAM_BAIL_P (return false);
305+
306+ if (isatty (0) && (cp = ttyname (0)) != NULL)
307+ {
308+ const char *tty;
309+
310+ if (strncmp (cp, "/dev/", 5) == 0)
311+ tty = cp + 5;
312+ else
313+ tty = cp;
314+ retval = pam_set_item (pamh, PAM_TTY, tty);
315+ PAM_BAIL_P (return false);
316+ }
317+#if 0 /* Manpage discourages use of getlogin. */
318+ cp = getlogin ();
319+ if (!(cp && *cp && (lpw = getpwnam (cp)) != NULL && lpw->pw_uid == getuid ()))
320+#endif
321+ lpw = getpwuid (getuid ());
322+ if (lpw && lpw->pw_name)
323+ {
324+ retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
325+ PAM_BAIL_P (return false);
326+ }
327+ retval = pam_authenticate (pamh, 0);
328+ PAM_BAIL_P (return false);
329+ retval = pam_acct_mgmt (pamh, 0);
330+ if (retval == PAM_NEW_AUTHTOK_REQD)
331+ {
332+ /* Password has expired. Offer option to change it. */
333+ retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
334+ PAM_BAIL_P (return false);
335+ }
336+ PAM_BAIL_P (return false);
337+ /* Must be authenticated if this point was reached. */
338+ return true;
339+#else /* !USE_PAM */
340 char *unencrypted, *encrypted, *correct;
341 #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
342 /* Shadow passwd stuff for SVR3 and maybe other systems. */
343- struct spwd *sp = getspnam (pw->pw_name);
344+ const struct spwd *sp = getspnam (pw->pw_name);
345
346 endspent ();
347 if (sp)
6987acf5 348@@ -244,6 +477,7 @@ correct_password (const struct passwd *p
1555d43c
SS
349 encrypted = crypt (unencrypted, correct);
350 memset (unencrypted, 0, strlen (unencrypted));
351 return STREQ (encrypted, correct);
352+#endif /* !USE_PAM */
353 }
354
6987acf5
MT
355 /* Update 'environ' for the new shell based on PW, with SHELL being
356@@ -286,19 +520,41 @@ modify_environment (const struct passwd
1555d43c
SS
357 }
358 }
359 }
360+
361+#ifdef USE_PAM
362+ export_pamenv ();
363+#endif
364 }
365
366 /* Become the user and group(s) specified by PW. */
367
368 static void
369-change_identity (const struct passwd *pw)
370+init_groups (const struct passwd *pw)
371 {
372 #ifdef HAVE_INITGROUPS
373 errno = 0;
374 if (initgroups (pw->pw_name, pw->pw_gid) == -1)
375- error (EXIT_CANCELED, errno, _("cannot set groups"));
376+ {
377+#ifdef USE_PAM
378+ cleanup_pam (PAM_ABORT);
379+#endif
380+ error (EXIT_FAILURE, errno, _("cannot set groups"));
381+ }
382 endgrent ();
383 #endif
384+
385+#ifdef USE_PAM
386+ retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
387+ if (retval != PAM_SUCCESS)
388+ error (EXIT_FAILURE, 0, "%s", pam_strerror (pamh, retval));
389+ else
390+ _pam_cred_established = 1;
391+#endif
392+}
393+
394+static void
395+change_identity (const struct passwd *pw)
396+{
397 if (setgid (pw->pw_gid))
398 error (EXIT_CANCELED, errno, _("cannot set group id"));
399 if (setuid (pw->pw_uid))
6987acf5 400@@ -511,9 +767,21 @@ main (int argc, char **argv)
1555d43c
SS
401 shell = NULL;
402 }
403 shell = xstrdup (shell ? shell : pw->pw_shell);
404- modify_environment (pw, shell);
405+
406+ init_groups (pw);
407+
408+#ifdef USE_PAM
409+ create_watching_parent ();
410+ /* Now we're in the child. */
411+#endif
412
413 change_identity (pw);
414+
415+ /* Set environment after pam_open_session, which may put KRB5CCNAME
416+ into the pam_env, etc. */
417+
418+ modify_environment (pw, shell);
419+
420 if (simulate_login && chdir (pw->pw_dir) != 0)
421 error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
422