diff options
author | shin <shin@FreeBSD.org> | 2000-01-27 09:28:38 +0000 |
---|---|---|
committer | shin <shin@FreeBSD.org> | 2000-01-27 09:28:38 +0000 |
commit | ce15efb7c04858f00b57c16093d4a3043809048e (patch) | |
tree | 8b3d00f78a4a5a34cc3b17e29c28b4472d93a35c /libexec/ftpd | |
parent | dcbae417f8f4365a5eea807290162acd308b720d (diff) | |
download | FreeBSD-src-ce15efb7c04858f00b57c16093d4a3043809048e.zip FreeBSD-src-ce15efb7c04858f00b57c16093d4a3043809048e.tar.gz |
another tcp apps IPv6 updates.(should be make world safe)
ftp, telnet, ftpd, faithd
also telnet related sync with crypto, secure, kerberosIV
Obtained from: KAME project
Diffstat (limited to 'libexec/ftpd')
-rw-r--r-- | libexec/ftpd/Makefile | 1 | ||||
-rw-r--r-- | libexec/ftpd/extern.h | 16 | ||||
-rw-r--r-- | libexec/ftpd/ftpcmd.y | 364 | ||||
-rw-r--r-- | libexec/ftpd/ftpd.8 | 23 | ||||
-rw-r--r-- | libexec/ftpd/ftpd.c | 503 | ||||
-rw-r--r-- | libexec/ftpd/logwtmp.c | 23 | ||||
-rw-r--r-- | libexec/ftpd/popen.c | 1 |
7 files changed, 812 insertions, 119 deletions
diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile index c23e85b..4ddfb53 100644 --- a/libexec/ftpd/Makefile +++ b/libexec/ftpd/Makefile @@ -7,6 +7,7 @@ SRCS= ftpd.c ftpcmd.y logwtmp.c popen.c skey-stuff.c CFLAGS+=-DSETPROCTITLE -DSKEY -DLOGIN_CAP -DVIRTUAL_HOSTING -Wall \ -I${.CURDIR}/../../contrib-crypto/telnet +CFLAGS+=-DINET6 -g YFLAGS= LDADD= -lskey -lmd -lcrypt -lutil diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h index 9888cb6..684703e 100644 --- a/libexec/ftpd/extern.h +++ b/libexec/ftpd/extern.h @@ -49,6 +49,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 *)); @@ -71,3 +72,18 @@ int yyparse __P((void)); char *skey_challenge __P((char *, struct passwd *, int)); #endif int ls_main __P((int, char **)); + +struct sockaddr_in; +struct sockaddr_in6; +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 e085e29..bb5bff9 100644 --- a/libexec/ftpd/ftpcmd.y +++ b/libexec/ftpd/ftpcmd.y @@ -58,6 +58,7 @@ static const char rcsid[] = #include <ctype.h> #include <errno.h> #include <glob.h> +#include <netdb.h> #include <pwd.h> #include <setjmp.h> #include <signal.h> @@ -71,7 +72,7 @@ static const char rcsid[] = #include "extern.h" -extern struct sockaddr_in data_dest, his_addr; +extern union sockunion data_dest, his_addr; extern int logged_in; extern struct passwd *pw; extern int guest; @@ -98,6 +99,8 @@ static int cmd_bytesz; char cbuf[512]; char *fromname; +extern int epsvall; + %} %union { @@ -108,6 +111,7 @@ char *fromname; %token A B C E F I L N P R S T + ALL SP CRLF COMMA @@ -118,6 +122,7 @@ char *fromname; ABOR DELE CWD LIST NLST SITE STAT HELP NOOP MKD RMD PWD CDUP STOU SMNT SYST SIZE MDTM + LPRT LPSV EPRT EPSV UMASK IDLE CHMOD @@ -128,7 +133,8 @@ char *fromname; %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 <s> pathstring pathname password username ext_arg +%type <s> ALL %start cmd_list @@ -157,31 +163,194 @@ cmd } | PORT check_login SP host_port CRLF { - if ($2) { - if (paranoid && - ((ntohs(data_dest.sin_port) < - IPPORT_RESERVED) || - memcmp(&data_dest.sin_addr, - &his_addr.sin_addr, - sizeof(data_dest.sin_addr)))) { - usedefault = 1; + if (epsvall) { + reply(501, "no PORT allowed after EPSV ALL"); + goto port_done; + } + if (!$2) + goto port_done; + if (port_check("PORT") == 1) + goto port_done; +#ifdef INET6 + if ((his_addr.su_family != AF_INET6 || + !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { + /* shoud never happen */ + usedefault = 1; + reply(500, "Invalid address rejected."); + goto port_done; + } + port_check_v6("pcmd"); +#endif + port_done: + } + | LPRT check_login SP host_long_port CRLF + { + if (epsvall) { + reply(501, "no LPRT allowed after EPSV ALL"); + goto lprt_done; + } + if (!$2) + goto lprt_done; + if (port_check("LPRT") == 1) + goto lprt_done; +#ifdef INET6 + if (his_addr.su_family != AF_INET6) { + usedefault = 1; + reply(500, "Invalid address rejected."); + goto lprt_done; + } + if (port_check_v6("LPRT") == 1) + goto lprt_done; +#endif + lprt_done: + } + | EPRT check_login SP STRING CRLF + { + char delim; + char *tmp = NULL; + char *p, *q; + char *result[3]; + struct addrinfo hints; + struct addrinfo *res; + int i; + + if (epsvall) { + reply(501, "no EPRT allowed after EPSV ALL"); + goto eprt_done; + } + if (!$2) + goto eprt_done; + + memset(&data_dest, 0, sizeof(data_dest)); + tmp = strdup($4); + if (debug) + syslog(LOG_DEBUG, "%s", tmp); + 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, - "Illegal PORT range rejected."); - } else { - usedefault = 0; - if (pdata >= 0) { - (void) close(pdata); - pdata = -1; - } - reply(200, "PORT command successful."); + "Invalid argument, rejected."); + if (tmp) + free(tmp); + usedefault = 1; + goto eprt_done; } + *q++ = '\0'; + result[i] = p; + if (debug) + syslog(LOG_DEBUG, "%d: %s", 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++; + } + + /* grab address */ + memset(&hints, 0, sizeof(hints)); + if (atoi(result[0]) == 1) + hints.ai_family = PF_INET; +#ifdef INET6 + else if (atoi(result[0]) == 2) + hints.ai_family = PF_INET6; +#endif + else + hints.ai_family = PF_UNSPEC; /*XXX*/ + hints.ai_socktype = SOCK_STREAM; + i = getaddrinfo(result[1], result[2], &hints, &res); + if (i) + goto parsefail; + memcpy(&data_dest, res->ai_addr, res->ai_addrlen); +#ifdef INET6 + 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; + } +#endif + free(tmp); + tmp = NULL; + + if (port_check("EPRT") == 1) + goto eprt_done; +#ifdef INET6 + if (his_addr.su_family != AF_INET6) { + usedefault = 1; + reply(500, "Invalid address rejected."); + goto eprt_done; + } + if (port_check_v6("EPRT") == 1) + goto eprt_done; +#endif + eprt_done:; } | PASV check_login CRLF { - if ($2) + if (epsvall) + reply(501, "no PASV allowed after EPSV ALL"); + else if ($2) passive(); } + | LPSV check_login CRLF + { + if (epsvall) + reply(501, "no LPSV allowed after EPSV ALL"); + else if ($2) + long_passive("LPSV", PF_UNSPEC); + } + | EPSV check_login SP NUMBER CRLF + { + if ($2) { + int pf; + switch ($4) { + case 1: + pf = PF_INET; + break; +#ifdef INET6 + case 2: + pf = PF_INET6; + break; +#endif + default: + pf = -1; /*junk value*/ + break; + } + long_passive("EPSV", pf); + } + } + | EPSV check_login SP ALL CRLF + { + if ($2) { + reply(200, + "EPSV ALL command successful."); + epsvall++; + } + } + | EPSV check_login CRLF + { + if ($2) + long_passive("EPSV", PF_UNSPEC); + } | TYPE SP type_code CRLF { switch (cmd_type) { @@ -576,15 +745,61 @@ host_port { char *a, *p; - data_dest.sin_len = sizeof(struct sockaddr_in); - data_dest.sin_family = AF_INET; - p = (char *)&data_dest.sin_port; + data_dest.su_len = sizeof(struct sockaddr_in); + data_dest.su_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; } ; +host_long_port + : 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; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_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; + } + if ($1 != 6 || $3 != 16 || $37 != 2) + memset(&data_dest, 0, sizeof(data_dest)); + } + | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER + { + char *a, *p; + + memset(&data_dest, 0, sizeof(data_dest)); + 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; + if ($1 != 4 || $3 != 4 || $13 != 2) + memset(&data_dest, 0, sizeof(data_dest)); + } + ; + form_code : N { @@ -774,7 +989,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)" }, @@ -829,8 +1048,11 @@ static char *copy __P((char *)); static void help __P((struct tab *, char *)); static struct tab * lookup __P((struct tab *, char *)); +static int port_check __P((const char *)); +static int port_check_v6 __P((const char *)); static void sizecmd __P((char *)); static void toolong __P((int)); +static void v4map_data_dest __P((void)); static int yylex __P((void)); static struct tab * @@ -1085,6 +1307,11 @@ yylex() cbuf[cpos] = c; return (NUMBER); } + if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 + && !isalnum(cbuf[cpos + 3])) { + cpos += 3; + return ALL; + } switch (cbuf[cpos++]) { case '\n': @@ -1289,3 +1516,94 @@ sizecmd(filename) reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); } } + +/* Return 1, if port check is done. Return 0, if not yet. */ +static int +port_check(pcmd) + const char *pcmd; +{ + if (his_addr.su_family == AF_INET) { + if (data_dest.su_family != AF_INET) { + usedefault = 1; + reply(500, "Invalid address rejected."); + return 1; + } + if (paranoid && + ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || + 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 range rejected."); + } else { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "%s command successful.", pcmd); + } + return 1; + } + return 0; +} + +#ifdef INET6 +/* Return 1, if port check is done. Return 0, if not yet. */ +static int +port_check_v6(pcmd) + const char *pcmd; +{ + if (his_addr.su_family == AF_INET6) { + if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) + /* Convert data_dest into v4 mapped sockaddr.*/ + v4map_data_dest(); + if (data_dest.su_family != AF_INET6) { + usedefault = 1; + reply(500, "Invalid address rejected."); + return 1; + } + if (paranoid && + ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || + memcmp(&data_dest.su_sin6.sin6_addr, + &his_addr.su_sin6.sin6_addr, + sizeof(data_dest.su_sin6.sin6_addr)))) { + usedefault = 1; + reply(500, "Illegal PORT range rejected."); + } else { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "%s command successful.", pcmd); + } + return 1; + } + return 0; +} + +static void +v4map_data_dest() +{ + struct in_addr savedaddr; + int savedport; + + if (data_dest.su_family != AF_INET) { + usedefault = 1; + reply(500, "Invalid address rejected."); + return; + } + + savedaddr = data_dest.su_sin.sin_addr; + savedport = data_dest.su_port; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); + data_dest.su_sin6.sin6_family = AF_INET6; + data_dest.su_sin6.sin6_port = savedport; + memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); + memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], + (caddr_t)&savedaddr, sizeof(savedaddr)); +} +#endif diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 index 8f2cca7..fb10aa6 100644 --- a/libexec/ftpd/ftpd.8 +++ b/libexec/ftpd/ftpd.8 @@ -32,7 +32,7 @@ .\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 .\" $FreeBSD$ .\" -.Dd April 19, 1994 +.Dd January 27, 2000 .Dt FTPD 8 .Os BSD 4.2 .Sh NAME @@ -41,6 +41,8 @@ Internet File Transfer Protocol server .Sh SYNOPSIS .Nm ftpd +.Op Fl 4 +.Op Fl 6 .Op Fl d .Op Fl l Op Fl l .Op Fl A @@ -135,6 +137,20 @@ When .Fl D is specified, write the daemon's process ID to .Ar file . +.It Fl 6 +When +.Fl D +is specified, accept connections via AF_INET6 socket. +.It Fl 4 +When +.Fl D +is specified, accept IPv4 connections. +When +.Fl 6 +is also specified, accept IPv4 connection via AF_INET6 socket. +When +.Fl 6 +is not specified, accept IPv4 connection via AF_INET socket. .It Fl A Allow only anonymous ftp access. .El @@ -203,6 +219,10 @@ The case of the requests is ignored. .It XMKD Ta "make a directory (deprecated)" .It XPWD Ta "print the current working directory (deprecated)" .It XRMD Ta "remove a directory (deprecated)" +.It LPSV Ta "prepare for server-to-server transfer, multiprotocol" +.It LPRT Ta "specify data connection port, multiprotocol" +.It EPSV Ta "prepare for server-to-server transfer, multiprotocol" +.It EPRT Ta "specify data connection port, multiprotocol" .El .Pp The following non-standard or @@ -453,3 +473,4 @@ The .Nm command appeared in .Bx 4.2 . +IPv6 support was added in WIDE Hydrangea IPv6 stack kit. diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index 0f9d20c..cd72ed2 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -110,15 +110,20 @@ static const char rcsid[] = static char version[] = "Version 6.00LS"; #undef main +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + 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; int data; @@ -155,9 +160,11 @@ char *hostname; #ifdef VIRTUAL_HOSTING char *ftpuser; +int epsvall = 0; + static struct ftphost { struct ftphost *next; - struct in_addr hostaddr; + union sockunion hostaddr; char *hostname; char *anonuser; char *statfile; @@ -176,7 +183,6 @@ char *tty = ttyline; /* for klogin */ static int auth_pam __P((struct passwd**, const char*)); #endif -struct in_addr bind_address; char *pid_file = NULL; /* @@ -200,7 +206,7 @@ char proctitle[LINE_MAX]; /* initial part of title */ #ifdef SKEY int pwok = 0; -char addr_string[20]; /* XXX */ +char addr_string[INET6_ADDRSTRLEN]; /* XXX */ #endif #define LOGCMD(cmd, file) \ @@ -224,13 +230,13 @@ char addr_string[20]; /* XXX */ #ifdef VIRTUAL_HOSTING static void inithosts __P((void)); -static void selecthost __P((struct in_addr *)); +static void selecthost __P((union sockunion *)); #endif static void ack __P((char *)); static void myoob __P((int)); static int checkuser __P((char *, char *, int)); 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 *)); @@ -266,6 +272,10 @@ main(argc, argv, envp) int addrlen, ch, on = 1, tos; char *cp, line[LINE_MAX]; FILE *fd; + int error; + char *bindname = NULL; + int family = AF_UNSPEC; + int enable_v4 = 0; tzset(); /* in case no timezone database in ~ftp */ @@ -280,8 +290,7 @@ main(argc, argv, envp) #endif /* OLD_SETPROCTITLE */ - bind_address.s_addr = htonl(INADDR_ANY); - while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:")) != -1) { + while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:46")) != -1) { switch (ch) { case 'D': daemon_mode++; @@ -320,8 +329,7 @@ main(argc, argv, envp) break; case 'a': - if (!inet_aton(optarg, &bind_address)) - errx(1, "invalid address for -a"); + bindname = optarg; break; case 'p': @@ -347,6 +355,16 @@ main(argc, argv, envp) debug = 1; break; + case '4': + enable_v4 = 1; + if (family == AF_UNSPEC) + family = AF_INET; + break; + + case '6': + family = AF_INET6; + break; + default: warnx("unknown flag -%c ignored", optopt); break; @@ -366,7 +384,7 @@ main(argc, argv, envp) if (daemon_mode) { int ctl_sock, fd; - struct servent *sv; + struct addrinfo hints, *res; /* * Detach from parent. @@ -376,30 +394,56 @@ main(argc, argv, envp) exit(1); } (void) signal(SIGCHLD, reapchild); - /* - * Get port number for ftp/tcp. - */ - sv = getservbyname("ftp", "tcp"); - if (sv == NULL) { - syslog(LOG_ERR, "getservbyname for ftp failed"); + /* init bind_sa */ + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = family == AF_UNSPEC ? AF_INET : family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(bindname, "ftp", &hints, &res); + if (error) { + if (family == AF_UNSPEC) { + hints.ai_family = AF_UNSPEC; + error = getaddrinfo(bindname, "ftp", &hints, + &res); + } + if (error == 0 && res->ai_addr != NULL) + family = res->ai_addr->sa_family; + } + if (error) { + syslog(LOG_ERR, gai_strerror(error)); + if (error == EAI_SYSTEM) + syslog(LOG_ERR, strerror(errno)); + exit(1); + } + if (res->ai_addr == NULL) { + syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname); exit(1); } /* * 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); } 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 = bind_address; - server_addr.sin_port = sv->s_port; - if (bind(ctl_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) { + syslog(LOG_ERR, "control setsockopt: %m"); +#ifdef IPV6_BINDV6ONLY + if (family == AF_INET6 && enable_v4 == 0) { + if (setsockopt(ctl_sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, + (char *)&on, sizeof (on)) < 0) + syslog(LOG_ERR, + "control setsockopt(IPV6_BINDV6ONLY): %m"); + } +#endif /* IPV6_BINDV6ONLY */ + memcpy(&server_addr, res->ai_addr, res->ai_addr->sa_len); + if (bind(ctl_sock, (struct sockaddr *)&server_addr, + server_addr.su_len) < 0) { syslog(LOG_ERR, "control bind: %m"); exit(1); } @@ -434,7 +478,7 @@ main(argc, argv, envp) * children to handle them. */ while (1) { - addrlen = sizeof(his_addr); + addrlen = server_addr.su_len; fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen); if (fork() == 0) { /* child */ @@ -459,7 +503,9 @@ main(argc, argv, envp) syslog(LOG_ERR, "signal: %m"); #ifdef SKEY - strncpy(addr_string, inet_ntoa(his_addr.sin_addr), sizeof(addr_string)); + getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + addr_string, sizeof(addr_string) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); #endif addrlen = sizeof(ctrl_addr); if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { @@ -468,12 +514,15 @@ main(argc, argv, envp) } #ifdef VIRTUAL_HOSTING /* select our identity from virtual host table */ - selecthost(&ctrl_addr.sin_addr); + selecthost(&ctrl_addr); #endif #ifdef IP_TOS + if (ctrl_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 /* * Disable Nagle on the control channel so that we don't have to wait @@ -482,7 +531,7 @@ main(argc, argv, envp) if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "control setsockopt TCP_NODELAY: %m"); - data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); + data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); /* set this here so klogin can use it... */ (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); @@ -497,7 +546,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 */ @@ -567,9 +616,9 @@ inithosts() { FILE *fp; char *cp; - struct hostent *hp; struct ftphost *hrp, *lhrp; char line[1024]; + struct addrinfo hints, *res, *ai; /* * Fill in the default host information @@ -579,20 +628,28 @@ inithosts() if ((hrp = malloc(sizeof(struct ftphost))) == NULL || (hrp->hostname = strdup(line)) == NULL) fatal("Ran out of memory."); - memset(&hrp->hostaddr, 0, sizeof hrp->hostaddr); - if ((hp = gethostbyname(hrp->hostname)) != NULL) - (void) memcpy(&hrp->hostaddr, - hp->h_addr_list[0], - sizeof(hrp->hostaddr)); + memset(&hrp->hostaddr, 0, sizeof(hrp->hostaddr)); + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + getaddrinfo(hrp->hostname, NULL, &hints, &res); + if (res) + memcpy(&hrp->hostaddr, res->ai_addr, res->ai_addrlen); hrp->statfile = _PATH_FTPDSTATFILE; hrp->welcome = _PATH_FTPWELCOME; hrp->loginmsg = _PATH_FTPLOGINMESG; hrp->anonuser = "ftp"; hrp->next = NULL; thishost = firsthost = lhrp = hrp; + freeaddrinfo(res); if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { + int addrsize, error; + void *addr; + struct hostent *hp; + while (fgets(line, sizeof(line), fp) != NULL) { - int i; + int i, hp_error; if ((cp = strchr(line, '\n')) == NULL) { /* ignore long lines */ @@ -606,13 +663,21 @@ inithosts() /* skip comments and empty lines */ if (cp == NULL || line[0] == '#') continue; - /* first, try a standard gethostbyname() */ - if ((hp = gethostbyname(cp)) == NULL) + + hints.ai_flags = 0; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(cp, NULL, &hints, &res); + if (error != NULL) continue; + for (ai = res; ai != NULL && ai->ai_addr != NULL; + ai = ai->ai_next) + { + for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { if (memcmp(&hrp->hostaddr, - hp->h_addr_list[0], - sizeof(hrp->hostaddr)) == 0) + ai->ai_addr, + ai->ai_addr->sa_len) == 0) break; } if (hrp == NULL) { @@ -628,16 +693,32 @@ inithosts() lhrp = hrp; } (void) memcpy(&hrp->hostaddr, - hp->h_addr_list[0], - sizeof(hrp->hostaddr)); + ai->ai_addr, + ai->ai_addr->sa_len); /* * determine hostname to use. - * force defined name if it is a valid alias + * force defined name if there is a valid alias * otherwise fallback to primary hostname */ - if ((hp = gethostbyaddr((char*)&hrp->hostaddr, - sizeof(hrp->hostaddr), - AF_INET)) != NULL) { + /* XXX: getaddrinfo() can't do alias check */ + switch(hrp->hostaddr.su_family) { + case AF_INET: + addr = &((struct sockaddr_in *)&hrp->hostaddr)->sin_addr; + addrsize = sizeof(struct sockaddr_in); + break; + case AF_INET6: + addr = &((struct sockaddr_in6 *)&hrp->hostaddr)->sin6_addr; + addrsize = sizeof(struct sockaddr_in6); + break; + default: + /* should not reach here */ + free(hrp); + continue; + /* NOTREACHED */ + } + if ((hp = getipnodebyaddr((char*)addr, addrsize, + hrp->hostaddr.su_family, + &hp_error)) != NULL) { if (strcmp(cp, hp->h_name) != 0) { if (hp->h_aliases == NULL) cp = hp->h_name; @@ -652,6 +733,7 @@ inithosts() } } hrp->hostname = strdup(cp); + freehostent(hp); /* ok, now we now peel off the rest */ i = 0; while (i < 4 && (cp = strtok(NULL, " \t")) != NULL) { @@ -673,25 +755,55 @@ inithosts() } ++i; } + /* XXX: re-initialization for getaddrinfo() loop */ + cp = strtok(line, " \t"); + } } (void) fclose(fp); } } static void -selecthost(a) - struct in_addr *a; +selecthost(su) + union sockunion *su; { struct ftphost *hrp; + u_int16_t port; +#ifdef INET6 + struct in6_addr *mapped_in6 = NULL; +#endif + +#ifdef INET6 + /* + * XXX IPv4 mapped IPv6 addr consideraton, + * specified in rfc2373. + */ + if (su->su_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) + mapped_in6 = &su->su_sin6.sin6_addr; +#endif hrp = thishost = firsthost; /* default */ + port = su->su_port; + su->su_port = 0; while (hrp != NULL) { - if (memcmp(a, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) { + if (memcmp(su, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) { + thishost = hrp; + break; + } +#ifdef INET6 + /* XXX IPv4 mapped IPv6 addr consideraton */ + if (hrp->hostaddr.su_family == AF_INET && mapped_in6 != NULL && + (memcmp(&mapped_in6->s6_addr[12], + &hrp->hostaddr.su_sin.sin_addr, + sizeof(struct in_addr)) == 0)) { thishost = hrp; break; } +#endif hrp = hrp->next; } + su->su_port = port; /* setup static variables as appropriate */ hostname = thishost->hostname; ftpuser = thishost->anonuser; @@ -1107,8 +1219,9 @@ skip: if ((lc = login_getpwclass(pw)) != NULL) { char remote_ip[MAXHOSTNAMELEN]; - strncpy(remote_ip, inet_ntoa(his_addr.sin_addr), - sizeof(remote_ip) - 1); + getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + remote_ip, sizeof(remote_ip) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); remote_ip[sizeof(remote_ip) - 1] = 0; if (!auth_hostok(lc, remotehost, remote_ip)) { syslog(LOG_INFO|LOG_AUTH, @@ -1402,19 +1515,19 @@ getdatasock(mode) if (data >= 0) return (fdopen(data, mode)); (void) seteuid((uid_t)0); - s = socket(AF_INET, SOCK_STREAM, 0); + + s = socket(data_dest.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; @@ -1422,9 +1535,12 @@ getdatasock(mode) } (void) seteuid((uid_t)pw->pw_uid); #ifdef IP_TOS + if (data_source.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 /* @@ -1470,8 +1586,8 @@ dataconn(name, size, mode) else *sizebuf = '\0'; if (pdata >= 0) { - struct sockaddr_in from; - int s, fromlen = sizeof(from); + union sockunion from; + int s, fromlen = ctrl_addr.su_len; struct timeval timeout; fd_set set; @@ -1491,9 +1607,12 @@ dataconn(name, size, mode) (void) close(pdata); pdata = s; #ifdef IP_TOS + if (from.su_family == AF_INET) + { 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); @@ -1510,14 +1629,18 @@ 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 hostbuf[BUFSIZ], portbuf[BUFSIZ]; + getnameinfo((struct sockaddr *)&data_source, + data_source.su_len, hostbuf, sizeof(hostbuf) - 1, + portbuf, sizeof(portbuf), + NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID); + reply(425, "Can't create data socket (%s,%s): %s.", + hostbuf, portbuf, strerror(errno)); return (NULL); } data = fileno(file); 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; @@ -1768,14 +1891,20 @@ statfilecmd(filename) void statcmd() { - struct sockaddr_in *sin; + union sockunion *su; u_char *a, *p; + char hname[INET6_ADDRSTRLEN]; + int ispassive; lreply(211, "%s FTP server status:", hostname, version); printf(" %s\r\n", version); printf(" Connected to %s", remotehost); - if (!isdigit(remotehost[0])) - printf(" (%s)", inet_ntoa(his_addr.sin_addr)); + if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + hname, sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID)) { + if (strcmp(hname, remotehost) != 0) + printf(" (%s)", hname); + } printf("\r\n"); if (logged_in) { if (guest) @@ -1800,18 +1929,85 @@ statcmd() if (data != -1) printf(" Data connection open\r\n"); else if (pdata != -1) { - printf(" in Passive mode"); - sin = &pasv_addr; + ispassive = 1; + su = &pasv_addr; goto printaddr; } else if (usedefault == 0) { - printf(" PORT"); - sin = &data_dest; + ispassive = 0; + su = &data_dest; printaddr: - a = (u_char *) &sin->sin_addr; - p = (u_char *) &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])); + if (epsvall) { + printf(" EPSV only mode (EPSV ALL)\r\n"); + goto epsvonly; + } + + /* PORT/PASV */ + if (su->su_family == AF_INET) { + a = (u_char *) &su->su_sin.sin_addr; + p = (u_char *) &su->su_sin.sin_port; + printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", + ispassive ? "PASV" : "PORT", + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(p[0]), UC(p[1])); + } + + /* LPRT/LPSV */ + { + int alen, af, i; + + 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) { + printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", + af, alen); + for (i = 0; i < alen; i++) + printf("%d,", UC(a[i])); + printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); + } + } + +epsvonly:; + /* EPRT/EPSV */ + { + int af; + + switch (su->su_family) { + case AF_INET: + af = 1; + break; + case AF_INET6: + af = 2; + break; + default: + af = 0; + break; + } + if (af) { + if (!getnameinfo((struct sockaddr *)su, su->su_len, + hname, sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST)) { + printf(" %s |%d|%s|%d|\r\n", + ispassive ? "EPSV" : "EPRT", + af, hname, htons(su->su_port)); + } + } + } #undef UC } else printf(" No data connection\r\n"); @@ -2015,10 +2211,12 @@ renamecmd(from, to) } static void -dolog(sin) - struct sockaddr_in *sin; +dolog(who) + struct sockaddr *who; { - realhostname(remotehost, sizeof(remotehost) - 1, &sin->sin_addr); + int error; + + realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); #ifdef SETPROCTITLE #ifdef VIRTUAL_HOSTING @@ -2039,8 +2237,16 @@ dolog(sin) remotehost, hostname); else #endif + { + char who_name[MAXHOSTNAMELEN]; + + error = getnameinfo(who, who->sa_len, + who_name, sizeof(who_name) - 1, + NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); syslog(LOG_INFO, "connection from %s (%s)", remotehost, - inet_ntoa(sin->sin_addr)); + error == 0 ? who_name : ""); + } } } @@ -2112,7 +2318,7 @@ passive() if (pdata >= 0) /* close old port if one set */ close(pdata); - pdata = socket(AF_INET, SOCK_STREAM, 0); + pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); if (pdata < 0) { perror_reply(425, "Can't open passive connection"); return; @@ -2121,7 +2327,7 @@ passive() (void) seteuid((uid_t)0); #ifdef IP_PORTRANGE - { + if (ctrl_addr.su_family == AF_INET) { int on = restricted_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; @@ -2132,9 +2338,8 @@ passive() #endif pasv_addr = ctrl_addr; - pasv_addr.sin_port = 0; - if (bind(pdata, (struct sockaddr *)&pasv_addr, - sizeof(pasv_addr)) < 0) + pasv_addr.su_port = 0; + if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) goto pasv_error; (void) seteuid((uid_t)pw->pw_uid); @@ -2144,8 +2349,15 @@ passive() goto pasv_error; if (listen(pdata, 1) < 0) goto pasv_error; - a = (char *) &pasv_addr.sin_addr; - p = (char *) &pasv_addr.sin_port; + if (pasv_addr.su_family == AF_INET) + a = (char *) &pasv_addr.su_sin.sin_addr; + else if (pasv_addr.su_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) + a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; + else + goto pasv_error; + + p = (char *) &pasv_addr.su_port; #define UC(b) (((int) b) & 0xff) @@ -2162,6 +2374,121 @@ pasv_error: } /* + * Long Passive defined in RFC 1639. + * 228 Entering Long Passive Mode + * (af, hal, h1, h2, h3,..., pal, p1, p2...) + */ + +void +long_passive(cmd, pf) + char *cmd; + int pf; +{ + int len; + char *p, *a; + + if (pdata >= 0) /* close old port if one set */ + close(pdata); + + 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; + } + + (void) seteuid((uid_t)0); + + pasv_addr = ctrl_addr; + pasv_addr.su_port = 0; + len = pasv_addr.su_len; + + if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) + goto pasv_error; + + (void) seteuid((uid_t)pw->pw_uid); + + if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) + goto pasv_error; + if (listen(pdata, 1) < 0) + goto pasv_error; + +#define UC(b) (((int) b) & 0xff) + + if (strcmp(cmd, "LPSV") == 0) { + p = (char *)&pasv_addr.su_port; + switch (pasv_addr.su_family) { + case AF_INET: + a = (char *) &pasv_addr.su_sin.sin_addr; + v4_reply: + 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: + if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { + a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; + goto v4_reply; + } + 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; + } + } 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) seteuid((uid_t)pw->pw_uid); + (void) close(pdata); + pdata = -1; + perror_reply(425, "Can't open passive connection"); + return; +} + +/* * Generate unique name for file with basename "local". * The file named "local" is already known to exist. * Generates failure reply on error. diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c index d3f2ec5..2ac57d1 100644 --- a/libexec/ftpd/logwtmp.c +++ b/libexec/ftpd/logwtmp.c @@ -43,6 +43,7 @@ static const char rcsid[] = #include <sys/stat.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <sys/socket.h> #include <fcntl.h> #include <time.h> @@ -68,15 +69,23 @@ ftpd_logwtmp(line, name, host) struct stat buf; if (strlen(host) > UT_HOSTSIZE) { - struct hostent *hp = gethostbyname(host); + struct addrinfo hints, *res; + int error; + static char hostbuf[BUFSIZ]; - if (hp != NULL) { - struct in_addr in; - - memmove(&in, hp->h_addr, sizeof(in)); - host = inet_ntoa(in); - } else + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + error = getaddrinfo(host, NULL, &hints, &res); + if (error) host = "invalid hostname"; + else { + getnameinfo(res->ai_addr, res->ai_addrlen, + hostbuf, sizeof(hostbuf), NULL, 0, + NI_NUMERICHOST); + host = hostbuf; + if (strlen(host) > UT_HOSTSIZE) + host[UT_HOSTSIZE] = '\0'; + } } if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c index 606d17a..82cfd58 100644 --- a/libexec/ftpd/popen.c +++ b/libexec/ftpd/popen.c @@ -44,6 +44,7 @@ static const char rcsid[] = #include <sys/types.h> #include <sys/wait.h> +#include <netinet/in.h> #include <errno.h> #include <glob.h> |