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