diff options
author | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 1999-12-08 13:15:22 +0000 |
---|---|---|
committer | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 1999-12-08 13:15:22 +0000 |
commit | 484ffacaeb80ae7d76c14c94e481c6a5e02fe3cc (patch) | |
tree | 773772ec7853df32406e943f79716f9caf63b4ca | |
parent | 608666bc5b614c4a821f9571e6e7516c490a1d2e (diff) |
IPv6 support from KAME.
XXX kerberos and tcp_wrapper needs checking
-rw-r--r-- | libexec/ftpd/Makefile | 5 | ||||
-rw-r--r-- | libexec/ftpd/extern.h | 43 | ||||
-rw-r--r-- | libexec/ftpd/ftpcmd.y | 281 | ||||
-rw-r--r-- | libexec/ftpd/ftpd.c | 478 | ||||
-rw-r--r-- | libexec/ftpd/logwtmp.c | 2 | ||||
-rw-r--r-- | libexec/ftpd/popen.c | 3 |
6 files changed, 716 insertions, 96 deletions
diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile index ed079b0a150..6a12e7f2601 100644 --- a/libexec/ftpd/Makefile +++ b/libexec/ftpd/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.14 1999/02/26 00:15:54 art Exp $ +# $OpenBSD: Makefile,v 1.15 1999/12/08 13:15:21 itojun Exp $ # $NetBSD: Makefile,v 1.13 1996/02/16 02:07:41 cgd Exp $ # @(#)Makefile 8.2 (Berkeley) 4/4/94 @@ -17,6 +17,9 @@ LSDIR = ${.CURDIR}/../../bin/ls SRCS += ls.c cmp.c print.c stat_flags.c util.c CFLAGS += -I${LSDIR} +# not really used +CPPFLAGS+=-DINET6 + .if (${SKEY} == "yes") CFLAGS+=-DSKEY LDADD+= -lskey diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h index 6fffb87f5f2..cd92578a36a 100644 --- a/libexec/ftpd/extern.h +++ b/libexec/ftpd/extern.h @@ -1,5 +1,34 @@ /* $NetBSD: extern.h,v 1.2 1995/04/11 02:44:49 cgd Exp $ */ +/* + * Copyright (C) 1997 and 1998 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) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -50,6 +79,7 @@ void makedir __P((char *)); void nack __P((char *)); void pass __P((char *)); void passive __P((void)); +void long_passive __P((char *, int)); void perror_reply __P((int, char *)); void pwd __P((void)); void removedir __P((char *)); @@ -68,3 +98,16 @@ void yyerror __P((char *)); void toolong __P((int)); int yyparse __P((void)); + +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 diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y index c146e989bbf..232747892a3 100644 --- a/libexec/ftpd/ftpcmd.y +++ b/libexec/ftpd/ftpcmd.y @@ -1,4 +1,4 @@ -/* $OpenBSD: ftpcmd.y,v 1.17 1999/10/08 14:40:35 deraadt Exp $ */ +/* $OpenBSD: ftpcmd.y,v 1.18 1999/12/08 13:15:21 itojun Exp $ */ /* $NetBSD: ftpcmd.y,v 1.7 1996/04/08 19:03:11 jtc Exp $ */ /* @@ -47,7 +47,7 @@ #if 0 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; #else -static char rcsid[] = "$OpenBSD: ftpcmd.y,v 1.17 1999/10/08 14:40:35 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: ftpcmd.y,v 1.18 1999/12/08 13:15:21 itojun Exp $"; #endif #endif /* not lint */ @@ -71,10 +71,11 @@ static char rcsid[] = "$OpenBSD: ftpcmd.y,v 1.17 1999/10/08 14:40:35 deraadt Exp #include <syslog.h> #include <time.h> #include <unistd.h> +#include <netdb.h> #include "extern.h" -extern struct sockaddr_in data_dest; +extern union sockunion data_dest; extern int logged_in; extern struct passwd *pw; extern int guest; @@ -91,7 +92,7 @@ extern int usedefault; extern int transflag; extern char tmpline[]; extern int portcheck; -extern struct sockaddr_in his_addr; +extern union sockunion his_addr; off_t restart_point; @@ -111,6 +112,7 @@ char *fromname; %token A B C E F I L N P R S T + ALL SP CRLF COMMA @@ -122,17 +124,20 @@ char *fromname; STAT HELP NOOP MKD RMD PWD CDUP STOU SMNT SYST SIZE MDTM + LPRT LPSV EPRT EPSV + UMASK IDLE CHMOD LEXERR %token <s> STRING +%token <s> ALL %token <i> NUMBER %type <i> check_login octal_number byte_size %type <i> struct_code mode_code type_code form_code %type <s> pathstring pathname password username -%type <i> host_port +%type <i> host_port host_long_port4 host_long_port6 %start cmd_list @@ -168,14 +173,14 @@ cmd reply(500, "Illegal PORT rejected (range errors)."); } else if (portcheck && - ntohs(data_dest.sin_port) < IPPORT_RESERVED) { + ntohs(data_dest.su_sin.sin_port) < IPPORT_RESERVED) { usedefault = 1; reply(500, "Illegal PORT rejected (reserved port)."); } else if (portcheck && - memcmp(&data_dest.sin_addr, - &his_addr.sin_addr, - sizeof data_dest.sin_addr)) { + memcmp(&data_dest.su_sin.sin_addr, + &his_addr.su_sin.sin_addr, + sizeof data_dest.su_sin.sin_addr)) { usedefault = 1; reply(500, "Illegal PORT rejected (address wrong)."); @@ -189,12 +194,174 @@ cmd } } } + | LPRT check_login SP host_long_port4 CRLF + { + /* reject invalid host_long_port4 */ + if ($4) { + reply(500, "Illegal LPRT command rejected"); + usedefault = 1; + } else if (epsvall) { + reply(501, "LPRT disallowed after EPSV ALL"); + usedefault = 1; + } else { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "LPRT command successful."); + } + } + + | LPRT check_login SP host_long_port6 CRLF + { + /* reject invalid host_long_port6 */ + if ($4) { + reply(500, "Illegal LPRT command rejected"); + usedefault = 1; + } else if (epsvall) { + reply(501, "LPRT disallowed after EPSV ALL"); + usedefault = 1; + } else { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "LPRT command successful."); + } + } + + | EPRT check_login SP STRING CRLF + { + char *tmp = NULL; + char *result[3]; + char *p, *q; + char delim; + struct addrinfo hints; + struct addrinfo *res; + int i; + + if (epsvall) { + reply(501, "EPRT disallowed after EPSV ALL"); + goto eprt_done; + } + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + + /*XXX checks for login */ + + tmp = strdup($4); + if (!tmp) { + fatal("not enough core."); + /*NOTREACHED*/ + } + p = tmp; + delim = p[0]; + p++; + memset(result, 0, sizeof(result)); + for (i = 0; i < 3; i++) { + q = strchr(p, delim); + if (!q || *q != delim) { + parsefail: + reply(500, "Invalid argument, rejected."); + if (tmp) + free(tmp); + usedefault = 1; + goto eprt_done; + } + *q++ = '\0'; + result[i] = p; + p = q; + } + + /* some more sanity check */ + p = result[0]; + while (*p) { + if (!isdigit(*p)) + goto parsefail; + p++; + } + p = result[2]; + while (*p) { + if (!isdigit(*p)) + goto parsefail; + p++; + } + + memset(&hints, 0, sizeof(hints)); + if (atoi(result[0]) == 1) + hints.ai_family = PF_INET; + if (atoi(result[0]) == 2) + hints.ai_family = PF_INET6; + else + hints.ai_family = PF_UNSPEC; /*XXX*/ + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(result[1], result[2], &hints, &res)) + goto parsefail; + memcpy(&data_dest, res->ai_addr, res->ai_addrlen); + if (his_addr.su_family == AF_INET6 + && data_dest.su_family == AF_INET6) { + /* XXX more sanity checks! */ + data_dest.su_sin6.sin6_scope_id = + his_addr.su_sin6.sin6_scope_id; + } + free(tmp); + tmp = NULL; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "EPRT command successful."); + eprt_done:; + } + | PASV check_login CRLF { if ($2) { passive(); } } + | LPSV CRLF + { + if (epsvall) + reply(501, "LPSV disallowed after EPSV ALL"); + else + long_passive("LPSV", PF_UNSPEC); + } + | EPSV SP NUMBER CRLF + { + int pf; + switch ($3) { + case 1: + pf = PF_INET; + break; + case 2: + pf = PF_INET6; + break; + default: + pf = -1; /*junk*/ + break; + } + long_passive("EPSV", pf); + } + | EPSV SP ALL CRLF + { + if (!logged_in) { + syslog(LOG_NOTICE, "long passive but not logged in"); + reply(503, "Login with USER first."); + } else { + reply(200, "EPSV ALL command successful."); + epsvall++; + } + } + | EPSV CRLF + { + long_passive("EPSV", PF_UNSPEC); + } | TYPE check_login SP type_code CRLF { if ($2) { @@ -620,17 +787,93 @@ host_port $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255) { $$ = 1; } else { - data_dest.sin_len = sizeof(struct sockaddr_in); - data_dest.sin_family = AF_INET; - p = (char *)&data_dest.sin_port; + data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); + data_dest.su_sin.sin_family = AF_INET; + p = (char *)&data_dest.su_sin.sin_port; p[0] = $9; p[1] = $11; - a = (char *)&data_dest.sin_addr; + a = (char *)&data_dest.su_sin.sin_addr; a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; $$ = 0; } } ; +host_long_port4 + : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER + { + char *a, *p; + + /* reject invalid LPRT command */ + if ($1 != 4 || $3 != 4 + || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 + || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 + || $13 != 2 + || $15 < 0 || $15 > 255 || $17 < 0 || $17 > 255) { + $$ = 1; + } else { + data_dest.su_sin.sin_len = + sizeof(struct sockaddr_in); + data_dest.su_family = AF_INET; + p = (char *)&data_dest.su_port; + p[0] = $15; p[1] = $17; + a = (char *)&data_dest.su_sin.sin_addr; + a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; + $$ = 0; + } + } + ; + +host_long_port6 + : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER + { + char *a, *p; + + /* reject invalid LPRT command */ + if ($1 != 6 || $3 != 16 + || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 + || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 + || $13 < 0 || $13 > 255 || $15 < 0 || $15 > 255 + || $17 < 0 || $17 > 255 || $19 < 0 || $19 > 255 + || $21 < 0 || $21 > 255 || $23 < 0 || $23 > 255 + || $25 < 0 || $25 > 255 || $27 < 0 || $27 > 255 + || $29 < 0 || $29 > 255 || $31 < 0 || $31 > 255 + || $33 < 0 || $33 > 255 || $35 < 0 || $35 > 255 + || $37 != 2 + || $39 < 0 || $39 > 255 || $41 < 0 || $41 > 255) { + $$ = 1; + } else { + data_dest.su_sin6.sin6_len = + sizeof(struct sockaddr_in6); + data_dest.su_family = AF_INET6; + p = (char *)&data_dest.su_port; + p[0] = $39; p[1] = $41; + a = (char *)&data_dest.su_sin6.sin6_addr; + a[0] = $5; a[1] = $7; + a[2] = $9; a[3] = $11; + a[4] = $13; a[5] = $15; + a[6] = $17; a[7] = $19; + a[8] = $21; a[9] = $23; + a[10] = $25; a[11] = $27; + a[12] = $29; a[13] = $31; + a[14] = $33; a[15] = $35; + if (his_addr.su_family == AF_INET6) { + /* XXX more sanity checks! */ + data_dest.su_sin6.sin6_scope_id = + his_addr.su_sin6.sin6_scope_id; + } + + $$ = 0; + } + } + ; + form_code : N { @@ -831,7 +1074,11 @@ struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, + { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, + { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, + { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, + { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, { "STRU", STRU, ARGS, 1, "(specify file structure)" }, { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, @@ -888,6 +1135,8 @@ static struct tab * static void sizecmd __P((char *)); static int yylex __P((void)); +extern int epsvall; + static struct tab * lookup(p, cmd) struct tab *p; @@ -1148,6 +1397,12 @@ yylex() cbuf[cpos] = c; return (NUMBER); } + if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 + && !isalnum(cbuf[cpos + 3])) { + yylval.s = strdup("ALL"); + cpos += 3; + return ALL; + } switch (cbuf[cpos++]) { case '\n': diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index 23a1120a08a..e9c4b2446d1 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -1,7 +1,36 @@ -/* $OpenBSD: ftpd.c,v 1.62 1999/12/03 01:22:46 millert Exp $ */ +/* $OpenBSD: ftpd.c,v 1.63 1999/12/08 13:15:21 itojun Exp $ */ /* $NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $ */ /* + * Copyright (C) 1997 and 1998 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) 1985, 1988, 1990, 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * @@ -111,12 +140,12 @@ static char version[] = "Version 6.4/OpenBSD"; extern off_t restart_point; extern char cbuf[]; -struct sockaddr_in server_addr; -struct sockaddr_in ctrl_addr; -struct sockaddr_in data_source; -struct sockaddr_in data_dest; -struct sockaddr_in his_addr; -struct sockaddr_in pasv_addr; +union sockunion server_addr; +union sockunion ctrl_addr; +union sockunion data_source; +union sockunion data_dest; +union sockunion his_addr; +union sockunion pasv_addr; int daemon_mode = 0; int data; @@ -142,6 +171,7 @@ int mode; int doutmp = 0; /* update utmp file */ int usedefault = 1; /* for data transfers */ int pdata = -1; /* for passive mode */ +int family = AF_INET; sig_atomic_t transflag; off_t file_size; off_t byte_count; @@ -172,6 +202,8 @@ char *krbtkfile_env = NULL; char *ident = NULL; +int epsvall = 0; + /* * Timeout intervals for retrying connections * to hosts that don't accept PORT cmds. This @@ -210,7 +242,7 @@ static void ack __P((char *)); static void myoob __P((int)); static int checkuser __P((char *, char *)); static FILE *dataconn __P((char *, off_t, char *)); -static void dolog __P((struct sockaddr_in *)); +static void dolog __P((struct sockaddr *)); static char *curdir __P((void)); static void end_login __P((void)); static FILE *getdatasock __P((char *)); @@ -224,7 +256,7 @@ static struct passwd * sgetpwnam __P((char *)); static char *sgetsave __P((char *)); static void reapchild __P((int)); -static int check_host __P((struct sockaddr_in *)); +static int check_host __P((struct sockaddr *)); static void usage __P((void)); void logxfer __P((char *, off_t, time_t)); @@ -242,7 +274,7 @@ curdir() return (guest ? path+1 : path); } -char *argstr = "AdDhlMSt:T:u:UvP"; +char *argstr = "AdDhlMSt:T:u:UvP46"; static void usage() @@ -335,6 +367,14 @@ main(argc, argv, envp) debug = 1; break; + case '4': + family = AF_INET; + break; + + case '6': + family = AF_INET6; + break; + default: usage(); break; @@ -373,7 +413,7 @@ main(argc, argv, envp) * Open a socket, bind it to the FTP port, and start * listening. */ - ctl_sock = socket(AF_INET, SOCK_STREAM, 0); + ctl_sock = socket(family, SOCK_STREAM, 0); if (ctl_sock < 0) { syslog(LOG_ERR, "control socket: %m"); exit(1); @@ -381,11 +421,20 @@ main(argc, argv, envp) if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "control setsockopt: %m");; - server_addr.sin_family = AF_INET; - server_addr.sin_addr.s_addr = INADDR_ANY; - server_addr.sin_port = sv->s_port; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.su_sin.sin_family = family; + switch (family) { + case AF_INET: + server_addr.su_len = sizeof(struct sockaddr_in); + server_addr.su_sin.sin_port = sv->s_port; + break; + case AF_INET6: + server_addr.su_len = sizeof(struct sockaddr_in6); + server_addr.su_sin6.sin6_port = sv->s_port; + break; + } if (bind(ctl_sock, (struct sockaddr *)&server_addr, - sizeof(server_addr))) { + server_addr.su_len)) { syslog(LOG_ERR, "control bind: %m"); exit(1); } @@ -421,7 +470,7 @@ main(argc, argv, envp) #if defined(TCPWRAPPERS) /* ..in the child. */ - if (!check_host(&his_addr)) + if (!check_host((struct sockaddr *)&his_addr)) exit(1); #endif /* TCPWRAPPERS */ } else { @@ -450,12 +499,55 @@ main(argc, argv, envp) syslog(LOG_ERR, "getsockname (%s): %m", argv[0]); exit(1); } + if (his_addr.su_family == AF_INET6 + && IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) { +#if 1 + /* + * IPv4 control connection arrived to AF_INET6 socket. + * I hate to do this, but this is the easiest solution. + */ + union sockunion tmp_addr; + const int off = sizeof(struct in6_addr) - sizeof(struct in_addr); + + tmp_addr = his_addr; + memset(&his_addr, 0, sizeof(his_addr)); + his_addr.su_sin.sin_family = AF_INET; + his_addr.su_sin.sin_len = sizeof(his_addr.su_sin); + memcpy(&his_addr.su_sin.sin_addr, + &tmp_addr.su_sin6.sin6_addr.s6_addr[off], + sizeof(his_addr.su_sin.sin_addr)); + his_addr.su_sin.sin_port = tmp_addr.su_sin6.sin6_port; + + tmp_addr = ctrl_addr; + memset(&ctrl_addr, 0, sizeof(ctrl_addr)); + ctrl_addr.su_sin.sin_family = AF_INET; + ctrl_addr.su_sin.sin_len = sizeof(ctrl_addr.su_sin); + memcpy(&ctrl_addr.su_sin.sin_addr, + &tmp_addr.su_sin6.sin6_addr.s6_addr[off], + sizeof(ctrl_addr.su_sin.sin_addr)); + ctrl_addr.su_sin.sin_port = tmp_addr.su_sin6.sin6_port; +#else + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(530, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + reply(530, + "Connection from IPv4 mapped address is not supported."); + exit(0); +#endif + } #ifdef IP_TOS - tos = IPTOS_LOWDELAY; - if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) - syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + if (his_addr.su_family == AF_INET) { + tos = IPTOS_LOWDELAY; + if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, + sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } #endif - data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); + data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); /* Try to handle urgent data inline */ #ifdef SO_OOBINLINE @@ -467,7 +559,7 @@ main(argc, argv, envp) if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) syslog(LOG_ERR, "fcntl F_SETOWN: %m"); #endif - dolog(&his_addr); + dolog((struct sockaddr *)&his_addr); /* * Set up default state */ @@ -508,14 +600,8 @@ main(argc, argv, envp) strcpy(hostname, hp->h_name); if (multihome) { - hp = gethostbyaddr((char *) &ctrl_addr.sin_addr, - sizeof (struct in_addr), AF_INET); - if (hp != NULL) { - strcpy(dhostname, hp->h_name); - } else { - /* Default. */ - strcpy(dhostname, inet_ntoa(ctrl_addr.sin_addr)); - } + getnameinfo((struct sockaddr *)&ctrl_addr, ctrl_addr.su_len, + dhostname, sizeof(dhostname), NULL, 0, 0); } reply(220, "%s FTP server (%s) ready.", @@ -1112,19 +1198,18 @@ getdatasock(mode) sigfillset(&allsigs); sigprocmask (SIG_BLOCK, &allsigs, NULL); (void) seteuid((uid_t)0); - s = socket(AF_INET, SOCK_STREAM, 0); + s = socket(ctrl_addr.su_family, SOCK_STREAM, 0); if (s < 0) goto bad; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) goto bad; /* anchor socket to avoid multi-homing problems */ - data_source.sin_len = sizeof(struct sockaddr_in); - data_source.sin_family = AF_INET; - data_source.sin_addr = ctrl_addr.sin_addr; + data_source = ctrl_addr; + data_source.su_port = htons(20); /* ftp-data port */ for (tries = 1; ; tries++) { if (bind(s, (struct sockaddr *)&data_source, - sizeof(data_source)) >= 0) + data_source.su_len) >= 0) break; if (errno != EADDRINUSE || tries > 10) goto bad; @@ -1135,9 +1220,12 @@ getdatasock(mode) sigprocmask (SIG_UNBLOCK, &allsigs, NULL); #ifdef IP_TOS - on = IPTOS_THROUGHPUT; - if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) - syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + if (ctrl_addr.su_family == AF_INET) { + on = IPTOS_THROUGHPUT; + if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, + sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } #endif #ifdef TCP_NOPUSH /* @@ -1176,7 +1264,10 @@ dataconn(name, size, mode) { char sizebuf[32]; FILE *file; - int retry = 0, tos; + int retry = 0; + in_port_t *p; + char *fa, *ha; + int alen; file_size = size; byte_count = 0; @@ -1186,7 +1277,7 @@ dataconn(name, size, mode) } else sizebuf[0] = '\0'; if (pdata >= 0) { - struct sockaddr_in from; + union sockunion from; int s, fromlen = sizeof(from); signal (SIGALRM, toolong); @@ -1199,14 +1290,35 @@ dataconn(name, size, mode) pdata = -1; return (NULL); } - if (ntohs(from.sin_port) < IPPORT_RESERVED) { + switch (from.su_family) { + case AF_INET: + p = (in_port_t *)&from.su_sin.sin_port; + fa = (u_char *)&from.su_sin.sin_addr; + ha = (u_char *)&his_addr.su_sin.sin_addr; + alen = sizeof(struct in_addr);; + break; + case AF_INET6: + p = (in_port_t *)&from.su_sin6.sin6_port; + fa = (u_char *)&from.su_sin6.sin6_addr; + ha = (u_char *)&his_addr.su_sin6.sin6_addr; + alen = sizeof(struct in6_addr);; + break; + default: + perror_reply(425, "Can't build data connection"); + (void) close(pdata); + (void) close(s); + pdata = -1; + return (NULL); + } + if (from.su_family != his_addr.su_family + || ntohs(*p) < IPPORT_RESERVED) { perror_reply(425, "Can't build data connection"); (void) close(pdata); (void) close(s); pdata = -1; return (NULL); } - if (from.sin_addr.s_addr != his_addr.sin_addr.s_addr) { + if (memcmp(fa, ha, alen) != 0) { perror_reply(435, "Can't build data connection"); (void) close(pdata); (void) close(s); @@ -1215,11 +1327,6 @@ dataconn(name, size, mode) } (void) close(pdata); pdata = s; -#ifdef IP_TOS - tos = IPTOS_THROUGHPUT; - (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, - sizeof(int)); -#endif reply(150, "Opening %s mode data connection for '%s'%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return (fdopen(pdata, mode)); @@ -1235,9 +1342,12 @@ dataconn(name, size, mode) usedefault = 1; file = getdatasock(mode); if (file == NULL) { - reply(425, "Can't create data socket (%s,%d): %s.", - inet_ntoa(data_source.sin_addr), - ntohs(data_source.sin_port), strerror(errno)); + char hbuf[MAXHOSTNAMELEN], pbuf[10]; + getnameinfo((struct sockaddr *)&data_source, data_source.su_len, + hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), + NI_NUMERICHOST | NI_NUMERICSERV); + reply(425, "Can't create data socket (%s,%s): %s.", + hbuf, pbuf, strerror(errno)); return (NULL); } data = fileno(file); @@ -1246,21 +1356,40 @@ dataconn(name, size, mode) * attempt to connect to reserved port on client machine; * this looks like an attack */ - if (ntohs(data_dest.sin_port) < IPPORT_RESERVED || - ntohs(data_dest.sin_port) == 2049) { /* XXX */ + switch (data_dest.su_family) { + case AF_INET: + p = (in_port_t *)&data_dest.su_sin.sin_port; + fa = (u_char *)&data_dest.su_sin.sin_addr; + ha = (u_char *)&his_addr.su_sin.sin_addr; + alen = sizeof(struct in_addr);; + break; + case AF_INET6: + p = (in_port_t *)&data_dest.su_sin6.sin6_port; + fa = (u_char *)&data_dest.su_sin6.sin6_addr; + ha = (u_char *)&his_addr.su_sin6.sin6_addr; + alen = sizeof(struct in6_addr);; + break; + default: + perror_reply(425, "Can't build data connection"); + (void) fclose(file); + pdata = -1; + return (NULL); + } + if (data_dest.su_family != his_addr.su_family + || ntohs(*p) < IPPORT_RESERVED || ntohs(*p) == 2049) { /* XXX */ perror_reply(425, "Can't build data connection"); (void) fclose(file); data = -1; return NULL; } - if (data_dest.sin_addr.s_addr != his_addr.sin_addr.s_addr) { + if (memcmp(fa, ha, alen) != 0) { perror_reply(435, "Can't build data connection"); (void) fclose(file); data = -1; return NULL; } while (connect(data, (struct sockaddr *)&data_dest, - sizeof(data_dest)) < 0) { + data_dest.su_len) < 0) { if (errno == EADDRINUSE && retry < swaitmax) { sleep((unsigned) swaitint); retry += swaitint; @@ -1516,14 +1645,18 @@ statfilecmd(filename) void statcmd() { - struct sockaddr_in *sin; + union sockunion *su; u_char *a, *p; + char hbuf[MAXHOSTNAMELEN]; + int ispassive; lreply(211, "%s FTP server status:", hostname, version); printf(" %s\r\n", version); + getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); printf(" Connected to %s", remotehost); - if (!isdigit(remotehost[0])) - printf(" (%s)", inet_ntoa(his_addr.sin_addr)); + if (strcmp(remotehost, hbuf) != 0) + printf(" (%s)", hbuf); printf("\r\n"); if (logged_in) { if (guest) @@ -1545,22 +1678,96 @@ statcmd() #endif printf("; STRUcture: %s; transfer MODE: %s\r\n", strunames[stru], modenames[mode]); + ispassive = 0; if (data != -1) printf(" Data connection open\r\n"); else if (pdata != -1) { printf(" in Passive mode"); - sin = &pasv_addr; + su = (union sockunion *)&pasv_addr; + ispassive++; goto printaddr; } else if (usedefault == 0) { - printf(" PORT"); - sin = &data_dest; + su = (union sockunion *)&data_dest; printaddr: - a = (u_char *) &sin->sin_addr; - p = (u_char *) &sin->sin_port; + /* PASV/PORT */ + if (su->su_family == AF_INET) { + if (ispassive) + printf("211- PASV "); + else + printf("211- PORT "); + a = (u_char *) &su->su_sin.sin_addr; + p = (u_char *) &su->su_sin.sin_port; #define UC(b) (((int) b) & 0xff) - printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), - UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); + printf("(%d,%d,%d,%d,%d,%d)\r\n", + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(p[0]), UC(p[1])); + } + + /* LPSV/LPRT */ + { + int alen, af, i; + + alen = 0; + switch (su->su_family) { + case AF_INET: + a = (u_char *) &su->su_sin.sin_addr; + p = (u_char *) &su->su_sin.sin_port; + alen = sizeof(su->su_sin.sin_addr); + af = 4; + break; + case AF_INET6: + a = (u_char *) &su->su_sin6.sin6_addr; + p = (u_char *) &su->su_sin6.sin6_port; + alen = sizeof(su->su_sin6.sin6_addr); + af = 6; + break; + default: + af = 0; + break; + } + if (af) { + if (ispassive) + printf("211- LPSV "); + else + printf("211- LPRT "); + printf("(%d,%d", af, alen); + for (i = 0; i < alen; i++) + printf("%d,", UC(a[alen])); + printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); #undef UC + } + } + + /* EPRT/EPSV */ +epsvonly:; + { + int af; + + switch (su->su_family) { + case AF_INET: + af = 1; + break; + case AF_INET6: + af = 2; + break; + default: + af = 0; + break; + } + if (af) { + char hbuf[MAXHOSTNAMELEN], pbuf[10]; + if (getnameinfo((struct sockaddr *)su, su->su_len, + hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), + NI_NUMERICHOST) == 0) { + if (ispassive) + printf("211 - EPSV "); + else + printf("211 - EPRT "); + printf("(|%d|%s|%s|)\r\n", + af, hbuf, pbuf); + } + } + } } else printf(" No data connection\r\n"); reply(211, "End of status"); @@ -1785,17 +1992,14 @@ renamecmd(from, to) } static void -dolog(sin) - struct sockaddr_in *sin; +dolog(sa) + struct sockaddr *sa; { - struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, - sizeof(struct in_addr), AF_INET); + char hbuf[sizeof(remotehost)]; + + getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0, 0); + (void) strncpy(remotehost, hbuf, sizeof(remotehost)-1); - if (hp) - (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)-1); - else - (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), - sizeof(remotehost)-1); remotehost[sizeof(remotehost)-1] = '\0'; #ifdef HASSETPROCTITLE snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); @@ -1899,9 +2103,9 @@ passive() #endif pasv_addr = ctrl_addr; - pasv_addr.sin_port = 0; + pasv_addr.su_sin.sin_port = 0; if (bind(pdata, (struct sockaddr *)&pasv_addr, - sizeof(pasv_addr)) < 0) + pasv_addr.su_len) < 0) goto pasv_error; len = sizeof(pasv_addr); @@ -1909,8 +2113,8 @@ passive() goto pasv_error; if (listen(pdata, 1) < 0) goto pasv_error; - a = (char *) &pasv_addr.sin_addr; - p = (char *) &pasv_addr.sin_port; + a = (char *) &pasv_addr.su_sin.sin_addr; + p = (char *) &pasv_addr.su_sin.sin_port; #define UC(b) (((int) b) & 0xff) @@ -1919,6 +2123,111 @@ passive() return; pasv_error: + (void) seteuid((uid_t)pw->pw_uid); + (void) close(pdata); + pdata = -1; + perror_reply(425, "Can't open passive connection"); + return; +} + +/* + * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...) + * 229 Entering Extended Passive Mode (|||port|) + */ +void +long_passive(char *cmd, int pf) +{ + int len; + register char *p, *a; + + if (!logged_in) { + syslog(LOG_NOTICE, "long passive but not logged in"); + reply(503, "Login with USER first."); + return; + } + + if (pf != PF_UNSPEC) { + if (ctrl_addr.su_family != pf) { + switch (ctrl_addr.su_family) { + case AF_INET: + pf = 1; + break; + case AF_INET6: + pf = 2; + break; + default: + pf = 0; + break; + } + /* + * XXX + * only EPRT/EPSV ready clients will understand this + */ + if (strcmp(cmd, "EPSV") == 0 && pf) { + reply(522, "Network protocol mismatch, " + "use (%d)", pf); + } else + reply(501, "Network protocol mismatch"); /*XXX*/ + + return; + } + } + + pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); + if (pdata < 0) { + perror_reply(425, "Can't open passive connection"); + return; + } + pasv_addr = ctrl_addr; + pasv_addr.su_port = 0; + (void) seteuid((uid_t) 0); + if (bind(pdata, (struct sockaddr *) &pasv_addr, pasv_addr.su_len) < 0) { + (void) seteuid((uid_t) pw->pw_uid); + goto pasv_error; + } + (void) seteuid((uid_t) pw->pw_uid); + len = pasv_addr.su_len; + if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) + goto pasv_error; + if (listen(pdata, 1) < 0) + goto pasv_error; + p = (char *) &pasv_addr.su_port; + +#define UC(b) (((int) b) & 0xff) + + if (strcmp(cmd, "LPSV") == 0) { + switch (pasv_addr.su_family) { + case AF_INET: + a = (char *) &pasv_addr.su_sin.sin_addr; + reply(228, "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", + 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + 2, UC(p[0]), UC(p[1])); + return; + case AF_INET6: + a = (char *) &pasv_addr.su_sin6.sin6_addr; + reply(228, "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", + 6, 16, + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), + UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), + UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), + 2, UC(p[0]), UC(p[1])); + return; + } +#undef UC + } else if (strcmp(cmd, "EPSV") == 0) { + switch (pasv_addr.su_family) { + case AF_INET: + case AF_INET6: + reply(229, "Entering Extended Passive Mode (|||%d|)", + ntohs(pasv_addr.su_port)); + return; + } + } else { + /* more proper error code? */ + } + + pasv_error: (void) close(pdata); pdata = -1; perror_reply(425, "Can't open passive connection"); @@ -2172,13 +2481,20 @@ logxfer(name, size, start) #if defined(TCPWRAPPERS) static int -check_host(sin) - struct sockaddr_in *sin; +check_host(sa) + struct sockaddr *sa; { - struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, - sizeof(struct in_addr), AF_INET); - char *addr = inet_ntoa(sin->sin_addr); + struct sockaddr_in *sin; + struct hostent *hp; + char *addr; + if (sa->sa_family != AF_INET) + return 1; /*XXX*/ + + sin = (struct sockaddr_in *)sa; + hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof(struct in_addr), AF_INET); + addr = inet_ntoa(sin->sin_addr); if (hp) { if (!hosts_ctl("ftpd", hp->h_name, addr, STRING_UNKNOWN)) { syslog(LOG_NOTICE, "tcpwrappers rejected: %s [%s]", diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c index 1b3416cd791..36c761210c9 100644 --- a/libexec/ftpd/logwtmp.c +++ b/libexec/ftpd/logwtmp.c @@ -51,6 +51,8 @@ static char rcsid[] = "$NetBSD: logwtmp.c,v 1.4 1995/04/11 02:44:58 cgd Exp $"; #include <unistd.h> #include <stdio.h> #include <string.h> + +#include <netinet/in.h> #include "extern.h" static int fd = -1; diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c index c195cc2ccad..696d835b3b5 100644 --- a/libexec/ftpd/popen.c +++ b/libexec/ftpd/popen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: popen.c,v 1.10 1999/02/26 00:15:54 art Exp $ */ +/* $OpenBSD: popen.c,v 1.11 1999/12/08 13:15:21 itojun Exp $ */ /* $NetBSD: popen.c,v 1.5 1995/04/11 02:45:00 cgd Exp $ */ /* @@ -58,6 +58,7 @@ static char rcsid[] = "$NetBSD: popen.c,v 1.5 1995/04/11 02:45:00 cgd Exp $"; #include <syslog.h> #include <unistd.h> +#include <netinet/in.h> #include "extern.h" /* |