]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/su/su.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[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   "$FreeBSD$";
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 #endif
67
68 #ifdef  SKEY
69 #include <skey.h>
70 #endif
71
72 #ifdef KERBEROS
73 #include <openssl/des.h>
74 #include <krb.h>
75 #include <netdb.h>
76
77 #ifdef LOGIN_CAP
78 #define ARGSTR  "-Kflmc:"
79 #else
80 #define ARGSTR  "-Kflm"
81 #endif
82
83 static int kerberos(char *username, char *user, int uid, char *pword);
84 static int koktologin(char *name, char *toname);
85
86 int use_kerberos = 1;
87 #else /* !KERBEROS */
88 #ifdef LOGIN_CAP
89 #define ARGSTR  "-flmc:"
90 #else
91 #define ARGSTR  "-flm"
92 #endif
93 #endif /* KERBEROS */
94
95 char   *ontty __P((void));
96 int     chshell __P((char *));
97 static void usage __P((void));
98
99 int
100 main(argc, argv)
101         int argc;
102         char **argv;
103 {
104         extern char **environ;
105         struct passwd *pwd;
106 #ifdef WHEELSU
107         char *targetpass;
108         int iswheelsu;
109 #endif /* WHEELSU */
110         char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np;
111         struct group *gr;
112         uid_t ruid;
113         gid_t gid;
114         int asme, ch, asthem, fastlogin, prio, i;
115         enum { UNSET, YES, NO } iscsh = UNSET;
116 #ifdef LOGIN_CAP
117         login_cap_t *lc;
118         char *class=NULL;
119         int setwhat;
120 #endif
121 #ifdef KERBEROS
122         char *k;
123 #endif
124         char shellbuf[MAXPATHLEN];
125
126 #ifdef WHEELSU
127         iswheelsu =
128 #endif /* WHEELSU */
129         asme = asthem = fastlogin = 0;
130         user = "root";
131         while((ch = getopt(argc, argv, ARGSTR)) != -1) 
132                 switch((char)ch) {
133 #ifdef KERBEROS
134                 case 'K':
135                         use_kerberos = 0;
136                         break;
137 #endif
138                 case 'f':
139                         fastlogin = 1;
140                         break;
141                 case '-':
142                 case 'l':
143                         asme = 0;
144                         asthem = 1;
145                         break;
146                 case 'm':
147                         asme = 1;
148                         asthem = 0;
149                         break;
150 #ifdef LOGIN_CAP
151                 case 'c':
152                         class = optarg;
153                         break;
154 #endif
155                 case '?':
156                 default:
157                         usage();
158                 }
159
160         if (optind < argc)
161                 user = argv[optind++];
162
163         if (strlen(user) > MAXLOGNAME - 1) {
164                 (void)fprintf(stderr, "su: username too long.\n");
165                 exit(1);
166         }
167                 
168         if (user == NULL)
169                 usage();
170
171         if ((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) {
172             errx(1, "malloc failure");
173         }
174
175         nargv[argc + 3] = NULL;
176         for (i = argc; i >= optind; i--)
177             nargv[i + 3] = argv[i];
178         np = &nargv[i + 3];
179
180         argv += optind;
181
182 #ifdef KERBEROS
183         k = auth_getval("auth_list");
184         if (k && !strstr(k, "kerberos"))
185             use_kerberos = 0;
186 #endif
187         errno = 0;
188         prio = getpriority(PRIO_PROCESS, 0);
189         if (errno)
190                 prio = 0;
191         (void)setpriority(PRIO_PROCESS, 0, -2);
192         openlog("su", LOG_CONS, 0);
193
194         /* get current login name and shell */
195         ruid = getuid();
196         username = getlogin();
197         if (username == NULL || (pwd = getpwnam(username)) == NULL ||
198             pwd->pw_uid != ruid)
199                 pwd = getpwuid(ruid);
200         if (pwd == NULL)
201                 errx(1, "who are you?");
202         username = strdup(pwd->pw_name);
203         gid = pwd->pw_gid;
204         if (username == NULL)
205                 err(1, NULL);
206         if (asme) {
207                 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
208                         /* copy: pwd memory is recycled */
209                         shell = strncpy(shellbuf,  pwd->pw_shell, sizeof shellbuf);
210                         shellbuf[sizeof shellbuf - 1] = '\0';
211                 } else {
212                         shell = _PATH_BSHELL;
213                         iscsh = NO;
214                 }
215         }
216
217         /* get target login information, default to root */
218         if ((pwd = getpwnam(user)) == NULL) {
219                 errx(1, "unknown login: %s", user);
220         }
221 #ifdef LOGIN_CAP
222         if (class==NULL) {
223                 lc = login_getpwclass(pwd);
224         } else {
225                 if (ruid)
226                         errx(1, "only root may use -c");
227                 lc = login_getclass(class);
228                 if (lc == NULL)
229                         errx(1, "unknown class: %s", class);
230         }
231 #endif
232
233 #ifdef WHEELSU
234         targetpass = strdup(pwd->pw_passwd);
235 #endif /* WHEELSU */
236
237         if (ruid) {
238 #ifdef KERBEROS
239                 if (use_kerberos && koktologin(username, user)
240                     && !pwd->pw_uid) {
241                         warnx("kerberos: not in %s's ACL.", user);
242                         use_kerberos = 0;
243                 }
244 #endif
245                 {
246                         /*
247                          * Only allow those with pw_gid==0 or those listed in
248                          * group zero to su to root.  If group zero entry is
249                          * missing or empty, then allow anyone to su to root.
250                          * iswheelsu will only be set if the user is EXPLICITLY
251                          * listed in group zero.
252                          */
253                         if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) &&
254                             gr->gr_mem && *(gr->gr_mem))
255                                 for (g = gr->gr_mem;; ++g) {
256                                         if (!*g) {
257                                                 if (gid == 0)
258                                                         break;
259                                                 else
260                                                         errx(1, "you are not in the correct group to su %s.", user);
261                                         }
262                                         if (strcmp(username, *g) == 0) {
263 #ifdef WHEELSU
264                                                 iswheelsu = 1;
265 #endif /* WHEELSU */
266                                                 break;
267                                         }
268                                 }
269                 }
270                 /* if target requires a password, verify it */
271                 if (*pwd->pw_passwd) {
272 #ifdef  SKEY
273 #ifdef WHEELSU
274                         if (iswheelsu) {
275                                 pwd = getpwnam(username);
276                         }
277 #endif /* WHEELSU */
278                         p = skey_getpass("Password:", pwd, 1);
279                         if (!(!strcmp(pwd->pw_passwd, skey_crypt(p, pwd->pw_passwd, pwd, 1))
280 #ifdef WHEELSU
281                               || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass)))
282 #endif /* WHEELSU */
283                               )) {
284 #else
285                         p = getpass("Password:");
286                         if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
287 #endif
288 #ifdef KERBEROS
289                                 if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p)))
290 #endif
291                                         {
292                                         fprintf(stderr, "Sorry\n");
293                                         syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty());
294                                         exit(1);
295                                 }
296                         }
297 #ifdef WHEELSU
298                         if (iswheelsu) {
299                                 pwd = getpwnam(user);
300                         }
301 #endif /* WHEELSU */
302                 }
303                 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) {
304                         fprintf(stderr, "Sorry - account expired\n");
305                         syslog(LOG_AUTH|LOG_WARNING,
306                                 "BAD SU %s to %s%s", username,
307                                 user, ontty());
308                         exit(1);
309                 }
310         }
311
312         if (asme) {
313                 /* if asme and non-standard target shell, must be root */
314                 if (!chshell(pwd->pw_shell) && ruid)
315                         errx(1, "permission denied (shell).");
316         } else if (pwd->pw_shell && *pwd->pw_shell) {
317                 shell = pwd->pw_shell;
318                 iscsh = UNSET;
319         } else {
320                 shell = _PATH_BSHELL;
321                 iscsh = NO;
322         }
323
324         /* if we're forking a csh, we want to slightly muck the args */
325         if (iscsh == UNSET) {
326                 p = strrchr(shell, '/');
327                 if (p)
328                         ++p;
329                 else
330                         p = shell;
331                 if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO)
332                     iscsh = strcmp(p, "tcsh") ? NO : YES;
333         }
334
335         (void)setpriority(PRIO_PROCESS, 0, prio);
336
337 #ifdef LOGIN_CAP
338         /* Set everything now except the environment & umask */
339         setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
340         /*
341          * Don't touch resource/priority settings if -m has been
342          * used or -l and -c hasn't, and we're not su'ing to root.
343          */
344         if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
345                 setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES);
346         if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
347                 err(1, "setusercontext");
348 #else
349         /* set permissions */
350         if (setgid(pwd->pw_gid) < 0)
351                 err(1, "setgid");
352         if (initgroups(user, pwd->pw_gid))
353                 errx(1, "initgroups failed");
354         if (setuid(pwd->pw_uid) < 0)
355                 err(1, "setuid");
356 #endif
357
358         if (!asme) {
359                 if (asthem) {
360                         p = getenv("TERM");
361 #ifdef KERBEROS
362                         k = getenv("KRBTKFILE");
363 #endif
364                         if ((cleanenv = calloc(20, sizeof(char*))) == NULL)
365                                 errx(1, "calloc");
366                         cleanenv[0] = NULL;
367                         environ = cleanenv;
368 #ifdef LOGIN_CAP
369                         /* set the su'd user's environment & umask */
370                         setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
371 #else
372                         (void)setenv("PATH", _PATH_DEFPATH, 1);
373 #endif
374                         if (p)
375                                 (void)setenv("TERM", p, 1);
376 #ifdef KERBEROS
377                         if (k)
378                                 (void)setenv("KRBTKFILE", k, 1);
379 #endif
380                         if (chdir(pwd->pw_dir) < 0)
381                                 errx(1, "no directory");
382                 }
383                 if (asthem || pwd->pw_uid)
384                         (void)setenv("USER", pwd->pw_name, 1);
385                 (void)setenv("HOME", pwd->pw_dir, 1);
386                 (void)setenv("SHELL", shell, 1);
387         }
388         if (iscsh == YES) {
389                 if (fastlogin)
390                         *np-- = "-f";
391                 if (asme)
392                         *np-- = "-m";
393         }
394
395         /* csh strips the first character... */
396         *np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
397
398         if (ruid != 0)
399                 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
400                     username, user, ontty());
401
402         login_close(lc);
403
404         execv(shell, np);
405         err(1, "%s", shell);
406 }
407
408 static void
409 usage()
410 {
411         (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR);
412         exit(1);
413 }
414
415 int
416 chshell(sh)
417         char *sh;
418 {
419         int  r = 0;
420         char *cp;
421
422         setusershell();
423         while (!r && (cp = getusershell()) != NULL)
424                 r = strcmp(cp, sh) == 0;
425         endusershell();
426         return r;
427 }
428
429 char *
430 ontty()
431 {
432         char *p;
433         static char buf[MAXPATHLEN + 4];
434
435         buf[0] = 0;
436         p = ttyname(STDERR_FILENO);
437         if (p)
438                 snprintf(buf, sizeof(buf), " on %s", p);
439         return (buf);
440 }
441
442 #ifdef KERBEROS
443 int
444 kerberos(username, user, uid, pword)
445         char *username, *user;
446         int uid;
447         char *pword;
448 {
449         KTEXT_ST ticket;
450         AUTH_DAT authdata;
451         int kerno;
452         u_long faddr;
453         char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
454         char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
455         char *krb_get_phost();
456         struct hostent *hp;
457
458         if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
459                 return (1);
460         (void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user,
461             (unsigned long)getuid());
462
463         (void)setenv("KRBTKFILE", krbtkfile, 1);
464         (void)krb_set_tkt_string(krbtkfile);
465         /*
466          * Set real as well as effective ID to 0 for the moment,
467          * to make the kerberos library do the right thing.
468          */
469         if (setuid(0) < 0) {
470                 warn("setuid");
471                 return (1);
472         }
473
474         /*
475          * Little trick here -- if we are su'ing to root,
476          * we need to get a ticket for "xxx.root", where xxx represents
477          * the name of the person su'ing.  Otherwise (non-root case),
478          * we need to get a ticket for "yyy.", where yyy represents
479          * the name of the person being su'd to, and the instance is null
480          *
481          * We should have a way to set the ticket lifetime,
482          * with a system default for root.
483          */
484         kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
485                 (uid == 0 ? "root" : ""), lrealm,
486                 "krbtgt", lrealm, DEFAULT_TKT_LIFE, pword);
487
488         if (kerno != KSUCCESS) {
489                 if (kerno == KDC_PR_UNKNOWN) {
490                         warnx("kerberos: principal unknown: %s.%s@%s",
491                                 (uid == 0 ? username : user),
492                                 (uid == 0 ? "root" : ""), lrealm);
493                         return (1);
494                 }
495                 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
496                 syslog(LOG_NOTICE|LOG_AUTH,
497                     "BAD Kerberos SU: %s to %s%s: %s",
498                     username, user, ontty(), krb_err_txt[kerno]);
499                 return (1);
500         }
501
502         if (chown(krbtkfile, uid, -1) < 0) {
503                 warn("chown");
504                 (void)unlink(krbtkfile);
505                 return (1);
506         }
507
508         (void)setpriority(PRIO_PROCESS, 0, -2);
509
510         if (gethostname(hostname, sizeof(hostname)) == -1) {
511                 warn("gethostname");
512                 dest_tkt();
513                 return (1);
514         }
515
516         (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
517         savehost[sizeof(savehost) - 1] = '\0';
518
519         kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
520
521         if (kerno == KDC_PR_UNKNOWN) {
522                 warnx("Warning: TGT not verified.");
523                 syslog(LOG_NOTICE|LOG_AUTH,
524                     "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
525                     username, user, ontty(), krb_err_txt[kerno],
526                     "rcmd", savehost);
527         } else if (kerno != KSUCCESS) {
528                 warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
529                 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
530                     username, user, ontty(), krb_err_txt[kerno]);
531                 dest_tkt();
532                 return (1);
533         } else {
534                 if (!(hp = gethostbyname(hostname))) {
535                         warnx("can't get addr of %s", hostname);
536                         dest_tkt();
537                         return (1);
538                 }
539                 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr));
540
541                 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
542                     &authdata, "")) != KSUCCESS) {
543                         warnx("kerberos: unable to verify rcmd ticket: %s\n",
544                             krb_err_txt[kerno]);
545                         syslog(LOG_NOTICE|LOG_AUTH,
546                             "failed su: %s to %s%s: %s", username,
547                              user, ontty(), krb_err_txt[kerno]);
548                         dest_tkt();
549                         return (1);
550                 }
551         }
552         return (0);
553 }
554
555 int
556 koktologin(name, toname)
557         char *name, *toname;
558 {
559         AUTH_DAT *kdata;
560         AUTH_DAT kdata_st;
561         char realm[REALM_SZ];
562
563         if (krb_get_lrealm(realm, 1) != KSUCCESS)
564                 return (1);
565         kdata = &kdata_st;
566         memset((char *)kdata, 0, sizeof(*kdata));
567         (void)strncpy(kdata->pname, name, sizeof kdata->pname - 1);
568         (void)strncpy(kdata->pinst,
569             ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof kdata->pinst - 1);
570         (void)strncpy(kdata->prealm, realm, sizeof kdata->prealm - 1);
571         return (kuserok(kdata, toname));
572 }
573 #endif