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