* implement /etc/ftpd.conf, which adds support for the following features,

controllable on a per class (which is one of: real, chroot, guest,
  all or none) basis:
    * on-the-fly execution of a command to build the file (a ``conversion''),
      providing support for "get dirname.tar" and the like.
    * displaying the contents of a file when a directory is entered
      for the first time.
    * maximum value for timeout (replaces -T).
    * control usage of CHMOD, DELE, MKD, RMD, UMASK; replacing -DINSECURE_GUEST.
    * notifying the user of the existance of a files matching a glob
      pattern when a directory is entered for the first time.
    * default value for timeout (replaces -t).
    * default umask (replaces -DGUEST_CMASK and -u).
  The conversion, display, and notify functionality was based on code by
  Simon Burge <simonb@telstra.com.au>.
* clean up and re-order parts of the man page into subsections.
* STAT displays the settings defined for the class of the current user.
* bump version from 6.00 to 7.00, because of ftpd.conf.
* deprecate -DGUEST_CMASK and -DINSECURE_GUEST in the Makefile, and
  -t, -T and -u, as ftpd.conf allows finer control of these.
* add "nostderr" argument to ftpd_popen(), because you don't want the
  stderr stream mixing with the stdout stream during a conversion,
  as this can corrupt the stream.
This commit is contained in:
lukem 1997-06-14 08:43:26 +00:00
parent 0a5a273048
commit 31547ec641
8 changed files with 835 additions and 182 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.16 1997/04/27 03:21:38 lukem Exp $
# $NetBSD: Makefile,v 1.17 1997/06/14 08:43:26 lukem Exp $
# @(#)Makefile 8.2 (Berkeley) 4/4/94
# XXX Work-around a compiler bug for now... can't use -O
@ -6,14 +6,9 @@
CFLAGS=
.endif
# uncomment this to disallow group directory owners access to uploaded files
#CFLAGS+= -DGUEST_CMASK=0777
# uncomment this to enable umask, rm and other commands for anonymous users
#CFLAGS+= -DINSECURE_GUEST
PROG= ftpd
CFLAGS+=-DHASSETPROCTITLE -DSKEY -Dunix
SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c
SRCS= conf.c ftpd.c ftpcmd.c logwtmp.c popen.c
MAN= ftpd.8
CLEANFILES+=ftpcmd.c y.tab.h
.PATH: ${.CURDIR}/../../usr.bin/ftp ${.CURDIR}/../../usr.bin/login

434
libexec/ftpd/conf.c Normal file
View File

