From 33841545909f4a4ee94aa148b3a9cbcdc1abb02a Mon Sep 17 00:00:00 2001 From: Hajimu UMEMOTO Date: Mon, 11 Jun 2001 12:39:29 +0000 Subject: Sync with recent KAME. This work was based on kame-20010528-freebsd43-snap.tgz and some critical problem after the snap was out were fixed. There are many many changes since last KAME merge. TODO: - The definitions of SADB_* in sys/net/pfkeyv2.h are still different from RFC2407/IANA assignment because of binary compatibility issue. It should be fixed under 5-CURRENT. - ip6po_m member of struct ip6_pktopts is no longer used. But, it is still there because of binary compatibility issue. It should be removed under 5-CURRENT. Reviewed by: itojun Obtained from: KAME MFC after: 3 weeks --- usr.sbin/faithd/Makefile | 2 +- usr.sbin/faithd/README | 50 ++++--- usr.sbin/faithd/faithd.8 | 151 +++++++++++++++----- usr.sbin/faithd/faithd.c | 191 ++++++++++++++++++------- usr.sbin/faithd/faithd.h | 11 +- usr.sbin/faithd/ftp.c | 5 +- usr.sbin/faithd/prefix.c | 360 +++++++++++++++++++++++++++++++++++++++++++++++ usr.sbin/faithd/prefix.h | 52 +++++++ usr.sbin/faithd/rsh.c | 2 +- usr.sbin/faithd/tcp.c | 9 +- 10 files changed, 713 insertions(+), 120 deletions(-) create mode 100644 usr.sbin/faithd/prefix.c create mode 100644 usr.sbin/faithd/prefix.h (limited to 'usr.sbin/faithd') diff --git a/usr.sbin/faithd/Makefile b/usr.sbin/faithd/Makefile index c63b09793712..42da8df1ba27 100644 --- a/usr.sbin/faithd/Makefile +++ b/usr.sbin/faithd/Makefile @@ -14,7 +14,7 @@ # $FreeBSD$ PROG= faithd -SRCS= faithd.c tcp.c ftp.c rsh.c +SRCS= faithd.c tcp.c ftp.c rsh.c prefix.c MAN= faithd.8 #CFLAGS+= -DFAITH4 CFLAGS+= -Wall diff --git a/usr.sbin/faithd/README b/usr.sbin/faithd/README index 4808b4af2544..2ad0592a193c 100644 --- a/usr.sbin/faithd/README +++ b/usr.sbin/faithd/README @@ -1,7 +1,7 @@ Configuring FAITH IPv6-to-IPv4 TCP relay Kazu Yamamoto and Jun-ichiro itojun Hagino -$KAME: README,v 1.4 2000/05/31 03:16:14 itojun Exp $ +$KAME: README,v 1.7 2001/04/25 11:25:19 itojun Exp $ $FreeBSD$ Introduction @@ -27,13 +27,13 @@ invoked per each TCP services (TCP port number). clients IPv6 node "src" | You will have to allocate an IPv6 address prefix to map IPv4 addresses into. -The following description uses 3ffe:0501:1234:ffff:: as example. +The following description uses 3ffe:0501:ffff:0000:: as example. Please use a prefix which belongs to your site. FAITH will make it possible to make a IPv6 TCP connection From IPv6 node "src", toward IPv4 node "dest", by specifying FAITH-mapped address -3ffe:0501:1234:ffff::123.4.5.6 -(which is, 3ffe:0501:1234:ffff:0000:0000:7b04:0506). -The address mapping can be performed by hand:-), by speical nameserver on +3ffe:0501:ffff:0000::123.4.5.6 +(which is, 3ffe:0501:ffff:0000:0000:0000:7b04:0506). +The address mapping can be performed by hand:-), by special nameserver on the network, or by special resolver on the source node. @@ -41,7 +41,7 @@ Setup ===== The following example assumes: -- You have assigned 3ffe:0501:1234:ffff:: as FAITH adderss prefix. +- You have assigned 3ffe:0501:ffff:0000:: as FAITH adderss prefix. - You are willing to provide IPv6-to IPv4 TCP relay for telnet. <> @@ -57,12 +57,12 @@ The following example assumes: (3) Route packets toward FAITH prefix into "faith0" interface. # ifconfig faith0 up - # route add -inet6 3ffe:0501:1234:ffff:: -prefixlen 64 \ - fe80::xxxx:yyyy:zzzz:wwww%faith0 + # route add -inet6 3ffe:0501:ffff:0000:: -prefixlen 64 ::1 + # route change -inet6 3ffe:0501:ffff:0000:: -prefixlen 64 -ifp faith0 (4) Execute "faithd" by root as follows: - # faithd telnet /usr/local/v6/libexec/telnetd telnetd + # faithd telnet /usr/libexec/telnetd telnetd 1st argument is a service name you are willing to provide TCP relay. (it can be specified either by number "23" or by string "telnet") @@ -73,11 +73,14 @@ The following example assumes: More examples: - # faithd login /usr/local/v6/libexec/rlogin rlogind - # faithd shell /usr/local/v6/libexec/rshd rshd - # faithd ftpd /usr/local/v6/libexec/ftpd ftpd -l + # faithd login /usr/libexec/rlogin rlogind + # faithd shell /usr/libexec/rshd rshd + # faithd ftpd /usr/libexec/ftpd ftpd -l # faithd sshd +If inetd(8) on your platform have special support for faithd, it is possible +to setup faithd services via inetd(8). Consult manpage for details. + <> @@ -95,7 +98,7 @@ There are two ways to translate IPv4 address to IPv6 address: (5.b) Add an entry into /etc/hosts so that you can resolve hostname into faked IPv6 addrss. For example, add the following line for www.netbsd.org: - 3ffe:0501:1234:ffff::140.160.140.252 www.netbsd.org + 3ffe:0501:ffff:0000::140.160.140.252 www.netbsd.org <> @@ -107,18 +110,31 @@ in "/var/log/daemon". daemon.* /var/log/daemon +Access control +============== + +Since faithd implements TCP relaying service, it is critical to implement +proper access control to cope with malicious use. Bad guy may try to +use your relay router to circumvent access controls, or may try to +abuse your network (like sending SPAMs from IPv4 address that belong to you). +Install IPv6 packet filter directives that would reject traffic from +unwanted source. If you are using inetd-based setup, you may be able to +use access control mechanisms in inetd. + + Advanced configuration ====================== If you would like to restrict IPv4 destination for translation, you may want to do the following: - # route add -inet6 3ffe:0501:1234:ffff::123.0.0.0 -prefixlen 104 \ - -interface faith0 + # route add -inet6 3ffe:0501:ffff:0000::123.0.0.0 -prefixlen 104 ::1 + # route change -inet6 3ffe:0501:ffff:0000::123.0.0.0 -prefixlen 104 \ + -ifp faith0 By this way, you can restrict IPv4 destination to 123.0.0.0/8. -You may also want to reject packets toward 3ffe:0501:1234:ffff::/64 which -is not in 3ffe:0501:1234:ffff::123.0.0.0/104. This will be left as excerside +You may also want to reject packets toward 3ffe:0501:ffff:0000::/64 which +is not in 3ffe:0501:ffff:0000::123.0.0.0/104. This will be left as excerside for the reader. By doing this, you will be able to provide your IPv4 web server to outside diff --git a/usr.sbin/faithd/faithd.8 b/usr.sbin/faithd/faithd.8 index 6d552eb3ad56..ff0fa655a6fc 100644 --- a/usr.sbin/faithd/faithd.8 +++ b/usr.sbin/faithd/faithd.8 @@ -1,4 +1,4 @@ -.\" $KAME: faithd.8,v 1.12 2000/07/04 13:15:01 itojun Exp $ +.\" $KAME: faithd.8,v 1.30 2001/05/24 20:47:56 itojun Exp $ .\" .\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. .\" All rights reserved. @@ -38,7 +38,9 @@ .Sh SYNOPSIS .Nm .Op Fl dp -.Op Ar service Op Ar serverpath Op Ar serverargs +.Op Fl f Ar configfile +.Ar service +.Op Ar serverpath Op Ar serverargs .Sh DESCRIPTION .Nm provides IPv6-to-IPv4 TCP relay. @@ -96,6 +98,24 @@ address prefix, by using and .Xr sysctl 8 commands. +.Pp +.Nm +needs a special name-to-address translation logic, so that +hostnames gets resolved into special +.Tn IPv6 +address prefix. +For small-scale installation, use +.Xr hosts 5 . +For large-scale installation, it is useful to have +a DNS server with special address translation support. +An implementation called +.Nm totd +is available +at +.Pa http://www.vermicelli.pasta.cs.uit.no/ipv6/software.html . +Make sure you do not propagate translated DNS records to normal DNS cloud, +it is highly harmful. +.Pp .Ss Daemon mode When .Nm @@ -136,26 +156,14 @@ You can also specify .Ar serverargs for the arguments for the local daemon. .Pp -If -.Ar service -is not given, -.Li telnet -is assumed, and -.Nm -will relay TCP traffic on TCP port -.Li telnet . -With -.Ar service , -.Nm -will work as TCP relaying daemon for specified -.Ar service -as described above. -.Pp The following options are available: .Bl -tag -width indent .It Fl d Debugging information will be generated using .Xr syslog 3 . +.It Fl f Ar configfile +Specify a configuration file for access control. +See below. .It Fl p Use privileged TCP port number as source port, for IPv4 TCP connection toward final destination. @@ -200,7 +208,7 @@ is invoked via .Xr inetd 8 , .Nm will handle connection passed from standard input. -If it the connection endpoint is in the reserved IPv6 address prefix. +If the connection endpoint is in the reserved IPv6 address prefix, .Nm will relay the connection. Otherwise, @@ -223,6 +231,52 @@ The operation mode requires special support for .Nm in .Xr inetd 8 . +.Ss Access control +To prevent malicious accesses, +.Nm +implements a simple address-based access control. +With +.Pa /etc/faithd.conf +.Po +or +.Ar configfile +specified by +.Fl f +.Pc , +.Nm +will avoid relaying unwanted traffic. +The +.Pa faithd.conf +contains directives with the following format: +.Bl -bullet +.It +.Xo +.Ic Ar src/slen Li deny Ar dst/dlen +.Xc +.Pp +If the source address of a query matches +.Ar src/slen , +and the translated destination address matches +.Ar dst/dlen , +deny the connection. +.It +.Xo +.Ic Ar src/slen Li permit Ar dst/dlen +.Xc +.Pp +If the source address of a query matches +.Ar src/slen , +and the translated destination address matches +.Ar dst/dlen , +permit the connection. +.El +.Pp +The directives are evaluated in sequence, +and the first matching entry will be effective. +.Pp +With inetd mode, +traffic may be filtered by using access control functionality in +.Xr inetd 8 . .Sh EXAMPLES Before invoking .Nm , @@ -241,9 +295,8 @@ To translate .Li telnet service, and provide no local telnet service, invoke .Nm -as either of the following: +as follows: .Bd -literal -offset -# faithd # faithd telnet .Ed .Pp @@ -258,7 +311,7 @@ use the following command line: .Pp If you would like to pass extra arguments to the local daemon: .Bd -literal -offset -# faithd ftpd /usr/local/v6/libexec/ftpd ftpd -l +# faithd ftp /usr/local/v6/libexec/ftpd ftpd -l .Ed .Pp Here are some other examples. @@ -266,14 +319,15 @@ You may need .Fl p to translate rsh/rlogin services. .Bd -literal -offset -# faithd sshd +# faithd ssh # faithd login /usr/local/v6/libexec/rlogin rlogind # faithd shell /usr/local/v6/libexec/rshd rshd .Ed .Pp However, you should be careful when translating rlogin or rsh -connections. See -.Sx SECURITY NOTICE +connections. +See +.Sx SECURITY CONSIDERATIONS for more details. .Ss inetd mode samples Add the following lines into @@ -282,7 +336,7 @@ Syntax may vary depending upon your operating system. .Bd -literal -offset telnet stream tcp6/faith nowait root /usr/sbin/faithd telnetd ftp stream tcp6/faith nowait root /usr/sbin/faithd ftpd -l -ssh stream tcp6/faith nowait root /usr/sbin/faithd /usr/pkg/bin/sshd -i +ssh stream tcp6/faith nowait root /usr/sbin/faithd /usr/sbin/sshd -i .Ed .Pp .Xr inetd 8 @@ -298,6 +352,20 @@ Otherwise, .Nm will invoke service-specific daemon like .Xr telnetd 8 . +.Ss Access control samples +The following illustrates a simple +.Pa faithd.conf +setting. +.Bd -literal -offset +# permit anyone from 3ffe:501:ffff::/48 to use the translator, +# to connect to the following IPv4 destinations: +# - any location except 10.0.0.0/8 and 127.0.0.0/8. +# Permit no other connections. +# +3ffe:501:ffff::/48 deny 10.0.0.0/8 +3ffe:501:ffff::/48 deny 127.0.0.0/8 +3ffe:501:ffff::/48 permit 0.0.0.0/0 +.Ed .Sh RETURN VALUES .Nm exits with @@ -316,20 +384,34 @@ on error. .%A Kazu Yamamoto .%T "An IPv6-to-IPv4 transport relay translator" .%R internet draft -.%N draft-ietf-ngtrans-tcpudp-relay-01.txt +.%N draft-ietf-ngtrans-tcpudp-relay-04.txt .%O work in progress material .Re -.Sh SECURITY NOTICE +.\" +.Sh HISTORY +The +.Nm +command first appeared in WIDE Hydrangea IPv6 protocol stack kit. +.\" +.Pp +IPv6 and IPsec support based on the KAME Project (http://www.kame.net/) stack +was initially integrated into +.Fx 4.0 +.Sh SECURITY CONSIDERATIONS It is very insecure to use .Xr rhosts 5 and other IP-address based authentication, for connections relayed by .Nm .Pq and any other TCP relaying services . .Pp +Administrators are advised to limit accesses to .Nm -itself does not implement access controls, as -it intends to implement transparent TCP relay services. -Administrators are advised to filter packets based on IPv6 address. +using +.Pa faithd.conf , +or by using IPv6 packet filters. +It is to protect +.Nm +service from malicious parties and avoid theft of service/bandwidth. IPv6 destination address can be limited by carefully configuring routing entries that points to .Xr faith 4 , @@ -339,12 +421,3 @@ IPv6 source address needs to be filtered by using packet filters. Documents listed in .Sx SEE ALSO have more discussions on this topic. -.\" -.Sh HISTORY -The -.Nm -command first appeared in WIDE Hydrangea IPv6 protocol stack kit. -.Pp -IPv6 and IPsec support based on the KAME Project (http://www.kame.net/) stack -was initially integrated into -.Fx 4.0 diff --git a/usr.sbin/faithd/faithd.c b/usr.sbin/faithd/faithd.c index fddf4023cd14..2f02da16539e 100644 --- a/usr.sbin/faithd/faithd.c +++ b/usr.sbin/faithd/faithd.c @@ -1,4 +1,4 @@ -/* $KAME: faithd.c,v 1.20 2000/07/01 11:40:45 itojun Exp $ */ +/* $KAME: faithd.c,v 1.39 2001/04/25 11:20:42 itojun Exp $ */ /* * Copyright (C) 1997 and 1998 WIDE Project. @@ -47,6 +47,9 @@ #include #include #include +#ifdef __FreeBSD__ +#include +#endif #include #include @@ -83,6 +86,7 @@ #endif #include "faithd.h" +#include "prefix.h" char *serverpath = NULL; char *serverarg[MAXARGV + 1]; @@ -101,6 +105,7 @@ static int sockfd = 0; int dflag = 0; static int pflag = 0; static int inetd = 0; +static char *configfile = NULL; int main __P((int, char **)); static int inetd_main __P((int, char **)); @@ -115,6 +120,8 @@ static int map4to6 __P((struct sockaddr_in *, struct sockaddr_in6 *)); static void sig_child __P((int)); static void sig_terminate __P((int)); static void start_daemon __P((void)); +static void exit_stderr __P((const char *, ...)) + __attribute__((__format__(__printf__, 1, 2))); #ifndef HAVE_GETIFADDRS static unsigned int if_maxindex __P((void)); #endif @@ -156,6 +163,11 @@ inetd_main(int argc, char **argv) const int on = 1; char sbuf[NI_MAXSERV], snum[NI_MAXSERV]; + if (config_load(configfile) < 0 && configfile) { + exit_failure("could not load config file"); + /*NOTREACHED*/ + } + if (strrchr(argv[0], '/') == NULL) snprintf(path, sizeof(path), "%s/%s", DEFAULT_DIR, argv[0]); else @@ -166,17 +178,21 @@ inetd_main(int argc, char **argv) sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC); if (sockfd < 0) { - exit_error("socket(PF_ROUTE): %s", ERRSTR); + exit_failure("socket(PF_ROUTE): %s", ERRSTR); /*NOTREACHED*/ } #endif melen = sizeof(me); - if (getsockname(STDIN_FILENO, (struct sockaddr *)&me, &melen) < 0) - exit_error("getsockname"); + if (getsockname(STDIN_FILENO, (struct sockaddr *)&me, &melen) < 0) { + exit_failure("getsockname: %s", ERRSTR); + /*NOTREACHED*/ + } fromlen = sizeof(from); - if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) - exit_error("getpeername"); + if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) { + exit_failure("getpeername: %s", ERRSTR); + /*NOTREACHED*/ + } if (getnameinfo((struct sockaddr *)&me, melen, NULL, 0, sbuf, sizeof(sbuf), NI_NUMERICHOST) == 0) service = sbuf; @@ -190,8 +206,10 @@ inetd_main(int argc, char **argv) snprintf(procname, sizeof(procname), "accepting port %s", snum); openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); - if (argc >= MAXARGV) + if (argc >= MAXARGV) { exit_failure("too many arguments"); + /*NOTREACHED*/ + } serverarg[0] = serverpath = path; for (i = 1; i < argc; i++) serverarg[i] = argv[i]; @@ -199,8 +217,10 @@ inetd_main(int argc, char **argv) error = setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)); - if (error < 0) - exit_error("setsockopt(SO_OOBINLINE): %s", ERRSTR); + if (error < 0) { + exit_failure("setsockopt(SO_OOBINLINE): %s", ERRSTR); + /*NOTREACHED*/ + } play_child(STDIN_FILENO, (struct sockaddr *)&from); exit_failure("should not reach here"); @@ -218,11 +238,14 @@ daemon_main(int argc, char **argv) char *ns; #endif /* FAITH_NS */ - while ((c = getopt(argc, argv, "dp46")) != -1) { + while ((c = getopt(argc, argv, "df:p46")) != -1) { switch (c) { case 'd': dflag++; break; + case 'f': + configfile = optarg; + break; case 'p': pflag++; break; @@ -236,12 +259,17 @@ daemon_main(int argc, char **argv) #endif default: usage(); - break; + /*NOTREACHED*/ } } argc -= optind; argv += optind; + if (config_load(configfile) < 0 && configfile) { + exit_failure("could not load config file"); + /*NOTREACHED*/ + } + #ifdef FAITH_NS if ((ns = getenv(FAITH_NS)) != NULL) { struct sockaddr_storage ss; @@ -266,15 +294,12 @@ daemon_main(int argc, char **argv) switch (argc) { case 0: - serverpath = DEFAULT_PATH; - serverarg[0] = DEFAULT_NAME; - serverarg[1] = NULL; - service = DEFAULT_PORT_NAME; - break; + usage(); + /*NOTREACHED*/ default: serverargc = argc - NUMARG; if (serverargc >= MAXARGV) - exit_error("too many augments"); + exit_stderr("too many arguments"); serverpath = malloc(strlen(argv[NUMPRG]) + 1); strcpy(serverpath, argv[NUMPRG]); @@ -300,17 +325,17 @@ daemon_main(int argc, char **argv) hints.ai_protocol = 0; error = getaddrinfo(NULL, service, &hints, &res); if (error) - exit_error("getaddrinfo: %s", gai_strerror(error)); + exit_stderr("getaddrinfo: %s", gai_strerror(error)); s_wld = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s_wld == -1) - exit_error("socket: %s", ERRSTR); + exit_stderr("socket: %s", ERRSTR); #ifdef IPV6_FAITH if (res->ai_family == AF_INET6) { error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_FAITH, &on, sizeof(on)); if (error == -1) - exit_error("setsockopt(IPV6_FAITH): %s", ERRSTR); + exit_stderr("setsockopt(IPV6_FAITH): %s", ERRSTR); } #endif #ifdef FAITH4 @@ -318,31 +343,31 @@ daemon_main(int argc, char **argv) if (res->ai_family == AF_INET) { error = setsockopt(s_wld, IPPROTO_IP, IP_FAITH, &on, sizeof(on)); if (error == -1) - exit_error("setsockopt(IP_FAITH): %s", ERRSTR); + exit_stderr("setsockopt(IP_FAITH): %s", ERRSTR); } #endif #endif /* FAITH4 */ error = setsockopt(s_wld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (error == -1) - exit_error("setsockopt(SO_REUSEADDR): %s", ERRSTR); + exit_stderr("setsockopt(SO_REUSEADDR): %s", ERRSTR); error = setsockopt(s_wld, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)); if (error == -1) - exit_error("setsockopt(SO_OOBINLINE): %s", ERRSTR); + exit_stderr("setsockopt(SO_OOBINLINE): %s", ERRSTR); error = bind(s_wld, (struct sockaddr *)res->ai_addr, res->ai_addrlen); if (error == -1) - exit_error("bind: %s", ERRSTR); + exit_stderr("bind: %s", ERRSTR); error = listen(s_wld, 5); if (error == -1) - exit_error("listen: %s", ERRSTR); + exit_stderr("listen: %s", ERRSTR); #ifdef USE_ROUTE sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC); if (sockfd < 0) { - exit_error("socket(PF_ROUTE): %s", ERRSTR); + exit_stderr("socket(PF_ROUTE): %s", ERRSTR); /*NOTREACHED*/ } #endif @@ -359,7 +384,7 @@ daemon_main(int argc, char **argv) syslog(LOG_INFO, "Staring faith daemon for %s port", service); play_service(s_wld); - /*NOTREACHED*/ + /* NOTREACHED */ exit(1); /*pacify gcc*/ } @@ -407,8 +432,10 @@ again: len = sizeof(srcaddr); s_src = accept(s_wld, (struct sockaddr *)&srcaddr, &len); - if (s_src == -1) + if (s_src == -1) { exit_failure("socket: %s", ERRSTR); + /*NOTREACHED*/ + } child_pid = fork(); @@ -419,6 +446,7 @@ again: openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); play_child(s_src, (struct sockaddr *)&srcaddr); exit_failure("should never reach here"); + /*NOTREACHED*/ } else { /* parent process */ close(s_src); @@ -441,6 +469,7 @@ play_child(int s_src, struct sockaddr *srcaddr) int s_dst, error, hport, nresvport, on = 1; struct timeval tv; struct sockaddr *sa4; + const struct config *conf; tv.tv_sec = 1; tv.tv_usec = 0; @@ -450,8 +479,10 @@ play_child(int s_src, struct sockaddr *srcaddr) syslog(LOG_INFO, "accepted a client from %s", src); error = getsockname(s_src, (struct sockaddr *)&dstaddr6, &len); - if (error == -1) + if (error == -1) { exit_failure("getsockname: %s", ERRSTR); + /*NOTREACHED*/ + } getnameinfo((struct sockaddr *)&dstaddr6, len, dst6, sizeof(dst6), NULL, 0, NI_NUMERICHOST); @@ -487,7 +518,8 @@ play_child(int s_src, struct sockaddr *srcaddr) if (!map6to4((struct sockaddr_in6 *)&dstaddr6, (struct sockaddr_in *)&dstaddr4)) { close(s_src); - exit_error("map6to4 failed"); + exit_failure("map6to4 failed"); + /*NOTREACHED*/ } syslog(LOG_INFO, "translating from v6 to v4"); break; @@ -496,20 +528,35 @@ play_child(int s_src, struct sockaddr *srcaddr) if (!map4to6((struct sockaddr_in *)&dstaddr6, (struct sockaddr_in6 *)&dstaddr4)) { close(s_src); - exit_error("map4to6 failed"); + exit_failure("map4to6 failed"); + /*NOTREACHED*/ } syslog(LOG_INFO, "translating from v4 to v6"); break; #endif default: close(s_src); - exit_error("family not supported"); + exit_failure("family not supported"); /*NOTREACHED*/ } sa4 = (struct sockaddr *)&dstaddr4; getnameinfo(sa4, sa4->sa_len, dst4, sizeof(dst4), NULL, 0, NI_NUMERICHOST); + + conf = config_match(srcaddr, sa4); + if (!conf || !conf->permit) { + close(s_src); + if (conf) { + exit_failure("translation to %s not permitted for %s", + dst4, prefix_string(&conf->match)); + /*NOTREACHED*/ + } else { + exit_failure("translation to %s not permitted", dst4); + /*NOTREACHED*/ + } + } + syslog(LOG_INFO, "the translator is connecting to %s", dst4); setproctitle("port %s, %s -> %s", service, src, dst4); @@ -531,31 +578,55 @@ play_child(int s_src, struct sockaddr *srcaddr) s_dst = socket(sa4->sa_family, SOCK_STREAM, 0); break; } - if (s_dst == -1) + if (s_dst < 0) { exit_failure("socket: %s", ERRSTR); + /*NOTREACHED*/ + } + + if (conf->src.a.ss_family) { + if (bind(s_dst, (struct sockaddr *)&conf->src.a, + conf->src.a.ss_len) < 0) { + exit_failure("bind: %s", ERRSTR); + /*NOTREACHED*/ + } + } error = setsockopt(s_dst, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)); - if (error == -1) - exit_error("setsockopt(SO_OOBINLINE): %s", ERRSTR); + if (error < 0) { + exit_failure("setsockopt(SO_OOBINLINE): %s", ERRSTR); + /*NOTREACHED*/ + } error = setsockopt(s_src, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - if (error == -1) - exit_error("setsockopt(SO_SNDTIMEO): %s", ERRSTR); + if (error < 0) { + exit_failure("setsockopt(SO_SNDTIMEO): %s", ERRSTR); + /*NOTREACHED*/ + } error = setsockopt(s_dst, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - if (error == -1) - exit_error("setsockopt(SO_SNDTIMEO): %s", ERRSTR); + if (error < 0) { + exit_failure("setsockopt(SO_SNDTIMEO): %s", ERRSTR); + /*NOTREACHED*/ + } error = connect(s_dst, sa4, sa4->sa_len); - if (error == -1) + if (error < 0) { exit_failure("connect: %s", ERRSTR); + /*NOTREACHED*/ + } switch (hport) { case FTP_PORT: ftp_relay(s_src, s_dst); break; case RSH_PORT: + syslog(LOG_WARNING, + "WARINNG: it is insecure to relay rsh port"); rsh_relay(s_src, s_dst); break; + case RLOGIN_PORT: + syslog(LOG_WARNING, + "WARINNG: it is insecure to relay rlogin port"); + /*FALLTHROUGH*/ default: tcp_relay(s_src, s_dst, service); break; @@ -581,8 +652,10 @@ faith_prefix(struct sockaddr *dst) mib[2] = IPPROTO_IPV6; mib[3] = IPV6CTL_FAITH_PREFIX; size = sizeof(struct in6_addr); - if (sysctl(mib, 4, &faith_prefix, &size, NULL, 0) < 0) - exit_error("sysctl: %s", ERRSTR); + if (sysctl(mib, 4, &faith_prefix, &size, NULL, 0) < 0) { + exit_failure("sysctl: %s", ERRSTR); + /*NOTREACHED*/ + } if (memcmp(dst, &faith_prefix, sizeof(struct in6_addr) - sizeof(struct in_addr) == 0) { @@ -649,7 +722,7 @@ map6to4(struct sockaddr_in6 *dst6, struct sockaddr_in *dst4) if (dst4->sin_addr.s_addr == INADDR_ANY || dst4->sin_addr.s_addr == INADDR_BROADCAST - || IN_MULTICAST(dst4->sin_addr.s_addr)) + || IN_MULTICAST(ntohl(dst4->sin_addr.s_addr))) return 0; return 1; @@ -695,7 +768,7 @@ sig_child(int sig) pid_t pid; pid = wait3(&status, WNOHANG, (struct rusage *)0); - if (pid && status) + if (pid && WEXITSTATUS(status)) syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status); } @@ -709,18 +782,34 @@ sig_terminate(int sig) static void start_daemon(void) { - if (daemon(0, 0) == -1) - exit_error("daemon: %s", ERRSTR); +#ifdef SA_NOCLDWAIT + struct sigaction sa; +#endif - if (signal(SIGCHLD, sig_child) == SIG_ERR) + if (daemon(0, 0) == -1) + exit_stderr("daemon: %s", ERRSTR); + +#ifdef SA_NOCLDWAIT + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sig_child; + sa.sa_flags = SA_NOCLDWAIT; + sigemptyset(&sa.sa_mask); + sigaction(SIGCHLD, &sa, (struct sigaction *)0); +#else + if (signal(SIGCHLD, sig_child) == SIG_ERR) { exit_failure("signal CHLD: %s", ERRSTR); + /*NOTREACHED*/ + } +#endif - if (signal(SIGTERM, sig_terminate) == SIG_ERR) + if (signal(SIGTERM, sig_terminate) == SIG_ERR) { exit_failure("signal TERM: %s", ERRSTR); + /*NOTREACHED*/ + } } -void -exit_error(const char *fmt, ...) +static void +exit_stderr(const char *fmt, ...) { va_list ap; char buf[BUFSIZ]; @@ -977,7 +1066,7 @@ update_myaddrs() static void usage() { - fprintf(stderr, "usage: %s [-dp] [service [serverpath [serverargs]]]\n", + fprintf(stderr, "usage: %s [-dp] [-f conf] service [serverpath [serverargs]]\n", faithdname); exit(0); } diff --git a/usr.sbin/faithd/faithd.h b/usr.sbin/faithd/faithd.h index b882aad9bb71..8a5021b9e552 100644 --- a/usr.sbin/faithd/faithd.h +++ b/usr.sbin/faithd/faithd.h @@ -1,4 +1,4 @@ -/* $KAME: faithd.h,v 1.2 2000/05/31 03:06:07 itojun Exp $ */ +/* $KAME: faithd.h,v 1.6 2000/10/05 22:20:37 itojun Exp $ */ /* * Copyright (C) 1997 and 1998 WIDE Project. @@ -40,12 +40,13 @@ extern int ftp_active __P((int, int, int *, int *)); extern int ftp_passive __P((int, int, int *, int *)); extern void rsh_relay __P((int, int)); extern void rsh_dual_relay __P((int, int)); -extern void exit_error __P((const char *fmt, ...)); -extern void exit_success __P((const char *fmt, ...)); -extern void exit_failure __P((const char *fmt, ...)); +extern void exit_success __P((const char *, ...)) + __attribute__((__format__(__printf__, 1, 2))); +extern void exit_failure __P((const char *, ...)) + __attribute__((__format__(__printf__, 1, 2))); #define DEFAULT_PORT_NAME "telnet" -#define DEFAULT_DIR "/usr/local/v6/libexec" +#define DEFAULT_DIR "/usr/libexec" #define DEFAULT_NAME "telnetd" #define DEFAULT_PATH (DEFAULT_DIR "/" DEFAULT_NAME) diff --git a/usr.sbin/faithd/ftp.c b/usr.sbin/faithd/ftp.c index e4838eb6b9e2..b0daa5abc174 100644 --- a/usr.sbin/faithd/ftp.c +++ b/usr.sbin/faithd/ftp.c @@ -1,4 +1,4 @@ -/* $KAME$ */ +/* $KAME: ftp.c,v 1.10 2000/09/14 00:23:39 itojun Exp $ */ /* * Copyright (C) 1997 and 1998 WIDE Project. @@ -514,7 +514,7 @@ passivefail: error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH, &on, sizeof(on)); if (error == -1) - exit_error("setsockopt(IPV6_FAITH): %s", ERRSTR); + exit_failure("setsockopt(IPV6_FAITH): %s", ERRSTR); } #endif error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len); @@ -924,6 +924,7 @@ eprtparamfail: } memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(hostp, portp, &hints, &res); if (error) { n = snprintf(sbuf, sizeof(sbuf), diff --git a/usr.sbin/faithd/prefix.c b/usr.sbin/faithd/prefix.c new file mode 100644 index 000000000000..739ae564af2b --- /dev/null +++ b/usr.sbin/faithd/prefix.c @@ -0,0 +1,360 @@ +/* $KAME: prefix.c,v 1.8 2000/11/24 06:16:56 itojun Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (C) 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef offsetof +#define offsetof(type, member) ((size_t)(u_long)(&((type *)0)->member)) +#endif + +#include "faithd.h" +#include "prefix.h" + +static int prefix_set __P((const char *, struct prefix *, int)); +static struct config *config_load1 __P((const char *)); +#if 0 +static void config_show1 __P((const struct config *)); +static void config_show __P((void)); +#endif + +struct config *config_list = NULL; +#ifdef NI_WITHSCOPEID +const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; +#else +const int niflags = NI_NUMERICHOST; +#endif + +static int +prefix_set(s, prefix, slash) + const char *s; + struct prefix *prefix; + int slash; +{ + char *p, *q, *r; + struct addrinfo hints, *res = NULL; + int max; + char *a; + + p = strdup(s); + q = strchr(p, '/'); + if (q) { + if (!slash) + goto fail; + *q++ = '\0'; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(p, "0", &hints, &res)) + goto fail; + if (res->ai_next || res->ai_addrlen > sizeof(prefix->a)) + goto fail; + memcpy(&prefix->a, res->ai_addr, res->ai_addrlen); + + switch (prefix->a.ss_family) { + case AF_INET: + max = 32; + a = (char *)&((struct sockaddr_in *)&prefix->a)->sin_addr; + break; + case AF_INET6: + max = 128; + a = (char *)&((struct sockaddr_in6 *)&prefix->a)->sin6_addr; + break; + default: + a = NULL; + max = -1; + break; + } + + if (q) { + r = NULL; + prefix->l = (int)strtoul(q, &r, 10); + if (!*q || *r) + goto fail; + if (prefix->l < 0 || prefix->l > max) + goto fail; + } else + prefix->l = max; + + if (p) + free(p); + if (res) + freeaddrinfo(res); + return 0; + +fail: + if (p) + free(p); + if (res) + freeaddrinfo(res); + return -1; +} + +const char * +prefix_string(prefix) + const struct prefix *prefix; +{ + static char buf[NI_MAXHOST + 20]; + char hbuf[NI_MAXHOST]; + + if (getnameinfo((struct sockaddr *)&prefix->a, prefix->a.ss_len, hbuf, + sizeof(hbuf), NULL, 0, niflags)) + return NULL; + snprintf(buf, sizeof(buf), "%s/%d", hbuf, prefix->l); + return buf; +} + +int +prefix_match(prefix, sa) + const struct prefix *prefix; + const struct sockaddr *sa; +{ + struct sockaddr_storage a, b; + char *pa, *pb; + int off, l; + + if (prefix->a.ss_family != sa->sa_family || + prefix->a.ss_len != sa->sa_len) + return 0; + + if (prefix->a.ss_len > sizeof(a) || sa->sa_len > sizeof(b)) + return 0; + + switch (prefix->a.ss_family) { + case AF_INET: + off = offsetof(struct sockaddr_in, sin_addr); + break; + case AF_INET6: + off = offsetof(struct sockaddr_in6, sin6_addr); + break; + default: + if (memcmp(&prefix->a, sa, prefix->a.ss_len) != 0) + return 0; + else + return 1; + } + + memcpy(&a, &prefix->a, prefix->a.ss_len); + memcpy(&b, sa, sa->sa_len); + l = prefix->l / 8 + (prefix->l % 8 ? 1 : 0); + + /* overrun check */ + if (off + l > a.ss_len) + return 0; + + pa = ((char *)&a) + off; + pb = ((char *)&b) + off; + if (prefix->l % 8) { + pa[prefix->l / 8] &= 0xff00 >> (prefix->l % 8); + pb[prefix->l / 8] &= 0xff00 >> (prefix->l % 8); + } + if (memcmp(pa, pb, l) != 0) + return 0; + else + return 1; +} + +/* + * prefix/prefixlen permit/deny prefix/prefixlen [srcaddr] + * 3ffe::/16 permit 10.0.0.0/8 10.1.1.1 + */ +static struct config * +config_load1(line) + const char *line; +{ + struct config *conf; + char buf[BUFSIZ]; + char *p; + char *token[4]; + int i; + + if (strlen(line) + 1 > sizeof(buf)) + return NULL; + strlcpy(buf, line, sizeof(buf)); + + p = strchr(buf, '\n'); + if (!p) + return NULL; + *p = '\0'; + p = strchr(buf, '#'); + if (p) + *p = '\0'; + if (strlen(buf) == 0) + return NULL; + + p = buf; + memset(token, 0, sizeof(token)); + for (i = 0; i < sizeof(token) / sizeof(token[0]); i++) { + token[i] = strtok(p, "\t "); + p = NULL; + if (token[i] == NULL) + break; + } + /* extra tokens? */ + if (strtok(p, "\t ") != NULL) + return NULL; + /* insufficient tokens */ + switch (i) { + case 3: + case 4: + break; + default: + return NULL; + } + + conf = (struct config *)malloc(sizeof(*conf)); + if (conf == NULL) + return NULL; + memset(conf, 0, sizeof(*conf)); + + if (strcasecmp(token[1], "permit") == 0) + conf->permit = 1; + else if (strcasecmp(token[1], "deny") == 0) + conf->permit = 0; + else { + /* invalid keyword is considered as "deny" */ + conf->permit = 0; + } + + if (prefix_set(token[0], &conf->match, 1) < 0) + goto fail; + if (prefix_set(token[2], &conf->dest, 1) < 0) + goto fail; + if (token[3]) { + if (prefix_set(token[3], &conf->src, 0) < 0) + goto fail; + } + + return conf; + +fail: + free(conf); + return NULL; +} + +int +config_load(configfile) + const char *configfile; +{ + FILE *fp; + char buf[BUFSIZ]; + struct config *conf, *p; + struct config sentinel; + + config_list = NULL; + + if (!configfile) + configfile = _PATH_PREFIX_CONF; + fp = fopen(configfile, "r"); + if (fp == NULL) + return -1; + + p = &sentinel; + while (fgets(buf, sizeof(buf), fp) != NULL) { + conf = config_load1(buf); + if (conf) { + p->next = conf; + p = p->next; + } + } + config_list = sentinel.next; + + fclose(fp); + return 0; +} + +#if 0 +static void +config_show1(conf) + const struct config *conf; +{ + const char *p; + + p = prefix_string(&conf->match); + printf("%s", p ? p : "?"); + + if (conf->permit) + printf(" permit"); + else + printf(" deny"); + + p = prefix_string(&conf->dest); + printf(" %s", p ? p : "?"); + + printf("\n"); +} + +static void +config_show() +{ + struct config *conf; + + for (conf = config_list; conf; conf = conf->next) + config_show1(conf); +} +#endif + +const struct config * +config_match(sa1, sa2) + struct sockaddr *sa1, *sa2; +{ + static struct config conf; + const struct config *p; + + if (sa1->sa_len > sizeof(conf.match.a) || + sa2->sa_len > sizeof(conf.dest.a)) + return NULL; + + memset(&conf, 0, sizeof(conf)); + if (!config_list) { + conf.permit = 1; + memcpy(&conf.match.a, sa1, sa1->sa_len); + memcpy(&conf.dest.a, sa2, sa2->sa_len); + return &conf; + } + + for (p = config_list; p; p = p->next) + if (prefix_match(&p->match, sa1) && prefix_match(&p->dest, sa2)) + return p; + + return NULL; +} diff --git a/usr.sbin/faithd/prefix.h b/usr.sbin/faithd/prefix.h new file mode 100644 index 000000000000..3ef56e351116 --- /dev/null +++ b/usr.sbin/faithd/prefix.h @@ -0,0 +1,52 @@ +/* $KAME: prefix.h,v 1.3 2000/11/19 11:45:38 itojun Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (C) 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. + */ + +struct prefix { + struct sockaddr_storage a; + int l; +}; + +struct config { + struct config *next; + + int permit; + struct prefix match; + struct prefix dest; + struct prefix src; /* src to use for outgoing connection */ +}; + +#define _PATH_PREFIX_CONF "/etc/faithd.conf" + +extern const char *prefix_string __P((const struct prefix *)); +extern int prefix_match __P((const struct prefix *, const struct sockaddr *)); +extern int config_load __P((const char *)); +extern const struct config *config_match __P((struct sockaddr *, struct sockaddr *)); diff --git a/usr.sbin/faithd/rsh.c b/usr.sbin/faithd/rsh.c index 6d811472e351..4e11d76c57de 100644 --- a/usr.sbin/faithd/rsh.c +++ b/usr.sbin/faithd/rsh.c @@ -1,4 +1,4 @@ -/* $KAME$ */ +/* $KAME: rsh.c,v 1.5 2001/02/15 17:28:04 itojun Exp $ */ /* * Copyright (C) 1997 and 1998 WIDE Project. diff --git a/usr.sbin/faithd/tcp.c b/usr.sbin/faithd/tcp.c index 004686fe9781..c754dfcfc4a3 100644 --- a/usr.sbin/faithd/tcp.c +++ b/usr.sbin/faithd/tcp.c @@ -1,4 +1,4 @@ -/* $KAME$ */ +/* $KAME: tcp.c,v 1.5 2000/09/29 03:48:31 sakane Exp $ */ /* * Copyright (C) 1997 and 1998 WIDE Project. @@ -93,9 +93,9 @@ sig_child(int sig) pid_t pid; pid = wait3(&status, WNOHANG, (struct rusage *)0); - if (pid && status) + if (pid && WEXITSTATUS(status)) syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status); - exit_failure("terminate connection due to child termination"); + exit_success("terminate connection due to child termination"); } static void @@ -195,7 +195,8 @@ relay(int s_rcv, int s_snd, const char *service, int direction) FD_ZERO(&exceptfds); fcntl(s_snd, F_SETFD, O_NONBLOCK); oreadfds = readfds; owritefds = writefds; oexceptfds = exceptfds; - FD_SET(s_rcv, &readfds); FD_SET(s_rcv, &exceptfds); + FD_SET(s_rcv, &readfds); + FD_SET(s_rcv, &exceptfds); oob_exists = 0; maxfd = (s_rcv > s_snd) ? s_rcv : s_snd; -- cgit v1.2.3