- new ftpd.conf directives:

maxfilesize	set the maximum size of uploaded files
	sanenames	if set, only permit uploaded filenames that contain
			characters from the set "-+,._A-Za-z0-9" and that
			don't start with `.'

- new/changed command line options:
	-e emailaddr	define email address for %E (see below)
	-P dataport	use dataport as the dataport (instead of ctrlport-1)
	-q		use pid files to count users	[default]
	-Q		don't use pid files to count users
	-u		write entries to utmp
	-U		don't write entries to utmp	[default]
	-w		write entries to wtmp		[default]
	-W		don't write entries to wtmp

	  NOTE:	-U used to mean `write utmp entries'. Its meaning has changed
		so that it's orthogonal with -q/-Q and -w/-W. This isn't
		considered a major problem, because using -U isn't going to
		enable something you don't want, but will disable something
		you did want (which is safer).

- new display file escape sequences:
	%E	email address
	%s	literal `s' if the previous %M or %N wasn't ``1''.
	%S	literal `S' if the previous %M or %N wasn't ``1''.

- expand the description of building ~ftp/incoming to cover the
  appropriate ftpd.conf(5) directives (which are defaults, but it pays
  to explicitly explain them)

- replace strsuftoi() with strsuftoll(), which returns a long long if
  supported, otherwise a long

- rework the way that check_modify and check_upload are done in the yacc
  parser; they're merged into a common check_write() function which is
  called explicitly

- merge all ftpclass `flag variables' into a single bitfield-based flag element

- move various common bits of parse_conf() into a couple of macros

- clean up some comments
This commit is contained in:
lukem 2000-11-16 13:15:13 +00:00
parent 3f648dea6e
commit 999fd3d617
8 changed files with 484 additions and 293 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: cmds.c,v 1.7 2000/11/15 02:32:30 lukem Exp $ */
/* $NetBSD: cmds.c,v 1.8 2000/11/16 13:15:13 lukem Exp $ */
/*
* Copyright (c) 1999-2000 The NetBSD Foundation, Inc.
@ -101,7 +101,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: cmds.c,v 1.7 2000/11/15 02:32:30 lukem Exp $");
__RCSID("$NetBSD: cmds.c,v 1.8 2000/11/16 13:15:13 lukem Exp $");
#endif /* not lint */
#include <sys/param.h>
@ -602,7 +602,7 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
* since we only need this info in such a case.
*/
pdir = fe->pdirstat;
if (pdir == NULL && curclass.modify) {
if (pdir == NULL && CURCLASS_FLAGS_ISSET(modify)) {
size_t len;
char realdir[MAXPATHLEN], *p;
struct stat dir;
@ -636,15 +636,15 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
}
/* 'a': can APPE to file */
if (wok && curclass.upload && S_ISREG(fe->stat->st_mode))
if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode))
CPUTC('a', fd);
/* 'c': can create or append to files in directory */
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
CPUTC('c', fd);
/* 'd': can delete file or directory */
if (pdirwok && curclass.modify) {
if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) {
int candel;
candel = 1;
@ -674,7 +674,7 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
CPUTC('e', fd);
/* 'f': can rename file or directory */
if (pdirwok && curclass.modify)
if (pdirwok && CURCLASS_FLAGS_ISSET(modify))
CPUTC('f', fd);
/* 'l': can list directory */
@ -682,11 +682,11 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
CPUTC('l', fd);
/* 'm': can create directory */
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
CPUTC('m', fd);
/* 'p': can remove files in directory */
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
CPUTC('p', fd);
/* 'r': can RETR file */
@ -694,7 +694,7 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
CPUTC('r', fd);
/* 'w': can STOR file */
if (wok && curclass.upload && S_ISREG(fe->stat->st_mode))
if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode))
CPUTC('w', fd);
CPUTC(';', fd);

View File