@ -0,0 +1,434 @@
/* $NetBSD: conf.c,v 1.1 1997/06/14 08:43:27 lukem Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Simon Burge and Luke Mewburn.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef lint
static char rcsid[] = "$NetBSD: conf.c,v 1.1 1997/06/14 08:43:27 lukem Exp $";
#endif /* not lint */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <errno.h>
#include <glob.h>
#include <stdio.h>
#include <string.h>
#include <stringlist.h>
#include <syslog.h>
#include "extern.h"
#include "pathnames.h"
struct ftpclass curclass;
/*
* Parse the configuration file, looking for the named class, and
* define curclass to contain the appropriate settings.
*/
void
parse_conf(findclass)
const char *findclass;
{
FILE *f;
char *buf, *p;
size_t len;
int none, match, cnum;
char *endp;
char *class, *word, *arg;
char *types, *disable, *convcmd;
const char *infile;
int line;
unsigned int timeout;
struct ftpconv *conv, *cnext;
#define REASSIGN(X,Y) if (X) free(X); (X)=(Y)
#define NEXTWORD(W) while ((W = strsep(&buf, " \t")) != NULL && *W == '\0')
#define EMPTYSTR(W) (W == NULL || *W == '\0')
REASSIGN(curclass.classname, findclass);
for (conv = curclass.conversions; conv != NULL; conv=cnext) {
REASSIGN(conv->suffix, NULL);
REASSIGN(conv->types, NULL);
REASSIGN(conv->disable, NULL);
REASSIGN(conv->command, NULL);
cnext = conv->next;
free(conv);
}
curclass.conversions = NULL;
REASSIGN(curclass.display, NULL);
curclass.modify = 1;
curclass.maxtimeout = 7200; /* 2 hours */
REASSIGN(curclass.notify, NULL);
curclass.timeout = 900; /* 15 minutes */
curclass.umask = 027;
if (strcasecmp(findclass, "guest") == 0) {
curclass.umask = 0707;
curclass.modify = 0;
}
infile = _PATH_FTPDCONF;
if ((f = fopen(infile, "r")) == NULL)
return;
line = 0;
while ((buf = fgetln(f, &len)) != NULL) {
none = match = 0;
line++;
if (buf[len - 1] == '\n')
buf[--len] = '\0';
if ((p = strchr(buf, '#')) != NULL)
*p = '\0';
if (EMPTYSTR(buf))
continue;
NEXTWORD(word);
NEXTWORD(class);
NEXTWORD(arg);
if (EMPTYSTR(word) || EMPTYSTR(class))
continue;
if (strcasecmp(class, "none") == 0)
none = 1;
if (strcasecmp(class, findclass) != 0 &&
!none && strcasecmp(class, "all") != 0)
continue;
if (strcasecmp(word, "conversion") == 0) {
if (EMPTYSTR(arg)) {
syslog(LOG_WARNING,
"%s line %d: %s requires a suffix",
infile, line, word);
continue; /* need a suffix */
}
NEXTWORD(types);
NEXTWORD(disable);
convcmd = buf;
if (convcmd)
convcmd += strspn(convcmd, " \t");
if (none || EMPTYSTR(types) ||
EMPTYSTR(disable) || EMPTYSTR(convcmd)) {
types = NULL;
disable = NULL;
convcmd = NULL;
} else {
types = strdup(types);
disable = strdup(disable);
convcmd = strdup(convcmd);
}
for (conv = curclass.conversions; conv != NULL;
conv = conv->next) {
if (strcmp(conv->suffix, arg) == 0)
break;
}
if (conv == NULL) {
conv = (struct ftpconv *)
calloc(1, sizeof(struct ftpconv));
if (conv == NULL) {
syslog(LOG_WARNING, "can't malloc");
continue;
}
conv->next = curclass.conversions;
curclass.conversions = conv;
}
REASSIGN(conv->suffix, arg);
REASSIGN(conv->types, types);
REASSIGN(conv->disable, disable);
REASSIGN(conv->command, convcmd);
} else if (strcasecmp(word, "display") == 0) {
if (none || EMPTYSTR(arg))
arg = NULL;
else
arg = strdup(arg);
REASSIGN(curclass.display, arg);
} else if (strcasecmp(word, "maxtimeout") == 0) {
if (none || EMPTYSTR(arg))
continue;
timeout = (unsigned int)strtoul(arg, &endp, 10);
if (*endp != 0) {
syslog(LOG_WARNING,
"%s line %d: invalid maxtimeout %s",
infile, line, arg);
continue;
}
if (timeout < 30) {
syslog(LOG_WARNING,
"%s line %d: maxtimeout %d < 30 seconds",
infile, line, timeout);
continue;
}
if (timeout < curclass.timeout) {
syslog(LOG_WARNING,
"%s line %d: maxtimeout %d < timeout (%d)",
infile, line, timeout, curclass.timeout);
continue;
}
curclass.maxtimeout = timeout;
} else if (strcasecmp(word, "modify") == 0) {
if (none ||
!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)
curclass.modify = 0;
else
curclass.modify = 1;
} else if (strcasecmp(word, "notify") == 0) {
if (none || EMPTYSTR(arg))
arg = NULL;
else
arg = strdup(arg);
REASSIGN(curclass.notify, arg);
} else if (strcasecmp(word, "timeout") == 0) {
if (none || EMPTYSTR(arg))
continue;
timeout = (unsigned int)strtoul(arg, &endp, 10);
if (*endp != 0) {
syslog(LOG_WARNING,
"%s line %d: invalid timeout %s",
infile, line, arg);
continue;
}
if (timeout < 30) {
syslog(LOG_WARNING,
"%s line %d: timeout %d < 30 seconds",
infile, line, timeout);
continue;
}
if (timeout > curclass.maxtimeout) {
syslog(LOG_WARNING,
"%s line %d: timeout %d > maxtimeout (%d)",
infile, line, timeout, curclass.maxtimeout);
continue;
}
curclass.timeout = timeout;
} else if (strcasecmp(word, "umask") == 0) {
mode_t umask;
if (none || EMPTYSTR(arg))
continue;
umask = (mode_t)strtoul(arg, &endp, 8);
if (*endp != 0 || umask > 0777) {
syslog(LOG_WARNING,
"%s line %d: invalid umask %s",
infile, line, arg);
continue;
}
curclass.umask = umask;
} else {
syslog(LOG_WARNING,
"%s line %d: unknown directive '%s'",
infile, line, word);
continue;
}
}
#undef REASSIGN
#undef NEXTWORD
#undef EMPTYSTR
fclose(f);
}
/*
* Show file listed in curclass.display first time in, and list all the
* files named in curclass.notify in the current directory. Send back
* responses with the "reply" prefix.
*/
void
show_chdir_messages(code)
int code;
{
static StringList *slist = NULL;
struct stat st;
struct tm *t;
glob_t gl;
time_t now, then;
int age;
char cwd[MAXPATHLEN + 1];
char line[BUFSIZ];
char *cp, **rlist;
FILE *f;
/* Setup list for directory cache */
if (slist == NULL)
slist = sl_init();
/* Check if this directory has already been visited */
if (getcwd(cwd, sizeof(cwd) - 1) == NULL) {
syslog(LOG_WARNING, "show_chdir_messages: can't malloc");
return;
}
if (sl_find(slist, cwd) != NULL)
return;
if ((cp = strdup(cwd)) == NULL) {
syslog(LOG_WARNING, "show_chdir_messages: can't strdup");
return;
}
sl_add(slist, cp);
/* First check for a display file */
if (curclass.display != NULL && curclass.display[0] &&
(f = fopen(curclass.display, "r")) != NULL) {
while (fgets(line, BUFSIZ, f)) {
if ((cp = strchr(line, '\n')) != NULL)
*cp = '\0';
lreply(code, "%s", line);
}
fclose(f);
lreply(code, "");
}
/* Now see if there are any notify files */
if (curclass.notify == NULL || curclass.notify[0] == '\0')
return;
if (glob(curclass.notify, 0, NULL, &gl) != 0 || gl.gl_matchc == 0)
return;
time(&now);
for (rlist = gl.gl_pathv; *rlist != NULL; rlist++) {
if (stat(*rlist, &st) != 0)
continue;
if ((st.st_mode & S_IFMT) != S_IFREG)
continue;
then = st.st_mtime;
lreply(code, "Please read the file %s", *rlist);
t = localtime(&now);
age = 365 * t->tm_year + t->tm_yday;
t = localtime(&then);
age -= 365 * t->tm_year + t->tm_yday;
lreply(code, " it was last modified on %.24s - %d day%s ago",
ctime(&then), age, age == 1 ? "" : "s");
}
globfree(&gl);
}
/*
* Find s2 at the end of s1. If found, return a string up and up (but
* not including) s2, otherwise returns NULL.
*/
static char *
strend(s1, s2)
char *s1, *s2;
{
static char buf[MAXPATHLEN + 1];
char *start;
size_t l1, l2;
l1 = strlen(s1);
l2 = strlen(s2);
if (l2 >= l1)
return(NULL);
strncpy(buf, s1, MAXPATHLEN);
start = buf + (l1 - l2);
if (strcmp(start, s2) == 0) {
*start = '\0';
return(buf);
} else
return(NULL);
}
static int
filetypematch(types, mode)
char *types;
int mode;
{
for ( ; types[0] != '\0'; types++)
switch (*types) {
case 'd':
if (S_ISDIR(mode))
return(1);
break;
case 'f':
if (S_ISREG(mode))
return(1);
break;
}
return(0);
}
/*
* Look for a conversion. If we succeed, return a pointer to the
* command to execute for the conversion.
*
* The command is stored in a static array so there's no memory
* leak problems, and not too much to change in ftpd.c. This
* routine doesn't need to be re-entrant unless we start using a
* multi-threaded ftpd, and that's not likely for a while...
*/
char *
do_conversion(fname)
const char *fname;
{
static char cmd[LINE_MAX];
struct ftpconv *cp;
struct stat st;
int o_errno;
char *base;
o_errno = errno;
for (cp = curclass.conversions; cp != NULL; cp = cp->next) {
if ((base = strend(fname, cp->suffix)) == NULL)
continue;
if (cp->suffix == NULL || cp->types == NULL ||
cp->command == NULL)
continue;
/* Is it enabled? */
if (strcmp(cp->disable, ".") != 0 &&
stat(cp->disable, &st) == 0)
continue;
/* Does the base exist? */
if (stat(base, &st) < 0)
continue;
/* Is the file type ok */
if (!filetypematch(cp->types, st.st_mode))
continue;
break; /* "We have a winner!" */
}
/* If we got through the list, no conversion */
if (cp == NULL) {
errno = o_errno;
return(NULL);
}
snprintf(cmd, LINE_MAX, cp->command, base);
syslog(LOG_INFO, "get command is: %s", cmd);
return(cmd);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: extern.h,v 1.2 1995/04/11 02:44:49 cgd Exp $ */
/* $NetBSD: extern.h,v 1.3 1997/06/14 08:43:28 lukem Exp $ */
/*-
* Copyright (c) 1992, 1993
@ -39,15 +39,17 @@ void blkfree __P((char **));
char **copyblk __P((char **));
void cwd __P((char *));
void delete __P((char *));
char *do_conversion __P((const char *));
void dologout __P((int));
void fatal __P((char *));
int ftpd_pclose __P((FILE *));
FILE *ftpd_popen __P((char *, char *));
FILE *ftpd_popen __P((char *, char *, int));
char *getline __P((char *, int, FILE *));
void logwtmp __P((char *, char *, char *));
void lreply __P((int, const char *, ...));
void makedir __P((char *));
void nack __P((char *));
void parse_conf __P((const char *));
void pass __P((char *));
void passive __P((void));
void perror_reply __P((int, char *));
@ -58,10 +60,34 @@ char *renamefrom __P((char *));
void reply __P((int, const char *, ...));
void retrieve __P((char *, char *));
void send_file_list __P((char *));
void setproctitle __P((const char *, ...));
void show_chdir_messages __P((int));
void statcmd __P((void));
void statfilecmd __P((char *));
void store __P((char *, char *, int));
void upper __P((char *));
void user __P((char *));
void yyerror __P((char *));
#define CLASS_CHROOT "chroot"
#define CLASS_GUEST "guest"
#define CLASS_REAL "real"
struct ftpconv {
struct ftpconv *next;
const char *suffix; /* Suffix of requested name */
const char *types; /* Valid file types */
const char *disable; /* File to disable conversions */
const char *command; /* Command to do the conversion */
};
struct ftpclass {
const char *classname; /* Current class */
struct ftpconv *conversions; /* List of conversions */
const char *display; /* Files to display upon chdir */
unsigned int maxtimeout; /* Maximum permitted timeout */
int modify; /* Allow dele, mkd, rmd, umask, chmod */
const char *notify; /* Files to notify about upon chdir */
unsigned int timeout; /* Default timeout */
mode_t umask; /* Umask to use */
};

