summaryrefslogtreecommitdiff
path: root/src/lib/libc/net/rcmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libc/net/rcmd.c')
-rw-r--r--src/lib/libc/net/rcmd.c607
1 files changed, 607 insertions, 0 deletions
diff --git a/src/lib/libc/net/rcmd.c b/src/lib/libc/net/rcmd.c
new file mode 100644
index 0000000000..c933f5b447
--- /dev/null
+++ b/src/lib/libc/net/rcmd.c
@@ -0,0 +1,607 @@
1/*
2 * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved.
3 * Copyright (c) 1983, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * This product includes software developed by Theo de Raadt.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#if defined(LIBC_SCCS) && !defined(lint)
37static char *rcsid = "$OpenBSD: rcmd.c,v 1.31 1998/03/19 00:30:05 millert Exp $";
38#endif /* LIBC_SCCS and not lint */
39
40#include <sys/param.h>
41#include <sys/socket.h>
42#include <sys/stat.h>
43
44#include <netinet/in.h>
45#include <arpa/inet.h>
46
47#include <signal.h>
48#include <fcntl.h>
49#include <netdb.h>
50#include <unistd.h>
51#include <pwd.h>
52#include <errno.h>
53#include <stdio.h>
54#include <ctype.h>
55#include <string.h>
56#include <syslog.h>
57#include <stdlib.h>
58#include <netgroup.h>
59
60int __ivaliduser __P((FILE *, in_addr_t, const char *, const char *));
61static int __icheckhost __P((u_int32_t, const char *));
62static char *__gethostloop __P((u_int32_t));
63
64int
65rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
66 char **ahost;
67 in_port_t rport;
68 const char *locuser, *remuser, *cmd;
69 int *fd2p;
70{
71 struct hostent *hp;
72 struct sockaddr_in sin, from;
73 fd_set *readsp = NULL;
74 int oldmask;
75 pid_t pid;
76 int s, lport, timo;
77 char c, *p;
78
79 /* call rcmdsh() with specified remote shell if appropriate. */
80 if (!issetugid() && (p = getenv("RSH"))) {
81 struct servent *sp = getservbyname("shell", "tcp");
82
83 if (sp && sp->s_port == rport)
84 return (rcmdsh(ahost, rport, locuser, remuser,
85 cmd, p));
86 }
87
88 /* use rsh(1) if non-root and remote port is shell. */
89 if (geteuid()) {
90 struct servent *sp = getservbyname("shell", "tcp");
91
92 if (sp && sp->s_port == rport)
93 return (rcmdsh(ahost, rport, locuser, remuser,
94 cmd, NULL));
95 }
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
105 oldmask = sigblock(sigmask(SIGURG));
106 for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
107 s = rresvport(&lport);
108 if (s < 0) {
109 if (errno == EAGAIN)
110 (void)fprintf(stderr,
111 "rcmd: socket: All ports in use\n");
112 else
113 (void)fprintf(stderr, "rcmd: socket: %s\n",
114 strerror(errno));
115 sigsetmask(oldmask);
116 return (-1);
117 }
118 fcntl(s, F_SETOWN, pid);
119 bzero(&sin, sizeof sin);
120 sin.sin_len = sizeof(struct sockaddr_in);
121 sin.sin_family = hp->h_addrtype;
122 sin.sin_port = rport;
123 bcopy(hp->h_addr_list[0], &sin.sin_addr, hp->h_length);
124 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
125 break;
126 (void)close(s);
127 if (errno == EADDRINUSE) {
128 lport--;
129 continue;
130 }
131 if (errno == ECONNREFUSED && timo <= 16) {
132 (void)sleep(timo);
133 timo *= 2;
134 continue;
135 }
136 if (hp->h_addr_list[1] != NULL) {
137 int oerrno = errno;
138
139 (void)fprintf(stderr, "connect to address %s: ",
140 inet_ntoa(sin.sin_addr));
141 errno = oerrno;
142 perror(0);
143 hp->h_addr_list++;
144 bcopy(hp->h_addr_list[0], &sin.sin_addr, hp->h_length);
145 (void)fprintf(stderr, "Trying %s...\n",
146 inet_ntoa(sin.sin_addr));
147 continue;
148 }
149 (void)fprintf(stderr, "%s: %s\n", hp->h_name, strerror(errno));
150 sigsetmask(oldmask);
151 return (-1);
152 }
153#if 0
154 /*
155 * try to rresvport() to the same port. This will make rresvport()
156 * fail it's first bind, resulting in it choosing a random port.
157 */
158 lport--;
159#endif
160 if (fd2p == 0) {
161 write(s, "", 1);
162 lport = 0;
163 } else {
164 char num[8];
165 int s2 = rresvport(&lport), s3;
166 int len = sizeof(from);
167 int fdssize = howmany(MAX(s, s2)+1, NFDBITS) * sizeof(fd_mask);
168
169 if (s2 < 0)
170 goto bad;
171 readsp = (fd_set *)malloc(fdssize);
172 if (readsp == NULL)
173 goto bad;
174 listen(s2, 1);
175 (void)snprintf(num, sizeof(num), "%d", lport);
176 if (write(s, num, strlen(num)+1) != strlen(num)+1) {
177 (void)fprintf(stderr,
178 "rcmd: write (setting up stderr): %s\n",
179 strerror(errno));
180 (void)close(s2);
181 goto bad;
182 }
183again:
184 bzero(readsp, fdssize);
185 FD_SET(s, readsp);
186 FD_SET(s2, readsp);
187 errno = 0;
188 if (select(MAX(s, s2) + 1, readsp, 0, 0, 0) < 1 ||
189 !FD_ISSET(s2, readsp)) {
190 if (errno != 0)
191 (void)fprintf(stderr,
192 "rcmd: select (setting up stderr): %s\n",
193 strerror(errno));
194 else
195 (void)fprintf(stderr,
196 "select: protocol failure in circuit setup\n");
197 (void)close(s2);
198 goto bad;
199 }
200 s3 = accept(s2, (struct sockaddr *)&from, &len);
201 /*
202 * XXX careful for ftp bounce attacks. If discovered, shut them
203 * down and check for the real auxiliary channel to connect.
204 */
205 if (from.sin_family == AF_INET && from.sin_port == htons(20)) {
206 close(s3);
207 goto again;
208 }
209 (void)close(s2);
210 if (s3 < 0) {
211 (void)fprintf(stderr,
212 "rcmd: accept: %s\n", strerror(errno));
213 lport = 0;
214 goto bad;
215 }
216 *fd2p = s3;
217 from.sin_port = ntohs(from.sin_port);
218 if (from.sin_family != AF_INET ||
219 from.sin_port >= IPPORT_RESERVED ||
220 from.sin_port < IPPORT_RESERVED / 2) {
221 (void)fprintf(stderr,
222 "socket: protocol failure in circuit setup.\n");
223 goto bad2;
224 }
225 }
226 (void)write(s, locuser, strlen(locuser)+1);
227 (void)write(s, remuser, strlen(remuser)+1);
228 (void)write(s, cmd, strlen(cmd)+1);
229 if (read(s, &c, 1) != 1) {
230 (void)fprintf(stderr,
231 "rcmd: %s: %s\n", *ahost, strerror(errno));
232 goto bad2;
233 }
234 if (c != 0) {
235 while (read(s, &c, 1) == 1) {
236 (void)write(STDERR_FILENO, &c, 1);
237 if (c == '\n')
238 break;
239 }
240 goto bad2;
241 }
242 sigsetmask(oldmask);
243 free(readsp);
244 return (s);
245bad2:
246 if (lport)
247 (void)close(*fd2p);
248bad:
249 if (readsp)
250 free(readsp);
251 (void)close(s);
252 sigsetmask(oldmask);
253 return (-1);
254}
255
256int
257rresvport(alport)
258 int *alport;
259{
260 struct sockaddr_in sin;
261 int s;
262
263 bzero(&sin, sizeof sin);
264 sin.sin_len = sizeof(struct sockaddr_in);
265 sin.sin_family = AF_INET;
266 sin.sin_addr.s_addr = INADDR_ANY;
267 s = socket(AF_INET, SOCK_STREAM, 0);
268 if (s < 0)
269 return (-1);
270 sin.sin_port = htons((in_port_t)*alport);
271 if (*alport < IPPORT_RESERVED - 1) {
272 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
273 return (s);
274 if (errno != EADDRINUSE) {
275 (void)close(s);
276 return (-1);
277 }
278 }
279 sin.sin_port = 0;
280 if (bindresvport(s, &sin) == -1) {
281 (void)close(s);
282 return (-1);
283 }
284 *alport = (int)ntohs(sin.sin_port);
285 return (s);
286}
287
288int __check_rhosts_file = 1;
289char *__rcmd_errstr;
290
291int
292ruserok(rhost, superuser, ruser, luser)
293 const char *rhost, *ruser, *luser;
294 int superuser;
295{
296 struct hostent *hp;
297 char **ap;
298 int i;
299#define MAXADDRS 35
300 u_int32_t addrs[MAXADDRS + 1];
301
302 if ((hp = gethostbyname(rhost)) == NULL)
303 return (-1);
304 for (i = 0, ap = hp->h_addr_list; *ap && i < MAXADDRS; ++ap, ++i)
305 bcopy(*ap, &addrs[i], sizeof(addrs[i]));
306 addrs[i] = 0;
307
308 for (i = 0; i < MAXADDRS && addrs[i]; i++)
309 if (iruserok((in_addr_t)addrs[i], superuser, ruser, luser) == 0)
310 return (0);
311 return (-1);
312}
313
314/*
315 * New .rhosts strategy: We are passed an ip address. We spin through
316 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
317 * has ip addresses, we don't have to trust a nameserver. When it
318 * contains hostnames, we spin through the list of addresses the nameserver
319 * gives us and look for a match.
320 *
321 * Returns 0 if ok, -1 if not ok.
322 */
323int
324iruserok(raddr, superuser, ruser, luser)
325 u_int32_t raddr;
326 int superuser;
327 const char *ruser, *luser;
328{
329 register char *cp;
330 struct stat sbuf;
331 struct passwd *pwd;
332 FILE *hostf;
333 uid_t uid;
334 int first;
335 char pbuf[MAXPATHLEN];
336
337 first = 1;
338 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
339again:
340 if (hostf) {
341 if (__ivaliduser(hostf, raddr, luser, ruser) == 0) {
342 (void)fclose(hostf);
343 return (0);
344 }
345 (void)fclose(hostf);
346 }
347 if (first == 1 && (__check_rhosts_file || superuser)) {
348 first = 0;
349 if ((pwd = getpwnam(luser)) == NULL)
350 return (-1);
351 (void)strcpy(pbuf, pwd->pw_dir);
352 (void)strcat(pbuf, "/.rhosts");
353
354 /*
355 * Change effective uid while opening .rhosts. If root and
356 * reading an NFS mounted file system, can't read files that
357 * are protected read/write owner only.
358 */
359 uid = geteuid();
360 (void)seteuid(pwd->pw_uid);
361 hostf = fopen(pbuf, "r");
362 (void)seteuid(uid);
363
364 if (hostf == NULL)
365 return (-1);
366 /*
367 * If not a regular file, or is owned by someone other than
368 * user or root or if writeable by anyone but the owner, quit.
369 */
370 cp = NULL;
371 if (lstat(pbuf, &sbuf) < 0)
372 cp = ".rhosts lstat failed";
373 else if (!S_ISREG(sbuf.st_mode))
374 cp = ".rhosts not regular file";
375 else if (fstat(fileno(hostf), &sbuf) < 0)
376 cp = ".rhosts fstat failed";
377 else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
378 cp = "bad .rhosts owner";
379 else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
380 cp = ".rhosts writeable by other than owner";
381 /* If there were any problems, quit. */
382 if (cp) {
383 __rcmd_errstr = cp;
384 (void)fclose(hostf);
385 return (-1);
386 }
387 goto again;
388 }
389 return (-1);
390}
391
392/*
393 * XXX
394 * Don't make static, used by lpd(8).
395 *
396 * Returns 0 if ok, -1 if not ok.
397 */
398int
399__ivaliduser(hostf, raddrl, luser, ruser)
400 FILE *hostf;
401 in_addr_t raddrl;
402 const char *luser, *ruser;
403{
404 register char *user, *p;
405 char *buf;
406 const char *auser, *ahost;
407 int hostok, userok;
408 char *rhost = (char *)-1;
409 char domain[MAXHOSTNAMELEN];
410 u_int32_t raddr = (u_int32_t)raddrl;
411 size_t buflen;
412
413 getdomainname(domain, sizeof(domain));
414
415 while ((buf = fgetln(hostf, &buflen))) {
416 p = buf;
417 if (*p == '#')
418 continue;
419 while (*p != '\n' && *p != ' ' && *p != '\t' && p < buf + buflen) {
420 if (!isprint(*p))
421 goto bail;
422 *p = isupper(*p) ? tolower(*p) : *p;
423 p++;
424 }
425 if (p >= buf + buflen)
426 continue;
427 if (*p == ' ' || *p == '\t') {
428 *p++ = '\0';
429 while ((*p == ' ' || *p == '\t') && p < buf + buflen)
430 p++;
431 if (p >= buf + buflen)
432 continue;
433 user = p;
434 while (*p != '\n' && *p != ' ' &&
435 *p != '\t' && p < buf + buflen) {
436 if (!isprint(*p))
437 goto bail;
438 p++;
439 }
440 } else
441 user = p;
442 *p = '\0';
443
444 if (p == buf)
445 continue;
446
447 auser = *user ? user : luser;
448 ahost = buf;
449
450 if (strlen(ahost) >= MAXHOSTNAMELEN)
451 continue;
452
453 /*
454 * innetgr() must lookup a hostname (we do not attempt
455 * to change the semantics so that netgroups may have
456 * #.#.#.# addresses in the list.)
457 */
458 if (ahost[0] == '+')
459 switch (ahost[1]) {
460 case '\0':
461 hostok = 1;
462 break;
463 case '@':
464 if (rhost == (char *)-1)
465 rhost = __gethostloop(raddr);
466 hostok = 0;
467 if (rhost)
468 hostok = innetgr(&ahost[2], rhost,
469 NULL, domain);
470 break;
471 default:
472 hostok = __icheckhost(raddr, &ahost[1]);
473 break;
474 }
475 else if (ahost[0] == '-')
476 switch (ahost[1]) {
477 case '\0':
478 hostok = -1;
479 break;
480 case '@':
481 if (rhost == (char *)-1)
482 rhost = __gethostloop(raddr);
483 hostok = 0;
484 if (rhost)
485 hostok = -innetgr(&ahost[2], rhost,
486 NULL, domain);
487 break;
488 default:
489 hostok = -__icheckhost(raddr, &ahost[1]);
490 break;
491 }
492 else
493 hostok = __icheckhost(raddr, ahost);
494
495
496 if (auser[0] == '+')
497 switch (auser[1]) {
498 case '\0':
499 userok = 1;
500 break;
501 case '@':
502 userok = innetgr(&auser[2], NULL, ruser,
503 domain);
504 break;
505 default:
506 userok = strcmp(ruser, &auser[1]) ? 0 : 1;
507 break;
508 }
509 else if (auser[0] == '-')
510 switch (auser[1]) {
511 case '\0':
512 userok = -1;
513 break;
514 case '@':
515 userok = -innetgr(&auser[2], NULL, ruser,
516 domain);
517 break;
518 default:
519 userok = strcmp(ruser, &auser[1]) ? 0 : -1;
520 break;
521 }
522 else
523 userok = strcmp(ruser, auser) ? 0 : 1;
524
525 /* Check if one component did not match */
526 if (hostok == 0 || userok == 0)
527 continue;
528
529 /* Check if we got a forbidden pair */
530 if (userok <= -1 || hostok <= -1)
531 return (-1);
532
533 /* Check if we got a valid pair */
534 if (hostok >= 1 && userok >= 1)
535 return (0);
536 }
537bail:
538 return (-1);
539}
540
541/*
542 * Returns "true" if match, 0 if no match. If we do not find any
543 * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work.
544 */
545static int
546__icheckhost(raddr, lhost)
547 u_int32_t raddr;
548 const char *lhost;
549{
550 register struct hostent *hp;
551 register char **pp;
552 struct in_addr in;
553
554 hp = gethostbyname(lhost);
555 if (hp != NULL) {
556 /* Spin through ip addresses. */
557 for (pp = hp->h_addr_list; *pp; ++pp)
558 if (!bcmp(&raddr, *pp, sizeof(raddr)))
559 return (1);
560 }
561
562 in.s_addr = raddr;
563 if (strcmp(lhost, inet_ntoa(in)) == 0)
564 return (1);
565 return (0);
566}
567
568/*
569 * Return the hostname associated with the supplied address.
570 * Do a reverse lookup as well for security. If a loop cannot
571 * be found, pack the result of inet_ntoa() into the string.
572 */
573static char *
574__gethostloop(raddr)
575 u_int32_t raddr;
576{
577 static char remotehost[MAXHOSTNAMELEN];
578 struct hostent *hp;
579 struct in_addr in;
580
581 hp = gethostbyaddr((char *) &raddr, sizeof(raddr), AF_INET);
582 if (hp == NULL)
583 return (NULL);
584
585 /*
586 * Look up the name and check that the supplied
587 * address is in the list
588 */
589 strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
590 remotehost[sizeof(remotehost) - 1] = '\0';
591 hp = gethostbyname(remotehost);
592 if (hp == NULL)
593 return (NULL);
594
595 for (; hp->h_addr_list[0] != NULL; hp->h_addr_list++)
596 if (!bcmp(hp->h_addr_list[0], (caddr_t)&raddr, sizeof(raddr)))
597 return (remotehost);
598
599 /*
600 * either the DNS adminstrator has made a configuration
601 * mistake, or someone has attempted to spoof us
602 */
603 in.s_addr = raddr;
604 syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s",
605 inet_ntoa(in), hp->h_name);
606 return (NULL);
607}