@ -1,4 +1,4 @@
/* $NetBSD: conf.c,v 1.35 2000/11/15 02:32:30 lukem Exp $ */
/* $NetBSD: conf.c,v 1.36 2000/11/16 13:15:13 lukem Exp $ */
/*-
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
@ -38,7 +38,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: conf.c,v 1.35 2000/11/15 02:32:30 lukem Exp $");
__RCSID("$NetBSD: conf.c,v 1.36 2000/11/16 13:15:13 lukem Exp $");
#endif /* not lint */
#include <sys/types.h>
@ -88,7 +88,6 @@ init_curclass(void)
free(conv);
}
curclass.checkportcmd = 1;
REASSIGN(curclass.chroot, NULL);
REASSIGN(curclass.classname, NULL);
curclass.conversions = NULL;
@ -96,13 +95,12 @@ init_curclass(void)
REASSIGN(curclass.homedir, NULL);
curclass.limit = -1; /* unlimited connections */
REASSIGN(curclass.limitfile, NULL);
curclass.maxfilesize = -1; /* unlimited file size */
curclass.maxrateget = 0;
curclass.maxrateput = 0;
curclass.maxtimeout = 7200; /* 2 hours */
curclass.modify = 1;
REASSIGN(curclass.motd, xstrdup(_PATH_FTPLOGINMESG));
REASSIGN(curclass.notify, NULL);
curclass.passive = 1;
curclass.portmin = 0;
curclass.portmax = 0;
curclass.rateget = 0;
@ -110,7 +108,12 @@ init_curclass(void)
curclass.timeout = 900; /* 15 minutes */
/* curclass.type is set elsewhere */
curclass.umask = 027;
curclass.upload = 1;
CURCLASS_FLAGS_SET(checkportcmd);
CURCLASS_FLAGS_SET(modify);
CURCLASS_FLAGS_SET(passive);
CURCLASS_FLAGS_CLR(sanenames);
CURCLASS_FLAGS_SET(upload);
}
/*
@ -123,7 +126,8 @@ parse_conf(const char *findclass)
FILE *f;
char *buf, *p;
size_t len;
int none, match, rate;
LLT llval;
int none, match;
char *endp;
char *class, *word, *arg, *template;
const char *infile;
@ -134,7 +138,7 @@ parse_conf(const char *findclass)
init_curclass();
REASSIGN(curclass.classname, xstrdup(findclass));
if (strcasecmp(findclass, "guest") == 0) {
curclass.modify = 0;
CURCLASS_FLAGS_CLR(modify);
curclass.umask = 0707;
}
@ -170,19 +174,29 @@ parse_conf(const char *findclass)
strcasecmp(class, "all") == 0) )
continue;
#define CONF_FLAG(x) \
do { \
if (none || \
(!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) \
CURCLASS_FLAGS_CLR(x); \
else \
CURCLASS_FLAGS_SET(x); \
} while (0)
#define CONF_STRING(x) \
do { \
if (none || EMPTYSTR(arg)) \
arg = NULL; \
else \
arg = xstrdup(arg); \
REASSIGN(curclass.x, arg); \
} while (0)
if (strcasecmp(word, "checkportcmd") == 0) {
if (none ||
(!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0))
curclass.checkportcmd = 0;
else
curclass.checkportcmd = 1;
CONF_FLAG(checkportcmd);
} else if (strcasecmp(word, "chroot") == 0) {
if (none || EMPTYSTR(arg))
arg = NULL;
else
arg = xstrdup(arg);
REASSIGN(curclass.chroot, arg);
CONF_STRING(chroot);
} else if (strcasecmp(word, "classtype") == 0) {
if (!none && !EMPTYSTR(arg)) {
@ -253,18 +267,22 @@ parse_conf(const char *findclass)
REASSIGN(conv->command, convcmd);
} else if (strcasecmp(word, "display") == 0) {
if (none || EMPTYSTR(arg))
arg = NULL;
else
arg = xstrdup(arg);
REASSIGN(curclass.display, arg);
CONF_STRING(display);
} else if (strcasecmp(word, "homedir") == 0) {
CONF_STRING(homedir);
} else if (strcasecmp(word, "maxfilesize") == 0) {
if (none || EMPTYSTR(arg))
arg = NULL;
else
arg = xstrdup(arg);
REASSIGN(curclass.homedir, arg);
continue;
llval = strsuftoll(arg);
if (llval == -1) {
syslog(LOG_WARNING,
"%s line %d: invalid maxfilesize %s",
infile, (int)line, arg);
continue;
}
curclass.maxfilesize = llval;
} else if (strcasecmp(word, "limit") == 0) {
int limit;
@ -308,33 +326,16 @@ parse_conf(const char *findclass)
curclass.maxtimeout = timeout;
} else if (strcasecmp(word, "modify") == 0) {
if (none ||
(!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0))
curclass.modify = 0;
else
curclass.modify = 1;
CONF_FLAG(modify);
} else if (strcasecmp(word, "motd") == 0) {
if (none || EMPTYSTR(arg))
arg = NULL;
else
arg = xstrdup(arg);
REASSIGN(curclass.motd, arg);
CONF_STRING(motd);
} else if (strcasecmp(word, "notify") == 0) {
if (none || EMPTYSTR(arg))
arg = NULL;
else
arg = xstrdup(arg);
REASSIGN(curclass.notify, arg);
CONF_STRING(notify);
} else if (strcasecmp(word, "passive") == 0) {
if (none ||
(!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0))
curclass.passive = 0;
else
curclass.passive = 1;
CONF_FLAG(passive);
} else if (strcasecmp(word, "portrange") == 0) {
int minport, maxport;
@ -383,28 +384,31 @@ parse_conf(const char *findclass)
} else if (strcasecmp(word, "rateget") == 0) {
if (none || EMPTYSTR(arg))
continue;
rate = strsuftoi(arg);
if (rate == -1) {
llval = strsuftoll(arg);
if (llval == -1) {
syslog(LOG_WARNING,
"%s line %d: invalid rateget %s",
infile, (int)line, arg);
continue;
}
curclass.maxrateget = rate;
curclass.rateget = rate;
curclass.maxrateget = llval;
curclass.rateget = llval;
} else if (strcasecmp(word, "rateput") == 0) {
if (none || EMPTYSTR(arg))
continue;
rate = strsuftoi(arg);
if (rate == -1) {
llval = strsuftoll(arg);
if (llval == -1) {
syslog(LOG_WARNING,
"%s line %d: invalid rateput %s",
infile, (int)line, arg);
continue;
}
curclass.maxrateput = rate;
curclass.rateput = rate;
curclass.maxrateput = llval;
curclass.rateput = llval;
} else if (strcasecmp(word, "sanenames") == 0) {
CONF_FLAG(sanenames);
} else if (strcasecmp(word, "timeout") == 0) {
if (none || EMPTYSTR(arg))
@ -451,12 +455,9 @@ parse_conf(const char *findclass)
curclass.umask = umask;
} else if (strcasecmp(word, "upload") == 0) {
if (none ||
(!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) {
curclass.modify = 0;
curclass.upload = 0;
} else
curclass.upload = 1;
CONF_FLAG(upload);
if (! CURCLASS_FLAGS_ISSET(upload))
CURCLASS_FLAGS_CLR(modify);
} else {
syslog(LOG_WARNING,
@ -547,8 +548,10 @@ display_file(const char *file, int code)
FILE *f;
char *buf, *p, *cwd;
size_t len;
off_t lastnum;
time_t now;
lastnum = 0;
if (quietmessages)
return (0);
@ -587,7 +590,9 @@ display_file(const char *file, int code)
break;
case 'E':
/* XXXX email address */
if (! EMPTYSTR(emailaddr))
cprintf(stdout, "%s",
emailaddr);
break;
case 'L':
@ -595,23 +600,35 @@ display_file(const char *file, int code)
break;
case 'M':
if (curclass.limit == -1)
if (curclass.limit == -1) {
cprintf(stdout, "unlimited");
else
lastnum = 0;
} else {
cprintf(stdout, "%d",
curclass.limit);
lastnum = curclass.limit;
}
break;
case 'N':
if (connections > 0)
cprintf(stdout, "%d",
connections);
cprintf(stdout, "%d", connections);
lastnum = connections;
break;
case 'R':
cprintf(stdout, "%s", remotehost);
break;
case 's':
if (lastnum != 1)
cprintf(stdout, "s");
break;
case 'S':
if (lastnum != 1)
cprintf(stdout, "S");
break;
case 'T':
now = time(NULL);
cprintf(stdout, "%.24s", ctime(&now));
@ -810,19 +827,19 @@ do_conversion(const char *fname)
}
/*
* Convert the string `arg' to an int, which may have an optional SI suffix
* (`b', `k', `m', `g'). Returns the number for success, -1 otherwise.
* Convert the string `arg' to a long long, which may have an optional SI suffix
* (`b', `k', `m', `g', `t'). Returns the number for success, -1 otherwise.
*/
int
strsuftoi(const char *arg)
LLT
strsuftoll(const char *arg)
{
char *cp;
long val;
LLT val;
if (!isdigit((unsigned char)arg[0]))
return (-1);
val = strtol(arg, &cp, 10);
val = STRTOLL(arg, &cp, 10);
if (cp != NULL) {
if (cp[0] != '\0' && cp[1] != '\0')
return (-1);
@ -839,11 +856,14 @@ strsuftoi(const char *arg)
case 'g':
val <<= 30;
break;
case 't':
val <<= 40;
break;
default:
return (-1);
}
}
if (val < 0 || val > INT_MAX)
if (val < 0)
return (-1);
return (val);

