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[] =
48 #include <sys/param.h>
50 #include <sys/resource.h>
65 #include <login_cap.h>
73 #include <openssl/des.h>
78 #define ARGSTR "-Kflmc:"
80 #define ARGSTR "-Kflm"
83 static int kerberos(char *username, char *user, int uid, char *pword);
84 static int koktologin(char *name, char *toname);
89 #define ARGSTR "-flmc:"
95 char *ontty __P((void));
96 int chshell __P((char *));
97 static void usage __P((void));
104 extern char **environ;
110 char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np;
114 int asme, ch, asthem, fastlogin, prio, i;
115 enum { UNSET, YES, NO } iscsh = UNSET;
124 char shellbuf[MAXPATHLEN];
129 asme = asthem = fastlogin = 0;
131 while((ch = getopt(argc, argv, ARGSTR)) != -1)
161 user = argv[optind++];
163 if (strlen(user) > MAXLOGNAME - 1) {
164 (void)fprintf(stderr, "su: username too long.\n");
171 if ((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) {
172 errx(1, "malloc failure");
175 nargv[argc + 3] = NULL;
176 for (i = argc; i >= optind; i--)
177 nargv[i + 3] = argv[i];
183 k = auth_getval("auth_list");
184 if (k && !strstr(k, "kerberos"))
188 prio = getpriority(PRIO_PROCESS, 0);
191 (void)setpriority(PRIO_PROCESS, 0, -2);
192 openlog("su", LOG_CONS, 0);
194 /* get current login name and shell */
196 username = getlogin();
197 if (username == NULL || (pwd = getpwnam(username)) == NULL ||
199 pwd = getpwuid(ruid);
201 errx(1, "who are you?");
202 username = strdup(pwd->pw_name);
204 if (username == NULL)
207 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
208 /* copy: pwd memory is recycled */
209 shell = strncpy(shellbuf, pwd->pw_shell, sizeof shellbuf);
210 shellbuf[sizeof shellbuf - 1] = '\0';
212 shell = _PATH_BSHELL;
217 /* get target login information, default to root */
218 if ((pwd = getpwnam(user)) == NULL) {
219 errx(1, "unknown login: %s", user);
223 lc = login_getpwclass(pwd);
226 errx(1, "only root may use -c");
227 lc = login_getclass(class);
229 errx(1, "unknown class: %s", class);
234 targetpass = strdup(pwd->pw_passwd);
239 if (use_kerberos && koktologin(username, user)
241 warnx("kerberos: not in %s's ACL.", user);
247 * Only allow those with pw_gid==0 or those listed in
248 * group zero to su to root. If group zero entry is
249 * missing or empty, then allow anyone to su to root.
250 * iswheelsu will only be set if the user is EXPLICITLY
251 * listed in group zero.
253 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) &&
254 gr->gr_mem && *(gr->gr_mem))
255 for (g = gr->gr_mem;; ++g) {
260 errx(1, "you are not in the correct group to su %s.", user);
262 if (strcmp(username, *g) == 0) {
270 /* if target requires a password, verify it */
271 if (*pwd->pw_passwd) {
275 pwd = getpwnam(username);
278 p = skey_getpass("Password:", pwd, 1);
279 if (!(!strcmp(pwd->pw_passwd, skey_crypt(p, pwd->pw_passwd, pwd, 1))
281 || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass)))
285 p = getpass("Password:");
286 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
289 if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p)))
292 fprintf(stderr, "Sorry\n");
293 syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty());
299 pwd = getpwnam(user);
303 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) {
304 fprintf(stderr, "Sorry - account expired\n");
305 syslog(LOG_AUTH|LOG_WARNING,
306 "BAD SU %s to %s%s", username,
313 /* if asme and non-standard target shell, must be root */
314 if (!chshell(pwd->pw_shell) && ruid)
315 errx(1, "permission denied (shell).");
316 } else if (pwd->pw_shell && *pwd->pw_shell) {
317 shell = pwd->pw_shell;
320 shell = _PATH_BSHELL;
324 /* if we're forking a csh, we want to slightly muck the args */
325 if (iscsh == UNSET) {
326 p = strrchr(shell, '/');
331 if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO)
332 iscsh = strcmp(p, "tcsh") ? NO : YES;
335 (void)setpriority(PRIO_PROCESS, 0, prio);
338 /* Set everything now except the environment & umask */
339 setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
341 * Don't touch resource/priority settings if -m has been
342 * used or -l and -c hasn't, and we're not su'ing to root.
344 if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
345 setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES);
346 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
347 err(1, "setusercontext");
349 /* set permissions */
350 if (setgid(pwd->pw_gid) < 0)
352 if (initgroups(user, pwd->pw_gid))
353 errx(1, "initgroups failed");
354 if (setuid(pwd->pw_uid) < 0)
362 k = getenv("KRBTKFILE");
364 if ((cleanenv = calloc(20, sizeof(char*))) == NULL)
369 /* set the su'd user's environment & umask */
370 setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
372 (void)setenv("PATH", _PATH_DEFPATH, 1);
375 (void)setenv("TERM", p, 1);
378 (void)setenv("KRBTKFILE", k, 1);
380 if (chdir(pwd->pw_dir) < 0)
381 errx(1, "no directory");
383 if (asthem || pwd->pw_uid)
384 (void)setenv("USER", pwd->pw_name, 1);
385 (void)setenv("HOME", pwd->pw_dir, 1);
386 (void)setenv("SHELL", shell, 1);
395 /* csh strips the first character... */
396 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
399 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
400 username, user, ontty());
411 (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR);
423 while (!r && (cp = getusershell()) != NULL)
424 r = strcmp(cp, sh) == 0;
433 static char buf[MAXPATHLEN + 4];
436 p = ttyname(STDERR_FILENO);
438 snprintf(buf, sizeof(buf), " on %s", p);
444 kerberos(username, user, uid, pword)
445 char *username, *user;
453 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
454 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
455 char *krb_get_phost();
458 if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
460 (void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user,
461 (unsigned long)getuid());
463 (void)setenv("KRBTKFILE", krbtkfile, 1);
464 (void)krb_set_tkt_string(krbtkfile);
466 * Set real as well as effective ID to 0 for the moment,
467 * to make the kerberos library do the right thing.
475 * Little trick here -- if we are su'ing to root,
476 * we need to get a ticket for "xxx.root", where xxx represents
477 * the name of the person su'ing. Otherwise (non-root case),
478 * we need to get a ticket for "yyy.", where yyy represents
479 * the name of the person being su'd to, and the instance is null
481 * We should have a way to set the ticket lifetime,
482 * with a system default for root.
484 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
485 (uid == 0 ? "root" : ""), lrealm,
486 "krbtgt", lrealm, DEFAULT_TKT_LIFE, pword);
488 if (kerno != KSUCCESS) {
489 if (kerno == KDC_PR_UNKNOWN) {
490 warnx("kerberos: principal unknown: %s.%s@%s",
491 (uid == 0 ? username : user),
492 (uid == 0 ? "root" : ""), lrealm);
495 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
496 syslog(LOG_NOTICE|LOG_AUTH,
497 "BAD Kerberos SU: %s to %s%s: %s",
498 username, user, ontty(), krb_err_txt[kerno]);
502 if (chown(krbtkfile, uid, -1) < 0) {
504 (void)unlink(krbtkfile);
508 (void)setpriority(PRIO_PROCESS, 0, -2);
510 if (gethostname(hostname, sizeof(hostname)) == -1) {
516 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
517 savehost[sizeof(savehost) - 1] = '\0';
519 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
521 if (kerno == KDC_PR_UNKNOWN) {
522 warnx("Warning: TGT not verified.");
523 syslog(LOG_NOTICE|LOG_AUTH,
524 "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
525 username, user, ontty(), krb_err_txt[kerno],
527 } else if (kerno != KSUCCESS) {
528 warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
529 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
530 username, user, ontty(), krb_err_txt[kerno]);
534 if (!(hp = gethostbyname(hostname))) {
535 warnx("can't get addr of %s", hostname);
539 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr));
541 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
542 &authdata, "")) != KSUCCESS) {
543 warnx("kerberos: unable to verify rcmd ticket: %s\n",
545 syslog(LOG_NOTICE|LOG_AUTH,
546 "failed su: %s to %s%s: %s", username,
547 user, ontty(), krb_err_txt[kerno]);
556 koktologin(name, toname)
561 char realm[REALM_SZ];
563 if (krb_get_lrealm(realm, 1) != KSUCCESS)
566 memset((char *)kdata, 0, sizeof(*kdata));
567 (void)strncpy(kdata->pname, name, sizeof kdata->pname - 1);
568 (void)strncpy(kdata->pinst,
569 ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof kdata->pinst - 1);
570 (void)strncpy(kdata->prealm, realm, sizeof kdata->prealm - 1);
571 return (kuserok(kdata, toname));