2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved.
26 * Copyright (c) 1983, 1993, 1994
27 * The Regents of the University of California. All rights reserved.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
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 * This product includes software developed by Theo de Raadt.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 #if defined(LIBC_SCCS) && !defined(lint)
60 static char *rcsid
= "$OpenBSD: rcmd.c,v 1.30 1998/02/12 02:21:19 deraadt Exp $";
61 #endif /* LIBC_SCCS and not lint */
63 #include <sys/param.h>
64 #include <sys/socket.h>
67 #include <netinet/in.h>
68 #include <arpa/inet.h>
69 #include <netinfo/ni_util.h>
83 typedef u_int32_t in_addr_t
; /* base type for internet address */
84 typedef u_int16_t in_port_t
; /* IP port type */
86 extern int bindresvport(int sd
, struct sockaddr_in
*sin
);
87 extern int getdomainname(char *val
, size_t len
);
88 extern int rcmdsh(char **ahost
, int rport
, const char *locuser
, const char *remuser
, const char *cmd
, char *rshprog
);
90 int __ivaliduser
__P((FILE *, in_addr_t
, const char *, const char *));
91 static int __icheckhost
__P((u_int32_t
, const char *));
92 static char *__gethostloop
__P((u_int32_t
));
95 rcmd(ahost
, rport
, locuser
, remuser
, cmd
, fd2p
)
98 const char *locuser
, *remuser
, *cmd
;
102 struct sockaddr_in sin
, from
;
103 fd_set
*readsp
= NULL
;
109 /* call rcmdsh() with specified remote shell if appropriate. */
110 if ((getuid() == geteuid()) && (p
= getenv("RSH"))) {
111 struct servent
*sp
= getservbyname("shell", "tcp");
113 if (sp
&& sp
->s_port
== rport
)
114 return (rcmdsh(ahost
, rport
, locuser
, remuser
,
118 /* use rsh(1) if non-root and remote port is shell. */
120 struct servent
*sp
= getservbyname("shell", "tcp");
122 if (sp
&& sp
->s_port
== rport
)
123 return (rcmdsh(ahost
, rport
, locuser
, remuser
,
128 hp
= gethostbyname(*ahost
);
135 oldmask
= sigblock(sigmask(SIGURG
));
136 for (timo
= 1, lport
= IPPORT_RESERVED
- 1;;) {
137 s
= rresvport(&lport
);
140 (void)fprintf(stderr
,
141 "rcmd: socket: All ports in use\n");
143 (void)fprintf(stderr
, "rcmd: socket: %s\n",
148 fcntl(s
, F_SETOWN
, pid
);
149 bzero(&sin
, sizeof sin
);
150 sin
.sin_len
= sizeof(struct sockaddr_in
);
151 sin
.sin_family
= hp
->h_addrtype
;
152 sin
.sin_port
= rport
;
153 bcopy(hp
->h_addr_list
[0], &sin
.sin_addr
, hp
->h_length
);
154 if (connect(s
, (struct sockaddr
*)&sin
, sizeof(sin
)) >= 0)
157 if (errno
== EADDRINUSE
) {
161 if (errno
== ECONNREFUSED
&& timo
<= 16) {
166 if (hp
->h_addr_list
[1] != NULL
) {
169 (void)fprintf(stderr
, "connect to address %s: ",
170 inet_ntoa(sin
.sin_addr
));
174 bcopy(hp
->h_addr_list
[0], &sin
.sin_addr
, hp
->h_length
);
175 (void)fprintf(stderr
, "Trying %s...\n",
176 inet_ntoa(sin
.sin_addr
));
179 (void)fprintf(stderr
, "%s: %s\n", hp
->h_name
, strerror(errno
));
185 * try to rresvport() to the same port. This will make rresvport()
186 * fail it's first bind, resulting in it choosing a random port.
195 int s2
= rresvport(&lport
), s3
;
196 int len
= sizeof(from
);
197 int fdssize
= howmany(MAX(s
, s2
)+1, NFDBITS
) * sizeof(fd_mask
);
201 readsp
= (fd_set
*)malloc(fdssize
);
205 (void)snprintf(num
, sizeof(num
), "%d", lport
);
206 if (write(s
, num
, strlen(num
)+1) != strlen(num
)+1) {
207 (void)fprintf(stderr
,
208 "rcmd: write (setting up stderr): %s\n",
214 bzero(readsp
, fdssize
);
218 if (select(MAX(s
, s2
) + 1, readsp
, 0, 0, 0) < 1 ||
219 !FD_ISSET(s2
, readsp
)) {
221 (void)fprintf(stderr
,
222 "rcmd: select (setting up stderr): %s\n",
225 (void)fprintf(stderr
,
226 "select: protocol failure in circuit setup\n");
230 s3
= accept(s2
, (struct sockaddr
*)&from
, &len
);
232 * XXX careful for ftp bounce attacks. If discovered, shut them
233 * down and check for the real auxiliary channel to connect.
235 if (from
.sin_family
== AF_INET
&& from
.sin_port
== htons(20)) {
241 (void)fprintf(stderr
,
242 "rcmd: accept: %s\n", strerror(errno
));
247 from
.sin_port
= ntohs(from
.sin_port
);
248 if (from
.sin_family
!= AF_INET
||
249 from
.sin_port
>= IPPORT_RESERVED
||
250 from
.sin_port
< IPPORT_RESERVED
/ 2) {
251 (void)fprintf(stderr
,
252 "socket: protocol failure in circuit setup.\n");
256 (void)write(s
, locuser
, strlen(locuser
)+1);
257 (void)write(s
, remuser
, strlen(remuser
)+1);
258 (void)write(s
, cmd
, strlen(cmd
)+1);
259 if (read(s
, &c
, 1) != 1) {
260 (void)fprintf(stderr
,
261 "rcmd: %s: %s\n", *ahost
, strerror(errno
));
265 while (read(s
, &c
, 1) == 1) {
266 (void)write(STDERR_FILENO
, &c
, 1);
290 struct sockaddr_in sin
;
293 bzero(&sin
, sizeof sin
);
294 sin
.sin_len
= sizeof(struct sockaddr_in
);
295 sin
.sin_family
= AF_INET
;
296 sin
.sin_addr
.s_addr
= INADDR_ANY
;
297 s
= socket(AF_INET
, SOCK_STREAM
, 0);
300 sin
.sin_port
= htons((in_port_t
)*alport
);
301 if (*alport
< IPPORT_RESERVED
- 1) {
302 if (bind(s
, (struct sockaddr
*)&sin
, sizeof(sin
)) >= 0)
304 if (errno
!= EADDRINUSE
) {
310 if (bindresvport(s
, &sin
) == -1) {
314 *alport
= (int)ntohs(sin
.sin_port
);
318 int __check_rhosts_file
= 1;
322 ruserok(rhost
, superuser
, ruser
, luser
)
323 const char *rhost
, *ruser
, *luser
;
330 u_int32_t addrs
[MAXADDRS
+ 1];
332 if ((hp
= gethostbyname(rhost
)) == NULL
)
334 for (i
= 0, ap
= hp
->h_addr_list
; *ap
&& i
< MAXADDRS
; ++ap
, ++i
)
335 bcopy(*ap
, &addrs
[i
], sizeof(addrs
[i
]));
338 for (i
= 0; i
< MAXADDRS
&& addrs
[i
]; i
++)
339 if (iruserok((in_addr_t
)addrs
[i
], superuser
, ruser
, luser
) == 0)
345 * New .rhosts strategy: We are passed an ip address. We spin through
346 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
347 * has ip addresses, we don't have to trust a nameserver. When it
348 * contains hostnames, we spin through the list of addresses the nameserver
349 * gives us and look for a match.
351 * Returns 0 if ok, -1 if not ok.
354 iruserok(raddr
, superuser
, ruser
, luser
)
357 const char *ruser
, *luser
;
365 char pbuf
[MAXPATHLEN
];
368 hostf
= superuser ? NULL
: fopen(_PATH_HEQUIV
, "r");
371 if (__ivaliduser(hostf
, raddr
, luser
, ruser
) == 0) {
377 if (first
== 1 && (__check_rhosts_file
|| superuser
)) {
379 if ((pwd
= getpwnam(luser
)) == NULL
)
381 (void)strcpy(pbuf
, pwd
->pw_dir
);
382 (void)strcat(pbuf
, "/.rhosts");
385 * Change effective uid while opening .rhosts. If root and
386 * reading an NFS mounted file system, can't read files that
387 * are protected read/write owner only.
390 (void)seteuid(pwd
->pw_uid
);
391 hostf
= fopen(pbuf
, "r");
397 * If not a regular file, or is owned by someone other than
398 * user or root or if writeable by anyone but the owner, quit.
401 if (lstat(pbuf
, &sbuf
) < 0)
402 cp
= ".rhosts lstat failed";
403 else if (!S_ISREG(sbuf
.st_mode
))
404 cp
= ".rhosts not regular file";
405 else if (fstat(fileno(hostf
), &sbuf
) < 0)
406 cp
= ".rhosts fstat failed";
407 else if (sbuf
.st_uid
&& sbuf
.st_uid
!= pwd
->pw_uid
)
408 cp
= "bad .rhosts owner";
409 else if (sbuf
.st_mode
& (S_IWGRP
|S_IWOTH
))
410 cp
= ".rhosts writeable by other than owner";
411 /* If there were any problems, quit. */
424 * Don't make static, used by lpd(8).
426 * Returns 0 if ok, -1 if not ok.
429 __ivaliduser(hostf
, raddrl
, luser
, ruser
)
432 const char *luser
, *ruser
;
434 register char *user
, *p
;
436 const char *auser
, *ahost
;
438 char *rhost
= (char *)-1;
439 char domain
[MAXHOSTNAMELEN
];
440 u_int32_t raddr
= (u_int32_t
)raddrl
;
443 getdomainname(domain
, sizeof(domain
));
445 while ((buf
= fgetln(hostf
, &buflen
))) {
449 while (*p
!= '\n' && *p
!= ' ' && *p
!= '\t' && p
< buf
+ buflen
) {
452 *p
= isupper(*p
) ?
tolower(*p
) : *p
;
455 if (p
>= buf
+ buflen
)
457 if (*p
== ' ' || *p
== '\t') {
459 while (*p
== ' ' || *p
== '\t' && p
< buf
+ buflen
)
461 if (p
>= buf
+ buflen
)
464 while (*p
!= '\n' && *p
!= ' ' &&
465 *p
!= '\t' && p
< buf
+ buflen
) {
477 auser
= *user ? user
: luser
;
480 if (strlen(ahost
) >= MAXHOSTNAMELEN
)
484 * innetgr() must lookup a hostname (we do not attempt
485 * to change the semantics so that netgroups may have
486 * #.#.#.# addresses in the list.)
494 if (rhost
== (char *)-1)
495 rhost
= __gethostloop(raddr
);
498 hostok
= innetgr(&ahost
[2], rhost
,
502 hostok
= __icheckhost(raddr
, &ahost
[1]);
505 else if (ahost
[0] == '-')
511 if (rhost
== (char *)-1)
512 rhost
= __gethostloop(raddr
);
515 hostok
= -innetgr(&ahost
[2], rhost
,
519 hostok
= -__icheckhost(raddr
, &ahost
[1]);
523 hostok
= __icheckhost(raddr
, ahost
);
532 userok
= innetgr(&auser
[2], NULL
, ruser
,
536 userok
= strcmp(ruser
, &auser
[1]) ?
0 : 1;
539 else if (auser
[0] == '-')
545 userok
= -innetgr(&auser
[2], NULL
, ruser
,
549 userok
= strcmp(ruser
, &auser
[1]) ?
0 : -1;
553 userok
= strcmp(ruser
, auser
) ?
0 : 1;
555 /* Check if one component did not match */
556 if (hostok
== 0 || userok
== 0)
559 /* Check if we got a forbidden pair */
560 if (userok
<= -1 || hostok
<= -1)
563 /* Check if we got a valid pair */
564 if (hostok
>= 1 && userok
>= 1)
572 * Returns "true" if match, 0 if no match. If we do not find any
573 * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work.
576 __icheckhost(raddr
, lhost
)
580 register struct hostent
*hp
;
584 hp
= gethostbyname(lhost
);
586 /* Spin through ip addresses. */
587 for (pp
= hp
->h_addr_list
; *pp
; ++pp
)
588 if (!bcmp(&raddr
, *pp
, sizeof(raddr
)))
593 if (strcmp(lhost
, inet_ntoa(in
)) == 0)
599 * Return the hostname associated with the supplied address.
600 * Do a reverse lookup as well for security. If a loop cannot
601 * be found, pack the result of inet_ntoa() into the string.
607 static char remotehost
[MAXHOSTNAMELEN
];
611 hp
= gethostbyaddr((char *) &raddr
, sizeof(raddr
), AF_INET
);
616 * Look up the name and check that the supplied
617 * address is in the list
619 strncpy(remotehost
, hp
->h_name
, sizeof(remotehost
) - 1);
620 remotehost
[sizeof(remotehost
) - 1] = '\0';
621 hp
= gethostbyname(remotehost
);
625 for (; hp
->h_addr_list
[0] != NULL
; hp
->h_addr_list
++)
626 if (!bcmp(hp
->h_addr_list
[0], (caddr_t
)&raddr
, sizeof(raddr
)))
630 * either the DNS adminstrator has made a configuration
631 * mistake, or someone has attempted to spoof us
634 syslog(LOG_NOTICE
, "rcmd: address %s not listed for host %s",
635 inet_ntoa(in
), hp
->h_name
);