From 0cac72f42c2f9dfe7953cb3dd7c2e93d1d0c61cf Mon Sep 17 00:00:00 2001 From: Yoshinobu Inoue Date: Tue, 25 Jan 2000 14:52:10 +0000 Subject: [PATCH] several tcp apps IPv6 update -inetd -rshd -rlogind -telnetd -rsh -rlogin Reviewed by: freebsd-arch, cvs-committers Obtained from: KAME project --- include/unistd.h | 2 + lib/libc/net/Makefile.inc | 4 +- lib/libc/net/rcmd.3 | 21 +- lib/libc/net/rcmd.c | 13 +- lib/libutil/Makefile | 3 +- lib/libutil/libutil.h | 3 + lib/libutil/realhostname.3 | 3 +- lib/libutil/realhostname.c | 95 ++++++ lib/libutil/realhostname_sa.3 | 135 ++++++++ libexec/rlogind/Makefile | 2 +- libexec/rlogind/rlogind.8 | 2 + libexec/rlogind/rlogind.c | 85 ++++- libexec/rshd/Makefile | 3 + libexec/rshd/rshd.8 | 2 + libexec/rshd/rshd.c | 83 +++-- libexec/telnetd/Makefile | 1 + libexec/telnetd/telnetd.8 | 2 + libexec/telnetd/telnetd.c | 88 +++-- usr.bin/rlogin/rlogin.1 | 2 + usr.bin/rlogin/rlogin.c | 18 +- usr.bin/rsh/rsh.c | 6 +- usr.sbin/inetd/Makefile | 5 +- usr.sbin/inetd/builtins.c | 88 +++-- usr.sbin/inetd/inetd.8 | 77 ++++- usr.sbin/inetd/inetd.c | 614 +++++++++++++++++++++++++++++----- usr.sbin/inetd/inetd.h | 27 +- 26 files changed, 1185 insertions(+), 199 deletions(-) create mode 100644 lib/libutil/realhostname_sa.3 diff --git a/include/unistd.h b/include/unistd.h index 4fe9e4dccfb8..029546149fdb 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -165,6 +165,8 @@ int profil __P((char *, size_t, vm_offset_t, int)); ssize_t pwrite __P((int, const void *, size_t, off_t)); int rcmd __P((char **, int, const char *, const char *, const char *, int *)); +int rcmd_af __P((char **, int, const char *, + const char *, const char *, int *, int)); char *re_comp __P((const char *)); int re_exec __P((const char *)); int readlink __P((const char *, char *, int)); diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index 87ac304c2a1f..cde2734fed18 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -58,7 +58,9 @@ MLINKS+=if_indextoname.3 if_nametoindex.3 if_indextoname.3 if_nameindex.3 \ if_indextoname.3 if_freenameindex.3 MLINKS+=linkaddr.3 link_addr.3 linkaddr.3 link_ntoa.3 #MLINKS+=ns.3 ns_addr.3 ns.3 ns_ntoa.3 -MLINKS+=rcmd.3 iruserok.3 rcmd.3 rresvport.3 rcmd.3 ruserok.3 +MLINKS+=rcmd.3 iruserok.3 rcmd.3 rresvport.3 rcmd.3 ruserok.3 \ + rcmd.3 iruserok_af.3 rcmd.3 rresvport_af.3 rcmd.3 ruserok_af.3 \ + rcmd.3 rcmd_af.3 MLINKS+=resolver.3 dn_comp.3 resolver.3 dn_expand.3 resolver.3 res_init.3 \ resolver.3 res_mkquery.3 resolver.3 res_query.3 \ resolver.3 res_search.3 resolver.3 res_send.3 diff --git a/lib/libc/net/rcmd.3 b/lib/libc/net/rcmd.3 index b00f94a3cd72..ed6478bd4600 100644 --- a/lib/libc/net/rcmd.3 +++ b/lib/libc/net/rcmd.3 @@ -40,6 +40,7 @@ .Nm rresvport , .Nm iruserok , .Nm ruserok , +.Nm rcmd_af , .Nm rresvport_af , .Nm iruserok_af , .Nm ruserok_af @@ -55,7 +56,9 @@ .Ft int .Fn ruserok "const char *rhost" "int superuser" "const char *ruser" "const char *luser" .Ft int -.Fn rresvport_af "int *port" "int family" +.Fn rcmd_af "char **ahost" "int inport" "const char *locuser" "const char *remuser" "const char *cmd" "int *fd2p" "int af" +.Ft int +.Fn rresvport_af "int *port" "int af" .Ft int .Fn iruserok_af "void *raddr" "int superuser" "const char *ruser" "const char *luser" "int af" .Ft int @@ -184,17 +187,26 @@ It requires trusting the local DNS at most, while the function requires trusting the entire DNS, which can be spoofed. .Pp Functions with ``_af'' suffix, i.e. +.Fn rcmd_af , .Fn rresvport_af , -.Fn iruserok_af and +.Fn iruserok_af +and .Fn ruserok_af , works just as same as functions without ``_af'', and is capable of handling both IPv6 port and IPv4 port. +Functions without +.Dq Li _af +works for IPv4 only. To switch address family, .Fa af argument must be filled with -.Dv AF_INET +.Dv AF_INET , or .Dv AF_INET6 . +For +.Fn rcmd_af , +.Dv PF_UNSPEC +is also allowed. .Sh DIAGNOSTICS The .Fn rcmd @@ -230,6 +242,9 @@ functions appeared in .Fn rresvport_af appeared in RFC2292, and implemented by WIDE project for Hydrangea IPv6 protocol stack kit. +.Fn rcmd_af +appeared in draft-ietf-ipngwg-rfc2292bis-01.txt, +and implemented by WIDE/KAME IPv6 protocol stack kit. .Fn iruserok_af and .Fn rusreok_af diff --git a/lib/libc/net/rcmd.c b/lib/libc/net/rcmd.c index a1416ed79da2..8130410ff45b 100644 --- a/lib/libc/net/rcmd.c +++ b/lib/libc/net/rcmd.c @@ -79,6 +79,17 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p) u_short rport; const char *locuser, *remuser, *cmd; int *fd2p; +{ + return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET); +} + +int +rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af) + char **ahost; + u_short rport; + const char *locuser, *remuser, *cmd; + int *fd2p; + int af; { struct addrinfo hints, *res, *ai; struct sockaddr_storage from; @@ -94,7 +105,7 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p) memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; - hints.ai_family = AF_UNSPEC; + hints.ai_family = af; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; (void)snprintf(num, sizeof(num), "%d", ntohs(rport)); diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index f0e9fdac34d1..e79b4add8d05 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -5,6 +5,7 @@ LIB= util SHLIB_MAJOR= 2 SHLIB_MINOR= 2 CFLAGS+=-Wall -DLIBC_SCCS -I${.CURDIR} -I${.CURDIR}/../../sys +CFLAGS+=-DINET6 SRCS= login.c login_tty.c logout.c logwtmp.c pty.c setproctitle.c \ login_cap.c login_class.c login_auth.c login_times.c login_ok.c \ _secure_path.c uucplock.c property.c auth.c realhostname.c fparseln.c \ @@ -13,7 +14,7 @@ INCS= libutil.h login_cap.h MAN3+= login.3 login_auth.3 login_tty.3 logout.3 logwtmp.3 pty.3 \ setproctitle.3 login_cap.3 login_class.3 login_times.3 login_ok.3 \ _secure_path.3 uucplock.3 property.3 auth.3 realhostname.3 \ - trimdomain.3 fparseln.3 + realhostname_sa.3 trimdomain.3 fparseln.3 MAN5+= login.conf.5 auth.conf.5 MLINKS+= property.3 properties_read.3 property.3 properties_free.3 MLINKS+= property.3 property_find.3 diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h index 492370043f7a..927a384fa435 100644 --- a/lib/libutil/libutil.h +++ b/lib/libutil/libutil.h @@ -60,6 +60,9 @@ void properties_free __P((properties list)); char *property_find __P((properties list, const char *name)); char *auth_getval __P((const char *name)); int realhostname __P((char *host, size_t hsize, const struct in_addr *ip)); +struct sockaddr; +int realhostname_sa __P((char *host, size_t hsize, struct sockaddr *addr, + int addrlen)); #ifdef _STDIO_H_ /* avoid adding new includes */ char *fparseln __P((FILE *, size_t *, size_t *, const char[3], int)); #endif diff --git a/lib/libutil/realhostname.3 b/lib/libutil/realhostname.3 index 1f2e6a9b8f6b..acf370b9d168 100644 --- a/lib/libutil/realhostname.3 +++ b/lib/libutil/realhostname.3 @@ -103,4 +103,5 @@ now contains the numeric value of .Sh SEE ALSO .Xr gethostbyaddr 3 , .Xr gethostbyname 3 , -.Xr inet_ntoa 3 +.Xr inet_ntoa 3 , +.Xr realhostname_sa 3 diff --git a/lib/libutil/realhostname.c b/lib/libutil/realhostname.c index fa7f4b1a9805..7236890dab0b 100644 --- a/lib/libutil/realhostname.c +++ b/lib/libutil/realhostname.c @@ -38,6 +38,17 @@ #include "libutil.h" +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + +struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; +}; + int realhostname(char *host, size_t hsize, const struct in_addr *ip) { @@ -71,3 +82,87 @@ realhostname(char *host, size_t hsize, const struct in_addr *ip) return result; } + +int +realhostname_sa(char *host, size_t hsize, struct sockaddr *addr, int addrlen) +{ + int result, error; + + result = HOSTNAME_INVALIDADDR; + + error = getnameinfo(addr, addrlen, host, hsize, NULL, 0, 0); + if (error == NULL) { + struct addrinfo hints, *res, *ores; + struct sockaddr *sa; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + + error = getaddrinfo(host, NULL, &hints, &res); + if (error) { + result = HOSTNAME_INVALIDNAME; + goto numeric; + } else for (ores = res; ; res = res->ai_next) { + if (res == NULL) { + freeaddrinfo(ores); + result = HOSTNAME_INCORRECTNAME; + goto numeric; + } + sa = res->ai_addr; + if (sa == NULL) { + freeaddrinfo(ores); + result = HOSTNAME_INCORRECTNAME; + goto numeric; + } + if (sa->sa_len == addrlen && + sa->sa_family == addr->sa_family) { + u_int16_t port; + + port = ((struct sockinet *)addr)->si_port; + ((struct sockinet *)addr)->si_port = 0; + if (!memcmp(sa, addr, sa->sa_len)) { + strncpy(host, res->ai_canonname, + hsize); + result = HOSTNAME_FOUND; + ((struct sockinet *)addr)->si_port = + port; + break; + } + ((struct sockinet *)addr)->si_port = port; + } +#ifdef INET6 + /* + * XXX IPv4 mapped IPv6 addr consideraton, + * specified in rfc2373. + */ + if (sa->sa_family == AF_INET && + addr->sa_family == AF_INET6) { + struct in_addr *in; + struct in6_addr *in6; + + in = &((struct sockaddr_in *)sa)->sin_addr; + in6 = &((struct sockaddr_in6 *)addr)->sin6_addr; + if (IN6_IS_ADDR_V4MAPPED(in6) && + !memcmp(&in6->s6_addr[12], in, + sizeof(*in))) { + strncpy(host, res->ai_canonname, + hsize); + result = HOSTNAME_FOUND; + break; + } + } +#endif + } + freeaddrinfo(ores); + } else { + numeric: + getnameinfo(addr, addrlen, host, hsize, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + /* XXX: do 'error' check */ + } + + return result; +} + + diff --git a/lib/libutil/realhostname_sa.3 b/lib/libutil/realhostname_sa.3 new file mode 100644 index 000000000000..6fef960a24d8 --- /dev/null +++ b/lib/libutil/realhostname_sa.3 @@ -0,0 +1,135 @@ +.\" Copyright (C) 1995, 1996, 1997, 1998, 1999, and 2000 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project 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 PROJECT 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 PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" Copyright (c) 1999 Brian Somers +.\" 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$ +.\" +.Dd January 11, 2000 +.Os +.Dt REALHOSTNAME_SA 3 +.Sh NAME +.Nm realhostname_sa +.Nd "convert an struct sockaddr to the real host name" +.Sh SYNOPSIS +.Fd #include +.Fd #include +.Fd #include +.Ft int +.Fn realhostname_sa "char *host" "size_t hsize" "struct sockaddr *addr" \ +"int addrlen" +.Pp +Link with +.Va -lutil +on the +.Xr cc 1 +command line. +.Sh DESCRIPTION +The function +.Fn realhostname_sa +converts +.Ar addr +to the corresponding host name. This is done by resolving +.Ar addr +to a host name and then ensuring that the host name resolves +back to +.Ar addr . +.Pp +.Ar host +must point to a buffer of at least +.Ar hsize +bytes, and will always be written to by this function. +.Pp +If the name resolution doesn't work both ways or if the host name is longer +than +.Ar hsize +bytes, +.Xr getnameinfo 3 +with NI_NUMERICHOST specified, is used to convert +.Ar addr +to an ASCII form. +.Pp +If the string written to +.Ar host +is +.Ar hsize +bytes long, +.Ar host +will not be NUL terminated. +.Sh RETURN VALUES +.Fn realhostname_sa +will return one of the following constants which are defined in +.Pa libutil.h : +.Pp +.Bl -tag -width XXX -offset XXX +.It Li HOSTNAME_FOUND +A valid host name was found. +.It Li HOSTNAME_INCORRECTNAME +A host name was found, but it did not resolve back to the passed +.Ar ip . +.Ar host +now contains the numeric value of +.Ar ip . +.It Li HOSTNAME_INVALIDADDR +.Ar ip +could not be resolved. +.Ar host +now contains the numeric value of +.Ar ip . +.It Li HOSTNAME_INVALIDNAME +A host name was found, but it could not be resolved back to any ip number. +.Ar host +now contains the numeric value of +.Ar ip . +.El +.Sh SEE ALSO +.Xr getaddrinfo 3 , +.Xr getnameinfo 3 , +.Xr realhostname 3 + diff --git a/libexec/rlogind/Makefile b/libexec/rlogind/Makefile index bae4a4b542ed..f22df18a1bf8 100644 --- a/libexec/rlogind/Makefile +++ b/libexec/rlogind/Makefile @@ -6,7 +6,7 @@ SRCS= rlogind.c MAN8= rlogind.8 DPADD= ${LIBUTIL} LDADD= -lutil -CFLAGS+= -Wall +CFLAGS+= -Wall -DINET6 .if defined(NOPAM) CFLAGS+= -DNO_PAM diff --git a/libexec/rlogind/rlogind.8 b/libexec/rlogind/rlogind.8 index 14d7a93de5f0..969fb084496e 100644 --- a/libexec/rlogind/rlogind.8 +++ b/libexec/rlogind/rlogind.8 @@ -204,3 +204,5 @@ The .Nm command appeared in .Bx 4.2 . +.Pp +IPv6 support was added by WIDE/KAME project. \ No newline at end of file diff --git a/libexec/rlogind/rlogind.c b/libexec/rlogind/rlogind.c index 111a56bff5f7..817f6c857ae5 100644 --- a/libexec/rlogind/rlogind.c +++ b/libexec/rlogind/rlogind.c @@ -91,6 +91,11 @@ static const char rcsid[] = #define ARGSTR "Dalnx" +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + char *env[2]; #define NMAX 30 char lusername[NMAX+1], rusername[NMAX+1]; @@ -102,12 +107,25 @@ int no_delay; struct passwd *pwd; -void doit __P((int, struct sockaddr_in *)); +union sockunion { + struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; + } su_si; + struct sockaddr_in su_sin; + struct sockaddr_in6 su_sin6; +}; +#define su_len su_si.si_len +#define su_family su_si.si_family +#define su_port su_si.si_port + +void doit __P((int, union sockunion *)); int control __P((int, char *, int)); void protocol __P((int, int)); void cleanup __P((int)); void fatal __P((int, char *, int)); -int do_rlogin __P((struct sockaddr_in *)); +int do_rlogin __P((union sockunion *)); void getstr __P((char *, int, char *)); void setup_term __P((int)); int do_krb_login __P((struct sockaddr_in *)); @@ -123,7 +141,7 @@ main(argc, argv) char *argv[]; { extern int __check_rhosts_file; - struct sockaddr_in from; + union sockunion from; int ch, fromlen, on; openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); @@ -168,9 +186,12 @@ main(argc, argv) if (no_delay && setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); + if (from.su_family == AF_INET) + { on = IPTOS_LOWDELAY; if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } doit(0, &from); return 0; @@ -187,11 +208,12 @@ struct winsize win = { 0, 0, 0, 0 }; void doit(f, fromp) int f; - struct sockaddr_in *fromp; + union sockunion *fromp; { int master, pid, on = 1; int authenticated = 0; - char hostname[MAXHOSTNAMELEN]; + char hostname[2 * MAXHOSTNAMELEN + 1]; + char nameinfo[2 * INET6_ADDRSTRLEN + 1]; char c; alarm(60); @@ -201,20 +223,33 @@ doit(f, fromp) exit(1); alarm(0); - fromp->sin_port = ntohs((u_short)fromp->sin_port); - realhostname(hostname, sizeof(hostname) - 1, &fromp->sin_addr); + + realhostname_sa(hostname, sizeof(hostname) - 1, + (struct sockaddr *)fromp, fromp->su_len); + /* error check ? */ + fromp->su_port = ntohs((u_short)fromp->su_port); hostname[sizeof(hostname) - 1] = '\0'; { - if (fromp->sin_family != AF_INET || - fromp->sin_port >= IPPORT_RESERVED || - fromp->sin_port < IPPORT_RESERVED/2) { + if ((fromp->su_family != AF_INET && +#ifdef INET6 + fromp->su_family != AF_INET6 +#endif + ) || + fromp->su_port >= IPPORT_RESERVED || + fromp->su_port < IPPORT_RESERVED/2) { + getnameinfo((struct sockaddr *)fromp, + fromp->su_len, + nameinfo, sizeof(nameinfo), NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + /* error check ? */ syslog(LOG_NOTICE, "Connection from %s on illegal port", - inet_ntoa(fromp->sin_addr)); + nameinfo); fatal(f, "Permission denied", 0); } #ifdef IP_OPTIONS - { + if (fromp->su_family == AF_INET) + { u_char optbuf[BUFSIZ/3]; int optsize = sizeof(optbuf), ipproto, i; struct protoent *ip; @@ -230,7 +265,7 @@ doit(f, fromp) if (c == IPOPT_LSRR || c == IPOPT_SSRR) { syslog(LOG_NOTICE, "Connection refused from %s with IP option %s", - inet_ntoa(fromp->sin_addr), + inet_ntoa(fromp->su_sin.sin_addr), c == IPOPT_LSRR ? "LSRR" : "SSRR"); exit(1); } @@ -239,7 +274,7 @@ doit(f, fromp) i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; } } - } + } #endif if (do_rlogin(fromp) == 0) authenticated++; @@ -533,9 +568,11 @@ fatal(f, msg, syserr) int do_rlogin(dest) - struct sockaddr_in *dest; + union sockunion *dest; { int retval; + int af; + char *addr; getstr(rusername, sizeof(rusername), "remuser too long"); getstr(lusername, sizeof(lusername), "locuser too long"); @@ -559,8 +596,22 @@ do_rlogin(dest) if (pwd == NULL) return (-1); /* XXX why don't we syslog() failure? */ - return (iruserok(dest->sin_addr.s_addr, pwd->pw_uid == 0, - rusername, lusername)); + + af = dest->su_family; + switch (af) { + case AF_INET: + addr = (char *)&dest->su_sin.sin_addr; + break; +#ifdef INET6 + case AF_INET6: + addr = (char *)&dest->su_sin6.sin6_addr; + break; +#endif + default: + return -1; /*EAFNOSUPPORT*/ + } + + return (iruserok_af(addr, pwd->pw_uid == 0, rusername, lusername, af)); } void diff --git a/libexec/rshd/Makefile b/libexec/rshd/Makefile index 20051220d5d4..ca0d15571944 100644 --- a/libexec/rshd/Makefile +++ b/libexec/rshd/Makefile @@ -19,6 +19,9 @@ CFLAGS+=-DLOGIN_CAP -Wall DPADD+= ${LIBUTIL} LDADD+= -lutil +# IPv6 support +CFLAGS+= -DINET6 + .include .PATH: ${.CURDIR}/../rlogind diff --git a/libexec/rshd/rshd.8 b/libexec/rshd/rshd.8 index 80cdbc121b7e..cba4482762eb 100644 --- a/libexec/rshd/rshd.8 +++ b/libexec/rshd/rshd.8 @@ -250,3 +250,5 @@ A facility to allow all data exchanges to be encrypted should be present. .Pp A more extensible protocol (such as Telnet) should be used. +.Sh HISTORY +IPv6 support was added by WIDE/KAME project. diff --git a/libexec/rshd/rshd.c b/libexec/rshd/rshd.c index 8533644663f7..54fa4a69d0fa 100644 --- a/libexec/rshd/rshd.c +++ b/libexec/rshd/rshd.c @@ -80,6 +80,11 @@ static const char rcsid[] = #include #endif +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + int keepalive = 1; int log_success; /* If TRUE, log all successful accesses */ int sent_null; @@ -88,7 +93,20 @@ int no_delay; int doencrypt = 0; #endif -void doit __P((struct sockaddr_in *)); +union sockunion { + struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; + } su_si; + struct sockaddr_in su_sin; + struct sockaddr_in6 su_sin6; +}; +#define su_len su_si.si_len +#define su_family su_si.si_family +#define su_port su_si.si_port + +void doit __P((union sockunion *)); void error __P((const char *, ...)); void getstr __P((char *, int, char *)); int local_domain __P((char *)); @@ -109,7 +127,7 @@ main(argc, argv) extern int __check_rhosts_file; struct linger linger; int ch, on = 1, fromlen; - struct sockaddr_in from; + struct sockaddr_storage from; openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON); @@ -169,7 +187,7 @@ main(argc, argv) if (no_delay && setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); - doit(&from); + doit((union sockunion *)&from); /* NOTREACHED */ return(0); } @@ -184,7 +202,7 @@ char **environ; void doit(fromp) - struct sockaddr_in *fromp; + union sockunion *fromp; { extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ struct passwd *pwd; @@ -195,7 +213,9 @@ doit(fromp) char *errorstr; char *cp, sig, buf[BUFSIZ]; char cmdbuf[NCARGS+1], locuser[16], remuser[16]; - char fromhost[MAXHOSTNAMELEN]; + char fromhost[2 * MAXHOSTNAMELEN + 1]; + char numericname[INET6_ADDRSTRLEN]; + int af = fromp->su_family, err; int retval; #ifdef CRYPT int rc; @@ -216,14 +236,21 @@ doit(fromp) } } #endif - fromp->sin_port = ntohs((u_short)fromp->sin_port); - if (fromp->sin_family != AF_INET) { - syslog(LOG_ERR, "malformed \"from\" address (af %d)", - fromp->sin_family); + fromp->su_port = ntohs((u_short)fromp->su_port); + if (af != AF_INET +#ifdef INET6 + && af != AF_INET6 +#endif + ) { + syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", af); exit(1); } + err = getnameinfo((struct sockaddr *)fromp, fromp->su_len, numericname, + sizeof(numericname), NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + /* XXX: do 'err' check */ #ifdef IP_OPTIONS - { + if (af == AF_INET) { u_char optbuf[BUFSIZ/3]; int optsize = sizeof(optbuf), ipproto, i; struct protoent *ip; @@ -239,7 +266,7 @@ doit(fromp) if (c == IPOPT_LSRR || c == IPOPT_SSRR) { syslog(LOG_NOTICE, "connection refused from %s with IP option %s", - inet_ntoa(fromp->sin_addr), + numericname, c == IPOPT_LSRR ? "LSRR" : "SSRR"); exit(1); } @@ -251,12 +278,12 @@ doit(fromp) } #endif - if (fromp->sin_port >= IPPORT_RESERVED || - fromp->sin_port < IPPORT_RESERVED/2) { + if (fromp->su_port >= IPPORT_RESERVED || + fromp->su_port < IPPORT_RESERVED/2) { syslog(LOG_NOTICE|LOG_AUTH, "connection from %s on illegal port %u", - inet_ntoa(fromp->sin_addr), - fromp->sin_port); + numericname, + fromp->su_port); exit(1); } @@ -279,7 +306,7 @@ doit(fromp) (void) alarm(0); if (port != 0) { int lport = IPPORT_RESERVED - 1; - s = rresvport(&lport); + s = rresvport_af(&lport, af); if (s < 0) { syslog(LOG_ERR, "can't get stderr port: %m"); exit(1); @@ -288,11 +315,11 @@ doit(fromp) port < IPPORT_RESERVED/2) { syslog(LOG_NOTICE|LOG_AUTH, "2nd socket from %s on unreserved port %u", - inet_ntoa(fromp->sin_addr), + numericname, port); exit(1); } - fromp->sin_port = htons(port); + fromp->su_port = htons(port); if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) { syslog(LOG_INFO, "connect second port %d: %m", port); exit(1); @@ -300,11 +327,13 @@ doit(fromp) } errorstr = NULL; - realhostname(fromhost, sizeof(fromhost) - 1, &fromp->sin_addr); + realhostname_sa(fromhost, sizeof(fromhost) - 1, + (struct sockaddr *)fromp, + fromp->su_len); fromhost[sizeof(fromhost) - 1] = '\0'; #ifdef CRYPT - if (doencrypt) { + if (doencrypt && af == AF_INET) { struct sockaddr_in local_addr; rc = sizeof(local_addr); if (getsockname(0, (struct sockaddr *)&local_addr, @@ -379,8 +408,14 @@ doit(fromp) if (errorstr || (pwd->pw_expire && time(NULL) >= pwd->pw_expire) || (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && - iruserok(fromp->sin_addr.s_addr, pwd->pw_uid == 0, - remuser, locuser) < 0)) { + iruserok_af( +#ifdef INET6 + (af == AF_INET6) + ? (void *)&fromp->su_sin6.sin6_addr : +#endif + (void *)&fromp->su_sin.sin_addr, + pwd->pw_uid == 0, + remuser, locuser, af) < 0)) { if (__rcmd_errstr) syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", @@ -402,10 +437,10 @@ doit(fromp) exit(1); } #ifdef LOGIN_CAP - if (lc != NULL) { + if (lc != NULL && fromp->su_family == AF_INET) { /*XXX*/ char remote_ip[MAXHOSTNAMELEN]; - strncpy(remote_ip, inet_ntoa(fromp->sin_addr), + strncpy(remote_ip, numericname, sizeof(remote_ip) - 1); remote_ip[sizeof(remote_ip) - 1] = 0; if (!auth_hostok(lc, fromhost, remote_ip)) { diff --git a/libexec/telnetd/Makefile b/libexec/telnetd/Makefile index 068087a62c65..7766276ca15f 100644 --- a/libexec/telnetd/Makefile +++ b/libexec/telnetd/Makefile @@ -6,6 +6,7 @@ CFLAGS+=-DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS #CFLAGS+=-DKLUDGELINEMODE CFLAGS+=-DOLD_ENVIRON -DENV_HACK CFLAGS+=-I${.CURDIR}/../../lib +CFLAGS+=-DINET6 SRCS= global.c slc.c state.c sys_term.c telnetd.c \ termstat.c utility.c DPADD= ${LIBUTIL} ${LIBTERMCAP} ${LIBTELNET} diff --git a/libexec/telnetd/telnetd.8 b/libexec/telnetd/telnetd.8 index a2973ef611f3..06e98f946deb 100644 --- a/libexec/telnetd/telnetd.8 +++ b/libexec/telnetd/telnetd.8 @@ -623,3 +623,5 @@ never sends .Tn TELNET .Dv IAC GA (go ahead) commands. +.Sh HISTORY +IPv6 support was added by WIDE/KAME project. \ No newline at end of file diff --git a/libexec/telnetd/telnetd.c b/libexec/telnetd/telnetd.c index 5141c2aecf42..57a972aaf10e 100644 --- a/libexec/telnetd/telnetd.c +++ b/libexec/telnetd/telnetd.c @@ -71,6 +71,12 @@ static const char rcsid[] = #include #include # endif /* SO_SEC_MULTI */ + +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + int secflag; char tty_dev[16]; struct secdev dv; @@ -137,7 +143,7 @@ int debug = 0; int keepalive = 1; char *altlogin; -void doit __P((struct sockaddr_in *)); +void doit __P((struct sockaddr *)); int terminaltypeok __P((char *)); void startslave __P((char *, int, char *)); extern void usage P((void)); @@ -149,6 +155,7 @@ extern void usage P((void)); */ char valid_opts[] = { 'd', ':', 'h', 'k', 'n', 'p', ':', 'S', ':', 'u', ':', 'U', + '4', '6', #ifdef AUTHENTICATION 'a', ':', 'X', ':', #endif @@ -173,11 +180,13 @@ char valid_opts[] = { '\0' }; - int +int family = AF_INET; + +int main(argc, argv) char *argv[]; { - struct sockaddr_in from; + struct sockaddr_storage from; int on = 1, fromlen; register int ch; #if defined(IPPROTO_IP) && defined(IP_TOS) @@ -381,6 +390,16 @@ main(argc, argv) break; #endif /* AUTHENTICATION */ + case '4': + family = AF_INET; + break; + +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif + default: warnx("%c: unknown option", ch); /* FALLTHROUGH */ @@ -394,43 +413,41 @@ main(argc, argv) argv += optind; if (debug) { - int s, ns, foo; - struct servent *sp; - static struct sockaddr_in sin = { AF_INET }; + int s, ns, foo, error; + char *service = "telnet"; + struct addrinfo hints, *res; if (argc > 1) { usage(); /* NOT REACHED */ - } else if (argc == 1) { - if ((sp = getservbyname(*argv, "tcp"))) { - sin.sin_port = sp->s_port; - } else { - sin.sin_port = atoi(*argv); - if ((int)sin.sin_port <= 0) { - warnx("%s: bad port #", *argv); - usage(); - /* NOT REACHED */ - } - sin.sin_port = htons((u_short)sin.sin_port); - } - } else { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) - errx(1, "tcp/telnet: unknown service"); - sin.sin_port = sp->s_port; + } else if (argc == 1) + service = *argv; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(NULL, service, &hints, &res); + + if (error) { + errx(1, "tcp/%s: %s\n", service, gai_strerror(error)); + if (error == EAI_SYSTEM) + errx(1, "tcp/%s: %s\n", service, strerror(errno)); + usage(); } - s = socket(AF_INET, SOCK_STREAM, 0); + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s < 0) err(1, "socket"); (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); - if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) err(1, "bind"); if (listen(s, 1) < 0) err(1, "listen"); - foo = sizeof sin; - ns = accept(s, (struct sockaddr *)&sin, &foo); + foo = res->ai_addrlen; + ns = accept(s, res->ai_addr, &foo); if (ns < 0) err(1, "accept"); (void) dup2(ns, 0); @@ -512,7 +529,7 @@ main(argc, argv) } #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (from.ss_family == AF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) @@ -528,7 +545,7 @@ main(argc, argv) } #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ net = 0; - doit(&from); + doit((struct sockaddr *)&from); /* NOTREACHED */ return(0); } /* end of main */ @@ -773,8 +790,9 @@ char user_name[256]; */ void doit(who) - struct sockaddr_in *who; + struct sockaddr *who; { + int err; int ptynum; /* @@ -817,16 +835,18 @@ doit(who) #endif /* _SC_CRAY_SECURE_SYS */ /* get name of connected client */ - if (realhostname(remote_hostname, sizeof(remote_hostname) - 1, - &who->sin_addr) == HOSTNAME_INVALIDADDR && registerd_host_only) + if (realhostname_sa(remote_hostname, sizeof(remote_hostname) - 1, + who, who->sa_len) == HOSTNAME_INVALIDADDR && registerd_host_only) fatal(net, "Couldn't resolve your address into a host name.\r\n\ Please contact your net administrator"); remote_hostname[sizeof(remote_hostname) - 1] = '\0'; trimdomain(remote_hostname, UT_HOSTSIZE); if (!isdigit(remote_hostname[0]) && strlen(remote_hostname) > utmp_len) - strncpy(remote_hostname, inet_ntoa(who->sin_addr), - sizeof(remote_hostname) - 1); + err = getnameinfo(who, who->sa_len, remote_hostname, + sizeof(remote_hostname), NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + /* XXX: do 'err' check */ (void) gethostname(host_name, sizeof(host_name) - 1); host_name[sizeof(host_name) - 1] = '\0'; diff --git a/usr.bin/rlogin/rlogin.1 b/usr.bin/rlogin/rlogin.1 index e2fa76ce0847..3e3a749bf78e 100644 --- a/usr.bin/rlogin/rlogin.1 +++ b/usr.bin/rlogin/rlogin.1 @@ -226,6 +226,8 @@ The .Nm command appeared in .Bx 4.2 . +.Pp +IPv6 support was added by WIDE/KAME project. .Sh BUGS .Nm Rlogin will be replaced by diff --git a/usr.bin/rlogin/rlogin.c b/usr.bin/rlogin/rlogin.c index 0df97a34bcf0..4f2cb6bfb112 100644 --- a/usr.bin/rlogin/rlogin.c +++ b/usr.bin/rlogin/rlogin.c @@ -154,6 +154,8 @@ main(argc, argv) #ifdef KERBEROS char *k; #endif + struct sockaddr_storage ss; + int sslen; argoff = dflag = Dflag = 0; one = 1; @@ -331,10 +333,11 @@ main(argc, argv) if (doencrypt) errx(1, "the -x flag requires Kerberos authentication"); #endif /* CRYPT */ - rem = rcmd(&host, sp->s_port, localname, user, term, 0); + rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, + PF_UNSPEC); } #else - rem = rcmd(&host, sp->s_port, localname, user, term, 0); + rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, PF_UNSPEC); #endif /* KERBEROS */ if (rem < 0) @@ -347,9 +350,16 @@ main(argc, argv) setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) warn("setsockopt NODELAY (ignored)"); + sslen = sizeof(ss); one = IPTOS_LOWDELAY; - if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0) - warn("setsockopt TOS (ignored)"); + if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 && + ss.ss_family == AF_INET) { + if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, + sizeof(int)) < 0) + warn("setsockopt TOS (ignored)"); + } else + if (ss.ss_family == AF_INET) + warn("setsockopt getsockname failed"); (void)setuid(uid); doit(omask); diff --git a/usr.bin/rsh/rsh.c b/usr.bin/rsh/rsh.c index deea9d94990d..aecb81888e7c 100644 --- a/usr.bin/rsh/rsh.c +++ b/usr.bin/rsh/rsh.c @@ -269,10 +269,12 @@ main(argc, argv) } else { if (doencrypt) errx(1, "the -x flag requires Kerberos authentication"); - rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2); + rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args, + &rfd2, PF_UNSPEC); } #else - rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2); + rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args, &rfd2, + PF_UNSPEC); #endif if (rem < 0) diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile index 0df97554b47c..738bbed95283 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 diff --git a/usr.sbin/inetd/builtins.c b/usr.sbin/inetd/builtins.c index 6ba404c2a639..f05971fc0952 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 2a3b411ba83f..66584d67387c 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 33787536978d..06bad40e8f0d 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 #include @@ -130,10 +137,23 @@ static const char rcsid[] = #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 @@ -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 @@ getconfigent() 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 @@ getconfigent() 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 @@ getconfigent() } 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]; + struct sockaddr_storage ss; + char buf[80], pbuf[INET6_ADDRSTRLEN]; - 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); 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 ab78c734c54e..f956c1a8669e 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));