- 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. * Copyright (c) 1999-2000 The NetBSD Foundation, Inc.
@ -101,7 +101,7 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#ifndef lint #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 */ #endif /* not lint */
#include <sys/param.h> #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. * since we only need this info in such a case.
*/ */
pdir = fe->pdirstat; pdir = fe->pdirstat;
if (pdir == NULL && curclass.modify) { if (pdir == NULL && CURCLASS_FLAGS_ISSET(modify)) {
size_t len; size_t len;
char realdir[MAXPATHLEN], *p; char realdir[MAXPATHLEN], *p;
struct stat dir; struct stat dir;
@ -636,15 +636,15 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
} }
/* 'a': can APPE to file */ /* '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); CPUTC('a', fd);
/* 'c': can create or append to files in directory */ /* '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); CPUTC('c', fd);
/* 'd': can delete file or directory */ /* 'd': can delete file or directory */
if (pdirwok && curclass.modify) { if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) {
int candel; int candel;
candel = 1; candel = 1;
@ -674,7 +674,7 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
CPUTC('e', fd); CPUTC('e', fd);
/* 'f': can rename file or directory */ /* 'f': can rename file or directory */
if (pdirwok && curclass.modify) if (pdirwok && CURCLASS_FLAGS_ISSET(modify))
CPUTC('f', fd); CPUTC('f', fd);
/* 'l': can list directory */ /* 'l': can list directory */
@ -682,11 +682,11 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
CPUTC('l', fd); CPUTC('l', fd);
/* 'm': can create directory */ /* '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); CPUTC('m', fd);
/* 'p': can remove files in directory */ /* '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); CPUTC('p', fd);
/* 'r': can RETR file */ /* 'r': can RETR file */
@ -694,7 +694,7 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
CPUTC('r', fd); CPUTC('r', fd);
/* 'w': can STOR file */ /* '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('w', fd);
CPUTC(';', 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. * Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
@ -38,7 +38,7 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#ifndef lint #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 */ #endif /* not lint */
#include <sys/types.h> #include <sys/types.h>
@ -88,7 +88,6 @@ init_curclass(void)
free(conv); free(conv);
} }
curclass.checkportcmd = 1;
REASSIGN(curclass.chroot, NULL); REASSIGN(curclass.chroot, NULL);
REASSIGN(curclass.classname, NULL); REASSIGN(curclass.classname, NULL);
curclass.conversions = NULL; curclass.conversions = NULL;
@ -96,13 +95,12 @@ init_curclass(void)
REASSIGN(curclass.homedir, NULL); REASSIGN(curclass.homedir, NULL);
curclass.limit = -1; /* unlimited connections */ curclass.limit = -1; /* unlimited connections */
REASSIGN(curclass.limitfile, NULL); REASSIGN(curclass.limitfile, NULL);
curclass.maxfilesize = -1; /* unlimited file size */
curclass.maxrateget = 0; curclass.maxrateget = 0;
curclass.maxrateput = 0; curclass.maxrateput = 0;
curclass.maxtimeout = 7200; /* 2 hours */ curclass.maxtimeout = 7200; /* 2 hours */
curclass.modify = 1;
REASSIGN(curclass.motd, xstrdup(_PATH_FTPLOGINMESG)); REASSIGN(curclass.motd, xstrdup(_PATH_FTPLOGINMESG));
REASSIGN(curclass.notify, NULL); REASSIGN(curclass.notify, NULL);
curclass.passive = 1;
curclass.portmin = 0; curclass.portmin = 0;
curclass.portmax = 0; curclass.portmax = 0;
curclass.rateget = 0; curclass.rateget = 0;
@ -110,7 +108,12 @@ init_curclass(void)
curclass.timeout = 900; /* 15 minutes */ curclass.timeout = 900; /* 15 minutes */
/* curclass.type is set elsewhere */ /* curclass.type is set elsewhere */
curclass.umask = 027; 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; FILE *f;
char *buf, *p; char *buf, *p;
size_t len; size_t len;
int none, match, rate; LLT llval;
int none, match;
char *endp; char *endp;
char *class, *word, *arg, *template; char *class, *word, *arg, *template;
const char *infile; const char *infile;
@ -134,7 +138,7 @@ parse_conf(const char *findclass)
init_curclass(); init_curclass();
REASSIGN(curclass.classname, xstrdup(findclass)); REASSIGN(curclass.classname, xstrdup(findclass));
if (strcasecmp(findclass, "guest") == 0) { if (strcasecmp(findclass, "guest") == 0) {
curclass.modify = 0; CURCLASS_FLAGS_CLR(modify);
curclass.umask = 0707; curclass.umask = 0707;
} }
@ -170,19 +174,29 @@ parse_conf(const char *findclass)
strcasecmp(class, "all") == 0) ) strcasecmp(class, "all") == 0) )
continue; 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 (strcasecmp(word, "checkportcmd") == 0) {
if (none || CONF_FLAG(checkportcmd);
(!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0))
curclass.checkportcmd = 0;
else
curclass.checkportcmd = 1;
} else if (strcasecmp(word, "chroot") == 0) { } else if (strcasecmp(word, "chroot") == 0) {
if (none || EMPTYSTR(arg)) CONF_STRING(chroot);
arg = NULL;
else
arg = xstrdup(arg);
REASSIGN(curclass.chroot, arg);
} else if (strcasecmp(word, "classtype") == 0) { } else if (strcasecmp(word, "classtype") == 0) {
if (!none && !EMPTYSTR(arg)) { if (!none && !EMPTYSTR(arg)) {
@ -253,18 +267,22 @@ parse_conf(const char *findclass)
REASSIGN(conv->command, convcmd); REASSIGN(conv->command, convcmd);
} else if (strcasecmp(word, "display") == 0) { } else if (strcasecmp(word, "display") == 0) {
if (none || EMPTYSTR(arg)) CONF_STRING(display);
arg = NULL;
else
arg = xstrdup(arg);
REASSIGN(curclass.display, arg);
} else if (strcasecmp(word, "homedir") == 0) { } else if (strcasecmp(word, "homedir") == 0) {
CONF_STRING(homedir);
} else if (strcasecmp(word, "maxfilesize") == 0) {
if (none || EMPTYSTR(arg)) if (none || EMPTYSTR(arg))
arg = NULL; continue;
else llval = strsuftoll(arg);
arg = xstrdup(arg); if (llval == -1) {
REASSIGN(curclass.homedir, arg); syslog(LOG_WARNING,
"%s line %d: invalid maxfilesize %s",
infile, (int)line, arg);
continue;
}
curclass.maxfilesize = llval;
} else if (strcasecmp(word, "limit") == 0) { } else if (strcasecmp(word, "limit") == 0) {
int limit; int limit;
@ -308,33 +326,16 @@ parse_conf(const char *findclass)
curclass.maxtimeout = timeout; curclass.maxtimeout = timeout;
} else if (strcasecmp(word, "modify") == 0) { } else if (strcasecmp(word, "modify") == 0) {
if (none || CONF_FLAG(modify);
(!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0))
curclass.modify = 0;
else
curclass.modify = 1;
} else if (strcasecmp(word, "motd") == 0) { } else if (strcasecmp(word, "motd") == 0) {
if (none || EMPTYSTR(arg)) CONF_STRING(motd);
arg = NULL;
else
arg = xstrdup(arg);
REASSIGN(curclass.motd, arg);
} else if (strcasecmp(word, "notify") == 0) { } else if (strcasecmp(word, "notify") == 0) {
if (none || EMPTYSTR(arg)) CONF_STRING(notify);
arg = NULL;
else
arg = xstrdup(arg);
REASSIGN(curclass.notify, arg);
} else if (strcasecmp(word, "passive") == 0) { } else if (strcasecmp(word, "passive") == 0) {
if (none || CONF_FLAG(passive);
(!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0))
curclass.passive = 0;
else
curclass.passive = 1;
} else if (strcasecmp(word, "portrange") == 0) { } else if (strcasecmp(word, "portrange") == 0) {
int minport, maxport; int minport, maxport;
@ -383,28 +384,31 @@ parse_conf(const char *findclass)
} else if (strcasecmp(word, "rateget") == 0) { } else if (strcasecmp(word, "rateget") == 0) {
if (none || EMPTYSTR(arg)) if (none || EMPTYSTR(arg))
continue; continue;
rate = strsuftoi(arg); llval = strsuftoll(arg);
if (rate == -1) { if (llval == -1) {
syslog(LOG_WARNING, syslog(LOG_WARNING,
"%s line %d: invalid rateget %s", "%s line %d: invalid rateget %s",
infile, (int)line, arg); infile, (int)line, arg);
continue; continue;
} }
curclass.maxrateget = rate; curclass.maxrateget = llval;
curclass.rateget = rate; curclass.rateget = llval;
} else if (strcasecmp(word, "rateput") == 0) { } else if (strcasecmp(word, "rateput") == 0) {
if (none || EMPTYSTR(arg)) if (none || EMPTYSTR(arg))
continue; continue;
rate = strsuftoi(arg); llval = strsuftoll(arg);
if (rate == -1) { if (llval == -1) {
syslog(LOG_WARNING, syslog(LOG_WARNING,
"%s line %d: invalid rateput %s", "%s line %d: invalid rateput %s",
infile, (int)line, arg); infile, (int)line, arg);
continue; continue;
} }
curclass.maxrateput = rate; curclass.maxrateput = llval;
curclass.rateput = rate; curclass.rateput = llval;
} else if (strcasecmp(word, "sanenames") == 0) {
CONF_FLAG(sanenames);
} else if (strcasecmp(word, "timeout") == 0) { } else if (strcasecmp(word, "timeout") == 0) {
if (none || EMPTYSTR(arg)) if (none || EMPTYSTR(arg))
@ -451,12 +455,9 @@ parse_conf(const char *findclass)
curclass.umask = umask; curclass.umask = umask;
} else if (strcasecmp(word, "upload") == 0) { } else if (strcasecmp(word, "upload") == 0) {
if (none || CONF_FLAG(upload);
(!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) { if (! CURCLASS_FLAGS_ISSET(upload))
curclass.modify = 0; CURCLASS_FLAGS_CLR(modify);
curclass.upload = 0;
} else
curclass.upload = 1;
} else { } else {
syslog(LOG_WARNING, syslog(LOG_WARNING,
@ -547,8 +548,10 @@ display_file(const char *file, int code)
FILE *f; FILE *f;
char *buf, *p, *cwd; char *buf, *p, *cwd;
size_t len; size_t len;
off_t lastnum;
time_t now; time_t now;
lastnum = 0;
if (quietmessages) if (quietmessages)
return (0); return (0);
@ -587,7 +590,9 @@ display_file(const char *file, int code)
break; break;
case 'E': case 'E':
/* XXXX email address */ if (! EMPTYSTR(emailaddr))
cprintf(stdout, "%s",
emailaddr);
break; break;
case 'L': case 'L':
@ -595,23 +600,35 @@ display_file(const char *file, int code)
break; break;
case 'M': case 'M':
if (curclass.limit == -1) if (curclass.limit == -1) {
cprintf(stdout, "unlimited"); cprintf(stdout, "unlimited");
else lastnum = 0;
} else {
cprintf(stdout, "%d", cprintf(stdout, "%d",
curclass.limit); curclass.limit);
lastnum = curclass.limit;
}
break; break;
case 'N': case 'N':
if (connections > 0) cprintf(stdout, "%d", connections);
cprintf(stdout, "%d", lastnum = connections;
connections);
break; break;
case 'R': case 'R':
cprintf(stdout, "%s", remotehost); cprintf(stdout, "%s", remotehost);
break; break;
case 's':
if (lastnum != 1)
cprintf(stdout, "s");
break;
case 'S':
if (lastnum != 1)
cprintf(stdout, "S");
break;
case 'T': case 'T':
now = time(NULL); now = time(NULL);
cprintf(stdout, "%.24s", ctime(&now)); 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 * Convert the string `arg' to a long long, which may have an optional SI suffix
* (`b', `k', `m', `g'). Returns the number for success, -1 otherwise. * (`b', `k', `m', `g', `t'). Returns the number for success, -1 otherwise.
*/ */
int LLT
strsuftoi(const char *arg) strsuftoll(const char *arg)
{ {
char *cp; char *cp;
long val; LLT val;
if (!isdigit((unsigned char)arg[0])) if (!isdigit((unsigned char)arg[0]))
return (-1); return (-1);
val = strtol(arg, &cp, 10); val = STRTOLL(arg, &cp, 10);
if (cp != NULL) { if (cp != NULL) {
if (cp[0] != '\0' && cp[1] != '\0') if (cp[0] != '\0' && cp[1] != '\0')
return (-1); return (-1);
@ -839,11 +856,14 @@ strsuftoi(const char *arg)
case 'g': case 'g':
val <<= 30; val <<= 30;
break; break;
case 't':
val <<= 40;
break;
default: default:
return (-1); return (-1);
} }
} }
if (val < 0 || val > INT_MAX) if (val < 0)
return (-1); return (-1);
return (val); 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 * Copyright (c) 1992, 1993
@ -100,6 +100,24 @@
* SUCH DAMAGE. * 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 blkfree(char **);
void closedataconn(FILE *); void closedataconn(FILE *);
char *conffilename(const char *); char *conffilename(const char *);
@ -152,17 +170,11 @@ void sizecmd(const char *);
void statcmd(void); void statcmd(void);
void statfilecmd(const char *); void statfilecmd(const char *);
void store(const char *, const char *, int); void store(const char *, const char *, int);
int strsuftoi(const char *); LLT strsuftoll(const char *);
void user(const char *); void user(const char *);
char *xstrdup(const char *); char *xstrdup(const char *);
void yyerror(char *); void yyerror(char *);
typedef enum {
CLASS_GUEST,
CLASS_CHROOT,
CLASS_REAL
} class_ft;
struct tab { struct tab {
char *name; char *name;
short token; short token;
@ -180,32 +192,48 @@ struct ftpconv {
char *command; /* Command to do the conversion */ 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 { struct ftpclass {
int checkportcmd; /* Check PORT commands are valid */
char *chroot; /* Directory to chroot(2) to at login */ char *chroot; /* Directory to chroot(2) to at login */
char *classname; /* Current class */ char *classname; /* Current class */
struct ftpconv *conversions; /* List of conversions */ 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 */ char *homedir; /* Directory to chdir(2) to at login */
classflag_t flags; /* Flags; see classflag_t above */
int limit; /* Max connections (-1 = unlimited) */ int limit; /* Max connections (-1 = unlimited) */
char *limitfile; /* File to display if limit reached */ char *limitfile; /* File to display if limit reached */
int maxrateget; /* Maximum get transfer rate throttle */ LLT maxfilesize; /* Maximum file size of uploads */
int maxrateput; /* Maximum put transfer rate throttle */ LLT maxrateget; /* Maximum get transfer rate throttle */
LLT maxrateput; /* Maximum put transfer rate throttle */
unsigned int maxtimeout; /* Maximum permitted timeout */ unsigned int maxtimeout; /* Maximum permitted timeout */
int modify; /* Allow CHMOD, DELE, MKD, RMD, RNFR,
UMASK */
char *motd; /* MotD file to display after login */ char *motd; /* MotD file to display after login */
char *notify; /* Files to notify about upon chdir */ char *notify; /* Files to notify about upon chdir */
int passive; /* Allow PASV mode */
int portmin; /* Minumum port for passive mode */ int portmin; /* Minumum port for passive mode */
int portmax; /* Maximum port for passive mode */ int portmax; /* Maximum port for passive mode */
int rateget; /* Get (RETR) transfer rate throttle */ LLT rateget; /* Get (RETR) transfer rate throttle */
int rateput; /* Put (STOR) transfer rate throttle */ LLT rateput; /* Put (STOR) transfer rate throttle */
unsigned int timeout; /* Default timeout */ unsigned int timeout; /* Default timeout */
class_ft type; /* Class type */ class_ft type; /* Class type */
mode_t umask; /* Umask to use */ mode_t umask; /* Umask to use */
int upload; /* As per modify, but also allow
APPE, STOR, STOU */
}; };
#include <netinet/in.h> #include <netinet/in.h>
@ -255,6 +283,7 @@ GLOBAL int connections;
GLOBAL struct ftpclass curclass; GLOBAL struct ftpclass curclass;
GLOBAL int debug; GLOBAL int debug;
GLOBAL jmp_buf errcatch; GLOBAL jmp_buf errcatch;
GLOBAL char *emailaddr;
GLOBAL int form; GLOBAL int form;
GLOBAL int gidcount; /* number of entries in gidlist[] */ GLOBAL int gidcount; /* number of entries in gidlist[] */
GLOBAL gid_t gidlist[NGROUPS_MAX]; GLOBAL gid_t gidlist[NGROUPS_MAX];
@ -319,21 +348,3 @@ extern struct tab cmdtab[];
#ifndef IPPORT_ANONMAX #ifndef IPPORT_ANONMAX
# define IPPORT_ANONMAX 65535 # define IPPORT_ANONMAX 65535
#endif #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. * Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
@ -83,7 +83,7 @@
#if 0 #if 0
static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
#else #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
#endif /* not lint */ #endif /* not lint */
@ -165,7 +165,7 @@ char *fromname;
%token <s> ALL %token <s> ALL
%token <i> NUMBER %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 <i> struct_code mode_code type_code form_code decimal_integer
%type <s> pathstring pathname password username %type <s> pathstring pathname password username
%type <s> mechanism_name base64data prot_code %type <s> mechanism_name base64data prot_code
@ -282,7 +282,7 @@ cmd
| PASV check_login CRLF | PASV check_login CRLF
{ {
if ($2) { if ($2) {
if (curclass.passive) if (CURCLASS_FLAGS_ISSET(passive))
passive(); passive();
else else
reply(500, "PASV mode not available."); reply(500, "PASV mode not available.");
@ -398,28 +398,28 @@ cmd
free($4); free($4);
} }
| STOR check_upload SP pathname CRLF | STOR SP pathname CRLF
{ {
if ($2 && $4 != NULL) if (check_write($3, 1))
store($4, "w", 0); store($3, "w", 0);
if ($4 != NULL) if ($3 != NULL)
free($4); free($3);
} }
| STOU check_upload SP pathname CRLF | STOU SP pathname CRLF
{ {
if ($2 && $4 != NULL) if (check_write($3, 1))
store($4, "w", 1); store($3, "w", 1);
if ($4 != NULL) if ($3 != NULL)
free($4); free($3);
} }
| APPE check_upload SP pathname CRLF | APPE SP pathname CRLF
{ {
if ($2 && $4 != NULL) if (check_write($3, 1))
store($4, "a", 0); store($3, "a", 0);
if ($4 != NULL) if ($3 != NULL)
free($4); free($3);
} }
| ALLO check_login SP NUMBER CRLF | ALLO check_login SP NUMBER CRLF
@ -434,18 +434,19 @@ cmd
reply(202, "ALLO command ignored."); reply(202, "ALLO command ignored.");
} }
| RNTO check_login SP pathname CRLF | RNTO SP pathname CRLF
{ {
if ($2) { if (check_write($3, 0)) {
if (fromname) { if (fromname) {
renamecmd(fromname, $4); renamecmd(fromname, $3);
free(fromname); free(fromname);
fromname = NULL; fromname = NULL;
} else { } else {
reply(503, "Bad sequence of commands."); reply(503, "Bad sequence of commands.");
} }
} }
free($4); if ($3 != NULL)
free($3);
} }
| ABOR check_login CRLF | ABOR check_login CRLF
@ -454,28 +455,28 @@ cmd
reply(225, "ABOR command successful."); reply(225, "ABOR command successful.");
} }
| DELE check_modify SP pathname CRLF | DELE SP pathname CRLF
{ {
if ($2 && $4 != NULL) if (check_write($3, 0))
delete($4); delete($3);
if ($4 != NULL) if ($3 != NULL)
free($4); free($3);
} }
| RMD check_modify SP pathname CRLF | RMD SP pathname CRLF
{ {
if ($2 && $4 != NULL) if (check_write($3, 0))
removedir($4); removedir($3);
if ($4 != NULL) if ($3 != NULL)
free($4); free($3);
} }
| MKD check_modify SP pathname CRLF | MKD SP pathname CRLF
{ {
if ($2 && $4 != NULL) if (check_write($3, 0))
makedir($4); makedir($3);
if ($4 != NULL) if ($3 != NULL)
free($4); free($3);
} }
| PWD check_login CRLF | PWD check_login CRLF
@ -522,19 +523,19 @@ cmd
help(sitetab, NULL); 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 (check_write($7, 0)) {
if ($6 > 0777) if ($5 > 0777)
reply(501, reply(501,
"CHMOD: Mode value must be between 0 and 0777"); "CHMOD: Mode value must be between 0 and 0777");
else if (chmod($8, $6) < 0) else if (chmod($7, $5) < 0)
perror_reply(550, $8); perror_reply(550, $7);
else else
reply(200, "CHMOD command successful."); reply(200, "CHMOD command successful.");
} }
if ($8 != NULL) if ($7 != NULL)
free($8); free($7);
} }
| SITE SP HELP SP STRING CRLF | SITE SP HELP SP STRING CRLF
@ -572,30 +573,32 @@ cmd
| SITE SP RATEGET check_login CRLF | SITE SP RATEGET check_login CRLF
{ {
if ($4) { if ($4) {
reply(200, "Current RATEGET is %d bytes/sec", reply(200,
curclass.rateget); "Current RATEGET is " LLF " bytes/sec",
(LLT)curclass.rateget);
} }
} }
| SITE SP RATEGET check_login SP STRING CRLF | SITE SP RATEGET check_login SP STRING CRLF
{ {
char *p = $6; char *p = $6;
int rate; LLT rate;
if ($4) { if ($4) {
rate = strsuftoi(p); rate = strsuftoll(p);
if (rate == -1) if (rate == -1)
reply(501, "Invalid RATEGET %s", p); reply(501, "Invalid RATEGET %s", p);
else if (curclass.maxrateget && else if (curclass.maxrateget &&
rate > curclass.maxrateget) rate > curclass.maxrateget)
reply(501, reply(501,
"RATEGET %d is larger than maximum RATEGET %d", "RATEGET " LLF " is larger than maximum RATEGET " LLF,
rate, curclass.maxrateget); (LLT)rate,
(LLT)curclass.maxrateget);
else { else {
curclass.rateget = rate; curclass.rateget = rate;
reply(200, reply(200,
"RATEGET set to %d bytes/sec", "RATEGET set to " LLF " bytes/sec",
curclass.rateget); (LLT)curclass.rateget);
} }
} }
free($6); free($6);
@ -604,30 +607,32 @@ cmd
| SITE SP RATEPUT check_login CRLF | SITE SP RATEPUT check_login CRLF
{ {
if ($4) { if ($4) {
reply(200, "Current RATEPUT is %d bytes/sec", reply(200,
curclass.rateput); "Current RATEPUT is " LLF " bytes/sec",
(LLT)curclass.rateput);
} }
} }
| SITE SP RATEPUT check_login SP STRING CRLF | SITE SP RATEPUT check_login SP STRING CRLF
{ {
char *p = $6; char *p = $6;
int rate; LLT rate;
if ($4) { if ($4) {
rate = strsuftoi(p); rate = strsuftoll(p);
if (rate == -1) if (rate == -1)
reply(501, "Invalid RATEPUT %s", p); reply(501, "Invalid RATEPUT %s", p);
else if (curclass.maxrateput && else if (curclass.maxrateput &&
rate > curclass.maxrateput) rate > curclass.maxrateput)
reply(501, reply(501,
"RATEPUT %d is larger than maximum RATEPUT %d", "RATEPUT " LLF " is larger than maximum RATEPUT " LLF,
rate, curclass.maxrateput); (LLT)rate,
(LLT)curclass.maxrateput);
else { else {
curclass.rateput = rate; curclass.rateput = rate;
reply(200, reply(200,
"RATEPUT set to %d bytes/sec", "RATEPUT set to " LLF " bytes/sec",
curclass.rateput); (LLT)curclass.rateput);
} }
} }
free($6); 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; int oldmask;
if ($4) { if ($4 && CURCLASS_FLAGS_ISSET(modify)) {
if (($6 == -1) || ($6 > 0777)) { if (($6 == -1) || ($6 > 0777)) {
reply(501, "Bad UMASK value"); reply(501, "Bad UMASK value");
} else { } else {
@ -854,21 +859,20 @@ rcmd
{ {
if ($2) { if ($2) {
fromname = NULL; fromname = NULL;
restart_point = $4; /* XXX $3 is only "int" */ restart_point = $4; /* XXX $4 is only "int" */
reply(350, reply(350,
"Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
(LLT)restart_point); (LLT)restart_point);
} }
} }
| RNFR check_modify SP pathname CRLF | RNFR SP pathname CRLF
{ {
restart_point = (off_t) 0; restart_point = (off_t) 0;
if ($2 && $4) { if (check_write($3, 0))
fromname = renamefrom($4); fromname = renamefrom($3);
} if ($3 != NULL)
if ($4) free($3);
free($4);
} }
; ;
@ -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 */ #define CMD 0 /* beginning of command */
@ -1290,13 +1255,55 @@ struct tab sitetab[] = {
{ NULL, 0, 0, 0, NULL } { NULL, 0, 0, 0, NULL }
}; };
static void help(struct tab *, const char *); static int check_write(const char *, int);
static void port_check(const char *, int); static void help(struct tab *, const char *);
static void toolong(int); static void port_check(const char *, int);
static int yylex(void); static void toolong(int);
static int yylex(void);
extern int epsvall; 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 * struct tab *
lookup(struct tab *p, const char *cmd) lookup(struct tab *p, const char *cmd)
{ {
@ -1750,7 +1757,7 @@ port_check(const char *cmd, int family)
goto port_check_fail; goto port_check_fail;
/* be paranoid, if told so */ /* be paranoid, if told so */
if (curclass.checkportcmd) { if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
if ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || if ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
(data_dest.su_len != his_addr.su_len)) (data_dest.su_len != his_addr.su_len))
goto port_check_fail; 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. .\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
.\" All rights reserved. .\" All rights reserved.
@ -67,7 +67,7 @@
.\" .\"
.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 .\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94
.\" .\"
.Dd July 26, 2000 .Dd November 16, 2000
.Dt FTPD 8 .Dt FTPD 8
.Os .Os
.Sh NAME .Sh NAME
@ -76,11 +76,13 @@
Internet File Transfer Protocol server Internet File Transfer Protocol server
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl dHlrsUW .Op Fl dHlqQrsuUwW
.Op Fl a Ar anondir .Op Fl a Ar anondir
.Op Fl c Ar confdir .Op Fl c Ar confdir
.Op Fl C Ar user .Op Fl C Ar user
.Op Fl e Ar emailaddr
.Op Fl h Ar hostname .Op Fl h Ar hostname
.Op Fl P Ar dataport
.Op Fl V Ar version .Op Fl V Ar version
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
@ -119,6 +121,13 @@ This can be useful for testing configurations.
.It Fl d .It Fl d
Debugging information is written to the syslog using Debugging information is written to the syslog using
.Dv LOG_FTP . .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 .It Fl h Ar hostname
Explicitly set the hostname to advertise as to Explicitly set the hostname to advertise as to
.Ar hostname . .Ar hostname .
@ -149,7 +158,24 @@ session is logged using syslog with a facility of
.Dv LOG_FTP . .Dv LOG_FTP .
If this option is specified twice, the retrieve (get), store (put), append, If this option is specified twice, the retrieve (get), store (put), append,
delete, make directory, remove directory and rename operations and 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 .It Fl r
Permanently drop root privileges once the user is logged in. Permanently drop root privileges once the user is logged in.
The use of this option may result in the server using a port other The use of this option may result in the server using a port other
@ -163,13 +189,19 @@ See
below for more details. below for more details.
.It Fl s .It Fl s
Require a secure authentication mechanism like Kerberos or S/Key to be used. Require a secure authentication mechanism like Kerberos or S/Key to be used.
.It Fl U .It Fl u
Each concurrent Log each concurrent
.Tn FTP .Tn FTP
session is logged to the file session to
.Pa /var/run/utmp , .Pa /var/run/utmp ,
making them visible to commands such as making them visible to commands such as
.Xr who 1 . .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 .It Fl V Ar version
Use Use
.Ar version .Ar version
@ -183,14 +215,19 @@ If
is empty or is empty or
.Sq - .Sq -
then don't display any version information. then don't display any version information.
.It Fl W .It Fl w
By default each Log each
.Tn FTP .Tn FTP
session is logged to session to
.Pa /var/log/wtmp , .Pa /var/log/wtmp ,
making them visible to commands such as making them visible to commands such as
.Xr last 1 . .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 .El
.Pp .Pp
The file The file
@ -443,6 +480,9 @@ The supported escape strings are:
Class name. Class name.
.It "\&%C" .It "\&%C"
Current working directory. Current working directory.
.It "\&%E"
Email address given with
.Fl e .
.It "\&%L" .It "\&%L"
Local hostname. Local hostname.
.It "\&%M" .It "\&%M"
@ -454,6 +494,24 @@ if there's no limit.
Current number of users for this class. Current number of users for this class.
.It "\&%R" .It "\&%R"
Remote hostname. 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" .It "\&%T"
Current time. Current time.
.It "\&%U" .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 files in this directory the permissions should be 770, otherwise
they should be 370. they should be 370.
.Pp .Pp
Anonymous users will be able to upload files to this directory, The following
but they will not be able to download them, delete them, or overwrite .Xr ftpd.conf 5
them, due to the umask and disabling of the commands mentioned 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. above.
.It Pa ~ftp/tmp .It Pa ~ftp/tmp
This directory is used to create temporary files which contain This directory is used to create temporary files which contain
@ -596,10 +660,10 @@ State file of logged-in processes for the
.Nm .Nm
class class
.Sq CLASS . .Sq CLASS .
.It Pa /var/log/wtmp
Login history database.
.It Pa /var/run/utmp .It Pa /var/run/utmp
List of logged-in users on the system. List of logged-in users on the system.
.It Pa /var/log/wtmp
Login history database.
.El .El
.Sh SEE ALSO .Sh SEE ALSO
.Xr ftp 1 , .Xr ftp 1 ,
@ -696,7 +760,8 @@ communicate back to the client for the
.Sy LPTR , .Sy LPTR ,
and and
.Sy PORT .Sy PORT
commands. commands, unless overridden with
.Fl P Ar dataport .
As the default port for As the default port for
.Nm .Nm
(21) is a privileged port below (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. * Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
@ -109,7 +109,7 @@ __COPYRIGHT(
#if 0 #if 0
static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95"; static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
#else #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
#endif /* not lint */ #endif /* not lint */
@ -172,6 +172,8 @@ struct passwd *pw;
int sflag; int sflag;
int stru; /* avoid C keyword */ int stru; /* avoid C keyword */
int mode; int mode;
int dataport; /* use specific data port */
int dopidfile; /* maintain pid file */
int doutmp; /* update utmp file */ int doutmp; /* update utmp file */
int dowtmp; /* update wtmp file */ int dowtmp; /* update wtmp file */
int dropprivs; /* if privileges should or have been dropped */ int dropprivs; /* if privileges should or have been dropped */
@ -181,8 +183,8 @@ off_t byte_count;
static char ttyline[20]; static char ttyline[20];
static struct utmp utmp; /* for utmp */ static struct utmp utmp; /* for utmp */
static char *anondir = NULL; static const char *anondir = NULL;
static char confdir[MAXPATHLEN]; static const char *confdir = NULL;
#if defined(KERBEROS) || defined(KERBEROS5) #if defined(KERBEROS) || defined(KERBEROS5)
int has_ccache = 0; int has_ccache = 0;
@ -236,31 +238,41 @@ main(int argc, char *argv[])
#ifdef KERBEROS5 #ifdef KERBEROS5
krb5_error_code kerror; krb5_error_code kerror;
#endif #endif
char *p;
connections = 1; connections = 1;
debug = 0; debug = 0;
logging = 0; logging = 0;
pdata = -1; pdata = -1;
sflag = 0; sflag = 0;
dataport = 0;
dopidfile = 1; /* default: DO use a pid file to count users */
doutmp = 0; /* default: don't log to utmp */ doutmp = 0; /* default: don't log to utmp */
dowtmp = 1; /* default: DO log to wtmp */ dowtmp = 1; /* default: DO log to wtmp */
dropprivs = 0; dropprivs = 0;
mapped = 0; mapped = 0;
usedefault = 1; usedefault = 1;
(void)strcpy(confdir, _DEFAULT_CONFDIR); emailaddr = NULL;
hostname[0] = '\0'; hostname[0] = '\0';
homedir[0] = '\0'; homedir[0] = '\0';
gidcount = 0; gidcount = 0;
version = FTPD_VERSION; 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) { switch (ch) {
case 'a': case 'a':
anondir = optarg; anondir = optarg;
break; break;
case 'c': case 'c':
(void)strlcpy(confdir, optarg, sizeof(confdir)); confdir = optarg;
break; break;
case 'C': case 'C':
@ -273,6 +285,10 @@ main(int argc, char *argv[])
debug = 1; debug = 1;
break; break;
case 'e':
emailaddr = optarg;
break;
case 'h': case 'h':
strlcpy(hostname, optarg, sizeof(hostname)); strlcpy(hostname, optarg, sizeof(hostname));
break; break;
@ -287,6 +303,24 @@ main(int argc, char *argv[])
logging++; /* > 1 == extra logging */ logging++; /* > 1 == extra logging */
break; 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': case 'r':
dropprivs = 1; dropprivs = 1;
break; break;
@ -297,15 +331,19 @@ main(int argc, char *argv[])
case 't': case 't':
case 'T': case 'T':
case 'u': syslog(LOG_ERR,
warnx("-%c has been deprecated in favour of ftpd.conf", "-%c has been deprecated in favour of ftpd.conf",
ch); ch);
break; break;
case 'U': case 'u':
doutmp = 1; doutmp = 1;
break; break;
case 'U':
doutmp = 0;
break;
case 'V': case 'V':
if (EMPTYSTR(optarg) || strcmp(optarg, "-") == 0) if (EMPTYSTR(optarg) || strcmp(optarg, "-") == 0)
version = NULL; version = NULL;
@ -313,6 +351,10 @@ main(int argc, char *argv[])
version = xstrdup(optarg); version = xstrdup(optarg);
break; break;
case 'w':
dowtmp = 1;
break;
case 'W': case 'W':
dowtmp = 0; dowtmp = 0;
break; break;
@ -320,16 +362,13 @@ main(int argc, char *argv[])
default: default:
if (optopt == 'a' || optopt == 'C') if (optopt == 'a' || optopt == 'C')
exit(1); exit(1);
warnx("unknown flag -%c ignored", optopt); syslog(LOG_ERR, "unknown flag -%c ignored", optopt);
break; 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)); memset((char *)&his_addr, 0, sizeof (his_addr));
addrlen = sizeof(his_addr.si_su); addrlen = sizeof(his_addr.si_su);
if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0) { 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 ftpd.conf, setting up various parameters */
parse_conf(class); parse_conf(class);
count_users(); connections = 1;
if (dopidfile)
count_users();
if (curclass.limit != -1 && connections > curclass.limit) { if (curclass.limit != -1 && connections > curclass.limit) {
if (! EMPTYSTR(curclass.limitfile)) if (! EMPTYSTR(curclass.limitfile))
(void)display_file(conffilename(curclass.limitfile), (void)display_file(conffilename(curclass.limitfile),
@ -1090,9 +1131,11 @@ pass(const char *passwd)
} }
(void) umask(curclass.umask); (void) umask(curclass.umask);
goto cleanuppass; goto cleanuppass;
bad: bad:
/* Forget all about it... */ /* Forget all about it... */
end_login(); end_login();
cleanuppass: cleanuppass:
if (class) if (class)
free(class); free(class);
@ -1338,7 +1381,10 @@ getdatasock(const char *mode)
* would be < IPPORT_RESERVED, use a random port * would be < IPPORT_RESERVED, use a random port
* instead. * 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) if (dropprivs && port < IPPORT_RESERVED)
port = 0; /* use random port */ port = 0; /* use random port */
data_source.su_port = htons(port); data_source.su_port = htons(port);
@ -1632,6 +1678,7 @@ static int
receive_data(FILE *instr, FILE *outstr) receive_data(FILE *instr, FILE *outstr)
{ {
int c, bare_lfs, netfd, filefd, rval; int c, bare_lfs, netfd, filefd, rval;
off_t byteswritten;
char buf[BUFSIZ]; char buf[BUFSIZ];
#ifdef __GNUC__ #ifdef __GNUC__
(void) &bare_lfs; (void) &bare_lfs;
@ -1640,9 +1687,19 @@ receive_data(FILE *instr, FILE *outstr)
bare_lfs = 0; bare_lfs = 0;
transflag = 1; transflag = 1;
rval = -1; rval = -1;
byteswritten = 0;
if (setjmp(urgcatch)) if (setjmp(urgcatch))
goto cleanup_recv_data; goto cleanup_recv_data;
#define FILESIZECHECK(x) \
do { \
if (curclass.maxfilesize != -1 && \
(x) > curclass.maxfilesize) { \
errno = EFBIG; \
goto file_err; \
} \
} while (0)
switch (type) { switch (type) {
case TYPE_I: case TYPE_I:
@ -1662,8 +1719,9 @@ receive_data(FILE *instr, FILE *outstr)
if ((c = read(netfd, buf, if ((c = read(netfd, buf,
MIN(sizeof(buf), bufrem))) <= 0) MIN(sizeof(buf), bufrem))) <= 0)
goto recvdone; goto recvdone;
FILESIZECHECK(byte_count + c);
if ((d = write(filefd, buf, c)) != c) if ((d = write(filefd, buf, c)) != c)
goto recvdone; goto file_err;
(void) alarm(curclass.timeout); (void) alarm(curclass.timeout);
bufrem -= c; bufrem -= c;
byte_count += c; byte_count += c;
@ -1679,6 +1737,7 @@ receive_data(FILE *instr, FILE *outstr)
} }
} else { } else {
while ((c = read(netfd, buf, sizeof(buf))) > 0) { while ((c = read(netfd, buf, sizeof(buf))) > 0) {
FILESIZECHECK(byte_count + c);
if (write(filefd, buf, c) != c) if (write(filefd, buf, c) != c)
goto file_err; goto file_err;
(void) alarm(curclass.timeout); (void) alarm(curclass.timeout);
@ -1723,13 +1782,17 @@ receive_data(FILE *instr, FILE *outstr)
total_bytes++; total_bytes++;
if ((byte_count % 4096) == 0) if ((byte_count % 4096) == 0)
(void) alarm(curclass.timeout); (void) alarm(curclass.timeout);
byteswritten++;
FILESIZECHECK(byteswritten);
(void) putc ('\r', outstr); (void) putc ('\r', outstr);
if (c == '\0' || c == EOF) if (c == '\0' || c == EOF)
goto contin2; goto contin2;
} }
} }
byteswritten++;
FILESIZECHECK(byteswritten);
(void) putc(c, outstr); (void) putc(c, outstr);
contin2: ; contin2: ;
} }
(void) alarm(0); (void) alarm(0);
fflush(outstr); fflush(outstr);
@ -1750,6 +1813,7 @@ receive_data(FILE *instr, FILE *outstr)
reply(550, "Unimplemented TYPE %d in receive_data", type); reply(550, "Unimplemented TYPE %d in receive_data", type);
goto cleanup_recv_data; goto cleanup_recv_data;
} }
#undef FILESIZECHECK(x)
data_err: data_err:
(void) alarm(0); (void) alarm(0);
@ -1925,7 +1989,7 @@ statcmd(void)
reply(0, "Class: %s, type: %s", reply(0, "Class: %s, type: %s",
curclass.classname, CURCLASSTYPE); curclass.classname, CURCLASSTYPE);
reply(0, "Check PORT/LPRT commands: %sabled", reply(0, "Check PORT/LPRT commands: %sabled",
curclass.checkportcmd ? "en" : "dis"); CURCLASS_FLAGS_ISSET(checkportcmd) ? "en" : "dis");
if (! EMPTYSTR(curclass.display)) if (! EMPTYSTR(curclass.display))
reply(0, "Display file: %s", curclass.display); reply(0, "Display file: %s", curclass.display);
if (! EMPTYSTR(curclass.notify)) if (! EMPTYSTR(curclass.notify))
@ -1938,30 +2002,39 @@ statcmd(void)
else else
reply(0, "Maximum connections: %d", curclass.limit); reply(0, "Maximum connections: %d", curclass.limit);
if (curclass.limitfile) if (curclass.limitfile)
reply(0, "Connection limit exceeded file: %s", reply(0, "Connection limit exceeded message file: %s",
curclass.limitfile); curclass.limitfile);
if (! EMPTYSTR(curclass.chroot)) if (! EMPTYSTR(curclass.chroot))
reply(0, "Chroot format: %s", curclass.chroot); reply(0, "Chroot format: %s", curclass.chroot);
if (! EMPTYSTR(curclass.homedir)) if (! EMPTYSTR(curclass.homedir))
reply(0, "Homedir format: %s", 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)) if (! EMPTYSTR(curclass.motd))
reply(0, "MotD file: %s", curclass.motd); reply(0, "MotD file: %s", curclass.motd);
reply(0, reply(0,
"Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled", "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", 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) if (curclass.portmin && curclass.portmax)
reply(0, "PASV port range: %d - %d", reply(0, "PASV port range: %d - %d",
curclass.portmin, curclass.portmax); curclass.portmin, curclass.portmax);
if (curclass.rateget) if (curclass.rateget)
reply(0, "Rate get limit: %d bytes/sec", reply(0, "Rate get limit: " LLF " bytes/sec",
curclass.rateget); (LLT)curclass.rateget);
else else
reply(0, "Rate get limit: disabled"); reply(0, "Rate get limit: disabled");
if (curclass.rateput) if (curclass.rateput)
reply(0, "Rate put limit: %d bytes/sec", reply(0, "Rate put limit: " LLF " bytes/sec",
curclass.rateput); (LLT)curclass.rateput);
else else
reply(0, "Rate put limit: disabled"); reply(0, "Rate put limit: disabled");
reply(0, "Umask: %.04o", curclass.umask); 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) { if (pf != PF_UNSPEC && ctrl_addr.su_family != pf) {
/* /*
* XXX * XXX: only EPRT/EPSV ready clients will understand this
* only EPRT/EPSV ready clients will understand this
*/ */
if (strcmp(cmd, "EPSV") != 0) if (strcmp(cmd, "EPSV") != 0)
reply(501, "Network protocol mismatch"); /*XXX*/ reply(501, "Network protocol mismatch"); /*XXX*/
@ -2364,7 +2436,7 @@ extended_port(const char *arg)
p = q; p = q;
} }
/* some more sanity check */ /* some more sanity checks */
p = NULL; p = NULL;
(void)strtoul(result[2], &p, 10); (void)strtoul(result[2], &p, 10);
if (!*result[2] || *p) if (!*result[2] || *p)
@ -2389,7 +2461,7 @@ extended_port(const char *arg)
memcpy(&data_dest, res->ai_addr, res->ai_addrlen); memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
if (his_addr.su_family == AF_INET6 && if (his_addr.su_family == AF_INET6 &&
data_dest.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; 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); proto = af2epsvproto(ctrl_addr.su_family);
if (proto < 0) if (proto < 0)
reply(501, "%s", message); /*XXX*/ reply(501, "%s", message); /* XXX */
else else
reply(522, "%s, use (%d)", message, proto); 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. .\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
.\" All rights reserved. .\" All rights reserved.
@ -34,7 +34,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE. .\" POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.Dd November 7, 2000 .Dd November 16, 2000
.Dt FTPD.CONF 5 .Dt FTPD.CONF 5
.Os .Os
.Sh NAME .Sh NAME
@ -58,7 +58,7 @@ This allows
.Sq wildcard .Sq wildcard
entries to define defaults, and then have class-specific overrides. entries to define defaults, and then have class-specific overrides.
.Pp .Pp
A directive line has the format A directive line has the format:
.Dl command class [arguments] .Dl command class [arguments]
.Pp .Pp
A A
@ -211,7 +211,7 @@ Valid types are:
(directory). (directory).
.It Ar disable .It Ar disable
The name of file that will prevent conversion if it exists. The name of file that will prevent conversion if it exists.
A filename of A file name of
.Dq Pa \&. .Dq Pa \&.
will prevent this disabling action will prevent this disabling action
(i.e., the conversion is always permitted.) (i.e., the conversion is always permitted.)
@ -295,6 +295,9 @@ for
and and
.Sy CHROOT .Sy CHROOT
users. 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 .It Sy maxtimeout Ar class Ar time
Set the maximum timeout period that a client may request, Set the maximum timeout period that a client may request,
defaulting to two hours. defaulting to two hours.
@ -392,6 +395,19 @@ to
bytes per second, bytes per second,
which is parsed as per which is parsed as per
.Sy rateget Ar rate . .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 .It Sy template Ar class Op Ar refclass
Define Define
.Ar refclass .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. * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
@ -36,5 +36,5 @@
*/ */
#ifndef FTPD_VERSION #ifndef FTPD_VERSION
#define FTPD_VERSION "NetBSD-ftpd 20001115" #define FTPD_VERSION "NetBSD-ftpd 20001116"
#endif #endif