/*-
* Copyright (c) 1998, 2003 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
* NASA Ames Research Center and by Matthias Scheler.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 1983, 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1983, 1991, 1993, 1994\
The Regents of the University of California. All rights reserved.");
#if 0
static char sccsid[] = "@(#)inetd.c 8.4 (Berkeley) 4/13/94";
#else
__RCSID("$NetBSD: inetd.c,v 1.141 2022/08/10 08:37:53 christos Exp $");
#endif
#endif /* not lint */
/*
* Inetd - Internet super-server
*
* This program invokes all internet services as needed. Connection-oriented
* services are invoked each time a connection is made, by creating a process.
* This process is passed the connection as file descriptor 0 and is expected
* to do a getpeername to find out the source host and port.
*
* Datagram oriented services are invoked when a datagram
* arrives; a process is created and passed a pending message
* on file descriptor 0. Datagram servers may either connect
* to their peer, freeing up the original socket for inetd
* to receive further messages on, or ``take over the socket'',
* processing all arriving datagrams and, eventually, timing
* out. The first type of server is said to be ``multi-threaded'';
* the second type of server ``single-threaded''.
*
* Inetd uses a configuration file which is read at startup
* and, possibly, at some later time in response to a hangup signal.
* The configuration file is ``free format'' with fields given in the
* order shown below. Continuation lines for an entry must being with
* a space or tab. All fields must be present in each entry.
*
* service name must be in /etc/services or must
* name a tcpmux service
* socket type[:accf[,arg]] stream/dgram/raw/rdm/seqpacket,
only stream can name an accept filter
* protocol must be in /etc/protocols
* wait/nowait[:max] single-threaded/multi-threaded, max #
* user[:group] user/group to run daemon as
* server program full path name
* server program arguments maximum of MAXARGV (64)
*
* For RPC services
* service name/version must be in /etc/rpc
* socket type stream/dgram/raw/rdm/seqpacket
* protocol must be in /etc/protocols
* wait/nowait[:max] single-threaded/multi-threaded
* user[:group] user to run daemon as
* server program full path name
* server program arguments maximum of MAXARGV (64)
*
* For non-RPC services, the "service name" can be of the form
* hostaddress:servicename, in which case the hostaddress is used
* as the host portion of the address to listen on. If hostaddress
* consists of a single `*' character, INADDR_ANY is used.
*
* A line can also consist of just
* hostaddress:
* where hostaddress is as in the preceding paragraph. Such a line must
* have no further fields; the specified hostaddress is remembered and
* used for all further lines that have no hostaddress specified,
* until the next such line (or EOF). (This is why * is provided to
* allow explicit specification of INADDR_ANY.) A line
* *:
* is implicitly in effect at the beginning of the file.
*
* The hostaddress specifier may (and often will) contain dots;
* the service name must not.
*
* For RPC services, host-address specifiers are accepted and will
* work to some extent; however, because of limitations in the
* portmapper interface, it will not work to try to give more than
* one line for any given RPC service, even if the host-address
* specifiers are different.
*
* TCP services without official port numbers are handled with the
* RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
* requests. When a connection is made from a foreign host, the service
* requested is passed to tcpmux, which looks it up in the servtab list
* and returns the proper entry for the service. Tcpmux returns a
* negative reply if the service doesn't exist, otherwise the invoked
* server is expected to return the positive reply if the service type in
* inetd.conf file has the prefix "tcpmux/". If the service type has the
* prefix "tcpmux/+", tcpmux will return the positive reply for the
* process; this is for compatibility with older server code, and also
* allows you to invoke programs that use stdin/stdout without putting any
* special server code in them. Services that use tcpmux are "nowait"
* because they do not have a well-known port and hence cannot listen
* for new requests.
*
* Comment lines are indicated by a `#' in column 1.
*
* #ifdef IPSEC
* Comment lines that start with "#@" denote IPsec policy string, as described
* in ipsec_set_policy(3). This will affect all the following items in
* inetd.conf(8). To reset the policy, just use "#@" line. By default,
* there's no IPsec policy.
* #endif
*/
/*
* Here's the scoop concerning the user:group feature:
*
* 1) set-group-option off.
*
* a) user = root: NO setuid() or setgid() is done
*
* b) other: setuid()
* setgid(primary group as found in passwd)
* initgroups(name, primary group)
*
* 2) set-group-option on.
*
* a) user = root: NO setuid()
* setgid(specified group)
* NO initgroups()
*
* b) other: setuid()
* setgid(specified group)
* initgroups(name, specified group)
*
*/
static bool foreground;
int debug;
#ifdef LIBWRAP
int lflag;
#endif
int maxsock;
int kq;
int options;
int timingout;
const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
struct biltin {
const char *bi_service; /* internally provided service name */
int bi_socktype; /* type of socket supported */
short bi_fork; /* 1 if should fork before call */
short bi_wait; /* 1 if should wait for child */
void (*bi_fn)(int, struct servtab *);
/* function which performs it */
} biltins[] = {
/* Echo received data */
{ "echo", SOCK_STREAM, true, false, echo_stream },
{ "echo", SOCK_DGRAM, false, false, echo_dg },
/* list of "bad" ports. I.e. ports that are most obviously used for
* "cycling packets" denial of service attacks. See /etc/services.
* List must end with port number "0".
*/
/* for internal dgram, setsockopt() is required for recvfromto() */
if (sep->se_socktype == SOCK_DGRAM && sep->se_bi != NULL) {
switch (sep->se_family) {
case AF_INET:
if (setsockopt(sep->se_fd, IPPROTO_IP,
IP_RECVDSTADDR, &on, sizeof(on)) < 0)
syslog(LOG_ERR,
"setsockopt (IP_RECVDSTADDR): %m");
break;
#ifdef INET6
case AF_INET6:
if (setsockopt(sep->se_fd, IPPROTO_IPV6,
IPV6_RECVPKTINFO, &on, sizeof(on)) < 0)
syslog(LOG_ERR,
"setsockopt (IPV6_RECVPKTINFO): %m");
break;
#endif
}
}
/* Set the accept filter, if specified. To be done after listen.*/
if (sep->se_accf.af_name[0] != 0 && setsockopt(sep->se_fd, SOL_SOCKET,
SO_ACCEPTFILTER, &sep->se_accf,
(socklen_t)sizeof(sep->se_accf)) < 0)
syslog(LOG_ERR, "setsockopt(SO_ACCEPTFILTER %s): %m",
sep->se_accf.af_name);
ev = allocchange();
EV_SET(ev, sep->se_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0,
(intptr_t)sep);
if (sep->se_fd > maxsock) {
maxsock = sep->se_fd;
if (maxsock > (int)(rlim_ofile_cur - FD_MARGIN))
bump_nofile();
}
DPRINTF(SERV_FMT ": registered on fd %d", SERV_PARAMS(sep), sep->se_fd);
}
/*
* Finish with a service and its socket.
*/
void
close_sep(struct servtab *sep)
{
/*
* In order to get the destination address (`to') with recvfromto(),
* IP_RECVDSTADDR or IP_RECVPKTINFO for AF_INET, or IPV6_RECVPKTINFO
* for AF_INET6, must be enabled with setsockopt(2).
*
* .sin_port and .sin6_port in 'to' are always stored as zero.
* If necessary, extract them using getsockname(2).
*/
static ssize_t
recvfromto(int s, void * restrict buf, size_t len, int flags,
struct sockaddr * restrict from, socklen_t * restrict fromlen,
struct sockaddr * restrict to, socklen_t * restrict tolen)
{
struct msghdr msg;
struct iovec vec;
struct cmsghdr *cmsg;
struct sockaddr_storage ss;
char cmsgbuf[1024];
ssize_t rc;
/*
* Return a machine readable date and time, in the form of the
* number of seconds since midnight, Jan 1, 1900. Since gettimeofday
* returns the number of seconds since midnight, Jan 1, 1970,
* we must add 2208988800 seconds to this figure to make up for
* some seventy years Bell Labs was asleep.
*/
/* Get requested service name */
if ((len = get_line(ctrl, service, MAX_SERV_LEN)) < 0) {
strwrite(ctrl, "-Error reading service name\r\n");
goto reject;
}
service[len] = '\0';
DPRINTF("tcpmux: %s: service requested", service);
/*
* Help is a required command, and lists available services,
* one per line.
*/
if (strcasecmp(service, "help") == 0) {
strwrite(ctrl, "+Available services:\r\n");
strwrite(ctrl, "help\r\n");
for (sep = servtab; sep != NULL; sep = sep->se_next) {
if (!ISMUX(sep))
continue;
(void)write(ctrl, sep->se_service,
strlen(sep->se_service));
strwrite(ctrl, "\r\n");
}
goto reject;
}
/* Try matching a service in inetd.conf with the request */
for (sep = servtab; sep != NULL; sep = sep->se_next) {
if (!ISMUX(sep))
continue;
if (strcasecmp(service, sep->se_service) == 0) {
if (ISMUXPLUS(sep))
strwrite(ctrl, "+Go\r\n");
run_service(ctrl, sep, true /* forked */);
return;
}
}
strwrite(ctrl, "-Service not available\r\n");
reject:
_exit(EXIT_FAILURE);
}
/*
* check if the address/port where send data to is one of the obvious ports
* that are used for denial of service attacks like two echo ports
* just echoing data between them
*/
static int
port_good_dg(struct sockaddr *sa)
{
struct in_addr in;
struct sockaddr_in *sin;
#ifdef INET6
struct in6_addr *in6;
struct sockaddr_in6 *sin6;
#endif
u_int16_t port;
int i;
char hbuf[NI_MAXHOST];
switch (sa->sa_family) {
case AF_INET:
sin = (struct sockaddr_in *)(void *)sa;
in.s_addr = ntohl(sin->sin_addr.s_addr);
port = ntohs(sin->sin_port);
#ifdef INET6
v4chk:
#endif
if (IN_MULTICAST(in.s_addr))
goto bad;
switch ((in.s_addr & 0xff000000) >> 24) {
case 0: case 127: case 255:
goto bad;
}
if (dg_broadcast(&in))
goto bad;
break;
#ifdef INET6
case AF_INET6:
sin6 = (struct sockaddr_in6 *)(void *)sa;
in6 = &sin6->sin6_addr;
port = ntohs(sin6->sin6_port);
if (IN6_IS_ADDR_MULTICAST(in6) || IN6_IS_ADDR_UNSPECIFIED(in6))
goto bad;
if (IN6_IS_ADDR_V4MAPPED(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
memcpy(&in, &in6->s6_addr[12], sizeof(in));
in.s_addr = ntohl(in.s_addr);
goto v4chk;
}
break;
#endif
default:
/* XXX unsupported af, is it safe to assume it to be safe? */
return true;
}
for (i = 0; bad_ports[i] != 0; i++) {
if (port == bad_ports[i])
goto bad;
}
return true;
bad:
if (getnameinfo(sa, sa->sa_len, hbuf, (socklen_t)sizeof(hbuf), NULL, 0,
niflags) != 0)
strlcpy(hbuf, "?", sizeof(hbuf));
syslog(LOG_WARNING,"Possible DoS attack from %s, Port %d",
hbuf, port);
return false;
}
/* XXX need optimization */
static int
dg_broadcast(struct in_addr *in)
{
struct ifaddrs *ifa, *ifap;
struct sockaddr_in *sin;
if (getifaddrs(&ifap) < 0)
return false;
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != AF_INET ||
(ifa->ifa_flags & IFF_BROADCAST) == 0)
continue;
sin = (struct sockaddr_in *)(void *)ifa->ifa_broadaddr;
if (sin->sin_addr.s_addr == in->s_addr) {
freeifaddrs(ifap);
return true;
}
}
freeifaddrs(ifap);
return false;
}
static int
my_kevent(const struct kevent *changelist, size_t nchanges,
struct kevent *eventlist, size_t nevents)
{
int result;