View File

@ -1,4 +1,4 @@
/* $NetBSD: extern.h,v 1.34 2000/11/15 02:32:30 lukem Exp $ */
/* $NetBSD: extern.h,v 1.35 2000/11/16 13:15:13 lukem Exp $ */
/*-
* Copyright (c) 1992, 1993
@ -100,6 +100,24 @@
* SUCH DAMAGE.
*/
#ifdef NO_LONG_LONG
# define LLF "%ld"
# define LLFP(x) "%" x "ld"
# define LLT long
# define ULLF "%lu"
# define ULLFP(x) "%" x "lu"
# define ULLT unsigned long
# define STRTOLL(x,y,z) strtol(x,y,z)
#else
# define LLF "%lld"
# define LLFP(x) "%" x "lld"
# define LLT long long
# define ULLF "%llu"
# define ULLFP(x) "%" x "llu"
# define ULLT unsigned long long
# define STRTOLL(x,y,z) strtoll(x,y,z)
#endif
void blkfree(char **);
void closedataconn(FILE *);
char *conffilename(const char *);
@ -152,17 +170,11 @@ void sizecmd(const char *);
void statcmd(void);
void statfilecmd(const char *);
void store(const char *, const char *, int);
int strsuftoi(const char *);
LLT strsuftoll(const char *);
void user(const char *);
char *xstrdup(const char *);
void yyerror(char *);
typedef enum {
CLASS_GUEST,
CLASS_CHROOT,
CLASS_REAL
} class_ft;
struct tab {
char *name;
short token;
@ -180,32 +192,48 @@ struct ftpconv {
char *command; /* Command to do the conversion */
};
typedef enum {
CLASS_GUEST,
CLASS_CHROOT,
CLASS_REAL
} class_ft;
typedef enum {
FLAG_checkportcmd = 1<<0, /* Check port commands */
FLAG_modify = 1<<1, /* Allow CHMOD, DELE, MKD, RMD, RNFR,
UMASK */
FLAG_passive = 1<<2, /* Allow PASV mode */
FLAG_sanenames = 1<<3, /* Restrict names of uploaded files */
FLAG_upload = 1<<4 /* As per modify, but also allow
APPE, STOR, STOU */
} classflag_t;
#define CURCLASS_FLAGS_SET(x) (curclass.flags |= (FLAG_ ## x))
#define CURCLASS_FLAGS_CLR(x) (curclass.flags &= ~(FLAG_ ## x))
#define CURCLASS_FLAGS_ISSET(x) (curclass.flags & (FLAG_ ## x))
struct ftpclass {
int checkportcmd; /* Check PORT commands are valid */
char *chroot; /* Directory to chroot(2) to at login */
char *classname; /* Current class */
struct ftpconv *conversions; /* List of conversions */
char *display; /* Files to display upon chdir */
char *display; /* File to display upon chdir */
char *homedir; /* Directory to chdir(2) to at login */
classflag_t flags; /* Flags; see classflag_t above */
int limit; /* Max connections (-1 = unlimited) */
char *limitfile; /* File to display if limit reached */
int maxrateget; /* Maximum get transfer rate throttle */
int maxrateput; /* Maximum put transfer rate throttle */
LLT maxfilesize; /* Maximum file size of uploads */
LLT maxrateget; /* Maximum get transfer rate throttle */
LLT maxrateput; /* Maximum put transfer rate throttle */
unsigned int maxtimeout; /* Maximum permitted timeout */
int modify; /* Allow CHMOD, DELE, MKD, RMD, RNFR,
UMASK */
char *motd; /* MotD file to display after login */
char *notify; /* Files to notify about upon chdir */
int passive; /* Allow PASV mode */
int portmin; /* Minumum port for passive mode */
int portmax; /* Maximum port for passive mode */
int rateget; /* Get (RETR) transfer rate throttle */
int rateput; /* Put (STOR) transfer rate throttle */
LLT rateget; /* Get (RETR) transfer rate throttle */
LLT rateput; /* Put (STOR) transfer rate throttle */
unsigned int timeout; /* Default timeout */
class_ft type; /* Class type */
mode_t umask; /* Umask to use */
int upload; /* As per modify, but also allow
APPE, STOR, STOU */
};
#include <netinet/in.h>
@ -255,6 +283,7 @@ GLOBAL int connections;
GLOBAL struct ftpclass curclass;
GLOBAL int debug;
GLOBAL jmp_buf errcatch;
GLOBAL char *emailaddr;
GLOBAL int form;
GLOBAL int gidcount; /* number of entries in gidlist[] */
GLOBAL gid_t gidlist[NGROUPS_MAX];
@ -319,21 +348,3 @@ extern struct tab cmdtab[];
#ifndef IPPORT_ANONMAX
# define IPPORT_ANONMAX 65535
#endif
#ifdef NO_LONG_LONG
# define LLF "%ld"
# define LLFP(x) "%" x "ld"
# define LLT long
# define ULLF "%lu"
# define ULLFP(x) "%" x "lu"
# define ULLT unsigned long
# define STRTOLL(x,y,z) strtol(x,y,z)
#else
# define LLF "%lld"
# define LLFP(x) "%" x "lld"
# define LLT long long
# define ULLF "%llu"
# define ULLFP(x) "%" x "llu"
# define ULLT unsigned long long
# define STRTOLL(x,y,z) strtoll(x,y,z)
#endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: ftpcmd.y,v 1.55 2000/11/15 02:32:30 lukem Exp $ */
/* $NetBSD: ftpcmd.y,v 1.56 2000/11/16 13:15:14 lukem Exp $ */
/*-
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
@ -83,7 +83,7 @@
#if 0
static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
#else
__RCSID("$NetBSD: ftpcmd.y,v 1.55 2000/11/15 02:32:30 lukem Exp $");
__RCSID("$NetBSD: ftpcmd.y,v 1.56 2000/11/16 13:15:14 lukem Exp $");
#endif
#endif /* not lint */
@ -165,7 +165,7 @@ char *fromname;
%token <s> ALL
%token <i> NUMBER
%type <i> check_login check_modify check_upload octal_number byte_size
%type <i> check_login octal_number byte_size
%type <i> struct_code mode_code type_code form_code decimal_integer
%type <s> pathstring pathname password username
%type <s> mechanism_name base64data prot_code
@ -282,7 +282,7 @@ cmd
| PASV check_login CRLF
{
if ($2) {
if (curclass.passive)
if (CURCLASS_FLAGS_ISSET(passive))
passive();
else
reply(500, "PASV mode not available.");
@ -398,28 +398,28 @@ cmd
free($4);
}
| STOR check_upload SP pathname CRLF
| STOR SP pathname CRLF
{
if ($2 && $4 != NULL)
store($4, "w", 0);
if ($4 != NULL)
free($4);
if (check_write($3, 1))
store($3, "w", 0);
if ($3 != NULL)
free($3);
}
| STOU check_upload SP pathname CRLF
| STOU SP pathname CRLF
{
if ($2 && $4 != NULL)
store($4, "w", 1);
if ($4 != NULL)
free($4);
if (check_write($3, 1))
store($3, "w", 1);
if ($3 != NULL)
free($3);
}
| APPE check_upload SP pathname CRLF
| APPE SP pathname CRLF
{
if ($2 && $4 != NULL)
store($4, "a", 0);
if ($4 != NULL)
free($4);
if (check_write($3, 1))
store($3, "a", 0);
if ($3 != NULL)
free($3);
}
| ALLO check_login SP NUMBER CRLF
@ -434,18 +434,19 @@ cmd
reply(202, "ALLO command ignored.");
}
| RNTO check_login SP pathname CRLF
| RNTO SP pathname CRLF
{
if ($2) {
if (check_write($3, 0)) {
if (fromname) {
renamecmd(fromname, $4);
renamecmd(fromname, $3);
free(fromname);
fromname = NULL;
} else {
reply(503, "Bad sequence of commands.");
}
}
free($4);
if ($3 != NULL)
free($3);
}
| ABOR check_login CRLF
@ -454,28 +455,28 @@ cmd
reply(225, "ABOR command successful.");
}
| DELE check_modify SP pathname CRLF
| DELE SP pathname CRLF
{
if ($2 && $4 != NULL)
delete($4);
if ($4 != NULL)
free($4);
if (check_write($3, 0))
delete($3);
if ($3 != NULL)
free($3);
}
| RMD check_modify SP pathname CRLF
| RMD SP pathname CRLF
{
if ($2 && $4 != NULL)
removedir($4);
if ($4 != NULL)
free($4);
if (check_write($3, 0))
removedir($3);
if ($3 != NULL)
free($3);
}
| MKD check_modify SP pathname CRLF
| MKD SP pathname CRLF
{
if ($2 && $4 != NULL)
makedir($4);
if ($4 != NULL)
free($4);
if (check_write($3, 0))
makedir($3);
if ($3 != NULL)
free($3);
}
| PWD check_login CRLF
@ -522,19 +523,19 @@ cmd
help(sitetab, NULL);
}
| SITE SP CHMOD check_modify SP octal_number SP pathname CRLF
| SITE SP CHMOD SP octal_number SP pathname CRLF
{
if ($4 && ($8 != NULL)) {
if ($6 > 0777)
if (check_write($7, 0)) {
if ($5 > 0777)
reply(501,
"CHMOD: Mode value must be between 0 and 0777");
else if (chmod($8, $6) < 0)
perror_reply(550, $8);
else if (chmod($7, $5) < 0)
perror_reply(550, $7);
else
reply(200, "CHMOD command successful.");
}
if ($8 != NULL)
free($8);
if ($7 != NULL)
free($7);
}
| SITE SP HELP SP STRING CRLF
@ -572,30 +573,32 @@ cmd
| SITE SP RATEGET check_login CRLF
{
if ($4) {
reply(200, "Current RATEGET is %d bytes/sec",
curclass.rateget);
reply(200,
"Current RATEGET is " LLF " bytes/sec",
(LLT)curclass.rateget);
}
}
| SITE SP RATEGET check_login SP STRING CRLF
{
char *p = $6;
int rate;
LLT rate;
if ($4) {
rate = strsuftoi(p);
rate = strsuftoll(p);
if (rate == -1)
reply(501, "Invalid RATEGET %s", p);
else if (curclass.maxrateget &&
rate > curclass.maxrateget)
reply(501,
"RATEGET %d is larger than maximum RATEGET %d",
rate, curclass.maxrateget);
"RATEGET " LLF " is larger than maximum RATEGET " LLF,
(LLT)rate,
(LLT)curclass.maxrateget);
else {
curclass.rateget = rate;
reply(200,
"RATEGET set to %d bytes/sec",
curclass.rateget);
"RATEGET set to " LLF " bytes/sec",
(LLT)curclass.rateget);
}
}
free($6);
@ -604,30 +607,32 @@ cmd
| SITE SP RATEPUT check_login CRLF
{
if ($4) {
reply(200, "Current RATEPUT is %d bytes/sec",
curclass.rateput);
reply(200,
"Current RATEPUT is " LLF " bytes/sec",
(LLT)curclass.rateput);
}
}
| SITE SP RATEPUT check_login SP STRING CRLF
{
char *p = $6;
int rate;
LLT rate;
if ($4) {
rate = strsuftoi(p);
rate = strsuftoll(p);
if (rate == -1)
reply(501, "Invalid RATEPUT %s", p);
else if (curclass.maxrateput &&
rate > curclass.maxrateput)
reply(501,
"RATEPUT %d is larger than maximum RATEPUT %d",
rate, curclass.maxrateput);
"RATEPUT " LLF " is larger than maximum RATEPUT " LLF,
(LLT)rate,
(LLT)curclass.maxrateput);
else {
curclass.rateput = rate;
reply(200,
"RATEPUT set to %d bytes/sec",
curclass.rateput);
"RATEPUT set to " LLF " bytes/sec",
(LLT)curclass.rateput);
}
}
free($6);
@ -644,11 +649,11 @@ cmd
}
}
| SITE SP UMASK check_modify SP octal_number CRLF
| SITE SP UMASK check_login SP octal_number CRLF
{
int oldmask;
if ($4) {
if ($4 && CURCLASS_FLAGS_ISSET(modify)) {
if (($6 == -1) || ($6 > 0777)) {
reply(501, "Bad UMASK value");
} else {
@ -854,21 +859,20 @@ rcmd
{
if ($2) {
fromname = NULL;
restart_point = $4; /* XXX $3 is only "int" */
restart_point = $4; /* XXX $4 is only "int" */
reply(350,
"Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
(LLT)restart_point);
}
}
| RNFR check_modify SP pathname CRLF
| RNFR SP pathname CRLF
{
restart_point = (off_t) 0;
if ($2 && $4) {
fromname = renamefrom($4);
}
if ($4)
free($4);
if (check_write($3, 0))
fromname = renamefrom($3);
if ($3 != NULL)
free($3);
}
;
@ -1151,45 +1155,6 @@ check_login
}
;
check_modify
: /* empty */
{
if (logged_in) {
if (curclass.modify)
$$ = 1;
else {
reply(502,
"No permission to use this command.");
$$ = 0;
hasyyerrored = 1;
}
} else {
reply(530, "Please login with USER and PASS.");
$$ = 0;
hasyyerrored = 1;
}
}
check_upload
: /* empty */
{
if (logged_in) {
if (curclass.upload)
$$ = 1;
else {
reply(502,
"No permission to use this command.");
$$ = 0;
hasyyerrored = 1;
}
} else {
reply(530, "Please login with USER and PASS.");
$$ = 0;
hasyyerrored = 1;
}
}
%%
#define CMD 0 /* beginning of command */
@ -1290,13 +1255,55 @@ struct tab sitetab[] = {
{ NULL, 0, 0, 0, NULL }
};
static void help(struct tab *, const char *);
static void port_check(const char *, int);
static void toolong(int);
static int yylex(void);
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'.
*/
static int
check_write(const char *file, int isupload)
{
if (file == NULL)
return (0);
if (! logged_in) {
reply(530, "Please login with USER and PASS.");
return (0);
}
/* checking modify */
if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
reply(502, "No permission to use this command.");
return (0);
}
/* checking upload */
if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
reply(502, "No permission to use this command.");
return (0);
}
/* checking sanenames */
if (CURCLASS_FLAGS_ISSET(sanenames)) {
const char *p;
if (file[0] == '.')
goto insane_name;
for (p = file; *p; p++) {
if (isalnum(*p) || *p == '-' || *p == '+' ||
*p == ',' || *p == '.' || *p == '_')
continue;
insane_name:
reply(553, "File name `%s' not allowed.", file);
return (0);
}
}
return (1);
}
struct tab *
lookup(struct tab *p, const char *cmd)
{
@ -1750,7 +1757,7 @@ port_check(const char *cmd, int family)
goto port_check_fail;
/* be paranoid, if told so */
if (curclass.checkportcmd) {
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;

View File

@ -1,4 +1,4 @@
.\" $NetBSD: ftpd.8,v 1.60 2000/07/28 12:54:01 lukem Exp $
.\" $NetBSD: ftpd.8,v 1.61 2000/11/16 13:15:14 lukem Exp $
.\"
.\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
.\" All rights reserved.
@ -67,7 +67,7 @@
.\"
.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94
.\"
.Dd July 26, 2000
.Dd November 16, 2000
.Dt FTPD 8
.Os
.Sh NAME
@ -76,11 +76,13 @@
Internet File Transfer Protocol server
.Sh SYNOPSIS
.Nm
.Op Fl dHlrsUW
.Op Fl dHlqQrsuUwW
.Op Fl a Ar anondir
.Op Fl c Ar confdir
.Op Fl C Ar user
.Op Fl e Ar emailaddr
.Op Fl h Ar hostname
.Op Fl P Ar dataport
.Op Fl V Ar version
.Sh DESCRIPTION
.Nm
@ -119,6 +121,13 @@ This can be useful for testing configurations.
.It Fl d
Debugging information is written to the syslog using
.Dv LOG_FTP .
.It Fl e Ar emailaddr
Use
.Ar emailaddr
for the
.Dq "\&%E"
escape sequence (see
.Sx Display file escape sequences )
.It Fl h Ar hostname
Explicitly set the hostname to advertise as to
.Ar hostname .
@ -149,7 +158,24 @@ session is logged using syslog with a facility of
.Dv LOG_FTP .
If this option is specified twice, the retrieve (get), store (put), append,
delete, make directory, remove directory and rename operations and
their filename arguments are also logged.
their file name arguments are also logged.
.It Fl P Ar dataport
Use
.Ar dataport
as the data port, overriding the default of using the port one less
that the port
.Nm
is listening on.
.It Fl q
Enable the use of pid files for keeping track of the number of logged-in
users per class.
This is the default.
.It Fl Q
Disable the use of pid files for keeping track of the number of logged-in
users per class.
This may reduce the load on heavily loaded
.Tn FTP
servers.
.It Fl r
Permanently drop root privileges once the user is logged in.
The use of this option may result in the server using a port other
@ -163,13 +189,19 @@ See
below for more details.
.It Fl s
Require a secure authentication mechanism like Kerberos or S/Key to be used.
.It Fl U
Each concurrent
.It Fl u
Log each concurrent
.Tn FTP
session is logged to the file
session to
.Pa /var/run/utmp ,
making them visible to commands such as
.Xr who 1 .
.It Fl U
Don't log each concurrent
.Tn FTP
session to
.Pa /var/run/utmp .
This is the default.
.It Fl V Ar version
Use
.Ar version
@ -183,14 +215,19 @@ If
is empty or
.Sq -
then don't display any version information.
.It Fl W
By default each
.It Fl w
Log each
.Tn FTP
session is logged to
session to
.Pa /var/log/wtmp ,
making them visible to commands such as
.Xr last 1 .
This option prevents that from occurring.
This is the default.
.It Fl W
Don't log each
.Tn FTP
session to
.Pa /var/log/wtmp .
.El
.Pp
The file
@ -443,6 +480,9 @@ The supported escape strings are:
Class name.
.It "\&%C"
Current working directory.
.It "\&%E"
Email address given with
.Fl e .
.It "\&%L"
Local hostname.
.It "\&%M"
@ -454,6 +494,24 @@ if there's no limit.
Current number of users for this class.
.It "\&%R"
Remote hostname.
.It "\&%s"
If the result of the most recent
.Dq "\&%M"
or
.Dq "\&%N"
was not
.Dq Li 1 ,
print an
.Dq s .
.It "\&%S"
If the result of the most recent
.Dq "\&%M"
or
.Dq "\&%N"
was not
.Dq Li 1 ,
print an
.Dq S .
.It "\&%T"
Current time.
.It "\&%U"
@ -532,9 +590,15 @@ users to be able to see the names of the
files in this directory the permissions should be 770, otherwise
they should be 370.
.Pp
Anonymous users will be able to upload files to this directory,
but they will not be able to download them, delete them, or overwrite
them, due to the umask and disabling of the commands mentioned
The following
.Xr ftpd.conf 5
directives should be used:
.Dl "modify guest off"
.Dl "umask guest 0707"
.Pp
This will result in anonymous users being able to upload files to this
directory, but they will not be able to download them, delete them, or
overwrite them, due to the umask and disabling of the commands mentioned
above.
.It Pa ~ftp/tmp
This directory is used to create temporary files which contain
@ -596,10 +660,10 @@ State file of logged-in processes for the
.Nm
class
.Sq CLASS .
.It Pa /var/log/wtmp
Login history database.
.It Pa /var/run/utmp
List of logged-in users on the system.
.It Pa /var/log/wtmp
Login history database.
.El
.Sh SEE ALSO
.Xr ftp 1 ,
@ -696,7 +760,8 @@ communicate back to the client for the
.Sy LPTR ,
and
.Sy PORT
commands.
commands, unless overridden with
.Fl P Ar dataport .
As the default port for
.Nm
(21) is a privileged port below

View File

@ -1,4 +1,4 @@
/* $NetBSD: ftpd.c,v 1.110 2000/11/15 04:07:07 itojun Exp $ */
/* $NetBSD: ftpd.c,v 1.111 2000/11/16 13:15:14 lukem Exp $ */
/*
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
@ -109,7 +109,7 @@ __COPYRIGHT(
#if 0
static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
#else
__RCSID("$NetBSD: ftpd.c,v 1.110 2000/11/15 04:07:07 itojun Exp $");
__RCSID("$NetBSD: ftpd.c,v 1.111 2000/11/16 13:15:14 lukem Exp $");
#endif
#endif /* not lint */
@ -172,6 +172,8 @@ struct passwd *pw;
int sflag;
int stru; /* avoid C keyword */
int mode;
int dataport; /* use specific data port */
int dopidfile; /* maintain pid file */
int doutmp; /* update utmp file */
int dowtmp; /* update wtmp file */
int dropprivs; /* if privileges should or have been dropped */
@ -181,8 +183,8 @@ off_t byte_count;
static char ttyline[20];
static struct utmp utmp; /* for utmp */
static char *anondir = NULL;
static char confdir[MAXPATHLEN];
static const char *anondir = NULL;
static const char *confdir = NULL;
#if defined(KERBEROS) || defined(KERBEROS5)
int has_ccache = 0;
@ -236,31 +238,41 @@ main(int argc, char *argv[])
#ifdef KERBEROS5
krb5_error_code kerror;
#endif
char *p;
connections = 1;
debug = 0;
logging = 0;
pdata = -1;
sflag = 0;
dataport = 0;
dopidfile = 1; /* default: DO use a pid file to count users */
doutmp = 0; /* default: don't log to utmp */
dowtmp = 1; /* default: DO log to wtmp */
dropprivs = 0;
mapped = 0;
usedefault = 1;
(void)strcpy(confdir, _DEFAULT_CONFDIR);
emailaddr = NULL;
hostname[0] = '\0';
homedir[0] = '\0';
gidcount = 0;
version = FTPD_VERSION;
while ((ch = getopt(argc, argv, "a:c:C:dh:Hlrst:T:u:UvV:W")) != -1) {
/*
* LOG_NDELAY sets up the logging connection immediately,
* necessary for anonymous ftp's that chroot and can't do it later.
*/
openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
while ((ch = getopt(argc, argv, "a:c:C:de:h:HlP:qQrst:T:uUvV:wW"))
!= -1) {
switch (ch) {
case 'a':
anondir = optarg;
break;
case 'c':
(void)strlcpy(confdir, optarg, sizeof(confdir));
confdir = optarg;
break;
case 'C':
@ -273,6 +285,10 @@ main(int argc, char *argv[])
debug = 1;
break;
case 'e':
emailaddr = optarg;
break;
case 'h':
strlcpy(hostname, optarg, sizeof(hostname));
break;
@ -287,6 +303,24 @@ main(int argc, char *argv[])
logging++; /* > 1 == extra logging */
break;
case 'P':
dataport = (int)strtol(optarg, &p, 10);
if (*p != '\0' || dataport < IPPORT_RESERVED ||
dataport > IPPORT_ANONMAX) {
syslog(LOG_WARNING, "Invalid dataport %s",
optarg);
dataport = 0;
}
break;
case 'q':
dopidfile = 1;
break;
case 'Q':
dopidfile = 0;
break;
case 'r':
dropprivs = 1;
break;
@ -297,15 +331,19 @@ main(int argc, char *argv[])
case 't':
case 'T':
case 'u':
warnx("-%c has been deprecated in favour of ftpd.conf",
syslog(LOG_ERR,
"-%c has been deprecated in favour of ftpd.conf",
ch);
break;
case 'U':
case 'u':
doutmp = 1;
break;
case 'U':
doutmp = 0;
break;
case 'V':
if (EMPTYSTR(optarg) || strcmp(optarg, "-") == 0)
version = NULL;
@ -313,6 +351,10 @@ main(int argc, char *argv[])
version = xstrdup(optarg);
break;
case 'w':
dowtmp = 1;
break;
case 'W':
dowtmp = 0;
break;
@ -320,16 +362,13 @@ main(int argc, char *argv[])
default:
if (optopt == 'a' || optopt == 'C')
exit(1);
warnx("unknown flag -%c ignored", optopt);
syslog(LOG_ERR, "unknown flag -%c ignored", optopt);
break;
}
}
if (EMPTYSTR(confdir))
confdir = _DEFAULT_CONFDIR;
/*
* LOG_NDELAY sets up the logging connection immediately,
* necessary for anonymous ftp's that chroot and can't do it later.
*/
openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
memset((char *)&his_addr, 0, sizeof (his_addr));
addrlen = sizeof(his_addr.si_su);
if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0) {
@ -938,7 +977,9 @@ pass(const char *passwd)
/* parse ftpd.conf, setting up various parameters */
parse_conf(class);
count_users();
connections = 1;
if (dopidfile)
count_users();
if (curclass.limit != -1 && connections > curclass.limit) {
if (! EMPTYSTR(curclass.limitfile))
(void)display_file(conffilename(curclass.limitfile),
@ -1090,9 +1131,11 @@ pass(const char *passwd)
}
(void) umask(curclass.umask);
goto cleanuppass;
bad:
/* Forget all about it... */
end_login();
cleanuppass:
if (class)
free(class);
@ -1338,7 +1381,10 @@ getdatasock(const char *mode)
* would be < IPPORT_RESERVED, use a random port
* instead.
*/
port = ntohs(ctrl_addr.su_port) - 1;
if (dataport)
port = dataport;
else
port = ntohs(ctrl_addr.su_port) - 1;
if (dropprivs && port < IPPORT_RESERVED)
port = 0; /* use random port */
data_source.su_port = htons(port);
@ -1632,6 +1678,7 @@ static int
receive_data(FILE *instr, FILE *outstr)
{
int c, bare_lfs, netfd, filefd, rval;
off_t byteswritten;
char buf[BUFSIZ];
#ifdef __GNUC__
(void) &bare_lfs;
@ -1640,9 +1687,19 @@ receive_data(FILE *instr, FILE *outstr)
bare_lfs = 0;
transflag = 1;
rval = -1;
byteswritten = 0;
if (setjmp(urgcatch))
goto cleanup_recv_data;
#define FILESIZECHECK(x) \
do { \
if (curclass.maxfilesize != -1 && \
(x) > curclass.maxfilesize) { \
errno = EFBIG; \
goto file_err; \
} \
} while (0)
switch (type) {
case TYPE_I:
@ -1662,8 +1719,9 @@ receive_data(FILE *instr, FILE *outstr)
if ((c = read(netfd, buf,
MIN(sizeof(buf), bufrem))) <= 0)
goto recvdone;
FILESIZECHECK(byte_count + c);
if ((d = write(filefd, buf, c)) != c)
goto recvdone;
goto file_err;
(void) alarm(curclass.timeout);
bufrem -= c;
byte_count += c;
@ -1679,6 +1737,7 @@ receive_data(FILE *instr, FILE *outstr)
}
} else {
while ((c = read(netfd, buf, sizeof(buf))) > 0) {
FILESIZECHECK(byte_count + c);
if (write(filefd, buf, c) != c)
goto file_err;
(void) alarm(curclass.timeout);
@ -1723,13 +1782,17 @@ receive_data(FILE *instr, FILE *outstr)
total_bytes++;
if ((byte_count % 4096) == 0)
(void) alarm(curclass.timeout);
byteswritten++;
FILESIZECHECK(byteswritten);
(void) putc ('\r', outstr);
if (c == '\0' || c == EOF)
goto contin2;
}
}
byteswritten++;
FILESIZECHECK(byteswritten);
(void) putc(c, outstr);
contin2: ;
contin2: ;
}
(void) alarm(0);
fflush(outstr);
@ -1750,6 +1813,7 @@ receive_data(FILE *instr, FILE *outstr)
reply(550, "Unimplemented TYPE %d in receive_data", type);
goto cleanup_recv_data;
}
#undef FILESIZECHECK(x)
data_err:
(void) alarm(0);
@ -1925,7 +1989,7 @@ statcmd(void)
reply(0, "Class: %s, type: %s",
curclass.classname, CURCLASSTYPE);
reply(0, "Check PORT/LPRT commands: %sabled",
curclass.checkportcmd ? "en" : "dis");
CURCLASS_FLAGS_ISSET(checkportcmd) ? "en" : "dis");
if (! EMPTYSTR(curclass.display))
reply(0, "Display file: %s", curclass.display);
if (! EMPTYSTR(curclass.notify))
@ -1938,30 +2002,39 @@ statcmd(void)
else
reply(0, "Maximum connections: %d", curclass.limit);
if (curclass.limitfile)
reply(0, "Connection limit exceeded file: %s",
reply(0, "Connection limit exceeded message file: %s",
curclass.limitfile);
if (! EMPTYSTR(curclass.chroot))
reply(0, "Chroot format: %s", curclass.chroot);
if (! EMPTYSTR(curclass.homedir))
reply(0, "Homedir format: %s", curclass.homedir);
if (curclass.maxfilesize == -1)
reply(0, "Maximum file size: unlimited");
else
reply(0, "Maximum file size: " LLF,
(LLT)curclass.maxfilesize);
if (! EMPTYSTR(curclass.motd))
reply(0, "MotD file: %s", curclass.motd);
reply(0,
"Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled",
curclass.modify ? "en" : "dis");
CURCLASS_FLAGS_ISSET(modify) ? "en" : "dis");
reply(0, "Upload commands (APPE, STOR, STOU): %sabled",
curclass.upload ? "en" : "dis");
CURCLASS_FLAGS_ISSET(upload) ? "en" : "dis");
reply(0, "Sanitize file names: %sabled",
CURCLASS_FLAGS_ISSET(sanenames) ? "en" : "dis");
reply(0, "PASV/LPSV/EPSV connections: %sabled",
CURCLASS_FLAGS_ISSET(passive) ? "en" : "dis");
if (curclass.portmin && curclass.portmax)
reply(0, "PASV port range: %d - %d",
curclass.portmin, curclass.portmax);
if (curclass.rateget)
reply(0, "Rate get limit: %d bytes/sec",
curclass.rateget);
reply(0, "Rate get limit: " LLF " bytes/sec",
(LLT)curclass.rateget);
else
reply(0, "Rate get limit: disabled");
if (curclass.rateput)
reply(0, "Rate put limit: %d bytes/sec",
curclass.rateput);
reply(0, "Rate put limit: " LLF " bytes/sec",
(LLT)curclass.rateput);
else
reply(0, "Rate put limit: disabled");
reply(0, "Umask: %.04o", curclass.umask);
@ -2262,8 +2335,7 @@ long_passive(char *cmd, int pf)
if (pf != PF_UNSPEC && ctrl_addr.su_family != pf) {
/*
* XXX
* only EPRT/EPSV ready clients will understand this
* XXX: only EPRT/EPSV ready clients will understand this
*/
if (strcmp(cmd, "EPSV") != 0)
reply(501, "Network protocol mismatch"); /*XXX*/
@ -2364,7 +2436,7 @@ extended_port(const char *arg)
p = q;
}
/* some more sanity check */
/* some more sanity checks */
p = NULL;
(void)strtoul(result[2], &p, 10);
if (!*result[2] || *p)
@ -2389,7 +2461,7 @@ extended_port(const char *arg)
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! */
/* XXX: more sanity checks! */
data_dest.su_scope_id = his_addr.su_scope_id;
}
@ -2431,7 +2503,7 @@ epsv_protounsupp(const char *message)
proto = af2epsvproto(ctrl_addr.su_family);
if (proto < 0)
reply(501, "%s", message); /*XXX*/
reply(501, "%s", message); /* XXX */
else
reply(522, "%s, use (%d)", message, proto);
}

