diff options
author | shin <shin@FreeBSD.org> | 2000-01-25 14:52:10 +0000 |
---|---|---|
committer | shin <shin@FreeBSD.org> | 2000-01-25 14:52:10 +0000 |
commit | fc29f7bcf7236935ba5c171ea553ac7dca533e8c (patch) | |
tree | 13e93b815761d57bc661099e5a2b7bcb2882b762 /usr.sbin | |
parent | 4497b0fbabcff52472040173a4ea879ec212ecaa (diff) | |
download | FreeBSD-src-fc29f7bcf7236935ba5c171ea553ac7dca533e8c.zip FreeBSD-src-fc29f7bcf7236935ba5c171ea553ac7dca533e8c.tar.gz |
several tcp apps IPv6 update
-inetd
-rshd
-rlogind
-telnetd
-rsh
-rlogin
Reviewed by: freebsd-arch, cvs-committers
Obtained from: KAME project
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/inetd/Makefile | 5 | ||||
-rw-r--r-- | usr.sbin/inetd/builtins.c | 88 | ||||
-rw-r--r-- | usr.sbin/inetd/inetd.8 | 77 | ||||
-rw-r--r-- | usr.sbin/inetd/inetd.c | 616 | ||||
-rw-r--r-- | usr.sbin/inetd/inetd.h | 27 |
5 files changed, 702 insertions, 111 deletions
diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile index 0df9755..738bbed 100644 --- a/usr.sbin/inetd/Makefile +++ b/usr.sbin/inetd/Makefile @@ -10,7 +10,8 @@ MAINTAINER=des@freebsd.org COPTS+= -Wall -DLOGIN_CAP #COPTS+= -DSANITY_CHECK -DPADD+= ${LIBUTIL} ${LIBWRAP} -LDADD+= -lutil -lwrap +CFLAGS+=-DINET6 -DIPSEC +DPADD+= ${LIBUTIL} ${LIBWRAP} ${LIBIPSEC} +LDADD+= -lutil -lwrap -lipsec .include <bsd.prog.mk> diff --git a/usr.sbin/inetd/builtins.c b/usr.sbin/inetd/builtins.c index 6ba404c..f05971f 100644 --- a/usr.sbin/inetd/builtins.c +++ b/usr.sbin/inetd/builtins.c @@ -57,7 +57,7 @@ extern struct servtab *servtab; char ring[128]; char *endring; -int check_loop __P((struct sockaddr_in *, struct servtab *sep)); +int check_loop __P((struct sockaddr *, struct servtab *sep)); void inetd_setproctitle __P((char *, int)); struct biltin biltins[] = { @@ -111,7 +111,7 @@ chargen_dg(s, sep) /* Character generator */ int s; struct servtab *sep; { - struct sockaddr_in sin; + struct sockaddr_storage ss; static char *rs; int len, size; char text[LINESIZ+2]; @@ -121,12 +121,12 @@ chargen_dg(s, sep) /* Character generator */ rs = ring; } - size = sizeof(sin); + size = sizeof(ss); if (recvfrom(s, text, sizeof(text), 0, - (struct sockaddr *)&sin, &size) < 0) + (struct sockaddr *)&ss, &size) < 0) return; - if (check_loop(&sin, sep)) + if (check_loop((struct sockaddr *)&ss, sep)) return; if ((len = endring - rs) >= LINESIZ) @@ -140,7 +140,7 @@ chargen_dg(s, sep) /* Character generator */ text[LINESIZ] = '\r'; text[LINESIZ + 1] = '\n'; (void) sendto(s, text, sizeof(text), 0, - (struct sockaddr *)&sin, sizeof(sin)); + (struct sockaddr *)&ss, sizeof(ss)); } /* ARGSUSED */ @@ -189,22 +189,22 @@ daytime_dg(s, sep) /* Return human-readable time of day */ { char buffer[256]; time_t clock; - struct sockaddr_in sin; + struct sockaddr_storage ss; int size; clock = time((time_t *) 0); - size = sizeof(sin); + size = sizeof(ss); if (recvfrom(s, buffer, sizeof(buffer), 0, - (struct sockaddr *)&sin, &size) < 0) + (struct sockaddr *)&ss, &size) < 0) return; - if (check_loop(&sin, sep)) + 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 *)&sin, sizeof(sin)); + (struct sockaddr *)&ss, sizeof(ss)); } /* ARGSUSED */ @@ -270,18 +270,18 @@ echo_dg(s, sep) /* Echo service -- echo data back */ { char buffer[BUFSIZE]; int i, size; - struct sockaddr_in sin; + struct sockaddr_storage ss; - size = sizeof(sin); + size = sizeof(ss); if ((i = recvfrom(s, buffer, sizeof(buffer), 0, - (struct sockaddr *)&sin, &size)) < 0) + (struct sockaddr *)&ss, &size)) < 0) return; - if (check_loop(&sin, sep)) + if (check_loop((struct sockaddr *)&ss, sep)) return; - (void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin, - sizeof(sin)); + (void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, + sizeof(ss)); } /* ARGSUSED */ @@ -335,6 +335,8 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ struct utsname un; struct stat sb; struct sockaddr_in sin[2]; + struct sockaddr_in6 sin6[2]; + struct sockaddr_storage ss[2]; struct ucred uc; struct timeval tv = { 10, @@ -345,7 +347,7 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL, garbage[7]; char *fallback = NULL; int len, c, fflag = 0, nflag = 0, rflag = 0, argc = 0, usedfallback = 0; - int gflag = 0, Rflag = 0; + int gflag = 0, Rflag = 0, getcredfail = 0; u_short lport, fport; inetd_setproctitle(sep->se_service, s); @@ -434,11 +436,11 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ iderror(0, 0, s, errno); osname = un.sysname; } - len = sizeof(sin[0]); - if (getsockname(s, (struct sockaddr *)&sin[0], &len) == -1) + len = sizeof(ss[0]); + if (getsockname(s, (struct sockaddr *)&ss[0], &len) == -1) iderror(0, 0, s, errno); - len = sizeof(sin[1]); - if (getpeername(s, (struct sockaddr *)&sin[1], &len) == -1) + len = sizeof(ss[1]); + if (getpeername(s, (struct sockaddr *)&ss[1], &len) == -1) iderror(0, 0, s, errno); /* * We're going to prepare for and execute reception of a @@ -476,11 +478,35 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ * arrays have been filled in above via get{peer,sock}name(), * so right here we are only setting the ports. */ - sin[0].sin_port = htons(lport); - sin[1].sin_port = htons(fport); + if (ss[0].ss_family != ss[1].ss_family) + iderror(lport, fport, s, errno); len = sizeof(uc); - if (sysctlbyname("net.inet.tcp.getcred", &uc, &len, sin, - sizeof(sin)) == -1) { + 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, &len, 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, &len, 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; @@ -611,20 +637,20 @@ machtime_dg(s, sep) struct servtab *sep; { unsigned long result; - struct sockaddr_in sin; + struct sockaddr_storage ss; int size; - size = sizeof(sin); + size = sizeof(ss); if (recvfrom(s, (char *)&result, sizeof(result), 0, - (struct sockaddr *)&sin, &size) < 0) + (struct sockaddr *)&ss, &size) < 0) return; - if (check_loop(&sin, sep)) + if (check_loop((struct sockaddr *)&ss, sep)) return; result = machtime(); (void) sendto(s, (char *) &result, sizeof(result), 0, - (struct sockaddr *)&sin, sizeof(sin)); + (struct sockaddr *)&ss, sizeof(ss)); } /* ARGSUSED */ diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8 index 2a3b411..66584d6 100644 --- a/usr.sbin/inetd/inetd.8 +++ b/usr.sbin/inetd/inetd.8 @@ -47,7 +47,7 @@ .Op Fl W .Op Fl c Ar maximum .Op Fl C Ar rate -.Op Fl a Ar address +.Op Fl a Ar address|hostname .Op Fl p Ar filename .Op Fl R Ar rate .Op Ar configuration file @@ -101,6 +101,22 @@ Specify the maximum number of times a service can be invoked in one minute; the default is 256. .It Fl a Specify a specific IP address to bind to. +Or a hostname can also be specified, and then an IP address and/or an +IPv6 address corresponds to the hostname is used. Usually hostname +specification is used in conjunction with +.Xr jail 8 , +where the hostname corresponds to a jail environment. + +When hostname specification is used and either of IPv4 bind and IPv6 +bind is desired, you need to specify 2 entry for each service, one for +IPv4 and one for IPv6, in /etc/inetd.conf. +For example, +.Bd -literal +telnet stream tcp4 nowait root /usr/libexec/telnetd telnetd +telnet stream tcp6 nowait root /usr/libexec/telnetd telnetd +.Ed + +See explanation for protocol field in /etc/inetd.conf for details. .It Fl p Specify an alternate file in which to store the process ID. .El @@ -221,14 +237,39 @@ Examples might be .Dq tcp or .Dq udp . +In this case, this entry only accept IPv4 to keep backword +compatibility. +The names +.Dq tcp4 , +.Dq udp4 +specialized the entry to IPv4 only. +The names +.Dq tcp6 , +.Dq udp6 +specialized the entry to IPv6 only. +The names +.Dq tcp46 , +.Dq udp46 +let the entry accept each of IPv4 and IPv6 via AF_INET6 wildcard binded socket. If it is desired that the service is reachable via T/TCP, one should specify .Dq tcp/ttcp . +This entry only accept IPv4 to keep backword compatibility. +The name +.Dq tcp4/ttcp , +specialized the entry to IPv4 only. +The name +.Dq tcp6/ttcp , +specialized the entry to IPv6 only. +The name +.Dq tcp46/ttcp , +let the entry accept each of IPv4 and IPv6 via AF_INET6 wildcard binded socket. Rpc based services are specified with the .Dq rpc/tcp or .Dq rpc/udp service type. +Currently only IPv4 is supported for rpc services. TCPMUX services must use .Dq tcp . .Pp @@ -436,6 +477,9 @@ in tcpmux stream tcp nowait root internal .Ed .Pp +Or if you wish to provide TCPMUX services also over IPv6, you can +specify tcp46 or tcp6 instead of tcp above. +.Pp When given the .Fl l option @@ -528,6 +572,30 @@ causes .Nm to list TCPMUX services in .Pa inetd.conf . +.Sh IPSEC +The implementation includes tiny hack to support IPsec policy setting for +each of the socket. +A special form of comment line, starting with +.Dq Li "#@" , +will work as policy specifier. +The content of the above comment line will be treated as IPsec policy string, +as described in +.Xr ipsec_set_policy 3 . +A +.Li "#@" +line will affect all the following lines in +.Pa inetd.conf , +so you may want to reset IPsec policy by using a comment line with +.Li "#@" +only +.Pq with no policy string . +.Pp +If invalid IPsec policy string appears on +.Pa inetd.conf , +.Nm +will leave error message using +.Xr syslog 3 , +and terminates itself. .Sh "FILES" .Bl -tag -width /var/run/inetd.pid -compact .It Pa /etc/inetd.conf @@ -546,9 +614,14 @@ 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 @@ -632,6 +705,7 @@ socket but was unable to. .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 , @@ -662,6 +736,7 @@ based services is modeled after that provided by .Tn SunOS 4.1. +IPsec hack was made by KAME project, in 1999. The .Tn FreeBSD TCP Wrappers support first appeared in diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c index 3378753..06bad40 100644 --- a/usr.sbin/inetd/inetd.c +++ b/usr.sbin/inetd/inetd.c @@ -102,6 +102,13 @@ static const char rcsid[] = * 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 <sys/param.h> #include <sys/ioctl.h> @@ -130,10 +137,23 @@ static const char rcsid[] = #include <unistd.h> #include <libutil.h> #include <sysexits.h> +#include <ctype.h> #include "inetd.h" #include "pathnames.h" +#ifdef IPSEC +#include <netinet6/ipsec.h> +#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 @@ -193,7 +213,13 @@ int maxchild = MAXCHILD; int maxcpm = MAXCPM; struct servent *sp; struct rpcent *rpc; -struct in_addr bind_address; +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; @@ -248,8 +274,20 @@ main(argc, argv, envp) int denied; char *service = NULL; char *pnm; - struct sockaddr_in peer; + 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 @@ -263,7 +301,6 @@ main(argc, argv, envp) openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); - bind_address.s_addr = htonl(INADDR_ANY); while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:")) != -1) switch(ch) { case 'd': @@ -286,11 +323,7 @@ main(argc, argv, envp) "-C %s: bad value for maximum children/minute"); break; case 'a': - if (!inet_aton(optarg, &bind_address)) { - syslog(LOG_ERR, - "-a %s: invalid IP address", optarg); - exit(EX_USAGE); - } + hostname = optarg; break; case 'p': pid_file = optarg; @@ -309,6 +342,66 @@ main(argc, argv, envp) " [-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; @@ -457,18 +550,33 @@ main(argc, argv, envp) } else ctrl = sep->se_fd; if (log && !ISWRAP(sep)) { + char pname[INET6_ADDRSTRLEN]; pnm = "unknown"; - i = sizeof peer; + i = sizeof peermax; if (getpeername(ctrl, (struct sockaddr *) - &peer, &i)) { - i = sizeof peer; + &peermax, &i)) { + i = sizeof peermax; if (recvfrom(ctrl, buf, sizeof(buf), MSG_PEEK, - (struct sockaddr *)&peer, &i) >= 0) - pnm = inet_ntoa(peer.sin_addr); + (struct sockaddr *)&peermax, + &i) >= 0) { + getnameinfo((struct sockaddr *)&peermax, + sizeof(peermax), + pname, sizeof(pname), + NULL, 0, + NI_NUMERICHOST| + NI_WITHSCOPEID); + pnm = pname; + } + } else { + getnameinfo((struct sockaddr *)&peermax, + sizeof(peermax), + pname, sizeof(pname), + NULL, 0, + NI_NUMERICHOST| + NI_WITHSCOPEID); + pnm = pname; } - else - pnm = inet_ntoa(peer.sin_addr); syslog(LOG_INFO,"%s from %s", sep->se_service, pnm); } (void) sigblock(SIGBLOCK); @@ -783,13 +891,18 @@ void config() #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) + 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) { new->se_numchild = sep->se_numchild; @@ -823,6 +936,10 @@ void config() 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) @@ -837,21 +954,63 @@ void config() 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_service, sep->se_proto); sep->se_checked = 0; continue; } - if (sp->s_port != sep->se_ctrladdr.sin_port) { - sep->se_ctrladdr.sin_family = AF_INET; - sep->se_ctrladdr.sin_addr = bind_address; - sep->se_ctrladdr.sin_port = sp->s_port; - if (sep->se_fd >= 0) - close_sep(sep); + 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) { @@ -950,7 +1109,7 @@ setup(sep) { int on = 1; - if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) { + 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); @@ -969,13 +1128,38 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) 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) && + (sep->se_nomapped != 0) && + (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_BINDV6ONLY, + (char *)&on, sizeof (on)) < 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, - sizeof (sep->se_ctrladdr)) < 0) { + sep->se_ctrladdr_size) < 0) { if (debug) warn("bind failed on %s/%s", sep->se_service, sep->se_proto); @@ -990,8 +1174,16 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) return; } if (sep->se_rpc) { - int i, len = sizeof(struct sockaddr); + 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", @@ -1007,9 +1199,8 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) pmap_set(sep->se_rpc_prog, i, (sep->se_socktype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP, - ntohs(sep->se_ctrladdr.sin_port)); + ntohs(sep->se_ctrladdr4.sin_port)); } - } if (sep->se_socktype == SOCK_STREAM) listen(sep->se_fd, 64); @@ -1020,6 +1211,80 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) } } +#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) { + syslog(LOG_ERR, + "%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) { + syslog(LOG_ERR, + "%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. */ @@ -1173,10 +1438,42 @@ getconfigent() 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)) && (*cp == '#' || *cp == '\0')) - ; + 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); /* @@ -1216,21 +1513,29 @@ more: sep->se_socktype = -1; arg = sskip(&cp); - if (strcmp(arg, "tcp/ttcp") == 0) { - sep->se_type = TTCP_TYPE; - sep->se_proto = newstr("tcp"); - } else { + 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 (sep->se_family != AF_INET) { + syslog(LOG_ERR, "IPv6 for RPC is not supported yet"); + 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; - sep->se_ctrladdr.sin_family = AF_INET; - sep->se_ctrladdr.sin_port = 0; - sep->se_ctrladdr.sin_addr = bind_address; + memcpy(&sep->se_ctrladdr4, bind_sa4, + sizeof(sep->se_ctrladdr4)); if ((versp = rindex(sep->se_service, '/'))) { *versp++ = '\0'; switch (sscanf(versp, "%d-%d", @@ -1255,6 +1560,64 @@ more: 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; + } +#endif + else { /* 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; @@ -1372,6 +1735,9 @@ more: } while (argc <= MAXARGV) sep->se_argv[argc++] = NULL; +#ifdef IPSEC + sep->se_policy = policy ? newstr(policy) : NULL; +#endif return (sep); } @@ -1400,6 +1766,10 @@ freeconfig(cp) 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 } @@ -1490,14 +1860,16 @@ inetd_setproctitle(a, s) { int size; char *cp; - struct sockaddr_in sin; - char buf[80]; + struct sockaddr_storage ss; + char buf[80], pbuf[INET6_ADDRSTRLEN]; cp = Argv[0]; - size = sizeof(sin); - if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) - (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr)); - else + 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); @@ -1511,13 +1883,15 @@ inetd_setproctitle(a, s) int s; { int size; - struct sockaddr_in sin; - char buf[80]; - - size = sizeof(sin); - if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) - (void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr)); - else + 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); } @@ -1528,24 +1902,41 @@ inetd_setproctitle(a, s) * Internet services provided internally by inetd: */ -int check_loop(sin, sep) - struct sockaddr_in *sin; +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; - if (sin->sin_port == se2->se_ctrladdr.sin_port) { - syslog(LOG_WARNING, - "%s/%s:%s/%s loop request REFUSED from %s", - sep->se_service, sep->se_proto, - se2->se_service, se2->se_proto, - inet_ntoa(sin->sin_addr)); - return 1; + 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; } @@ -1560,17 +1951,25 @@ print_service(action, sep) struct servtab *sep; { fprintf(stderr, + "%s: %s proto=%s accept=%d max=%d user=%s group=%s" #ifdef LOGIN_CAP - "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%p server=%s\n", -#else - "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%p server=%s\n", + "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); + (void *) sep->se_bi, sep->se_server +#ifdef IPSEC + , (sep->se_policy ? sep->se_policy : "") +#endif + ); } #define CPMHSIZE 256 @@ -1584,7 +1983,13 @@ typedef struct CTime { } CTime; typedef struct CHash { - struct in_addr ch_Addr; + 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]; @@ -1597,8 +2002,8 @@ cpmip(sep, ctrl) struct servtab *sep; int ctrl; { - struct sockaddr_in rsin; - int rsinLen = sizeof(rsin); + struct sockaddr_storage rss; + int rssLen = sizeof(rss); int r = 0; /* @@ -1607,21 +2012,43 @@ cpmip(sep, ctrl) */ if (sep->se_maxcpm > 0 && - getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 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; + int i, addrlen; - for (i = 0, p = (char *)&rsin.sin_addr; - i < sizeof(rsin.sin_addr); - ++i, ++p) { + 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)); @@ -1629,26 +2056,57 @@ cpmip(sep, ctrl) for (i = 0; i < 5; ++i) { CHash *ch = &CHashAry[(hv + i) & CPMHMASK]; - if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr && + 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 (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr || + 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_Addr = rsin.sin_addr; + 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]; @@ -1666,10 +2124,16 @@ cpmip(sep, ctrl) } } 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, inet_ntoa(rsin.sin_addr), + sep->se_service, pname, sep->se_maxcpm); } } diff --git a/usr.sbin/inetd/inetd.h b/usr.sbin/inetd/inetd.h index ab78c73..f956c1a 100644 --- a/usr.sbin/inetd/inetd.h +++ b/usr.sbin/inetd/inetd.h @@ -47,6 +47,7 @@ #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) @@ -55,6 +56,7 @@ 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 */ @@ -70,8 +72,19 @@ struct servtab { 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 */ +#endif int se_fd; /* open descriptor */ - struct sockaddr_in se_ctrladdr;/* bound address */ + 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 */ @@ -82,8 +95,17 @@ struct servtab { 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 *)); @@ -117,6 +139,9 @@ 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)); |