75bee69036a179f65d7b8452c5679c9c48e5d47e
[dragonfly.git] / usr.bin / su / su.c
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