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.91 2009/12/13 03:14:06 delphij 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>
109 #include <bsm/audit_session.h>
110 #endif /* __APPLE__ */
112 #define PAM_END() do { \
114 if (pamh != NULL) { \
115 local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
116 if (local_ret != PAM_SUCCESS) \
117 syslog(LOG_ERR, "pam_setcred: %s", \
118 pam_strerror(pamh, local_ret)); \
120 local_ret = pam_close_session(pamh, 0); \
121 if (local_ret != PAM_SUCCESS) \
122 syslog(LOG_ERR, "pam_close_session: %s",\
123 pam_strerror(pamh, local_ret)); \
125 local_ret = pam_end(pamh, local_ret); \
126 if (local_ret != PAM_SUCCESS) \
127 syslog(LOG_ERR, "pam_end: %s", \
128 pam_strerror(pamh, local_ret)); \
133 #define PAM_SET_ITEM(what, item) do { \
135 local_ret = pam_set_item(pamh, what, item); \
136 if (local_ret != PAM_SUCCESS) { \
137 syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
138 pam_strerror(pamh, local_ret)); \
139 errx(1, "pam_set_item(" #what "): %s", \
140 pam_strerror(pamh, local_ret)); \
145 enum tristate
{ UNSET
, YES
, NO
};
147 static pam_handle_t
*pamh
= NULL
;
148 static char **environ_pam
;
150 static char *ontty(void);
151 static int chshell(const char *);
152 static void usage(void) __dead2
;
153 static void export_pam_environment(void);
154 static int ok_to_export(const char *);
156 extern char **environ
;
159 main(int argc
, char *argv
[])
161 static char *cleanenv
;
163 struct pam_conv conv
= { openpam_ttyconv
, NULL
};
167 #endif /* !__APPLE__ */
173 pid_t child_pid
, child_pgrp
, pid
;
174 int asme
, ch
, asthem
, fastlogin
, prio
, i
, retcode
,
175 statusp
, setmaclabel
;
178 #endif /* !__APPLE__ */
179 char *username
, *class, shellbuf
[MAXPATHLEN
];
180 const char *p
, *user
, *shell
, *mytty
, **nargv
;
182 struct sigaction sa
, sa_int
, sa_quit
, sa_pipe
;
191 char avshellbuf
[MAXPATHLEN
];
192 #endif /* __APPLE__ */
194 shell
= class = cleanenv
= NULL
;
195 asme
= asthem
= fastlogin
= statusp
= 0;
201 while ((ch
= getopt(argc
, argv
, "-flm")) != -1)
203 while ((ch
= getopt(argc
, argv
, "-flmsc:")) != -1)
204 #endif /* __APPLE__ */
225 #endif /* !__APPLE__ */
233 user
= argv
[optind
++];
240 * Try to provide more helpful debugging output if su(1) is running
241 * non-setuid, or was run from a file system not mounted setuid.
244 errx(1, "not running setuid");
247 if (getauid(&auid
) < 0 && errno
!= ENOSYS
) {
248 syslog(LOG_AUTH
| LOG_ERR
, "getauid: %s", strerror(errno
));
249 errx(1, "Permission denied");
252 if (strlen(user
) > MAXLOGNAME
- 1) {
254 if (audit_submit(AUE_su
, auid
,
255 EPERM
, 1, "username too long: '%s'", user
))
256 errx(1, "Permission denied");
258 errx(1, "username too long");
261 nargv
= malloc(sizeof(char *) * (size_t)(argc
+ 4));
263 errx(1, "malloc failure");
265 nargv
[argc
+ 3] = NULL
;
266 for (i
= argc
; i
>= optind
; i
--)
267 nargv
[i
+ 3] = argv
[i
];
268 np
.a
= &nargv
[i
+ 3];
273 prio
= getpriority(PRIO_PROCESS
, 0);
277 setpriority(PRIO_PROCESS
, 0, -2);
278 openlog("su", LOG_CONS
, LOG_AUTH
);
280 /* get current login name, real uid and shell */
282 username
= getlogin();
283 pwd
= getpwnam(username
);
284 if (username
== NULL
|| pwd
== NULL
|| pwd
->pw_uid
!= ruid
)
285 pwd
= getpwuid(ruid
);
288 if (audit_submit(AUE_su
, auid
, EPERM
, 1,
289 "unable to determine invoking subject: '%s'", username
))
290 errx(1, "Permission denied");
292 errx(1, "who are you?");
295 username
= strdup(pwd
->pw_name
);
296 if (username
== NULL
)
297 err(1, "strdup failure");
300 if (pwd
->pw_shell
!= NULL
&& *pwd
->pw_shell
!= '\0') {
301 /* must copy - pwd memory is recycled */
302 shell
= strncpy(shellbuf
, pwd
->pw_shell
,
304 shellbuf
[sizeof(shellbuf
) - 1] = '\0';
307 shell
= _PATH_BSHELL
;
312 /* Do the whole PAM startup thing */
313 retcode
= pam_start("su", user
, &conv
, &pamh
);
314 if (retcode
!= PAM_SUCCESS
) {
315 syslog(LOG_ERR
, "pam_start: %s", pam_strerror(pamh
, retcode
));
316 errx(1, "pam_start: %s", pam_strerror(pamh
, retcode
));
319 PAM_SET_ITEM(PAM_RUSER
, username
);
321 mytty
= ttyname(STDERR_FILENO
);
324 PAM_SET_ITEM(PAM_TTY
, mytty
);
326 retcode
= pam_authenticate(pamh
, 0);
327 if (retcode
!= PAM_SUCCESS
) {
329 if (audit_submit(AUE_su
, auid
, EPERM
, 1, "bad su %s to %s on %s",
330 username
, user
, mytty
))
331 errx(1, "Permission denied");
333 syslog(LOG_AUTH
|LOG_WARNING
, "BAD SU %s to %s on %s",
334 username
, user
, mytty
);
338 if (audit_submit(AUE_su
, auid
, 0, 0, "successful authentication"))
339 errx(1, "Permission denied");
341 retcode
= pam_get_item(pamh
, PAM_USER
, &v
);
342 if (retcode
== PAM_SUCCESS
)
345 syslog(LOG_ERR
, "pam_get_item(PAM_USER): %s",
346 pam_strerror(pamh
, retcode
));
347 pwd
= getpwnam(user
);
350 if (audit_submit(AUE_su
, auid
, EPERM
, 1,
351 "unknown subject: %s", user
))
352 errx(1, "Permission denied");
354 errx(1, "unknown login: %s", user
);
357 retcode
= pam_acct_mgmt(pamh
, 0);
358 if (retcode
== PAM_NEW_AUTHTOK_REQD
) {
359 retcode
= pam_chauthtok(pamh
,
360 PAM_CHANGE_EXPIRED_AUTHTOK
);
361 if (retcode
!= PAM_SUCCESS
) {
363 aerr
= pam_strerror(pamh
, retcode
);
365 aerr
= "Unknown PAM error";
366 if (audit_submit(AUE_su
, auid
, EPERM
, 1,
367 "pam_chauthtok: %s", aerr
))
368 errx(1, "Permission denied");
370 syslog(LOG_ERR
, "pam_chauthtok: %s",
371 pam_strerror(pamh
, retcode
));
375 if (retcode
!= PAM_SUCCESS
) {
377 if (audit_submit(AUE_su
, auid
, EPERM
, 1, "pam_acct_mgmt: %s",
378 pam_strerror(pamh
, retcode
)))
379 errx(1, "Permission denied");
381 syslog(LOG_ERR
, "pam_acct_mgmt: %s",
382 pam_strerror(pamh
, retcode
));
387 /* get target login information */
389 lc
= login_getpwclass(pwd
);
393 if (audit_submit(AUE_su
, auid
, EPERM
, 1,
394 "only root may use -c"))
395 errx(1, "Permission denied");
397 errx(1, "only root may use -c");
399 lc
= login_getclass(class);
401 errx(1, "unknown class: %s", class);
403 #endif /* !__APPLE__ */
405 /* if asme and non-standard target shell, must be root */
407 if (ruid
!= 0 && !chshell(pwd
->pw_shell
))
408 errx(1, "permission denied (shell)");
410 else if (pwd
->pw_shell
&& *pwd
->pw_shell
) {
413 shell
= strncpy(shellbuf
, pwd
->pw_shell
, sizeof(shellbuf
));
414 shellbuf
[sizeof(shellbuf
) - 1] = '\0';
416 shell
= pwd
->pw_shell
;
417 #endif /* __APPLE__ */
421 shell
= _PATH_BSHELL
;
425 /* if we're forking a csh, we want to slightly muck the args */
426 if (iscsh
== UNSET
) {
427 p
= strrchr(shell
, '/');
432 iscsh
= strcmp(p
, "csh") ? (strcmp(p
, "tcsh") ? NO
: YES
) : YES
;
434 setpriority(PRIO_PROCESS
, 0, prio
);
437 * PAM modules might add supplementary groups in pam_setcred(), so
438 * initialize them first.
441 if (initgroups(user
, pwd
->pw_gid
))
442 err(1, "initgroups");
444 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETGROUP
) < 0)
445 err(1, "setusercontext");
446 #endif /* __APPLE__ */
451 auditinfo_addr_t auinfo
= {
452 .ai_termid
= { .at_type
= AU_IPv4
},
453 .ai_asid
= AU_ASSIGN_ASID
,
457 if (setaudit_addr(&auinfo
, sizeof(auinfo
)) == 0) {
459 snprintf(session
, sizeof(session
), "%x", auinfo
.ai_asid
);
460 setenv("SECURITYSESSIONID", session
, 1);
462 errx(1, "failed to create session.");
465 #endif /* __APPLE__ */
467 retcode
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
468 if (retcode
!= PAM_SUCCESS
) {
469 syslog(LOG_ERR
, "pam_setcred: %s",
470 pam_strerror(pamh
, retcode
));
471 errx(1, "failed to establish credentials.");
474 retcode
= pam_open_session(pamh
, 0);
475 if (retcode
!= PAM_SUCCESS
) {
476 syslog(LOG_ERR
, "pam_open_session: %s",
477 pam_strerror(pamh
, retcode
));
478 errx(1, "failed to open session.");
483 * We must fork() before setuid() because we need to call
484 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
486 sa
.sa_flags
= SA_RESTART
;
487 sa
.sa_handler
= SIG_IGN
;
488 sigemptyset(&sa
.sa_mask
);
489 sigaction(SIGINT
, &sa
, &sa_int
);
490 sigaction(SIGQUIT
, &sa
, &sa_quit
);
491 sigaction(SIGPIPE
, &sa
, &sa_pipe
);
492 sa
.sa_handler
= SIG_DFL
;
493 sigaction(SIGTSTP
, &sa
, NULL
);
495 if (pipe(fds
) == -1) {
502 sa
.sa_handler
= SIG_IGN
;
503 sigaction(SIGTTOU
, &sa
, NULL
);
505 setpgid(child_pid
, child_pid
);
506 if (tcgetpgrp(STDERR_FILENO
) == getpgrp())
507 tcsetpgrp(STDERR_FILENO
, child_pid
);
509 sigaction(SIGPIPE
, &sa_pipe
, NULL
);
510 while ((pid
= waitpid(child_pid
, &statusp
, WUNTRACED
)) != -1) {
511 if (WIFSTOPPED(statusp
)) {
512 child_pgrp
= getpgid(child_pid
);
513 if (tcgetpgrp(STDERR_FILENO
) == child_pgrp
)
514 tcsetpgrp(STDERR_FILENO
, getpgrp());
515 kill(getpid(), SIGSTOP
);
516 if (tcgetpgrp(STDERR_FILENO
) == getpgrp()) {
517 child_pgrp
= getpgid(child_pid
);
518 tcsetpgrp(STDERR_FILENO
, child_pgrp
);
520 kill(child_pid
, SIGCONT
);
526 tcsetpgrp(STDERR_FILENO
, getpgrp());
530 exit(WEXITSTATUS(statusp
));
536 read(fds
[0], &temp
, 1);
538 sigaction(SIGPIPE
, &sa_pipe
, NULL
);
539 sigaction(SIGINT
, &sa_int
, NULL
);
540 sigaction(SIGQUIT
, &sa_quit
, NULL
);
543 if (setgid(pwd
->pw_gid
))
545 /* Call initgroups(2) after setgid(2) to re-establish memberd */
546 if (initgroups(user
, pwd
->pw_gid
))
547 err(1, "initgroups");
548 if (setuid(pwd
->pw_uid
))
552 * Set all user context except for: Environmental variables
553 * Umask Login records (wtmp, etc) Path
555 setwhat
= LOGIN_SETALL
& ~(LOGIN_SETENV
| LOGIN_SETUMASK
|
556 LOGIN_SETLOGIN
| LOGIN_SETPATH
| LOGIN_SETGROUP
|
559 * If -s is present, also set the MAC label.
562 setwhat
|= LOGIN_SETMAC
;
564 * Don't touch resource/priority settings if -m has been used
565 * or -l and -c hasn't, and we're not su'ing to root.
567 if ((asme
|| (!asthem
&& class == NULL
)) && pwd
->pw_uid
)
568 setwhat
&= ~(LOGIN_SETPRIORITY
| LOGIN_SETRESOURCES
);
569 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, setwhat
) < 0)
570 err(1, "setusercontext");
571 #endif /* __APPLE__ */
579 if (asthem
|| pwd
->pw_uid
)
580 setenv("USER", pwd
->pw_name
, 1);
581 setenv("HOME", pwd
->pw_dir
, 1);
582 setenv("SHELL", shell
, 1);
585 #endif /* __APPLE__ */
589 * Add any environmental variables that the
590 * PAM modules may have set.
592 environ_pam
= pam_getenvlist(pamh
);
594 export_pam_environment();
597 /* 5276965: As documented, set $PATH. */
598 setenv("PATH", "/bin:/usr/bin", 1);
600 /* set the su'd user's environment & umask */
601 setusercontext(lc
, pwd
, pwd
->pw_uid
,
602 LOGIN_SETPATH
| LOGIN_SETUMASK
|
604 #endif /* __APPLE__ */
606 setenv("TERM", p
, 1);
608 p
= pam_getenv(pamh
, "HOME");
609 if (chdir(p
? p
: pwd
->pw_dir
) < 0)
610 errx(1, "no directory");
615 #endif /* !__APPLE__ */
625 if ((p
= strrchr(shell
, '/')) != NULL
)
632 strlcpy(avshellbuf
+1, avshell
, sizeof(avshellbuf
) - 1);
633 avshell
= avshellbuf
;
636 /* csh *no longer* strips the first character... */
639 /* csh strips the first character... */
640 *np
.a
= asthem
? "-su" : iscsh
== YES
? "_su" : "su";
641 #endif /* __APPLE__ */
644 syslog(LOG_NOTICE
, "%s to %s%s", username
, user
,
653 export_pam_environment(void)
658 for (pp
= environ_pam
; *pp
!= NULL
; pp
++) {
659 if (ok_to_export(*pp
)) {
660 p
= strchr(*pp
, '=');
662 setenv(*pp
, p
+ 1, 1);
669 * Sanity checks on PAM environmental variables:
670 * - Make sure there is an '=' in the string.
671 * - Make sure the string doesn't run on too long.
672 * - Do not export certain variables. This list was taken from the
673 * Solaris pam_putenv(3) man page.
674 * Note that if the user is chrooted, PAM may have a better idea than we
675 * do of where her home directory is.
678 ok_to_export(const char *s
)
680 static const char *noexport
[] = {
681 "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
687 if (strlen(s
) > 1024 || strchr(s
, '=') == NULL
)
689 if (strncmp(s
, "LD_", 3) == 0)
691 for (pp
= noexport
; *pp
!= NULL
; pp
++) {
693 if (s
[n
] == '=' && strncmp(s
, *pp
, n
) == 0)
704 fprintf(stderr
, "usage: su [-] [-flm] [login [args]]\n");
706 fprintf(stderr
, "usage: su [-] [-flms] [-c class] [login [args]]\n");
707 #endif /* __APPLE__ */
713 chshell(const char *sh
)
720 while ((cp
= getusershell()) != NULL
&& !r
)
721 r
= (strcmp(cp
, sh
) == 0);
730 static char buf
[MAXPATHLEN
+ 4];
733 p
= ttyname(STDERR_FILENO
);
735 snprintf(buf
, sizeof(buf
), " on %s", p
);