xref: /dragonfly/usr.bin/su/su.c (revision 1de703da)
1 /*
2  * Copyright (c) 1988, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
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  * @(#) Copyright (c) 1988, 1993, 1994 The Regents of the University of California.  All rights reserved.
34  * @(#)su.c	8.3 (Berkeley) 4/2/94
35  * $FreeBSD: src/usr.bin/su/su.c,v 1.34.2.4 2002/06/16 21:04:15 nectar Exp $
36  * $DragonFly: src/usr.bin/su/su.c,v 1.2 2003/06/17 04:29:32 dillon Exp $
37  */
38 
39 #include <sys/param.h>
40 #include <sys/time.h>
41 #include <sys/resource.h>
42 
43 #include <err.h>
44 #include <errno.h>
45 #include <grp.h>
46 #include <paths.h>
47 #include <pwd.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <syslog.h>
52 #include <unistd.h>
53 #include <libutil.h>
54 
55 #ifdef LOGIN_CAP
56 #include <login_cap.h>
57 #endif
58 
59 #ifdef	SKEY
60 #include <skey.h>
61 #endif
62 
63 #ifdef KERBEROS5
64 #include <krb5.h>
65 
66 static long get_su_principal(krb5_context context, const char *target_user,
67     const char *current_user, char **su_principal_name,
68     krb5_principal *su_principal);
69 static long kerberos5(krb5_context context, const char *current_user,
70     const char *target_user, krb5_principal su_principal,
71     const char *pass);
72 
73 int use_kerberos5 = 1;
74 #endif
75 
76 #ifdef KERBEROS4
77 #include <openssl/des.h>
78 #include <krb.h>
79 #include <netdb.h>
80 
81 
82 static int kerberos4(char *username, char *user, int uid, char *pword);
83 static int koktologin(char *name, char *toname);
84 
85 int use_kerberos4 = 1;
86 #endif /* KERBEROS4 */
87 
88 #ifdef LOGIN_CAP
89 #define LOGIN_CAP_ARG(x) x
90 #else
91 #define LOGIN_CAP_ARG(x)
92 #endif
93 #if defined(KERBEROS4) || defined(KERBEROS5)
94 #define KERBEROS_ARG(x) x
95 #else
96 #define KERBEROS_ARG(x)
97 #endif
98 #define COMMON_ARG(x) x
99 #define ARGSTR	"-" COMMON_ARG("flm") LOGIN_CAP_ARG("c:") KERBEROS_ARG("K")
100 
101 char   *ontty __P((void));
102 int	chshell __P((char *));
103 static void usage __P((void));
104 
105 int
106 main(argc, argv)
107 	int argc;
108 	char **argv;
109 {
110 	extern char **environ;
111 	struct passwd *pwd;
112 #ifdef WHEELSU
113 	char *targetpass;
114 	int iswheelsu;
115 #endif /* WHEELSU */
116 	char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np;
117 	struct group *gr;
118 	uid_t ruid;
119 	gid_t gid;
120 	int asme, ch, asthem, fastlogin, prio, i;
121 	enum { UNSET, YES, NO } iscsh = UNSET;
122 #ifdef LOGIN_CAP
123 	login_cap_t *lc;
124 	char *class=NULL;
125 	int setwhat;
126 #endif
127 #if defined(KERBEROS4) || defined(KERBEROS5)
128 	char *k;
129 #endif
130 #ifdef KERBEROS5
131 	char *su_principal_name, *ccname;
132 	krb5_context context;
133 	krb5_principal su_principal;
134 #endif
135 	char shellbuf[MAXPATHLEN];
136 
137 #ifdef WHEELSU
138 	iswheelsu =
139 #endif /* WHEELSU */
140 	asme = asthem = fastlogin = 0;
141 	user = "root";
142 	while((ch = getopt(argc, argv, ARGSTR)) != -1)
143 		switch((char)ch) {
144 #if defined(KERBEROS4) || defined(KERBEROS5)
145 		case 'K':
146 #ifdef KERBEROS4
147 			use_kerberos4 = 0;
148 #endif
149 #ifdef KERBEROS5
150 			use_kerberos5 = 0;
151 #endif
152 			break;
153 #endif
154 		case 'f':
155 			fastlogin = 1;
156 			break;
157 		case '-':
158 		case 'l':
159 			asme = 0;
160 			asthem = 1;
161 			break;
162 		case 'm':
163 			asme = 1;
164 			asthem = 0;
165 			break;
166 #ifdef LOGIN_CAP
167 		case 'c':
168 			class = optarg;
169 			break;
170 #endif
171 		case '?':
172 		default:
173 			usage();
174 		}
175 
176 	if (optind < argc)
177 		user = argv[optind++];
178 
179 	if (strlen(user) > MAXLOGNAME - 1) {
180 		(void)fprintf(stderr, "su: username too long.\n");
181 		exit(1);
182 	}
183 
184 	if (user == NULL)
185 		usage();
186 
187 	if ((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) {
188 	    errx(1, "malloc failure");
189 	}
190 
191 	nargv[argc + 3] = NULL;
192 	for (i = argc; i >= optind; i--)
193 	    nargv[i + 3] = argv[i];
194 	np = &nargv[i + 3];
195 
196 	argv += optind;
197 
198 #if defined(KERBEROS4) || defined(KERBEROS5)
199 	k = auth_getval("auth_list");
200 	if (k && !strstr(k, "kerberos")) {
201 #ifdef KERBEROS4
202 	    use_kerberos4 = 0;
203 #endif
204 #ifdef KERBEROS5
205 	    use_kerberos5 = 0;
206 #endif
207 	}
208 #endif
209 #ifdef KERBEROS5
210 	su_principal_name = NULL;
211 	su_principal = NULL;
212 	if (krb5_init_context(&context) != 0)
213 		use_kerberos5 = 0;
214 #endif
215 	errno = 0;
216 	prio = getpriority(PRIO_PROCESS, 0);
217 	if (errno)
218 		prio = 0;
219 	(void)setpriority(PRIO_PROCESS, 0, -2);
220 	openlog("su", LOG_CONS, 0);
221 
222 	/* get current login name and shell */
223 	ruid = getuid();
224 	username = getlogin();
225 	if (username == NULL || (pwd = getpwnam(username)) == NULL ||
226 	    pwd->pw_uid != ruid)
227 		pwd = getpwuid(ruid);
228 	if (pwd == NULL)
229 		errx(1, "who are you?");
230 	username = strdup(pwd->pw_name);
231 	gid = pwd->pw_gid;
232 	if (username == NULL)
233 		err(1, NULL);
234 	if (asme) {
235 		if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
236 			/* copy: pwd memory is recycled */
237 			shell = strncpy(shellbuf,  pwd->pw_shell, sizeof shellbuf);
238 			shellbuf[sizeof shellbuf - 1] = '\0';
239 		} else {
240 			shell = _PATH_BSHELL;
241 			iscsh = NO;
242 		}
243 	}
244 
245 	/* get target login information, default to root */
246 	if ((pwd = getpwnam(user)) == NULL) {
247 		errx(1, "unknown login: %s", user);
248 	}
249 #ifdef LOGIN_CAP
250 	if (class==NULL) {
251 		lc = login_getpwclass(pwd);
252 	} else {
253 		if (ruid)
254 			errx(1, "only root may use -c");
255 		lc = login_getclass(class);
256 		if (lc == NULL)
257 			errx(1, "unknown class: %s", class);
258 	}
259 #endif
260 
261 #ifdef WHEELSU
262 	targetpass = strdup(pwd->pw_passwd);
263 #endif /* WHEELSU */
264 
265 	if (ruid) {
266 #ifdef KERBEROS4
267 		if (use_kerberos4 && koktologin(username, user)
268 		    && !pwd->pw_uid) {
269 			warnx("kerberos4: not in %s's ACL.", user);
270 			use_kerberos4 = 0;
271 		}
272 #endif
273 #ifdef KERBEROS5
274 		if (use_kerberos5) {
275 			if (get_su_principal(context, user, username,
276 			    &su_principal_name, &su_principal) != 0 ||
277 			    !krb5_kuserok(context, su_principal, user)) {
278 				warnx("kerberos5: not in %s's ACL.", user);
279 				use_kerberos5 = 0;
280 			}
281 		}
282 #endif
283 		{
284 			/*
285 			 * Only allow those with pw_gid==0 or those listed in
286 			 * group zero to su to root.  If group zero entry is
287 			 * missing or empty, then allow anyone to su to root.
288 			 * iswheelsu will only be set if the user is EXPLICITLY
289 			 * listed in group zero.
290 			 */
291 			if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) &&
292 			    gr->gr_mem && *(gr->gr_mem))
293 				for (g = gr->gr_mem;; ++g) {
294 					if (!*g) {
295 						if (gid == 0)
296 							break;
297 						else
298 							errx(1,
299 			     "you are not in the correct group (%s) to su %s.",
300 							    gr->gr_name,
301 							    user);
302 					}
303 					if (strcmp(username, *g) == 0) {
304 #ifdef WHEELSU
305 						iswheelsu = 1;
306 #endif /* WHEELSU */
307 						break;
308 					}
309 				}
310 		}
311 		/* if target requires a password, verify it */
312 		if (*pwd->pw_passwd) {
313 #ifdef	SKEY
314 #ifdef WHEELSU
315 			if (iswheelsu) {
316 				pwd = getpwnam(username);
317 			}
318 #endif /* WHEELSU */
319 			p = skey_getpass("Password:", pwd, 1);
320 			if (!(!strcmp(pwd->pw_passwd, skey_crypt(p, pwd->pw_passwd, pwd, 1))
321 #ifdef WHEELSU
322 			      || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass)))
323 #endif /* WHEELSU */
324 			      )) {
325 #else
326 			p = getpass("Password:");
327 			if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
328 #endif
329 #ifdef KERBEROS4
330 				if (use_kerberos4 && kerberos4(username, user,
331 				    pwd->pw_uid, p) == 0)
332 					goto authok;
333 #endif
334 #ifdef KERBEROS5
335 				if (use_kerberos5 && kerberos5(context,
336 				    username, user, su_principal, p) == 0)
337 					goto authok;
338 #endif
339 				fprintf(stderr, "Sorry\n");
340 				syslog(LOG_AUTH|LOG_WARNING,
341 				    "BAD SU %s to %s%s", username, user,
342 				    ontty());
343 				exit(1);
344 			}
345 #if defined(KERBEROS4) || defined(KERBEROS5)
346 		authok:
347 #endif
348 #ifdef WHEELSU
349 			if (iswheelsu) {
350 				pwd = getpwnam(user);
351 			}
352 #endif /* WHEELSU */
353 		}
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,
358 				user, ontty());
359 			exit(1);
360 		}
361 	}
362 
363 	if (asme) {
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;
369 		iscsh = UNSET;
370 	} else {
371 		shell = _PATH_BSHELL;
372 		iscsh = NO;
373 	}
374 
375 	/* if we're forking a csh, we want to slightly muck the args */
376 	if (iscsh == UNSET) {
377 		p = strrchr(shell, '/');
378 		if (p)
379 			++p;
380 		else
381 			p = shell;
382 		if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO)
383 		    iscsh = strcmp(p, "tcsh") ? NO : YES;
384 	}
385 
386 	(void)setpriority(PRIO_PROCESS, 0, prio);
387 
388 #ifdef LOGIN_CAP
389 	/* Set everything now except the environment & umask */
390 	setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
391 	/*
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.
394 	 */
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");
399 #else
400 	/* set permissions */
401 	if (setgid(pwd->pw_gid) < 0)
402 		err(1, "setgid");
403 	if (initgroups(user, pwd->pw_gid))
404 		errx(1, "initgroups failed");
405 	if (setuid(pwd->pw_uid) < 0)
406 		err(1, "setuid");
407 #endif
408 
409 	if (!asme) {
410 		if (asthem) {
411 			p = getenv("TERM");
412 #ifdef KERBEROS4
413 			k = getenv("KRBTKFILE");
414 #endif
415 #ifdef KERBEROS5
416 			ccname = getenv("KRB5CCNAME");
417 #endif
418 			if ((cleanenv = calloc(20, sizeof(char*))) == NULL)
419 				errx(1, "calloc");
420 			cleanenv[0] = NULL;
421 			environ = cleanenv;
422 #ifdef LOGIN_CAP
423 			/* set the su'd user's environment & umask */
424 			setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
425 #else
426 			(void)setenv("PATH", _PATH_DEFPATH, 1);
427 #endif
428 			if (p)
429 				(void)setenv("TERM", p, 1);
430 #ifdef KERBEROS4
431 			if (k)
432 				(void)setenv("KRBTKFILE", k, 1);
433 #endif
434 #ifdef KERBEROS5
435 			if (ccname)
436 				(void)setenv("KRB5CCNAME", ccname, 1);
437 #endif
438 			if (chdir(pwd->pw_dir) < 0)
439 				errx(1, "no directory");
440 		}
441 		if (asthem || pwd->pw_uid)
442 			(void)setenv("USER", pwd->pw_name, 1);
443 		(void)setenv("HOME", pwd->pw_dir, 1);
444 		(void)setenv("SHELL", shell, 1);
445 	}
446 	if (iscsh == YES) {
447 		if (fastlogin)
448 			*np-- = "-f";
449 		if (asme)
450 			*np-- = "-m";
451 	}
452 
453 	/* csh strips the first character... */
454 	*np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
455 
456 	if (ruid != 0)
457 		syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
458 		    username, user, ontty());
459 
460 #ifdef LOGIN_CAP
461 	login_close(lc);
462 #endif
463 
464 	execv(shell, np);
465 	err(1, "%s", shell);
466 }
467 
468 static void
469 usage()
470 {
471 	(void)fprintf(stderr, "usage: su [-] [-%s] %s[login [args]]\n",
472 	    KERBEROS_ARG("K") COMMON_ARG("flm"),
473 #ifdef LOGIN_CAP
474 	    "[-c class] "
475 #else
476 	    ""
477 #endif
478 	    );
479 	exit(1);
480 }
481 
482 int
483 chshell(sh)
484 	char *sh;
485 {
486 	int  r = 0;
487 	char *cp;
488 
489 	setusershell();
490 	while (!r && (cp = getusershell()) != NULL)
491 		r = strcmp(cp, sh) == 0;
492 	endusershell();
493 	return r;
494 }
495 
496 char *
497 ontty()
498 {
499 	char *p;
500 	static char buf[MAXPATHLEN + 4];
501 
502 	buf[0] = 0;
503 	p = ttyname(STDERR_FILENO);
504 	if (p)
505 		snprintf(buf, sizeof(buf), " on %s", p);
506 	return (buf);
507 }
508 
509 #ifdef KERBEROS5
510 const char superuser[] = "root";
511 
512 /* Authenticate using Kerberos 5.
513  *   context           -- An initialized krb5_context.
514  *   current_user      -- The current username.
515  *   target_user       -- The target account name.
516  *   su_principal      -- The target krb5_principal.
517  *   pass              -- The user's password.
518  * Note that a valid keytab in the default location with a host entry
519  * must be available.
520  * Returns 0 if authentication was successful, or a com_err error code if
521  * it was not.
522  */
523 static long
524 kerberos5(krb5_context context, const char *current_user,
525     const char *target_user, krb5_principal su_principal,
526     const char *pass)
527 {
528 	krb5_creds	 creds;
529 	krb5_get_init_creds_opt gic_opt;
530 	krb5_verify_init_creds_opt vic_opt;
531 	long		 rv;
532 
533 	krb5_get_init_creds_opt_init(&gic_opt);
534 	krb5_verify_init_creds_opt_init(&vic_opt);
535 	rv = krb5_get_init_creds_password(context, &creds, su_principal,
536 	    pass, NULL, NULL, 0, NULL, &gic_opt);
537 	if (rv != 0) {
538 		syslog(LOG_NOTICE|LOG_AUTH, "BAD Kerberos5 SU: %s to %s%s: %s",
539 		    current_user, target_user, ontty(),
540 		    krb5_get_err_text(context, rv));
541 		return (rv);
542 	}
543 	krb5_verify_init_creds_opt_set_ap_req_nofail(&vic_opt, 1);
544 	rv = krb5_verify_init_creds(context, &creds, NULL, NULL, NULL,
545 	    &vic_opt);
546 	krb5_free_cred_contents(context, &creds);
547 	if (rv != 0) {
548 		syslog(LOG_NOTICE|LOG_AUTH, "BAD Kerberos5 SU: %s to %s%s: %s",
549 		    current_user, target_user, ontty(),
550 		    krb5_get_err_text(context, rv));
551 		return (rv);
552 	}
553 	return (0);
554 }
555 
556 /* Determine the target principal given the current user and the target user.
557  *   context           -- An initialized krb5_context.
558  *   target_user       -- The target username.
559  *   current_user      -- The current username.
560  *   su_principal_name -- (out) The target principal name.
561  *   su_principal      -- (out) The target krb5_principal.
562  *
563  * When target_user is `root', the su_principal will be a `root
564  * instance', e.g. `luser/root@REA.LM'.  Otherwise, the su_principal
565  * will simply be the current user's default principal name.  Note that
566  * in any case, if KRB5CCNAME is set and a credentials cache exists, the
567  * principal name found there will be the `starting point', rather than
568  * the current_user parameter.
569  *
570  * Returns 0 for success, or a com_err error code on failure.
571  */
572 static long
573 get_su_principal(krb5_context context, const char *target_user,
574     const char *current_user, char **su_principal_name,
575     krb5_principal *su_principal)
576 {
577 	krb5_principal	 default_principal;
578 	krb5_ccache	 ccache;
579 	char		*principal_name, *ccname, *p;
580 	long		 rv;
581 	uid_t		 euid, ruid;
582 
583 	*su_principal = NULL;
584 	default_principal = NULL;
585 	/* Lower privs while messing about with the credentials
586 	 * cache.
587 	 */
588 	ruid = getuid();
589 	euid = geteuid();
590 	rv = seteuid(getuid());
591 	if (rv != 0)
592 		return (errno);
593 	p = getenv("KRB5CCNAME");
594 	if (p != NULL)
595 		ccname = strdup(p);
596 	else
597 		(void)asprintf(&ccname, "%s%lu", KRB5_DEFAULT_CCROOT,
598 		    (unsigned long)ruid);
599 	if (ccname == NULL)
600 		return (errno);
601 	rv = krb5_cc_resolve(context, ccname, &ccache);
602 	free(ccname);
603 	if (rv == 0) {
604 		rv = krb5_cc_get_principal(context, ccache,
605 		    &default_principal);
606 		krb5_cc_close(context, ccache);
607 		if (rv != 0)
608 			default_principal = NULL; /* just to be safe */
609 	}
610 	rv = seteuid(euid);
611 	if (rv != 0)
612 		return (errno);
613 	if (default_principal == NULL) {
614 		rv = krb5_make_principal(context, &default_principal, NULL,
615 		    current_user, NULL);
616 		if (rv != 0) {
617 			warnx("Could not determine default principal name.");
618 			return (rv);
619 		}
620 	}
621 	/* Now that we have some principal, if the target account is
622 	 * `root', then transform it into a `root' instance, e.g.
623 	 * `user@REA.LM' -> `user/root@REA.LM'.
624 	 */
625 	rv = krb5_unparse_name(context, default_principal, &principal_name);
626 	krb5_free_principal(context, default_principal);
627 	if (rv != 0) {
628 		warnx("krb5_unparse_name: %s", krb5_get_err_text(context, rv));
629 		return (rv);
630 	}
631 	if (strcmp(target_user, superuser) == 0) {
632 		p = strrchr(principal_name, '@');
633 		if (p == NULL) {
634 			warnx("malformed principal name `%s'", principal_name);
635 			free(principal_name);
636 			return (rv);
637 		}
638 		*p++ = '\0';
639 		(void)asprintf(su_principal_name, "%s/%s@%s", principal_name,
640 		    superuser, p);
641 		free(principal_name);
642 	} else
643 		*su_principal_name = principal_name;
644 	if (*su_principal_name == NULL)
645 		return errno;
646 	rv = krb5_parse_name(context, *su_principal_name, &default_principal);
647 	if (rv != 0) {
648 		warnx("krb5_parse_name `%s': %s", *su_principal_name,
649 		    krb5_get_err_text(context, rv));
650 		free(*su_principal_name);
651 		return (rv);
652 	}
653 	*su_principal = default_principal;
654 	return 0;
655 }
656 
657 #endif
658 
659 #ifdef KERBEROS4
660 int
661 kerberos4(username, user, uid, pword)
662 	char *username, *user;
663 	int uid;
664 	char *pword;
665 {
666 	KTEXT_ST ticket;
667 	AUTH_DAT authdata;
668 	int kerno;
669 	u_long faddr;
670 	char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
671 	char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
672 	char *krb_get_phost();
673 	struct hostent *hp;
674 
675 	if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
676 		return (1);
677 	(void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user,
678 	    (unsigned long)getuid());
679 
680 	(void)setenv("KRBTKFILE", krbtkfile, 1);
681 	(void)krb_set_tkt_string(krbtkfile);
682 	/*
683 	 * Set real as well as effective ID to 0 for the moment,
684 	 * to make the kerberos library do the right thing.
685 	 */
686 	if (setuid(0) < 0) {
687 		warn("setuid");
688 		return (1);
689 	}
690 
691 	/*
692 	 * Little trick here -- if we are su'ing to root,
693 	 * we need to get a ticket for "xxx.root", where xxx represents
694 	 * the name of the person su'ing.  Otherwise (non-root case),
695 	 * we need to get a ticket for "yyy.", where yyy represents
696 	 * the name of the person being su'd to, and the instance is null
697 	 *
698 	 * We should have a way to set the ticket lifetime,
699 	 * with a system default for root.
700 	 */
701 	kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
702 		(uid == 0 ? "root" : ""), lrealm,
703 	    	"krbtgt", lrealm, DEFAULT_TKT_LIFE, pword);
704 
705 	if (kerno != KSUCCESS) {
706 		if (kerno == KDC_PR_UNKNOWN) {
707 			warnx("kerberos4: principal unknown: %s.%s@%s",
708 				(uid == 0 ? username : user),
709 				(uid == 0 ? "root" : ""), lrealm);
710 			return (1);
711 		}
712 		warnx("kerberos4: unable to su: %s", krb_err_txt[kerno]);
713 		syslog(LOG_NOTICE|LOG_AUTH,
714 		    "BAD Kerberos4 SU: %s to %s%s: %s",
715 		    username, user, ontty(), krb_err_txt[kerno]);
716 		return (1);
717 	}
718 
719 	if (chown(krbtkfile, uid, -1) < 0) {
720 		warn("chown");
721 		(void)unlink(krbtkfile);
722 		return (1);
723 	}
724 
725 	(void)setpriority(PRIO_PROCESS, 0, -2);
726 
727 	if (gethostname(hostname, sizeof(hostname)) == -1) {
728 		warn("gethostname");
729 		dest_tkt();
730 		return (1);
731 	}
732 
733 	(void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
734 	savehost[sizeof(savehost) - 1] = '\0';
735 
736 	kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
737 
738 	if (kerno == KDC_PR_UNKNOWN) {
739 		warnx("Warning: TGT not verified.");
740 		syslog(LOG_NOTICE|LOG_AUTH,
741 		    "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
742 		    username, user, ontty(), krb_err_txt[kerno],
743 		    "rcmd", savehost);
744 	} else if (kerno != KSUCCESS) {
745 		warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
746 		syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
747 		    username, user, ontty(), krb_err_txt[kerno]);
748 		dest_tkt();
749 		return (1);
750 	} else {
751 		if (!(hp = gethostbyname(hostname))) {
752 			warnx("can't get addr of %s", hostname);
753 			dest_tkt();
754 			return (1);
755 		}
756 		memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr));
757 
758 		if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
759 		    &authdata, "")) != KSUCCESS) {
760 			warnx("kerberos4: unable to verify rcmd ticket: %s\n",
761 			    krb_err_txt[kerno]);
762 			syslog(LOG_NOTICE|LOG_AUTH,
763 			    "failed su: %s to %s%s: %s", username,
764 			     user, ontty(), krb_err_txt[kerno]);
765 			dest_tkt();
766 			return (1);
767 		}
768 	}
769 	return (0);
770 }
771 
772 int
773 koktologin(name, toname)
774 	char *name, *toname;
775 {
776 	AUTH_DAT *kdata;
777 	AUTH_DAT kdata_st;
778 	char realm[REALM_SZ];
779 
780 	if (krb_get_lrealm(realm, 1) != KSUCCESS)
781 		return (1);
782 	kdata = &kdata_st;
783 	memset((char *)kdata, 0, sizeof(*kdata));
784 	(void)strncpy(kdata->pname, name, sizeof kdata->pname - 1);
785 	(void)strncpy(kdata->pinst,
786 	    ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof kdata->pinst - 1);
787 	(void)strncpy(kdata->prealm, realm, sizeof kdata->prealm - 1);
788 	return (kuserok(kdata, toname));
789 }
790 #endif
791