diff options
author | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2000-11-14 20:27:02 +0000 |
---|---|---|
committer | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2000-11-14 20:27:02 +0000 |
commit | 8ab39446f715fe46cc5f457c3c652fbf776d8e35 (patch) | |
tree | 91602b21699761e26d7aff16390d8f7bfd44ce04 /libexec/ftpd | |
parent | 87b4863a111bc9fb81fdd1fa9a4478f5fc939bb7 (diff) |
cleanup EPSV/EPRT error handling. avoid possible memory leak (getaddrinfo).
correct error code on unsupported protocol parameter against EPRT (522).
Diffstat (limited to 'libexec/ftpd')
-rw-r--r-- | libexec/ftpd/extern.h | 6 | ||||
-rw-r--r-- | libexec/ftpd/ftpcmd.y | 110 | ||||
-rw-r--r-- | libexec/ftpd/ftpd.c | 213 |
3 files changed, 200 insertions, 129 deletions
diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h index cd92578a36a..131164b1f68 100644 --- a/libexec/ftpd/extern.h +++ b/libexec/ftpd/extern.h @@ -79,7 +79,13 @@ void makedir __P((char *)); void nack __P((char *)); void pass __P((char *)); void passive __P((void)); +int lpsvproto2af __P((int)); +int af2lpsvproto __P((int)); +int epsvproto2af __P((int)); +int af2epsvproto __P((int)); void long_passive __P((char *, int)); +int extended_port __P((const char *)); +void epsv_protounsupp __P((const char *)); void perror_reply __P((int, char *)); void pwd __P((void)); void removedir __P((char *)); diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y index 1631f12520c..1ec6ab95af2 100644 --- a/libexec/ftpd/ftpcmd.y +++ b/libexec/ftpd/ftpcmd.y @@ -1,4 +1,4 @@ -/* $OpenBSD: ftpcmd.y,v 1.23 2000/11/13 16:14:44 itojun Exp $ */ +/* $OpenBSD: ftpcmd.y,v 1.24 2000/11/14 20:27:01 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.23 2000/11/13 16:14:44 itojun Exp $"; +static char rcsid[] = "$OpenBSD: ftpcmd.y,v 1.24 2000/11/14 20:27:01 itojun Exp $"; #endif #endif /* not lint */ @@ -235,93 +235,8 @@ cmd | EPRT check_login_epsvall SP STRING CRLF { - char *tmp = NULL; - char *result[3]; - char *p, *q; - char delim; - struct addrinfo hints; - struct addrinfo *res; - int i; - - if ($2) { /* XXX indentation */ - - 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:; - - } + if ($2) + extended_port($4); } | PASV check_login_epsvall CRLF @@ -336,21 +251,8 @@ cmd } | EPSV check_login SP NUMBER CRLF { - int pf; - if ($2) { - switch ($4) { - case 1: - pf = PF_INET; - break; - case 2: - pf = PF_INET6; - break; - default: - pf = -1; /*junk*/ - break; - } - long_passive("EPSV", pf); - } + if ($2) + long_passive("EPSV", epsvproto2af($4)); } | EPSV check_login SP ALL CRLF { diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index 0d1a817b7d0..d1f93705ff3 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ftpd.c,v 1.79 2000/09/15 07:13:45 deraadt Exp $ */ +/* $OpenBSD: ftpd.c,v 1.80 2000/11/14 20:27:01 itojun Exp $ */ /* $NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $ */ /* @@ -2179,6 +2179,61 @@ pasv_error: } /* + * convert protocol identifier to/from AF + */ +int +lpsvproto2af(int proto) +{ + + switch (proto) { + case 4: return AF_INET; +#ifdef INET6 + case 6: return AF_INET6; +#endif + default: return -1; + } +} + +int +af2lpsvproto(int af) +{ + + switch (af) { + case AF_INET: return 4; +#ifdef INET6 + case AF_INET6: return 6; +#endif + default: return -1; + } +} + +int +epsvproto2af(int proto) +{ + + switch (proto) { + case 1: return AF_INET; +#ifdef INET6 + case 2: return AF_INET6; +#endif + default: return -1; + } +} + +int +af2epsvproto(int af) +{ + + switch (af) { + case AF_INET: return 1; +#ifdef INET6 + case AF_INET6: return 2; +#endif + default: return -1; + } +} + +/* * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...) * 229 Entering Extended Passive Mode (|||port|) */ @@ -2194,31 +2249,17 @@ long_passive(char *cmd, int pf) 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*/ + if (pf != PF_UNSPEC && ctrl_addr.su_family != pf) { + /* + * XXX + * only EPRT/EPSV ready clients will understand this + */ + if (strcmp(cmd, "EPSV") != 0) + reply(501, "Network protocol mismatch"); /*XXX*/ + else + epsv_protounsupp("Network protocol mismatch"); - return; - } + return; } if (pdata >= 0) @@ -2312,6 +2353,128 @@ long_passive(char *cmd, int pf) } /* + * EPRT |proto|addr|port| + */ +int +extended_port(const char *arg) +{ + char *tmp = NULL; + char *result[3]; + char *p, *q; + char delim; + struct addrinfo hints; + struct addrinfo *res = NULL; + int i; + unsigned long proto; + + if (epsvall) { + reply(501, "EPRT disallowed after EPSV ALL"); + return -1; + } + + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + + tmp = strdup(arg); + 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) + goto parsefail; + *q++ = '\0'; + result[i] = p; + p = q; + } + + /* some more sanity check */ + p = NULL; + (void)strtoul(result[2], &p, 10); + if (!*result[2] || *p) + goto protounsupp; + p = NULL; + proto = strtoul(result[0], &p, 10); + if (!*result[0] || *p) + goto protounsupp; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = epsvproto2af((int)proto); + if (hints.ai_family < 0) + goto protounsupp; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; /*no DNS*/ + if (getaddrinfo(result[1], result[2], &hints, &res)) + goto parsefail; + if (res->ai_next) + goto parsefail; + if (sizeof(data_dest) < res->ai_addrlen) + 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; + } + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "EPRT command successful."); + + if (tmp) + free(tmp); + if (res) + freeaddrinfo(res); + return 0; + +parsefail: + reply(500, "Invalid argument, rejected."); + usedefault = 1; + if (tmp) + free(tmp); + if (res) + freeaddrinfo(res); + return -1; + +protounsupp: + epsv_protounsupp("Protocol not supported"); + usedefault = 1; + if (tmp) + free(tmp); + if (res) + freeaddrinfo(res); + return -1; +} + +/* + * 522 Protocol not supported (proto,...) + * as we assume address family for control and data connections are the same, + * we do not return the list of address families we support - instead, we + * return the address family of the control connection. + */ +void +epsv_protounsupp(const char *message) +{ + int proto; + + proto = af2epsvproto(ctrl_addr.su_family); + if (proto < 0) + reply(501, "%s", message); /*XXX*/ + else + reply(522, "%s, use (%d)", message, proto); +} + +/* * Generate unique name for file with basename "local". * The file named "local" is already known to exist. * Generates failure reply on error. |