View File

@ -1,4 +1,4 @@
/* $NetBSD: ftpcmd.y,v 1.11 1997/05/23 22:09:48 cjs Exp $ */
/* $NetBSD: ftpcmd.y,v 1.12 1997/06/14 08:43:29 lukem Exp $ */
/*
* Copyright (c) 1985, 1988, 1993, 1994
@ -46,7 +46,7 @@
#if 0
static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
#else
static char rcsid[] = "$NetBSD: ftpcmd.y,v 1.11 1997/05/23 22:09:48 cjs Exp $";
static char rcsid[] = "$NetBSD: ftpcmd.y,v 1.12 1997/06/14 08:43:29 lukem Exp $";
#endif
#endif /* not lint */
@ -80,14 +80,13 @@ extern int logging;
extern int type;
extern int form;
extern int debug;
extern int timeout;
extern int maxtimeout;
extern int pdata;
extern char hostname[], remotehost[];
extern char proctitle[];
extern int usedefault;
extern int transflag;
extern char tmpline[];
extern struct ftpclass curclass;
off_t restart_point;
@ -125,7 +124,7 @@ char *fromname;
%token <s> STRING
%token <i> NUMBER
%type <i> check_login check_login_noguest octal_number byte_size
%type <i> check_login check_modify octal_number byte_size
%type <i> struct_code mode_code type_code form_code
%type <s> pathstring pathname password username
@ -290,7 +289,7 @@ cmd
{
statcmd();
}
| DELE check_login_noguest SP pathname CRLF
| DELE check_modify SP pathname CRLF
{
if ($2 && $4 != NULL)
delete($4);
@ -347,14 +346,14 @@ cmd
{
reply(200, "NOOP command successful.");
}
| MKD check_login_noguest SP pathname CRLF
| MKD check_modify SP pathname CRLF
{
if ($2 && $4 != NULL)
makedir($4);
if ($4 != NULL)
free($4);
}
| RMD check_login_noguest SP pathname CRLF
| RMD check_modify SP pathname CRLF
{
if ($2 && $4 != NULL)
removedir($4);
@ -389,7 +388,7 @@ cmd
reply(200, "Current UMASK is %03o", oldmask);
}
}
| SITE SP UMASK check_login_noguest SP octal_number CRLF
| SITE SP UMASK check_modify SP octal_number CRLF
{
int oldmask;
@ -404,7 +403,7 @@ cmd
}
}
}
| SITE SP CHMOD check_login_noguest SP octal_number SP pathname CRLF
| SITE SP CHMOD check_modify SP octal_number SP pathname CRLF
{
if ($4 && ($8 != NULL)) {
if ($6 > 0777)
@ -422,20 +421,20 @@ cmd
{
reply(200,
"Current IDLE time limit is %d seconds; max %d",
timeout, maxtimeout);
curclass.timeout, curclass.maxtimeout);
}
| SITE SP IDLE SP NUMBER CRLF
{
if ($5 < 30 || $5 > maxtimeout) {
if ($5 < 30 || $5 > curclass.maxtimeout) {
reply(501,
"Maximum IDLE time must be between 30 and %d seconds",
maxtimeout);
"IDLE time limit must be between 30 and %d seconds",
curclass.maxtimeout);
} else {
timeout = $5;
(void) alarm((unsigned) timeout);
curclass.timeout = $5;
(void) alarm(curclass.timeout);
reply(200,
"Maximum IDLE time set to %d seconds",
timeout);
"IDLE time limit set to %d seconds",
curclass.timeout);
}
}
| STOU check_login SP pathname CRLF
@ -730,18 +729,16 @@ check_login
}
}
;
check_login_noguest
check_modify
: /* empty */
{
if (logged_in) {
#ifndef INSECURE_GUEST
if (guest) {
reply(502,
"Guest users may not use this command.");
$$ = 0;
if (curclass.modify) {
$$ = 1;
} else
#endif
$$ = 1;
reply(502,
"No permission to use this command.");
$$ = 0;
} else {
reply(530, "Please login with USER and PASS.");
$$ = 0;
@ -936,10 +933,11 @@ toolong(signo)
{
reply(421,
"Timeout (%d seconds): closing control connection.", timeout);
"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"), timeout);
(pw ? pw -> pw_name : "unknown"), curclass.timeout);
dologout(1);
}
@ -957,7 +955,7 @@ yylex()
case CMD:
(void) signal(SIGALRM, toolong);
(void) alarm((unsigned) timeout);
(void) alarm(curclass.timeout);
if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
reply(221, "You could at least say goodbye.");
dologout(0);

