]>
Commit | Line | Data |
---|---|---|
5b2abdfb A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * Copyright (c) 1983, 1993, 1994 | |
24 | * The Regents of the University of California. All rights reserved. | |
25 | * | |
26 | * Redistribution and use in source and binary forms, with or without | |
27 | * modification, are permitted provided that the following conditions | |
28 | * are met: | |
29 | * 1. Redistributions of source code must retain the above copyright | |
30 | * notice, this list of conditions and the following disclaimer. | |
31 | * 2. Redistributions in binary form must reproduce the above copyright | |
32 | * notice, this list of conditions and the following disclaimer in the | |
33 | * documentation and/or other materials provided with the distribution. | |
34 | * 3. All advertising materials mentioning features or use of this software | |
35 | * must display the following acknowledgement: | |
36 | * This product includes software developed by the University of | |
37 | * California, Berkeley and its contributors. | |
38 | * 4. Neither the name of the University nor the names of its contributors | |
39 | * may be used to endorse or promote products derived from this software | |
40 | * without specific prior written permission. | |
41 | * | |
42 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
43 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
44 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
45 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
46 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
47 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
48 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
49 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
50 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
51 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
52 | * SUCH DAMAGE. | |
53 | */ | |
54 | ||
55 | #if defined(LIBC_SCCS) && !defined(lint) | |
56 | static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; | |
57 | #endif /* LIBC_SCCS and not lint */ | |
58 | ||
59 | #include <sys/param.h> | |
60 | #include <sys/socket.h> | |
61 | #include <sys/stat.h> | |
62 | ||
63 | #include <netinet/in.h> | |
64 | #include <arpa/inet.h> | |
65 | ||
66 | #include <signal.h> | |
67 | #include <fcntl.h> | |
68 | #include <netdb.h> | |
69 | #include <unistd.h> | |
70 | #include <pwd.h> | |
71 | #include <errno.h> | |
72 | #include <stdio.h> | |
73 | #include <ctype.h> | |
74 | #include <string.h> | |
75 | ||
76 | int __ivaliduser __P((FILE *, u_long, const char *, const char *)); | |
77 | static int __icheckhost __P((u_long, char *)); | |
78 | ||
79 | int | |
80 | rcmd(ahost, rport, locuser, remuser, cmd, fd2p) | |
81 | char **ahost; | |
82 | u_short rport; | |
83 | const char *locuser, *remuser, *cmd; | |
84 | int *fd2p; | |
85 | { | |
86 | struct hostent *hp; | |
87 | struct sockaddr_in sin, from; | |
88 | fd_set reads; | |
89 | long oldmask; | |
90 | pid_t pid; | |
91 | int s, lport, timo; | |
92 | char c; | |
93 | ||
94 | pid = getpid(); | |
95 | hp = gethostbyname(*ahost); | |
96 | if (hp == NULL) { | |
97 | herror(*ahost); | |
98 | return (-1); | |
99 | } | |
100 | *ahost = hp->h_name; | |
101 | oldmask = sigblock(sigmask(SIGURG)); | |
102 | for (timo = 1, lport = IPPORT_RESERVED - 1;;) { | |
103 | s = rresvport(&lport); | |
104 | if (s < 0) { | |
105 | if (errno == EAGAIN) | |
106 | (void)fprintf(stderr, | |
107 | "rcmd: socket: All ports in use\n"); | |
108 | else | |
109 | (void)fprintf(stderr, "rcmd: socket: %s\n", | |
110 | strerror(errno)); | |
111 | sigsetmask(oldmask); | |
112 | return (-1); | |
113 | } | |
114 | fcntl(s, F_SETOWN, pid); | |
115 | sin.sin_family = hp->h_addrtype; | |
116 | bcopy(hp->h_addr_list[0], &sin.sin_addr, hp->h_length); | |
117 | sin.sin_port = rport; | |
118 | if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) | |
119 | break; | |
120 | (void)close(s); | |
121 | if (errno == EADDRINUSE) { | |
122 | lport--; | |
123 | continue; | |
124 | } | |
125 | if (errno == ECONNREFUSED && timo <= 16) { | |
126 | (void)sleep(timo); | |
127 | timo *= 2; | |
128 | continue; | |
129 | } | |
130 | if (hp->h_addr_list[1] != NULL) { | |
131 | int oerrno = errno; | |
132 | ||
133 | (void)fprintf(stderr, "connect to address %s: ", | |
134 | inet_ntoa(sin.sin_addr)); | |
135 | errno = oerrno; | |
136 | perror(0); | |
137 | hp->h_addr_list++; | |
138 | bcopy(hp->h_addr_list[0], &sin.sin_addr, hp->h_length); | |
139 | (void)fprintf(stderr, "Trying %s...\n", | |
140 | inet_ntoa(sin.sin_addr)); | |
141 | continue; | |
142 | } | |
143 | (void)fprintf(stderr, "%s: %s\n", hp->h_name, strerror(errno)); | |
144 | sigsetmask(oldmask); | |
145 | return (-1); | |
146 | } | |
147 | lport--; | |
148 | if (fd2p == 0) { | |
149 | write(s, "", 1); | |
150 | lport = 0; | |
151 | } else { | |
152 | char num[8]; | |
153 | int s2 = rresvport(&lport), s3; | |
154 | int len = sizeof(from); | |
155 | ||
156 | if (s2 < 0) | |
157 | goto bad; | |
158 | listen(s2, 1); | |
159 | (void)snprintf(num, sizeof(num), "%d", lport); | |
160 | if (write(s, num, strlen(num)+1) != strlen(num)+1) { | |
161 | (void)fprintf(stderr, | |
162 | "rcmd: write (setting up stderr): %s\n", | |
163 | strerror(errno)); | |
164 | (void)close(s2); | |
165 | goto bad; | |
166 | } | |
167 | FD_ZERO(&reads); | |
168 | FD_SET(s, &reads); | |
169 | FD_SET(s2, &reads); | |
170 | errno = 0; | |
171 | if (select(32, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)) { | |
172 | if (errno != 0) | |
173 | (void)fprintf(stderr, | |
174 | "rcmd: select (setting up stderr): %s\n", | |
175 | strerror(errno)); | |
176 | else | |
177 | (void)fprintf(stderr, | |
178 | "select: protocol failure in circuit setup\n"); | |
179 | (void)close(s2); | |
180 | goto bad; | |
181 | } | |
182 | s3 = accept(s2, (struct sockaddr *)&from, &len); | |
183 | (void)close(s2); | |
184 | if (s3 < 0) { | |
185 | (void)fprintf(stderr, | |
186 | "rcmd: accept: %s\n", strerror(errno)); | |
187 | lport = 0; | |
188 | goto bad; | |
189 | } | |
190 | *fd2p = s3; | |
191 | from.sin_port = ntohs((u_short)from.sin_port); | |
192 | if (from.sin_family != AF_INET || | |
193 | from.sin_port >= IPPORT_RESERVED || | |
194 | from.sin_port < IPPORT_RESERVED / 2) { | |
195 | (void)fprintf(stderr, | |
196 | "socket: protocol failure in circuit setup.\n"); | |
197 | goto bad2; | |
198 | } | |
199 | } | |
200 | (void)write(s, locuser, strlen(locuser)+1); | |
201 | (void)write(s, remuser, strlen(remuser)+1); | |
202 | (void)write(s, cmd, strlen(cmd)+1); | |
203 | if (read(s, &c, 1) != 1) { | |
204 | (void)fprintf(stderr, | |
205 | "rcmd: %s: %s\n", *ahost, strerror(errno)); | |
206 | goto bad2; | |
207 | } | |
208 | if (c != 0) { | |
209 | while (read(s, &c, 1) == 1) { | |
210 | (void)write(STDERR_FILENO, &c, 1); | |
211 | if (c == '\n') | |
212 | break; | |
213 | } | |
214 | goto bad2; | |
215 | } | |
216 | sigsetmask(oldmask); | |
217 | return (s); | |
218 | bad2: | |
219 | if (lport) | |
220 | (void)close(*fd2p); | |
221 | bad: | |
222 | (void)close(s); | |
223 | sigsetmask(oldmask); | |
224 | return (-1); | |
225 | } | |
226 | ||
227 | int | |
228 | rresvport(alport) | |
229 | int *alport; | |
230 | { | |
231 | struct sockaddr_in sin; | |
232 | int s; | |
233 | ||
234 | sin.sin_family = AF_INET; | |
235 | sin.sin_addr.s_addr = INADDR_ANY; | |
236 | s = socket(AF_INET, SOCK_STREAM, 0); | |
237 | if (s < 0) | |
238 | return (-1); | |
239 | for (;;) { | |
240 | sin.sin_port = htons((u_short)*alport); | |
241 | if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) | |
242 | return (s); | |
243 | if (errno != EADDRINUSE) { | |
244 | (void)close(s); | |
245 | return (-1); | |
246 | } | |
247 | (*alport)--; | |
248 | if (*alport == IPPORT_RESERVED/2) { | |
249 | (void)close(s); | |
250 | errno = EAGAIN; /* close */ | |
251 | return (-1); | |
252 | } | |
253 | } | |
254 | } | |
255 | ||
256 | int __check_rhosts_file = 1; | |
257 | char *__rcmd_errstr; | |
258 | ||
259 | int | |
260 | ruserok(rhost, superuser, ruser, luser) | |
261 | const char *rhost, *ruser, *luser; | |
262 | int superuser; | |
263 | { | |
264 | struct hostent *hp; | |
265 | u_long addr; | |
266 | char **ap; | |
267 | ||
268 | if ((hp = gethostbyname(rhost)) == NULL) | |
269 | return (-1); | |
270 | for (ap = hp->h_addr_list; *ap; ++ap) { | |
271 | bcopy(*ap, &addr, sizeof(addr)); | |
272 | if (iruserok(addr, superuser, ruser, luser) == 0) | |
273 | return (0); | |
274 | } | |
275 | return (-1); | |
276 | } | |
277 | ||
278 | /* | |
279 | * New .rhosts strategy: We are passed an ip address. We spin through | |
280 | * hosts.equiv and .rhosts looking for a match. When the .rhosts only | |
281 | * has ip addresses, we don't have to trust a nameserver. When it | |
282 | * contains hostnames, we spin through the list of addresses the nameserver | |
283 | * gives us and look for a match. | |
284 | * | |
285 | * Returns 0 if ok, -1 if not ok. | |
286 | */ | |
287 | int | |
288 | iruserok(raddr, superuser, ruser, luser) | |
289 | u_long raddr; | |
290 | int superuser; | |
291 | const char *ruser, *luser; | |
292 | { | |
293 | register char *cp; | |
294 | struct stat sbuf; | |
295 | struct passwd *pwd; | |
296 | FILE *hostf; | |
297 | uid_t uid; | |
298 | int first; | |
299 | char pbuf[MAXPATHLEN]; | |
300 | ||
301 | first = 1; | |
302 | hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); | |
303 | again: | |
304 | if (hostf) { | |
305 | if (__ivaliduser(hostf, raddr, luser, ruser) == 0) { | |
306 | (void)fclose(hostf); | |
307 | return (0); | |
308 | } | |
309 | (void)fclose(hostf); | |
310 | } | |
311 | if (first == 1 && (__check_rhosts_file || superuser)) { | |
312 | first = 0; | |
313 | if ((pwd = getpwnam(luser)) == NULL) | |
314 | return (-1); | |
315 | (void)strcpy(pbuf, pwd->pw_dir); | |
316 | (void)strcat(pbuf, "/.rhosts"); | |
317 | ||
318 | /* | |
319 | * Change effective uid while opening .rhosts. If root and | |
320 | * reading an NFS mounted file system, can't read files that | |
321 | * are protected read/write owner only. | |
322 | */ | |
323 | uid = geteuid(); | |
324 | (void)seteuid(pwd->pw_uid); | |
325 | hostf = fopen(pbuf, "r"); | |
326 | (void)seteuid(uid); | |
327 | ||
328 | if (hostf == NULL) | |
329 | return (-1); | |
330 | /* | |
331 | * If not a regular file, or is owned by someone other than | |
332 | * user or root or if writeable by anyone but the owner, quit. | |
333 | */ | |
334 | cp = NULL; | |
335 | if (lstat(pbuf, &sbuf) < 0) | |
336 | cp = ".rhosts lstat failed"; | |
337 | else if (!S_ISREG(sbuf.st_mode)) | |
338 | cp = ".rhosts not regular file"; | |
339 | else if (fstat(fileno(hostf), &sbuf) < 0) | |
340 | cp = ".rhosts fstat failed"; | |
341 | else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) | |
342 | cp = "bad .rhosts owner"; | |
343 | else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) | |
344 | cp = ".rhosts writeable by other than owner"; | |
345 | /* If there were any problems, quit. */ | |
346 | if (cp) { | |
347 | __rcmd_errstr = cp; | |
348 | (void)fclose(hostf); | |
349 | return (-1); | |
350 | } | |
351 | goto again; | |
352 | } | |
353 | return (-1); | |
354 | } | |
355 | ||
356 | /* | |
357 | * XXX | |
358 | * Don't make static, used by lpd(8). | |
359 | * | |
360 | * Returns 0 if ok, -1 if not ok. | |
361 | */ | |
362 | int | |
363 | __ivaliduser(hostf, raddr, luser, ruser) | |
364 | FILE *hostf; | |
365 | u_long raddr; | |
366 | const char *luser, *ruser; | |
367 | { | |
368 | register char *user, *p; | |
369 | int ch; | |
370 | char buf[MAXHOSTNAMELEN + 128]; /* host + login */ | |
371 | ||
372 | while (fgets(buf, sizeof(buf), hostf)) { | |
373 | p = buf; | |
374 | /* Skip lines that are too long. */ | |
375 | if (strchr(p, '\n') == NULL) { | |
376 | while ((ch = getc(hostf)) != '\n' && ch != EOF); | |
377 | continue; | |
378 | } | |
379 | while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { | |
380 | *p = isupper(*p) ? tolower(*p) : *p; | |
381 | p++; | |
382 | } | |
383 | if (*p == ' ' || *p == '\t') { | |
384 | *p++ = '\0'; | |
385 | while (*p == ' ' || *p == '\t') | |
386 | p++; | |
387 | user = p; | |
388 | while (*p != '\n' && *p != ' ' && | |
389 | *p != '\t' && *p != '\0') | |
390 | p++; | |
391 | } else | |
392 | user = p; | |
393 | *p = '\0'; | |
394 | if (__icheckhost(raddr, buf) && | |
395 | strcmp(ruser, *user ? user : luser) == 0) { | |
396 | return (0); | |
397 | } | |
398 | } | |
399 | return (-1); | |
400 | } | |
401 | ||
402 | /* | |
403 | * Returns "true" if match, 0 if no match. | |
404 | */ | |
405 | static int | |
406 | __icheckhost(raddr, lhost) | |
407 | u_long raddr; | |
408 | register char *lhost; | |
409 | { | |
410 | register struct hostent *hp; | |
411 | register u_long laddr; | |
412 | register char **pp; | |
413 | ||
414 | /* Try for raw ip address first. */ | |
415 | if (isdigit(*lhost) && (long)(laddr = inet_addr(lhost)) != -1) | |
416 | return (raddr == laddr); | |
417 | ||
418 | /* Better be a hostname. */ | |
419 | if ((hp = gethostbyname(lhost)) == NULL) | |
420 | return (0); | |
421 | ||
422 | /* Spin through ip addresses. */ | |
423 | for (pp = hp->h_addr_list; *pp; ++pp) | |
424 | if (!bcmp(&raddr, *pp, sizeof(u_long))) | |
425 | return (1); | |
426 | ||
427 | /* No match. */ | |
428 | return (0); | |
429 | } |