Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/libexec/ftpd/ftpcmd.y,v rcsdiff: /ftp/cvs/cvsroot/src/libexec/ftpd/ftpcmd.y,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.48.2.2 retrieving revision 1.91 diff -u -p -r1.48.2.2 -r1.91 --- src/libexec/ftpd/ftpcmd.y 2001/03/29 14:14:17 1.48.2.2 +++ src/libexec/ftpd/ftpcmd.y 2011/01/14 23:56:13 1.91 @@ -1,7 +1,7 @@ -/* $NetBSD: ftpcmd.y,v 1.48.2.2 2001/03/29 14:14:17 lukem Exp $ */ +/* $NetBSD: ftpcmd.y,v 1.91 2011/01/14 23:56:13 christos Exp $ */ /*- - * Copyright (c) 1997-2000 The NetBSD Foundation, Inc. + * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -15,13 +15,6 @@ * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -48,11 +41,7 @@ * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -83,7 +72,7 @@ #if 0 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; #else -__RCSID("$NetBSD: ftpcmd.y,v 1.48.2.2 2001/03/29 14:14:17 lukem Exp $"); +__RCSID("$NetBSD: ftpcmd.y,v 1.91 2011/01/14 23:56:13 christos Exp $"); #endif #endif /* not lint */ @@ -97,10 +86,7 @@ __RCSID("$NetBSD: ftpcmd.y,v 1.48.2.2 20 #include #include -#include #include -#include -#include #include #include #include @@ -121,21 +107,34 @@ static int cmd_type; static int cmd_form; static int cmd_bytesz; -char cbuf[512]; +char cbuf[FTP_BUFLEN]; +char *cmdp; char *fromname; +extern int epsvall; +struct tab sitetab[]; + +static int check_write(const char *, int); +static void help(struct tab *, const char *); +static void port_check(const char *, int); + int yylex(void); + %} %union { - int i; - char *s; + struct { + LLT ll; + int i; + } u; + char *s; + const char *cs; } %token A B C E F I L N P R S T - SP CRLF COMMA + SP CRLF COMMA ALL USER PASS ACCT CWD CDUP SMNT QUIT REIN PORT PASV TYPE STRU @@ -161,28 +160,25 @@ char *fromname; LEXERR %token STRING -%token ALL -%token NUMBER +%token NUMBER -%type check_login octal_number byte_size -%type struct_code mode_code type_code form_code decimal_integer +%type check_login octal_number byte_size +%type struct_code mode_code type_code form_code decimal_integer %type pathstring pathname password username %type mechanism_name base64data prot_code -%start cmd_list +%start cmd_sel %% -cmd_list - : /* empty */ - - | cmd_list cmd +cmd_sel + : cmd { - fromname = NULL; + REASSIGN(fromname, NULL); restart_point = (off_t) 0; } - | cmd_list rcmd + | rcmd ; @@ -295,32 +291,48 @@ cmd | LPSV check_login CRLF { if ($2) { - if (epsvall) - reply(501, - "LPSV disallowed after EPSV ALL"); - else - long_passive("LPSV", PF_UNSPEC); + if (CURCLASS_FLAGS_ISSET(passive)) { + if (epsvall) + reply(501, + "LPSV disallowed after EPSV ALL"); + else + long_passive("LPSV", PF_UNSPEC); + } else + reply(500, "LPSV mode not available."); } } | EPSV check_login SP NUMBER CRLF { - if ($2) - long_passive("EPSV", epsvproto2af($4)); + if ($2) { + if (CURCLASS_FLAGS_ISSET(passive)) + long_passive("EPSV", + epsvproto2af($4.i)); + else + reply(500, "EPSV mode not available."); + } } | EPSV check_login SP ALL CRLF { if ($2) { - reply(200, "EPSV ALL command successful."); - epsvall++; + if (CURCLASS_FLAGS_ISSET(passive)) { + reply(200, + "EPSV ALL command successful."); + epsvall++; + } else + reply(500, "EPSV mode not available."); } } | EPSV check_login CRLF { - if ($2) - long_passive("EPSV", PF_UNSPEC); + if ($2) { + if (CURCLASS_FLAGS_ISSET(passive)) + long_passive("EPSV", PF_UNSPEC); + else + reply(500, "EPSV mode not available."); + } } | TYPE check_login SP type_code CRLF @@ -442,8 +454,7 @@ cmd if (check_write($3, 0)) { if (fromname) { renamecmd(fromname, $3); - free(fromname); - fromname = NULL; + REASSIGN(fromname, NULL); } else { reply(503, "Bad sequence of commands."); } @@ -454,7 +465,9 @@ cmd | ABOR check_login CRLF { - if ($2) + if (is_oob) + abor(); + else if ($2) reply(225, "ABOR command successful."); } @@ -490,16 +503,20 @@ cmd | LIST check_login CRLF { - char *argv[] = { INTERNAL_LS, "-lgA", NULL }; + const char *argv[] = { INTERNAL_LS, "-lgA", NULL }; + if (CURCLASS_FLAGS_ISSET(hidesymlinks)) + argv[1] = "-LlgA"; if ($2) retrieve(argv, ""); } | LIST check_login SP pathname CRLF { - char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; + const char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; + if (CURCLASS_FLAGS_ISSET(hidesymlinks)) + argv[1] = "-LlgA"; if ($2 && $4 != NULL) { argv[2] = $4; retrieve(argv, $4); @@ -529,7 +546,7 @@ cmd | SITE SP CHMOD SP octal_number SP pathname CRLF { if (check_write($7, 0)) { - if ($5 > 0777) + if (($5 == -1) || ($5 > 0777)) reply(501, "CHMOD: Mode value must be between 0 and 0777"); else if (chmod($7, $5) < 0) @@ -551,24 +568,28 @@ cmd { if ($4) { reply(200, - "Current IDLE time limit is %d seconds; max %d", - curclass.timeout, curclass.maxtimeout); + "Current IDLE time limit is " LLF + " seconds; max " LLF, + (LLT)curclass.timeout, + (LLT)curclass.maxtimeout); } } | SITE SP IDLE check_login SP NUMBER CRLF { if ($4) { - if ($6 < 30 || $6 > curclass.maxtimeout) { + if ($6.i < 30 || $6.i > curclass.maxtimeout) { reply(501, - "IDLE time limit must be between 30 and %d seconds", - curclass.maxtimeout); + "IDLE time limit must be between 30 and " + LLF " seconds", + (LLT)curclass.maxtimeout); } else { - curclass.timeout = $6; + curclass.timeout = $6.i; (void) alarm(curclass.timeout); reply(200, - "IDLE time limit set to %d seconds", - curclass.timeout); + "IDLE time limit set to " + LLF " seconds", + (LLT)curclass.timeout); } } } @@ -584,19 +605,17 @@ cmd | SITE SP RATEGET check_login SP STRING CRLF { + char errbuf[100]; char *p = $6; LLT rate; if ($4) { - rate = strsuftoll(p); - if (rate == -1) - reply(501, "Invalid RATEGET %s", p); - else if (curclass.maxrateget && - rate > curclass.maxrateget) - reply(501, - "RATEGET " LLF " is larger than maximum RATEGET " LLF, - (LLT)rate, - (LLT)curclass.maxrateget); + rate = strsuftollx("RATEGET", p, 0, + curclass.maxrateget + ? curclass.maxrateget + : LLTMAX, errbuf, sizeof(errbuf)); + if (errbuf[0]) + reply(501, "%s", errbuf); else { curclass.rateget = rate; reply(200, @@ -618,19 +637,17 @@ cmd | SITE SP RATEPUT check_login SP STRING CRLF { + char errbuf[100]; char *p = $6; LLT rate; if ($4) { - rate = strsuftoll(p); - if (rate == -1) - reply(501, "Invalid RATEPUT %s", p); - else if (curclass.maxrateput && - rate > curclass.maxrateput) - reply(501, - "RATEPUT " LLF " is larger than maximum RATEPUT " LLF, - (LLT)rate, - (LLT)curclass.maxrateput); + rate = strsuftollx("RATEPUT", p, 0, + curclass.maxrateput + ? curclass.maxrateput + : LLTMAX, errbuf, sizeof(errbuf)); + if (errbuf[0]) + reply(501, "%s", errbuf); else { curclass.rateput = rate; reply(200, @@ -656,7 +673,7 @@ cmd { int oldmask; - if ($4 && CURCLASS_FLAGS_ISSET(modify)) { + if ($4 && check_write("", 0)) { if (($6 == -1) || ($6 > 0777)) { reply(501, "Bad UMASK value"); } else { @@ -687,7 +704,10 @@ cmd | STAT CRLF { - statcmd(); + if (is_oob) + statxfer(); + else + statcmd(); } | HELP CRLF @@ -782,7 +802,7 @@ cmd } - /* extensions from draft-ietf-ftpext-mlst-11 */ + /* RFC 3659 */ /* * Return size of file in a format suitable for @@ -858,11 +878,11 @@ cmd ; rcmd - : REST check_login SP byte_size CRLF + : REST check_login SP NUMBER CRLF { if ($2) { - fromname = NULL; - restart_point = $4; /* XXX: $4 is only "int" */ + REASSIGN(fromname, NULL); + restart_point = (off_t)$4.ll; reply(350, "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", (LLT)restart_point); @@ -872,8 +892,10 @@ rcmd | RNFR SP pathname CRLF { restart_point = (off_t) 0; - if (check_write($3, 0)) + if (check_write($3, 0)) { + REASSIGN(fromname, NULL); fromname = renamefrom($3); + } if ($3 != NULL) free($3); } @@ -894,6 +916,9 @@ password byte_size : NUMBER + { + $$ = $1.i; + } ; host_port @@ -906,9 +931,9 @@ host_port data_dest.su_len = sizeof(struct sockaddr_in); data_dest.su_family = AF_INET; p = (char *)&data_dest.su_port; - p[0] = $9; p[1] = $11; + p[0] = $9.i; p[1] = $11.i; a = (char *)&data_dest.su_addr; - a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; + a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; } ; @@ -923,12 +948,12 @@ host_long_port4 data_dest.su_len = sizeof(struct sockaddr_in); data_dest.su_family = AF_INET; p = (char *)&data_dest.su_port; - p[0] = $15; p[1] = $17; + p[0] = $15.i; p[1] = $17.i; a = (char *)&data_dest.su_addr; - a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; + a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; /* reject invalid LPRT command */ - if ($1 != 4 || $3 != 4 || $13 != 2) + if ($1.i != 4 || $3.i != 4 || $13.i != 2) memset(&data_dest, 0, sizeof(data_dest)); } ; @@ -942,18 +967,24 @@ host_long_port6 NUMBER { #ifdef INET6 - char *a, *p; + unsigned char buf[16]; - memset(&data_dest, 0, sizeof(data_dest)); + (void)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.si_su.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; + buf[0] = $39.i; buf[1] = $41.i; + (void)memcpy(&data_dest.su_port, buf, + sizeof(&data_dest.su_port)); + buf[0] = $5.i; buf[1] = $7.i; + buf[2] = $9.i; buf[3] = $11.i; + buf[4] = $13.i; buf[5] = $15.i; + buf[6] = $17.i; buf[7] = $19.i; + buf[8] = $21.i; buf[9] = $23.i; + buf[10] = $25.i; buf[11] = $27.i; + buf[12] = $29.i; buf[13] = $31.i; + buf[14] = $33.i; buf[15] = $35.i; + (void)memcpy(&data_dest.si_su.su_sin6.sin6_addr, + buf, sizeof(data_dest.si_su.su_sin6.sin6_addr)); if (his_addr.su_family == AF_INET6) { /* XXX: more sanity checks! */ data_dest.su_scope_id = his_addr.su_scope_id; @@ -962,7 +993,7 @@ host_long_port6 memset(&data_dest, 0, sizeof(data_dest)); #endif /* INET6 */ /* reject invalid LPRT command */ - if ($1 != 6 || $3 != 16 || $37 != 2) + if ($1.i != 6 || $3.i != 16 || $37.i != 2) memset(&data_dest, 0, sizeof(data_dest)); } ; @@ -1078,21 +1109,33 @@ pathname * others. */ if (logged_in && $1 && *$1 == '~') { - glob_t gl; - int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; + char *path, *home, *result; + size_t len; + path = strchr($1 + 1, '/'); + if (path != NULL) + *path++ = '\0'; if ($1[1] == '\0') - $$ = xstrdup(homedir); + home = homedir; else { - memset(&gl, 0, sizeof(gl)); - if (glob($1, flags, NULL, &gl) || - gl.gl_pathc == 0) { - reply(550, "not found"); - $$ = NULL; - } else - $$ = xstrdup(gl.gl_pathv[0]); - globfree(&gl); + struct passwd *hpw; + + if ((hpw = getpwnam($1 + 1)) != NULL) + home = hpw->pw_dir; + else + home = $1; + } + len = strlen(home) + 1; + if (path != NULL) + len += strlen(path) + 1; + if ((result = malloc(len)) == NULL) + fatal("Local resource failure: malloc"); + strlcpy(result, home, len); + if (path != NULL) { + strlcat(result, "/", len); + strlcat(result, path, len); } + $$ = result; free($1); } else $$ = $1; @@ -1112,7 +1155,7 @@ octal_number * Convert a number that was read as decimal number * to what it would be if it had been read as octal. */ - dec = $1; + dec = $1.i; multby = 1; ret = 0; while (dec) { @@ -1143,6 +1186,9 @@ prot_code decimal_integer : NUMBER + { + $$ = $1.i; + } ; check_login @@ -1170,105 +1216,100 @@ check_login #define SITECMD 7 /* SITE command */ #define NSTR 8 /* Number followed by a string */ #define NOARGS 9 /* No arguments allowed */ +#define EOLN 10 /* End of line */ struct tab cmdtab[] = { /* From RFC 959, in order defined (5.3.1) */ - { "USER", USER, STR1, 1, " username" }, - { "PASS", PASS, ZSTR1, 1, " password" }, - { "ACCT", ACCT, STR1, 0, "(specify account)" }, - { "CWD", CWD, OSTR, 1, "[ directory-name ]" }, - { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" }, - { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, - { "QUIT", QUIT, NOARGS, 1, "(terminate service)" }, - { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" }, - { "PORT", PORT, ARGS, 1, " b0, b1, b2, b3, b4" }, - { "LPRT", LPRT, ARGS, 1, " af, hal, h1, h2, h3,..., pal, p1, p2..." }, - { "EPRT", EPRT, STR1, 1, " |af|addr|port|" }, - { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" }, - { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, - { "EPSV", EPSV, ARGS, 1, "[ af|ALL]" }, - { "TYPE", TYPE, ARGS, 1, " [ A | E | I | L ]" }, - { "STRU", STRU, ARGS, 1, "(specify file structure)" }, - { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, - { "RETR", RETR, STR1, 1, " file-name" }, - { "STOR", STOR, STR1, 1, " file-name" }, - { "STOU", STOU, STR1, 1, " file-name" }, - { "APPE", APPE, STR1, 1, " file-name" }, - { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, - { "REST", REST, ARGS, 1, " offset (restart command)" }, - { "RNFR", RNFR, STR1, 1, " file-name" }, - { "RNTO", RNTO, STR1, 1, " file-name" }, - { "ABOR", ABOR, NOARGS, 1, "(abort operation)" }, - { "DELE", DELE, STR1, 1, " file-name" }, - { "RMD", RMD, STR1, 1, " path-name" }, - { "MKD", MKD, STR1, 1, " path-name" }, - { "PWD", PWD, NOARGS, 1, "(return current directory)" }, - { "LIST", LIST, OSTR, 1, "[ path-name ]" }, - { "NLST", NLST, OSTR, 1, "[ path-name ]" }, - { "SITE", SITE, SITECMD, 1, "site-cmd [ arguments ]" }, - { "SYST", SYST, NOARGS, 1, "(get type of operating system)" }, - { "STAT", STAT, OSTR, 1, "[ path-name ]" }, - { "HELP", HELP, OSTR, 1, "[ ]" }, - { "NOOP", NOOP, NOARGS, 2, "" }, + { "USER", USER, STR1, 1, " username", 0, }, + { "PASS", PASS, ZSTR1, 1, " password", 0, }, + { "ACCT", ACCT, STR1, 0, "(specify account)", 0, }, + { "CWD", CWD, OSTR, 1, "[ directory-name ]", 0, }, + { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, + { "SMNT", SMNT, ARGS, 0, "(structure mount)", 0, }, + { "QUIT", QUIT, NOARGS, 1, "(terminate service)", 0, }, + { "REIN", REIN, NOARGS, 0, "(reinitialize server state)", 0, }, + { "PORT", PORT, ARGS, 1, " b0, b1, b2, b3, b4, b5", 0, }, + { "LPRT", LPRT, ARGS, 1, " af, hal, h1, h2, h3,..., pal, p1, p2...", 0, }, + { "EPRT", EPRT, STR1, 1, " |af|addr|port|", 0, }, + { "PASV", PASV, NOARGS, 1, "(set server in passive mode)", 0, }, + { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)", 0, }, + { "EPSV", EPSV, ARGS, 1, "[ af|ALL]", 0, }, + { "TYPE", TYPE, ARGS, 1, " [ A | E | I | L ]", 0, }, + { "STRU", STRU, ARGS, 1, "(specify file structure)", 0, }, + { "MODE", MODE, ARGS, 1, "(specify transfer mode)", 0, }, + { "RETR", RETR, STR1, 1, " file-name", 0, }, + { "STOR", STOR, STR1, 1, " file-name", 0, }, + { "STOU", STOU, STR1, 1, " file-name", 0, }, + { "APPE", APPE, STR1, 1, " file-name", 0, }, + { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)", 0, }, + { "REST", REST, ARGS, 1, " offset (restart command)", 0, }, + { "RNFR", RNFR, STR1, 1, " file-name", 0, }, + { "RNTO", RNTO, STR1, 1, " file-name", 0, }, + { "ABOR", ABOR, NOARGS, 4, "(abort operation)", 0, }, + { "DELE", DELE, STR1, 1, " file-name", 0, }, + { "RMD", RMD, STR1, 1, " path-name", 0, }, + { "MKD", MKD, STR1, 1, " path-name", 0, }, + { "PWD", PWD, NOARGS, 1, "(return current directory)", 0, }, + { "LIST", LIST, OSTR, 1, "[ path-name ]", 0, }, + { "NLST", NLST, OSTR, 1, "[ path-name ]", 0, }, + { "SITE", SITE, SITECMD, 1, "site-cmd [ arguments ]", 0, }, + { "SYST", SYST, NOARGS, 1, "(get type of operating system)", 0, }, + { "STAT", STAT, OSTR, 4, "[ path-name ]", 0, }, + { "HELP", HELP, OSTR, 1, "[ ]", 0, }, + { "NOOP", NOOP, NOARGS, 2, "", 0, }, /* From RFC 2228, in order defined */ - { "AUTH", AUTH, STR1, 1, " mechanism-name" }, - { "ADAT", ADAT, STR1, 1, " base-64-data" }, - { "PROT", PROT, STR1, 1, " prot-code" }, - { "PBSZ", PBSZ, ARGS, 1, " decimal-integer" }, - { "CCC", CCC, NOARGS, 1, "(Disable data protection)" }, - { "MIC", MIC, STR1, 1, " base64data" }, - { "CONF", CONF, STR1, 1, " base64data" }, - { "ENC", ENC, STR1, 1, " base64data" }, + { "AUTH", AUTH, STR1, 1, " mechanism-name", 0, }, + { "ADAT", ADAT, STR1, 1, " base-64-data", 0, }, + { "PROT", PROT, STR1, 1, " prot-code", 0, }, + { "PBSZ", PBSZ, ARGS, 1, " decimal-integer", 0, }, + { "CCC", CCC, NOARGS, 1, "(Disable data protection)", 0, }, + { "MIC", MIC, STR1, 4, " base64data", 0, }, + { "CONF", CONF, STR1, 4, " base64data", 0, }, + { "ENC", ENC, STR1, 4, " base64data", 0, }, /* From RFC 2389, in order defined */ - { "FEAT", FEAT, NOARGS, 1, "(display extended features)" }, - { "OPTS", OPTS, STR1, 1, " command [ options ]" }, + { "FEAT", FEAT, NOARGS, 1, "(display extended features)", 0, }, + { "OPTS", OPTS, STR1, 1, " command [ options ]", 0, }, - /* from draft-ietf-ftpext-mlst-11 */ - { "MDTM", MDTM, OSTR, 1, " path-name" }, - { "SIZE", SIZE, OSTR, 1, " path-name" }, - { "MLST", MLST, OSTR, 2, "[ path-name ]" }, - { "MLSD", MLSD, OSTR, 1, "[ directory-name ]" }, + /* From RFC 3659, in order defined */ + { "MDTM", MDTM, OSTR, 1, " path-name", 0, }, + { "SIZE", SIZE, OSTR, 1, " path-name", 0, }, + { "MLST", MLST, OSTR, 2, "[ path-name ]", 0, }, + { "MLSD", MLSD, OSTR, 1, "[ directory-name ]", 0, }, /* obsolete commands */ - { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, - { "MLFL", MLFL, OSTR, 0, "(mail file)" }, - { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, - { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, - { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, - { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, - { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, - { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" }, - { "XCWD", CWD, OSTR, 1, "[ directory-name ]" }, - { "XMKD", MKD, STR1, 1, " path-name" }, - { "XPWD", PWD, NOARGS, 1, "(return current directory)" }, - { "XRMD", RMD, STR1, 1, " path-name" }, + { "MAIL", MAIL, OSTR, 0, "(mail to user)", 0, }, + { "MLFL", MLFL, OSTR, 0, "(mail file)", 0, }, + { "MRCP", MRCP, STR1, 0, "(mail recipient)", 0, }, + { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)", 0, }, + { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)", 0, }, + { "MSND", MSND, OSTR, 0, "(mail send to terminal)", 0, }, + { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)", 0, }, + { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, + { "XCWD", CWD, OSTR, 1, "[ directory-name ]", 0, }, + { "XMKD", MKD, STR1, 1, " path-name", 0, }, + { "XPWD", PWD, NOARGS, 1, "(return current directory)", 0, }, + { "XRMD", RMD, STR1, 1, " path-name", 0, }, - { NULL, 0, 0, 0, 0 } + { NULL, 0, 0, 0, 0, 0, } }; struct tab sitetab[] = { - { "CHMOD", CHMOD, NSTR, 1, " mode file-name" }, - { "HELP", HELP, OSTR, 1, "[ ]" }, - { "IDLE", IDLE, ARGS, 1, "[ maximum-idle-time ]" }, - { "RATEGET", RATEGET,OSTR, 1, "[ get-throttle-rate ]" }, - { "RATEPUT", RATEPUT,OSTR, 1, "[ put-throttle-rate ]" }, - { "UMASK", UMASK, ARGS, 1, "[ umask ]" }, - { NULL, 0, 0, 0, NULL } + { "CHMOD", CHMOD, NSTR, 1, " mode file-name", 0, }, + { "HELP", HELP, OSTR, 1, "[ ]", 0, }, + { "IDLE", IDLE, ARGS, 1, "[ maximum-idle-time ]", 0, }, + { "RATEGET", RATEGET,OSTR, 1, "[ get-throttle-rate ]", 0, }, + { "RATEPUT", RATEPUT,OSTR, 1, "[ put-throttle-rate ]", 0, }, + { "UMASK", UMASK, ARGS, 1, "[ umask ]", 0, }, + { NULL, 0, 0, 0, 0, 0, } }; -static int check_write(const char *, int); -static void help(struct tab *, const char *); -static void port_check(const char *, int); -static void toolong(int); -static int yylex(void); - -extern int epsvall; - /* * Check if a filename is allowed to be modified (isupload == 0) or * uploaded (isupload == 1), and if necessary, check the filename is `sane'. + * If the filename is NULL, fail. + * If the filename is "", don't do the sane name check. */ static int check_write(const char *file, int isupload) @@ -1289,14 +1330,15 @@ check_write(const char *file, int isuplo reply(502, "No permission to use this command."); return (0); } + /* checking sanenames */ - if (CURCLASS_FLAGS_ISSET(sanenames)) { + if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) { const char *p; if (file[0] == '.') goto insane_name; for (p = file; *p; p++) { - if (isalnum(*p) || *p == '-' || *p == '+' || + if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' || *p == ',' || *p == '.' || *p == '_') continue; insane_name: @@ -1320,10 +1362,14 @@ lookup(struct tab *p, const char *cmd) #include /* - * getline - a hacked up version of fgets to ignore TELNET escape codes. + * get_line - a hacked up version of fgets to ignore TELNET escape codes. + * `s' is the buffer to read into. + * `n' is the 1 less than the size of the buffer, to allow trailing NUL + * `iop' is the FILE to read from. + * Returns 0 on success, -1 on EOF, -2 if the command was too long. */ -char * -getline(char *s, int n, FILE *iop) +int +get_line(char *s, int n, FILE *iop) { int c; char *cs; @@ -1334,10 +1380,10 @@ getline(char *s, int n, FILE *iop) *cs++ = tmpline[c]; if (tmpline[c] == '\n') { *cs++ = '\0'; - if (debug) + if (ftpd_debug) syslog(LOG_DEBUG, "command: %s", s); tmpline[0] = '\0'; - return(s); + return(0); } if (c == 0) tmpline[0] = '\0'; @@ -1376,13 +1422,27 @@ getline(char *s, int n, FILE *iop) } } *cs++ = c; - if (--n <= 0 || c == '\n') + if (--n <= 0) { + /* + * If command doesn't fit into buffer, discard the + * rest of the command and indicate truncation. + * This prevents the command to be split up into + * multiple commands. + */ + if (ftpd_debug) + syslog(LOG_DEBUG, + "command too long, last char: %d", c); + while (c != '\n' && (c = getc(iop)) != EOF) + continue; + return (-2); + } + if (c == '\n') break; } if (c == EOF && cs == s) - return (NULL); + return (-1); *cs++ = '\0'; - if (debug) { + if (ftpd_debug) { if ((curclass.type != CLASS_GUEST && strncasecmp(s, "PASS ", 5) == 0) || strncasecmp(s, "ACCT ", 5) == 0) { @@ -1402,23 +1462,39 @@ getline(char *s, int n, FILE *iop) syslog(LOG_DEBUG, "command: %.*s", len, s); } } - return (s); + return (0); } -static void -toolong(int signo) +void +ftp_handle_line(char *cp) { - reply(421, - "Timeout (%d seconds): closing control connection.", - curclass.timeout); - if (logging) - syslog(LOG_INFO, "User %s timed out after %d seconds", - (pw ? pw->pw_name : "unknown"), curclass.timeout); - dologout(1); + cmdp = cp; + yyparse(); } -static int +void +ftp_loop(void) +{ + int ret; + + while (1) { + (void) alarm(curclass.timeout); + ret = get_line(cbuf, sizeof(cbuf)-1, stdin); + (void) alarm(0); + if (ret == -1) { + reply(221, "You could at least say goodbye."); + dologout(0); + } else if (ret == -2) { + reply(500, "Command too long."); + } else { + ftp_handle_line(cbuf); + } + } + /*NOTREACHED*/ +} + +int yylex(void) { static int cpos, state; @@ -1431,56 +1507,52 @@ yylex(void) case CMD: hasyyerrored = 0; - (void) signal(SIGALRM, toolong); - (void) alarm(curclass.timeout); - if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { - reply(221, "You could at least say goodbye."); - dologout(0); - } - (void) alarm(0); - if ((cp = strchr(cbuf, '\r'))) { + if ((cp = strchr(cmdp, '\r'))) { *cp = '\0'; -#if HAVE_SETPROCTITLE - if (strncasecmp(cbuf, "PASS", 4) != 0 && - strncasecmp(cbuf, "ACCT", 4) != 0) - setproctitle("%s: %s", proctitle, cbuf); -#endif /* HAVE_SETPROCTITLE */ +#if defined(HAVE_SETPROCTITLE) + if (strncasecmp(cmdp, "PASS", 4) != 0 && + strncasecmp(cmdp, "ACCT", 4) != 0) + setproctitle("%s: %s", proctitle, cmdp); +#endif /* defined(HAVE_SETPROCTITLE) */ *cp++ = '\n'; *cp = '\0'; } - if ((cp = strpbrk(cbuf, " \n"))) - cpos = cp - cbuf; + if ((cp = strpbrk(cmdp, " \n"))) + cpos = cp - cmdp; if (cpos == 0) cpos = 4; - c = cbuf[cpos]; - cbuf[cpos] = '\0'; - p = lookup(cmdtab, cbuf); - cbuf[cpos] = c; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + p = lookup(cmdtab, cmdp); + cmdp[cpos] = c; if (p != NULL) { - if (! CMD_IMPLEMENTED(p)) { + if (is_oob && ! CMD_OOB(p)) { + /* command will be handled in-band */ + return (0); + } else if (! CMD_IMPLEMENTED(p)) { reply(502, "%s command not implemented.", p->name); hasyyerrored = 1; break; } state = p->state; - yylval.s = p->name; + yylval.cs = p->name; return (p->token); } break; case SITECMD: - if (cbuf[cpos] == ' ') { + if (cmdp[cpos] == ' ') { cpos++; return (SP); } - cp = &cbuf[cpos]; + cp = &cmdp[cpos]; if ((cp2 = strpbrk(cp, " \n"))) - cpos = cp2 - cbuf; - c = cbuf[cpos]; - cbuf[cpos] = '\0'; + cpos = cp2 - cmdp; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; p = lookup(sitetab, cp); - cbuf[cpos] = c; + cmdp[cpos] = c; if (p != NULL) { if (!CMD_IMPLEMENTED(p)) { reply(502, "SITE %s command not implemented.", @@ -1489,14 +1561,14 @@ yylex(void) break; } state = p->state; - yylval.s = p->name; + yylval.cs = p->name; return (p->token); } break; case OSTR: - if (cbuf[cpos] == '\n') { - state = CMD; + if (cmdp[cpos] == '\n') { + state = EOLN; return (CRLF); } /* FALLTHROUGH */ @@ -1504,7 +1576,7 @@ yylex(void) case STR1: case ZSTR1: dostr1: - if (cbuf[cpos] == ' ') { + if (cmdp[cpos] == ' ') { cpos++; state = state == OSTR ? STR2 : state+1; return (SP); @@ -1512,41 +1584,41 @@ yylex(void) break; case ZSTR2: - if (cbuf[cpos] == '\n') { - state = CMD; + if (cmdp[cpos] == '\n') { + state = EOLN; return (CRLF); } /* FALLTHROUGH */ case STR2: - cp = &cbuf[cpos]; + cp = &cmdp[cpos]; n = strlen(cp); cpos += n - 1; /* * Make sure the string is nonempty and \n terminated. */ - if (n > 1 && cbuf[cpos] == '\n') { - cbuf[cpos] = '\0'; - yylval.s = xstrdup(cp); - cbuf[cpos] = '\n'; + if (n > 1 && cmdp[cpos] == '\n') { + cmdp[cpos] = '\0'; + yylval.s = ftpd_strdup(cp); + cmdp[cpos] = '\n'; state = ARGS; return (STRING); } break; case NSTR: - if (cbuf[cpos] == ' ') { + if (cmdp[cpos] == ' ') { cpos++; return (SP); } - if (isdigit(cbuf[cpos])) { - cp = &cbuf[cpos]; - while (isdigit(cbuf[++cpos])) + if (isdigit((unsigned char)cmdp[cpos])) { + cp = &cmdp[cpos]; + while (isdigit((unsigned char)cmdp[++cpos])) ; - c = cbuf[cpos]; - cbuf[cpos] = '\0'; - yylval.i = atoi(cp); - cbuf[cpos] = c; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + yylval.u.i = atoi(cp); + cmdp[cpos] = c; state = STR1; return (NUMBER); } @@ -1554,26 +1626,26 @@ yylex(void) goto dostr1; case ARGS: - if (isdigit(cbuf[cpos])) { - cp = &cbuf[cpos]; - while (isdigit(cbuf[++cpos])) + if (isdigit((unsigned char)cmdp[cpos])) { + cp = &cmdp[cpos]; + while (isdigit((unsigned char)cmdp[++cpos])) ; - c = cbuf[cpos]; - cbuf[cpos] = '\0'; - yylval.i = atoi(cp); - cbuf[cpos] = c; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + yylval.u.i = atoi(cp); + yylval.u.ll = STRTOLL(cp, (char **)NULL, 10); + cmdp[cpos] = c; return (NUMBER); } - if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 - && !isalnum(cbuf[cpos + 3])) { - yylval.s = xstrdup("ALL"); + if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 + && !isalnum((unsigned char)cmdp[cpos + 3])) { cpos += 3; - return ALL; + return (ALL); } - switch (cbuf[cpos++]) { + switch (cmdp[cpos++]) { case '\n': - state = CMD; + state = EOLN; return (CRLF); case ' ': @@ -1634,37 +1706,40 @@ yylex(void) break; case NOARGS: - if (cbuf[cpos] == '\n') { - state = CMD; + if (cmdp[cpos] == '\n') { + state = EOLN; return (CRLF); } - c = cbuf[cpos]; - cbuf[cpos] = '\0'; - reply(501, "'%s' command does not take any arguments.", cbuf); + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + reply(501, "'%s' command does not take any arguments.", cmdp); hasyyerrored = 1; - cbuf[cpos] = c; + cmdp[cpos] = c; break; + case EOLN: + state = CMD; + return (0); + default: fatal("Unknown state in scanner."); } yyerror(NULL); state = CMD; - longjmp(errcatch, 0); - /* NOTREACHED */ + return (0); } /* ARGSUSED */ void -yyerror(char *s) +yyerror(const char *s) { char *cp; - if (hasyyerrored) + if (hasyyerrored || is_oob) return; - if ((cp = strchr(cbuf,'\n')) != NULL) + if ((cp = strchr(cmdp,'\n')) != NULL) *cp = '\0'; - reply(500, "'%s': command not understood.", cbuf); + reply(500, "'%s': command not understood.", cmdp); hasyyerrored = 1; } @@ -1673,12 +1748,12 @@ help(struct tab *ctab, const char *s) { struct tab *c; int width, NCMDS; - char *type; + const char *htype; if (ctab == sitetab) - type = "SITE "; + htype = "SITE "; else - type = ""; + htype = ""; width = 0, NCMDS = 0; for (c = ctab; c->name != NULL; c++) { int len = strlen(c->name); @@ -1693,7 +1768,7 @@ help(struct tab *ctab, const char *s) int columns, lines; reply(-214, "%s", ""); - reply(0, "The following %scommands are recognized.", type); + reply(0, "The following %scommands are recognized.", htype); reply(0, "(`-' = not implemented, `+' = supports options)"); columns = 76 / width; if (columns == 0) @@ -1728,13 +1803,13 @@ help(struct tab *ctab, const char *s) } c = lookup(ctab, s); if (c == (struct tab *)0) { - reply(502, "Unknown command %s.", s); + reply(502, "Unknown command '%s'.", s); return; } if (CMD_IMPLEMENTED(c)) - reply(214, "Syntax: %s%s %s", type, c->name, c->help); + reply(214, "Syntax: %s%s %s", htype, c->name, c->help); else - reply(214, "%s%-*s\t%s; not implemented.", type, width, + reply(504, "%s%-*s\t%s; not implemented.", htype, width, c->name, c->help); } @@ -1746,6 +1821,13 @@ help(struct tab *ctab, const char *s) static void port_check(const char *cmd, int family) { + char h1[NI_MAXHOST], h2[NI_MAXHOST]; + char s1[NI_MAXHOST], s2[NI_MAXHOST]; +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; +#endif if (epsvall) { reply(501, "%s disallowed after EPSV ALL", cmd); @@ -1763,27 +1845,25 @@ port_check(const char *cmd, int family) /* be paranoid, if told so */ if (CURCLASS_FLAGS_ISSET(checkportcmd)) { - if ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || - (data_dest.su_len != his_addr.su_len)) - goto port_check_fail; - switch (data_dest.su_family) { - case AF_INET: - if (memcmp(&data_dest.su_addr, &his_addr.su_addr, - data_dest.su_len) != 0) - goto port_check_fail; - break; #ifdef INET6 - case AF_INET6: - if (memcmp(&data_dest.su_6addr, &his_addr.su_6addr, - sizeof(data_dest.su_6addr)) != 0) - goto port_check_fail; - if (data_dest.su_scope_id != his_addr.su_scope_id) - goto port_check_fail; - break; + /* + * be paranoid, there are getnameinfo implementation that does + * not present scopeid portion + */ + if (data_dest.su_family == AF_INET6 && + data_dest.su_scope_id != his_addr.su_scope_id) + goto port_check_fail; #endif - default: + + if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, + h1, sizeof(h1), s1, sizeof(s1), niflags)) + goto port_check_fail; + if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + h2, sizeof(h2), s2, sizeof(s2), niflags)) + goto port_check_fail; + + if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) goto port_check_fail; - } } usedefault = 0;