Index: stable/4/usr.sbin/inetd/Makefile =================================================================== --- stable/4/usr.sbin/inetd/Makefile (revision 67517) +++ stable/4/usr.sbin/inetd/Makefile (revision 67518) @@ -1,19 +1,21 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 # $FreeBSD$ +MAINTAINER= dwmalone + PROG= inetd SRCS= inetd.c builtins.c MAN8= inetd.8 MLINKS= inetd.8 inetd.conf.5 COPTS+= -Wall -DLOGIN_CAP #COPTS+= -DSANITY_CHECK DPADD+= ${LIBUTIL} ${LIBWRAP} LDADD+= -lutil -lwrap .if !defined(RELEASE_CRUNCH) CFLAGS+=-DINET6 -DIPSEC DPADD+= ${LIBIPSEC} LDADD+= -lipsec .endif .include Index: stable/4/usr.sbin/inetd/builtins.c =================================================================== --- stable/4/usr.sbin/inetd/builtins.c (revision 67517) +++ stable/4/usr.sbin/inetd/builtins.c (revision 67518) @@ -1,792 +1,794 @@ /*- * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inetd.h" extern int debug; extern struct servtab *servtab; char ring[128]; char *endring; int check_loop __P((struct sockaddr *, struct servtab *sep)); void inetd_setproctitle __P((char *, int)); struct biltin biltins[] = { /* Echo received data */ { "echo", SOCK_STREAM, 1, -1, echo_stream }, { "echo", SOCK_DGRAM, 0, 1, echo_dg }, /* Internet /dev/null */ { "discard", SOCK_STREAM, 1, -1, discard_stream }, { "discard", SOCK_DGRAM, 0, 1, discard_dg }, /* Return 32 bit time since 1970 */ { "time", SOCK_STREAM, 0, -1, machtime_stream }, { "time", SOCK_DGRAM, 0, 1, machtime_dg }, /* Return human-readable time */ { "daytime", SOCK_STREAM, 0, -1, daytime_stream }, { "daytime", SOCK_DGRAM, 0, 1, daytime_dg }, /* Familiar character generator */ { "chargen", SOCK_STREAM, 1, -1, chargen_stream }, { "chargen", SOCK_DGRAM, 0, 1, chargen_dg }, { "tcpmux", SOCK_STREAM, 1, -1, (void (*)())tcpmux }, { "auth", SOCK_STREAM, 1, -1, ident_stream }, { NULL } }; /* * RFC864 Character Generator Protocol. Generates character data without * any regard for input. */ void initring() { int i; endring = ring; for (i = 0; i <= 128; ++i) if (isprint(i)) *endring++ = i; } /* ARGSUSED */ void chargen_dg(s, sep) /* Character generator */ int s; struct servtab *sep; { struct sockaddr_storage ss; static char *rs; int len; socklen_t size; char text[LINESIZ+2]; if (endring == 0) { initring(); rs = ring; } size = sizeof(ss); if (recvfrom(s, text, sizeof(text), 0, (struct sockaddr *)&ss, &size) < 0) return; if (check_loop((struct sockaddr *)&ss, sep)) return; if ((len = endring - rs) >= LINESIZ) memmove(text, rs, LINESIZ); else { memmove(text, rs, len); memmove(text + len, ring, LINESIZ - len); } if (++rs == endring) rs = ring; text[LINESIZ] = '\r'; text[LINESIZ + 1] = '\n'; (void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size); } /* ARGSUSED */ void chargen_stream(s, sep) /* Character generator */ int s; struct servtab *sep; { int len; char *rs, text[LINESIZ+2]; inetd_setproctitle(sep->se_service, s); if (!endring) { initring(); rs = ring; } text[LINESIZ] = '\r'; text[LINESIZ + 1] = '\n'; for (rs = ring;;) { if ((len = endring - rs) >= LINESIZ) memmove(text, rs, LINESIZ); else { memmove(text, rs, len); memmove(text + len, ring, LINESIZ - len); } if (++rs == endring) rs = ring; if (write(s, text, sizeof(text)) != sizeof(text)) break; } exit(0); } /* * RFC867 Daytime Protocol. Sends the current date and time as an ascii * character string without any regard for input. */ /* ARGSUSED */ void daytime_dg(s, sep) /* Return human-readable time of day */ int s; struct servtab *sep; { char buffer[256]; time_t clock; struct sockaddr_storage ss; socklen_t size; clock = time((time_t *) 0); size = sizeof(ss); if (recvfrom(s, buffer, sizeof(buffer), 0, (struct sockaddr *)&ss, &size) < 0) return; if (check_loop((struct sockaddr *)&ss, sep)) return; (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); (void) sendto(s, buffer, strlen(buffer), 0, (struct sockaddr *)&ss, size); } /* ARGSUSED */ void daytime_stream(s, sep) /* Return human-readable time of day */ int s; struct servtab *sep; { char buffer[256]; time_t clock; clock = time((time_t *) 0); (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); (void) send(s, buffer, strlen(buffer), MSG_EOF); } /* * RFC863 Discard Protocol. Any data received is thrown away and no response * is sent. */ /* ARGSUSED */ void discard_dg(s, sep) /* Discard service -- ignore data */ int s; struct servtab *sep; { char buffer[BUFSIZE]; (void) read(s, buffer, sizeof(buffer)); } /* ARGSUSED */ void discard_stream(s, sep) /* Discard service -- ignore data */ int s; struct servtab *sep; { int ret; char buffer[BUFSIZE]; inetd_setproctitle(sep->se_service, s); while (1) { while ((ret = read(s, buffer, sizeof(buffer))) > 0) ; if (ret == 0 || errno != EINTR) break; } exit(0); } /* * RFC862 Echo Protocol. Any data received is sent back to the sender as * received. */ /* ARGSUSED */ void echo_dg(s, sep) /* Echo service -- echo data back */ int s; struct servtab *sep; { char buffer[BUFSIZE]; int i; socklen_t size; struct sockaddr_storage ss; size = sizeof(ss); if ((i = recvfrom(s, buffer, sizeof(buffer), 0, (struct sockaddr *)&ss, &size)) < 0) return; if (check_loop((struct sockaddr *)&ss, sep)) return; (void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size); } /* ARGSUSED */ void echo_stream(s, sep) /* Echo service -- echo data back */ int s; struct servtab *sep; { char buffer[BUFSIZE]; int i; inetd_setproctitle(sep->se_service, s); while ((i = read(s, buffer, sizeof(buffer))) > 0 && write(s, buffer, i) > 0) ; exit(0); } /* * RFC1413 Identification Protocol. Given a TCP port number pair, return a * character string which identifies the owner of that connection on the * server's system. Extended to allow for ~/.fakeid support and ~/.noident * support. */ /* ARGSUSED */ void iderror(lport, fport, s, er) /* Generic ident_stream error-sending func */ int lport, fport, s, er; { char *p; asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR"); if (p == NULL) { syslog(LOG_ERR, "asprintf: %m"); exit(EX_OSERR); } send(s, p, strlen(p), MSG_EOF); free(p); exit(0); } /* ARGSUSED */ void ident_stream(s, sep) /* Ident service (AKA "auth") */ int s; struct servtab *sep; { struct utsname un; struct stat sb; struct sockaddr_in sin[2]; #ifdef INET6 struct sockaddr_in6 sin6[2]; #endif struct sockaddr_storage ss[2]; struct ucred uc; struct timeval tv = { 10, 0 }, to; struct passwd *pw = NULL; fd_set fdset; char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL, garbage[7], e; char *fallback = NULL; socklen_t socklen; ssize_t ssize; size_t size, bufsiz; int c, fflag = 0, nflag = 0, rflag = 0, argc = 0, usedfallback = 0; int gflag = 0, getcredfail = 0, onreadlen; u_short lport, fport; inetd_setproctitle(sep->se_service, s); /* * Reset getopt() since we are a fork() but not an exec() from * a parent which used getopt() already. */ optind = 1; optreset = 1; /* * Take the internal argument vector and count it out to make an * argument count for getopt. This can be used for any internal * service to read arguments and use getopt() easily. */ for (av = sep->se_argv; *av; av++) argc++; if (argc) { int sec, usec; size_t i; u_int32_t random; while ((c = getopt(argc, sep->se_argv, "d:fgno:rt:")) != -1) switch (c) { case 'd': fallback = optarg; break; case 'f': fflag = 1; break; case 'g': gflag = 1; random = 0; /* Shush, compiler. */ /* * The number of bits in "random" divided * by the number of bits needed per iteration * gives a more optimal way to reload the * random number only when necessary. * * I'm using base-36, so I need at least 6 * bits; round it up to 8 bits to make it * easier. */ for (i = 0; i < sizeof(garbage) - 1; i++) { const char *const base36 = "0123456789" "abcdefghijklmnopqrstuvwxyz"; if (i % (sizeof(random) * 8 / 8) == 0) random = arc4random(); garbage[i] = base36[(random & 0xff) % 36]; random >>= 8; } garbage[i] = '\0'; break; case 'n': nflag = 1; break; case 'o': osname = optarg; break; case 'r': rflag = 1; break; case 't': switch (sscanf(optarg, "%d.%d", &sec, &usec)) { case 2: tv.tv_usec = usec; case 1: tv.tv_sec = sec; break; default: if (debug) warnx("bad -t argument"); break; } break; default: break; } } if (osname == NULL) { if (uname(&un) == -1) iderror(0, 0, s, errno); osname = un.sysname; } socklen = sizeof(ss[0]); if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1) iderror(0, 0, s, errno); socklen = sizeof(ss[1]); if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1) iderror(0, 0, s, errno); /* * We're going to prepare for and execute reception of a * packet of data from the user. The data is in the format * "local_port , foreign_port\r\n" (with local being the * server's port and foreign being the client's.) */ gettimeofday(&to, NULL); to.tv_sec += tv.tv_sec; if ((to.tv_usec += tv.tv_usec) >= 1000000) { to.tv_usec -= 1000000; to.tv_sec++; } size = 0; bufsiz = sizeof(buf) - 1; FD_ZERO(&fdset); while (bufsiz > 0 && (size == 0 || buf[size - 1] != '\n')) { gettimeofday(&tv, NULL); tv.tv_sec = to.tv_sec - tv.tv_sec; tv.tv_usec = to.tv_usec - tv.tv_usec; if (tv.tv_usec < 0) { tv.tv_usec += 1000000; tv.tv_sec--; } if (tv.tv_sec < 0) break; FD_SET(s, &fdset); if (select(s + 1, &fdset, NULL, NULL, &tv) == -1) iderror(0, 0, s, errno); if (ioctl(s, FIONREAD, &onreadlen) == -1) iderror(0, 0, s, errno); if (onreadlen > bufsiz) onreadlen = bufsiz; ssize = read(s, &buf[size], (size_t)onreadlen); if (ssize == -1) iderror(0, 0, s, errno); + else if (ssize == 0) + break; bufsiz -= ssize; size += ssize; } buf[size] = '\0'; /* Read two characters, and check for a delimiting character */ if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e)) iderror(0, 0, s, 0); if (gflag) { cp = garbage; goto printit; } /* * If not "real" (-r), send a HIDDEN-USER error for everything. * If -d is used to set a fallback username, this is used to * override it, and the fallback is returned instead. */ if (!rflag) { if (fallback == NULL) iderror(lport, fport, s, -1); else { cp = fallback; goto printit; } } /* * We take the input and construct an array of two sockaddr_ins * which contain the local address information and foreign * address information, respectively, used to look up the * credentials for the socket (which are returned by the * sysctl "net.inet.tcp.getcred" when we call it.) The * arrays have been filled in above via get{peer,sock}name(), * so right here we are only setting the ports. */ if (ss[0].ss_family != ss[1].ss_family) iderror(lport, fport, s, errno); size = sizeof(uc); switch (ss[0].ss_family) { case AF_INET: sin[0] = *(struct sockaddr_in *)&ss[0]; sin[0].sin_port = htons(lport); sin[1] = *(struct sockaddr_in *)&ss[1]; sin[1].sin_port = htons(fport); if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin, sizeof(sin)) == -1) getcredfail = 1; break; #ifdef INET6 case AF_INET6: sin6[0] = *(struct sockaddr_in6 *)&ss[0]; sin6[0].sin6_port = htons(lport); sin6[1] = *(struct sockaddr_in6 *)&ss[1]; sin6[1].sin6_port = htons(fport); if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6, sizeof(sin6)) == -1) getcredfail = 1; break; #endif default: /* should not reach here */ getcredfail = 1; break; } if (getcredfail != 0) { if (fallback == NULL) /* Use a default, if asked to */ iderror(lport, fport, s, errno); usedfallback = 1; } else { /* Look up the pw to get the username */ pw = getpwuid(uc.cr_uid); } if (pw == NULL && !usedfallback) /* No such user... */ iderror(lport, fport, s, errno); /* * If enabled, we check for a file named ".noident" in the user's * home directory. If found, we return HIDDEN-USER. */ if (nflag && !usedfallback) { if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1) iderror(lport, fport, s, errno); if (lstat(p, &sb) == 0) { free(p); iderror(lport, fport, s, -1); } free(p); } /* * Here, if enabled, we read a user's ".fakeid" file in their * home directory. It consists of a line containing the name * they want. */ if (fflag && !usedfallback) { FILE *fakeid = NULL; if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1) iderror(lport, fport, s, errno); /* * Here we set ourself to effectively be the user, so we don't * open any files we have no permission to open, especially * symbolic links to sensitive root-owned files or devices. */ seteuid(pw->pw_uid); setegid(pw->pw_gid); /* * If we were to lstat() here, it would do no good, since it * would introduce a race condition and could be defeated. * Therefore, we open the file we have permissions to open * and if it's not a regular file, we close it and end up * returning the user's real username. */ fakeid = fopen(p, "r"); free(p); if (fakeid != NULL && fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) { buf[sizeof(buf) - 1] = '\0'; if (fgets(buf, sizeof(buf), fakeid) == NULL) { cp = pw->pw_name; fclose(fakeid); goto printit; } fclose(fakeid); /* * Usually, the file will have the desired identity * in the form "identity\n", so we use strtok() to * end the string (which fgets() doesn't do.) */ buf[strcspn(buf, "\r\n")] = '\0'; cp = buf; /* Allow for beginning white space... */ while (isspace(*cp)) cp++; /* ...and ending white space. */ cp[strcspn(cp, " \t")] = '\0'; /* User names of >16 characters are invalid */ if (strlen(cp) > 16) cp[16] = '\0'; /* * If the name is a zero-length string or matches * the name of another user, it's invalid, so * we will return their real identity instead. */ if (!*cp || getpwnam(cp)) cp = getpwuid(uc.cr_uid)->pw_name; } else cp = pw->pw_name; } else if (!usedfallback) cp = pw->pw_name; else cp = fallback; printit: /* Finally, we make and send the reply. */ if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname, cp) == -1) { syslog(LOG_ERR, "asprintf: %m"); exit(EX_OSERR); } send(s, p, strlen(p), MSG_EOF); free(p); exit(0); } /* * RFC738 Time Server. * 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. */ unsigned long machtime() { struct timeval tv; if (gettimeofday(&tv, (struct timezone *)NULL) < 0) { if (debug) warnx("unable to get time of day"); return (0L); } #define OFFSET ((u_long)25567 * 24*60*60) return (htonl((long)(tv.tv_sec + OFFSET))); #undef OFFSET } /* ARGSUSED */ void machtime_dg(s, sep) int s; struct servtab *sep; { unsigned long result; struct sockaddr_storage ss; socklen_t size; size = sizeof(ss); if (recvfrom(s, (char *)&result, sizeof(result), 0, (struct sockaddr *)&ss, &size) < 0) return; if (check_loop((struct sockaddr *)&ss, sep)) return; result = machtime(); (void) sendto(s, (char *) &result, sizeof(result), 0, (struct sockaddr *)&ss, size); } /* ARGSUSED */ void machtime_stream(s, sep) int s; struct servtab *sep; { unsigned long result; result = machtime(); (void) send(s, (char *) &result, sizeof(result), MSG_EOF); } /* * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to * services based on the service name sent. * * Based on TCPMUX.C by Mark K. Lottor November 1988 * sri-nic::ps:tcpmux.c */ #define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ #define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) static int /* # of characters upto \r,\n or \0 */ getline(fd, buf, len) int fd; char *buf; int len; { int count = 0, n; struct sigaction sa; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_DFL; sigaction(SIGALRM, &sa, (struct sigaction *)0); do { alarm(10); n = read(fd, buf, len-count); alarm(0); if (n == 0) return (count); if (n < 0) return (-1); while (--n >= 0) { if (*buf == '\r' || *buf == '\n' || *buf == '\0') return (count); count++; buf++; } } while (count < len); return (count); } struct servtab * tcpmux(s) int s; { struct servtab *sep; char service[MAX_SERV_LEN+1]; int len; /* Get requested service name */ if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { strwrite(s, "-Error reading service name\r\n"); return (NULL); } service[len] = '\0'; if (debug) warnx("tcpmux: someone wants %s", service); /* * Help is a required command, and lists available services, * one per line. */ if (!strcasecmp(service, "help")) { for (sep = servtab; sep; sep = sep->se_next) { if (!ISMUX(sep)) continue; (void)write(s,sep->se_service,strlen(sep->se_service)); strwrite(s, "\r\n"); } return (NULL); } /* Try matching a service in inetd.conf with the request */ for (sep = servtab; sep; sep = sep->se_next) { if (!ISMUX(sep)) continue; if (!strcasecmp(service, sep->se_service)) { if (ISMUXPLUS(sep)) { strwrite(s, "+Go\r\n"); } return (sep); } } strwrite(s, "-Service not available\r\n"); return (NULL); } Index: stable/4/usr.sbin/inetd/inetd.8 =================================================================== --- stable/4/usr.sbin/inetd/inetd.8 (revision 67517) +++ stable/4/usr.sbin/inetd/inetd.8 (revision 67518) @@ -1,805 +1,808 @@ .\" Copyright (c) 1985, 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. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. 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. .\" .\" from: @(#)inetd.8 8.3 (Berkeley) 4/13/94 .\" $FreeBSD$ .\" .Dd February 7, 1996 .Dt INETD 8 .Os BSD 4.4 .Sh NAME .Nm inetd .Nd internet .Dq super-server .Sh SYNOPSIS .Nm inetd .Op Fl d .Op Fl l .Op Fl w .Op Fl W .Op Fl c Ar maximum .Op Fl C Ar rate .Op Fl a Ar address|hostname .Op Fl p Ar filename .Op Fl R Ar rate .Op Ar configuration file .Sh DESCRIPTION The .Nm program should be run at boot time by .Pa /etc/rc (see .Xr rc 8 ) . It then listens for connections on certain internet sockets. When a connection is found on one of its sockets, it decides what service the socket corresponds to, and invokes a program to service the request. The server program is invoked with the service socket as its standard input, output and error descriptors. After the program is finished, .Nm continues to listen on the socket (except in some cases which will be described below). Essentially, .Nm allows running one daemon to invoke several others, reducing load on the system. .Pp The following options are available: .Bl -tag -width indent .It Fl d Turn on debugging. .It Fl l Turn on logging of successful connections. .It Fl w Turn on TCP Wrapping for external services. See the .Sx "IMPLEMENTATION NOTES" section for more information on TCP Wrappers support. .It Fl W Turn on TCP Wrapping for internal services which are built in to .Nm inetd . .It Fl c Ar maximum -Specify the default maximum number of services that can be invoked. +Specify the default maximum number of +simultaneous invocations of each service; +the default is unlimited. May be overridden on a per-service basis with the "max-child" parameter. .It Fl C Ar rate Specify the default maximum number of times a service can be invoked from a single IP address in one minute; the default is unlimited. May be overridden on a per-service basis with the "max-connections-per-ip-per-minute" parameter. .It Fl R Ar rate Specify the maximum number of times a service can be invoked in one minute; the default is 256. +A rate of 0 allows an unlimited number of invocations. .It Fl a Specify a specific IP address to bind to. Alternatively, a hostname can be specified, in which case the IPv4 or IPv6 address which corresponds to that hostname is used. Usually a hostname is specified when .Nm is run inside a .Xr jail 8 , in which case the hostname corresponds to the .Xr jail 8 environment. .Pp When hostname specification is used and both IPv4 and IPv6 bindings are desired, one entry with the appropriate .Em protocol type for each binding is required for each service in .Pa /etc/inetd.conf . For example, a TCP-based service would need two entries, one using .Dq tcp4 for the .Em protocol and the other using .Dq tcp6 . See the explanation of the .Pa /etc/inetd.conf .Em protocol field below. .It Fl p Specify an alternate file in which to store the process ID. .El .Pp Upon execution, .Nm reads its configuration information from a configuration file which, by default, is .Pa /etc/inetd.conf . There must be an entry for each field of the configuration file, with entries for each field separated by a tab or a space. Comments are denoted by a .Dq # at the beginning of a line. There must be an entry for each field. The fields of the configuration file are as follows: .Pp .Bd -unfilled -offset indent -compact service name socket type protocol {wait|nowait}[/max-child[/max-connections-per-ip-per-minute]] user[:group][/login-class] server program server program arguments .Ed .Pp To specify an .No Tn "ONC RPC" Ns -based service, the entry would contain these fields: .Pp .Bd -unfilled -offset indent -compact service name/version socket type rpc/protocol user[:group][/login-class] server program server program arguments .Ed .Pp There are two types of services that .Nm can start: standard and TCPMUX. A standard service has a well-known port assigned to it; it may be a service that implements an official Internet standard or is a BSD-specific service. As described in .Tn RFC 1078 , TCPMUX services are nonstandard services that do not have a well-known port assigned to them. They are invoked from .Nm when a program connects to the .Dq tcpmux well-known port and specifies the service name. This feature is useful for adding locally-developed servers. TCPMUX requests are only accepted when the multiplexor service itself is enabled, above and beyond and specific TCPMUX-based servers; see the discussion of internal services below. .Pp The .Em service-name entry is the name of a valid service in the file .Pa /etc/services . For .Dq internal services (discussed below), the service name should be the official name of the service (that is, the first entry in .Pa /etc/services ) . When used to specify an .No Tn "ONC RPC" Ns -based service, this field is a valid RPC service name in the file .Pa /etc/rpc . The part on the right of the .Dq / is the RPC version number. This can simply be a single numeric argument or a range of versions. A range is bounded by the low version to the high version - .Dq rusers/1-3 . For TCPMUX services, the value of the .Em service-name field consists of the string .Dq tcpmux followed by a slash and the locally-chosen service name. The service names listed in .Pa /etc/services and the name .Dq help are reserved. Try to choose unique names for your TCPMUX services by prefixing them with your organization's name and suffixing them with a version number. .Pp The .Em socket-type should be one of .Dq stream , .Dq dgram , .Dq raw , .Dq rdm , or .Dq seqpacket , depending on whether the socket is a stream, datagram, raw, reliably delivered message, or sequenced packet socket. TCPMUX services must use .Dq stream . .Pp The .Em protocol must be a valid protocol as given in .Pa /etc/protocols . Examples might be .Dq tcp or .Dq udp , both of which imply IPv4 for backward compatibility. The names .Dq tcp4 and .Dq udp4 specify IPv4 only. The names .Dq tcp6 and .Dq udp6 specify IPv6 only. The names .Dq tcp46 and .Dq udp46 specify that the entry accepts both IPv6 and IPv6 connections via a wildcard .Dv AF_INET6 socket. If it is desired that the service is reachable via T/TCP, one should specify .Dq tcp/ttcp , which implies IPv4 for backward compatibility. The name .Dq tcp4/ttcp specifies IPv4 only, while .Dq tcp6/ttcp specifies IPv6 only. The name .Dq tcp46/ttcp specify that the entry accepts both IPv6 and IPv6 connections via a wildcard .Dv AF_INET6 socket. Rpc based services (for which only IPv4 is supported at this time) are specified with the .Dq rpc/tcp or .Dq rpc/udp service type. TCPMUX services must use .Dq tcp , .Dq tcp4 , .Dq tcp6 or .Dq tcp46 . .Pp The .Em wait/nowait entry specifies whether the server that is invoked by .Nm will take over the socket associated with the service access point, and thus whether .Nm should wait for the server to exit before listening for new service requests. Datagram servers must use .Dq wait , as they are always invoked with the original datagram socket bound to the specified service address. These servers must read at least one datagram from the socket before exiting. If a datagram server connects to its peer, freeing the socket so .Nm can receive further messages on the socket, it is said to be a .Dq multi-threaded server; it should read one datagram from the socket and create a new socket connected to the peer. It should fork, and the parent should then exit to allow .Nm to check for new service requests to spawn new servers. Datagram servers which process all incoming datagrams on a socket and eventually time out are said to be .Dq single-threaded . .Xr Comsat 8 , .Pq Xr biff 1 and .Xr talkd 8 are both examples of the latter type of datagram server. .Xr Tftpd 8 is an example of a multi-threaded datagram server. .Pp Servers using stream sockets generally are multi-threaded and use the .Dq nowait entry. Connection requests for these services are accepted by .Nm Ns , and the server is given only the newly-accepted socket connected to a client of the service. Most stream-based services operate in this manner. Stream-based servers that use .Dq wait are started with the listening service socket, and must accept at least one connection request before exiting. Such a server would normally accept and process incoming connection requests until a timeout. TCPMUX services must use .Dq nowait . .Pp The maximum number of outstanding child processes (or .Dq threads ) for a .Dq nowait service may be explicitly specified by appending a .Dq / followed by the number to the .Dq nowait keyword. Normally (or if a value of zero is specified) there is no maximum. Otherwise, once the maximum is reached, further connection attempts will be queued up until an existing child process exits. This also works in the case of .Dq wait mode, although a value other than one (the default) might not make sense in some cases. You can also specify the maximum number of connections per minute for a given IP address by appending a .Dq / followed by the number to the maximum number of outstanding child processes. Once the maximum is reached, further connections from this IP address will be dropped until the end of the minute. .Pp The .Em user entry should contain the user name of the user as whom the server should run. This allows for servers to be given less permission than root. Optional .Em group part separated by .Dq \&: allows to specify group name different than default group for this user. Optional .Em login-class part separated by .Dq / allows to specify login class different than default .Dq daemon login class. .Pp The .Em server-program entry should contain the pathname of the program which is to be executed by .Nm when a request is found on its socket. If .Nm provides this service internally, this entry should be .Dq internal . .Pp The .Em server program arguments should be just as arguments normally are, starting with argv[0], which is the name of the program. If the service is provided internally, the .Em service-name of the service (and any arguments to it) or the word .Dq internal should take the place of this entry. .Pp Currently, the only internal service to take arguments is .Dq auth . Without options, the service will always return .Dq ERROR\ : HIDDEN-USER . The available arguments to this service that alter its behavior are: .Bl -tag -width indent .It Fl d Ar fallback Provide a .Ar fallback username. If the real .Dq auth service is enabled (with the .Fl r option discussed below), return this username instead of an error when look ups fail for either socket credentials or the username. If the real .Dq auth service is disabled, return this username for every request. This is primarily useful when running this service on a NAT machine. .It Fl t Ar sec[.usec] Specify a timeout for the service. The default timeout is 10.0 seconds. .It Fl r Offer a real .Dq auth service, as per RFC 1413. All the remaining flags apply only in this case. .It Fl f If the file .Pa .fakeid exists in the home directory of the identified user, report the username found in that file instead of the real username. .It Fl g Instead of returning the user's name to the ident requester, report a username made up of random alphanumeric characters, e.g. .Dq c0c993 . The .Fl g flag overrides not only the user names, but also any .Pa .fakeid or .Pa .noident files. .It Fl n If the file .Pa .noident exists in the home directory of the identified user, return .Dq ERROR\ : HIDDEN-USER . instead. .It Fl o Ar osname Use .Ar osname instead of the name of the system as reported by .Xr uname 3 . .El .Pp The .Nm program also provides several other .Dq trivial services internally by use of routines within itself. These services are .Dq echo , .Dq discard , .Dq chargen (character generator), .Dq daytime (human readable time), and .Dq time (machine readable time, in the form of the number of seconds since midnight, January 1, 1900). All of these services are available in both TCP and UDP versions; the UDP versions will refuse service if the request specifies a reply port corresponding to any internal service. (This is done as a defense against looping attacks; the remote IP address is logged.) For details of these services, consult the appropriate .Tn RFC document. .Pp The TCPMUX-demultiplexing service is also implemented as an internal service. For any TCPMUX-based service to function, the following line must be included in .Pa inetd.conf : .Bd -literal -offset indent tcpmux stream tcp nowait root internal .Ed .Pp When given the .Fl l option .Nm will log an entry to syslog each time a connection is accepted, noting the service selected and the IP-number of the remote requestor if available. Unless otherwise specified in the configuration file, and in the absence of the .Fl W and .Fl w options, .Nm will log to the .Dq daemon facility. .Pp The .Nm program rereads its configuration file when it receives a hangup signal, .Dv SIGHUP . Services may be added, deleted or modified when the configuration file is reread. Except when started in debugging mode, .Nm records its process ID in the file .Pa /var/run/inetd.pid to assist in reconfiguration. .Sh IMPLEMENTATION NOTES When given the .Fl w option, .Nm will wrap all services specified as .Dq stream nowait or .Dq dgram except for .Dq internal services. If the .Fl W option is given, such .Dq internal services will be wrapped. If both options are given, wrapping for both internal and external services will be enabled. Either wrapping option will cause failed connections to be logged to the .Dq auth syslog facility. Adding the .Fl l flag to the wrapping options will include successful connections in the logging to the .Dq auth facility. .Pp Note that .Nm only wraps requests for a .Dq wait service while no servers are available to service requests. Once a connection to such a service has been allowed, inetd has no control over subsequent connections to the service until no more servers are left listening for connection requests. .Pp When wrapping is enabled, the .Pa tcpd daemon is not required, as that functionality is builtin. For more information on TCP Wrappers; see the relevant documentation ( .Xr hosts_access 5 ). When reading that document, keep in mind that .Dq internal services have no associated daemon name. Therefore, the service name as specified in .Pa inetd.conf should be used as the daemon name for .Dq internal services. .Ss TCPMUX .Tn RFC 1078 describes the TCPMUX protocol: ``A TCP client connects to a foreign host on TCP port 1. It sends the service name followed by a carriage-return line-feed . The service name is never case sensitive. The server replies with a single character indicating positive (+) or negative (\-) acknowledgment, immediately followed by an optional message of explanation, terminated with a . If the reply was positive, the selected protocol begins; otherwise the connection is closed.'' The program is passed the TCP connection as file descriptors 0 and 1. .Pp If the TCPMUX service name begins with a .Dq + , .Nm returns the positive reply for the program. This allows you to invoke programs that use stdin/stdout without putting any special server code in them. .Pp The special service name .Dq help causes .Nm to list TCPMUX services in .Pa inetd.conf . .Ss IPsec The implementation includes a tiny hack to support IPsec policy settings for each socket. A special form of comment line, starting with .Dq Li #@ , is interpreted as a policy specifier. Everything after the .Dq Li #@ will be used as an IPsec policy string, as described in .Xr ipsec_set_policy 3 . Each policy specifier is applied to all the following lines in .Pa inetd.conf until the next policy specifier. -An empty policy specifer resets the IPsec policy. +An empty policy specifier resets the IPsec policy. .Pp If an invalid IPsec policy specifier appears in .Pa inetd.conf , .Nm will provide an error message via the .Xr syslog 3 interface and abort execution. .Sh "FILES" .Bl -tag -width /var/run/inetd.pid -compact .It Pa /etc/inetd.conf configuration file .It Pa /etc/rpc translation of service names to RPC program numbers .It Pa /etc/services translation of service names to port numbers .It Pa /var/run/inetd.pid the pid of the currently running .Nm .El .Sh "EXAMPLES" .Pp Here are several example service entries for the various types of services: .Bd -literal ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l ntalk dgram udp wait root /usr/libexec/ntalkd ntalkd telnet stream tcp6 nowait root /usr/libexec/telnetd telnetd shell stream tcp46 nowait root /usr/libexec/rshd rshd tcpmux/+date stream tcp nowait guest /bin/date date tcpmux/phonebook stream tcp nowait guest /usr/local/bin/phonebook phonebook rstatd/1-3 dgram rpc/udp wait root /usr/libexec/rpc.rstatd rpc.rstatd #@ ipsec ah/require chargen stream tcp nowait root internal #@ .Ed .Sh "ERROR MESSAGES" The .Nm server logs error messages using .Xr syslog 3 . Important error messages and their explanations are: .Pp .Bl -ohang -compact .It Xo .Ar service Ns / Ns Ar protocol .No " server failing (looping), service terminated." .Xc The number of requests for the specified service in the past minute exceeded the limit. The limit exists to prevent a broken program or a malicious user from swamping the system. This message may occur for several reasons: .Bl -enum -offset indent .It There are many hosts requesting the service within a short time period. .It A broken client program is requesting the service too frequently. .It A malicious user is running a program to invoke the service in a denial-of-service attack. .It The invoked service program has an error that causes clients to retry quickly. .El .Pp Use the .Fl R Ar rate option, as described above, to change the rate limit. Once the limit is reached, the service will be reenabled automatically in 10 minutes. .Pp .It Xo .Ar service Ns / Ns Ar protocol : .No \&No such user .Ar user , .No service ignored .Xc .It Xo .Ar service Ns / Ns Ar protocol : .No getpwnam : .Ar user : .No \&No such user .Xc No entry for .Ar user exists in the .Xr passwd 5 database. The first message occurs when .Nm (re)reads the configuration file. The second message occurs when the service is invoked. .Pp .It Xo .Ar service : .No can't set uid .Ar uid .Xc .It Xo .Ar service : .No can't set gid .Ar gid .Xc The user or group ID for the entry's .Ar user field is invalid. .Pp .It "setsockopt(SO_PRIVSTATE): Operation not supported" The .Nm program attempted to renounce the privileged state associated with a socket but was unable to. .El .Sh SEE ALSO .Xr hosts_access 5 , .Xr hosts_options 5 , .Xr ipsec_set_policy 3 , .Xr login.conf 5 , .Xr passwd 5 , .Xr rpc 5 , .Xr services 5 , .Xr comsat 8 , .Xr fingerd 8 , .Xr ftpd 8 , .Xr portmap 8 , .Xr rexecd 8 , .Xr rlogind 8 , .Xr rshd 8 , .Xr telnetd 8 , .Xr tftpd 8 .Rs .%A Michael C. St. Johns .%T Identification Protocol .%O RFC1413 .Re .Sh HISTORY The .Nm command appeared in .Bx 4.3 . TCPMUX is based on code and documentation by Mark Lottor. Support for .Tn "ONC RPC" based services is modeled after that provided by .Tn SunOS 4.1. The IPsec hack was contributed by the KAME project in 1999. The .Fx TCP Wrappers support first appeared in .Fx 3.2 . Index: stable/4/usr.sbin/inetd/inetd.c =================================================================== --- stable/4/usr.sbin/inetd/inetd.c (revision 67517) +++ stable/4/usr.sbin/inetd/inetd.c (revision 67518) @@ -1,2141 +1,2149 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1991, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)from: inetd.c 8.4 (Berkeley) 4/13/94"; #endif static const char rcsid[] = "$FreeBSD$"; #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 + * order shown below. Continuation lines for an entry must begin 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 stream/dgram/raw/rdm/seqpacket * protocol must be in /etc/protocols * wait/nowait single-threaded/multi-threaded * user user to run daemon as * server program full path name * server program arguments maximum of MAXARGS (20) * * 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. * * 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 single-threaded/multi-threaded * user user to run daemon as * server program full path name * server program arguments maximum of MAXARGS * * 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 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inetd.h" #include "pathnames.h" #ifdef IPSEC #include #ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */ #undef IPSEC #endif #endif /* wrapper for KAME-special getnameinfo() */ #ifndef NI_WITHSCOPEID #define NI_WITHSCOPEID 0 #endif #ifndef LIBWRAP_ALLOW_FACILITY # define LIBWRAP_ALLOW_FACILITY LOG_AUTH #endif #ifndef LIBWRAP_ALLOW_SEVERITY # define LIBWRAP_ALLOW_SEVERITY LOG_INFO #endif #ifndef LIBWRAP_DENY_FACILITY # define LIBWRAP_DENY_FACILITY LOG_AUTH #endif #ifndef LIBWRAP_DENY_SEVERITY # define LIBWRAP_DENY_SEVERITY LOG_WARNING #endif #define ISWRAP(sep) \ ( ((wrap_ex && !(sep)->se_bi) || (wrap_bi && (sep)->se_bi)) \ && ( ((sep)->se_accept && (sep)->se_socktype == SOCK_STREAM) \ || (sep)->se_socktype == SOCK_DGRAM)) #ifdef LOGIN_CAP #include /* see init.c */ #define RESOURCE_RC "daemon" #endif #ifndef MAXCHILD #define MAXCHILD -1 /* maximum number of this service < 0 = no limit */ #endif #ifndef MAXCPM #define MAXCPM -1 /* rate limit invocations from a single remote address, < 0 = no limit */ #endif +#ifndef TOOMANY #define TOOMANY 256 /* don't start more than TOOMANY */ +#endif #define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ #define RETRYTIME (60*10) /* retry after bind or server fail */ #define MAX_MAXCHLD 32767 /* max allowable max children */ #define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) int allow_severity; int deny_severity; int wrap_ex = 0; int wrap_bi = 0; int debug = 0; int log = 0; int maxsock; /* highest-numbered descriptor */ fd_set allsock; int options; int timingout; int toomany = TOOMANY; int maxchild = MAXCHILD; int maxcpm = MAXCPM; struct servent *sp; struct rpcent *rpc; char *hostname = NULL; struct sockaddr_in *bind_sa4; int no_v4bind = 1; #ifdef INET6 struct sockaddr_in6 *bind_sa6; int no_v6bind = 1; #endif int signalpipe[2]; #ifdef SANITY_CHECK int nsock; #endif struct servtab *servtab; extern struct biltin biltins[]; #define NUMINT (sizeof(intab) / sizeof(struct inent)) char *CONFIG = _PATH_INETDCONF; char *pid_file = _PATH_INETDPID; #ifdef OLD_SETPROCTITLE char **Argv; char *LastArg; #endif int getvalue(arg, value, whine) char *arg, *whine; int *value; { int tmp; char *p; tmp = strtol(arg, &p, 0); - if (tmp < 1 || *p) { + if (tmp < 0 || *p) { syslog(LOG_ERR, whine, arg); return 1; /* failure */ } *value = tmp; return 0; /* success */ } int main(argc, argv, envp) int argc; char *argv[], *envp[]; { struct servtab *sep; struct passwd *pwd; struct group *grp; struct sigaction sa, saalrm, sachld, sahup, sapipe; int tmpint, ch, dofork; pid_t pid; char buf[50]; #ifdef LOGIN_CAP login_cap_t *lc = NULL; #endif struct request_info req; int denied; char *service = NULL; char *pnm; union { struct sockaddr peer_un; struct sockaddr_in peer_un4; struct sockaddr_in6 peer_un6; struct sockaddr_storage peer_max; } p_un; #define peer p_un.peer_un #define peer4 p_un.peer_un4 #define peer6 p_un.peer_un6 #define peermax p_un.peer_max int i; struct addrinfo hints, *res; char *servname; int error; #ifdef OLD_SETPROCTITLE Argv = argv; if (envp == 0 || *envp == 0) envp = argv; while (*envp) envp++; LastArg = envp[-1] + strlen(envp[-1]); #endif openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:")) != -1) switch(ch) { case 'd': debug = 1; options |= SO_DEBUG; break; case 'l': log = 1; break; case 'R': getvalue(optarg, &toomany, "-R %s: bad value for service invocation rate"); break; case 'c': getvalue(optarg, &maxchild, "-c %s: bad value for maximum children"); break; case 'C': getvalue(optarg, &maxcpm, "-C %s: bad value for maximum children/minute"); break; case 'a': hostname = optarg; break; case 'p': pid_file = optarg; break; case 'w': wrap_ex++; break; case 'W': wrap_bi++; break; case '?': default: syslog(LOG_ERR, "usage: inetd [-dlwW] [-a address] [-R rate]" " [-c maximum] [-C rate]" " [-p pidfile] [conf-file]"); exit(EX_USAGE); } /* * Initialize Bind Addrs. * When hostname is NULL, wild card bind addrs are obtained from * getaddrinfo(). But getaddrinfo() requires at least one of * hostname or servname is non NULL. * So when hostname is NULL, set dummy value to servname. */ servname = (hostname == NULL) ? "discard" /* dummy */ : NULL; bzero(&hints, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_UNSPEC; error = getaddrinfo(hostname, servname, &hints, &res); if (error != 0) { syslog(LOG_ERR, "-a %s: %s", hostname, gai_strerror(error)); if (error == EAI_SYSTEM) syslog(LOG_ERR, "%s", strerror(errno)); exit(EX_USAGE); } do { if (res->ai_addr == NULL) { syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname); exit(EX_USAGE); } switch (res->ai_addr->sa_family) { case AF_INET: if (no_v4bind == 0) continue; bind_sa4 = (struct sockaddr_in *)res->ai_addr; /* init port num in case servname is dummy */ bind_sa4->sin_port = 0; no_v4bind = 0; continue; #ifdef INET6 case AF_INET6: if (no_v6bind == 0) continue; bind_sa6 = (struct sockaddr_in6 *)res->ai_addr; /* init port num in case servname is dummy */ bind_sa6->sin6_port = 0; no_v6bind = 0; continue; #endif } if (no_v4bind == 0 #ifdef INET6 && no_v6bind == 0 #endif ) break; } while ((res = res->ai_next) != NULL); if (no_v4bind != 0 #ifdef INET6 && no_v6bind != 0 #endif ) { syslog(LOG_ERR, "-a %s: unknown address family", hostname); exit(EX_USAGE); } argc -= optind; argv += optind; if (argc > 0) CONFIG = argv[0]; if (debug == 0) { FILE *fp; if (daemon(0, 0) < 0) { syslog(LOG_WARNING, "daemon(0,0) failed: %m"); } /* * In case somebody has started inetd manually, we need to * clear the logname, so that old servers run as root do not * get the user's logname.. */ if (setlogin("") < 0) { syslog(LOG_WARNING, "cannot clear logname: %m"); /* no big deal if it fails.. */ } pid = getpid(); fp = fopen(pid_file, "w"); if (fp) { fprintf(fp, "%ld\n", (long)pid); fclose(fp); } else { syslog(LOG_WARNING, "%s: %m", pid_file); } } sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGALRM); sigaddset(&sa.sa_mask, SIGCHLD); sigaddset(&sa.sa_mask, SIGHUP); sa.sa_handler = flag_retry; sigaction(SIGALRM, &sa, &saalrm); config(); sa.sa_handler = flag_config; sigaction(SIGHUP, &sa, &sahup); sa.sa_handler = flag_reapchild; sigaction(SIGCHLD, &sa, &sachld); sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, &sapipe); { /* space for daemons to overwrite environment for ps */ #define DUMMYSIZE 100 char dummy[DUMMYSIZE]; (void)memset(dummy, 'x', DUMMYSIZE - 1); dummy[DUMMYSIZE - 1] = '\0'; (void)setenv("inetd_dummy", dummy, 1); } if (pipe(signalpipe) != 0) { syslog(LOG_ERR, "pipe: %m"); exit(EX_OSERR); } FD_SET(signalpipe[0], &allsock); #ifdef SANITY_CHECK nsock++; #endif if (signalpipe[0] > maxsock) maxsock = signalpipe[0]; if (signalpipe[1] > maxsock) maxsock = signalpipe[1]; for (;;) { int n, ctrl; fd_set readable; #ifdef SANITY_CHECK if (nsock == 0) { syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); exit(EX_SOFTWARE); } #endif readable = allsock; if ((n = select(maxsock + 1, &readable, (fd_set *)0, (fd_set *)0, (struct timeval *)0)) <= 0) { if (n < 0 && errno != EINTR) { syslog(LOG_WARNING, "select: %m"); sleep(1); } continue; } /* handle any queued signal flags */ if (FD_ISSET(signalpipe[0], &readable)) { int n; if (ioctl(signalpipe[0], FIONREAD, &n) != 0) { syslog(LOG_ERR, "ioctl: %m"); exit(EX_OSERR); } while (--n >= 0) { char c; if (read(signalpipe[0], &c, 1) != 1) { syslog(LOG_ERR, "read: %m"); exit(EX_OSERR); } if (debug) warnx("handling signal flag %c", c); switch(c) { case 'A': /* sigalrm */ retry(); break; case 'C': /* sigchld */ reapchild(); break; case 'H': /* sighup */ config(); break; } } } for (sep = servtab; n && sep; sep = sep->se_next) if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { n--; if (debug) warnx("someone wants %s", sep->se_service); if (sep->se_accept && sep->se_socktype == SOCK_STREAM) { i = 1; if (ioctl(sep->se_fd, FIONBIO, &i) < 0) syslog(LOG_ERR, "ioctl (FIONBIO, 1): %m"); ctrl = accept(sep->se_fd, (struct sockaddr *)0, (int *)0); if (debug) warnx("accept, ctrl %d", ctrl); if (ctrl < 0) { if (errno != EINTR) syslog(LOG_WARNING, "accept (for %s): %m", sep->se_service); if (sep->se_accept && sep->se_socktype == SOCK_STREAM) close(ctrl); continue; } i = 0; if (ioctl(sep->se_fd, FIONBIO, &i) < 0) syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m"); if (ioctl(ctrl, FIONBIO, &i) < 0) syslog(LOG_ERR, "ioctl2(FIONBIO, 0): %m"); if (cpmip(sep, ctrl) < 0) { close(ctrl); continue; } } else ctrl = sep->se_fd; if (log && !ISWRAP(sep)) { char pname[INET6_ADDRSTRLEN]; pnm = "unknown"; i = sizeof peermax; if (getpeername(ctrl, (struct sockaddr *) &peermax, &i)) { i = sizeof peermax; if (recvfrom(ctrl, buf, sizeof(buf), MSG_PEEK, (struct sockaddr *)&peermax, &i) >= 0) { getnameinfo((struct sockaddr *)&peermax, peer.sa_len, pname, sizeof(pname), NULL, 0, NI_NUMERICHOST| NI_WITHSCOPEID); pnm = pname; } } else { getnameinfo((struct sockaddr *)&peermax, peer.sa_len, pname, sizeof(pname), NULL, 0, NI_NUMERICHOST| NI_WITHSCOPEID); pnm = pname; } syslog(LOG_INFO,"%s from %s", sep->se_service, pnm); } (void) sigblock(SIGBLOCK); pid = 0; /* * Fork for all external services, builtins which need to * fork and anything we're wrapping (as wrapping might * block or use hosts_options(5) twist). */ dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep); if (dofork) { if (sep->se_count++ == 0) (void)gettimeofday(&sep->se_time, (struct timezone *)NULL); - else if (sep->se_count >= toomany) { + else if (toomany > 0 && sep->se_count >= toomany) { struct timeval now; (void)gettimeofday(&now, (struct timezone *)NULL); if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) { sep->se_time = now; sep->se_count = 1; } else { syslog(LOG_ERR, "%s/%s server failing (looping), service terminated", sep->se_service, sep->se_proto); + if (sep->se_accept && + sep->se_socktype == SOCK_STREAM) + close(ctrl); close_sep(sep); sigsetmask(0L); if (!timingout) { timingout = 1; alarm(RETRYTIME); } continue; } } pid = fork(); } if (pid < 0) { syslog(LOG_ERR, "fork: %m"); if (sep->se_accept && sep->se_socktype == SOCK_STREAM) close(ctrl); sigsetmask(0L); sleep(1); continue; } if (pid) addchild(sep, pid); sigsetmask(0L); if (pid == 0) { if (dofork) { if (debug) warnx("+ closing from %d", maxsock); for (tmpint = maxsock; tmpint > 2; tmpint--) if (tmpint != ctrl) (void) close(tmpint); sigaction(SIGALRM, &saalrm, (struct sigaction *)0); sigaction(SIGCHLD, &sachld, (struct sigaction *)0); sigaction(SIGHUP, &sahup, (struct sigaction *)0); /* SIGPIPE reset before exec */ } /* * Call tcpmux to find the real service to exec. */ if (sep->se_bi && sep->se_bi->bi_fn == (void (*)()) tcpmux) { sep = tcpmux(ctrl); if (sep == NULL) { close(ctrl); _exit(0); } } if (ISWRAP(sep)) { inetd_setproctitle("wrapping", ctrl); service = sep->se_server_name ? sep->se_server_name : sep->se_service; request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, NULL); fromhost(&req); deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY; allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY; denied = !hosts_access(&req); if (denied) { syslog(deny_severity, "refused connection from %.500s, service %s (%s)", eval_client(&req), service, sep->se_proto); if (sep->se_socktype != SOCK_STREAM) recv(ctrl, buf, sizeof (buf), 0); - if (dofork) + if (dofork) { + sleep(1); _exit(0); + } } if (log) { syslog(allow_severity, "connection from %.500s, service %s (%s)", eval_client(&req), service, sep->se_proto); } } if (sep->se_bi) { (*sep->se_bi->bi_fn)(ctrl, sep); } else { if (debug) warnx("%d execl %s", getpid(), sep->se_server); dup2(ctrl, 0); close(ctrl); dup2(0, 1); dup2(0, 2); if ((pwd = getpwnam(sep->se_user)) == NULL) { syslog(LOG_ERR, "%s/%s: %s: no such user", sep->se_service, sep->se_proto, sep->se_user); if (sep->se_socktype != SOCK_STREAM) recv(0, buf, sizeof (buf), 0); _exit(EX_NOUSER); } grp = NULL; if ( sep->se_group != NULL && (grp = getgrnam(sep->se_group)) == NULL ) { syslog(LOG_ERR, "%s/%s: %s: no such group", sep->se_service, sep->se_proto, sep->se_group); if (sep->se_socktype != SOCK_STREAM) recv(0, buf, sizeof (buf), 0); _exit(EX_NOUSER); } if (grp != NULL) pwd->pw_gid = grp->gr_gid; #ifdef LOGIN_CAP if ((lc = login_getclass(sep->se_class)) == NULL) { /* error syslogged by getclass */ syslog(LOG_ERR, "%s/%s: %s: login class error", sep->se_service, sep->se_proto, sep->se_class); if (sep->se_socktype != SOCK_STREAM) recv(0, buf, sizeof (buf), 0); _exit(EX_NOUSER); } #endif if (setsid() < 0) { syslog(LOG_ERR, "%s: can't setsid(): %m", sep->se_service); /* _exit(EX_OSERR); not fatal yet */ } #ifdef LOGIN_CAP if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) { syslog(LOG_ERR, "%s: can't setusercontext(..%s..): %m", sep->se_service, sep->se_user); _exit(EX_OSERR); } #else if (pwd->pw_uid) { if (setlogin(sep->se_user) < 0) { syslog(LOG_ERR, "%s: can't setlogin(%s): %m", sep->se_service, sep->se_user); /* _exit(EX_OSERR); not yet */ } if (setgid(pwd->pw_gid) < 0) { syslog(LOG_ERR, "%s: can't set gid %d: %m", sep->se_service, pwd->pw_gid); _exit(EX_OSERR); } (void) initgroups(pwd->pw_name, pwd->pw_gid); if (setuid(pwd->pw_uid) < 0) { syslog(LOG_ERR, "%s: can't set uid %d: %m", sep->se_service, pwd->pw_uid); _exit(EX_OSERR); } } #endif sigaction(SIGPIPE, &sapipe, (struct sigaction *)0); execv(sep->se_server, sep->se_argv); syslog(LOG_ERR, "cannot execute %s: %m", sep->se_server); if (sep->se_socktype != SOCK_STREAM) recv(0, buf, sizeof (buf), 0); } if (dofork) _exit(0); } if (sep->se_accept && sep->se_socktype == SOCK_STREAM) close(ctrl); } } } /* * Add a signal flag to the signal flag queue for later handling */ void flag_signal(c) char c; { if (write(signalpipe[1], &c, 1) != 1) { syslog(LOG_ERR, "write: %m"); _exit(EX_OSERR); } } /* * Record a new child pid for this service. If we've reached the * limit on children, then stop accepting incoming requests. */ void addchild(struct servtab *sep, pid_t pid) { + if (sep->se_maxchild <= 0) + return; #ifdef SANITY_CHECK if (sep->se_numchild >= sep->se_maxchild) { syslog(LOG_ERR, "%s: %d >= %d", __FUNCTION__, sep->se_numchild, sep->se_maxchild); exit(EX_SOFTWARE); } #endif - if (sep->se_maxchild == 0) - return; sep->se_pids[sep->se_numchild++] = pid; if (sep->se_numchild == sep->se_maxchild) disable(sep); } /* * Some child process has exited. See if it's on somebody's list. */ void flag_reapchild(signo) int signo; { flag_signal('C'); } void reapchild() { int k, status; pid_t pid; struct servtab *sep; for (;;) { pid = wait3(&status, WNOHANG, (struct rusage *)0); if (pid <= 0) break; if (debug) warnx("%d reaped, status %#x", pid, status); for (sep = servtab; sep; sep = sep->se_next) { for (k = 0; k < sep->se_numchild; k++) if (sep->se_pids[k] == pid) break; if (k == sep->se_numchild) continue; if (sep->se_numchild == sep->se_maxchild) enable(sep); sep->se_pids[k] = sep->se_pids[--sep->se_numchild]; if (status) syslog(LOG_WARNING, "%s[%d]: exit status 0x%x", sep->se_server, pid, status); break; } } } void flag_config(signo) int signo; { flag_signal('H'); } void config() { struct servtab *sep, *new, **sepp; long omask; if (!setconfig()) { syslog(LOG_ERR, "%s: %m", CONFIG); return; } for (sep = servtab; sep; sep = sep->se_next) sep->se_checked = 0; while ((new = getconfigent())) { if (getpwnam(new->se_user) == NULL) { syslog(LOG_ERR, "%s/%s: no such user '%s', service ignored", new->se_service, new->se_proto, new->se_user); continue; } if (new->se_group && getgrnam(new->se_group) == NULL) { syslog(LOG_ERR, "%s/%s: no such group '%s', service ignored", new->se_service, new->se_proto, new->se_group); continue; } #ifdef LOGIN_CAP if (login_getclass(new->se_class) == NULL) { /* error syslogged by getclass */ syslog(LOG_ERR, "%s/%s: %s: login class error, service ignored", new->se_service, new->se_proto, new->se_class); continue; } #endif for (sep = servtab; sep; sep = sep->se_next) if (strcmp(sep->se_service, new->se_service) == 0 && strcmp(sep->se_proto, new->se_proto) == 0 && sep->se_family == new->se_family) break; if (sep != 0) { int i; #define SWAP(a, b) { typeof(a) c = a; a = b; b = c; } omask = sigblock(SIGBLOCK); if (sep->se_nomapped != new->se_nomapped) { sep->se_nomapped = new->se_nomapped; sep->se_reset = 1; } /* copy over outstanding child pids */ - if (sep->se_maxchild && new->se_maxchild) { + if (sep->se_maxchild > 0 && new->se_maxchild > 0) { new->se_numchild = sep->se_numchild; if (new->se_numchild > new->se_maxchild) new->se_numchild = new->se_maxchild; memcpy(new->se_pids, sep->se_pids, new->se_numchild * sizeof(*new->se_pids)); } SWAP(sep->se_pids, new->se_pids); sep->se_maxchild = new->se_maxchild; sep->se_numchild = new->se_numchild; sep->se_maxcpm = new->se_maxcpm; + sep->se_bi = new->se_bi; /* might need to turn on or off service now */ if (sep->se_fd >= 0) { - if (sep->se_maxchild + if (sep->se_maxchild > 0 && sep->se_numchild == sep->se_maxchild) { if (FD_ISSET(sep->se_fd, &allsock)) disable(sep); } else { if (!FD_ISSET(sep->se_fd, &allsock)) enable(sep); } } sep->se_accept = new->se_accept; SWAP(sep->se_user, new->se_user); SWAP(sep->se_group, new->se_group); #ifdef LOGIN_CAP SWAP(sep->se_class, new->se_class); #endif SWAP(sep->se_server, new->se_server); SWAP(sep->se_server_name, new->se_server_name); for (i = 0; i < MAXARGV; i++) SWAP(sep->se_argv[i], new->se_argv[i]); #ifdef IPSEC SWAP(sep->se_policy, new->se_policy); ipsecsetup(sep); #endif sigsetmask(omask); freeconfig(new); if (debug) print_service("REDO", sep); } else { sep = enter(new); if (debug) print_service("ADD ", sep); } sep->se_checked = 1; if (ISMUX(sep)) { sep->se_fd = -1; continue; } switch (sep->se_family) { case AF_INET: if (no_v4bind != 0) { sep->se_fd = -1; continue; } break; #ifdef INET6 case AF_INET6: if (no_v6bind != 0) { sep->se_fd = -1; continue; } break; #endif } if (!sep->se_rpc) { sp = getservbyname(sep->se_service, sep->se_proto); if (sp == 0) { syslog(LOG_ERR, "%s/%s: unknown service", sep->se_service, sep->se_proto); sep->se_checked = 0; continue; } switch (sep->se_family) { case AF_INET: if (sep->se_ctladdrinitok == 0) { memcpy(&sep->se_ctrladdr4, bind_sa4, sizeof(sep->se_ctrladdr4)); sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr4); } if (sp->s_port != sep->se_ctrladdr4.sin_port) { sep->se_ctrladdr4.sin_port = sp->s_port; sep->se_reset = 1; } break; #ifdef INET6 case AF_INET6: if (sep->se_ctladdrinitok == 0) { memcpy(&sep->se_ctrladdr6, bind_sa6, sizeof(sep->se_ctrladdr6)); sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr6); } if (sp->s_port != sep->se_ctrladdr6.sin6_port) { sep->se_ctrladdr6.sin6_port = sp->s_port; sep->se_reset = 1; } break; #endif } if (sep->se_reset != 0 && sep->se_fd >= 0) close_sep(sep); } else { rpc = getrpcbyname(sep->se_service); if (rpc == 0) { syslog(LOG_ERR, "%s/%s unknown RPC service", sep->se_service, sep->se_proto); if (sep->se_fd != -1) (void) close(sep->se_fd); sep->se_fd = -1; continue; } if (rpc->r_number != sep->se_rpc_prog) { if (sep->se_rpc_prog) unregisterrpc(sep); sep->se_rpc_prog = rpc->r_number; if (sep->se_fd != -1) (void) close(sep->se_fd); sep->se_fd = -1; } } if (sep->se_fd == -1) setup(sep); } endconfig(); /* * Purge anything not looked at above. */ omask = sigblock(SIGBLOCK); sepp = &servtab; while ((sep = *sepp)) { if (sep->se_checked) { sepp = &sep->se_next; continue; } *sepp = sep->se_next; if (sep->se_fd >= 0) close_sep(sep); if (debug) print_service("FREE", sep); if (sep->se_rpc && sep->se_rpc_prog > 0) unregisterrpc(sep); freeconfig(sep); free((char *)sep); } (void) sigsetmask(omask); } void unregisterrpc(sep) struct servtab *sep; { int i; struct servtab *sepp; long omask; omask = sigblock(SIGBLOCK); for (sepp = servtab; sepp; sepp = sepp->se_next) { if (sepp == sep) continue; if (sep->se_checked == 0 || !sepp->se_rpc || sep->se_rpc_prog != sepp->se_rpc_prog) continue; return; } if (debug) print_service("UNREG", sep); for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) pmap_unset(sep->se_rpc_prog, i); if (sep->se_fd != -1) (void) close(sep->se_fd); sep->se_fd = -1; (void) sigsetmask(omask); } void flag_retry(signo) int signo; { flag_signal('A'); } void retry() { struct servtab *sep; timingout = 0; for (sep = servtab; sep; sep = sep->se_next) if (sep->se_fd == -1 && !ISMUX(sep)) setup(sep); } void setup(sep) struct servtab *sep; { int on = 1; if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { if (debug) warn("socket failed on %s/%s", sep->se_service, sep->se_proto); syslog(LOG_ERR, "%s/%s: socket: %m", sep->se_service, sep->se_proto); return; } #define turnon(fd, opt) \ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && turnon(sep->se_fd, SO_DEBUG) < 0) syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); if (turnon(sep->se_fd, SO_REUSEADDR) < 0) syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); #ifdef SO_PRIVSTATE if (turnon(sep->se_fd, SO_PRIVSTATE) < 0) syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m"); #endif /* tftpd opens a new connection then needs more infos */ if ((sep->se_family == AF_INET6) && (strcmp(sep->se_proto, "udp") == 0) && (sep->se_accept == 0) && (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on, sizeof (on)) < 0)) syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m"); #ifdef IPV6_BINDV6ONLY if (sep->se_family == AF_INET6) { int flag = sep->se_nomapped ? 1 : 0; if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&flag, sizeof (flag)) < 0) syslog(LOG_ERR, "setsockopt (IPV6_BINDV6ONLY): %m"); } #endif /* IPV6_BINDV6ONLY */ #undef turnon if (sep->se_type == TTCP_TYPE) if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof (on)) < 0) syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m"); #ifdef IPV6_FAITH if (sep->se_type == FAITH_TYPE) { if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH, &on, sizeof(on)) < 0) { syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m"); } } #endif #ifdef IPSEC ipsecsetup(sep); #endif if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) { if (debug) warn("bind failed on %s/%s", sep->se_service, sep->se_proto); syslog(LOG_ERR, "%s/%s: bind: %m", sep->se_service, sep->se_proto); (void) close(sep->se_fd); sep->se_fd = -1; if (!timingout) { timingout = 1; alarm(RETRYTIME); } return; } if (sep->se_rpc) { int i, len = sep->se_ctrladdr_size; if (sep->se_family != AF_INET) { syslog(LOG_ERR, "%s/%s: unsupported address family for rpc", sep->se_service, sep->se_proto); (void) close(sep->se_fd); sep->se_fd = -1; return; } if (getsockname(sep->se_fd, (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){ syslog(LOG_ERR, "%s/%s: getsockname: %m", sep->se_service, sep->se_proto); (void) close(sep->se_fd); sep->se_fd = -1; return; } if (debug) print_service("REG ", sep); for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { pmap_unset(sep->se_rpc_prog, i); pmap_set(sep->se_rpc_prog, i, (sep->se_socktype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP, ntohs(sep->se_ctrladdr4.sin_port)); } } if (sep->se_socktype == SOCK_STREAM) listen(sep->se_fd, 64); enable(sep); if (debug) { warnx("registered %s on %d", sep->se_server, sep->se_fd); } } #ifdef IPSEC void ipsecsetup(sep) struct servtab *sep; { char *buf; char *policy_in = NULL; char *policy_out = NULL; int level; int opt; switch (sep->se_family) { case AF_INET: level = IPPROTO_IP; opt = IP_IPSEC_POLICY; break; #ifdef INET6 case AF_INET6: level = IPPROTO_IPV6; opt = IPV6_IPSEC_POLICY; break; #endif default: return; } if (!sep->se_policy || sep->se_policy[0] == '\0') { policy_in = "in entrust"; policy_out = "out entrust"; } else { if (!strncmp("in", sep->se_policy, 2)) policy_in = sep->se_policy; else if (!strncmp("out", sep->se_policy, 3)) policy_out = sep->se_policy; else { syslog(LOG_ERR, "invalid security policy \"%s\"", sep->se_policy); return; } } if (policy_in != NULL) { buf = ipsec_set_policy(policy_in, strlen(policy_in)); if (buf != NULL) { if (setsockopt(sep->se_fd, level, opt, buf, ipsec_get_policylen(buf)) < 0 && debug != 0) warnx("%s/%s: ipsec initialization failed; %s", sep->se_service, sep->se_proto, policy_in); free(buf); } else syslog(LOG_ERR, "invalid security policy \"%s\"", policy_in); } if (policy_out != NULL) { buf = ipsec_set_policy(policy_out, strlen(policy_out)); if (buf != NULL) { if (setsockopt(sep->se_fd, level, opt, buf, ipsec_get_policylen(buf)) < 0 && debug != 0) warnx("%s/%s: ipsec initialization failed; %s", sep->se_service, sep->se_proto, policy_out); free(buf); } else syslog(LOG_ERR, "invalid security policy \"%s\"", policy_out); } } #endif /* * Finish with a service and its socket. */ void close_sep(sep) struct servtab *sep; { if (sep->se_fd >= 0) { if (FD_ISSET(sep->se_fd, &allsock)) disable(sep); (void) close(sep->se_fd); sep->se_fd = -1; } sep->se_count = 0; sep->se_numchild = 0; /* forget about any existing children */ } int matchservent(name1, name2, proto) char *name1, *name2, *proto; { char **alias; struct servent *se; if (strcmp(name1, name2) == 0) return(1); if ((se = getservbyname(name1, proto)) != NULL) { if (strcmp(name2, se->s_name) == 0) return(1); for (alias = se->s_aliases; *alias; alias++) if (strcmp(name2, *alias) == 0) return(1); } return(0); } struct servtab * enter(cp) struct servtab *cp; { struct servtab *sep; long omask; sep = (struct servtab *)malloc(sizeof (*sep)); if (sep == (struct servtab *)0) { syslog(LOG_ERR, "malloc: %m"); exit(EX_OSERR); } *sep = *cp; sep->se_fd = -1; omask = sigblock(SIGBLOCK); sep->se_next = servtab; servtab = sep; sigsetmask(omask); return (sep); } void enable(struct servtab *sep) { if (debug) warnx( "enabling %s, fd %d", sep->se_service, sep->se_fd); #ifdef SANITY_CHECK if (sep->se_fd < 0) { syslog(LOG_ERR, "%s: %s: bad fd", __FUNCTION__, sep->se_service); exit(EX_SOFTWARE); } if (ISMUX(sep)) { syslog(LOG_ERR, "%s: %s: is mux", __FUNCTION__, sep->se_service); exit(EX_SOFTWARE); } if (FD_ISSET(sep->se_fd, &allsock)) { syslog(LOG_ERR, "%s: %s: not off", __FUNCTION__, sep->se_service); exit(EX_SOFTWARE); } nsock++; #endif FD_SET(sep->se_fd, &allsock); if (sep->se_fd > maxsock) maxsock = sep->se_fd; } void disable(struct servtab *sep) { if (debug) warnx( "disabling %s, fd %d", sep->se_service, sep->se_fd); #ifdef SANITY_CHECK if (sep->se_fd < 0) { syslog(LOG_ERR, "%s: %s: bad fd", __FUNCTION__, sep->se_service); exit(EX_SOFTWARE); } if (ISMUX(sep)) { syslog(LOG_ERR, "%s: %s: is mux", __FUNCTION__, sep->se_service); exit(EX_SOFTWARE); } if (!FD_ISSET(sep->se_fd, &allsock)) { syslog(LOG_ERR, "%s: %s: not on", __FUNCTION__, sep->se_service); exit(EX_SOFTWARE); } if (nsock == 0) { syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); exit(EX_SOFTWARE); } nsock--; #endif FD_CLR(sep->se_fd, &allsock); if (sep->se_fd == maxsock) maxsock--; } FILE *fconfig = NULL; struct servtab serv; char line[LINE_MAX]; int setconfig() { if (fconfig != NULL) { fseek(fconfig, 0L, SEEK_SET); return (1); } fconfig = fopen(CONFIG, "r"); return (fconfig != NULL); } void endconfig() { if (fconfig) { (void) fclose(fconfig); fconfig = NULL; } } struct servtab * getconfigent() { struct servtab *sep = &serv; int argc; char *cp, *arg, *s; char *versp; static char TCPMUX_TOKEN[] = "tcpmux/"; #define MUX_LEN (sizeof(TCPMUX_TOKEN)-1) #ifdef IPSEC char *policy = NULL; #endif int v4bind = 0; #ifdef INET6 int v6bind = 0; #endif more: while ((cp = nextline(fconfig)) != NULL) { #ifdef IPSEC /* lines starting with #@ is not a comment, but the policy */ if (cp[0] == '#' && cp[1] == '@') { char *p; for (p = cp + 2; p && *p && isspace(*p); p++) ; if (*p == '\0') { if (policy) free(policy); policy = NULL; } else if (ipsec_get_policylen(p) >= 0) { if (policy) free(policy); policy = newstr(p); } else { syslog(LOG_ERR, "%s: invalid ipsec policy \"%s\"", CONFIG, p); exit(EX_CONFIG); } } #endif if (*cp == '#' || *cp == '\0') continue; break; } if (cp == NULL) return ((struct servtab *)0); /* * clear the static buffer, since some fields (se_ctrladdr, * for example) don't get initialized here. */ memset((caddr_t)sep, 0, sizeof *sep); arg = skip(&cp); if (cp == NULL) { /* got an empty line containing just blanks/tabs. */ goto more; } if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { char *c = arg + MUX_LEN; if (*c == '+') { sep->se_type = MUXPLUS_TYPE; c++; } else sep->se_type = MUX_TYPE; sep->se_service = newstr(c); } else { sep->se_service = newstr(arg); sep->se_type = NORM_TYPE; } arg = sskip(&cp); if (strcmp(arg, "stream") == 0) sep->se_socktype = SOCK_STREAM; else if (strcmp(arg, "dgram") == 0) sep->se_socktype = SOCK_DGRAM; else if (strcmp(arg, "rdm") == 0) sep->se_socktype = SOCK_RDM; else if (strcmp(arg, "seqpacket") == 0) sep->se_socktype = SOCK_SEQPACKET; else if (strcmp(arg, "raw") == 0) sep->se_socktype = SOCK_RAW; else sep->se_socktype = -1; arg = sskip(&cp); if (strncmp(arg, "tcp", 3) == 0) { sep->se_proto = newstr(strsep(&arg, "/")); if (arg != NULL) { if (strcmp(arg, "ttcp") == 0) sep->se_type = TTCP_TYPE; else if (strcmp(arg, "faith") == 0) sep->se_type = FAITH_TYPE; } } else sep->se_proto = newstr(arg); if (strncmp(sep->se_proto, "rpc/", 4) == 0) { if (no_v4bind != 0) { syslog(LOG_INFO, "IPv4 bind is ignored for %s", sep->se_service); freeconfig(sep); goto more; } memmove(sep->se_proto, sep->se_proto + 4, strlen(sep->se_proto) + 1 - 4); sep->se_rpc = 1; sep->se_rpc_prog = sep->se_rpc_lowvers = sep->se_rpc_lowvers = 0; memcpy(&sep->se_ctrladdr4, bind_sa4, sizeof(sep->se_ctrladdr4)); if ((versp = rindex(sep->se_service, '/'))) { *versp++ = '\0'; switch (sscanf(versp, "%d-%d", &sep->se_rpc_lowvers, &sep->se_rpc_highvers)) { case 2: break; case 1: sep->se_rpc_highvers = sep->se_rpc_lowvers; break; default: syslog(LOG_ERR, "bad RPC version specifier; %s", sep->se_service); freeconfig(sep); goto more; } } else { sep->se_rpc_lowvers = sep->se_rpc_highvers = 1; } } sep->se_nomapped = 0; while (isdigit(sep->se_proto[strlen(sep->se_proto) - 1])) { #ifdef INET6 if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') { if (no_v6bind != 0) { syslog(LOG_INFO, "IPv6 bind is ignored for %s", sep->se_service); freeconfig(sep); goto more; } sep->se_proto[strlen(sep->se_proto) - 1] = '\0'; v6bind = 1; continue; } #endif if (sep->se_proto[strlen(sep->se_proto) - 1] == '4') { sep->se_proto[strlen(sep->se_proto) - 1] = '\0'; v4bind = 1; continue; } /* illegal version num */ syslog(LOG_ERR, "bad IP version for %s", sep->se_proto); freeconfig(sep); goto more; } #ifdef INET6 if (v6bind != 0) { sep->se_family = AF_INET6; if (v4bind == 0 || no_v4bind != 0) sep->se_nomapped = 1; } else #endif { /* default to v4 bind if not v6 bind */ if (no_v4bind != 0) { syslog(LOG_INFO, "IPv4 bind is ignored for %s", sep->se_service); freeconfig(sep); goto more; } sep->se_family = AF_INET; } /* init ctladdr */ switch(sep->se_family) { case AF_INET: memcpy(&sep->se_ctrladdr4, bind_sa4, sizeof(sep->se_ctrladdr4)); sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr4); sep->se_ctladdrinitok = 1; break; #ifdef INET6 case AF_INET6: memcpy(&sep->se_ctrladdr6, bind_sa6, sizeof(sep->se_ctrladdr6)); sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr6); sep->se_ctladdrinitok = 1; break; #endif } arg = sskip(&cp); if (!strncmp(arg, "wait", 4)) sep->se_accept = 0; else if (!strncmp(arg, "nowait", 6)) sep->se_accept = 1; else { syslog(LOG_ERR, "%s: bad wait/nowait for service %s", CONFIG, sep->se_service); goto more; } sep->se_maxchild = -1; sep->se_maxcpm = -1; if ((s = strchr(arg, '/')) != NULL) { char *eptr; u_long val; val = strtoul(s + 1, &eptr, 10); if (eptr == s + 1 || val > MAX_MAXCHLD) { syslog(LOG_ERR, "%s: bad max-child for service %s", CONFIG, sep->se_service); goto more; } if (debug) if (!sep->se_accept && val != 1) warnx("maxchild=%lu for wait service %s" " not recommended", val, sep->se_service); sep->se_maxchild = val; if (*eptr == '/') sep->se_maxcpm = strtol(eptr + 1, &eptr, 10); /* * explicitly do not check for \0 for future expansion / * backwards compatibility */ } if (ISMUX(sep)) { /* * Silently enforce "nowait" mode for TCPMUX services * since they don't have an assigned port to listen on. */ sep->se_accept = 1; if (strcmp(sep->se_proto, "tcp")) { syslog(LOG_ERR, "%s: bad protocol for tcpmux service %s", CONFIG, sep->se_service); goto more; } if (sep->se_socktype != SOCK_STREAM) { syslog(LOG_ERR, "%s: bad socket type for tcpmux service %s", CONFIG, sep->se_service); goto more; } } sep->se_user = newstr(sskip(&cp)); #ifdef LOGIN_CAP if ((s = strrchr(sep->se_user, '/')) != NULL) { *s = '\0'; sep->se_class = newstr(s + 1); } else sep->se_class = newstr(RESOURCE_RC); #endif if ((s = strrchr(sep->se_user, ':')) != NULL) { *s = '\0'; sep->se_group = newstr(s + 1); } else sep->se_group = NULL; sep->se_server = newstr(sskip(&cp)); if ((sep->se_server_name = rindex(sep->se_server, '/'))) sep->se_server_name++; if (strcmp(sep->se_server, "internal") == 0) { struct biltin *bi; for (bi = biltins; bi->bi_service; bi++) if (bi->bi_socktype == sep->se_socktype && matchservent(bi->bi_service, sep->se_service, sep->se_proto)) break; if (bi->bi_service == 0) { syslog(LOG_ERR, "internal service %s unknown", sep->se_service); goto more; } sep->se_accept = 1; /* force accept mode for built-ins */ sep->se_bi = bi; } else sep->se_bi = NULL; if (sep->se_maxcpm < 0) sep->se_maxcpm = maxcpm; if (sep->se_maxchild < 0) { /* apply default max-children */ if (sep->se_bi && sep->se_bi->bi_maxchild >= 0) sep->se_maxchild = sep->se_bi->bi_maxchild; else if (sep->se_accept) sep->se_maxchild = maxchild > 0 ? maxchild : 0; else sep->se_maxchild = 1; } - if (sep->se_maxchild) { + if (sep->se_maxchild > 0) { sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); if (sep->se_pids == NULL) { syslog(LOG_ERR, "malloc: %m"); exit(EX_OSERR); } } argc = 0; for (arg = skip(&cp); cp; arg = skip(&cp)) if (argc < MAXARGV) { sep->se_argv[argc++] = newstr(arg); } else { syslog(LOG_ERR, "%s: too many arguments for service %s", CONFIG, sep->se_service); goto more; } while (argc <= MAXARGV) sep->se_argv[argc++] = NULL; #ifdef IPSEC sep->se_policy = policy ? newstr(policy) : NULL; #endif return (sep); } void freeconfig(cp) struct servtab *cp; { int i; if (cp->se_service) free(cp->se_service); if (cp->se_proto) free(cp->se_proto); if (cp->se_user) free(cp->se_user); if (cp->se_group) free(cp->se_group); #ifdef LOGIN_CAP if (cp->se_class) free(cp->se_class); #endif if (cp->se_server) free(cp->se_server); if (cp->se_pids) free(cp->se_pids); for (i = 0; i < MAXARGV; i++) if (cp->se_argv[i]) free(cp->se_argv[i]); #ifdef IPSEC if (cp->se_policy) free(cp->se_policy); #endif } /* * Safe skip - if skip returns null, log a syntax error in the * configuration file and exit. */ char * sskip(cpp) char **cpp; { char *cp; cp = skip(cpp); if (cp == NULL) { syslog(LOG_ERR, "%s: syntax error", CONFIG); exit(EX_DATAERR); } return (cp); } char * skip(cpp) char **cpp; { char *cp = *cpp; char *start; char quote = '\0'; again: while (*cp == ' ' || *cp == '\t') cp++; if (*cp == '\0') { int c; c = getc(fconfig); (void) ungetc(c, fconfig); if (c == ' ' || c == '\t') if ((cp = nextline(fconfig))) goto again; *cpp = (char *)0; return ((char *)0); } if (*cp == '"' || *cp == '\'') quote = *cp++; start = cp; if (quote) while (*cp && *cp != quote) cp++; else while (*cp && *cp != ' ' && *cp != '\t') cp++; if (*cp != '\0') *cp++ = '\0'; *cpp = cp; return (start); } char * nextline(fd) FILE *fd; { char *cp; if (fgets(line, sizeof (line), fd) == NULL) return ((char *)0); cp = strchr(line, '\n'); if (cp) *cp = '\0'; return (line); } char * newstr(cp) char *cp; { if ((cp = strdup(cp ? cp : ""))) return (cp); syslog(LOG_ERR, "strdup: %m"); exit(EX_OSERR); } #ifdef OLD_SETPROCTITLE void inetd_setproctitle(a, s) char *a; int s; { int size; char *cp; struct sockaddr_storage ss; char buf[80], pbuf[INET6_ADDRSTRLEN]; cp = Argv[0]; size = sizeof(ss); if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) { getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf), NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); (void) sprintf(buf, "-%s [%s]", a, pbuf); } else (void) sprintf(buf, "-%s", a); strncpy(cp, buf, LastArg - cp); cp += strlen(cp); while (cp < LastArg) *cp++ = ' '; } #else void inetd_setproctitle(a, s) char *a; int s; { int size; struct sockaddr_storage ss; char buf[80], pbuf[INET6_ADDRSTRLEN]; size = sizeof(ss); if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) { getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf), NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); (void) sprintf(buf, "%s [%s]", a, pbuf); } else (void) sprintf(buf, "%s", a); setproctitle("%s", buf); } #endif /* * Internet services provided internally by inetd: */ int check_loop(sa, sep) struct sockaddr *sa; struct servtab *sep; { struct servtab *se2; char pname[INET6_ADDRSTRLEN]; for (se2 = servtab; se2; se2 = se2->se_next) { if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM) continue; switch (se2->se_family) { case AF_INET: if (((struct sockaddr_in *)sa)->sin_port == se2->se_ctrladdr4.sin_port) goto isloop; continue; #ifdef INET6 case AF_INET6: if (((struct sockaddr_in *)sa)->sin_port == se2->se_ctrladdr4.sin_port) goto isloop; continue; #endif default: continue; } isloop: getnameinfo(sa, sa->sa_len, pname, sizeof(pname), NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); syslog(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s", sep->se_service, sep->se_proto, se2->se_service, se2->se_proto, pname); return 1; } return 0; } /* * print_service: * Dump relevant information to stderr */ void print_service(action, sep) char *action; struct servtab *sep; { fprintf(stderr, "%s: %s proto=%s accept=%d max=%d user=%s group=%s" #ifdef LOGIN_CAP "class=%s" #endif " builtin=%p server=%s" #ifdef IPSEC " policy=\"%s\"" #endif "\n", action, sep->se_service, sep->se_proto, sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group, #ifdef LOGIN_CAP sep->se_class, #endif (void *) sep->se_bi, sep->se_server #ifdef IPSEC , (sep->se_policy ? sep->se_policy : "") #endif ); } #define CPMHSIZE 256 #define CPMHMASK (CPMHSIZE-1) #define CHTGRAN 10 #define CHTSIZE 6 typedef struct CTime { unsigned long ct_Ticks; int ct_Count; } CTime; typedef struct CHash { union { struct in_addr c4_Addr; struct in6_addr c6_Addr; } cu_Addr; #define ch_Addr4 cu_Addr.c4_Addr #define ch_Addr6 cu_Addr.c6_Addr int ch_Family; time_t ch_LTime; char *ch_Service; CTime ch_Times[CHTSIZE]; } CHash; CHash CHashAry[CPMHSIZE]; int cpmip(sep, ctrl) struct servtab *sep; int ctrl; { struct sockaddr_storage rss; int rssLen = sizeof(rss); int r = 0; /* * If getpeername() fails, just let it through (if logging is * enabled the condition is caught elsewhere) */ if (sep->se_maxcpm > 0 && getpeername(ctrl, (struct sockaddr *)&rss, &rssLen) == 0 ) { time_t t = time(NULL); int hv = 0xABC3D20F; int i; int cnt = 0; CHash *chBest = NULL; unsigned int ticks = t / CHTGRAN; struct sockaddr_in *sin; #ifdef INET6 struct sockaddr_in6 *sin6; #endif sin = (struct sockaddr_in *)&rss; #ifdef INET6 sin6 = (struct sockaddr_in6 *)&rss; #endif { char *p; int i, addrlen; switch (rss.ss_family) { case AF_INET: p = (char *)&sin->sin_addr; addrlen = sizeof(struct in_addr); break; #ifdef INET6 case AF_INET6: p = (char *)&sin6->sin6_addr; addrlen = sizeof(struct in6_addr); break; #endif default: /* should not happen */ return -1; } for (i = 0; i < addrlen; ++i, ++p) { hv = (hv << 5) ^ (hv >> 23) ^ *p; } hv = (hv ^ (hv >> 16)); } for (i = 0; i < 5; ++i) { CHash *ch = &CHashAry[(hv + i) & CPMHMASK]; if (rss.ss_family == AF_INET && ch->ch_Family == AF_INET && sin->sin_addr.s_addr == ch->ch_Addr4.s_addr && ch->ch_Service && strcmp(sep->se_service, ch->ch_Service) == 0) { chBest = ch; break; } #ifdef INET6 if (rss.ss_family == AF_INET6 && ch->ch_Family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ch->ch_Addr6) != 0 && ch->ch_Service && strcmp(sep->se_service, ch->ch_Service) == 0) { chBest = ch; break; } #endif if (chBest == NULL || ch->ch_LTime == 0 || ch->ch_LTime < chBest->ch_LTime) { chBest = ch; } } if ((rss.ss_family == AF_INET && (chBest->ch_Family != AF_INET || sin->sin_addr.s_addr != chBest->ch_Addr4.s_addr)) || chBest->ch_Service == NULL || strcmp(sep->se_service, chBest->ch_Service) != 0) { chBest->ch_Family = sin->sin_family; chBest->ch_Addr4 = sin->sin_addr; if (chBest->ch_Service) free(chBest->ch_Service); chBest->ch_Service = strdup(sep->se_service); bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); } #ifdef INET6 if ((rss.ss_family == AF_INET6 && (chBest->ch_Family != AF_INET6 || IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &chBest->ch_Addr6) == 0)) || chBest->ch_Service == NULL || strcmp(sep->se_service, chBest->ch_Service) != 0) { chBest->ch_Family = sin6->sin6_family; chBest->ch_Addr6 = sin6->sin6_addr; if (chBest->ch_Service) free(chBest->ch_Service); chBest->ch_Service = strdup(sep->se_service); bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); } #endif chBest->ch_LTime = t; { CTime *ct = &chBest->ch_Times[ticks % CHTSIZE]; if (ct->ct_Ticks != ticks) { ct->ct_Ticks = ticks; ct->ct_Count = 0; } ++ct->ct_Count; } for (i = 0; i < CHTSIZE; ++i) { CTime *ct = &chBest->ch_Times[i]; if (ct->ct_Ticks <= ticks && ct->ct_Ticks >= ticks - CHTSIZE) { cnt += ct->ct_Count; } } if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) { char pname[INET6_ADDRSTRLEN]; getnameinfo((struct sockaddr *)&rss, ((struct sockaddr *)&rss)->sa_len, pname, sizeof(pname), NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); r = -1; syslog(LOG_ERR, "%s from %s exceeded counts/min (limit %d/min)", sep->se_service, pname, sep->se_maxcpm); } } return(r); } Index: stable/4/usr.sbin/inetd/inetd.h =================================================================== --- stable/4/usr.sbin/inetd/inetd.h (revision 67517) +++ stable/4/usr.sbin/inetd/inetd.h (revision 67518) @@ -1,159 +1,159 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. * * $FreeBSD$ */ #include #include #include #include #define BUFSIZE 8192 #define LINESIZ 72 #define NORM_TYPE 0 #define MUX_TYPE 1 #define MUXPLUS_TYPE 2 #define TTCP_TYPE 3 #define FAITH_TYPE 4 #define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \ ((sep)->se_type == MUXPLUS_TYPE)) #define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE) #define ISTTCP(sep) ((sep)->se_type == TTCP_TYPE) struct servtab { char *se_service; /* name of service */ int se_socktype; /* type of socket to use */ int se_family; /* address family */ char *se_proto; /* protocol used */ int se_maxchild; /* max number of children */ int se_maxcpm; /* max connects per IP per minute */ int se_numchild; /* current number of children */ pid_t *se_pids; /* array of child pids */ char *se_user; /* user name to run as */ char *se_group; /* group name to run as */ #ifdef LOGIN_CAP char *se_class; /* login class name to run with */ #endif struct biltin *se_bi; /* if built-in, description */ char *se_server; /* server program */ char *se_server_name; /* server program without path */ #define MAXARGV 20 char *se_argv[MAXARGV+1]; /* program arguments */ #ifdef IPSEC - char *se_policy; /* IPsec poilcy string */ + char *se_policy; /* IPsec policy string */ #endif int se_fd; /* open descriptor */ union { /* bound address */ struct sockaddr se_un_ctrladdr; struct sockaddr_in se_un_ctrladdr4; struct sockaddr_in6 se_un_ctrladdr6; } se_un; #define se_ctrladdr se_un.se_un_ctrladdr #define se_ctrladdr4 se_un.se_un_ctrladdr4 #define se_ctrladdr6 se_un.se_un_ctrladdr6 int se_ctrladdr_size; u_char se_type; /* type: normal, mux, or mux+ */ u_char se_checked; /* looked at during merge */ u_char se_accept; /* i.e., wait/nowait mode */ u_char se_rpc; /* ==1 if RPC service */ int se_rpc_prog; /* RPC program number */ u_int se_rpc_lowvers; /* RPC low version */ u_int se_rpc_highvers; /* RPC high version */ int se_count; /* number started since se_time */ struct timeval se_time; /* start of se_count */ struct servtab *se_next; struct se_flags { u_int se_nomapped : 1; u_int se_ctladdrinitok : 1; u_int se_reset : 1; } se_flags; }; #define se_nomapped se_flags.se_nomapped #define se_ctladdrinitok se_flags.se_ctladdrinitok #define se_reset se_flags.se_reset void chargen_dg __P((int, struct servtab *)); void chargen_stream __P((int, struct servtab *)); void close_sep __P((struct servtab *)); void flag_signal __P((char)); void flag_config __P((int)); void config __P((void)); void daytime_dg __P((int, struct servtab *)); void daytime_stream __P((int, struct servtab *)); void discard_dg __P((int, struct servtab *)); void discard_stream __P((int, struct servtab *)); void echo_dg __P((int, struct servtab *)); void echo_stream __P((int, struct servtab *)); void endconfig __P((void)); struct servtab *enter __P((struct servtab *)); void freeconfig __P((struct servtab *)); struct servtab *getconfigent __P((void)); void iderror __P((int, int, int, int)); void ident_stream __P((int, struct servtab *)); void machtime_dg __P((int, struct servtab *)); void machtime_stream __P((int, struct servtab *)); int matchservent __P((char *, char *, char *)); char *newstr __P((char *)); char *nextline __P((FILE *)); void print_service __P((char *, struct servtab *)); void addchild __P((struct servtab *, int)); void flag_reapchild __P((int)); void reapchild __P((void)); void enable __P((struct servtab *)); void disable __P((struct servtab *)); void flag_retry __P((int)); void retry __P((void)); int setconfig __P((void)); void setup __P((struct servtab *)); #ifdef IPSEC void ipsecsetup __P((struct servtab *)); #endif char *sskip __P((char **)); char *skip __P((char **)); struct servtab *tcpmux __P((int)); int cpmip __P((struct servtab *, int)); void inetd_setproctitle __P((char *, int)); void unregisterrpc __P((register struct servtab *sep)); struct biltin { char *bi_service; /* internally provided service name */ int bi_socktype; /* type of socket supported */ short bi_fork; /* 1 if should fork before call */ int bi_maxchild; /* max number of children, -1=default */ void (*bi_fn)(); /* function which performs it */ };