]> git.saurik.com Git - apple/shell_cmds.git/blame - su/su.c
shell_cmds-53.tar.gz
[apple/shell_cmds.git] / su / su.c
CommitLineData
44bd5ea7 1/*
c0fcf4e1
A
2 * Copyright (c) 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
44bd5ea7
A
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
20 *
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
31 * SUCH DAMAGE.
32 */
33
44bd5ea7 34#ifndef lint
c0fcf4e1
A
35static const char copyright[] =
36"@(#) Copyright (c) 1988, 1993, 1994\n\
37 The Regents of the University of California. All rights reserved.\n";
44bd5ea7
A
38#endif /* not lint */
39
40#ifndef lint
41#if 0
c0fcf4e1 42static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";
44bd5ea7 43#endif
c0fcf4e1
A
44static const char rcsid[] =
45 "$FreeBSD: src/usr.bin/su/su.c,v 1.48 2002/01/24 16:20:17 des Exp $";
44bd5ea7
A
46#endif /* not lint */
47
48#include <sys/param.h>
49#include <sys/time.h>
50#include <sys/resource.h>
c0fcf4e1
A
51#include <sys/wait.h>
52
44bd5ea7
A
53#include <err.h>
54#include <errno.h>
55#include <grp.h>
56#include <paths.h>
57#include <pwd.h>
c0fcf4e1 58#include <signal.h>
44bd5ea7 59#include <stdio.h>
44bd5ea7
A
60#include <stdlib.h>
61#include <string.h>
62#include <syslog.h>
44bd5ea7
A
63#include <unistd.h>
64
c0fcf4e1
A
65#include <pam/pam_appl.h>
66#include <pam/pam_misc.h>
67
68#define PAM_END() do { \
69 int local_ret; \
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)); \
80 } \
81} while (0)
82
83
84#define PAM_SET_ITEM(what, item) do { \
85 int local_ret; \
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)); \
92 } \
93} while (0)
94
95enum tristate { UNSET, YES, NO };
96
97static pam_handle_t *pamh = NULL;
98static int creds_set = 0;
99static char **environ_pam;
100
101static char *ontty(void);
102static int chshell(char *);
103static void usage(void);
104static int export_pam_environment(void);
105static int ok_to_export(const char *);
106
107extern char **environ;
44bd5ea7
A
108
109int
c0fcf4e1 110main(int argc, char *argv[])
44bd5ea7 111{
c0fcf4e1
A
112 struct passwd *pwd;
113 struct pam_conv conv = {misc_conv, NULL};
114 enum tristate iscsh;
115 union {
116 const char **a;
117 char * const *b;
118 } np;
119 uid_t ruid;
120 gid_t gid;
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;
125
126 shell = class = cleanenv = NULL;
127 asme = asthem = fastlogin = statusp = 0;
128 user = "root";
129 iscsh = UNSET;
130
131 while ((ch = getopt(argc, argv, "-flmc:")) != -1)
132 switch ((char)ch) {
44bd5ea7
A
133 case 'f':
134 fastlogin = 1;
135 break;
136 case '-':
137 case 'l':
138 asme = 0;
139 asthem = 1;
140 break;
141 case 'm':
142 asme = 1;
143 asthem = 0;
144 break;
c0fcf4e1
A
145 case 'c':
146 class = optarg;
147 break;
44bd5ea7
A
148 case '?':
149 default:
c0fcf4e1 150 usage();
44bd5ea7 151 }
c0fcf4e1
A
152
153 if (optind < argc)
154 user = argv[optind++];
155
156 if (user == NULL)
157 usage();
158
159 if (strlen(user) > MAXLOGNAME - 1)
160 errx(1, "username too long");
161
162 nargv = malloc(sizeof(char *) * (argc + 4));
163 if (nargv == NULL)
164 errx(1, "malloc failure");
165
166 nargv[argc + 3] = NULL;
167 for (i = argc; i >= optind; i--)
168 nargv[i + 3] = argv[i];
169 np.a = &nargv[i + 3];
170
44bd5ea7
A
171 argv += optind;
172
173 errno = 0;
174 prio = getpriority(PRIO_PROCESS, 0);
175 if (errno)
176 prio = 0;
44bd5ea7 177
c0fcf4e1
A
178 setpriority(PRIO_PROCESS, 0, -2);
179 openlog("su", LOG_CONS, LOG_AUTH);
180
181 /* get current login name, real uid and shell */
44bd5ea7
A
182 ruid = getuid();
183 username = getlogin();
c0fcf4e1
A
184 pwd = getpwnam(username);
185 if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
44bd5ea7
A
186 pwd = getpwuid(ruid);
187 if (pwd == NULL)
188 errx(1, "who are you?");
c0fcf4e1
A
189 gid = pwd->pw_gid;
190
44bd5ea7 191 username = strdup(pwd->pw_name);
44bd5ea7 192 if (username == NULL)
c0fcf4e1 193 err(1, "strdup failure");
44bd5ea7
A
194
195 if (asme) {
c0fcf4e1
A
196 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
197 /* must copy - pwd memory is recycled */
44bd5ea7 198 shell = strncpy(shellbuf, pwd->pw_shell,
c0fcf4e1 199 sizeof(shellbuf));
44bd5ea7 200 shellbuf[sizeof(shellbuf) - 1] = '\0';
c0fcf4e1
A
201 }
202 else {
44bd5ea7
A
203 shell = _PATH_BSHELL;
204 iscsh = NO;
205 }
206 }
44bd5ea7 207
c0fcf4e1
A
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));
213 }
44bd5ea7 214
c0fcf4e1
A
215 PAM_SET_ITEM(PAM_RUSER, getlogin());
216
217 mytty = ttyname(STDERR_FILENO);
218 if (!mytty)
219 mytty = "tty";
220 PAM_SET_ITEM(PAM_TTY, mytty);
221
222 retcode = pam_authenticate(pamh, 0);
223 if (retcode != PAM_SUCCESS) {
224 syslog(LOG_ERR, "pam_authenticate: %s",
225 pam_strerror(pamh, retcode));
226 errx(1, "Sorry");
227 }
228 retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
229 if (retcode == PAM_SUCCESS)
230 user = p;
231 else
232 syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
233 pam_strerror(pamh, retcode));
234
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));
242 errx(1, "Sorry");
44bd5ea7
A
243 }
244 }
c0fcf4e1
A
245 if (retcode != PAM_SUCCESS) {
246 syslog(LOG_ERR, "pam_acct_mgmt: %s",
247 pam_strerror(pamh, retcode));
248 errx(1, "Sorry");
249 }
44bd5ea7 250
c0fcf4e1
A
251 /* get target login information, default to root */
252 pwd = getpwnam(user);
253 if (pwd == NULL)
254 errx(1, "unknown login: %s", user);
255
256 /* if asme and non-standard target shell, must be root */
44bd5ea7 257 if (asme) {
c0fcf4e1
A
258 if (ruid != 0 && !chshell(pwd->pw_shell))
259 errx(1, "permission denied (shell).");
260 }
261 else if (pwd->pw_shell && *pwd->pw_shell) {
44bd5ea7
A
262 shell = pwd->pw_shell;
263 iscsh = UNSET;
c0fcf4e1
A
264 }
265 else {
44bd5ea7
A
266 shell = _PATH_BSHELL;
267 iscsh = NO;
268 }
269
44bd5ea7 270 /* if we're forking a csh, we want to slightly muck the args */
c0fcf4e1
A
271 if (iscsh == UNSET) {
272 p = strrchr(shell, '/');
273 if (p)
274 ++p;
275 else
276 p = shell;
277 iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
44bd5ea7 278 }
c0fcf4e1 279 setpriority(PRIO_PROCESS, 0, prio);
44bd5ea7 280
c0fcf4e1
A
281 /*
282 * PAM modules might add supplementary groups in pam_setcred(), so
283 * initialize them first.
284 */
285 if( initgroups(user, pwd->pw_gid) )
286 err(1, "initgroups failed");
44bd5ea7 287
c0fcf4e1
A
288 retcode = pam_open_session(pamh, 0);
289 if( retcode != PAM_SUCCESS ) {
290 syslog(LOG_ERR, "pam_open_session(pamh, 0): %s",
291 pam_strerror(pamh, retcode));
44bd5ea7 292 }
44bd5ea7 293
c0fcf4e1
A
294 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
295 if (retcode != PAM_SUCCESS)
296 syslog(LOG_ERR, "pam_setcred(pamh, PAM_ESTABLISH_CRED): %s",
297 pam_strerror(pamh, retcode));
298 else
299 creds_set = 1;
44bd5ea7 300
c0fcf4e1
A
301 /*
302 * We must fork() before setuid() because we need to call
303 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
304 */
44bd5ea7 305
c0fcf4e1
A
306 statusp = 1;
307 child_pid = fork();
308 switch (child_pid) {
309 default:
310 while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
311 if (WIFSTOPPED(statusp)) {
312 child_pgrp = tcgetpgrp(1);
313 kill(getpid(), SIGSTOP);
314 tcsetpgrp(1, child_pgrp);
315 kill(child_pid, SIGCONT);
316 statusp = 1;
317 continue;
318 }
319 break;
320 }
321 if (ret_pid == -1)
322 err(1, "waitpid");
323 PAM_END();
324 exit(WEXITSTATUS(statusp));
325 case -1:
326 err(1, "fork");
327 PAM_END();
328 exit(1);
329 case 0:
330 if( setgid(pwd->pw_gid) )
331 err(1, "setgid");
332 if( setuid(pwd->pw_uid) )
333 err(1, "setuid");
334
335 if (!asme) {
336 if (asthem) {
337 p = getenv("TERM");
338 *environ = NULL;
339
340 /*
341 * Add any environmental variables that the
342 * PAM modules may have set.
343 */
344 environ_pam = pam_getenvlist(pamh);
345 if (environ_pam)
346 export_pam_environment();
347
348 if (p)
349 setenv("TERM", p, 1);
350 if (chdir(pwd->pw_dir) < 0)
351 errx(1, "no directory");
352 }
353 if (asthem || pwd->pw_uid)
354 setenv("USER", pwd->pw_name, 1);
355 setenv("HOME", pwd->pw_dir, 1);
356 setenv("SHELL", shell, 1);
357 }
44bd5ea7 358
c0fcf4e1
A
359 if (iscsh == YES) {
360 if (fastlogin)
361 *np.a-- = "-f";
362 if (asme)
363 *np.a-- = "-m";
364 }
9bafe280
A
365 /* csh *no longer* strips the first character... */
366 *np.a = asthem ? "-su" : "su";
c0fcf4e1
A
367
368 if (ruid != 0)
369 syslog(LOG_NOTICE, "%s to %s%s", username, user,
370 ontty());
371
372 execv(shell, np.b);
373 err(1, "%s", shell);
374 }
44bd5ea7
A
375}
376
44bd5ea7 377static int
c0fcf4e1 378export_pam_environment(void)
44bd5ea7 379{
c0fcf4e1 380 char **pp;
44bd5ea7 381
c0fcf4e1
A
382 for (pp = environ_pam; *pp != NULL; pp++) {
383 if (ok_to_export(*pp))
384 putenv(*pp);
385 free(*pp);
44bd5ea7 386 }
c0fcf4e1
A
387 return PAM_SUCCESS;
388}
44bd5ea7 389
c0fcf4e1
A
390/*
391 * Sanity checks on PAM environmental variables:
392 * - Make sure there is an '=' in the string.
393 * - Make sure the string doesn't run on too long.
394 * - Do not export certain variables. This list was taken from the
395 * Solaris pam_putenv(3) man page.
396 */
397static int
398ok_to_export(const char *s)
399{
400 static const char *noexport[] = {
401 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
402 "IFS", "PATH", NULL
403 };
404 const char **pp;
405 size_t n;
406
407 if (strlen(s) > 1024 || strchr(s, '=') == NULL)
408 return 0;
409 if (strncmp(s, "LD_", 3) == 0)
410 return 0;
411 for (pp = noexport; *pp != NULL; pp++) {
412 n = strlen(*pp);
413 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
414 return 0;
44bd5ea7 415 }
c0fcf4e1
A
416 return 1;
417}
44bd5ea7 418
c0fcf4e1
A
419static void
420usage(void)
421{
44bd5ea7 422
c0fcf4e1
A
423 fprintf(stderr, "usage: su [-] [-flm] [-c class] [login [args]]\n");
424 exit(1);
44bd5ea7
A
425}
426
427static int
c0fcf4e1 428chshell(char *sh)
44bd5ea7 429{
c0fcf4e1
A
430 int r;
431 char *cp;
432
433 r = 0;
434 setusershell();
435 do {
436 cp = getusershell();
437 r = strcmp(cp, sh);
438 } while (!r && cp != NULL);
439 endusershell();
440 return r;
441}
442
443static char *
444ontty(void)
445{
446 char *p;
447 static char buf[MAXPATHLEN + 4];
448
449 buf[0] = 0;
450 p = ttyname(STDERR_FILENO);
451 if (p)
452 snprintf(buf, sizeof(buf), " on %s", p);
453 return buf;
44bd5ea7 454}