]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/su/su.c
Actually fixed ambiguous else. The previous revision had no effect.
[FreeBSD/FreeBSD.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
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1988, 1993, 1994\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)su.c        8.3 (Berkeley) 4/2/94";
43 #endif
44 static const char rcsid[] =
45         "$Id: su.c,v 1.30 1999/07/01 17:59:17 billf Exp $";
46 #endif /* not lint */
47
48 #include <sys/param.h>
49 #include <sys/time.h>
50 #include <sys/resource.h>
51
52 #include <err.h>
53 #include <errno.h>
54 #include <grp.h>
55 #include <paths.h>
56 #include <pwd.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <syslog.h>
61 #include <unistd.h>
62 #include <libutil.h>
63
64 #ifdef LOGIN_CAP
65 #include <login_cap.h>
66 #ifdef LOGIN_CAP_AUTH
67 #undef SKEY
68 #undef KERBEROS
69 #endif
70 #endif
71
72 #ifdef  SKEY
73 #include <skey.h>
74 #endif
75
76 #ifdef KERBEROS
77 #include <des.h>
78 #include <krb.h>
79 #include <netdb.h>
80
81 #ifdef LOGIN_CAP
82 #define ARGSTR  "-Kflmc:"
83 #else
84 #define ARGSTR  "-Kflm"
85 #endif
86
87 static int kerberos(char *username, char *user, int uid, char *pword);
88 static int koktologin(char *name, char *toname);
89
90 int use_kerberos = 1;
91 #else /* !KERBEROS */
92 #ifdef LOGIN_CAP
93 #define ARGSTR  "-flmc:"
94 #else
95 #define ARGSTR  "-flm"
96 #endif
97 #endif /* KERBEROS */
98
99 char   *ontty __P((void));
100 int     chshell __P((char *));
101 static void usage __P((void));
102
103 int
104 main(argc, argv)
105         int argc;
106         char **argv;
107 {
108         extern char **environ;
109         struct passwd *pwd;
110 #ifdef WHEELSU
111         char *targetpass;
112         int iswheelsu;
113 #endif /* WHEELSU */
114         char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np;
115         struct group *gr;
116         uid_t ruid;
117         gid_t gid;
118         int asme, ch, asthem, fastlogin, prio, i;
119         enum { UNSET, YES, NO } iscsh = UNSET;
120 #ifdef LOGIN_CAP
121         login_cap_t *lc;
122         char *class=NULL;
123         int setwhat;
124 #ifdef LOGIN_CAP_AUTH
125         char *style, *approvep, *auth_method = NULL;
126 #endif
127 #endif
128 #ifdef KERBEROS
129         char *k;
130 #endif
131         char shellbuf[MAXPATHLEN];
132
133 #ifdef WHEELSU
134         iswheelsu =
135 #endif /* WHEELSU */
136         asme = asthem = fastlogin = 0;
137         user = "root";
138         while((ch = getopt(argc, argv, ARGSTR)) != -1) 
139                 switch((char)ch) {
140 #ifdef KERBEROS
141                 case 'K':
142                         use_kerberos = 0;
143                         break;
144 #endif
145                 case 'f':
146                         fastlogin = 1;
147                         break;
148                 case '-':
149                 case 'l':
150                         asme = 0;
151                         asthem = 1;
152                         break;
153                 case 'm':
154                         asme = 1;
155                         asthem = 0;
156                         break;
157 #ifdef LOGIN_CAP
158                 case 'c':
159                         class = optarg;
160                         break;
161 #endif
162                 case '?':
163                 default:
164                         usage();
165                 }
166
167         if (optind < argc)
168                 user = argv[optind++];
169
170         if (strlen(user) > MAXLOGNAME - 1) {
171                 (void)fprintf(stderr, "su: username too long.\n");
172                 exit(1);
173         }
174                 
175         if (user == NULL)
176                 usage();
177
178         if ((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) {
179             errx(1, "malloc failure");
180         }
181
182         nargv[argc + 3] = NULL;
183         for (i = argc; i >= optind; i--)
184             nargv[i + 3] = argv[i];
185         np = &nargv[i + 3];
186
187         argv += optind;
188
189 #ifdef KERBEROS
190         k = auth_getval("auth_list");
191         if (k && !strstr(k, "kerberos"))
192             use_kerberos = 0;
193 #endif
194         errno = 0;
195         prio = getpriority(PRIO_PROCESS, 0);
196         if (errno)
197                 prio = 0;
198         (void)setpriority(PRIO_PROCESS, 0, -2);
199         openlog("su", LOG_CONS, 0);
200
201         /* get current login name and shell */
202         ruid = getuid();
203         username = getlogin();
204         if (username == NULL || (pwd = getpwnam(username)) == NULL ||
205             pwd->pw_uid != ruid)
206                 pwd = getpwuid(ruid);
207         if (pwd == NULL)
208                 errx(1, "who are you?");
209         username = strdup(pwd->pw_name);
210         gid = pwd->pw_gid;
211         if (username == NULL)
212                 err(1, NULL);
213         if (asme) {
214                 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
215                         /* copy: pwd memory is recycled */
216                         shell = strncpy(shellbuf,  pwd->pw_shell, sizeof shellbuf);
217                         shellbuf[sizeof shellbuf - 1] = '\0';
218                 } else {
219                         shell = _PATH_BSHELL;
220                         iscsh = NO;
221                 }
222         }
223
224 #ifdef LOGIN_CAP_AUTH
225         if (auth_method = strchr(user, ':')) {
226                 *auth_method = '\0';
227                 auth_method++;
228                 if (*auth_method == '\0')
229                         auth_method = NULL;
230         }
231 #endif /* !LOGIN_CAP_AUTH */
232
233         /* get target login information, default to root */
234         if ((pwd = getpwnam(user)) == NULL) {
235                 errx(1, "unknown login: %s", user);
236         }
237 #ifdef LOGIN_CAP
238         if (class==NULL) {
239                 lc = login_getpwclass(pwd);
240         } else {
241                 if (ruid)
242                         errx(1, "only root may use -c");
243                 lc = login_getclass(class);
244                 if (lc == NULL)
245                         errx(1, "unknown class: %s", class);
246         }
247 #endif
248
249 #ifdef WHEELSU
250         targetpass = strdup(pwd->pw_passwd);
251 #endif /* WHEELSU */
252
253         if (ruid) {
254 #ifdef KERBEROS
255                 if (use_kerberos && koktologin(username, user)
256                     && !pwd->pw_uid) {
257                         warnx("kerberos: not in %s's ACL.", user);
258                         use_kerberos = 0;
259                 }
260 #endif
261                 {
262                         /*
263                          * Only allow those with pw_gid==0 or those listed in
264                          * group zero to su to root.  If group zero entry is
265                          * missing or empty, then allow anyone to su to root.
266                          * iswheelsu will only be set if the user is EXPLICITLY
267                          * listed in group zero.
268                          */
269                         if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) &&
270                             gr->gr_mem && *(gr->gr_mem))
271                                 for (g = gr->gr_mem;; ++g) {
272                                         if (!*g) {
273                                                 if (gid == 0)
274                                                         break;
275                                                 else
276                                                         errx(1, "you are not in the correct group to su %s.", user);
277                                         }
278                                         if (strcmp(username, *g) == 0) {
279 #ifdef WHEELSU
280                                                 iswheelsu = 1;
281 #endif /* WHEELSU */
282                                                 break;
283                                         }
284                                 }
285                 }
286                 /* if target requires a password, verify it */
287                 if (*pwd->pw_passwd) {
288 #ifdef LOGIN_CAP_AUTH
289                 /*
290                  * This hands off authorisation to an authorisation program,
291                  * depending on the styles available for the "auth-su",
292                  * authorisation styles.
293                  */
294                 if ((style = login_getstyle(lc, auth_method, "su")) == NULL)
295                         errx(1, "auth method available for su.\n");
296                 if (authenticate(user, lc ? lc->lc_class : "default", style, "su") != 0) {
297 #ifdef WHEELSU
298                         if (!iswheelsu || authenticate(username, lc ? lc->lc_class : "default", style, "su") != 0) {
299 #endif /* WHEELSU */
300                         {
301                         fprintf(stderr, "Sorry\n");
302                         syslog(LOG_AUTH|LOG_WARNING,"BAD SU %s to %s%s", username, user, ontty());
303                         exit(1);
304                         }
305                 }
306
307                 /*
308                  * If authentication succeeds, run any approval
309                  * program, if applicable for this class.
310                  */
311                 approvep = login_getcapstr(lc, "approve", NULL, NULL);
312                 if (approvep==NULL || auth_script(approvep, approvep, username, lc->lc_class, 0) == 0) {
313                         int     r = auth_scan(AUTH_OKAY);
314                         /* See what the authorise program says */
315                         if (!(r & AUTH_ROOTOKAY) && pwd->pw_uid == 0) {
316                                 fprintf(stderr, "Sorry\n");
317                                 syslog(LOG_AUTH|LOG_WARNING,"UNAPPROVED ROOT SU %s%s", user, ontty());
318                                 exit(1);
319                         }
320                 }
321 #else /* !LOGIN_CAP_AUTH */
322 #ifdef  SKEY
323 #ifdef WHEELSU
324                         if (iswheelsu) {
325                                 pwd = getpwnam(username);
326                         }
327 #endif /* WHEELSU */
328                         p = skey_getpass("Password:", pwd, 1);
329                         if (!(!strcmp(pwd->pw_passwd, skey_crypt(p, pwd->pw_passwd, pwd, 1))
330 #ifdef WHEELSU
331                               || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass)))
332 #endif /* WHEELSU */
333                               )) {
334 #else
335                         p = getpass("Password:");
336                         if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
337 #endif
338 #ifdef KERBEROS
339                                 if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p)))
340 #endif
341                                         {
342                                         fprintf(stderr, "Sorry\n");
343                                         syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty());
344                                         exit(1);
345                                 }
346                         }
347 #ifdef WHEELSU
348                         if (iswheelsu) {
349                                 pwd = getpwnam(user);
350                         }
351 #endif /* WHEELSU */
352 #endif /* LOGIN_CAP_AUTH */
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 KERBEROS
413                         k = getenv("KRBTKFILE");
414 #endif
415                         if ((cleanenv = calloc(20, sizeof(char*))) == NULL)
416                                 errx(1, "calloc");
417                         cleanenv[0] = NULL;
418                         environ = cleanenv;
419 #ifdef LOGIN_CAP
420                         /* set the su'd user's environment & umask */
421                         setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
422 #else
423                         (void)setenv("PATH", _PATH_DEFPATH, 1);
424 #endif
425                         if (p)
426                                 (void)setenv("TERM", p, 1);
427 #ifdef KERBEROS
428                         if (k)
429                                 (void)setenv("KRBTKFILE", k, 1);
430 #endif
431                         if (chdir(pwd->pw_dir) < 0)
432                                 errx(1, "no directory");
433                 }
434                 if (asthem || pwd->pw_uid)
435                         (void)setenv("USER", pwd->pw_name, 1);
436                 (void)setenv("HOME", pwd->pw_dir, 1);
437                 (void)setenv("SHELL", shell, 1);
438         }
439         if (iscsh == YES) {
440                 if (fastlogin)
441                         *np-- = "-f";
442                 if (asme)
443                         *np-- = "-m";
444         }
445
446         /* csh strips the first character... */
447         *np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
448
449         if (ruid != 0)
450                 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
451                     username, user, ontty());
452
453         login_close(lc);
454
455         execv(shell, np);
456         err(1, "%s", shell);
457 }
458
459 static void
460 usage()
461 {
462         (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR);
463         exit(1);
464 }
465
466 int
467 chshell(sh)
468         char *sh;
469 {
470         int  r = 0;
471         char *cp;
472
473         setusershell();
474         while (!r && (cp = getusershell()) != NULL)
475                 r = strcmp(cp, sh) == 0;
476         endusershell();
477         return r;
478 }
479
480 char *
481 ontty()
482 {
483         char *p;
484         static char buf[MAXPATHLEN + 4];
485
486         buf[0] = 0;
487         p = ttyname(STDERR_FILENO);
488         if (p)
489                 snprintf(buf, sizeof(buf), " on %s", p);
490         return (buf);
491 }
492
493 #ifdef KERBEROS
494 int
495 kerberos(username, user, uid, pword)
496         char *username, *user;
497         int uid;
498         char *pword;
499 {
500         KTEXT_ST ticket;
501         AUTH_DAT authdata;
502         int kerno;
503         u_long faddr;
504         char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
505         char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
506         char *krb_get_phost();
507         struct hostent *hp;
508
509         if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
510                 return (1);
511         (void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user,
512             (unsigned long)getuid());
513
514         (void)setenv("KRBTKFILE", krbtkfile, 1);
515         (void)krb_set_tkt_string(krbtkfile);
516         /*
517          * Set real as well as effective ID to 0 for the moment,
518          * to make the kerberos library do the right thing.
519          */
520         if (setuid(0) < 0) {
521                 warn("setuid");
522                 return (1);
523         }
524
525         /*
526          * Little trick here -- if we are su'ing to root,
527          * we need to get a ticket for "xxx.root", where xxx represents
528          * the name of the person su'ing.  Otherwise (non-root case),
529          * we need to get a ticket for "yyy.", where yyy represents
530          * the name of the person being su'd to, and the instance is null
531          *
532          * We should have a way to set the ticket lifetime,
533          * with a system default for root.
534          */
535         kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
536                 (uid == 0 ? "root" : ""), lrealm,
537                 "krbtgt", lrealm, DEFAULT_TKT_LIFE, pword);
538
539         if (kerno != KSUCCESS) {
540                 if (kerno == KDC_PR_UNKNOWN) {
541                         warnx("kerberos: principal unknown: %s.%s@%s",
542                                 (uid == 0 ? username : user),
543                                 (uid == 0 ? "root" : ""), lrealm);
544                         return (1);
545                 }
546                 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
547                 syslog(LOG_NOTICE|LOG_AUTH,
548                     "BAD Kerberos SU: %s to %s%s: %s",
549                     username, user, ontty(), krb_err_txt[kerno]);
550                 return (1);
551         }
552
553         if (chown(krbtkfile, uid, -1) < 0) {
554                 warn("chown");
555                 (void)unlink(krbtkfile);
556                 return (1);
557         }
558
559         (void)setpriority(PRIO_PROCESS, 0, -2);
560
561         if (gethostname(hostname, sizeof(hostname)) == -1) {
562                 warn("gethostname");
563                 dest_tkt();
564                 return (1);
565         }
566
567         (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
568         savehost[sizeof(savehost) - 1] = '\0';
569
570         kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
571
572         if (kerno == KDC_PR_UNKNOWN) {
573                 warnx("Warning: TGT not verified.");
574                 syslog(LOG_NOTICE|LOG_AUTH,
575                     "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
576                     username, user, ontty(), krb_err_txt[kerno],
577                     "rcmd", savehost);
578         } else if (kerno != KSUCCESS) {
579                 warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
580                 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
581                     username, user, ontty(), krb_err_txt[kerno]);
582                 dest_tkt();
583                 return (1);
584         } else {
585                 if (!(hp = gethostbyname(hostname))) {
586                         warnx("can't get addr of %s", hostname);
587                         dest_tkt();
588                         return (1);
589                 }
590                 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr));
591
592                 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
593                     &authdata, "")) != KSUCCESS) {
594                         warnx("kerberos: unable to verify rcmd ticket: %s\n",
595                             krb_err_txt[kerno]);
596                         syslog(LOG_NOTICE|LOG_AUTH,
597                             "failed su: %s to %s%s: %s", username,
598                              user, ontty(), krb_err_txt[kerno]);
599                         dest_tkt();
600                         return (1);
601                 }
602         }
603         return (0);
604 }
605
606 int
607 koktologin(name, toname)
608         char *name, *toname;
609 {
610         AUTH_DAT *kdata;
611         AUTH_DAT kdata_st;
612         char realm[REALM_SZ];
613
614         if (krb_get_lrealm(realm, 1) != KSUCCESS)
615                 return (1);
616         kdata = &kdata_st;
617         memset((char *)kdata, 0, sizeof(*kdata));
618         (void)strncpy(kdata->pname, name, sizeof kdata->pname - 1);
619         (void)strncpy(kdata->pinst,
620             ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof kdata->pinst - 1);
621         (void)strncpy(kdata->prealm, realm, sizeof kdata->prealm - 1);
622         return (kuserok(kdata, toname));
623 }
624 #endif