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 "$Id: su.c,v 1.30 1999/07/01 17:59:17 billf Exp $";
48 #include <sys/param.h>
50 #include <sys/resource.h>
65 #include <login_cap.h>
82 #define ARGSTR "-Kflmc:"
84 #define ARGSTR "-Kflm"
87 static int kerberos(char *username, char *user, int uid, char *pword);
88 static int koktologin(char *name, char *toname);
93 #define ARGSTR "-flmc:"
99 char *ontty __P((void));
100 int chshell __P((char *));
101 static void usage __P((void));
108 extern char **environ;
114 char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np;
118 int asme, ch, asthem, fastlogin, prio, i;
119 enum { UNSET, YES, NO } iscsh = UNSET;
124 #ifdef LOGIN_CAP_AUTH
125 char *style, *approvep, *auth_method = NULL;
131 char shellbuf[MAXPATHLEN];
136 asme = asthem = fastlogin = 0;
138 while((ch = getopt(argc, argv, ARGSTR)) != -1)
168 user = argv[optind++];
170 if (strlen(user) > MAXLOGNAME - 1) {
171 (void)fprintf(stderr, "su: username too long.\n");
178 if ((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) {
179 errx(1, "malloc failure");
182 nargv[argc + 3] = NULL;
183 for (i = argc; i >= optind; i--)
184 nargv[i + 3] = argv[i];
190 k = auth_getval("auth_list");
191 if (k && !strstr(k, "kerberos"))
195 prio = getpriority(PRIO_PROCESS, 0);
198 (void)setpriority(PRIO_PROCESS, 0, -2);
199 openlog("su", LOG_CONS, 0);
201 /* get current login name and shell */
203 username = getlogin();
204 if (username == NULL || (pwd = getpwnam(username)) == NULL ||
206 pwd = getpwuid(ruid);
208 errx(1, "who are you?");
209 username = strdup(pwd->pw_name);
211 if (username == NULL)
214 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
215 /* copy: pwd memory is recycled */
216 shell = strncpy(shellbuf, pwd->pw_shell, sizeof shellbuf);
217 shellbuf[sizeof shellbuf - 1] = '\0';
219 shell = _PATH_BSHELL;
224 #ifdef LOGIN_CAP_AUTH
225 if (auth_method = strchr(user, ':')) {
228 if (*auth_method == '\0')
231 #endif /* !LOGIN_CAP_AUTH */
233 /* get target login information, default to root */
234 if ((pwd = getpwnam(user)) == NULL) {
235 errx(1, "unknown login: %s", user);
239 lc = login_getpwclass(pwd);
242 errx(1, "only root may use -c");
243 lc = login_getclass(class);
245 errx(1, "unknown class: %s", class);
250 targetpass = strdup(pwd->pw_passwd);
255 if (use_kerberos && koktologin(username, user)
257 warnx("kerberos: not in %s's ACL.", user);
263 * Only allow those with pw_gid==0 or those listed in
264 * group zero to su to root. If group zero entry is
265 * missing or empty, then allow anyone to su to root.
266 * iswheelsu will only be set if the user is EXPLICITLY
267 * listed in group zero.
269 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) &&
270 gr->gr_mem && *(gr->gr_mem))
271 for (g = gr->gr_mem;; ++g) {
276 errx(1, "you are not in the correct group to su %s.", user);
278 if (strcmp(username, *g) == 0) {
286 /* if target requires a password, verify it */
287 if (*pwd->pw_passwd) {
288 #ifdef LOGIN_CAP_AUTH
290 * This hands off authorisation to an authorisation program,
291 * depending on the styles available for the "auth-su",
292 * authorisation styles.
294 if ((style = login_getstyle(lc, auth_method, "su")) == NULL)
295 errx(1, "auth method available for su.\n");
296 if (authenticate(user, lc ? lc->lc_class : "default", style, "su") != 0) {
298 if (!iswheelsu || authenticate(username, lc ? lc->lc_class : "default", style, "su") != 0) {
301 fprintf(stderr, "Sorry\n");
302 syslog(LOG_AUTH|LOG_WARNING,"BAD SU %s to %s%s", username, user, ontty());
308 * If authentication succeeds, run any approval
309 * program, if applicable for this class.
311 approvep = login_getcapstr(lc, "approve", NULL, NULL);
312 if (approvep==NULL || auth_script(approvep, approvep, username, lc->lc_class, 0) == 0) {
313 int r = auth_scan(AUTH_OKAY);
314 /* See what the authorise program says */
315 if (!(r & AUTH_ROOTOKAY) && pwd->pw_uid == 0) {
316 fprintf(stderr, "Sorry\n");
317 syslog(LOG_AUTH|LOG_WARNING,"UNAPPROVED ROOT SU %s%s", user, ontty());
321 #else /* !LOGIN_CAP_AUTH */
325 pwd = getpwnam(username);
328 p = skey_getpass("Password:", pwd, 1);
329 if (!(!strcmp(pwd->pw_passwd, skey_crypt(p, pwd->pw_passwd, pwd, 1))
331 || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass)))
335 p = getpass("Password:");
336 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
339 if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p)))
342 fprintf(stderr, "Sorry\n");
343 syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty());
349 pwd = getpwnam(user);
352 #endif /* LOGIN_CAP_AUTH */
354 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) {
355 fprintf(stderr, "Sorry - account expired\n");
356 syslog(LOG_AUTH|LOG_WARNING,
357 "BAD SU %s to %s%s", username,
364 /* if asme and non-standard target shell, must be root */
365 if (!chshell(pwd->pw_shell) && ruid)
366 errx(1, "permission denied (shell).");
367 } else if (pwd->pw_shell && *pwd->pw_shell) {
368 shell = pwd->pw_shell;
371 shell = _PATH_BSHELL;
375 /* if we're forking a csh, we want to slightly muck the args */
376 if (iscsh == UNSET) {
377 p = strrchr(shell, '/');
382 if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO)
383 iscsh = strcmp(p, "tcsh") ? NO : YES;
386 (void)setpriority(PRIO_PROCESS, 0, prio);
389 /* Set everything now except the environment & umask */
390 setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
392 * Don't touch resource/priority settings if -m has been
393 * used or -l and -c hasn't, and we're not su'ing to root.
395 if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
396 setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES);
397 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
398 err(1, "setusercontext");
400 /* set permissions */
401 if (setgid(pwd->pw_gid) < 0)
403 if (initgroups(user, pwd->pw_gid))
404 errx(1, "initgroups failed");
405 if (setuid(pwd->pw_uid) < 0)
413 k = getenv("KRBTKFILE");
415 if ((cleanenv = calloc(20, sizeof(char*))) == NULL)
420 /* set the su'd user's environment & umask */
421 setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
423 (void)setenv("PATH", _PATH_DEFPATH, 1);
426 (void)setenv("TERM", p, 1);
429 (void)setenv("KRBTKFILE", k, 1);
431 if (chdir(pwd->pw_dir) < 0)
432 errx(1, "no directory");
434 if (asthem || pwd->pw_uid)
435 (void)setenv("USER", pwd->pw_name, 1);
436 (void)setenv("HOME", pwd->pw_dir, 1);
437 (void)setenv("SHELL", shell, 1);
446 /* csh strips the first character... */
447 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
450 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
451 username, user, ontty());
462 (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR);
474 while (!r && (cp = getusershell()) != NULL)
475 r = strcmp(cp, sh) == 0;
484 static char buf[MAXPATHLEN + 4];
487 p = ttyname(STDERR_FILENO);
489 snprintf(buf, sizeof(buf), " on %s", p);
495 kerberos(username, user, uid, pword)
496 char *username, *user;
504 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
505 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
506 char *krb_get_phost();
509 if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
511 (void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user,
512 (unsigned long)getuid());
514 (void)setenv("KRBTKFILE", krbtkfile, 1);
515 (void)krb_set_tkt_string(krbtkfile);
517 * Set real as well as effective ID to 0 for the moment,
518 * to make the kerberos library do the right thing.
526 * Little trick here -- if we are su'ing to root,
527 * we need to get a ticket for "xxx.root", where xxx represents
528 * the name of the person su'ing. Otherwise (non-root case),
529 * we need to get a ticket for "yyy.", where yyy represents
530 * the name of the person being su'd to, and the instance is null
532 * We should have a way to set the ticket lifetime,
533 * with a system default for root.
535 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
536 (uid == 0 ? "root" : ""), lrealm,
537 "krbtgt", lrealm, DEFAULT_TKT_LIFE, pword);
539 if (kerno != KSUCCESS) {
540 if (kerno == KDC_PR_UNKNOWN) {
541 warnx("kerberos: principal unknown: %s.%s@%s",
542 (uid == 0 ? username : user),
543 (uid == 0 ? "root" : ""), lrealm);
546 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
547 syslog(LOG_NOTICE|LOG_AUTH,
548 "BAD Kerberos SU: %s to %s%s: %s",
549 username, user, ontty(), krb_err_txt[kerno]);
553 if (chown(krbtkfile, uid, -1) < 0) {
555 (void)unlink(krbtkfile);
559 (void)setpriority(PRIO_PROCESS, 0, -2);
561 if (gethostname(hostname, sizeof(hostname)) == -1) {
567 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
568 savehost[sizeof(savehost) - 1] = '\0';
570 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
572 if (kerno == KDC_PR_UNKNOWN) {
573 warnx("Warning: TGT not verified.");
574 syslog(LOG_NOTICE|LOG_AUTH,
575 "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
576 username, user, ontty(), krb_err_txt[kerno],
578 } else if (kerno != KSUCCESS) {
579 warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
580 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
581 username, user, ontty(), krb_err_txt[kerno]);
585 if (!(hp = gethostbyname(hostname))) {
586 warnx("can't get addr of %s", hostname);
590 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr));
592 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
593 &authdata, "")) != KSUCCESS) {
594 warnx("kerberos: unable to verify rcmd ticket: %s\n",
596 syslog(LOG_NOTICE|LOG_AUTH,
597 "failed su: %s to %s%s: %s", username,
598 user, ontty(), krb_err_txt[kerno]);
607 koktologin(name, toname)
612 char realm[REALM_SZ];
614 if (krb_get_lrealm(realm, 1) != KSUCCESS)
617 memset((char *)kdata, 0, sizeof(*kdata));
618 (void)strncpy(kdata->pname, name, sizeof kdata->pname - 1);
619 (void)strncpy(kdata->pinst,
620 ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof kdata->pinst - 1);
621 (void)strncpy(kdata->prealm, realm, sizeof kdata->prealm - 1);
622 return (kuserok(kdata, toname));