2 * Copyright (c) 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 static const char copyright
[] =
36 "@(#) Copyright (c) 1988, 1993, 1994\n\
37 The Regents of the University of California. All rights reserved.\n";
42 static char sccsid
[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";
44 static const char rcsid
[] =
45 "$FreeBSD: src/usr.bin/su/su.c,v 1.48 2002/01/24 16:20:17 des Exp $";
48 #include <sys/param.h>
50 #include <sys/resource.h>
65 #include <pam/pam_appl.h>
66 #include <pam/pam_misc.h>
68 #define PAM_END() do { \
70 if (pamh != NULL && creds_set) { \
71 local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
72 if (local_ret != PAM_SUCCESS) \
73 syslog(LOG_ERR, "pam_setcred: %s", \
74 pam_strerror(pamh, local_ret)); \
75 local_ret = pam_close_session(pamh, 0); \
76 local_ret = pam_end(pamh, local_ret); \
77 if (local_ret != PAM_SUCCESS) \
78 syslog(LOG_ERR, "pam_end: %s", \
79 pam_strerror(pamh, local_ret)); \
84 #define PAM_SET_ITEM(what, item) do { \
86 local_ret = pam_set_item(pamh, what, item); \
87 if (local_ret != PAM_SUCCESS) { \
88 syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
89 pam_strerror(pamh, local_ret)); \
90 errx(1, "pam_set_item(" #what "): %s", \
91 pam_strerror(pamh, local_ret)); \
95 enum tristate
{ UNSET
, YES
, NO
};
97 static pam_handle_t
*pamh
= NULL
;
98 static int creds_set
= 0;
99 static char **environ_pam
;
101 static char *ontty(void);
102 static int chshell(char *);
103 static void usage(void);
104 static int export_pam_environment(void);
105 static int ok_to_export(const char *);
107 extern char **environ
;
110 main(int argc
, char *argv
[])
112 static char *cleanenv
;
114 struct pam_conv conv
= {misc_conv
, NULL
};
122 int asme
, ch
, asthem
, fastlogin
, prio
, i
, setwhat
, retcode
,
123 statusp
, child_pid
, child_pgrp
, ret_pid
;
124 char *username
, *class, shellbuf
[MAXPATHLEN
];
125 const char *p
, *user
, *shell
, *mytty
, **nargv
;
127 char avshellbuf
[MAXPATHLEN
];
129 shell
= class = cleanenv
= NULL
;
130 asme
= asthem
= fastlogin
= statusp
= 0;
135 while ((ch
= getopt(argc
, argv
, "-flm")) != -1)
137 while ((ch
= getopt(argc
, argv
, "-flmc:")) != -1)
138 #endif /* __APPLE__ */
156 #endif /* !__APPLE__ */
163 user
= argv
[optind
++];
168 if (strlen(user
) > MAXLOGNAME
- 1)
169 errx(1, "username too long");
171 nargv
= malloc(sizeof(char *) * (argc
+ 4));
173 errx(1, "malloc failure");
175 nargv
[argc
+ 3] = NULL
;
176 for (i
= argc
; i
>= optind
; i
--)
177 nargv
[i
+ 3] = argv
[i
];
178 np
.a
= &nargv
[i
+ 3];
183 prio
= getpriority(PRIO_PROCESS
, 0);
187 setpriority(PRIO_PROCESS
, 0, -2);
188 openlog("su", LOG_CONS
, LOG_AUTH
);
190 /* get current login name, real uid and shell */
192 username
= getlogin();
193 pwd
= getpwnam(username
);
194 if (username
== NULL
|| pwd
== NULL
|| pwd
->pw_uid
!= ruid
)
195 pwd
= getpwuid(ruid
);
197 errx(1, "who are you?");
200 username
= strdup(pwd
->pw_name
);
201 if (username
== NULL
)
202 err(1, "strdup failure");
205 if (pwd
->pw_shell
!= NULL
&& *pwd
->pw_shell
!= '\0') {
206 /* must copy - pwd memory is recycled */
207 shell
= strncpy(shellbuf
, pwd
->pw_shell
,
209 shellbuf
[sizeof(shellbuf
) - 1] = '\0';
212 shell
= _PATH_BSHELL
;
217 /* Do the whole PAM startup thing */
218 retcode
= pam_start("su", user
, &conv
, &pamh
);
219 if (retcode
!= PAM_SUCCESS
) {
220 syslog(LOG_ERR
, "pam_start: %s", pam_strerror(pamh
, retcode
));
221 errx(1, "pam_start: %s", pam_strerror(pamh
, retcode
));
224 PAM_SET_ITEM(PAM_RUSER
, getlogin());
226 mytty
= ttyname(STDERR_FILENO
);
229 PAM_SET_ITEM(PAM_TTY
, mytty
);
231 retcode
= pam_authenticate(pamh
, 0);
232 if (retcode
!= PAM_SUCCESS
) {
233 syslog(LOG_ERR
, "pam_authenticate: %s",
234 pam_strerror(pamh
, retcode
));
237 retcode
= pam_get_item(pamh
, PAM_USER
, (const void **)&p
);
238 if (retcode
== PAM_SUCCESS
)
241 syslog(LOG_ERR
, "pam_get_item(PAM_USER): %s",
242 pam_strerror(pamh
, retcode
));
244 retcode
= pam_acct_mgmt(pamh
, 0);
245 if (retcode
== PAM_NEW_AUTHTOK_REQD
) {
246 retcode
= pam_chauthtok(pamh
,
247 PAM_CHANGE_EXPIRED_AUTHTOK
);
248 if (retcode
!= PAM_SUCCESS
) {
249 syslog(LOG_ERR
, "pam_chauthtok: %s",
250 pam_strerror(pamh
, retcode
));
254 if (retcode
!= PAM_SUCCESS
) {
255 syslog(LOG_ERR
, "pam_acct_mgmt: %s",
256 pam_strerror(pamh
, retcode
));
260 /* get target login information, default to root */
261 pwd
= getpwnam(user
);
263 errx(1, "unknown login: %s", user
);
265 /* if asme and non-standard target shell, must be root */
267 if (ruid
!= 0 && !chshell(pwd
->pw_shell
))
268 errx(1, "permission denied (shell).");
270 else if (pwd
->pw_shell
&& *pwd
->pw_shell
) {
271 shell
= strncpy(shellbuf
, pwd
->pw_shell
, sizeof(shellbuf
));
272 shellbuf
[sizeof(shellbuf
) - 1] = '\0';
276 shell
= _PATH_BSHELL
;
280 if ((p
= strrchr(shell
, '/')) != NULL
)
285 /* if we're forking a csh, we want to slightly muck the args */
286 if (iscsh
== UNSET
) {
287 iscsh
= strcmp(avshell
, "csh") ? (strcmp(avshell
, "tcsh") ? NO
: YES
) : YES
;
289 setpriority(PRIO_PROCESS
, 0, prio
);
292 * PAM modules might add supplementary groups in pam_setcred(), so
293 * initialize them first.
295 if( initgroups(user
, pwd
->pw_gid
) )
296 err(1, "initgroups failed");
298 retcode
= pam_open_session(pamh
, 0);
299 if( retcode
!= PAM_SUCCESS
) {
300 syslog(LOG_ERR
, "pam_open_session(pamh, 0): %s",
301 pam_strerror(pamh
, retcode
));
304 retcode
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
305 if (retcode
!= PAM_SUCCESS
)
306 syslog(LOG_ERR
, "pam_setcred(pamh, PAM_ESTABLISH_CRED): %s",
307 pam_strerror(pamh
, retcode
));
312 * We must fork() before setuid() because we need to call
313 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
320 while ((ret_pid
= waitpid(child_pid
, &statusp
, WUNTRACED
)) != -1) {
321 if (WIFSTOPPED(statusp
)) {
322 child_pgrp
= tcgetpgrp(1);
323 kill(getpid(), SIGSTOP
);
324 tcsetpgrp(1, child_pgrp
);
325 kill(child_pid
, SIGCONT
);
334 exit(WEXITSTATUS(statusp
));
340 if( setgid(pwd
->pw_gid
) )
342 /* Call initgroups(2) after setgid(2) to re-establish memberd */
343 if( initgroups(user
, pwd
->pw_gid
) )
344 err(1, "initgroups");
345 if( setuid(pwd
->pw_uid
) )
354 if (asthem
|| pwd
->pw_uid
)
355 setenv("USER", pwd
->pw_name
, 1);
356 setenv("HOME", pwd
->pw_dir
, 1);
357 setenv("SHELL", shell
, 1);
361 * Add any environmental variables that the
362 * PAM modules may have set.
364 environ_pam
= pam_getenvlist(pamh
);
366 export_pam_environment();
369 /* 5276965: As documented, set $PATH. */
370 setenv("PATH", "/bin:/usr/bin", 1);
372 /* set the su'd user's environment & umask */
373 setusercontext(lc
, pwd
, pwd
->pw_uid
,
374 LOGIN_SETPATH
| LOGIN_SETUMASK
|
378 setenv("TERM", p
, 1);
380 p
= pam_getenv(pamh
, "HOME");
381 if (chdir(p
? p
: pwd
->pw_dir
) < 0)
382 errx(1, "no directory");
395 strlcpy(avshellbuf
+1, avshell
, sizeof(avshellbuf
) - 1);
396 avshell
= avshellbuf
;
399 /* csh *no longer* strips the first character... */
403 syslog(LOG_NOTICE
, "%s to %s%s", username
, user
,
412 export_pam_environment(void)
416 for (pp
= environ_pam
; *pp
!= NULL
; pp
++) {
417 if (ok_to_export(*pp
))
425 * Sanity checks on PAM environmental variables:
426 * - Make sure there is an '=' in the string.
427 * - Make sure the string doesn't run on too long.
428 * - Do not export certain variables. This list was taken from the
429 * Solaris pam_putenv(3) man page.
432 ok_to_export(const char *s
)
434 static const char *noexport
[] = {
435 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
441 if (strlen(s
) > 1024 || strchr(s
, '=') == NULL
)
443 if (strncmp(s
, "LD_", 3) == 0)
445 for (pp
= noexport
; *pp
!= NULL
; pp
++) {
447 if (s
[n
] == '=' && strncmp(s
, *pp
, n
) == 0)
458 fprintf(stderr
, "usage: su [-] [-flm] [login [args]]\n");
460 fprintf(stderr
, "usage: su [-] [-flm] [-c class] [login [args]]\n");
461 #endif /* __APPLE__ */
476 } while (!r
&& cp
!= NULL
);
485 static char buf
[MAXPATHLEN
+ 4];
488 p
= ttyname(STDERR_FILENO
);
490 snprintf(buf
, sizeof(buf
), " on %s", p
);