1*0a6a1f1dSLionel Sambuc /* $NetBSD: rcmd.c,v 1.71 2014/11/26 23:44:21 enami Exp $ */
22fe8fb19SBen Gras
32fe8fb19SBen Gras /*
42fe8fb19SBen Gras * Copyright (c) 1983, 1993, 1994
52fe8fb19SBen Gras * The Regents of the University of California. All rights reserved.
62fe8fb19SBen Gras *
72fe8fb19SBen Gras * Redistribution and use in source and binary forms, with or without
82fe8fb19SBen Gras * modification, are permitted provided that the following conditions
92fe8fb19SBen Gras * are met:
102fe8fb19SBen Gras * 1. Redistributions of source code must retain the above copyright
112fe8fb19SBen Gras * notice, this list of conditions and the following disclaimer.
122fe8fb19SBen Gras * 2. Redistributions in binary form must reproduce the above copyright
132fe8fb19SBen Gras * notice, this list of conditions and the following disclaimer in the
142fe8fb19SBen Gras * documentation and/or other materials provided with the distribution.
152fe8fb19SBen Gras * 3. Neither the name of the University nor the names of its contributors
162fe8fb19SBen Gras * may be used to endorse or promote products derived from this software
172fe8fb19SBen Gras * without specific prior written permission.
182fe8fb19SBen Gras *
192fe8fb19SBen Gras * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
202fe8fb19SBen Gras * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
212fe8fb19SBen Gras * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
222fe8fb19SBen Gras * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
232fe8fb19SBen Gras * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
242fe8fb19SBen Gras * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
252fe8fb19SBen Gras * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
262fe8fb19SBen Gras * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
272fe8fb19SBen Gras * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
282fe8fb19SBen Gras * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
292fe8fb19SBen Gras * SUCH DAMAGE.
302fe8fb19SBen Gras */
312fe8fb19SBen Gras
322fe8fb19SBen Gras #include <sys/cdefs.h>
332fe8fb19SBen Gras #if defined(LIBC_SCCS) && !defined(lint)
342fe8fb19SBen Gras #if 0
352fe8fb19SBen Gras static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94";
362fe8fb19SBen Gras #else
37*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: rcmd.c,v 1.71 2014/11/26 23:44:21 enami Exp $");
382fe8fb19SBen Gras #endif
392fe8fb19SBen Gras #endif /* LIBC_SCCS and not lint */
402fe8fb19SBen Gras
412fe8fb19SBen Gras #ifdef _LIBC
422fe8fb19SBen Gras #include "namespace.h"
432fe8fb19SBen Gras #endif
442fe8fb19SBen Gras #include <sys/param.h>
452fe8fb19SBen Gras #include <sys/socket.h>
462fe8fb19SBen Gras #include <sys/stat.h>
472fe8fb19SBen Gras #include <sys/poll.h>
482fe8fb19SBen Gras #include <sys/wait.h>
492fe8fb19SBen Gras
502fe8fb19SBen Gras #include <netinet/in.h>
5184d9c625SLionel Sambuc #if !defined(__minix)
522fe8fb19SBen Gras #include <rpc/rpc.h>
5384d9c625SLionel Sambuc #endif /* !defined(__minix) */
542fe8fb19SBen Gras #include <arpa/inet.h>
552fe8fb19SBen Gras #include <netgroup.h>
562fe8fb19SBen Gras
572fe8fb19SBen Gras #include <assert.h>
582fe8fb19SBen Gras #include <ctype.h>
592fe8fb19SBen Gras #include <err.h>
602fe8fb19SBen Gras #include <errno.h>
612fe8fb19SBen Gras #include <fcntl.h>
622fe8fb19SBen Gras #include <grp.h>
632fe8fb19SBen Gras #include <netdb.h>
642fe8fb19SBen Gras #include <paths.h>
652fe8fb19SBen Gras #include <pwd.h>
662fe8fb19SBen Gras #include <signal.h>
672fe8fb19SBen Gras #include <stdio.h>
682fe8fb19SBen Gras #include <stdlib.h>
692fe8fb19SBen Gras #include <string.h>
702fe8fb19SBen Gras #include <syslog.h>
712fe8fb19SBen Gras #include <unistd.h>
722fe8fb19SBen Gras
732fe8fb19SBen Gras #include "pathnames.h"
742fe8fb19SBen Gras
75f14fb602SLionel Sambuc int orcmd(char **, u_int, const char *, const char *, const char *, int *);
76f14fb602SLionel Sambuc int orcmd_af(char **, u_int, const char *, const char *, const char *,
77f14fb602SLionel Sambuc int *, int);
78f14fb602SLionel Sambuc int __ivaliduser(FILE *, u_int32_t, const char *, const char *);
79f14fb602SLionel Sambuc int __ivaliduser_sa(FILE *, const struct sockaddr *, socklen_t,
80f14fb602SLionel Sambuc const char *, const char *);
81f14fb602SLionel Sambuc static int rshrcmd(int, char **, u_int32_t, const char *,
82f14fb602SLionel Sambuc const char *, const char *, int *, const char *);
83f14fb602SLionel Sambuc static int resrcmd(struct addrinfo *, char **, u_int32_t, const char *,
84f14fb602SLionel Sambuc const char *, const char *, int *);
85f14fb602SLionel Sambuc static int __icheckhost(const struct sockaddr *, socklen_t,
86f14fb602SLionel Sambuc const char *);
87f14fb602SLionel Sambuc static char *__gethostloop(const struct sockaddr *, socklen_t);
882fe8fb19SBen Gras
892fe8fb19SBen Gras int
rcmd(char ** ahost,int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p)90f14fb602SLionel Sambuc rcmd(char **ahost, int rport, const char *locuser, const char *remuser,
91f14fb602SLionel Sambuc const char *cmd, int *fd2p)
922fe8fb19SBen Gras {
932fe8fb19SBen Gras
942fe8fb19SBen Gras return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
952fe8fb19SBen Gras }
962fe8fb19SBen Gras
972fe8fb19SBen Gras int
rcmd_af(char ** ahost,int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p,int af)98f14fb602SLionel Sambuc rcmd_af(char **ahost, int rport, const char *locuser, const char *remuser,
99f14fb602SLionel Sambuc const char *cmd, int *fd2p, int af)
1002fe8fb19SBen Gras {
1012fe8fb19SBen Gras static char hbuf[MAXHOSTNAMELEN];
1022fe8fb19SBen Gras char pbuf[NI_MAXSERV];
1032fe8fb19SBen Gras struct addrinfo hints, *res;
1042fe8fb19SBen Gras int error;
1052fe8fb19SBen Gras struct servent *sp;
1062fe8fb19SBen Gras
1072fe8fb19SBen Gras _DIAGASSERT(ahost != NULL);
1082fe8fb19SBen Gras _DIAGASSERT(locuser != NULL);
1092fe8fb19SBen Gras _DIAGASSERT(remuser != NULL);
1102fe8fb19SBen Gras _DIAGASSERT(cmd != NULL);
1112fe8fb19SBen Gras /* fd2p may be NULL */
1122fe8fb19SBen Gras
1132fe8fb19SBen Gras snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
1142fe8fb19SBen Gras memset(&hints, 0, sizeof(hints));
1152fe8fb19SBen Gras hints.ai_family = af;
1162fe8fb19SBen Gras hints.ai_socktype = SOCK_STREAM;
1172fe8fb19SBen Gras hints.ai_flags = AI_CANONNAME;
1182fe8fb19SBen Gras error = getaddrinfo(*ahost, pbuf, &hints, &res);
1192fe8fb19SBen Gras if (error) {
1202fe8fb19SBen Gras warnx("%s: %s", *ahost, gai_strerror(error)); /*XXX*/
121f14fb602SLionel Sambuc return -1;
1222fe8fb19SBen Gras }
1232fe8fb19SBen Gras if (res->ai_canonname) {
1242fe8fb19SBen Gras /*
1252fe8fb19SBen Gras * Canonicalise hostname.
1262fe8fb19SBen Gras * XXX: Should we really do this?
1272fe8fb19SBen Gras */
1282fe8fb19SBen Gras strlcpy(hbuf, res->ai_canonname, sizeof(hbuf));
1292fe8fb19SBen Gras *ahost = hbuf;
1302fe8fb19SBen Gras }
1312fe8fb19SBen Gras
1322fe8fb19SBen Gras /*
1332fe8fb19SBen Gras * Check if rport is the same as the shell port, and that the fd2p. If
1342fe8fb19SBen Gras * it is not, the program isn't expecting 'rsh' and so we can't use the
1352fe8fb19SBen Gras * RCMD_CMD environment.
1362fe8fb19SBen Gras */
1372fe8fb19SBen Gras sp = getservbyname("shell", "tcp");
1382fe8fb19SBen Gras if (sp != NULL && sp->s_port == rport)
139f14fb602SLionel Sambuc error = rshrcmd(af, ahost, (u_int32_t)rport,
1402fe8fb19SBen Gras locuser, remuser, cmd, fd2p, getenv("RCMD_CMD"));
1412fe8fb19SBen Gras else
1422fe8fb19SBen Gras error = resrcmd(res, ahost, (u_int32_t)rport,
1432fe8fb19SBen Gras locuser, remuser, cmd, fd2p);
1442fe8fb19SBen Gras freeaddrinfo(res);
145f14fb602SLionel Sambuc return error;
1462fe8fb19SBen Gras }
1472fe8fb19SBen Gras
1482fe8fb19SBen Gras /* this is simply a wrapper around hprcmd() that handles ahost first */
1492fe8fb19SBen Gras int
orcmd(char ** ahost,u_int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p)150f14fb602SLionel Sambuc orcmd(char **ahost, u_int rport, const char *locuser, const char *remuser,
151f14fb602SLionel Sambuc const char *cmd, int *fd2p)
1522fe8fb19SBen Gras {
1532fe8fb19SBen Gras return orcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
1542fe8fb19SBen Gras }
1552fe8fb19SBen Gras
1562fe8fb19SBen Gras int
orcmd_af(char ** ahost,u_int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p,int af)157f14fb602SLionel Sambuc orcmd_af(char **ahost, u_int rport, const char *locuser, const char *remuser,
158f14fb602SLionel Sambuc const char *cmd, int *fd2p, int af)
1592fe8fb19SBen Gras {
1602fe8fb19SBen Gras static char hbuf[MAXHOSTNAMELEN];
1612fe8fb19SBen Gras char pbuf[NI_MAXSERV];
1622fe8fb19SBen Gras struct addrinfo hints, *res;
1632fe8fb19SBen Gras int error;
1642fe8fb19SBen Gras
1652fe8fb19SBen Gras _DIAGASSERT(ahost != NULL);
1662fe8fb19SBen Gras _DIAGASSERT(locuser != NULL);
1672fe8fb19SBen Gras _DIAGASSERT(remuser != NULL);
1682fe8fb19SBen Gras _DIAGASSERT(cmd != NULL);
1692fe8fb19SBen Gras /* fd2p may be NULL */
1702fe8fb19SBen Gras
1712fe8fb19SBen Gras snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
1722fe8fb19SBen Gras memset(&hints, 0, sizeof(hints));
1732fe8fb19SBen Gras hints.ai_family = af;
1742fe8fb19SBen Gras hints.ai_socktype = SOCK_STREAM;
1752fe8fb19SBen Gras hints.ai_flags = AI_CANONNAME;
1762fe8fb19SBen Gras error = getaddrinfo(*ahost, pbuf, &hints, &res);
1772fe8fb19SBen Gras if (error) {
1782fe8fb19SBen Gras warnx("%s: %s", *ahost, gai_strerror(error)); /*XXX*/
179f14fb602SLionel Sambuc return -1;
1802fe8fb19SBen Gras }
1812fe8fb19SBen Gras if (res->ai_canonname) {
1822fe8fb19SBen Gras strlcpy(hbuf, res->ai_canonname, sizeof(hbuf));
1832fe8fb19SBen Gras *ahost = hbuf;
1842fe8fb19SBen Gras }
1852fe8fb19SBen Gras
1862fe8fb19SBen Gras error = resrcmd(res, ahost, rport, locuser, remuser, cmd, fd2p);
1872fe8fb19SBen Gras freeaddrinfo(res);
188f14fb602SLionel Sambuc return error;
1892fe8fb19SBen Gras }
1902fe8fb19SBen Gras
1912fe8fb19SBen Gras /*ARGSUSED*/
1922fe8fb19SBen Gras static int
resrcmd(struct addrinfo * res,char ** ahost,u_int32_t rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p)193f14fb602SLionel Sambuc resrcmd(struct addrinfo *res, char **ahost, u_int32_t rport,
194f14fb602SLionel Sambuc const char *locuser, const char *remuser, const char *cmd, int *fd2p)
1952fe8fb19SBen Gras {
1962fe8fb19SBen Gras struct addrinfo *r;
1972fe8fb19SBen Gras struct sockaddr_storage from;
1982fe8fb19SBen Gras struct pollfd reads[2];
1992fe8fb19SBen Gras sigset_t nmask, omask;
2002fe8fb19SBen Gras pid_t pid;
2012fe8fb19SBen Gras int s, lport, timo;
2022fe8fb19SBen Gras int pollr;
2032fe8fb19SBen Gras char c;
2042fe8fb19SBen Gras int refused;
2052fe8fb19SBen Gras
2062fe8fb19SBen Gras _DIAGASSERT(res != NULL);
2072fe8fb19SBen Gras _DIAGASSERT(ahost != NULL);
2082fe8fb19SBen Gras _DIAGASSERT(locuser != NULL);
2092fe8fb19SBen Gras _DIAGASSERT(remuser != NULL);
2102fe8fb19SBen Gras _DIAGASSERT(cmd != NULL);
2112fe8fb19SBen Gras /* fd2p may be NULL */
2122fe8fb19SBen Gras
2132fe8fb19SBen Gras r = res;
2142fe8fb19SBen Gras refused = 0;
2152fe8fb19SBen Gras pid = getpid();
2162fe8fb19SBen Gras sigemptyset(&nmask);
2172fe8fb19SBen Gras sigaddset(&nmask, SIGURG);
2182fe8fb19SBen Gras if (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)
2192fe8fb19SBen Gras return -1;
2202fe8fb19SBen Gras for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
2212fe8fb19SBen Gras s = rresvport_af(&lport, r->ai_family);
2222fe8fb19SBen Gras if (s < 0) {
2232fe8fb19SBen Gras if (errno == EAGAIN)
2242fe8fb19SBen Gras warnx("rcmd: socket: All ports in use");
2252fe8fb19SBen Gras else
2262fe8fb19SBen Gras warn("rcmd: socket");
2272fe8fb19SBen Gras if (r->ai_next) {
2282fe8fb19SBen Gras r = r->ai_next;
2292fe8fb19SBen Gras continue;
2302fe8fb19SBen Gras } else {
2312fe8fb19SBen Gras (void)sigprocmask(SIG_SETMASK, &omask, NULL);
232f14fb602SLionel Sambuc return -1;
2332fe8fb19SBen Gras }
2342fe8fb19SBen Gras }
2352fe8fb19SBen Gras fcntl(s, F_SETOWN, pid);
2362fe8fb19SBen Gras if (connect(s, r->ai_addr, r->ai_addrlen) >= 0)
2372fe8fb19SBen Gras break;
2382fe8fb19SBen Gras (void)close(s);
2392fe8fb19SBen Gras if (errno == EADDRINUSE) {
2402fe8fb19SBen Gras lport--;
2412fe8fb19SBen Gras continue;
2422fe8fb19SBen Gras } else if (errno == ECONNREFUSED)
2432fe8fb19SBen Gras refused++;
2442fe8fb19SBen Gras if (r->ai_next) {
2452fe8fb19SBen Gras int oerrno = errno;
2462fe8fb19SBen Gras char hbuf[NI_MAXHOST];
2472fe8fb19SBen Gras const int niflags = NI_NUMERICHOST;
2482fe8fb19SBen Gras
2492fe8fb19SBen Gras hbuf[0] = '\0';
2502fe8fb19SBen Gras if (getnameinfo(r->ai_addr, r->ai_addrlen,
251f14fb602SLionel Sambuc hbuf, (socklen_t)sizeof(hbuf), NULL, 0, niflags) !=
252f14fb602SLionel Sambuc 0)
2532fe8fb19SBen Gras strlcpy(hbuf, "(invalid)", sizeof(hbuf));
2542fe8fb19SBen Gras errno = oerrno;
2552fe8fb19SBen Gras warn("rcmd: connect to address %s", hbuf);
2562fe8fb19SBen Gras r = r->ai_next;
2572fe8fb19SBen Gras hbuf[0] = '\0';
2582fe8fb19SBen Gras if (getnameinfo(r->ai_addr, r->ai_addrlen,
259f14fb602SLionel Sambuc hbuf, (socklen_t)sizeof(hbuf), NULL, 0, niflags) !=
260f14fb602SLionel Sambuc 0)
2612fe8fb19SBen Gras strlcpy(hbuf, "(invalid)", sizeof(hbuf));
2622fe8fb19SBen Gras (void)fprintf(stderr, "Trying %s...\n", hbuf);
2632fe8fb19SBen Gras continue;
2642fe8fb19SBen Gras }
2652fe8fb19SBen Gras if (refused && timo <= 16) {
2662fe8fb19SBen Gras (void)sleep((unsigned int)timo);
2672fe8fb19SBen Gras timo *= 2;
2682fe8fb19SBen Gras r = res;
2692fe8fb19SBen Gras refused = 0;
2702fe8fb19SBen Gras continue;
2712fe8fb19SBen Gras }
2722fe8fb19SBen Gras (void)fprintf(stderr, "%s: %s\n", res->ai_canonname,
2732fe8fb19SBen Gras strerror(errno));
2742fe8fb19SBen Gras (void)sigprocmask(SIG_SETMASK, &omask, NULL);
275f14fb602SLionel Sambuc return -1;
2762fe8fb19SBen Gras }
2772fe8fb19SBen Gras lport--;
2782fe8fb19SBen Gras if (fd2p == 0) {
2792fe8fb19SBen Gras write(s, "", 1);
2802fe8fb19SBen Gras lport = 0;
2812fe8fb19SBen Gras } else {
2822fe8fb19SBen Gras char num[8];
2832fe8fb19SBen Gras int s2 = rresvport_af(&lport, r->ai_family), s3;
2842fe8fb19SBen Gras socklen_t len = sizeof(from);
2852fe8fb19SBen Gras
2862fe8fb19SBen Gras if (s2 < 0)
2872fe8fb19SBen Gras goto bad;
2882fe8fb19SBen Gras listen(s2, 1);
2892fe8fb19SBen Gras (void)snprintf(num, sizeof(num), "%d", lport);
2902fe8fb19SBen Gras if (write(s, num, strlen(num) + 1) !=
2912fe8fb19SBen Gras (ssize_t) (strlen(num) + 1)) {
2922fe8fb19SBen Gras warn("rcmd: write (setting up stderr)");
2932fe8fb19SBen Gras (void)close(s2);
2942fe8fb19SBen Gras goto bad;
2952fe8fb19SBen Gras }
2962fe8fb19SBen Gras reads[0].fd = s;
2972fe8fb19SBen Gras reads[0].events = POLLIN;
2982fe8fb19SBen Gras reads[1].fd = s2;
2992fe8fb19SBen Gras reads[1].events = POLLIN;
3002fe8fb19SBen Gras errno = 0;
3012fe8fb19SBen Gras pollr = poll(reads, 2, INFTIM);
3022fe8fb19SBen Gras if (pollr < 1 || (reads[1].revents & POLLIN) == 0) {
3032fe8fb19SBen Gras if (errno != 0)
3042fe8fb19SBen Gras warn("poll: setting up stderr");
3052fe8fb19SBen Gras else
306f14fb602SLionel Sambuc warnx(
307f14fb602SLionel Sambuc "poll: protocol failure in circuit setup");
3082fe8fb19SBen Gras (void)close(s2);
3092fe8fb19SBen Gras goto bad;
3102fe8fb19SBen Gras }
3112fe8fb19SBen Gras s3 = accept(s2, (struct sockaddr *)(void *)&from, &len);
3122fe8fb19SBen Gras (void)close(s2);
3132fe8fb19SBen Gras if (s3 < 0) {
3142fe8fb19SBen Gras warn("rcmd: accept");
3152fe8fb19SBen Gras lport = 0;
3162fe8fb19SBen Gras goto bad;
3172fe8fb19SBen Gras }
3182fe8fb19SBen Gras *fd2p = s3;
3192fe8fb19SBen Gras switch (((struct sockaddr *)(void *)&from)->sa_family) {
3202fe8fb19SBen Gras case AF_INET:
3212fe8fb19SBen Gras #ifdef INET6
3222fe8fb19SBen Gras case AF_INET6:
3232fe8fb19SBen Gras #endif
3242fe8fb19SBen Gras if (getnameinfo((struct sockaddr *)(void *)&from, len,
325f14fb602SLionel Sambuc NULL, 0, num, (socklen_t)sizeof(num),
326f14fb602SLionel Sambuc NI_NUMERICSERV) != 0 ||
3272fe8fb19SBen Gras (atoi(num) >= IPPORT_RESERVED ||
3282fe8fb19SBen Gras atoi(num) < IPPORT_RESERVED / 2)) {
329f14fb602SLionel Sambuc warnx(
330f14fb602SLionel Sambuc "rcmd: protocol failure in circuit setup.");
3312fe8fb19SBen Gras goto bad2;
3322fe8fb19SBen Gras }
3332fe8fb19SBen Gras break;
3342fe8fb19SBen Gras default:
3352fe8fb19SBen Gras break;
3362fe8fb19SBen Gras }
3372fe8fb19SBen Gras }
3382fe8fb19SBen Gras
3392fe8fb19SBen Gras (void)write(s, locuser, strlen(locuser)+1);
3402fe8fb19SBen Gras (void)write(s, remuser, strlen(remuser)+1);
3412fe8fb19SBen Gras (void)write(s, cmd, strlen(cmd)+1);
3422fe8fb19SBen Gras if (read(s, &c, 1) != 1) {
3432fe8fb19SBen Gras warn("%s", *ahost);
3442fe8fb19SBen Gras goto bad2;
3452fe8fb19SBen Gras }
3462fe8fb19SBen Gras if (c != 0) {
3472fe8fb19SBen Gras while (read(s, &c, 1) == 1) {
3482fe8fb19SBen Gras (void)write(STDERR_FILENO, &c, 1);
3492fe8fb19SBen Gras if (c == '\n')
3502fe8fb19SBen Gras break;
3512fe8fb19SBen Gras }
3522fe8fb19SBen Gras goto bad2;
3532fe8fb19SBen Gras }
3542fe8fb19SBen Gras (void)sigprocmask(SIG_SETMASK, &omask, NULL);
355f14fb602SLionel Sambuc return s;
3562fe8fb19SBen Gras bad2:
3572fe8fb19SBen Gras if (lport)
3582fe8fb19SBen Gras (void)close(*fd2p);
3592fe8fb19SBen Gras bad:
3602fe8fb19SBen Gras (void)close(s);
3612fe8fb19SBen Gras (void)sigprocmask(SIG_SETMASK, &omask, NULL);
362f14fb602SLionel Sambuc return -1;
3632fe8fb19SBen Gras }
3642fe8fb19SBen Gras
3652fe8fb19SBen Gras /*
3662fe8fb19SBen Gras * based on code written by Chris Siebenmann <cks@utcc.utoronto.ca>
3672fe8fb19SBen Gras */
3682fe8fb19SBen Gras /* ARGSUSED */
3692fe8fb19SBen Gras static int
rshrcmd(int af,char ** ahost,u_int32_t rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p,const char * rshcmd)370f14fb602SLionel Sambuc rshrcmd(int af, char **ahost, u_int32_t rport, const char *locuser,
371f14fb602SLionel Sambuc const char *remuser, const char *cmd, int *fd2p, const char *rshcmd)
3722fe8fb19SBen Gras {
3732fe8fb19SBen Gras pid_t pid;
3742fe8fb19SBen Gras int sp[2], ep[2];
3752fe8fb19SBen Gras char *p;
3762fe8fb19SBen Gras struct passwd *pw, pwres;
3772fe8fb19SBen Gras char pwbuf[1024];
3782fe8fb19SBen Gras
3792fe8fb19SBen Gras _DIAGASSERT(ahost != NULL);
3802fe8fb19SBen Gras _DIAGASSERT(locuser != NULL);
3812fe8fb19SBen Gras _DIAGASSERT(remuser != NULL);
3822fe8fb19SBen Gras _DIAGASSERT(cmd != NULL);
3832fe8fb19SBen Gras /* fd2p may be NULL */
3842fe8fb19SBen Gras
3852fe8fb19SBen Gras /* What rsh/shell to use. */
3862fe8fb19SBen Gras if (rshcmd == NULL)
3872fe8fb19SBen Gras rshcmd = _PATH_BIN_RCMD;
3882fe8fb19SBen Gras
3892fe8fb19SBen Gras /* locuser must exist on this host. */
3902fe8fb19SBen Gras if (getpwnam_r(locuser, &pwres, pwbuf, sizeof(pwbuf), &pw) != 0 ||
3912fe8fb19SBen Gras pw == NULL) {
392f14fb602SLionel Sambuc warnx("%s: unknown user: %s", __func__, locuser);
393f14fb602SLionel Sambuc return -1;
3942fe8fb19SBen Gras }
3952fe8fb19SBen Gras
3962fe8fb19SBen Gras /* get a socketpair we'll use for stdin and stdout. */
3972fe8fb19SBen Gras if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sp) < 0) {
398f14fb602SLionel Sambuc warn("%s: socketpair", __func__);
399f14fb602SLionel Sambuc return -1;
4002fe8fb19SBen Gras }
4012fe8fb19SBen Gras /* we will use this for the fd2 pointer */
4022fe8fb19SBen Gras if (fd2p) {
4032fe8fb19SBen Gras if (socketpair(AF_LOCAL, SOCK_STREAM, 0, ep) < 0) {
404f14fb602SLionel Sambuc warn("%s: socketpair", __func__);
405f14fb602SLionel Sambuc return -1;
4062fe8fb19SBen Gras }
4072fe8fb19SBen Gras *fd2p = ep[0];
4082fe8fb19SBen Gras }
4092fe8fb19SBen Gras
4102fe8fb19SBen Gras pid = fork();
4112fe8fb19SBen Gras if (pid < 0) {
412f14fb602SLionel Sambuc warn("%s: fork", __func__);
413f14fb602SLionel Sambuc return -1;
4142fe8fb19SBen Gras }
4152fe8fb19SBen Gras if (pid == 0) {
4162fe8fb19SBen Gras /*
4172fe8fb19SBen Gras * child
4182fe8fb19SBen Gras * - we use sp[1] to be stdin/stdout, and close sp[0]
4192fe8fb19SBen Gras * - with fd2p, we use ep[1] for stderr, and close ep[0]
4202fe8fb19SBen Gras */
4212fe8fb19SBen Gras (void)close(sp[0]);
4222fe8fb19SBen Gras if (dup2(sp[1], 0) < 0 || dup2(0, 1) < 0) {
423f14fb602SLionel Sambuc warn("%s: dup2", __func__);
4242fe8fb19SBen Gras _exit(1);
4252fe8fb19SBen Gras }
4262fe8fb19SBen Gras (void)close(sp[1]);
4272fe8fb19SBen Gras if (fd2p) {
4282fe8fb19SBen Gras if (dup2(ep[1], 2) < 0) {
429f14fb602SLionel Sambuc warn("%s: dup2", __func__);
4302fe8fb19SBen Gras _exit(1);
4312fe8fb19SBen Gras }
4322fe8fb19SBen Gras (void)close(ep[0]);
4332fe8fb19SBen Gras (void)close(ep[1]);
4342fe8fb19SBen Gras } else if (dup2(0, 2) < 0) {
435f14fb602SLionel Sambuc warn("%s: dup2", __func__);
4362fe8fb19SBen Gras _exit(1);
4372fe8fb19SBen Gras }
4382fe8fb19SBen Gras /* fork again to lose parent. */
4392fe8fb19SBen Gras pid = fork();
4402fe8fb19SBen Gras if (pid < 0) {
441f14fb602SLionel Sambuc warn("%s: second fork", __func__);
4422fe8fb19SBen Gras _exit(1);
4432fe8fb19SBen Gras }
4442fe8fb19SBen Gras if (pid > 0)
4452fe8fb19SBen Gras _exit(0);
4462fe8fb19SBen Gras
4472fe8fb19SBen Gras /* Orphan. Become local user for rshprog. */
4482fe8fb19SBen Gras if (setuid(pw->pw_uid)) {
449f14fb602SLionel Sambuc warn("%s: setuid(%lu)", __func__, (u_long)pw->pw_uid);
4502fe8fb19SBen Gras _exit(1);
4512fe8fb19SBen Gras }
4522fe8fb19SBen Gras
4532fe8fb19SBen Gras /*
454f14fb602SLionel Sambuc * If we are rcmd'ing to "localhost" as the same user as we
455f14fb602SLionel Sambuc * are, then avoid running remote shell for efficiency.
4562fe8fb19SBen Gras */
4572fe8fb19SBen Gras if (strcmp(*ahost, "localhost") == 0 &&
4582fe8fb19SBen Gras strcmp(locuser, remuser) == 0) {
4592fe8fb19SBen Gras if (pw->pw_shell[0] == '\0')
4602fe8fb19SBen Gras rshcmd = _PATH_BSHELL;
4612fe8fb19SBen Gras else
4622fe8fb19SBen Gras rshcmd = pw->pw_shell;
4632fe8fb19SBen Gras p = strrchr(rshcmd, '/');
4642fe8fb19SBen Gras execlp(rshcmd, p ? p + 1 : rshcmd, "-c", cmd, NULL);
4652fe8fb19SBen Gras } else {
466f14fb602SLionel Sambuc const char *program;
467f14fb602SLionel Sambuc program = strrchr(rshcmd, '/');
468f14fb602SLionel Sambuc program = program ? program + 1 : rshcmd;
469*0a6a1f1dSLionel Sambuc if (fd2p)
470*0a6a1f1dSLionel Sambuc /* ask rcmd to relay signal information */
471*0a6a1f1dSLionel Sambuc setenv("RCMD_RELAY_SIGNAL", "YES", 1);
472f14fb602SLionel Sambuc switch (af) {
473f14fb602SLionel Sambuc case AF_INET:
474f14fb602SLionel Sambuc execlp(rshcmd, program, "-4", "-l", remuser,
475f14fb602SLionel Sambuc *ahost, cmd, NULL);
476f14fb602SLionel Sambuc break;
477f14fb602SLionel Sambuc
478f14fb602SLionel Sambuc case AF_INET6:
479f14fb602SLionel Sambuc execlp(rshcmd, program, "-6", "-l", remuser,
480f14fb602SLionel Sambuc *ahost, cmd, NULL);
481f14fb602SLionel Sambuc break;
482f14fb602SLionel Sambuc
483f14fb602SLionel Sambuc default:
484f14fb602SLionel Sambuc /* typically AF_UNSPEC, plus whatever */
485f14fb602SLionel Sambuc execlp(rshcmd, program, "-l", remuser,
486f14fb602SLionel Sambuc *ahost, cmd, NULL);
487f14fb602SLionel Sambuc break;
4882fe8fb19SBen Gras }
489f14fb602SLionel Sambuc }
490f14fb602SLionel Sambuc warn("%s: exec %s", __func__, rshcmd);
4912fe8fb19SBen Gras _exit(1);
4922fe8fb19SBen Gras }
4932fe8fb19SBen Gras /* Parent */
4942fe8fb19SBen Gras (void)close(sp[1]);
4952fe8fb19SBen Gras if (fd2p)
4962fe8fb19SBen Gras (void)close(ep[1]);
4972fe8fb19SBen Gras
4982fe8fb19SBen Gras (void)waitpid(pid, NULL, 0);
499f14fb602SLionel Sambuc return sp[0];
5002fe8fb19SBen Gras }
5012fe8fb19SBen Gras
5022fe8fb19SBen Gras int
rresvport(int * alport)503f14fb602SLionel Sambuc rresvport(int *alport)
5042fe8fb19SBen Gras {
5052fe8fb19SBen Gras
5062fe8fb19SBen Gras _DIAGASSERT(alport != NULL);
5072fe8fb19SBen Gras
5082fe8fb19SBen Gras return rresvport_af(alport, AF_INET);
5092fe8fb19SBen Gras }
5102fe8fb19SBen Gras
5112fe8fb19SBen Gras int
rresvport_af(int * alport,int family)512f14fb602SLionel Sambuc rresvport_af(int *alport, int family)
513f14fb602SLionel Sambuc {
514f14fb602SLionel Sambuc return rresvport_af_addr(alport, family, NULL);
515f14fb602SLionel Sambuc }
516f14fb602SLionel Sambuc
517f14fb602SLionel Sambuc int
rresvport_af_addr(int * alport,int family,void * addr)518f14fb602SLionel Sambuc rresvport_af_addr(int *alport, int family, void *addr)
5192fe8fb19SBen Gras {
5202fe8fb19SBen Gras struct sockaddr_storage ss;
5212fe8fb19SBen Gras struct sockaddr *sa;
522f14fb602SLionel Sambuc socklen_t salen;
5232fe8fb19SBen Gras int s;
5242fe8fb19SBen Gras u_int16_t *portp;
5252fe8fb19SBen Gras
5262fe8fb19SBen Gras _DIAGASSERT(alport != NULL);
5272fe8fb19SBen Gras
5282fe8fb19SBen Gras memset(&ss, 0, sizeof(ss));
5292fe8fb19SBen Gras sa = (struct sockaddr *)(void *)&ss;
5302fe8fb19SBen Gras switch (family) {
5312fe8fb19SBen Gras case AF_INET:
5322fe8fb19SBen Gras #ifdef BSD4_4
5332fe8fb19SBen Gras sa->sa_len =
5342fe8fb19SBen Gras #endif
5352fe8fb19SBen Gras salen = sizeof(struct sockaddr_in);
536f14fb602SLionel Sambuc if (addr)
537f14fb602SLionel Sambuc ((struct sockaddr_in *)(void *)sa)->sin_addr =
538f14fb602SLionel Sambuc ((struct sockaddr_in *)addr)->sin_addr;
5392fe8fb19SBen Gras portp = &((struct sockaddr_in *)(void *)sa)->sin_port;
5402fe8fb19SBen Gras break;
5412fe8fb19SBen Gras #ifdef INET6
5422fe8fb19SBen Gras case AF_INET6:
5432fe8fb19SBen Gras #ifdef BSD4_4
5442fe8fb19SBen Gras sa->sa_len =
5452fe8fb19SBen Gras #endif
5462fe8fb19SBen Gras salen = sizeof(struct sockaddr_in6);
547f14fb602SLionel Sambuc if (addr)
548f14fb602SLionel Sambuc ((struct sockaddr_in6 *)(void *)sa)->sin6_addr =
549f14fb602SLionel Sambuc ((struct sockaddr_in6 *)addr)->sin6_addr;
5502fe8fb19SBen Gras portp = &((struct sockaddr_in6 *)(void *)sa)->sin6_port;
5512fe8fb19SBen Gras break;
5522fe8fb19SBen Gras #endif
5532fe8fb19SBen Gras default:
5542fe8fb19SBen Gras errno = EAFNOSUPPORT;
555f14fb602SLionel Sambuc return -1;
5562fe8fb19SBen Gras }
5572fe8fb19SBen Gras sa->sa_family = family;
5582fe8fb19SBen Gras s = socket(family, SOCK_STREAM, 0);
5592fe8fb19SBen Gras if (s < 0)
560f14fb602SLionel Sambuc return -1;
5611ef83ee9SBen Gras #if defined(BSD4_4) && !defined(__minix)
5622fe8fb19SBen Gras switch (family) {
5632fe8fb19SBen Gras case AF_INET:
5642fe8fb19SBen Gras case AF_INET6:
5652fe8fb19SBen Gras *portp = 0;
5662fe8fb19SBen Gras if (bindresvport(s, (struct sockaddr_in *)(void *)sa) < 0) {
5672fe8fb19SBen Gras int sverr = errno;
5682fe8fb19SBen Gras
5692fe8fb19SBen Gras (void)close(s);
5702fe8fb19SBen Gras errno = sverr;
571f14fb602SLionel Sambuc return -1;
5722fe8fb19SBen Gras }
5732fe8fb19SBen Gras *alport = (int)ntohs(*portp);
574f14fb602SLionel Sambuc return s;
5752fe8fb19SBen Gras default:
5762fe8fb19SBen Gras /* is it necessary to try keep code for other AFs? */
5772fe8fb19SBen Gras break;
5782fe8fb19SBen Gras }
5792fe8fb19SBen Gras #endif
5802fe8fb19SBen Gras for (;;) {
5812fe8fb19SBen Gras *portp = htons((u_short)*alport);
582f14fb602SLionel Sambuc if (bind(s, sa, salen) >= 0)
583f14fb602SLionel Sambuc return s;
5842fe8fb19SBen Gras if (errno != EADDRINUSE) {
5852fe8fb19SBen Gras (void)close(s);
586f14fb602SLionel Sambuc return -1;
5872fe8fb19SBen Gras }
5882fe8fb19SBen Gras (*alport)--;
5892fe8fb19SBen Gras if (*alport == IPPORT_RESERVED/2) {
5902fe8fb19SBen Gras (void)close(s);
5912fe8fb19SBen Gras errno = EAGAIN; /* close */
592f14fb602SLionel Sambuc return -1;
5932fe8fb19SBen Gras }
5942fe8fb19SBen Gras }
5952fe8fb19SBen Gras }
5962fe8fb19SBen Gras
5972fe8fb19SBen Gras int __check_rhosts_file = 1;
5982fe8fb19SBen Gras const char *__rcmd_errstr;
5992fe8fb19SBen Gras
6002fe8fb19SBen Gras int
ruserok(const char * rhost,int superuser,const char * ruser,const char * luser)601f14fb602SLionel Sambuc ruserok(const char *rhost, int superuser, const char *ruser, const char *luser)
6022fe8fb19SBen Gras {
6032fe8fb19SBen Gras struct addrinfo hints, *res, *r;
6042fe8fb19SBen Gras int error;
6052fe8fb19SBen Gras
6062fe8fb19SBen Gras _DIAGASSERT(rhost != NULL);
6072fe8fb19SBen Gras _DIAGASSERT(ruser != NULL);
6082fe8fb19SBen Gras _DIAGASSERT(luser != NULL);
6092fe8fb19SBen Gras
6102fe8fb19SBen Gras memset(&hints, 0, sizeof(hints));
6112fe8fb19SBen Gras hints.ai_family = PF_UNSPEC;
6122fe8fb19SBen Gras hints.ai_socktype = SOCK_DGRAM; /*dummy*/
6132fe8fb19SBen Gras error = getaddrinfo(rhost, "0", &hints, &res);
6142fe8fb19SBen Gras if (error)
615f14fb602SLionel Sambuc return -1;
6162fe8fb19SBen Gras
6172fe8fb19SBen Gras for (r = res; r; r = r->ai_next) {
6182fe8fb19SBen Gras if (iruserok_sa(r->ai_addr, (int)r->ai_addrlen, superuser,
6192fe8fb19SBen Gras ruser, luser) == 0) {
6202fe8fb19SBen Gras freeaddrinfo(res);
621f14fb602SLionel Sambuc return 0;
6222fe8fb19SBen Gras }
6232fe8fb19SBen Gras }
6242fe8fb19SBen Gras freeaddrinfo(res);
625f14fb602SLionel Sambuc return -1;
6262fe8fb19SBen Gras }
6272fe8fb19SBen Gras
6282fe8fb19SBen Gras /*
6292fe8fb19SBen Gras * New .rhosts strategy: We are passed an ip address. We spin through
6302fe8fb19SBen Gras * hosts.equiv and .rhosts looking for a match. When the .rhosts only
6312fe8fb19SBen Gras * has ip addresses, we don't have to trust a nameserver. When it
6322fe8fb19SBen Gras * contains hostnames, we spin through the list of addresses the nameserver
6332fe8fb19SBen Gras * gives us and look for a match.
6342fe8fb19SBen Gras *
6352fe8fb19SBen Gras * Returns 0 if ok, -1 if not ok.
6362fe8fb19SBen Gras */
6372fe8fb19SBen Gras int
iruserok(u_int32_t raddr,int superuser,const char * ruser,const char * luser)638f14fb602SLionel Sambuc iruserok(u_int32_t raddr, int superuser, const char *ruser, const char *luser)
6392fe8fb19SBen Gras {
6402fe8fb19SBen Gras struct sockaddr_in irsin;
6412fe8fb19SBen Gras
6422fe8fb19SBen Gras memset(&irsin, 0, sizeof(irsin));
6432fe8fb19SBen Gras irsin.sin_family = AF_INET;
6442fe8fb19SBen Gras #ifdef BSD4_4
645f14fb602SLionel Sambuc irsin.sin_len = sizeof(irsin);
6462fe8fb19SBen Gras #endif
6472fe8fb19SBen Gras memcpy(&irsin.sin_addr, &raddr, sizeof(irsin.sin_addr));
648f14fb602SLionel Sambuc return iruserok_sa(&irsin, (socklen_t)sizeof(irsin), superuser, ruser,
6492fe8fb19SBen Gras luser);
6502fe8fb19SBen Gras }
6512fe8fb19SBen Gras
6522fe8fb19SBen Gras /*
6532fe8fb19SBen Gras * 2nd and 3rd arguments are typed like this, to avoid dependency between
6542fe8fb19SBen Gras * unistd.h and sys/socket.h. There's no better way.
6552fe8fb19SBen Gras */
6562fe8fb19SBen Gras int
iruserok_sa(const void * raddr,int rlen,int superuser,const char * ruser,const char * luser)657f14fb602SLionel Sambuc iruserok_sa(const void *raddr, int rlen, int superuser, const char *ruser,
658f14fb602SLionel Sambuc const char *luser)
6592fe8fb19SBen Gras {
6602fe8fb19SBen Gras const struct sockaddr *sa;
6612fe8fb19SBen Gras struct stat sbuf;
6622fe8fb19SBen Gras struct passwd *pwd, pwres;
6632fe8fb19SBen Gras FILE *hostf;
6642fe8fb19SBen Gras uid_t uid;
6652fe8fb19SBen Gras gid_t gid;
6662fe8fb19SBen Gras int isvaliduser;
6672fe8fb19SBen Gras char pbuf[MAXPATHLEN];
6682fe8fb19SBen Gras char pwbuf[1024];
6692fe8fb19SBen Gras
6702fe8fb19SBen Gras _DIAGASSERT(raddr != NULL);
6712fe8fb19SBen Gras _DIAGASSERT(ruser != NULL);
6722fe8fb19SBen Gras _DIAGASSERT(luser != NULL);
6732fe8fb19SBen Gras
6742fe8fb19SBen Gras sa = raddr;
6752fe8fb19SBen Gras
6762fe8fb19SBen Gras __rcmd_errstr = NULL;
6772fe8fb19SBen Gras
678*0a6a1f1dSLionel Sambuc hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re");
6792fe8fb19SBen Gras
6802fe8fb19SBen Gras if (hostf) {
6812fe8fb19SBen Gras if (__ivaliduser_sa(hostf, sa, (socklen_t)rlen, luser,
6822fe8fb19SBen Gras ruser) == 0) {
6832fe8fb19SBen Gras (void)fclose(hostf);
684f14fb602SLionel Sambuc return 0;
6852fe8fb19SBen Gras }
6862fe8fb19SBen Gras (void)fclose(hostf);
6872fe8fb19SBen Gras }
6882fe8fb19SBen Gras
6892fe8fb19SBen Gras isvaliduser = -1;
6902fe8fb19SBen Gras if (__check_rhosts_file || superuser) {
6912fe8fb19SBen Gras
6922fe8fb19SBen Gras if (getpwnam_r(luser, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0
6932fe8fb19SBen Gras || pwd == NULL)
694f14fb602SLionel Sambuc return -1;
6952fe8fb19SBen Gras (void)strlcpy(pbuf, pwd->pw_dir, sizeof(pbuf));
6962fe8fb19SBen Gras (void)strlcat(pbuf, "/.rhosts", sizeof(pbuf));
6972fe8fb19SBen Gras
6982fe8fb19SBen Gras /*
6992fe8fb19SBen Gras * Change effective uid while opening and reading .rhosts.
7002fe8fb19SBen Gras * If root and reading an NFS mounted file system, can't
7012fe8fb19SBen Gras * read files that are protected read/write owner only.
7022fe8fb19SBen Gras */
7032fe8fb19SBen Gras uid = geteuid();
7042fe8fb19SBen Gras gid = getegid();
7052fe8fb19SBen Gras (void)setegid(pwd->pw_gid);
706*0a6a1f1dSLionel Sambuc (void)initgroups(pwd->pw_name, pwd->pw_gid);
7072fe8fb19SBen Gras (void)seteuid(pwd->pw_uid);
708*0a6a1f1dSLionel Sambuc hostf = fopen(pbuf, "re");
7092fe8fb19SBen Gras
7102fe8fb19SBen Gras if (hostf != NULL) {
7112fe8fb19SBen Gras /*
7122fe8fb19SBen Gras * If not a regular file, or is owned by someone other
7132fe8fb19SBen Gras * than user or root or if writable by anyone but the
7142fe8fb19SBen Gras * owner, quit.
7152fe8fb19SBen Gras */
7162fe8fb19SBen Gras if (lstat(pbuf, &sbuf) < 0)
7172fe8fb19SBen Gras __rcmd_errstr = ".rhosts lstat failed";
7182fe8fb19SBen Gras else if (!S_ISREG(sbuf.st_mode))
7192fe8fb19SBen Gras __rcmd_errstr = ".rhosts not regular file";
7202fe8fb19SBen Gras else if (fstat(fileno(hostf), &sbuf) < 0)
7212fe8fb19SBen Gras __rcmd_errstr = ".rhosts fstat failed";
7222fe8fb19SBen Gras else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
7232fe8fb19SBen Gras __rcmd_errstr = "bad .rhosts owner";
7242fe8fb19SBen Gras else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
7252fe8fb19SBen Gras __rcmd_errstr =
7262fe8fb19SBen Gras ".rhosts writable by other than owner";
7272fe8fb19SBen Gras else
7282fe8fb19SBen Gras isvaliduser =
7292fe8fb19SBen Gras __ivaliduser_sa(hostf, sa, (socklen_t)rlen,
7302fe8fb19SBen Gras luser, ruser);
7312fe8fb19SBen Gras
7322fe8fb19SBen Gras (void)fclose(hostf);
7332fe8fb19SBen Gras }
7342fe8fb19SBen Gras (void)seteuid(uid);
7352fe8fb19SBen Gras (void)setegid(gid);
7362fe8fb19SBen Gras
7372fe8fb19SBen Gras }
738f14fb602SLionel Sambuc return isvaliduser;
7392fe8fb19SBen Gras }
7402fe8fb19SBen Gras
7412fe8fb19SBen Gras /*
7422fe8fb19SBen Gras * XXX
7432fe8fb19SBen Gras * Don't make static, used by lpd(8). We will be able to change the function
7442fe8fb19SBen Gras * into static function, when we bump libc major #.
7452fe8fb19SBen Gras *
7462fe8fb19SBen Gras * Returns 0 if ok, -1 if not ok.
7472fe8fb19SBen Gras */
7482fe8fb19SBen Gras #ifdef notdef /*_LIBC*/
7492fe8fb19SBen Gras static
7502fe8fb19SBen Gras #endif
7512fe8fb19SBen Gras int
__ivaliduser(FILE * hostf,u_int32_t raddr,const char * luser,const char * ruser)752f14fb602SLionel Sambuc __ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser,
753f14fb602SLionel Sambuc const char *ruser)
7542fe8fb19SBen Gras {
7552fe8fb19SBen Gras struct sockaddr_in ivusin;
7562fe8fb19SBen Gras
7572fe8fb19SBen Gras memset(&ivusin, 0, sizeof(ivusin));
7582fe8fb19SBen Gras ivusin.sin_family = AF_INET;
7592fe8fb19SBen Gras #ifdef BSD4_4
760f14fb602SLionel Sambuc ivusin.sin_len = sizeof(ivusin);
7612fe8fb19SBen Gras #endif
7622fe8fb19SBen Gras memcpy(&ivusin.sin_addr, &raddr, sizeof(ivusin.sin_addr));
7632fe8fb19SBen Gras return __ivaliduser_sa(hostf, (struct sockaddr *)(void *)&ivusin,
764f14fb602SLionel Sambuc (socklen_t)sizeof(ivusin), luser, ruser);
7652fe8fb19SBen Gras }
7662fe8fb19SBen Gras
7672fe8fb19SBen Gras #ifdef notdef /*_LIBC*/
7682fe8fb19SBen Gras static
7692fe8fb19SBen Gras #endif
7702fe8fb19SBen Gras int
__ivaliduser_sa(FILE * hostf,const struct sockaddr * raddr,socklen_t salen,const char * luser,const char * ruser)771f14fb602SLionel Sambuc __ivaliduser_sa(FILE *hostf, const struct sockaddr *raddr, socklen_t salen,
772f14fb602SLionel Sambuc const char *luser, const char *ruser)
7732fe8fb19SBen Gras {
774f14fb602SLionel Sambuc char *user, *p;
7752fe8fb19SBen Gras int ch;
7762fe8fb19SBen Gras char buf[MAXHOSTNAMELEN + 128]; /* host + login */
7772fe8fb19SBen Gras const char *auser, *ahost;
7782fe8fb19SBen Gras int hostok, userok;
7792fe8fb19SBen Gras char *rhost = NULL;
7802fe8fb19SBen Gras int firsttime = 1;
7812fe8fb19SBen Gras char domain[MAXHOSTNAMELEN];
7822fe8fb19SBen Gras
7832fe8fb19SBen Gras getdomainname(domain, sizeof(domain));
7842fe8fb19SBen Gras
7852fe8fb19SBen Gras _DIAGASSERT(hostf != NULL);
7862fe8fb19SBen Gras _DIAGASSERT(luser != NULL);
7872fe8fb19SBen Gras _DIAGASSERT(ruser != NULL);
7882fe8fb19SBen Gras
789f14fb602SLionel Sambuc while (fgets(buf, (int)sizeof(buf), hostf)) {
7902fe8fb19SBen Gras p = buf;
7912fe8fb19SBen Gras /* Skip lines that are too long. */
7922fe8fb19SBen Gras if (strchr(p, '\n') == NULL) {
7932fe8fb19SBen Gras while ((ch = getc(hostf)) != '\n' && ch != EOF)
7942fe8fb19SBen Gras ;
7952fe8fb19SBen Gras continue;
7962fe8fb19SBen Gras }
7972fe8fb19SBen Gras while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
7982fe8fb19SBen Gras *p = isupper((unsigned char)*p) ?
7992fe8fb19SBen Gras tolower((unsigned char)*p) : *p;
8002fe8fb19SBen Gras p++;
8012fe8fb19SBen Gras }
8022fe8fb19SBen Gras if (*p == ' ' || *p == '\t') {
8032fe8fb19SBen Gras *p++ = '\0';
8042fe8fb19SBen Gras while (*p == ' ' || *p == '\t')
8052fe8fb19SBen Gras p++;
8062fe8fb19SBen Gras user = p;
8072fe8fb19SBen Gras while (*p != '\n' && *p != ' ' &&
8082fe8fb19SBen Gras *p != '\t' && *p != '\0')
8092fe8fb19SBen Gras p++;
8102fe8fb19SBen Gras } else
8112fe8fb19SBen Gras user = p;
8122fe8fb19SBen Gras *p = '\0';
8132fe8fb19SBen Gras
8142fe8fb19SBen Gras if (p == buf)
8152fe8fb19SBen Gras continue;
8162fe8fb19SBen Gras
8172fe8fb19SBen Gras auser = *user ? user : luser;
8182fe8fb19SBen Gras ahost = buf;
8192fe8fb19SBen Gras
8202fe8fb19SBen Gras if (ahost[0] == '+')
8212fe8fb19SBen Gras switch (ahost[1]) {
8222fe8fb19SBen Gras case '\0':
8232fe8fb19SBen Gras hostok = 1;
8242fe8fb19SBen Gras break;
8252fe8fb19SBen Gras
8262fe8fb19SBen Gras case '@':
8272fe8fb19SBen Gras if (firsttime) {
8282fe8fb19SBen Gras rhost = __gethostloop(raddr, salen);
8292fe8fb19SBen Gras firsttime = 0;
8302fe8fb19SBen Gras }
8312fe8fb19SBen Gras if (rhost)
8322fe8fb19SBen Gras hostok = innetgr(&ahost[2], rhost,
8332fe8fb19SBen Gras NULL, domain);
8342fe8fb19SBen Gras else
8352fe8fb19SBen Gras hostok = 0;
8362fe8fb19SBen Gras break;
8372fe8fb19SBen Gras
8382fe8fb19SBen Gras default:
8392fe8fb19SBen Gras hostok = __icheckhost(raddr, salen, &ahost[1]);
8402fe8fb19SBen Gras break;
8412fe8fb19SBen Gras }
8422fe8fb19SBen Gras else if (ahost[0] == '-')
8432fe8fb19SBen Gras switch (ahost[1]) {
8442fe8fb19SBen Gras case '\0':
8452fe8fb19SBen Gras hostok = -1;
8462fe8fb19SBen Gras break;
8472fe8fb19SBen Gras
8482fe8fb19SBen Gras case '@':
8492fe8fb19SBen Gras if (firsttime) {
8502fe8fb19SBen Gras rhost = __gethostloop(raddr, salen);
8512fe8fb19SBen Gras firsttime = 0;
8522fe8fb19SBen Gras }
8532fe8fb19SBen Gras if (rhost)
8542fe8fb19SBen Gras hostok = -innetgr(&ahost[2], rhost,
8552fe8fb19SBen Gras NULL, domain);
8562fe8fb19SBen Gras else
8572fe8fb19SBen Gras hostok = 0;
8582fe8fb19SBen Gras break;
8592fe8fb19SBen Gras
8602fe8fb19SBen Gras default:
861f14fb602SLionel Sambuc hostok =
862f14fb602SLionel Sambuc -__icheckhost(raddr, salen, &ahost[1]);
8632fe8fb19SBen Gras break;
8642fe8fb19SBen Gras }
8652fe8fb19SBen Gras else
8662fe8fb19SBen Gras hostok = __icheckhost(raddr, salen, ahost);
8672fe8fb19SBen Gras
8682fe8fb19SBen Gras
8692fe8fb19SBen Gras if (auser[0] == '+')
8702fe8fb19SBen Gras switch (auser[1]) {
8712fe8fb19SBen Gras case '\0':
8722fe8fb19SBen Gras userok = 1;
8732fe8fb19SBen Gras break;
8742fe8fb19SBen Gras
8752fe8fb19SBen Gras case '@':
8762fe8fb19SBen Gras userok = innetgr(&auser[2], NULL, ruser,
8772fe8fb19SBen Gras domain);
8782fe8fb19SBen Gras break;
8792fe8fb19SBen Gras
8802fe8fb19SBen Gras default:
8812fe8fb19SBen Gras userok = strcmp(ruser, &auser[1]) == 0;
8822fe8fb19SBen Gras break;
8832fe8fb19SBen Gras }
8842fe8fb19SBen Gras else if (auser[0] == '-')
8852fe8fb19SBen Gras switch (auser[1]) {
8862fe8fb19SBen Gras case '\0':
8872fe8fb19SBen Gras userok = -1;
8882fe8fb19SBen Gras break;
8892fe8fb19SBen Gras
8902fe8fb19SBen Gras case '@':
8912fe8fb19SBen Gras userok = -innetgr(&auser[2], NULL, ruser,
8922fe8fb19SBen Gras domain);
8932fe8fb19SBen Gras break;
8942fe8fb19SBen Gras
8952fe8fb19SBen Gras default:
8962fe8fb19SBen Gras userok =
8972fe8fb19SBen Gras -(strcmp(ruser, &auser[1]) == 0 ? 1 : 0);
8982fe8fb19SBen Gras break;
8992fe8fb19SBen Gras }
9002fe8fb19SBen Gras else
9012fe8fb19SBen Gras userok = strcmp(ruser, auser) == 0;
9022fe8fb19SBen Gras
9032fe8fb19SBen Gras /* Check if one component did not match */
9042fe8fb19SBen Gras if (hostok == 0 || userok == 0)
9052fe8fb19SBen Gras continue;
9062fe8fb19SBen Gras
9072fe8fb19SBen Gras /* Check if we got a forbidden pair */
9082fe8fb19SBen Gras if (userok == -1 || hostok == -1)
9092fe8fb19SBen Gras return -1;
9102fe8fb19SBen Gras
9112fe8fb19SBen Gras /* Check if we got a valid pair */
9122fe8fb19SBen Gras if (hostok == 1 && userok == 1)
9132fe8fb19SBen Gras return 0;
9142fe8fb19SBen Gras }
9152fe8fb19SBen Gras return -1;
9162fe8fb19SBen Gras }
9172fe8fb19SBen Gras
9182fe8fb19SBen Gras /*
9192fe8fb19SBen Gras * Returns "true" if match, 0 if no match.
9202fe8fb19SBen Gras */
9212fe8fb19SBen Gras static int
__icheckhost(const struct sockaddr * raddr,socklen_t salen,const char * lhost)922f14fb602SLionel Sambuc __icheckhost(const struct sockaddr *raddr, socklen_t salen, const char *lhost)
9232fe8fb19SBen Gras {
9242fe8fb19SBen Gras struct addrinfo hints, *res, *r;
9252fe8fb19SBen Gras char h1[NI_MAXHOST], h2[NI_MAXHOST];
9262fe8fb19SBen Gras int error;
9272fe8fb19SBen Gras const int niflags = NI_NUMERICHOST;
9282fe8fb19SBen Gras
9292fe8fb19SBen Gras _DIAGASSERT(raddr != NULL);
9302fe8fb19SBen Gras _DIAGASSERT(lhost != NULL);
9312fe8fb19SBen Gras
9322fe8fb19SBen Gras h1[0] = '\0';
933f14fb602SLionel Sambuc if (getnameinfo(raddr, salen, h1, (socklen_t)sizeof(h1), NULL, 0,
9342fe8fb19SBen Gras niflags) != 0)
935f14fb602SLionel Sambuc return 0;
9362fe8fb19SBen Gras
9372fe8fb19SBen Gras /* Resolve laddr into sockaddr */
9382fe8fb19SBen Gras memset(&hints, 0, sizeof(hints));
9392fe8fb19SBen Gras hints.ai_family = raddr->sa_family;
9402fe8fb19SBen Gras hints.ai_socktype = SOCK_DGRAM; /*dummy*/
9412fe8fb19SBen Gras res = NULL;
9422fe8fb19SBen Gras error = getaddrinfo(lhost, "0", &hints, &res);
9432fe8fb19SBen Gras if (error)
944f14fb602SLionel Sambuc return 0;
9452fe8fb19SBen Gras
9462fe8fb19SBen Gras /*
9472fe8fb19SBen Gras * Try string comparisons between raddr and laddr.
9482fe8fb19SBen Gras */
9492fe8fb19SBen Gras for (r = res; r; r = r->ai_next) {
9502fe8fb19SBen Gras h2[0] = '\0';
951f14fb602SLionel Sambuc if (getnameinfo(r->ai_addr, r->ai_addrlen, h2,
952f14fb602SLionel Sambuc (socklen_t)sizeof(h2), NULL, 0, niflags) != 0)
9532fe8fb19SBen Gras continue;
9542fe8fb19SBen Gras if (strcmp(h1, h2) == 0) {
9552fe8fb19SBen Gras freeaddrinfo(res);
956f14fb602SLionel Sambuc return 1;
9572fe8fb19SBen Gras }
9582fe8fb19SBen Gras }
9592fe8fb19SBen Gras
9602fe8fb19SBen Gras /* No match. */
9612fe8fb19SBen Gras freeaddrinfo(res);
962f14fb602SLionel Sambuc return 0;
9632fe8fb19SBen Gras }
9642fe8fb19SBen Gras
9652fe8fb19SBen Gras /*
9662fe8fb19SBen Gras * Return the hostname associated with the supplied address.
9672fe8fb19SBen Gras * Do a reverse lookup as well for security. If a loop cannot
9682fe8fb19SBen Gras * be found, pack the numeric IP address into the string.
9692fe8fb19SBen Gras */
9702fe8fb19SBen Gras static char *
__gethostloop(const struct sockaddr * raddr,socklen_t salen)971f14fb602SLionel Sambuc __gethostloop(const struct sockaddr *raddr, socklen_t salen)
9722fe8fb19SBen Gras {
9732fe8fb19SBen Gras static char remotehost[NI_MAXHOST];
9742fe8fb19SBen Gras char h1[NI_MAXHOST], h2[NI_MAXHOST];
9752fe8fb19SBen Gras struct addrinfo hints, *res, *r;
9762fe8fb19SBen Gras int error;
9772fe8fb19SBen Gras const int niflags = NI_NUMERICHOST;
9782fe8fb19SBen Gras
9792fe8fb19SBen Gras _DIAGASSERT(raddr != NULL);
9802fe8fb19SBen Gras
9812fe8fb19SBen Gras h1[0] = remotehost[0] = '\0';
982f14fb602SLionel Sambuc if (getnameinfo(raddr, salen, remotehost, (socklen_t)sizeof(remotehost),
9832fe8fb19SBen Gras NULL, 0, NI_NAMEREQD) != 0)
984f14fb602SLionel Sambuc return NULL;
985f14fb602SLionel Sambuc if (getnameinfo(raddr, salen, h1, (socklen_t)sizeof(h1), NULL, 0,
9862fe8fb19SBen Gras niflags) != 0)
987f14fb602SLionel Sambuc return NULL;
9882fe8fb19SBen Gras
9892fe8fb19SBen Gras /*
9902fe8fb19SBen Gras * Look up the name and check that the supplied
9912fe8fb19SBen Gras * address is in the list
9922fe8fb19SBen Gras */
9932fe8fb19SBen Gras memset(&hints, 0, sizeof(hints));
9942fe8fb19SBen Gras hints.ai_family = raddr->sa_family;
9952fe8fb19SBen Gras hints.ai_socktype = SOCK_DGRAM; /*dummy*/
9962fe8fb19SBen Gras hints.ai_flags = AI_CANONNAME;
9972fe8fb19SBen Gras res = NULL;
9982fe8fb19SBen Gras error = getaddrinfo(remotehost, "0", &hints, &res);
9992fe8fb19SBen Gras if (error)
1000f14fb602SLionel Sambuc return NULL;
10012fe8fb19SBen Gras
10022fe8fb19SBen Gras for (r = res; r; r = r->ai_next) {
10032fe8fb19SBen Gras h2[0] = '\0';
1004f14fb602SLionel Sambuc if (getnameinfo(r->ai_addr, r->ai_addrlen, h2,
1005f14fb602SLionel Sambuc (socklen_t)sizeof(h2), NULL, 0, niflags) != 0)
10062fe8fb19SBen Gras continue;
10072fe8fb19SBen Gras if (strcmp(h1, h2) == 0) {
10082fe8fb19SBen Gras freeaddrinfo(res);
1009f14fb602SLionel Sambuc return remotehost;
10102fe8fb19SBen Gras }
10112fe8fb19SBen Gras }
10122fe8fb19SBen Gras
10132fe8fb19SBen Gras /*
10142fe8fb19SBen Gras * either the DNS adminstrator has made a configuration
10152fe8fb19SBen Gras * mistake, or someone has attempted to spoof us
10162fe8fb19SBen Gras */
10172fe8fb19SBen Gras syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s",
10182fe8fb19SBen Gras h1, res->ai_canonname ? res->ai_canonname : remotehost);
10192fe8fb19SBen Gras freeaddrinfo(res);
1020f14fb602SLionel Sambuc return NULL;
10212fe8fb19SBen Gras }
1022