su.c revision 1.61 1 /* $NetBSD: su.c,v 1.61 2005/01/08 18:12:35 christos Exp $ */
2
3 /*
4 * Copyright (c) 1988 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT(
35 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
36 All rights reserved.\n");
37 #endif /* not lint */
38
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";*/
42 #else
43 __RCSID("$NetBSD: su.c,v 1.61 2005/01/08 18:12:35 christos Exp $");
44 #endif
45 #endif /* not lint */
46
47 #include <sys/param.h>
48 #include <sys/time.h>
49 #ifdef USE_PAM
50 #include <sys/wait.h>
51 #endif
52 #include <sys/resource.h>
53 #include <err.h>
54 #include <errno.h>
55 #include <grp.h>
56 #include <paths.h>
57 #include <pwd.h>
58 #include <stdio.h>
59 #ifdef SKEY
60 #include <skey.h>
61 #endif
62 #include <stdlib.h>
63 #include <string.h>
64 #include <syslog.h>
65 #include <time.h>
66 #include <tzfile.h>
67 #include <unistd.h>
68
69 #ifdef USE_PAM
70 #include <security/pam_appl.h>
71 #include <security/openpam.h> /* for openpam_ttyconv() */
72
73 static pam_handle_t *pamh;
74 static struct pam_conv pamc;
75
76 #define fatal(x) do { \
77 if (pam_err == PAM_SUCCESS) \
78 pam_end(pamh, pam_err); \
79 err x; \
80 } while (/*CONSTCOND*/ 0)
81 #define fatalx(x) do { \
82 if (pam_err == PAM_SUCCESS) \
83 pam_end(pamh, pam_err); \
84 errx x; \
85 } while (/*CONSTCOND*/ 0)
86
87 #else
88 #define fatal(x) err x
89 #define fatalx(x) errx x
90
91 #endif
92
93 #ifdef LOGIN_CAP
94 #include <login_cap.h>
95 #endif
96
97 #ifdef KERBEROS
98 #include <des.h>
99 #include <krb.h>
100 #include <netdb.h>
101
102 static int kerberos __P((char *, char *, int));
103 static int koktologin __P((char *, char *, char *));
104
105 #endif
106
107 #ifdef KERBEROS5
108 #include <krb5.h>
109
110 static int kerberos5 __P((char *, char *, int));
111
112 #endif
113
114 #if defined(KERBEROS) || defined(KERBEROS5)
115
116 #define ARGSTRX "-Kdflm"
117
118 int use_kerberos = 1;
119
120 #else
121 #define ARGSTRX "-dflm"
122 #endif
123
124 #ifndef SU_GROUP
125 #define SU_GROUP "wheel"
126 #endif
127
128 #ifdef LOGIN_CAP
129 #define ARGSTR ARGSTRX "c:"
130 #else
131 #define ARGSTR ARGSTRX
132 #endif
133
134 int main __P((int, char **));
135
136 static int chshell __P((const char *));
137 static char *ontty __P((void));
138 static int check_ingroup __P((int, const char *, const char *, int));
139
140
141 int
142 main(argc, argv)
143 int argc;
144 char **argv;
145 {
146 extern char **environ;
147 struct passwd *pwd;
148 char *p;
149 #ifdef BSD4_4
150 struct timeval tp;
151 #endif
152 uid_t ruid;
153 int asme, ch, asthem, fastlogin, prio, gohome;
154 enum { UNSET, YES, NO } iscsh = UNSET;
155 char *user, *shell, *avshell, *username, **np;
156 char *userpass, *class;
157 char shellbuf[MAXPATHLEN], avshellbuf[MAXPATHLEN];
158 time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
159 #ifdef LOGIN_CAP
160 login_cap_t *lc;
161 #endif
162 #ifdef USE_PAM
163 char **pam_envlist, **pam_env;
164 char hostname[MAXHOSTNAMELEN];
165 const char **usernamep;
166 char *tty;
167 int pam_err, status;
168 pid_t pid;
169 #ifdef notdef
170 extern int _openpam_debug;
171 _openpam_debug = 1;
172 #endif
173 #endif
174
175 asme = asthem = fastlogin = 0;
176 gohome = 1;
177 shell = class = NULL;
178 while ((ch = getopt(argc, argv, ARGSTR)) != -1)
179 switch((char)ch) {
180 #if defined(KERBEROS) || defined(KERBEROS5)
181 case 'K':
182 use_kerberos = 0;
183 break;
184 #endif
185 #ifdef LOGIN_CAP
186 case 'c':
187 class = optarg;
188 break;
189 #endif
190 case 'd':
191 asme = 0;
192 asthem = 1;
193 gohome = 0;
194 break;
195 case 'f':
196 fastlogin = 1;
197 break;
198 case '-':
199 case 'l':
200 asme = 0;
201 asthem = 1;
202 break;
203 case 'm':
204 asme = 1;
205 asthem = 0;
206 break;
207 case '?':
208 default:
209 (void)fprintf(stderr,
210 "usage: %s [%s] [login [shell arguments]]\n",
211 getprogname(), ARGSTR);
212 exit(1);
213 }
214 argv += optind;
215
216 /* Lower the priority so su runs faster */
217 errno = 0;
218 prio = getpriority(PRIO_PROCESS, 0);
219 if (errno)
220 prio = 0;
221 if (prio > -2)
222 (void)setpriority(PRIO_PROCESS, 0, -2);
223 openlog("su", 0, LOG_AUTH);
224
225 /* get current login name and shell */
226 ruid = getuid();
227 username = getlogin();
228 /* get target login information, default to root */
229 user = *argv ? *argv : "root";
230
231 #ifdef USE_PAM
232 pamc.conv = &openpam_ttyconv;
233 pam_start("su", user, &pamc, &pamh);
234
235 /* Fill in hostname, username and tty */
236 if ((pam_err = pam_set_item(pamh, PAM_RUSER, username)) != PAM_SUCCESS)
237 goto pam_failed;
238
239 gethostname(hostname, sizeof(hostname));
240 if ((pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS)
241 goto pam_failed;
242
243 tty = ttyname(STDERR_FILENO);
244 if ((pam_err = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS)
245 goto pam_failed;
246
247 /* authentication */
248 if ((pam_err = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
249 goto pam_failed;
250
251 if ((pam_err = pam_acct_mgmt(pamh, 0)) == PAM_NEW_AUTHTOK_REQD)
252 pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
253 if (pam_err != PAM_SUCCESS)
254 goto pam_failed;
255
256 /* Get user credentials */
257 if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
258 goto pam_failed;
259
260 /* Open the session */
261 if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
262 goto pam_failed;
263
264 /* New user name? */
265 usernamep = (const char **)&username;
266 pam_err = pam_get_item(pamh, PAM_USER, (const void **)usernamep);
267 if (pam_err != PAM_SUCCESS)
268 goto pam_failed;
269
270 pam_failed:
271 if (pam_err != PAM_SUCCESS) {
272 warnx("PAM failed, fallback to plain old authentication");
273 pam_end(pamh, pam_err);
274 username = getlogin();
275 }
276
277 /*
278 * Now any failure should use fatal/fatalx instead of err/errx
279 * so that pam_end() is called on errors.
280 */
281 #endif
282 if (username == NULL || (pwd = getpwnam(username)) == NULL ||
283 pwd->pw_uid != ruid)
284 pwd = getpwuid(ruid);
285 if (pwd == NULL)
286 fatalx((1, "who are you?"));
287
288 username = strdup(pwd->pw_name);
289 userpass = strdup(pwd->pw_passwd);
290 if (username == NULL || userpass == NULL)
291 fatal((1, "strdup"));
292
293 #ifdef USE_PAM
294 /* Get PAM environment */
295 if ((pam_err == PAM_SUCCESS) &&
296 ((pam_envlist = pam_getenvlist(pamh)) != NULL)) {
297 for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
298 putenv(*pam_env);
299 free(*pam_env);
300 }
301 free(pam_envlist);
302 }
303 #endif
304
305 if (asme) {
306 if (pwd->pw_shell && *pwd->pw_shell) {
307 strlcpy(shellbuf, pwd->pw_shell, sizeof(shellbuf));
308 shell = shellbuf;
309 } else {
310 shell = _PATH_BSHELL;
311 iscsh = NO;
312 }
313 }
314 np = *argv ? argv : argv-1;
315
316 if ((pwd = getpwnam(user)) == NULL)
317 fatalx((1, "unknown login %s", user));
318
319 #ifdef LOGIN_CAP
320 /* force the usage of specified class */
321 if (class) {
322 if (ruid)
323 fatalx((1, "Only root may use -c"));
324
325 pwd->pw_class = class;
326 }
327 lc = login_getclass(pwd->pw_class);
328
329 pw_warntime = login_getcaptime(lc, "password-warn",
330 _PASSWORD_WARNDAYS * SECSPERDAY,
331 _PASSWORD_WARNDAYS * SECSPERDAY);
332 #endif
333
334 if (ruid
335 #ifdef USE_PAM
336 && (pam_err != PAM_SUCCESS)
337 #endif
338 #ifdef KERBEROS5
339 && (!use_kerberos || kerberos5(username, user, pwd->pw_uid))
340 #endif
341 #ifdef KERBEROS
342 && (!use_kerberos || kerberos(username, user, pwd->pw_uid))
343 #endif
344 ) {
345 char *pass = pwd->pw_passwd;
346 int ok = pwd->pw_uid != 0;
347
348 #ifdef SU_ROOTAUTH
349 /*
350 * Allow those in group rootauth to su to root, by supplying
351 * their own password.
352 */
353 if (!ok) {
354 if ((ok = check_ingroup(-1, SU_ROOTAUTH, username, 0))) {
355 pass = userpass;
356 user = username;
357 }
358 }
359 #endif
360 /*
361 * Only allow those in group SU_GROUP to su to root,
362 * but only if that group has any members.
363 * If SU_GROUP has no members, allow anyone to su root
364 */
365 if (!ok) {
366 ok = check_ingroup(-1, SU_GROUP, username, 1);
367 }
368 if (!ok)
369 fatalx((1,
370 "you are not listed in the correct secondary group (%s) to su %s.",
371 SU_GROUP, user));
372 /* if target requires a password, verify it */
373 if (*pass) {
374 p = getpass("Password:");
375 #ifdef SKEY
376 if (strcasecmp(p, "s/key") == 0) {
377 if (skey_haskey(user)) {
378 fatalx((1,
379 "Sorry, you have no s/key."));
380 } else {
381 if (skey_authenticate(user)) {
382 goto badlogin;
383 }
384 }
385
386 } else
387 #endif
388 if (strcmp(pass, crypt(p, pass))) {
389 #ifdef SKEY
390 badlogin:
391 #endif
392 fprintf(stderr, "Sorry\n");
393 syslog(LOG_WARNING,
394 "BAD SU %s to %s%s", username,
395 pwd->pw_name, ontty());
396 exit(1);
397 }
398 }
399 }
400
401 if (asme) {
402 /* if asme and non-standard target shell, must be root */
403 if (!chshell(pwd->pw_shell) && ruid)
404 fatalx((1,"permission denied (shell)."));
405 } else if (pwd->pw_shell && *pwd->pw_shell) {
406 shell = pwd->pw_shell;
407 iscsh = UNSET;
408 } else {
409 shell = _PATH_BSHELL;
410 iscsh = NO;
411 }
412
413 if ((p = strrchr(shell, '/')) != NULL)
414 avshell = p+1;
415 else
416 avshell = shell;
417
418 /* if we're forking a csh, we want to slightly muck the args */
419 if (iscsh == UNSET)
420 iscsh = strstr(avshell, "csh") ? YES : NO;
421
422 #ifdef USE_PAM
423 /*
424 * If PAM is in use, we must release PAM resources and close
425 * the session after su child exits. That requires a fork now,
426 * before we drop the root privs (needed for PAM)
427 */
428 if (pam_err == PAM_SUCCESS) {
429 switch(pid = fork()) {
430 case -1:
431 fatal((1, "fork"));
432 break;
433 case 0: /* child */
434 break;
435 default: /* parent */
436 waitpid(pid, &status, 0);
437 pam_err = pam_close_session(pamh, 0);
438 pam_end(pamh, pam_err);
439
440 exit(WEXITSTATUS(status));
441 break;
442 }
443 }
444
445 /*
446 * Everything here happens in the child, it should not
447 * do any PAM operation anymore (don't use fatal/fatalx)
448 */
449 #endif
450 /* set permissions */
451 #ifdef LOGIN_CAP
452 if (setusercontext(lc, pwd, pwd->pw_uid,
453 (asthem ? (LOGIN_SETPRIORITY | LOGIN_SETUMASK) : 0) |
454 LOGIN_SETRESOURCES | LOGIN_SETGROUP | LOGIN_SETUSER))
455 err(1, "setting user context");
456 #else
457 if (setgid(pwd->pw_gid) < 0)
458 err(1, "setgid");
459 if (initgroups(user, pwd->pw_gid))
460 errx(1, "initgroups failed");
461 if (setuid(pwd->pw_uid) < 0)
462 err(1, "setuid");
463 #endif
464
465 if (!asme) {
466 if (asthem) {
467 p = getenv("TERM");
468 /* Create an empty environment */
469 if ((environ = malloc(sizeof(char *))) == NULL)
470 err(1, NULL);
471 environ[0] = NULL;
472 #ifdef LOGIN_CAP
473 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH))
474 err(1, "setting user context");
475 #else
476 (void)setenv("PATH", _PATH_DEFPATH, 1);
477 #endif
478 if (p)
479 (void)setenv("TERM", p, 1);
480 if (gohome && chdir(pwd->pw_dir) < 0)
481 errx(1, "no directory");
482 }
483
484 if (asthem || pwd->pw_uid)
485 (void)setenv("USER", pwd->pw_name, 1);
486 (void)setenv("HOME", pwd->pw_dir, 1);
487 (void)setenv("SHELL", shell, 1);
488 }
489 (void)setenv("SU_FROM", username, 1);
490
491 if (iscsh == YES) {
492 if (fastlogin)
493 *np-- = "-f";
494 if (asme)
495 *np-- = "-m";
496 } else {
497 if (fastlogin)
498 unsetenv("ENV");
499 }
500
501 if (asthem) {
502 avshellbuf[0] = '-';
503 (void)strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
504 avshell = avshellbuf;
505 } else if (iscsh == YES) {
506 /* csh strips the first character... */
507 avshellbuf[0] = '_';
508 (void)strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
509 avshell = avshellbuf;
510 }
511 *np = avshell;
512
513 #ifdef BSD4_4
514 if (pwd->pw_change || pwd->pw_expire)
515 (void)gettimeofday(&tp, (struct timezone *)NULL);
516 if (pwd->pw_change) {
517 if (tp.tv_sec >= pwd->pw_change) {
518 (void)printf("%s -- %s's password has expired.\n",
519 (ruid ? "Sorry" : "Note"), user);
520 if (ruid != 0)
521 exit(1);
522 } else if (pwd->pw_change - tp.tv_sec < pw_warntime)
523 (void)printf("Warning: %s's password expires on %s",
524 user, ctime(&pwd->pw_change));
525 }
526 if (pwd->pw_expire) {
527 if (tp.tv_sec >= pwd->pw_expire) {
528 (void)printf("%s -- %s's account has expired.\n",
529 (ruid ? "Sorry" : "Note"), user);
530 if (ruid != 0)
531 exit(1);
532 } else if (pwd->pw_expire - tp.tv_sec <
533 _PASSWORD_WARNDAYS * SECSPERDAY)
534 (void)printf("Warning: %s's account expires on %s",
535 user, ctime(&pwd->pw_expire));
536 }
537 #endif
538 if (ruid != 0)
539 syslog(LOG_NOTICE, "%s to %s%s",
540 username, pwd->pw_name, ontty());
541
542 /* Raise our priority back to what we had before */
543 (void)setpriority(PRIO_PROCESS, 0, prio);
544
545 execv(shell, np);
546 err(1, "%s", shell);
547 /* NOTREACHED */
548 }
549
550 static int
551 chshell(sh)
552 const char *sh;
553 {
554 const char *cp;
555
556 setusershell();
557 while ((cp = getusershell()) != NULL)
558 if (!strcmp(cp, sh))
559 return (1);
560 return (0);
561 }
562
563 static char *
564 ontty()
565 {
566 char *p;
567 static char buf[MAXPATHLEN + 4];
568
569 buf[0] = 0;
570 if ((p = ttyname(STDERR_FILENO)) != NULL)
571 (void)snprintf(buf, sizeof buf, " on %s", p);
572 return (buf);
573 }
574
575 #ifdef KERBEROS5
576 static int
577 kerberos5(username, user, uid)
578 char *username, *user;
579 int uid;
580 {
581 krb5_error_code ret;
582 krb5_context context;
583 krb5_principal princ = NULL;
584 krb5_ccache ccache, ccache2;
585 char *cc_name;
586 const char *filename;
587
588 ret = krb5_init_context(&context);
589 if (ret)
590 return (1);
591
592 if (strcmp (user, "root") == 0)
593 ret = krb5_make_principal(context, &princ,
594 NULL, username, "root", NULL);
595 else
596 ret = krb5_make_principal(context, &princ,
597 NULL, user, NULL);
598 if (ret)
599 goto fail;
600 if (!krb5_kuserok(context, princ, user) && !uid) {
601 warnx ("kerberos5: not in %s's ACL.", user);
602 goto fail;
603 }
604 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache);
605 if (ret)
606 goto fail;
607 ret = krb5_verify_user_lrealm(context, princ, ccache, NULL, TRUE,
608 NULL);
609 if (ret) {
610 krb5_cc_destroy(context, ccache);
611 switch (ret) {
612 case KRB5_LIBOS_PWDINTR :
613 break;
614 case KRB5KRB_AP_ERR_BAD_INTEGRITY:
615 case KRB5KRB_AP_ERR_MODIFIED:
616 krb5_warnx(context, "Password incorrect");
617 break;
618 default :
619 krb5_warn(context, ret, "krb5_verify_user");
620 break;
621 }
622 goto fail;
623 }
624 ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache2);
625 if (ret) {
626 krb5_cc_destroy(context, ccache);
627 goto fail;
628 }
629 ret = krb5_cc_copy_cache(context, ccache, ccache2);
630 if (ret) {
631 krb5_cc_destroy(context, ccache);
632 krb5_cc_destroy(context, ccache2);
633 goto fail;
634 }
635
636 filename = krb5_cc_get_name(context, ccache2);
637 asprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2),
638 filename);
639 if (chown (filename, uid, -1) < 0) {
640 warn("chown %s", filename);
641 free(cc_name);
642 krb5_cc_destroy(context, ccache);
643 krb5_cc_destroy(context, ccache2);
644 goto fail;
645 }
646
647 setenv("KRB5CCNAME", cc_name, 1);
648 free(cc_name);
649 krb5_cc_close(context, ccache2);
650 krb5_cc_destroy(context, ccache);
651 return (0);
652
653 fail:
654 if (princ != NULL)
655 krb5_free_principal (context, princ);
656 krb5_free_context (context);
657 return (1);
658 }
659 #endif
660
661 #ifdef KERBEROS
662 static int
663 kerberos(username, user, uid)
664 char *username, *user;
665 int uid;
666 {
667 KTEXT_ST ticket;
668 AUTH_DAT authdata;
669 struct hostent *hp;
670 int kerno;
671 u_long faddr;
672 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
673 char hostname[MAXHOSTNAMELEN + 1], savehost[MAXHOSTNAMELEN + 1];
674
675 if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
676 return (1);
677 if (koktologin(username, lrealm, user) && !uid) {
678 warnx("kerberos: not in %s's ACL.", user);
679 return (1);
680 }
681 (void)snprintf(krbtkfile, sizeof krbtkfile, "%s_%s_%d", TKT_ROOT,
682 user, getuid());
683
684 (void)setenv("KRBTKFILE", krbtkfile, 1);
685 (void)krb_set_tkt_string(krbtkfile);
686 /*
687 * Set real as well as effective ID to 0 for the moment,
688 * to make the kerberos library do the right thing.
689 */
690 if (setuid(0) < 0) {
691 warn("setuid");
692 return (1);
693 }
694
695 /*
696 * Little trick here -- if we are su'ing to root,
697 * we need to get a ticket for "xxx.root", where xxx represents
698 * the name of the person su'ing. Otherwise (non-root case),
699 * we need to get a ticket for "yyy.", where yyy represents
700 * the name of the person being su'd to, and the instance is null
701 *
702 * We should have a way to set the ticket lifetime,
703 * with a system default for root.
704 */
705 {
706 char prompt[128];
707 char passw[256];
708
709 (void)snprintf (prompt, sizeof(prompt),
710 "%s's Password: ",
711 krb_unparse_name_long ((uid == 0 ? username : user),
712 (uid == 0 ? "root" : ""),
713 lrealm));
714 if (des_read_pw_string (passw, sizeof (passw), prompt, 0)) {
715 memset (passw, 0, sizeof (passw));
716 return (1);
717 }
718 if (strlen(passw) == 0)
719 return (1); /* Empty passwords are not allowed */
720
721 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
722 (uid == 0 ? "root" : ""), lrealm,
723 KRB_TICKET_GRANTING_TICKET,
724 lrealm,
725 DEFAULT_TKT_LIFE,
726 passw);
727 memset (passw, 0, strlen (passw));
728 }
729
730 if (kerno != KSUCCESS) {
731 if (kerno == KDC_PR_UNKNOWN) {
732 warnx("kerberos: principal unknown: %s.%s@%s",
733 (uid == 0 ? username : user),
734 (uid == 0 ? "root" : ""), lrealm);
735 return (1);
736 }
737 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
738 syslog(LOG_WARNING,
739 "BAD Kerberos SU: %s to %s%s: %s",
740 username, user, ontty(), krb_err_txt[kerno]);
741 return (1);
742 }
743
744 if (chown(krbtkfile, uid, -1) < 0) {
745 warn("chown");
746 (void)unlink(krbtkfile);
747 return (1);
748 }
749
750 (void)setpriority(PRIO_PROCESS, 0, -2);
751
752 if (gethostname(hostname, sizeof(hostname)) == -1) {
753 warn("gethostname");
754 dest_tkt();
755 return (1);
756 }
757 hostname[sizeof(hostname) - 1] = '\0';
758
759 (void)strlcpy(savehost, krb_get_phost(hostname), sizeof(savehost));
760 savehost[sizeof(savehost) - 1] = '\0';
761
762 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
763
764 if (kerno == KDC_PR_UNKNOWN) {
765 warnx("Warning: TGT not verified.");
766 syslog(LOG_WARNING,
767 "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
768 username, user, ontty(), krb_err_txt[kerno],
769 "rcmd", savehost);
770 } else if (kerno != KSUCCESS) {
771 warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
772 syslog(LOG_WARNING, "failed su: %s to %s%s: %s",
773 username, user, ontty(), krb_err_txt[kerno]);
774 dest_tkt();
775 return (1);
776 } else {
777 if (!(hp = gethostbyname(hostname))) {
778 warnx("can't get addr of %s", hostname);
779 dest_tkt();
780 return (1);
781 }
782 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr));
783
784 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
785 &authdata, "")) != KSUCCESS) {
786 warnx("kerberos: unable to verify rcmd ticket: %s",
787 krb_err_txt[kerno]);
788 syslog(LOG_WARNING,
789 "failed su: %s to %s%s: %s", username,
790 user, ontty(), krb_err_txt[kerno]);
791 dest_tkt();
792 return (1);
793 }
794 }
795 return (0);
796 }
797
798 static int
799 koktologin(name, realm, toname)
800 char *name, *realm, *toname;
801 {
802 return krb_kuserok(name,
803 strcmp (toname, "root") == 0 ? "root" : "",
804 realm,
805 toname);
806 }
807 #endif
808
809 static int
810 check_ingroup (gid, gname, user, ifempty)
811 int gid;
812 const char *gname;
813 const char *user;
814 int ifempty;
815 {
816 struct group *gr;
817 char **g;
818 #ifdef SU_INDIRECT_GROUP
819 char **gr_mem;
820 int n = 0;
821 int i = 0;
822 #endif
823 int ok = 0;
824
825 if (gname == NULL)
826 gr = getgrgid((gid_t) gid);
827 else
828 gr = getgrnam(gname);
829
830 /*
831 * XXX we are relying on the fact that we only set ifempty when
832 * calling to check for SU_GROUP and that is the only time a
833 * missing group is acceptable.
834 */
835 if (gr == NULL)
836 return ifempty;
837 if (!*gr->gr_mem) /* empty */
838 return ifempty;
839
840 /*
841 * Ok, first see if user is in gr_mem
842 */
843 for (g = gr->gr_mem; *g; ++g) {
844 if (strcmp(*g, user) == 0)
845 return 1; /* ok */
846 #ifdef SU_INDIRECT_GROUP
847 ++n; /* count them */
848 #endif
849 }
850 #ifdef SU_INDIRECT_GROUP
851 /*
852 * No.
853 * Now we need to duplicate the gr_mem list, and recurse for
854 * each member to see if it is a group, and if so whether user is
855 * in it.
856 */
857 gr_mem = malloc((n + 1) * sizeof (char *));
858 for (g = gr->gr_mem, i = 0; *g; ++g) {
859 gr_mem[i] = strdup(*g);
860 if (!gr_mem[i])
861 err(1, "strdup");
862 i++;
863 }
864 gr_mem[i++] = NULL;
865
866 for (g = gr_mem; ok == 0 && *g; ++g) {
867 /*
868 * If we get this far we don't accept empty/missing groups.
869 */
870 ok = check_ingroup(-1, *g, user, 0);
871 }
872 for (g = gr_mem; *g; ++g) {
873 free(*g);
874 }
875 free(gr_mem);
876 #endif
877 return ok;
878 }
879