2 * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
5 * Portions of this software were developed for the FreeBSD Project by
6 * ThinkSec AS and NAI Labs, the Security Research Division of Network
7 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
8 * ("CBOSS"), as part of the DARPA CHATS research program.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * Copyright (c) 1988, 1993, 1994
33 * The Regents of the University of California. All rights reserved.
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 * must display the following acknowledgement:
45 * This product includes software developed by the University of
46 * California, Berkeley and its contributors.
47 * 4. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 static const char copyright
[] =
66 "@(#) Copyright (c) 1988, 1993, 1994\n\
67 The Regents of the University of California. All rights reserved.\n";
72 static char sccsid
[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";
76 #include <sys/cdefs.h>
77 __FBSDID("$FreeBSD: src/usr.bin/su/su.c,v 1.87 2007/10/18 11:05:30 davidxu Exp $");
79 #include <sys/param.h>
81 #include <sys/resource.h>
85 #include <bsm/libbsm.h>
86 #include <bsm/audit_uevents.h>
93 #include <login_cap.h>
94 #endif /* !__APPLE__ */
105 #include <security/pam_appl.h>
106 #include <security/openpam.h>
108 #define PAM_END() do { \
110 if (pamh != NULL) { \
111 local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
112 if (local_ret != PAM_SUCCESS) \
113 syslog(LOG_ERR, "pam_setcred: %s", \
114 pam_strerror(pamh, local_ret)); \
116 local_ret = pam_close_session(pamh, 0); \
117 if (local_ret != PAM_SUCCESS) \
118 syslog(LOG_ERR, "pam_close_session: %s",\
119 pam_strerror(pamh, local_ret)); \
121 local_ret = pam_end(pamh, local_ret); \
122 if (local_ret != PAM_SUCCESS) \
123 syslog(LOG_ERR, "pam_end: %s", \
124 pam_strerror(pamh, local_ret)); \
129 #define PAM_SET_ITEM(what, item) do { \
131 local_ret = pam_set_item(pamh, what, item); \
132 if (local_ret != PAM_SUCCESS) { \
133 syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
134 pam_strerror(pamh, local_ret)); \
135 errx(1, "pam_set_item(" #what "): %s", \
136 pam_strerror(pamh, local_ret)); \
141 enum tristate
{ UNSET
, YES
, NO
};
143 static pam_handle_t
*pamh
= NULL
;
144 static char **environ_pam
;
146 static char *ontty(void);
147 static int chshell(const char *);
148 static void usage(void) __dead2
;
149 static void export_pam_environment(void);
150 static int ok_to_export(const char *);
152 extern char **environ
;
155 main(int argc
, char *argv
[])
157 static char *cleanenv
;
159 struct pam_conv conv
= { openpam_ttyconv
, NULL
};
163 #endif /* !__APPLE__ */
169 pid_t child_pid
, child_pgrp
, pid
;
170 int asme
, ch
, asthem
, fastlogin
, prio
, i
, retcode
,
171 statusp
, setmaclabel
;
174 #endif /* !__APPLE__ */
175 char *username
, *class, shellbuf
[MAXPATHLEN
];
176 const char *p
, *user
, *shell
, *mytty
, **nargv
;
177 struct sigaction sa
, sa_int
, sa_quit
, sa_pipe
;
186 char avshellbuf
[MAXPATHLEN
];
187 #endif /* __APPLE__ */
189 shell
= class = cleanenv
= NULL
;
190 asme
= asthem
= fastlogin
= statusp
= 0;
196 while ((ch
= getopt(argc
, argv
, "-flm")) != -1)
198 while ((ch
= getopt(argc
, argv
, "-flmsc:")) != -1)
199 #endif /* __APPLE__ */
220 #endif /* !__APPLE__ */
228 user
= argv
[optind
++];
235 * Try to provide more helpful debugging output if su(1) is running
236 * non-setuid, or was run from a file system not mounted setuid.
239 errx(1, "not running setuid");
242 if (getauid(&auid
) < 0 && errno
!= ENOSYS
) {
243 syslog(LOG_AUTH
| LOG_ERR
, "getauid: %s", strerror(errno
));
244 errx(1, "Permission denied");
247 if (strlen(user
) > MAXLOGNAME
- 1) {
249 if (audit_submit(AUE_su
, auid
,
250 1, EPERM
, "username too long: '%s'", user
))
251 errx(1, "Permission denied");
253 errx(1, "username too long");
256 nargv
= malloc(sizeof(char *) * (size_t)(argc
+ 4));
258 errx(1, "malloc failure");
260 nargv
[argc
+ 3] = NULL
;
261 for (i
= argc
; i
>= optind
; i
--)
262 nargv
[i
+ 3] = argv
[i
];
263 np
.a
= &nargv
[i
+ 3];
268 prio
= getpriority(PRIO_PROCESS
, 0);
272 setpriority(PRIO_PROCESS
, 0, -2);
273 openlog("su", LOG_CONS
, LOG_AUTH
);
275 /* get current login name, real uid and shell */
277 username
= getlogin();
278 pwd
= getpwnam(username
);
279 if (username
== NULL
|| pwd
== NULL
|| pwd
->pw_uid
!= ruid
)
280 pwd
= getpwuid(ruid
);
283 if (audit_submit(AUE_su
, auid
, 1, EPERM
,
284 "unable to determine invoking subject: '%s'", username
))
285 errx(1, "Permission denied");
287 errx(1, "who are you?");
290 username
= strdup(pwd
->pw_name
);
291 if (username
== NULL
)
292 err(1, "strdup failure");
295 if (pwd
->pw_shell
!= NULL
&& *pwd
->pw_shell
!= '\0') {
296 /* must copy - pwd memory is recycled */
297 shell
= strncpy(shellbuf
, pwd
->pw_shell
,
299 shellbuf
[sizeof(shellbuf
) - 1] = '\0';
302 shell
= _PATH_BSHELL
;
307 /* Do the whole PAM startup thing */
308 retcode
= pam_start("su", user
, &conv
, &pamh
);
309 if (retcode
!= PAM_SUCCESS
) {
310 syslog(LOG_ERR
, "pam_start: %s", pam_strerror(pamh
, retcode
));
311 errx(1, "pam_start: %s", pam_strerror(pamh
, retcode
));
314 PAM_SET_ITEM(PAM_RUSER
, username
);
316 mytty
= ttyname(STDERR_FILENO
);
319 PAM_SET_ITEM(PAM_TTY
, mytty
);
321 retcode
= pam_authenticate(pamh
, 0);
322 if (retcode
!= PAM_SUCCESS
) {
324 if (audit_submit(AUE_su
, auid
, 1, EPERM
, "bad su %s to %s on %s",
325 username
, user
, mytty
))
326 errx(1, "Permission denied");
328 syslog(LOG_AUTH
|LOG_WARNING
, "BAD SU %s to %s on %s",
329 username
, user
, mytty
);
333 if (audit_submit(AUE_su
, auid
, 0, 0, "successful authentication"))
334 errx(1, "Permission denied");
336 retcode
= pam_get_item(pamh
, PAM_USER
, (const void **)&p
);
337 if (retcode
== PAM_SUCCESS
)
340 syslog(LOG_ERR
, "pam_get_item(PAM_USER): %s",
341 pam_strerror(pamh
, retcode
));
342 pwd
= getpwnam(user
);
345 if (audit_submit(AUE_su
, auid
, 1, EPERM
,
346 "unknown subject: %s", user
))
347 errx(1, "Permission denied");
349 errx(1, "unknown login: %s", user
);
352 retcode
= pam_acct_mgmt(pamh
, 0);
353 if (retcode
== PAM_NEW_AUTHTOK_REQD
) {
354 retcode
= pam_chauthtok(pamh
,
355 PAM_CHANGE_EXPIRED_AUTHTOK
);
356 if (retcode
!= PAM_SUCCESS
) {
358 aerr
= pam_strerror(pamh
, retcode
);
360 aerr
= "Unknown PAM error";
361 if (audit_submit(AUE_su
, auid
, 1, EPERM
,
362 "pam_chauthtok: %s", aerr
))
363 errx(1, "Permission denied");
365 syslog(LOG_ERR
, "pam_chauthtok: %s",
366 pam_strerror(pamh
, retcode
));
370 if (retcode
!= PAM_SUCCESS
) {
372 if (audit_submit(AUE_su
, auid
, 1, EPERM
, "pam_acct_mgmt: %s",
373 pam_strerror(pamh
, retcode
)))
374 errx(1, "Permission denied");
376 syslog(LOG_ERR
, "pam_acct_mgmt: %s",
377 pam_strerror(pamh
, retcode
));
382 /* get target login information */
384 lc
= login_getpwclass(pwd
);
388 if (audit_submit(AUE_su
, auid
, 1, EPERM
,
389 "only root may use -c"))
390 errx(1, "Permission denied");
392 errx(1, "only root may use -c");
394 lc
= login_getclass(class);
396 errx(1, "unknown class: %s", class);
398 #endif /* !__APPLE__ */
400 /* if asme and non-standard target shell, must be root */
402 if (ruid
!= 0 && !chshell(pwd
->pw_shell
))
403 errx(1, "permission denied (shell)");
405 else if (pwd
->pw_shell
&& *pwd
->pw_shell
) {
408 shell
= strncpy(shellbuf
, pwd
->pw_shell
, sizeof(shellbuf
));
409 shellbuf
[sizeof(shellbuf
) - 1] = '\0';
411 shell
= pwd
->pw_shell
;
412 #endif /* __APPLE__ */
416 shell
= _PATH_BSHELL
;
420 /* if we're forking a csh, we want to slightly muck the args */
421 if (iscsh
== UNSET
) {
422 p
= strrchr(shell
, '/');
427 iscsh
= strcmp(p
, "csh") ? (strcmp(p
, "tcsh") ? NO
: YES
) : YES
;
429 setpriority(PRIO_PROCESS
, 0, prio
);
432 * PAM modules might add supplementary groups in pam_setcred(), so
433 * initialize them first.
436 if (initgroups(user
, pwd
->pw_gid
))
437 err(1, "initgroups");
439 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETGROUP
) < 0)
440 err(1, "setusercontext");
441 #endif /* __APPLE__ */
443 retcode
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
444 if (retcode
!= PAM_SUCCESS
) {
445 syslog(LOG_ERR
, "pam_setcred: %s",
446 pam_strerror(pamh
, retcode
));
447 errx(1, "failed to establish credentials.");
450 retcode
= pam_open_session(pamh
, 0);
451 if (retcode
!= PAM_SUCCESS
) {
452 syslog(LOG_ERR
, "pam_open_session: %s",
453 pam_strerror(pamh
, retcode
));
454 errx(1, "failed to open session.");
459 * We must fork() before setuid() because we need to call
460 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
462 sa
.sa_flags
= SA_RESTART
;
463 sa
.sa_handler
= SIG_IGN
;
464 sigemptyset(&sa
.sa_mask
);
465 sigaction(SIGINT
, &sa
, &sa_int
);
466 sigaction(SIGQUIT
, &sa
, &sa_quit
);
467 sigaction(SIGPIPE
, &sa
, &sa_pipe
);
468 sa
.sa_handler
= SIG_DFL
;
469 sigaction(SIGTSTP
, &sa
, NULL
);
471 if (pipe(fds
) == -1) {
478 sa
.sa_handler
= SIG_IGN
;
479 sigaction(SIGTTOU
, &sa
, NULL
);
481 setpgid(child_pid
, child_pid
);
482 if (tcgetpgrp(STDERR_FILENO
) == getpgrp())
483 tcsetpgrp(STDERR_FILENO
, child_pid
);
485 sigaction(SIGPIPE
, &sa_pipe
, NULL
);
486 while ((pid
= waitpid(child_pid
, &statusp
, WUNTRACED
)) != -1) {
487 if (WIFSTOPPED(statusp
)) {
488 child_pgrp
= getpgid(child_pid
);
489 if (tcgetpgrp(STDERR_FILENO
) == child_pgrp
)
490 tcsetpgrp(STDERR_FILENO
, getpgrp());
491 kill(getpid(), SIGSTOP
);
492 if (tcgetpgrp(STDERR_FILENO
) == getpgrp()) {
493 child_pgrp
= getpgid(child_pid
);
494 tcsetpgrp(STDERR_FILENO
, child_pgrp
);
496 kill(child_pid
, SIGCONT
);
502 tcsetpgrp(STDERR_FILENO
, getpgrp());
506 exit(WEXITSTATUS(statusp
));
512 read(fds
[0], &temp
, 1);
514 sigaction(SIGPIPE
, &sa_pipe
, NULL
);
515 sigaction(SIGINT
, &sa_int
, NULL
);
516 sigaction(SIGQUIT
, &sa_quit
, NULL
);
519 if (setgid(pwd
->pw_gid
))
521 /* Call initgroups(2) after setgid(2) to re-establish memberd */
522 if (initgroups(user
, pwd
->pw_gid
))
523 err(1, "initgroups");
524 if (setuid(pwd
->pw_uid
))
528 * Set all user context except for: Environmental variables
529 * Umask Login records (wtmp, etc) Path
531 setwhat
= LOGIN_SETALL
& ~(LOGIN_SETENV
| LOGIN_SETUMASK
|
532 LOGIN_SETLOGIN
| LOGIN_SETPATH
| LOGIN_SETGROUP
|
535 * If -s is present, also set the MAC label.
538 setwhat
|= LOGIN_SETMAC
;
540 * Don't touch resource/priority settings if -m has been used
541 * or -l and -c hasn't, and we're not su'ing to root.
543 if ((asme
|| (!asthem
&& class == NULL
)) && pwd
->pw_uid
)
544 setwhat
&= ~(LOGIN_SETPRIORITY
| LOGIN_SETRESOURCES
);
545 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, setwhat
) < 0)
546 err(1, "setusercontext");
547 #endif /* __APPLE__ */
555 if (asthem
|| pwd
->pw_uid
)
556 setenv("USER", pwd
->pw_name
, 1);
557 setenv("HOME", pwd
->pw_dir
, 1);
558 setenv("SHELL", shell
, 1);
561 #endif /* __APPLE__ */
565 * Add any environmental variables that the
566 * PAM modules may have set.
568 environ_pam
= pam_getenvlist(pamh
);
570 export_pam_environment();
573 /* 5276965: As documented, set $PATH. */
574 setenv("PATH", "/bin:/usr/bin", 1);
576 /* set the su'd user's environment & umask */
577 setusercontext(lc
, pwd
, pwd
->pw_uid
,
578 LOGIN_SETPATH
| LOGIN_SETUMASK
|
580 #endif /* __APPLE__ */
582 setenv("TERM", p
, 1);
584 p
= pam_getenv(pamh
, "HOME");
585 if (chdir(p
? p
: pwd
->pw_dir
) < 0)
586 errx(1, "no directory");
591 #endif /* !__APPLE__ */
601 if ((p
= strrchr(shell
, '/')) != NULL
)
608 strlcpy(avshellbuf
+1, avshell
, sizeof(avshellbuf
) - 1);
609 avshell
= avshellbuf
;
612 /* csh *no longer* strips the first character... */
615 /* csh strips the first character... */
616 *np
.a
= asthem
? "-su" : iscsh
== YES
? "_su" : "su";
617 #endif /* __APPLE__ */
620 syslog(LOG_NOTICE
, "%s to %s%s", username
, user
,
629 export_pam_environment(void)
634 for (pp
= environ_pam
; *pp
!= NULL
; pp
++) {
635 if (ok_to_export(*pp
)) {
636 p
= strchr(*pp
, '=');
638 setenv(*pp
, p
+ 1, 1);
645 * Sanity checks on PAM environmental variables:
646 * - Make sure there is an '=' in the string.
647 * - Make sure the string doesn't run on too long.
648 * - Do not export certain variables. This list was taken from the
649 * Solaris pam_putenv(3) man page.
650 * Note that if the user is chrooted, PAM may have a better idea than we
651 * do of where her home directory is.
654 ok_to_export(const char *s
)
656 static const char *noexport
[] = {
657 "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
663 if (strlen(s
) > 1024 || strchr(s
, '=') == NULL
)
665 if (strncmp(s
, "LD_", 3) == 0)
667 for (pp
= noexport
; *pp
!= NULL
; pp
++) {
669 if (s
[n
] == '=' && strncmp(s
, *pp
, n
) == 0)
680 fprintf(stderr
, "usage: su [-] [-flm] [login [args]]\n");
682 fprintf(stderr
, "usage: su [-] [-flms] [-c class] [login [args]]\n");
683 #endif /* __APPLE__ */
689 chshell(const char *sh
)
696 while ((cp
= getusershell()) != NULL
&& !r
)
697 r
= (strcmp(cp
, sh
) == 0);
706 static char buf
[MAXPATHLEN
+ 4];
709 p
= ttyname(STDERR_FILENO
);
711 snprintf(buf
, sizeof(buf
), " on %s", p
);