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