View File

@ -1,4 +1,4 @@
.\" $NetBSD: ftpd.conf.5,v 1.13 2000/11/07 06:58:08 lukem Exp $
.\" $NetBSD: ftpd.conf.5,v 1.14 2000/11/16 13:15:14 lukem Exp $
.\"
.\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
.\" All rights reserved.
@ -34,7 +34,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd November 7, 2000
.Dd November 16, 2000
.Dt FTPD.CONF 5
.Os
.Sh NAME
@ -58,7 +58,7 @@ This allows
.Sq wildcard
entries to define defaults, and then have class-specific overrides.
.Pp
A directive line has the format
A directive line has the format:
.Dl command class [arguments]
.Pp
A
@ -211,7 +211,7 @@ Valid types are:
(directory).
.It Ar disable
The name of file that will prevent conversion if it exists.
A filename of
A file name of
.Dq Pa \&.
will prevent this disabling action
(i.e., the conversion is always permitted.)
@ -295,6 +295,9 @@ for
and
.Sy CHROOT
users.
.It Sy maxfilesize Ar class Ar size
Set the maximum size of an uploaded file to
.Ar size .
.It Sy maxtimeout Ar class Ar time
Set the maximum timeout period that a client may request,
defaulting to two hours.
@ -392,6 +395,19 @@ to
bytes per second,
which is parsed as per
.Sy rateget Ar rate .
.It Sy sanenames Ar class Op Sy off
If
.Ar class
is
.Dq none
or
.Sy off
is given, allow uploaded file names to contain any characters valid for a
file name.
Otherwise, only permit file names which don't start with a
.Sq \&.
and only comprise of characters from the set
.Dq [-+,._A-Za-z0-9] .
.It Sy template Ar class Op Ar refclass
Define
.Ar refclass

View File

@ -1,4 +1,4 @@
/* $NetBSD: version.h,v 1.21 2000/11/15 02:32:30 lukem Exp $ */
/* $NetBSD: version.h,v 1.22 2000/11/16 13:15:14 lukem Exp $ */
/*-
* Copyright (c) 1999, 2000 The NetBSD Foundation, Inc.
* All rights reserved.
@ -36,5 +36,5 @@
*/
#ifndef FTPD_VERSION
#define FTPD_VERSION "NetBSD-ftpd 20001115"
#define FTPD_VERSION "NetBSD-ftpd 20001116"
#endif