View File

@ -1,4 +1,4 @@
.\" $NetBSD: ftpd.8,v 1.13 1997/05/23 22:09:51 cjs Exp $
.\" $NetBSD: ftpd.8,v 1.14 1997/06/14 08:43:30 lukem Exp $
.\"
.\" Copyright (c) 1985, 1988, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@ -33,9 +33,9 @@
.\"
.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94
.\"
.Dd April 19, 1994
.Dd June 10, 1997
.Dt FTPD 8
.Os BSD 4.2
.Os NetBSD
.Sh NAME
.Nm ftpd
.Nd
@ -44,9 +44,6 @@ Internet File Transfer Protocol server
.Nm
.Op Fl dl
.Op Fl a Ar anondir
.Op Fl T Ar maxtimeout
.Op Fl t Ar timeout
.Op Fl u Ar umask
.Sh DESCRIPTION
.Nm
is the
@ -62,31 +59,19 @@ service specification; see
Available options:
.Bl -tag -width Ds
.It Fl a
Define the directory to which we chroot for anonymous logins.
Define the directory to
.Xr chroot 2
into for anonymous logins.
Default is the home directory for the ftp user.
.It Fl d
Debugging information is written to the syslog using LOG_FTP.
.It Fl l
Each successful and failed
Each successful and failed
.Xr ftp 1
session is logged using syslog with a facility of 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.
.It Fl T
A client may also request a different timeout period;
the maximum period allowed may be set to
.Ar timeout
seconds with the
.Fl T
option.
The default limit is 2 hours.
.It Fl t
The inactivity timeout period is set to
.Ar timeout
seconds (the default is 15 minutes).
.It Fl u
Sets the default umask. Must be in octal form, e.g. "002"
.El
.Pp
The file
@ -99,7 +84,7 @@ If the file
.Pa /etc/ftpwelcome
exists,
.Nm
prints it before issuing the
prints it before issuing the
.Dq ready
message.
If the file
@ -108,29 +93,6 @@ exists,
.Nm
prints it after a successful login.
.Pp
The
.Pa /etc/ftpusers
file is used to determine which users may use ftp.
If the file does not exist, all users are denied access. If it
does exist, each line is a a comment starting with ``#'' or a
glob pattern that uses the same syntax as /bin/sh, optionally
followed by whitespace and ``allow'' or ``deny''.
Each glob pattern is compared in turn against the username
until a match is found. If the word following the matched glob
pattern is ``allow'' the user is granted access; if the word is
anything else, or nothing at all, the user is denied access.
(No further comparisons are attempted after the first successful match.)
If no match is found, the user is granted access.
This syntax is backward-compatable with the old syntax.
.Pp
If a user requests a guest login, the ftp server checks to see that
both ``anonymous'' and ``ftp'' have access, so if you deny all
users by default, you will need to add both ``anonymous allow''
and ``ftp allow'' to
.Pa /etc/ftpusers
in order to allow guest logins.
.Pp
The ftp server currently supports the following ftp requests.
The case of the requests is ignored.
.Bl -column "Request" -offset indent
@ -183,10 +145,10 @@ SITE request.
.Pp
.Bl -column Request -offset indent
.It Sy Request Ta Sy Description
.It UMASK Ta change umask, e.g. ``SITE UMASK 002''
.It IDLE Ta set idle-timer, e.g. ``SITE IDLE 60''
.It CHMOD Ta change mode of a file, e.g. ``SITE CHMOD 755 filename''
.It HELP Ta give help information.
.It CHMOD Ta "change mode of a file, e.g. ``SITE CHMOD 755 filename''"
.It HELP Ta "give help information."
.It IDLE Ta "set idle-timer, e.g. ``SITE IDLE 60''"
.It UMASK Ta "change umask, e.g. ``SITE UMASK 002''"
.El
.Pp
The remaining ftp requests specified in Internet RFC 959
@ -212,9 +174,9 @@ conventions used by
.Xr csh 1 .
This allows users to utilize the metacharacters
.Dq Li \&*?[]{}~ .
.Pp
.Ss User authentication
.Nm
authenticates users according to five rules.
authenticates users according to five rules.
.Pp
.Bl -enum -offset indent
.It
@ -234,9 +196,9 @@ Bellcore.
.It
The login name must be allowed based on the information in
.Pa /etc/ftpusers
(see above).
(see below).
.It
The user must have a standard shell returned by
The user must have a standard shell returned by
.Xr getusershell 3 .
.It
If the user name appears in the file
@ -248,7 +210,7 @@ as for an
or
.Dq ftp
account (see next item). However, the user must still supply a password.
This feature is intended as a compromise between a fully anonymous account
This feature is intended as a compromise between a fully anonymous account
and a fully privileged account. The account should also be set up as for an
anonymous account.
.It
@ -263,21 +225,221 @@ file (user
In this case the user is allowed
to log in by specifying any password (by convention an email address for
the user should be used as the password).
.El
.Pp
In the last case,
.Nm
takes special measures to restrict the client's access privileges.
The server disables the MKD, XMKD, RMD, XRMD, DELE, SITE UMASK, and
SITE CHMOD commands, sets the umask to 0707, and performs a
The server performs a
.Xr chroot 2
to the home directory of the
.Dq ftp
user.
If other restrictions are required (such as disabling of certain
commands and the setting of a specific umask), then appropriate
entries in
.Pa /etc/ftpd.conf
are required.
.El
.Ss /etc/ftpusers
The file
.Pa /etc/ftpusers
is used to determine which users may use ftp.
If the file does not exist, all users are denied access. If it
does exist, each line is a a comment starting with
.Dq #
or a glob pattern that uses the same syntax as /bin/sh,
optionally followed by whitespace and
.Dq allow
or
.Dq deny .
Each glob pattern is compared in turn against the username
until a match is found. If the word following the matched glob
pattern is
.Dq allow
the user is granted access; if the word is
anything else, or nothing at all, the user is denied access.
(No further comparisons are attempted after the first successful match.)
If no match is found, the user is granted access.
This syntax is backward-compatable with the old syntax.
.Pp
If a user requests a guest login, the ftp server checks to see that
both
.Dq anonymous
and
.Dq ftp
have access, so if you deny all users by default, you will need to add both
.Dq "anonymous allow"
and
.Dq "ftp allow"
to
.Pa /etc/ftpusers
in order to allow guest logins.
.Ss /etc/ftpd.conf
The file
.Pa /etc/ftpd.conf
is used to configure various options.
Each line starting with a
.Dq #
is a comment (and ignored), and all other non-blank lines are treated
as configuration directives.
.Pp
Each configuration line may be one of:
.Bl -tag -width 4n
.It Xo Sy conversion Ar class
.Ar suffix Op Ar "type disable command"
.Xc
Define an automatic in-line file conversion.
If a file to retrieve ends in
.Ar suffix ,
and a real file (sans
.Ar suffix )
exists, then the output of
.Ar command
is returned instead of the contents of the file.
.Pp
.Bl -tag -width "disable" -offset indent
.It Ar suffix
The suffix to initiate the conversion.
.It Ar type
A list of valid filetypes for the conversion.
Valid types are:
.Sq f
(file), and
.Sq d
(directory).
.It Ar disable
The name of file that will prevent conversion if it exists.
A filename of
.Pa \&.
will prevent this disabling action.
.It Ar command
The command to run for the conversion.
The first word should be the full path name
of the command, as
.Xr execv 3
is used to execute the command.
The first instance of
.Sq %s
in
.Ar command
is replaced with the requested file (sans
.Ar suffix ) .
.El
.Pp
Conversion directives specified later in the file override earlier
conversions with the same suffix. The order in which conversions is
matched is the reverse of their order in the file (i.e., a LIFO).
.It Sy display Ar class Op Ar file
If
.Ar file
isn't given or
.Ar class
is
.Dq none ,
disable this.
Otherwise, each time the user enters a new directory, check if
.Ar file
exists, and if so, display its contents to the user.
.It Sy maxtimeout Ar class Ar time
Set the maximum timeout period that a client may request,
defaulting to two hours.
This cannot be lesser than 30 seconds, or the value for
.Sy timeout .
Ignored if class is
.Dq none
or
.Ar time
isn't specified.
.It Sy modify Ar class Op Sy off
If class is
.Dq none
or
.Sy off
is given, disable the following commands:
CHMOD, DELE, MKD, RMD, and UMASK.
Otherwise, enable them.
.It Sy notify Ar class Op Ar fileglob
If
.Ar fileglob
isn't given or
.Ar class
is
.Dq none ,
disable this.
Otherwise, each time the user enters a new directory,
notify the user of any files matching
.Ar fileglob .
.It Sy timeout Ar class Ar time
Set the inactivity timeout period.
(the default is fifteen minutes).
This cannot be lesser than 30 seconds, or greater than the value for
.Sy maxtimeout .
Ignored if class is
.Dq none
or
.Ar time
isn't specified.
.It Sy umask Ar class Ar umaskval
Set the umask to
.Ar umaskval .
Ignored if class is
.Dq none
or
.Ar umaskval
isn't specified.
.El
.Pp
In any configuration line,
.Ar class
is one of:
.Bl -tag -width "chroot" -compact -offset indent
.It Sy real
Normal user logins.
.It Sy chroot
Users that have been
.Xr chroot 2 ed.
.It Sy guest
.Dq anonymous
and
.Dq ftp
users.
.It Sy all
Matches any class.
.It Sy none
Matches no class.
.El
.Pp
The following defaults are used:
.Bd -literal -offset indent -compact
display none
maxtimeout all 7200 # 2 hours
modify all
modify guest off
notify none
timeout all 900 # 15 minutes
umask all 027
umask guest 0707
.Ed
.Pp
Directives that appear later in the file override settings by previous
directives. This allows
.Sq wildcard
entries to define defaults, and then have class-specific overrides.
.Pp
The
STAT
command will return the class settings for the current user as defined by
.Pa /etc/ftpd.conf .
.Ss Setting up a restricted ftp subtree
In order that system security is not breached, it is recommended
that the
subtrees for the
.Dq ftp
subtree be constructed with care, following these rules:
and
.Dq chroot
accounts be constructed with care, following these rules
(replace
.Dq ftp
in the following directory names
with the appropriate account name for
.Sq chroot
users):
.Bl -tag -width "~ftp/incoming" -offset indent
.It Pa ~ftp
Make the home directory owned by
@ -303,7 +465,7 @@ and
.Pa group
(see
.Xr group 5 )
must be present for the
must be present for the
.Xr ls
command to be able to produce owner names rather than numbers.
The password field in
@ -321,30 +483,34 @@ and be writable only by them (mode 755 or 775). They should
be owned or writable by ftp or its group.
.It Pa ~ftp/incoming
This directory is where anonymous users place files they upload.
The owners should be the user ``ftp'' and an appropriate group.
The owners should be the user
.Dq ftp
and an appropriate group.
Members of this group will be the only users with access to these
files after they have been uploaded; these should be people who
know how to deal with them appropriately. If you wish anonymous
ftp 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 files to this directory,
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
above.
.El
.Sh FILES
.Bl -tag -width /etc/ftpwelcome -compact
.It Pa /etc/ftpusers
List of unwelcome/restricted users.
.It Pa /etc/ftpchroot
List of normal users who should be chroot'd.
.It Pa /etc/ftpd.conf
Configure file conversions and other settings.
.It Pa /etc/ftpusers
List of unwelcome/restricted users.
.It Pa /etc/ftpwelcome
Welcome notice.
.It Pa /etc/motd
Welcome notice after login.
.It Pa /etc/nologin
Displayed and access refused.
If it exists, displayed and access is refused.
.El
.Sh SEE ALSO
.Xr ftp 1 ,
@ -358,8 +524,21 @@ an effective user id of the logged in user, reverting to
the super-user only when binding addresses to sockets. The
possible security holes have been extensively
scrutinized, but are possibly incomplete.
.Pp
The feedback to the client is inadequate in the case of an
error that occurs during a retrieval that uses a
.Dq conversion
command
(refer to
.Sx /etc/ftpd.conf ) .
.Sh HISTORY
The
.Nm
command appeared in
.Bx 4.2 .
.Pp
The
.Pa /etc/ftpd.conf
functionality was implemented in
.Nx 1.3
by Luke Mewburn, based on work by Simon Burge.

View File

@ -1,4 +1,4 @@
/* $NetBSD: ftpd.c,v 1.23 1997/05/29 10:31:48 lukem Exp $ */
/* $NetBSD: ftpd.c,v 1.24 1997/06/14 08:43:31 lukem Exp $ */
/*
* Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
@ -43,7 +43,7 @@ static char copyright[] =
#if 0
static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
#else
static char rcsid[] = "$NetBSD: ftpd.c,v 1.23 1997/05/29 10:31:48 lukem Exp $";
static char rcsid[] = "$NetBSD: ftpd.c,v 1.24 1997/06/14 08:43:31 lukem Exp $";
#endif
#endif /* not lint */
@ -84,8 +84,8 @@ static char rcsid[] = "$NetBSD: ftpd.c,v 1.23 1997/05/29 10:31:48 lukem Exp $";
#include <time.h>
#include <unistd.h>
#include "pathnames.h"
#include "extern.h"
#include "pathnames.h"
#if __STDC__
#include <stdarg.h>
@ -93,7 +93,7 @@ static char rcsid[] = "$NetBSD: ftpd.c,v 1.23 1997/05/29 10:31:48 lukem Exp $";
#include <varargs.h>
#endif
static char version[] = "Version 6.00";
static char version[] = "Version 7.00";
extern off_t restart_point;
extern char cbuf[];
@ -109,8 +109,6 @@ jmp_buf errcatch, urgcatch;
int logged_in;
struct passwd *pw;
int debug;
int timeout = 900; /* timeout after 15 minutes of inactivity */
int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
int logging;
int guest;
int dochroot;
@ -123,20 +121,15 @@ int pdata = -1; /* for passive mode */
sig_atomic_t transflag;
off_t file_size;
off_t byte_count;
#if !defined(CMASK) || CMASK == 0
#undef CMASK
#define CMASK 027
#endif
#if !defined(GUEST_CMASK)
#define GUEST_CMASK 0707
#endif
int defumask = CMASK; /* default umask value */
char tmpline[7];
char hostname[MAXHOSTNAMELEN];
char remotehost[MAXHOSTNAMELEN];
static char ttyline[20];
char *tty = ttyline; /* for klogin */
static char *anondir = NULL;
static struct timeval lastt;
extern struct ftpclass curclass;
#if defined(KERBEROS)
int notickets = 1;
@ -259,28 +252,11 @@ main(argc, argv, envp)
break;
case 't':
timeout = atoi(optarg);
if (maxtimeout < timeout)
maxtimeout = timeout;
break;
case 'T':
maxtimeout = atoi(optarg);
if (timeout > maxtimeout)
timeout = maxtimeout;
break;
case 'u':
{
long val = 0;
val = strtol(optarg, &optarg, 8);
if (*optarg != '\0' || val < 0)
warnx("bad value for -u");
else
defumask = val;
warnx("-%c has be deprecated in favour of ftpd.conf",
ch);
break;
}
default:
warnx("unknown flag -%c ignored", optopt);
@ -671,13 +647,23 @@ skip:
logged_in = 1;
dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);
/* parse ftpd.conf, setting up various parameters */
if (guest)
parse_conf(CLASS_GUEST);
else if (dochroot)
parse_conf(CLASS_CHROOT);
else
parse_conf(CLASS_REAL);
if (guest) {
/*
* We MUST do a chdir() after the chroot. Otherwise
* the old current directory will be accessible as "."
* outside the new root!
*/
if (chroot(anondir ? anondir : pw->pw_dir) < 0 || chdir("/") < 0) {
if (chroot(anondir ? anondir : pw->pw_dir) < 0 ||
chdir("/") < 0) {
reply(550, "Can't set guest privileges.");
goto bad;
}
@ -713,6 +699,7 @@ skip:
(void) fflush(stdout);
(void) fclose(fd);
}
show_chdir_messages(230);
if (guest) {
reply(230, "Guest login ok, access restrictions apply.");
#ifdef HASSETPROCTITLE
@ -736,12 +723,7 @@ skip:
syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
remotehost, pw->pw_name);
}
#ifndef INSECURE_GUEST
if (guest)
(void) umask(GUEST_CMASK);
else
#endif
(void) umask(defumask);
(void) umask(curclass.umask);
return;
bad:
/* Forget all about it... */
@ -755,22 +737,26 @@ retrieve(cmd, name)
FILE *fin, *dout;
struct stat st;
int (*closefunc) __P((FILE *));
int log;
log = (cmd == 0);
if (cmd == 0) {
fin = fopen(name, "r"), closefunc = fclose;
st.st_size = 0;
} else {
if (fin == NULL)
cmd = do_conversion(name);
}
if (cmd) {
char line[BUFSIZ];
(void) sprintf(line, cmd, name), name = line;
fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
fin = ftpd_popen(line, "r", 1), closefunc = ftpd_pclose;
st.st_size = -1;
st.st_blksize = BUFSIZ;
}
if (fin == NULL) {
if (errno != 0) {
perror_reply(550, name);
if (cmd == 0) {
if (log) {
LOGCMD("get", name);
}
}
@ -809,7 +795,7 @@ retrieve(cmd, name)
data = -1;
pdata = -1;
done:
if (cmd == 0)
if (log)
LOGBYTES("get", name, byte_count);
(*closefunc)(fin);
}
@ -1012,14 +998,15 @@ send_data(instr, outstr, blksize)
FILE *instr, *outstr;
off_t blksize;
{
int c, cnt, filefd, netfd;
char *buf;
int c, cnt, filefd, netfd;
char *buf;
transflag++;
if (setjmp(urgcatch)) {
transflag = 0;
return;
}
switch (type) {
case TYPE_A:
@ -1088,15 +1075,16 @@ static int
receive_data(instr, outstr)
FILE *instr, *outstr;
{
int c;
int cnt, bare_lfs = 0;
char buf[BUFSIZ];
int c, cnt, bare_lfs;
char buf[BUFSIZ];
bare_lfs = 0;
transflag++;
if (setjmp(urgcatch)) {
transflag = 0;
return (-1);
}
switch (type) {
case TYPE_I:
@ -1172,7 +1160,7 @@ statfilecmd(filename)
char line[LINE_MAX];
(void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
fin = ftpd_popen(line, "r");
fin = ftpd_popen(line, "r", 0);
lreply(211, "status of %s:", filename);
while ((c = getc(fin)) != EOF) {
if (c == '\n') {
@ -1201,22 +1189,23 @@ statcmd()
struct sockaddr_in *sin;
u_char *a, *p;
lreply(211, "%s FTP server status:", hostname, version);
printf(" %s\r\n", version);
printf(" Connected to %s", remotehost);
if (!isdigit(remotehost[0]))
printf(" (%s)", inet_ntoa(his_addr.sin_addr));
printf("\r\n");
lreply(211, "%s FTP server status:", hostname);
lreply(211, "%s", version);
if (isdigit(remotehost[0]))
lreply(211, "Connected to %s", remotehost);
else
lreply(211, "Connected to %s (%s)", remotehost,
inet_ntoa(his_addr.sin_addr));
if (logged_in) {
if (guest)
printf(" Logged in anonymously\r\n");
lreply(211, "Logged in anonymously");
else
printf(" Logged in as %s\r\n", pw->pw_name);
lreply(211, "Logged in as %s", pw->pw_name);
} else if (askpasswd)
printf(" Waiting for password\r\n");
lreply(211, "Waiting for password");
else
printf(" Waiting for user name\r\n");
printf(" TYPE: %s", typenames[type]);
lreply(211, "Waiting for user name");
printf("211- TYPE: %s", typenames[type]);
if (type == TYPE_A || type == TYPE_E)
printf(", FORM: %s", formnames[form]);
if (type == TYPE_L)
@ -1228,13 +1217,13 @@ statcmd()
printf("; STRUcture: %s; transfer MODE: %s\r\n",
strunames[stru], modenames[mode]);
if (data != -1)
printf(" Data connection open\r\n");
lreply(211, "Data connection open");
else if (pdata != -1) {
printf(" in Passive mode");
printf("211- in Passive mode");
sin = &pasv_addr;
goto printaddr;
} else if (usedefault == 0) {
printf(" PORT");
printf("211- PORT");
sin = &data_dest;
printaddr:
a = (u_char *) &sin->sin_addr;
@ -1244,7 +1233,32 @@ printaddr:
UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
#undef UC
} else
printf(" No data connection\r\n");
lreply(211, "No data connection");
if (logged_in) {
struct ftpconv *cp;
lreply(211, "");
lreply(211, "Class: %s", curclass.classname);
if (curclass.display)
lreply(211, "Display file: %s", curclass.display);
if (curclass.notify)
lreply(211, "Notify fileglob: %s", curclass.notify);
lreply(211, "Idle timeout: %d, maximum timeout: %d",
curclass.timeout, curclass.maxtimeout);
lreply(211, "dele, mkd, rmd, umask, chmod: %sabled",
curclass.modify ? "en" : "dis");
lreply(211, "Umask: %.04o", curclass.umask);
for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
if (cp->suffix == NULL || cp->types == NULL ||
cp->command == NULL)
continue;
lreply(211,
"Conversion: %s [%s] disable: %s, command: %s",
cp->suffix, cp->types, cp->disable, cp->command);
}
}
reply(211, "End of status");
}
@ -1372,8 +1386,10 @@ cwd(path)
if (chdir(path) < 0)
perror_reply(550, path);
else
else {
show_chdir_messages(250);
ack("CWD");
}
}
void

View File

@ -1,4 +1,4 @@
/* $NetBSD: pathnames.h,v 1.5 1995/04/11 02:44:59 cgd Exp $ */
/* $NetBSD: pathnames.h,v 1.6 1997/06/14 08:43:32 lukem Exp $ */
/*
* Copyright (c) 1989, 1993
@ -37,7 +37,8 @@
#include <paths.h>
#define _PATH_FTPUSERS "/etc/ftpusers"
#define _PATH_FTPCHROOT "/etc/ftpchroot"
#define _PATH_FTPWELCOME "/etc/ftpwelcome"
#define _PATH_FTPDCONF "/etc/ftpd.conf"
#define _PATH_FTPLOGINMESG "/etc/motd"
#define _PATH_FTPUSERS "/etc/ftpusers"
#define _PATH_FTPWELCOME "/etc/ftpwelcome"

View File

@ -1,4 +1,4 @@
/* $NetBSD: popen.c,v 1.6 1997/04/27 03:21:43 lukem Exp $ */
/* $NetBSD: popen.c,v 1.7 1997/06/14 08:43:33 lukem Exp $ */
/*
* Copyright (c) 1988, 1993, 1994
@ -41,7 +41,7 @@
#if 0
static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94";
#else
static char rcsid[] = "$NetBSD: popen.c,v 1.6 1997/04/27 03:21:43 lukem Exp $";
static char rcsid[] = "$NetBSD: popen.c,v 1.7 1997/06/14 08:43:33 lukem Exp $";
#endif
#endif /* not lint */
@ -67,8 +67,9 @@ static int *pids;
static int fds;
FILE *
ftpd_popen(program, type)
ftpd_popen(program, type, nostderr)
char *program, *type;
int nostderr;
{
char *cp;
FILE *iop;
@ -122,7 +123,10 @@ ftpd_popen(program, type)
dup2(pdes[1], STDOUT_FILENO);
(void)close(pdes[1]);
}
dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */
if (nostderr)
(void)close(STDERR_FILENO);
else /* stderr too! */
dup2(STDOUT_FILENO, STDERR_FILENO);
(void)close(pdes[0]);
} else {
if (pdes[0] != STDIN_FILENO) {