Annotation of src/usr.bin/su/su.c, Revision 1.3
1.1 deraadt 1: /*
2: * Copyright (c) 1988 The Regents of the University of California.
3: * 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: char copyright[] =
36: "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
37: All rights reserved.\n";
38: #endif /* not lint */
39:
40: #ifndef lint
41: /*static char sccsid[] = "from: @(#)su.c 5.26 (Berkeley) 7/6/91";*/
1.3 ! deraadt 42: static char rcsid[] = "$Id: su.c,v 1.2 1995/12/16 22:20:38 tholo Exp $";
1.1 deraadt 43: #endif /* not lint */
44:
45: #include <sys/param.h>
46: #include <sys/time.h>
47: #include <sys/resource.h>
48: #include <syslog.h>
49: #include <stdio.h>
50: #include <stdlib.h>
51: #include <pwd.h>
52: #include <grp.h>
53: #include <string.h>
54: #include <unistd.h>
55: #include <paths.h>
56:
57: #ifdef KERBEROS
58: #include <kerberosIV/des.h>
59: #include <kerberosIV/krb.h>
60: #include <netdb.h>
61:
62: #define ARGSTR "-Kflm"
63:
64: int use_kerberos = 1;
65: #else
66: #define ARGSTR "-flm"
67: #endif
68:
69: extern char *crypt();
70: int chshell();
71:
72: int
73: main(argc, argv)
74: int argc;
75: char **argv;
76: {
77: extern char **environ;
78: extern int errno, optind;
79: register struct passwd *pwd;
80: register char *p, **g;
81: struct group *gr;
82: uid_t ruid, getuid();
83: int asme, ch, asthem, fastlogin, prio;
84: enum { UNSET, YES, NO } iscsh = UNSET;
85: char *user, *shell, *avshell, *username, *cleanenv[10], **np;
86: char shellbuf[MAXPATHLEN], avshellbuf[MAXPATHLEN];
87: char *getpass(), *getenv(), *getlogin(), *ontty();
88:
89: asme = asthem = fastlogin = 0;
90: while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
91: switch((char)ch) {
92: #ifdef KERBEROS
93: case 'K':
94: use_kerberos = 0;
95: break;
96: #endif
97: case 'f':
98: fastlogin = 1;
99: break;
100: case '-':
101: case 'l':
102: asme = 0;
103: asthem = 1;
104: break;
105: case 'm':
106: asme = 1;
107: asthem = 0;
108: break;
109: case '?':
110: default:
111: (void)fprintf(stderr, "usage: su [%s] [login]\n",
112: ARGSTR);
113: exit(1);
114: }
115: argv += optind;
116:
117: errno = 0;
118: prio = getpriority(PRIO_PROCESS, 0);
119: if (errno)
120: prio = 0;
121: (void)setpriority(PRIO_PROCESS, 0, -2);
122: openlog("su", LOG_CONS, 0);
123:
124: /* get current login name and shell */
125: ruid = getuid();
126: username = getlogin();
127: if (username == NULL || (pwd = getpwnam(username)) == NULL ||
128: pwd->pw_uid != ruid)
129: pwd = getpwuid(ruid);
130: if (pwd == NULL) {
131: fprintf(stderr, "su: who are you?\n");
132: exit(1);
133: }
134: username = strdup(pwd->pw_name);
135: if (asme)
136: if (pwd->pw_shell && *pwd->pw_shell)
137: shell = strcpy(shellbuf, pwd->pw_shell);
138: else {
139: shell = _PATH_BSHELL;
140: iscsh = NO;
141: }
142:
143: /* get target login information, default to root */
144: user = *argv ? *argv : "root";
145: np = *argv ? argv : argv-1;
146:
147: if ((pwd = getpwnam(user)) == NULL) {
148: fprintf(stderr, "su: unknown login %s\n", user);
149: exit(1);
150: }
151:
152: if (ruid) {
153: #ifdef KERBEROS
154: if (!use_kerberos || kerberos(username, user, pwd->pw_uid))
155: #endif
156: {
157: /* only allow those in group zero to su to root. */
1.3 ! deraadt 158: if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0))
! 159: && gr->gr_mem && *(gr->gr_mem))
1.1 deraadt 160: for (g = gr->gr_mem;; ++g) {
161: if (!*g) {
162: (void)fprintf(stderr,
163: "su: you are not in the correct group to su %s.\n",
164: user);
165: exit(1);
166: }
167: if (!strcmp(username, *g))
168: break;
169: }
170: /* if target requires a password, verify it */
171: if (*pwd->pw_passwd) {
172: p = getpass("Password:");
173: #ifdef SKEY
174: if (strcasecmp(p, "s/key") == 0) {
175: if (skey_haskey(user)) {
176: fprintf(stderr, "Sorry, you have no s/key.\n");
177: exit(1);
178: } else {
179: if (skey_authenticate(user)) {
180: goto badlogin;
181: }
182: }
183:
184: } else
185: #endif
186: if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
187: badlogin:
188: fprintf(stderr, "Sorry\n");
189: syslog(LOG_AUTH|LOG_WARNING,
190: "BAD SU %s to %s%s", username,
191: user, ontty());
192: exit(1);
193: }
194: }
195: }
196: }
197:
198: if (asme) {
199: /* if asme and non-standard target shell, must be root */
200: if (!chshell(pwd->pw_shell) && ruid) {
201: (void)fprintf(stderr,
202: "su: permission denied (shell).\n");
203: exit(1);
204: }
205: } else if (pwd->pw_shell && *pwd->pw_shell) {
206: shell = pwd->pw_shell;
207: iscsh = UNSET;
208: } else {
209: shell = _PATH_BSHELL;
210: iscsh = NO;
211: }
212:
213: if (p = rindex(shell, '/'))
214: avshell = p+1;
215: else
216: avshell = shell;
217:
218: /* if we're forking a csh, we want to slightly muck the args */
219: if (iscsh == UNSET)
220: iscsh = strcmp(avshell, "csh") ? NO : YES;
221:
222: /* set permissions */
223: if (setgid(pwd->pw_gid) < 0) {
224: perror("su: setgid");
225: exit(1);
226: }
227: if (initgroups(user, pwd->pw_gid)) {
228: (void)fprintf(stderr, "su: initgroups failed.\n");
229: exit(1);
230: }
231: if (setuid(pwd->pw_uid) < 0) {
232: perror("su: setuid");
233: exit(1);
234: }
235:
236: if (!asme) {
237: if (asthem) {
238: p = getenv("TERM");
239: cleanenv[0] = NULL;
240: environ = cleanenv;
241: (void)setenv("PATH", _PATH_DEFPATH, 1);
242: (void)setenv("TERM", p, 1);
243: if (chdir(pwd->pw_dir) < 0) {
244: fprintf(stderr, "su: no directory\n");
245: exit(1);
246: }
247: }
248: if (asthem || pwd->pw_uid)
249: (void)setenv("USER", pwd->pw_name, 1);
250: (void)setenv("HOME", pwd->pw_dir, 1);
251: (void)setenv("SHELL", shell, 1);
252: }
253:
254: if (iscsh == YES) {
255: if (fastlogin)
256: *np-- = "-f";
257: if (asme)
258: *np-- = "-m";
259: }
260:
261: if (asthem) {
262: avshellbuf[0] = '-';
263: strcpy(avshellbuf+1, avshell);
264: avshell = avshellbuf;
265: } else if (iscsh == YES) {
266: /* csh strips the first character... */
267: avshellbuf[0] = '_';
268: strcpy(avshellbuf+1, avshell);
269: avshell = avshellbuf;
270: }
271:
272: *np = avshell;
273:
274: if (ruid != 0)
275: syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
276: username, user, ontty());
277:
278: (void)setpriority(PRIO_PROCESS, 0, prio);
279:
280: execv(shell, np);
281: (void)fprintf(stderr, "su: %s not found.\n", shell);
282: exit(1);
283: }
284:
285: int
286: chshell(sh)
287: char *sh;
288: {
289: register char *cp;
290: char *getusershell();
291:
292: while ((cp = getusershell()) != NULL)
293: if (!strcmp(cp, sh))
294: return (1);
295: return (0);
296: }
297:
298: char *
299: ontty()
300: {
301: char *p, *ttyname();
302: static char buf[MAXPATHLEN + 4];
303:
304: buf[0] = 0;
305: if (p = ttyname(STDERR_FILENO))
306: sprintf(buf, " on %s", p);
307: return (buf);
308: }
309:
310: #ifdef KERBEROS
311: kerberos(username, user, uid)
312: char *username, *user;
313: int uid;
314: {
315: KTEXT_ST ticket;
316: AUTH_DAT authdata;
317: struct hostent *hp;
318: register char *p;
319: int kerno;
320: u_long faddr;
321: char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
322: char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
323: char *ontty(), *krb_get_phost();
324:
325: if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
326: return (1);
327: if (koktologin(username, lrealm, user) && !uid) {
328: (void)fprintf(stderr, "kerberos su: not in %s's ACL.\n", user);
329: return (1);
330: }
331: (void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid());
332:
333: (void)setenv("KRBTKFILE", krbtkfile, 1);
334: (void)krb_set_tkt_string(krbtkfile);
335: /*
336: * Set real as well as effective ID to 0 for the moment,
337: * to make the kerberos library do the right thing.
338: */
339: if (setuid(0) < 0) {
340: perror("su: setuid");
341: return (1);
342: }
343:
344: /*
345: * Little trick here -- if we are su'ing to root,
346: * we need to get a ticket for "xxx.root", where xxx represents
347: * the name of the person su'ing. Otherwise (non-root case),
348: * we need to get a ticket for "yyy.", where yyy represents
349: * the name of the person being su'd to, and the instance is null
350: *
351: * We should have a way to set the ticket lifetime,
352: * with a system default for root.
353: */
354: kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
355: (uid == 0 ? "root" : ""), lrealm,
356: "krbtgt", lrealm, DEFAULT_TKT_LIFE, 0);
357:
358: if (kerno != KSUCCESS) {
359: if (kerno == KDC_PR_UNKNOWN) {
360: fprintf(stderr, "principal unknown: %s.%s@%s\n",
361: (uid == 0 ? username : user),
362: (uid == 0 ? "root" : ""), lrealm);
363: return (1);
364: }
365: (void)fprintf(stderr, "su: unable to su: %s\n",
366: krb_err_txt[kerno]);
367: syslog(LOG_NOTICE|LOG_AUTH,
368: "BAD Kerberos SU: %s to %s%s: %s",
369: username, user, ontty(), krb_err_txt[kerno]);
370: return (1);
371: }
372:
373: if (chown(krbtkfile, uid, -1) < 0) {
374: perror("su: chown:");
375: (void)unlink(krbtkfile);
376: return (1);
377: }
378:
379: (void)setpriority(PRIO_PROCESS, 0, -2);
380:
381: if (gethostname(hostname, sizeof(hostname)) == -1) {
382: perror("su: gethostname");
383: dest_tkt();
384: return (1);
385: }
386:
387: (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
388: savehost[sizeof(savehost) - 1] = '\0';
389:
390: kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
391:
392: if (kerno == KDC_PR_UNKNOWN) {
393: (void)fprintf(stderr, "Warning: TGT not verified.\n");
394: syslog(LOG_NOTICE|LOG_AUTH,
395: "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
396: username, user, ontty(), krb_err_txt[kerno],
397: "rcmd", savehost);
398: } else if (kerno != KSUCCESS) {
399: (void)fprintf(stderr, "Unable to use TGT: %s\n",
400: krb_err_txt[kerno]);
401: syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
402: username, user, ontty(), krb_err_txt[kerno]);
403: dest_tkt();
404: return (1);
405: } else {
406: if (!(hp = gethostbyname(hostname))) {
407: (void)fprintf(stderr, "su: can't get addr of %s\n",
408: hostname);
409: dest_tkt();
410: return (1);
411: }
412: (void)bcopy((char *)hp->h_addr, (char *)&faddr, sizeof(faddr));
413:
414: if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
415: &authdata, "")) != KSUCCESS) {
416: (void)fprintf(stderr,
417: "su: unable to verify rcmd ticket: %s\n",
418: krb_err_txt[kerno]);
419: syslog(LOG_NOTICE|LOG_AUTH,
420: "failed su: %s to %s%s: %s", username,
421: user, ontty(), krb_err_txt[kerno]);
422: dest_tkt();
423: return (1);
424: }
425: }
426: return (0);
427: }
428:
429: koktologin(name, realm, toname)
430: char *name, *realm, *toname;
431: {
432: register AUTH_DAT *kdata;
433: AUTH_DAT kdata_st;
434:
435: kdata = &kdata_st;
436: bzero((caddr_t) kdata, sizeof(*kdata));
437: (void)strcpy(kdata->pname, name);
438: (void)strcpy(kdata->pinst,
439: ((strcmp(toname, "root") == 0) ? "root" : ""));
440: (void)strcpy(kdata->prealm, realm);
441: return (kuserok(kdata, toname));
442: }
443: #endif