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