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
[])
113 struct pam_conv conv
= {misc_conv
, NULL
};
121 int asme
, ch
, asthem
, fastlogin
, prio
, i
, setwhat
, retcode
,
122 statusp
, child_pid
, child_pgrp
, ret_pid
;
123 char *username
, *cleanenv
, *class, shellbuf
[MAXPATHLEN
];
124 const char *p
, *user
, *shell
, *mytty
, **nargv
;
126 shell
= class = cleanenv
= NULL
;
127 asme
= asthem
= fastlogin
= statusp
= 0;
131 while ((ch
= getopt(argc
, argv
, "-flmc:")) != -1)
154 user
= argv
[optind
++];
159 if (strlen(user
) > MAXLOGNAME
- 1)
160 errx(1, "username too long");
162 nargv
= malloc(sizeof(char *) * (argc
+ 4));
164 errx(1, "malloc failure");
166 nargv
[argc
+ 3] = NULL
;
167 for (i
= argc
; i
>= optind
; i
--)
168 nargv
[i
+ 3] = argv
[i
];
169 np
.a
= &nargv
[i
+ 3];
174 prio
= getpriority(PRIO_PROCESS
, 0);
178 setpriority(PRIO_PROCESS
, 0, -2);
179 openlog("su", LOG_CONS
, LOG_AUTH
);
181 /* get current login name, real uid and shell */
183 username
= getlogin();
184 pwd
= getpwnam(username
);
185 if (username
== NULL
|| pwd
== NULL
|| pwd
->pw_uid
!= ruid
)
186 pwd
= getpwuid(ruid
);
188 errx(1, "who are you?");
191 username
= strdup(pwd
->pw_name
);
192 if (username
== NULL
)
193 err(1, "strdup failure");
196 if (pwd
->pw_shell
!= NULL
&& *pwd
->pw_shell
!= '\0') {
197 /* must copy - pwd memory is recycled */
198 shell
= strncpy(shellbuf
, pwd
->pw_shell
,
200 shellbuf
[sizeof(shellbuf
) - 1] = '\0';
203 shell
= _PATH_BSHELL
;
208 /* Do the whole PAM startup thing */
209 retcode
= pam_start("su", user
, &conv
, &pamh
);
210 if (retcode
!= PAM_SUCCESS
) {
211 syslog(LOG_ERR
, "pam_start: %s", pam_strerror(pamh
, retcode
));
212 errx(1, "pam_start: %s", pam_strerror(pamh
, retcode
));
215 PAM_SET_ITEM(PAM_RUSER
, getlogin());
217 mytty
= ttyname(STDERR_FILENO
);
220 PAM_SET_ITEM(PAM_TTY
, mytty
);
222 retcode
= pam_authenticate(pamh
, 0);
223 if (retcode
!= PAM_SUCCESS
) {
224 syslog(LOG_ERR
, "pam_authenticate: %s",
225 pam_strerror(pamh
, retcode
));
228 retcode
= pam_get_item(pamh
, PAM_USER
, (const void **)&p
);
229 if (retcode
== PAM_SUCCESS
)
232 syslog(LOG_ERR
, "pam_get_item(PAM_USER): %s",
233 pam_strerror(pamh
, retcode
));
235 retcode
= pam_acct_mgmt(pamh
, 0);
236 if (retcode
== PAM_NEW_AUTHTOK_REQD
) {
237 retcode
= pam_chauthtok(pamh
,
238 PAM_CHANGE_EXPIRED_AUTHTOK
);
239 if (retcode
!= PAM_SUCCESS
) {
240 syslog(LOG_ERR
, "pam_chauthtok: %s",
241 pam_strerror(pamh
, retcode
));
245 if (retcode
!= PAM_SUCCESS
) {
246 syslog(LOG_ERR
, "pam_acct_mgmt: %s",
247 pam_strerror(pamh
, retcode
));
251 /* get target login information, default to root */
252 pwd
= getpwnam(user
);
254 errx(1, "unknown login: %s", user
);
256 /* if asme and non-standard target shell, must be root */
258 if (ruid
!= 0 && !chshell(pwd
->pw_shell
))
259 errx(1, "permission denied (shell).");
261 else if (pwd
->pw_shell
&& *pwd
->pw_shell
) {
262 shell
= strncpy(shellbuf
, pwd
->pw_shell
, sizeof(shellbuf
));
263 shellbuf
[sizeof(shellbuf
) - 1] = '\0';
267 shell
= _PATH_BSHELL
;
271 /* if we're forking a csh, we want to slightly muck the args */
272 if (iscsh
== UNSET
) {
273 p
= strrchr(shell
, '/');
278 iscsh
= strcmp(p
, "csh") ? (strcmp(p
, "tcsh") ? NO
: YES
) : YES
;
280 setpriority(PRIO_PROCESS
, 0, prio
);
283 * PAM modules might add supplementary groups in pam_setcred(), so
284 * initialize them first.
286 if( initgroups(user
, pwd
->pw_gid
) )
287 err(1, "initgroups failed");
289 retcode
= pam_open_session(pamh
, 0);
290 if( retcode
!= PAM_SUCCESS
) {
291 syslog(LOG_ERR
, "pam_open_session(pamh, 0): %s",
292 pam_strerror(pamh
, retcode
));
295 retcode
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
296 if (retcode
!= PAM_SUCCESS
)
297 syslog(LOG_ERR
, "pam_setcred(pamh, PAM_ESTABLISH_CRED): %s",
298 pam_strerror(pamh
, retcode
));
303 * We must fork() before setuid() because we need to call
304 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
311 while ((ret_pid
= waitpid(child_pid
, &statusp
, WUNTRACED
)) != -1) {
312 if (WIFSTOPPED(statusp
)) {
313 child_pgrp
= tcgetpgrp(1);
314 kill(getpid(), SIGSTOP
);
315 tcsetpgrp(1, child_pgrp
);
316 kill(child_pid
, SIGCONT
);
325 exit(WEXITSTATUS(statusp
));
331 if( setgid(pwd
->pw_gid
) )
333 if( setuid(pwd
->pw_uid
) )
342 * Add any environmental variables that the
343 * PAM modules may have set.
345 environ_pam
= pam_getenvlist(pamh
);
347 export_pam_environment();
350 setenv("TERM", p
, 1);
351 if (chdir(pwd
->pw_dir
) < 0)
352 errx(1, "no directory");
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);
366 /* csh *no longer* strips the first character... */
367 *np
.a
= asthem
? "-su" : "su";
370 syslog(LOG_NOTICE
, "%s to %s%s", username
, user
,
379 export_pam_environment(void)
383 for (pp
= environ_pam
; *pp
!= NULL
; pp
++) {
384 if (ok_to_export(*pp
))
392 * Sanity checks on PAM environmental variables:
393 * - Make sure there is an '=' in the string.
394 * - Make sure the string doesn't run on too long.
395 * - Do not export certain variables. This list was taken from the
396 * Solaris pam_putenv(3) man page.
399 ok_to_export(const char *s
)
401 static const char *noexport
[] = {
402 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
408 if (strlen(s
) > 1024 || strchr(s
, '=') == NULL
)
410 if (strncmp(s
, "LD_", 3) == 0)
412 for (pp
= noexport
; *pp
!= NULL
; pp
++) {
414 if (s
[n
] == '=' && strncmp(s
, *pp
, n
) == 0)
424 fprintf(stderr
, "usage: su [-] [-flm] [-c class] [login [args]]\n");
439 } while (!r
&& cp
!= NULL
);
448 static char buf
[MAXPATHLEN
+ 4];
451 p
= ttyname(STDERR_FILENO
);
453 snprintf(buf
, sizeof(buf
), " on %s", p
);