2 * Copyright (c) 1988 The Regents of the University of California.
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
36 RCSID ("$Id: su.c,v 1.70.2.2 2000/12/07 14:04:19 assar Exp $");
39 #include "sysv_shadow.h"
42 static int kerberos (char *username, char *user, char *realm, int uid);
43 static int chshell (char *sh);
44 static char *ontty (void);
45 static int koktologin (char *name, char *realm, char *toname);
46 static int chshell (char *sh);
48 /* Handle '-' option after all the getopt options */
49 #define ARGSTR "Kkflmti:r:"
51 int destroy_tickets = 0;
52 static int use_kerberos = 1;
53 static char *root_inst = "root";
56 main (int argc, char **argv)
62 int asme, ch, asthem, fastlogin, prio;
63 enum { UNSET, YES, NO } iscsh = UNSET;
64 char *user, *shell, *avshell, *username, **np;
65 char shellbuf[MaxPathLen], avshellbuf[MaxPathLen];
68 set_progname (argv[0]);
73 asme = asthem = fastlogin = 0;
74 while ((ch = getopt (argc, argv, ARGSTR)) != -1)
105 "usage: su [-Kkflmt] [-i root-instance] [-r realm] [-] [login]\n");
108 /* Don't handle '-' option with getopt */
109 if (optind < argc && strcmp (argv[optind], "-") == 0) {
117 int fd = open (KEYFILE, O_RDONLY);
125 prio = getpriority (PRIO_PROCESS, 0);
128 setpriority (PRIO_PROCESS, 0, -2);
129 openlog ("su", LOG_CONS, LOG_AUTH);
131 /* get current login name and shell */
133 username = getlogin ();
134 if (username == NULL || (pwd = k_getpwnam (username)) == NULL ||
136 pwd = k_getpwuid (ruid);
138 errx (1, "who are you?");
139 username = strdup (pwd->pw_name);
140 if (username == NULL)
141 errx (1, "strdup: out of memory");
143 if (pwd->pw_shell && *pwd->pw_shell) {
144 strlcpy (shellbuf, pwd->pw_shell, sizeof(shellbuf));
147 shell = _PATH_BSHELL;
152 /* get target login information, default to root */
153 user = *argv ? *argv : "root";
154 np = *argv ? argv : argv - 1;
156 pwd = k_getpwnam (user);
158 errx (1, "unknown login %s", user);
159 if (pwd->pw_uid == 0 && strcmp ("root", user) != 0) {
160 syslog (LOG_ALERT, "NIS attack, user %s has uid 0", user);
161 errx (1, "unknown login %s", user);
163 if (!use_kerberos || kerberos (username, user, realm, pwd->pw_uid)) {
164 #ifndef PASSWD_FALLBACK
165 errx (1, "won't use /etc/passwd authentication");
167 /* getpwnam() is not reentrant and kerberos might use it! */
168 pwd = k_getpwnam (user);
170 errx (1, "unknown login %s", user);
171 /* only allow those in group zero to su to root. */
172 if (pwd->pw_uid == 0 && (gr = getgrgid ((gid_t) 0)))
173 for (g = gr->gr_mem;; ++g) {
176 /* if group 0 is empty or only
177 contains root su is still ok. */
178 if (gr->gr_mem[0] == 0)
179 break; /* group 0 is empty */
180 if (gr->gr_mem[1] == 0 &&
181 strcmp (gr->gr_mem[0], "root") == 0)
182 break; /* only root in group 0 */
184 errx (1, "you are not in the correct group to su %s.",
187 if (!strcmp (username, *g))
190 /* if target requires a password, verify it */
191 if (ruid && *pwd->pw_passwd) {
195 snprintf (prompt, sizeof(prompt), "%s's Password: ", pwd->pw_name);
196 if (des_read_pw_string (passwd, sizeof (passwd),
198 memset (passwd, 0, sizeof (passwd));
201 if (strcmp (pwd->pw_passwd,
202 crypt (passwd, pwd->pw_passwd))) {
203 memset (passwd, 0, sizeof (passwd));
204 syslog (LOG_AUTH | LOG_WARNING,
205 "BAD SU %s to %s%s", username,
209 memset (passwd, 0, sizeof (passwd));
213 /* if asme and non-standard target shell, must be root */
214 if (!chshell (pwd->pw_shell) && ruid)
215 errx (1, "permission denied (shell '%s' not in /etc/shells).",
217 } else if (pwd->pw_shell && *pwd->pw_shell) {
218 shell = pwd->pw_shell;
221 shell = _PATH_BSHELL;
225 if ((p = strrchr (shell, '/')) != 0)
230 /* if we're forking a csh, we want to slightly muck the args */
232 iscsh = strcmp (avshell, "csh") ? NO : YES;
234 /* set permissions */
236 if (setgid (pwd->pw_gid) < 0)
238 if (initgroups (user, pwd->pw_gid)) {
239 if (errno == E2BIG) /* Member of too many groups! */
240 warn("initgroups failed.");
242 errx(1, "initgroups failed.");
245 if (setuid (pwd->pw_uid) < 0)
248 if (pwd->pw_uid != 0 && setuid(0) != -1) {
249 syslog(LOG_ALERT | LOG_AUTH,
250 "Failed to drop privileges for user %s", pwd->pw_name);
256 char *k = getenv ("KRBTKFILE");
257 char *t = getenv ("TERM");
259 environ = malloc (10 * sizeof (char *));
263 setenv ("PATH", _PATH_DEFPATH, 1);
265 setenv ("TERM", t, 1);
267 setenv ("KRBTKFILE", k, 1);
268 if (chdir (pwd->pw_dir) < 0)
269 errx (1, "no directory");
271 if (asthem || pwd->pw_uid)
272 setenv ("USER", pwd->pw_name, 1);
273 setenv ("HOME", pwd->pw_dir, 1);
274 setenv ("SHELL", shell, 1);
283 snprintf (avshellbuf, sizeof(avshellbuf),
285 avshell = avshellbuf;
286 } else if (iscsh == YES) {
287 /* csh strips the first character... */
288 snprintf (avshellbuf, sizeof(avshellbuf),
290 avshell = avshellbuf;
295 syslog (LOG_NOTICE | LOG_AUTH, "%s to %s%s",
296 username, user, ontty ());
298 setpriority (PRIO_PROCESS, 0, prio);
303 if (k_setpag () != 0)
305 code = krb_afslog (0, 0);
306 if (code != KSUCCESS && code != KDC_PR_UNKNOWN)
307 warnx ("afsklog: %s", krb_get_err_text (code));
312 warn ("execv(%s)", shell);
313 if (getuid () == 0) {
314 execv (_PATH_BSHELL, np);
315 warn ("execv(%s)", _PATH_BSHELL);
325 while ((cp = getusershell ()) != NULL)
326 if (!strcmp (cp, sh))
335 static char buf[MaxPathLen + 4];
338 if ((p = ttyname (STDERR_FILENO)) != 0)
339 snprintf (buf, sizeof(buf), " on %s", p);
344 kerberos (char *username, char *user, char *lrealm, int uid)
351 char tmp_realm[REALM_SZ], krbtkfile[MaxPathLen];
352 char hostname[MaxHostNameLen], savehost[MaxHostNameLen];
356 if (lrealm != NULL) {
357 allowed = koktologin (username, lrealm, user) == 0;
359 for (n = 1; !allowed && krb_get_lrealm (tmp_realm, n) == KSUCCESS; ++n)
360 allowed = koktologin (username, tmp_realm, user) == 0;
363 if (!allowed && !uid) {
364 #ifndef PASSWD_FALLBACK
365 warnx ("not in %s's ACL.", user);
369 snprintf (krbtkfile, sizeof(krbtkfile),
370 "%s_%s_to_%s_%u", TKT_ROOT, username, user,
371 (unsigned) getpid ());
373 setenv ("KRBTKFILE", krbtkfile, 1);
374 krb_set_tkt_string (krbtkfile);
376 * Set real as well as effective ID to 0 for the moment,
377 * to make the kerberos library do the right thing.
385 * Little trick here -- if we are su'ing to root, we need to get a ticket
386 * for "xxx.root", where xxx represents the name of the person su'ing.
387 * Otherwise (non-root case), we need to get a ticket for "yyy.", where
388 * yyy represents the name of the person being su'd to, and the instance
391 * We should have a way to set the ticket lifetime, with a system default
398 snprintf (prompt, sizeof(prompt),
400 krb_unparse_name_long ((uid == 0 ? username : user),
401 (uid == 0 ? root_inst : ""),
403 if (des_read_pw_string (passw, sizeof (passw), prompt, 0)) {
404 memset (passw, 0, sizeof (passw));
407 if (strlen(passw) == 0)
408 return (1); /* Empty passwords is not allowed */
409 kerno = krb_get_pw_in_tkt ((uid == 0 ? username : user),
410 (uid == 0 ? root_inst : ""), lrealm,
411 KRB_TICKET_GRANTING_TICKET,
415 memset (passw, 0, strlen (passw));
418 if (kerno != KSUCCESS) {
419 if (kerno == KDC_PR_UNKNOWN) {
420 warnx ("principal unknown: %s",
421 krb_unparse_name_long ((uid == 0 ? username : user),
422 (uid == 0 ? root_inst : ""),
426 warnx ("unable to su: %s", krb_get_err_text (kerno));
427 syslog (LOG_NOTICE | LOG_AUTH,
428 "BAD SU: %s to %s%s: %s",
429 username, user, ontty (), krb_get_err_text (kerno));
432 if (chown (krbtkfile, uid, -1) < 0) {
437 setpriority (PRIO_PROCESS, 0, -2);
439 if (gethostname (hostname, sizeof (hostname)) == -1) {
440 warn ("gethostname");
444 strlcpy (savehost, krb_get_phost (hostname), sizeof (savehost));
446 for (n = 1; krb_get_lrealm (tmp_realm, n) == KSUCCESS; ++n) {
447 kerno = krb_mk_req (&ticket, "rcmd", savehost, tmp_realm, 33);
452 if (kerno == KDC_PR_UNKNOWN) {
453 warnx ("Warning: TGT not verified.");
454 syslog (LOG_NOTICE | LOG_AUTH,
455 "%s to %s%s, TGT not verified (%s); "
456 "%s.%s not registered?",
457 username, user, ontty (), krb_get_err_text (kerno),
459 #ifdef KLOGIN_PARANOID
461 * if the "VERIFY_SERVICE" doesn't exist in the KDC for this host, *
462 * don't allow kerberos login, also log the error condition.
464 warnx ("Trying local password!");
467 } else if (kerno != KSUCCESS) {
468 warnx ("Unable to use TGT: %s", krb_get_err_text (kerno));
469 syslog (LOG_NOTICE | LOG_AUTH, "failed su: %s to %s%s: %s",
470 username, user, ontty (), krb_get_err_text (kerno));
474 if (!(hp = gethostbyname (hostname))) {
475 warnx ("can't get addr of %s", hostname);
479 memcpy (&faddr, hp->h_addr, sizeof (faddr));
481 if ((kerno = krb_rd_req (&ticket, "rcmd", savehost, faddr,
482 &authdata, "")) != KSUCCESS) {
483 warnx ("unable to verify rcmd ticket: %s",
484 krb_get_err_text (kerno));
485 syslog (LOG_NOTICE | LOG_AUTH,
486 "failed su: %s to %s%s: %s", username,
487 user, ontty (), krb_get_err_text (kerno));
492 if (!destroy_tickets)
493 fprintf (stderr, "Don't forget to kdestroy before exiting the shell.\n");
498 koktologin (char *name, char *realm, char *toname)
500 return krb_kuserok (name,
501 strcmp (toname, "root") == 0 ? root_inst : "",