summaryrefslogtreecommitdiffstats
path: root/libexec/ftpd
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/ftpd')
-rw-r--r--libexec/ftpd/Makefile28
-rw-r--r--libexec/ftpd/extern.h10
-rw-r--r--libexec/ftpd/ftpcmd.y72
-rw-r--r--libexec/ftpd/ftpd.8189
-rw-r--r--libexec/ftpd/ftpd.c818
-rw-r--r--libexec/ftpd/logwtmp.c18
-rw-r--r--libexec/ftpd/pathnames.h8
-rw-r--r--libexec/ftpd/popen.c36
-rw-r--r--libexec/ftpd/skey-stuff.c29
9 files changed, 1068 insertions, 140 deletions
diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile
index 946aab7..49e98d5 100644
--- a/libexec/ftpd/Makefile
+++ b/libexec/ftpd/Makefile
@@ -1,9 +1,31 @@
# @(#)Makefile 8.2 (Berkeley) 4/4/94
+# $Id: Makefile,v 1.21 1997/04/26 12:12:10 davidn Exp $
PROG= ftpd
-CFLAGS+=-DSETPROCTITLE
-SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c
-MAN8= ftpd.0
+MAN8= ftpd.8
+SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c skey-stuff.c
+
+CFLAGS+=-DSETPROCTITLE -DSKEY -DLOGIN_CAP -DVIRTUAL_HOSTING -Wall
+
+LDADD= -lskey -lmd -lcrypt -lutil
+DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} ${LIBUTIL}
+
CLEANFILES+=ftpcmd.c y.tab.h
+.ifdef FTPD_INTERNAL_LS
+LSDIR= ../../bin/ls
+.PATH: ${.CURDIR}/${LSDIR}
+SRCS+= ls.c cmp.c print.c stat_flags.c util.c
+CFLAGS+=-DINTERNAL_LS -Dmain=ls_main -I${.CURDIR}/${LSDIR}
+.endif
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && defined(MAKE_EBONES)
+.PATH: ${.CURDIR}/../../usr.bin/login
+SRCS+= klogin.c
+LDADD+= -lkrb -ldes
+DPADD+= ${LIBKRB} ${LIBDES}
+CFLAGS+=-DKERBEROS
+DISTRIBUTION= krb
+.endif
+
.include <bsd.prog.mk>
diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h
index e3336b5..616da79 100644
--- a/libexec/ftpd/extern.h
+++ b/libexec/ftpd/extern.h
@@ -31,6 +31,7 @@
* SUCH DAMAGE.
*
* @(#)extern.h 8.2 (Berkeley) 4/4/94
+ * $Id: extern.h,v 1.8 1997/02/22 14:21:26 peter Exp $
*/
void blkfree __P((char **));
@@ -56,10 +57,19 @@ char *renamefrom __P((char *));
void reply __P((int, const char *, ...));
void retrieve __P((char *, char *));
void send_file_list __P((char *));
+#ifdef OLD_SETPROCTITLE
void setproctitle __P((const char *, ...));
+#endif
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 *));
+int yyparse __P((void));
+#if defined(SKEY) && defined(_PWD_H_) /* XXX evil */
+char *skey_challenge __P((char *, struct passwd *, int));
+#endif
+#if defined(INTERNAL_LS)
+int ls_main __P((int, char **));
+#endif
diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y
index 6ec3d25..659081b 100644
--- a/libexec/ftpd/ftpcmd.y
+++ b/libexec/ftpd/ftpcmd.y
@@ -31,6 +31,7 @@
* SUCH DAMAGE.
*
* @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
+ * $Id: ftpcmd.y,v 1.10 1997/02/22 14:21:27 peter Exp $
*/
/*
@@ -63,13 +64,15 @@ static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
#include <syslog.h>
#include <time.h>
#include <unistd.h>
+#include <libutil.h>
#include "extern.h"
-extern struct sockaddr_in data_dest;
+extern struct sockaddr_in data_dest, his_addr;
extern int logged_in;
extern struct passwd *pw;
extern int guest;
+extern int paranoid;
extern int logging;
extern int type;
extern int form;
@@ -77,7 +80,8 @@ extern int debug;
extern int timeout;
extern int maxtimeout;
extern int pdata;
-extern char hostname[], remotehost[];
+extern char *hostname;
+extern char remotehost[];
extern char proctitle[];
extern int usedefault;
extern int transflag;
@@ -148,18 +152,32 @@ cmd
pass($3);
free($3);
}
- | PORT SP host_port CRLF
- {
- usedefault = 0;
- if (pdata >= 0) {
- (void) close(pdata);
- pdata = -1;
+ | PORT check_login SP host_port CRLF
+ {
+ if ($2) {
+ if (paranoid &&
+ ((ntohs(data_dest.sin_port) <
+ IPPORT_RESERVED) ||
+ memcmp(&data_dest.sin_addr,
+ &his_addr.sin_addr,
+ sizeof(data_dest.sin_addr)))) {
+ usedefault = 1;
+ reply(500,
+ "Illegal PORT range rejected.");
+ } else {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "PORT command successful.");
+ }
}
- reply(200, "PORT command successful.");
}
- | PASV CRLF
+ | PASV check_login CRLF
{
- passive();
+ if ($2)
+ passive();
}
| TYPE SP type_code CRLF
{
@@ -291,16 +309,18 @@ cmd
if ($4 != NULL)
free($4);
}
- | RNTO SP pathname CRLF
+ | RNTO check_login SP pathname CRLF
{
- if (fromname) {
- renamecmd(fromname, $3);
- free(fromname);
- fromname = (char *) 0;
- } else {
- reply(503, "Bad sequence of commands.");
+ if ($2) {
+ if (fromname) {
+ renamecmd(fromname, $4);
+ free(fromname);
+ fromname = (char *) 0;
+ } else {
+ reply(503, "Bad sequence of commands.");
+ }
}
- free($3);
+ free($4);
}
| ABOR CRLF
{
@@ -490,8 +510,9 @@ cmd
struct tm *t;
t = gmtime(&stbuf.st_mtime);
reply(213,
- "19%02d%02d%02d%02d%02d%02d",
- t->tm_year, t->tm_mon+1, t->tm_mday,
+ "%04d%02d%02d%02d%02d%02d",
+ 1900 + t->tm_year,
+ t->tm_mon+1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
}
}
@@ -552,11 +573,12 @@ host_port
{
char *a, *p;
- a = (char *)&data_dest.sin_addr;
- a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
+ data_dest.sin_len = sizeof(struct sockaddr_in);
+ data_dest.sin_family = AF_INET;
p = (char *)&data_dest.sin_port;
p[0] = $9; p[1] = $11;
- data_dest.sin_family = AF_INET;
+ a = (char *)&data_dest.sin_addr;
+ a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
}
;
@@ -976,7 +998,7 @@ yylex()
upper(cp);
p = lookup(sitetab, cp);
cbuf[cpos] = c;
- if (p != 0) {
+ if (guest == 0 && p != 0) {
if (p->implemented == 0) {
state = CMD;
nack(p->name);
diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8
index eb93c38..b3fa6bd 100644
--- a/libexec/ftpd/ftpd.8
+++ b/libexec/ftpd/ftpd.8
@@ -29,9 +29,10 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" @(#)ftpd.8 8.3 (Berkeley) 6/1/94
+.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94
+.\" $Id: ftpd.8,v 1.17 1997/04/27 08:29:21 davidn Exp $
.\"
-.Dd June 1, 1994
+.Dd April 19, 1994
.Dt FTPD 8
.Os BSD 4.2
.Sh NAME
@@ -41,8 +42,14 @@ Internet File Transfer Protocol server
.Sh SYNOPSIS
.Nm ftpd
.Op Fl dl
+.Op Fl D
+.Op Fl R
+.Op Fl S
+.Op Fl U
.Op Fl T Ar maxtimeout
.Op Fl t Ar timeout
+.Op Fl a Ar address
+.Op Fl p Ar file
.Sh DESCRIPTION
.Nm Ftpd
is the
@@ -65,7 +72,47 @@ Each successful and failed
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.
+their filename arguments are also logged. Note: LOG_FTP messages
+are not displayed by
+.Xr syslogd 8
+by default, and may have to be enabled in
+.Xr syslogd 8 Ns 's
+configuration file.
+.It Fl D
+With this option set,
+.Nm ftpd
+will detach and become a daemon, accepting connections on the FTP port and
+forking children processes to handle them. This is lower overhead than
+starting
+.Nm ftpd
+from
+.Xr inetd 8
+and is thus useful on busy servers to reduce load.
+.It Fl R
+With this option set,
+.Nm ftpd
+will revert to historical behavior with regard to security checks on
+user operations and restrictions on PORT requests.
+Currently,
+.Nm ftpd
+will only honor PORT commands directed to unprivileged ports on the
+remote user's host (which violates the FTP protocol specification but
+closes some security holes).
+.
+.It Fl S
+With this option set,
+.Nm ftpd
+logs all anonymous transfers to the file
+.Pa /var/log/ftpd
+when this file exists.
+.
+.It Fl U
+In previous versions of
+.Nm ftpd ,
+when a passive mode client requested a data connection to the server,
+the server would use data ports in the range 1024..4999. Now, by default,
+the server will use data ports in the range 40000..44999. Specifying this
+option will revert to the old behavior.
.It Fl T
A client may also request a different timeout period;
the maximum period allowed may be set to
@@ -78,6 +125,18 @@ The default limit is 2 hours.
The inactivity timeout period is set to
.Ar timeout
seconds (the default is 15 minutes).
+.It Fl a
+When
+.Fl D
+is specified, accept connections only on the specified
+.Ar address .
+.It Fl p
+When
+.Fl D
+is specified, write the daemon's process ID to
+.Ar file .
+.It Fl A
+Allow only anonymous ftp access
.El
.Pp
The file
@@ -94,7 +153,7 @@ prints it before issuing the
.Dq ready
message.
If the file
-.Pa /etc/motd
+.Pa /etc/ftpmotd
exists,
.Nm
prints it after a successful login.
@@ -182,22 +241,55 @@ This allows users to utilize the metacharacters
.Dq Li \&*?[]{}~ .
.Pp
.Nm Ftpd
-authenticates users according to three rules.
+authenticates users according to five rules.
.Pp
.Bl -enum -offset indent
.It
-The login name must be in the password data base,
-.Pa /etc/passwd ,
+The login name must be in the password data base
and not have a null password.
In this case a password must be provided by the client before any
file operations may be performed.
+If the user has an S/Key key, the response from a successful USER
+command will include an S/Key challenge. The client may choose to respond
+with a PASS command giving either a standard password or an S/Key
+one-time password. The server will automatically determine which type of
+password it has been given and attempt to authenticate accordingly. See
+.Xr key 1
+for more information on S/Key authentication. S/Key is a Trademark of
+Bellcore.
.It
The login name must not appear in the file
.Pa /etc/ftpusers .
.It
+The login name must not be a member of a group specified in the file
+.Pa /etc/ftpusers .
+Entries in this file interpreted as group names are prefixed by an "at"
+.Ql \&@
+sign.
+.It
The user must have a standard shell returned by
.Xr getusershell 3 .
.It
+If the user name appears in the file
+.Pa /etc/ftpchroot ,
+or the user is a member of a group with a group entry in this file,
+i.e. one prefixed with
+.Ql \&@ ,
+the session's root will be changed to the user's login directory by
+.Xr chroot 2
+as for an
+.Dq anonymous
+or
+.Dq ftp
+account (see next item).
+This facility may also be triggered by enabling the boolean "ftp-chroot"
+capability in
+.Xr login.conf 5 .
+However, the user must still supply a password.
+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
If the user name is
.Dq anonymous
or
@@ -209,6 +301,9 @@ 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).
+When the
+.Fl S
+option is set, all transfers are logged as well.
.El
.Pp
In the last case,
@@ -228,7 +323,6 @@ subtree be constructed with care, following these rules:
Make the home directory owned by
.Dq root
and unwritable by anyone.
-.ne 1i
.It Pa ~ftp/bin
Make this directory owned by
.Dq root
@@ -241,8 +335,8 @@ This program should be mode 111.
Make this directory owned by
.Dq root
and unwritable by anyone (mode 555).
-The files
-.Xr passwd 5
+The files pwd.db (see
+.Xr passwd 5 )
and
.Xr group 5
must be present for the
@@ -252,7 +346,7 @@ The password field in
.Xr passwd
is not used, and should not contain real passwords.
The file
-.Pa motd ,
+.Pa ftpmotd ,
if present, will be printed after a successful login.
These files should be mode 444.
.It Pa ~ftp/pub
@@ -262,20 +356,91 @@ Guests
can then place files which are to be accessible via the anonymous
account in this directory.
.El
+.Pp
+If the system has multiple IP addresses,
+.Nm ftpd
+supports the idea of virtual hosts, which provides the ability to
+define multiple anonymous ftp areas, each one allocated to a different
+internet address.
+The file
+.Pa /etc/ftphosts
+contains information pertaining to each of the virtual hosts.
+Each host is defined on its own line which contains a number of
+fields separated by whitespace:
+.Bl -tag -offset indent -width hostname
+.It hostname
+Contains the hostname or IP address of the virtual host.
+.It user
+Contains a user record in the system password file.
+As with normal anonymous ftp, this user's access uid, gid and group
+memberships determine file access to the anonymous ftp area.
+The anonymous ftp area (to which any user is chrooted on login)
+is determined by the home directory defined for the account.
+User id and group for any ftp account may be the same as for the
+standard ftp user.
+.It statfile
+File to which all file transfers are logged, which
+defaults to
+.Pa /var/log/ftpd .
+.It welcome
+This file is the welcome message displayed before the server ready
+prompt.
+It defaults to
+.Pa /etc/ftpwelcome .
+.It motd
+This file is displayed after the user logs in.
+It defaults to
+.Pa /etc/ftpmotd .
+.El
+.Pp
+Defining a virtual host for the primary IP address or hostname
+changes the default for ftp logins to that address.
+The 'user', 'statfile', 'welcome' and 'motd' fields may be left
+blank, or a single hypen '-' used to indicate that the default
+value is to be used.
+.Pp
+As with any anonymous login configuration, due care must be given
+to setup and maintenance to guard against security related problems.
+.Pp
+If compiled with the
+.Em INTERNAL_LS
+option,
+.Nm ftpd
+will have internal support for handling remote requests to list
+files, and will not execute
+.Pa /bin/ls
+in either a chrooted or non-chrooted environment.
+In this case, the
+.Pa ~/bin/ls
+executable need not be placed into the chrooted tree, nor need the
+.Pa ~/bin
+directory exist.
+This support may be added by making ftpd with the
+.Em FTP_INTERNAL_LS
+variable set either in
+.Pa /etc/make.conf
+or in the shell's environment.
.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/ftpwelcome
Welcome notice.
-.It Pa /etc/motd
+.It Pa /etc/ftpmotd
Welcome notice after login.
.It Pa /etc/nologin
Displayed and access refused.
+.It Pa /var/log/ftpd
+Log file for anonymous transfers.
.El
.Sh SEE ALSO
.Xr ftp 1 ,
+.Xr key 1 ,
.Xr getusershell 3 ,
+.Xr login.conf 5 ,
+.Xr inetd 8 ,
.Xr syslogd 8
.Sh BUGS
The server must run as the super-user
diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c
index 875ec62..37407a3 100644
--- a/libexec/ftpd/ftpd.c
+++ b/libexec/ftpd/ftpd.c
@@ -29,17 +29,23 @@
* 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.
+ *
+ * $Id: ftpd.c,v 1.40 1997/05/21 23:24:41 danny Exp $
*/
+#if 0
#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
+#endif
+#if 0
#ifndef lint
-static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
+static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94";
#endif /* not lint */
+#endif
/*
* FTP server.
@@ -49,10 +55,12 @@ static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/wait.h>
+#include <sys/mman.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
+#include <netinet/tcp.h>
#define FTP_NAMES
#include <arpa/ftp.h>
@@ -68,6 +76,7 @@ static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
#include <limits.h>
#include <netdb.h>
#include <pwd.h>
+#include <grp.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
@@ -76,6 +85,14 @@ static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
#include <syslog.h>
#include <time.h>
#include <unistd.h>
+#include <libutil.h>
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+
+#ifdef SKEY
+#include <skey.h>
+#endif
#include "pathnames.h"
#include "extern.h"
@@ -86,17 +103,24 @@ static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
#include <varargs.h>
#endif
+#ifdef INTERNAL_LS
+static char version[] = "Version 6.00LS";
+#undef main
+#else
static char version[] = "Version 6.00";
+#endif
extern off_t restart_point;
extern char cbuf[];
+struct sockaddr_in server_addr;
struct sockaddr_in ctrl_addr;
struct sockaddr_in data_source;
struct sockaddr_in data_dest;
struct sockaddr_in his_addr;
struct sockaddr_in pasv_addr;
+int daemon_mode;
int data;
jmp_buf errcatch, urgcatch;
int logged_in;
@@ -105,7 +129,13 @@ 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 restricted_data_ports = 1;
+int paranoid = 1; /* be extra careful about security */
+int anon_only = 0; /* Only anonymous ftp allowed */
int guest;
+int dochroot;
+int stats;
+int statfd = -1;
int type;
int form;
int stru; /* avoid C keyword */
@@ -121,8 +151,39 @@ off_t byte_count;
#endif
int defumask = CMASK; /* default umask value */
char tmpline[7];
-char hostname[MAXHOSTNAMELEN];
+char *hostname;
+#ifdef VIRTUAL_HOSTING
+char *ftpuser;
+
+static struct ftphost {
+ struct ftphost *next;
+ struct in_addr hostaddr;
+ char *hostname;
+ char *anonuser;
+ char *statfile;
+ char *welcome;
+ char *loginmsg;
+} *thishost, *firsthost;
+
+#endif
char remotehost[MAXHOSTNAMELEN];
+char *ident = NULL;
+
+static char ttyline[20];
+char *tty = ttyline; /* for klogin */
+
+#ifdef KERBEROS
+int klogin __P((struct passwd *, char *, char *, char *));
+#endif
+
+struct in_addr bind_address;
+char *pid_file = NULL;
+
+#if defined(KERBEROS)
+int notickets = 1;
+int noticketsdontcomplain = 1;
+char *krbtkfile_env = NULL;
+#endif
/*
* Timeout intervals for retrying connections
@@ -136,11 +197,18 @@ int swaitmax = SWAITMAX;
int swaitint = SWAITINT;
#ifdef SETPROCTITLE
+#ifdef OLD_SETPROCTITLE
char **Argv = NULL; /* pointer to argument vector */
char *LastArgv = NULL; /* end of argv */
+#endif /* OLD_SETPROCTITLE */
char proctitle[LINE_MAX]; /* initial part of title */
#endif /* SETPROCTITLE */
+#ifdef SKEY
+int pwok = 0;
+char addr_string[20]; /* XXX */
+#endif
+
#define LOGCMD(cmd, file) \
if (logging > 1) \
syslog(LOG_INFO,"%s %s%s", cmd, \
@@ -160,9 +228,13 @@ char proctitle[LINE_MAX]; /* initial part of title */
cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
}
+#ifdef VIRTUAL_HOSTING
+static void inithosts __P((void));
+static void selecthost __P((struct in_addr *));
+#endif
static void ack __P((char *));
static void myoob __P((int));
-static int checkuser __P((char *));
+static int checkuser __P((char *, char *));
static FILE *dataconn __P((char *, off_t, char *));
static void dolog __P((struct sockaddr_in *));
static char *curdir __P((void));
@@ -171,10 +243,12 @@ static FILE *getdatasock __P((char *));
static char *gunique __P((char *));
static void lostconn __P((int));
static int receive_data __P((FILE *, FILE *));
-static void send_data __P((FILE *, FILE *, off_t));
+static void send_data __P((FILE *, FILE *, off_t, off_t, int));
static struct passwd *
sgetpwnam __P((char *));
static char *sgetsave __P((char *));
+static void reapchild __P((int));
+static void logxfer __P((char *, long, long));
static char *
curdir()
@@ -199,29 +273,9 @@ main(argc, argv, envp)
char *cp, line[LINE_MAX];
FILE *fd;
- /*
- * 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);
- addrlen = sizeof(his_addr);
- if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
- syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
- exit(1);
- }
- addrlen = sizeof(ctrl_addr);
- if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
- syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
- exit(1);
- }
-#ifdef IP_TOS
- tos = IPTOS_LOWDELAY;
- if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
- syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
-#endif
- data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
- debug = 0;
-#ifdef SETPROCTITLE
+ tzset(); /* in case no timezone database in ~ftp */
+
+#ifdef OLD_SETPROCTITLE
/*
* Save start and extent of argv for setproctitle.
*/
@@ -229,22 +283,30 @@ main(argc, argv, envp)
while (*envp)
envp++;
LastArgv = envp[-1] + strlen(envp[-1]);
-#endif /* SETPROCTITLE */
+#endif /* OLD_SETPROCTITLE */
+
- while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) {
+ bind_address.s_addr = htonl(INADDR_ANY);
+ while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:")) != -1) {
switch (ch) {
+ case 'D':
+ daemon_mode++;
+ break;
+
case 'd':
- debug = 1;
+ debug++;
break;
case 'l':
logging++; /* > 1 == extra logging */
break;
- case 't':
- timeout = atoi(optarg);
- if (maxtimeout < timeout)
- maxtimeout = timeout;
+ case 'R':
+ paranoid = 0;
+ break;
+
+ case 'S':
+ stats++;
break;
case 'T':
@@ -253,6 +315,25 @@ main(argc, argv, envp)
timeout = maxtimeout;
break;
+ case 't':
+ timeout = atoi(optarg);
+ if (maxtimeout < timeout)
+ maxtimeout = timeout;
+ break;
+
+ case 'U':
+ restricted_data_ports = 0;
+ break;
+
+ case 'a':
+ if (!inet_aton(optarg, &bind_address))
+ errx(1, "invalid address for -a");
+ break;
+
+ case 'p':
+ pid_file = optarg;
+ break;
+
case 'u':
{
long val = 0;
@@ -264,6 +345,9 @@ main(argc, argv, envp)
defumask = val;
break;
}
+ case 'A':
+ anon_only = 1;
+ break;
case 'v':
debug = 1;
@@ -274,12 +358,133 @@ main(argc, argv, envp)
break;
}
}
+
+#ifdef VIRTUAL_HOSTING
+ inithosts();
+#endif
(void) freopen(_PATH_DEVNULL, "w", stderr);
- (void) signal(SIGPIPE, lostconn);
+
+ /*
+ * 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);
+
+ if (daemon_mode) {
+ int ctl_sock, fd;
+ struct servent *sv;
+
+ /*
+ * Detach from parent.
+ */
+ if (daemon(1, 1) < 0) {
+ syslog(LOG_ERR, "failed to become a daemon");
+ exit(1);
+ }
+ (void) signal(SIGCHLD, reapchild);
+ /*
+ * Get port number for ftp/tcp.
+ */
+ sv = getservbyname("ftp", "tcp");
+ if (sv == NULL) {
+ syslog(LOG_ERR, "getservbyname for ftp failed");
+ exit(1);
+ }
+ /*
+ * Open a socket, bind it to the FTP port, and start
+ * listening.
+ */
+ ctl_sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (ctl_sock < 0) {
+ syslog(LOG_ERR, "control socket: %m");
+ exit(1);
+ }
+ if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "control setsockopt: %m");;
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_addr = bind_address;
+ server_addr.sin_port = sv->s_port;
+ if (bind(ctl_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) {
+ syslog(LOG_ERR, "control bind: %m");
+ exit(1);
+ }
+ if (listen(ctl_sock, 32) < 0) {
+ syslog(LOG_ERR, "control listen: %m");
+ exit(1);
+ }
+ /*
+ * Atomically write process ID
+ */
+ if (pid_file)
+ {
+ int fd;
+ char buf[20];
+
+ fd = open(pid_file, O_CREAT | O_WRONLY | O_TRUNC
+ | O_NONBLOCK | O_EXLOCK, 0644);
+ if (fd < 0)
+ if (errno == EAGAIN)
+ errx(1, "%s: file locked", pid_file);
+ else
+ err(1, "%s", pid_file);
+ snprintf(buf, sizeof(buf),
+ "%lu\n", (unsigned long) getpid());
+ if (write(fd, buf, strlen(buf)) < 0)
+ err(1, "%s: write", pid_file);
+ /* Leave the pid file open and locked */
+ }
+ /*
+ * Loop forever accepting connection requests and forking off
+ * children to handle them.
+ */
+ while (1) {
+ addrlen = sizeof(his_addr);
+ fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen);
+ if (fork() == 0) {
+ /* child */
+ (void) dup2(fd, 0);
+ (void) dup2(fd, 1);
+ close(ctl_sock);
+ break;
+ }
+ close(fd);
+ }
+ } else {
+ addrlen = sizeof(his_addr);
+ if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
+ exit(1);
+ }
+ }
+
(void) signal(SIGCHLD, SIG_IGN);
+ (void) signal(SIGPIPE, lostconn);
if ((int)signal(SIGURG, myoob) < 0)
syslog(LOG_ERR, "signal: %m");
+#ifdef SKEY
+ strncpy(addr_string, inet_ntoa(his_addr.sin_addr), sizeof(addr_string));
+#endif
+ addrlen = sizeof(ctrl_addr);
+ if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
+ exit(1);
+ }
+#ifdef VIRTUAL_HOSTING
+ /* select our identity from virtual host table */
+ selecthost(&ctrl_addr.sin_addr);
+#endif
+#ifdef IP_TOS
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
+ data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
+
+ /* set this here so klogin can use it... */
+ (void)sprintf(ttyline, "ftp%d", getpid());
+
/* Try to handle urgent data inline */
#ifdef SO_OOBINLINE
if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
@@ -313,7 +518,11 @@ main(argc, argv, envp)
reply(530, "System not available.");
exit(0);
}
+#ifdef VIRTUAL_HOSTING
+ if ((fd = fopen(thishost->welcome, "r")) != NULL) {
+#else
if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
+#endif
while (fgets(line, sizeof(line), fd) != NULL) {
if ((cp = strchr(line, '\n')) != NULL)
*cp = '\0';
@@ -323,7 +532,11 @@ main(argc, argv, envp)
(void) fclose(fd);
/* reply(220,) must follow */
}
- (void) gethostname(hostname, sizeof(hostname));
+#ifndef VIRTUAL_HOSTING
+ if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
+ fatal("Ran out of memory.");
+ (void) gethostname(hostname, MAXHOSTNAMELEN);
+#endif
reply(220, "%s FTP server (%s) ready.", hostname, version);
(void) setjmp(errcatch);
for (;;)
@@ -341,7 +554,146 @@ lostconn(signo)
dologout(-1);
}
-static char ttyline[20];
+#ifdef VIRTUAL_HOSTING
+/*
+ * read in virtual host tables (if they exist)
+ */
+
+static void
+inithosts()
+{
+ FILE *fp;
+ char *cp;
+ struct hostent *hp;
+ struct ftphost *hrp, *lhrp;
+ char line[1024];
+
+ /*
+ * Fill in the default host information
+ */
+ if (gethostname(line, sizeof(line)) < 0)
+ line[0] = '\0';
+ if ((hrp = malloc(sizeof(struct ftphost))) == NULL ||
+ (hrp->hostname = strdup(line)) == NULL)
+ fatal("Ran out of memory.");
+ memset(&hrp->hostaddr, 0, sizeof hrp->hostaddr);
+ if ((hp = gethostbyname(hrp->hostname)) != NULL)
+ (void) memcpy(&hrp->hostaddr,
+ hp->h_addr_list[0],
+ sizeof(hrp->hostaddr));
+ hrp->statfile = _PATH_FTPDSTATFILE;
+ hrp->welcome = _PATH_FTPWELCOME;
+ hrp->loginmsg = _PATH_FTPLOGINMESG;
+ hrp->anonuser = "ftp";
+ hrp->next = NULL;
+ thishost = firsthost = lhrp = hrp;
+ if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ int i;
+
+ if ((cp = strchr(line, '\n')) == NULL) {
+ /* ignore long lines */
+ while (fgets(line, sizeof(line), fp) != NULL &&
+ strchr(line, '\n') == NULL)
+ ;
+ continue;
+ }
+ *cp = '\0';
+ cp = strtok(line, " \t");
+ /* skip comments and empty lines */
+ if (cp == NULL || line[0] == '#')
+ continue;
+ /* first, try a standard gethostbyname() */
+ if ((hp = gethostbyname(cp)) == NULL)
+ continue;
+ for (hrp = firsthost; hrp != NULL; hrp = hrp->next) {
+ if (memcmp(&hrp->hostaddr,
+ hp->h_addr_list[0],
+ sizeof(hrp->hostaddr)) == 0)
+ break;
+ }
+ if (hrp == NULL) {
+ if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
+ continue;
+ /* defaults */
+ hrp->statfile = _PATH_FTPDSTATFILE;
+ hrp->welcome = _PATH_FTPWELCOME;
+ hrp->loginmsg = _PATH_FTPLOGINMESG;
+ hrp->anonuser = "ftp";
+ hrp->next = NULL;
+ lhrp->next = hrp;
+ lhrp = hrp;
+ }
+ (void) memcpy(&hrp->hostaddr,
+ hp->h_addr_list[0],
+ sizeof(hrp->hostaddr));
+ /*
+ * determine hostname to use.
+ * force defined name if it is a valid alias
+ * otherwise fallback to primary hostname
+ */
+ if ((hp = gethostbyaddr((char*)&hrp->hostaddr,
+ sizeof(hrp->hostaddr),
+ AF_INET)) != NULL) {
+ if (strcmp(cp, hp->h_name) != 0) {
+ if (hp->h_aliases == NULL)
+ cp = hp->h_name;
+ else {
+ i = 0;
+ while (hp->h_aliases[i] &&
+ strcmp(cp, hp->h_aliases[i]) != 0)
+ ++i;
+ if (hp->h_aliases[i] == NULL)
+ cp = hp->h_name;
+ }
+ }
+ }
+ hrp->hostname = strdup(cp);
+ /* ok, now we now peel off the rest */
+ i = 0;
+ while (i < 4 && (cp = strtok(NULL, " \t")) != NULL) {
+ if (*cp != '-' && (cp = strdup(cp)) != NULL) {
+ switch (i) {
+ case 0: /* anon user permissions */
+ hrp->anonuser = cp;
+ break;
+ case 1: /* statistics file */
+ hrp->statfile = cp;
+ break;
+ case 2: /* welcome message */
+ hrp->welcome = cp;
+ break;
+ case 3: /* login message */
+ hrp->loginmsg = cp;
+ break;
+ }
+ }
+ ++i;
+ }
+ }
+ (void) fclose(fp);
+ }
+}
+
+static void
+selecthost(a)
+ struct in_addr *a;
+{
+ struct ftphost *hrp;
+
+ hrp = thishost = firsthost; /* default */
+ while (hrp != NULL) {
+ if (memcmp(a, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) {
+ thishost = hrp;
+ break;
+ }
+ hrp = hrp->next;
+ }
+ /* setup static variables as appropriate */
+ hostname = thishost->hostname;
+ ftpuser = thishost->anonuser;
+}
+#endif
/*
* Helper function for sgetpwnam().
@@ -416,19 +768,27 @@ user(name)
if (guest) {
reply(530, "Can't change user from guest login.");
return;
+ } else if (dochroot) {
+ reply(530, "Can't change user from chroot user.");
+ return;
}
end_login();
}
guest = 0;
if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
- if (checkuser("ftp") || checkuser("anonymous"))
+ if (checkuser(_PATH_FTPUSERS, "ftp") ||
+ checkuser(_PATH_FTPUSERS, "anonymous"))
reply(530, "User %s access denied.", name);
+#ifdef VIRTUAL_HOSTING
+ else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) {
+#else
else if ((pw = sgetpwnam("ftp")) != NULL) {
+#endif
guest = 1;
askpasswd = 1;
reply(331,
- "Guest login ok, type your name as password.");
+ "Guest login ok, send your email address as password.");
} else
reply(530, "User %s unknown.", name);
if (!askpasswd && logging)
@@ -436,7 +796,12 @@ user(name)
"ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
return;
}
- if (pw = sgetpwnam(name)) {
+ if (anon_only != 0) {
+ reply(530, "Sorry, only anonymous ftp allowed.");
+ return;
+ }
+
+ if ((pw = sgetpwnam(name))) {
if ((shell = pw->pw_shell) == NULL || *shell == 0)
shell = _PATH_BSHELL;
while ((cp = getusershell()) != NULL)
@@ -444,7 +809,7 @@ user(name)
break;
endusershell();
- if (cp == NULL || checkuser(name)) {
+ if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) {
reply(530, "User %s access denied.", name);
if (logging)
syslog(LOG_NOTICE,
@@ -456,7 +821,12 @@ user(name)
}
if (logging)
strncpy(curname, name, sizeof(curname)-1);
+#ifdef SKEY
+ pwok = skeyaccess(name, NULL, remotehost, addr_string);
+ reply(331, "%s", skey_challenge(name, pw, pwok));
+#else
reply(331, "Password required for %s.", name);
+#endif
askpasswd = 1;
/*
* Delay before reading passwd after first failed
@@ -467,26 +837,42 @@ user(name)
}
/*
- * Check if a user is in the file _PATH_FTPUSERS
+ * Check if a user is in the file "fname"
*/
static int
-checkuser(name)
+checkuser(fname, name)
+ char *fname;
char *name;
{
FILE *fd;
int found = 0;
char *p, line[BUFSIZ];
- if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
- while (fgets(line, sizeof(line), fd) != NULL)
+ if ((fd = fopen(fname, "r")) != NULL) {
+ while (!found && fgets(line, sizeof(line), fd) != NULL)
if ((p = strchr(line, '\n')) != NULL) {
*p = '\0';
if (line[0] == '#')
continue;
- if (strcmp(line, name) == 0) {
- found = 1;
- break;
+ /*
+ * if first chr is '@', check group membership
+ */
+ if (line[0] == '@') {
+ int i = 0;
+ struct group *grp;
+
+ if ((grp = getgrnam(line+1)) == NULL)
+ continue;
+ while (!found && grp->gr_mem[i])
+ found = strcmp(name,
+ grp->gr_mem[i++])
+ == 0;
}
+ /*
+ * Otherwise, just check for username match
+ */
+ else
+ found = strcmp(line, name) == 0;
}
(void) fclose(fd);
}
@@ -505,16 +891,25 @@ end_login()
if (logged_in)
logwtmp(ttyline, "", "");
pw = NULL;
+#ifdef LOGIN_CAP
+ setusercontext(NULL, getpwuid(0), (uid_t)0,
+ LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK);
+#endif
logged_in = 0;
guest = 0;
+ dochroot = 0;
}
void
pass(passwd)
char *passwd;
{
- char *salt, *xpasswd;
+ int rval;
FILE *fd;
+#ifdef LOGIN_CAP
+ login_cap_t *lc = NULL;
+#endif
+ static char homedir[MAXPATHLEN];
if (logged_in || askpasswd == 0) {
reply(503, "Login with USER first.");
@@ -522,14 +917,33 @@ pass(passwd)
}
askpasswd = 0;
if (!guest) { /* "ftp" is only account allowed no password */
- if (pw == NULL)
- salt = "xx";
- else
- salt = pw->pw_passwd;
- xpasswd = crypt(passwd, salt);
+ if (pw == NULL) {
+ rval = 1; /* failure below */
+ goto skip;
+ }
+#if defined(KERBEROS)
+ rval = klogin(pw, "", hostname, passwd);
+ if (rval == 0)
+ goto skip;
+#endif
+#ifdef SKEY
+ rval = strcmp(skey_crypt(passwd, pw->pw_passwd, pw, pwok),
+ pw->pw_passwd);
+ pwok = 0;
+#else
+ rval = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd);
+#endif
/* The strcmp does not catch null passwords! */
- if (pw == NULL || *pw->pw_passwd == '\0' ||
- strcmp(xpasswd, pw->pw_passwd)) {
+ if (*pw->pw_passwd == '\0' ||
+ (pw->pw_expire && time(NULL) >= pw->pw_expire))
+ rval = 1; /* failure */
+skip:
+ /*
+ * If rval == 1, the user failed the authentication check
+ * above. If rval == 0, either Kerberos or local authentication
+ * succeeded.
+ */
+ if (rval) {
reply(530, "Login incorrect.");
if (logging)
syslog(LOG_NOTICE,
@@ -550,13 +964,52 @@ pass(passwd)
reply(550, "Can't set gid.");
return;
}
+ /* May be overridden by login.conf */
+ (void) umask(defumask);
+#ifdef LOGIN_CAP
+ if ((lc = login_getpwclass(pw)) != NULL) {
+ char remote_ip[MAXHOSTNAMELEN];
+
+ strncpy(remote_ip, inet_ntoa(his_addr.sin_addr),
+ sizeof(remote_ip) - 1);
+ remote_ip[sizeof(remote_ip) - 1] = 0;
+ if (!auth_hostok(lc, remotehost, remote_ip)) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "FTP LOGIN FAILED (HOST) as %s: permission denied.",
+ pw->pw_name);
+ reply(530, "Permission denied.\n");
+ pw = NULL;
+ return;
+ }
+ if (!auth_timeok(lc, time(NULL))) {
+ reply(530, "Login not available right now.\n");
+ pw = NULL;
+ return;
+ }
+ }
+ setusercontext(lc, pw, (uid_t)0,
+ LOGIN_SETGROUP|LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK);
+#else
(void) initgroups(pw->pw_name, pw->pw_gid);
+#endif
/* open wtmp before chroot */
- (void)sprintf(ttyline, "ftp%d", getpid());
logwtmp(ttyline, pw->pw_name, remotehost);
logged_in = 1;
+ if (guest && stats && statfd < 0)
+#ifdef VIRTUAL_HOSTING
+ if ((statfd = open(thishost->statfile, O_WRONLY|O_APPEND)) < 0)
+#else
+ if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
+#endif
+ stats = 0;
+
+ dochroot =
+#ifdef LOGIN_CAP /* Allow login.conf configuration as well */
+ login_getcapbool(lc, "ftp-chroot", 0) ||
+#endif
+ checkuser(_PATH_FTPCHROOT, pw->pw_name);
if (guest) {
/*
* We MUST do a chdir() after the chroot. Otherwise
@@ -567,6 +1020,11 @@ pass(passwd)
reply(550, "Can't set guest privileges.");
goto bad;
}
+ } else if (dochroot) {
+ if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
+ reply(550, "Can't change root.");
+ goto bad;
+ }
} else if (chdir(pw->pw_dir) < 0) {
if (chdir("/") < 0) {
reply(530, "User %s: can't change directory to %s.",
@@ -579,11 +1037,22 @@ pass(passwd)
reply(550, "Can't set uid.");
goto bad;
}
+
+ /*
+ * Set home directory so that use of ~ (tilde) works correctly.
+ */
+ if (getcwd(homedir, MAXPATHLEN) != NULL)
+ setenv("HOME", homedir, 1);
+
/*
* Display a login message, if it exists.
* N.B. reply(230,) must follow the message.
*/
+#ifdef VIRTUAL_HOSTING
+ if ((fd = fopen(thishost->loginmsg, "r")) != NULL) {
+#else
if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
+#endif
char *cp, line[LINE_MAX];
while (fgets(line, sizeof(line), fd) != NULL) {
@@ -595,32 +1064,56 @@ pass(passwd)
(void) fclose(fd);
}
if (guest) {
+ if (ident != NULL)
+ free(ident);
+ ident = strdup(passwd);
+ if (ident == NULL)
+ fatal("Ran out of memory.");
+
reply(230, "Guest login ok, access restrictions apply.");
#ifdef SETPROCTITLE
- snprintf(proctitle, sizeof(proctitle),
- "%s: anonymous/%.*s", remotehost,
- sizeof(proctitle) - sizeof(remotehost) -
- sizeof(": anonymous/"), passwd);
- setproctitle(proctitle);
+#ifdef VIRTUAL_HOSTING
+ if (thishost != firsthost)
+ snprintf(proctitle, sizeof(proctitle),
+ "%s: anonymous(%s)/%.*s", remotehost, hostname,
+ sizeof(proctitle) - sizeof(remotehost) -
+ sizeof(": anonymous/"), passwd);
+ else
+#endif
+ snprintf(proctitle, sizeof(proctitle),
+ "%s: anonymous/%.*s", remotehost,
+ sizeof(proctitle) - sizeof(remotehost) -
+ sizeof(": anonymous/"), passwd);
+ setproctitle("%s", proctitle);
#endif /* SETPROCTITLE */
if (logging)
syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
remotehost, passwd);
} else {
+ if (dochroot)
+ reply(230, "User %s logged in, access restrictions apply.",
+ pw->pw_name);
+ else
reply(230, "User %s logged in.", pw->pw_name);
+
#ifdef SETPROCTITLE
snprintf(proctitle, sizeof(proctitle),
- "%s: %s", remotehost, pw->pw_name);
- setproctitle(proctitle);
+ "%s: %s", remotehost, pw->pw_name);
+ setproctitle("%s", proctitle);
#endif /* SETPROCTITLE */
if (logging)
syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
remotehost, pw->pw_name);
}
- (void) umask(defumask);
+#ifdef LOGIN_CAP
+ login_close(lc);
+#endif
return;
bad:
/* Forget all about it... */
+#ifdef LOGIN_CAP
+ login_close(lc);
+#endif
end_login();
}
@@ -631,6 +1124,7 @@ retrieve(cmd, name)
FILE *fin, *dout;
struct stat st;
int (*closefunc) __P((FILE *));
+ long start;
if (cmd == 0) {
fin = fopen(name, "r"), closefunc = fclose;
@@ -680,7 +1174,11 @@ retrieve(cmd, name)
dout = dataconn(name, st.st_size, "w");
if (dout == NULL)
goto done;
- send_data(fin, dout, st.st_blksize);
+ time(&start);
+ send_data(fin, dout, st.st_blksize, st.st_size,
+ restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode));
+ if (cmd == 0 && guest && stats)
+ logxfer(name, st.st_size, start);
(void) fclose(dout);
data = -1;
pdata = -1;
@@ -699,7 +1197,7 @@ store(name, mode, unique)
struct stat st;
int (*closefunc) __P((FILE *));
- if (unique && stat(name, &st) == 0 &&
+ if ((unique || guest) && stat(name, &st) == 0 &&
(name = gunique(name)) == NULL) {
LOGCMD(*mode == 'w' ? "put" : "append", name);
return;
@@ -778,6 +1276,7 @@ getdatasock(mode)
(char *) &on, sizeof(on)) < 0)
goto bad;
/* anchor socket to avoid multi-homing problems */
+ data_source.sin_len = sizeof(struct sockaddr_in);
data_source.sin_family = AF_INET;
data_source.sin_addr = ctrl_addr.sin_addr;
for (tries = 1; ; tries++) {
@@ -794,6 +1293,23 @@ getdatasock(mode)
if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
#endif
+#ifdef TCP_NOPUSH
+ /*
+ * Turn off push flag to keep sender TCP from sending short packets
+ * at the boundaries of each write(). Should probably do a SO_SNDBUF
+ * to set the send buffer size as well, but that may not be desirable
+ * in heavy-load situations.
+ */
+ on = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0)
+ syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
+#endif
+#ifdef SO_SNDBUF
+ on = 65536;
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m");
+#endif
+
return (fdopen(s, mode));
bad:
/* Return the real value of errno (close may change it) */
@@ -823,9 +1339,17 @@ dataconn(name, size, mode)
if (pdata >= 0) {
struct sockaddr_in from;
int s, fromlen = sizeof(from);
+ struct timeval timeout;
+ fd_set set;
+
+ FD_ZERO(&set);
+ FD_SET(pdata, &set);
- s = accept(pdata, (struct sockaddr *)&from, &fromlen);
- if (s < 0) {
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 120;
+
+ if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) == 0 ||
+ (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) {
reply(425, "Can't open data connection.");
(void) close(pdata);
pdata = -1;
@@ -834,7 +1358,7 @@ dataconn(name, size, mode)
(void) close(pdata);
pdata = s;
#ifdef IP_TOS
- tos = IPTOS_LOWDELAY;
+ tos = IPTOS_THROUGHPUT;
(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
sizeof(int));
#endif
@@ -878,17 +1402,20 @@ dataconn(name, size, mode)
/*
* Tranfer the contents of "instr" to "outstr" peer using the appropriate
- * encapsulation of the data subject * to Mode, Structure, and Type.
+ * encapsulation of the data subject to Mode, Structure, and Type.
*
* NB: Form isn't handled.
*/
static void
-send_data(instr, outstr, blksize)
+send_data(instr, outstr, blksize, filesize, isreg)
FILE *instr, *outstr;
off_t blksize;
+ off_t filesize;
+ int isreg;
{
int c, cnt, filefd, netfd;
- char *buf;
+ char *buf, *bp;
+ size_t len;
transflag++;
if (setjmp(urgcatch)) {
@@ -918,13 +1445,45 @@ send_data(instr, outstr, blksize)
case TYPE_I:
case TYPE_L:
+ /*
+ * isreg is only set if we are not doing restart and we
+ * are sending a regular file
+ */
+ netfd = fileno(outstr);
+ filefd = fileno(instr);
+
+ if (isreg && filesize < (off_t)16 * 1024 * 1024) {
+ buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd,
+ (off_t)0);
+ if (buf == MAP_FAILED) {
+ syslog(LOG_WARNING, "mmap(%lu): %m",
+ (unsigned long)filesize);
+ goto oldway;
+ }
+ bp = buf;
+ len = filesize;
+ do {
+ cnt = write(netfd, bp, len);
+ len -= cnt;
+ bp += cnt;
+ if (cnt > 0) byte_count += cnt;
+ } while(cnt > 0 && len > 0);
+
+ transflag = 0;
+ munmap(buf, (size_t)filesize);
+ if (cnt < 0)
+ goto data_err;
+ reply(226, "Transfer complete.");
+ return;
+ }
+
+oldway:
if ((buf = malloc((u_int)blksize)) == NULL) {
transflag = 0;
perror_reply(451, "Local resource failure: malloc");
return;
}
- netfd = fileno(outstr);
- filefd = fileno(instr);
+
while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
write(netfd, buf, cnt) == cnt)
byte_count += cnt;
@@ -964,7 +1523,7 @@ receive_data(instr, outstr)
FILE *instr, *outstr;
{
int c;
- int cnt, bare_lfs = 0;
+ int cnt, bare_lfs;
char buf[BUFSIZ];
transflag++;
@@ -972,6 +1531,9 @@ receive_data(instr, outstr)
transflag = 0;
return (-1);
}
+
+ bare_lfs = 0;
+
switch (type) {
case TYPE_I:
@@ -1046,7 +1608,7 @@ statfilecmd(filename)
int c;
char line[LINE_MAX];
- (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
+ (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename);
fin = ftpd_popen(line, "r");
lreply(211, "status of %s:", filename);
while ((c = getc(fin)) != EOF) {
@@ -1209,7 +1771,7 @@ yyerror(s)
{
char *cp;
- if (cp = strchr(cbuf,'\n'))
+ if ((cp = strchr(cbuf,'\n')))
*cp = '\0';
reply(500, "'%s': command not understood.", cbuf);
}
@@ -1304,8 +1866,15 @@ void
renamecmd(from, to)
char *from, *to;
{
+ struct stat st;
LOGCMD2("rename", from, to);
+
+ if (guest && (stat(to, &st) == 0)) {
+ reply(550, "%s: permission denied", to);
+ return;
+ }
+
if (rename(from, to) < 0)
perror_reply(550, "rename");
else
@@ -1325,12 +1894,26 @@ dolog(sin)
(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
sizeof(remotehost));
#ifdef SETPROCTITLE
- snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
- setproctitle(proctitle);
+#ifdef VIRTUAL_HOSTING
+ if (thishost != firsthost)
+ snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)",
+ remotehost, hostname);
+ else
+#endif
+ snprintf(proctitle, sizeof(proctitle), "%s: connected",
+ remotehost);
+ setproctitle("%s", proctitle);
#endif /* SETPROCTITLE */
- if (logging)
- syslog(LOG_INFO, "connection from %s", remotehost);
+ if (logging) {
+#ifdef VIRTUAL_HOSTING
+ if (thishost != firsthost)
+ syslog(LOG_INFO, "connection from %s (to %s)",
+ remotehost, hostname);
+ else
+#endif
+ syslog(LOG_INFO, "connection from %s", remotehost);
+ }
}
/*
@@ -1341,10 +1924,19 @@ void
dologout(status)
int status;
{
+ /*
+ * Prevent reception of SIGURG from resulting in a resumption
+ * back to the main program loop.
+ */
+ transflag = 0;
if (logged_in) {
(void) seteuid((uid_t)0);
logwtmp(ttyline, "", "");
+#if defined(KERBEROS)
+ if (!notickets && krbtkfile_env)
+ unlink(krbtkfile_env);
+#endif
}
/* beware of flushing buffers after a SIGPIPE */
_exit(status);
@@ -1392,19 +1984,36 @@ passive()
int len;
char *p, *a;
+ if (pdata >= 0) /* close old port if one set */
+ close(pdata);
+
pdata = socket(AF_INET, SOCK_STREAM, 0);
if (pdata < 0) {
perror_reply(425, "Can't open passive connection");
return;
}
+
+ (void) seteuid((uid_t)0);
+
+#ifdef IP_PORTRANGE
+ {
+ int on = restricted_data_ports ? IP_PORTRANGE_HIGH
+ : IP_PORTRANGE_DEFAULT;
+
+ if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
+ (char *)&on, sizeof(on)) < 0)
+ goto pasv_error;
+ }
+#endif
+
pasv_addr = ctrl_addr;
pasv_addr.sin_port = 0;
- (void) seteuid((uid_t)0);
- if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
- (void) seteuid((uid_t)pw->pw_uid);
+ if (bind(pdata, (struct sockaddr *)&pasv_addr,
+ sizeof(pasv_addr)) < 0)
goto pasv_error;
- }
+
(void) seteuid((uid_t)pw->pw_uid);
+
len = sizeof(pasv_addr);
if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
goto pasv_error;
@@ -1420,6 +2029,7 @@ passive()
return;
pasv_error:
+ (void) seteuid((uid_t)pw->pw_uid);
(void) close(pdata);
pdata = -1;
perror_reply(425, "Can't open passive connection");
@@ -1449,7 +2059,7 @@ gunique(local)
}
if (cp)
*cp = '/';
- (void) strcpy(new, local);
+ (void) snprintf(new, sizeof(new), "%s", local);
cp = new + strlen(new);
*cp++ = '.';
for (count = 1; count < 100; count++) {
@@ -1515,7 +2125,7 @@ send_file_list(whichf)
transflag = 0;
goto out;
}
- while (dirname = *dirlist++) {
+ while ((dirname = *dirlist++)) {
if (stat(dirname, &st) < 0) {
/*
* If user typed "ls -l", etc, and the client
@@ -1523,7 +2133,7 @@ send_file_list(whichf)
*/
if (dirname[0] == '-' && *dirlist == NULL &&
transflag == 0) {
- retrieve("/bin/ls %s", dirname);
+ retrieve(_PATH_LS " %s", dirname);
goto out;
}
perror_reply(550, whichf);
@@ -1608,7 +2218,14 @@ out:
}
}
-#ifdef SETPROCTITLE
+void
+reapchild(signo)
+ int signo;
+{
+ while (wait3(NULL, WNOHANG, NULL) > 0);
+}
+
+#ifdef OLD_SETPROCTITLE
/*
* Clobber argv so ps will show what we're doing. (Stolen from sendmail.)
* Warning, since this is usually started from inetd.conf, it often doesn't
@@ -1651,4 +2268,23 @@ setproctitle(fmt, va_alist)
while (p < LastArgv)
*p++ = ' ';
}
-#endif /* SETPROCTITLE */
+#endif /* OLD_SETPROCTITLE */
+
+static void
+logxfer(name, size, start)
+ char *name;
+ long size;
+ long start;
+{
+ char buf[1024];
+ char path[MAXPATHLEN + 1];
+ long now;
+
+ if (statfd >= 0 && getwd(path) != NULL) {
+ time(&now);
+ snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s/%s!%ld!%ld\n",
+ ctime(&now)+4, ident, remotehost,
+ path, name, size, now - start + (now == start));
+ write(statfd, buf, strlen(buf));
+ }
+}
diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c
index d40840c..9562795 100644
--- a/libexec/ftpd/logwtmp.c
+++ b/libexec/ftpd/logwtmp.c
@@ -30,6 +30,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
+ * $Id$
*/
#ifndef lint
@@ -37,10 +38,13 @@ static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93";
#endif /* not lint */
#include <sys/types.h>
-#include <sys/time.h>
#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
#include <fcntl.h>
+#include <time.h>
+#include <netdb.h>
#include <utmp.h>
#include <unistd.h>
#include <stdio.h>
@@ -61,6 +65,18 @@ logwtmp(line, name, host)
struct utmp ut;
struct stat buf;
+ if (strlen(host) > UT_HOSTSIZE) {
+ struct hostent *hp = gethostbyname(host);
+
+ if (hp != NULL) {
+ struct in_addr in;
+
+ memmove(&in, hp->h_addr, sizeof(in));
+ host = inet_ntoa(in);
+ } else
+ host = "invalid hostname";
+ }
+
if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0)
return;
if (fstat(fd, &buf) == 0) {
diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h
index 2a50063..d2f8b73 100644
--- a/libexec/ftpd/pathnames.h
+++ b/libexec/ftpd/pathnames.h
@@ -31,10 +31,14 @@
* SUCH DAMAGE.
*
* @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ * $Id: pathnames.h,v 1.9 1997/04/26 12:12:10 davidn Exp $
*/
#include <paths.h>
-#define _PATH_FTPUSERS "/etc/ftpusers"
+#define _PATH_FTPCHROOT "/etc/ftpchroot"
#define _PATH_FTPWELCOME "/etc/ftpwelcome"
-#define _PATH_FTPLOGINMESG "/etc/motd"
+#define _PATH_FTPLOGINMESG "/etc/ftpmotd"
+#define _PATH_FTPHOSTS "/etc/ftphosts"
+#define _PATH_FTPDSTATFILE "/var/log/ftpd"
+#define _PATH_LS "/bin/ls"
diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c
index b26732e..ac0b76a 100644
--- a/libexec/ftpd/popen.c
+++ b/libexec/ftpd/popen.c
@@ -33,11 +33,14 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
+ * $Id: popen.c,v 1.7 1997/02/22 14:21:31 peter Exp $
*/
+#if 0
#ifndef lint
static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94";
#endif /* not lint */
+#endif
#include <sys/types.h>
#include <sys/wait.h>
@@ -51,6 +54,12 @@ static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94";
#include <unistd.h>
#include "extern.h"
+#ifdef INTERNAL_LS
+#include "pathnames.h"
+#endif
+
+#define MAXUSRARGS 100
+#define MAXGLOBARGS 1000
/*
* Special version of popen which avoids call to shell. This ensures noone
@@ -67,9 +76,9 @@ ftpd_popen(program, type)
char *cp;
FILE *iop;
int argc, gargc, pdes[2], pid;
- char **pop, *argv[100], *gargv[1000];
+ char **pop, *argv[MAXUSRARGS], *gargv[MAXGLOBARGS];
- if (*type != 'r' && *type != 'w' || type[1])
+ if (((*type != 'r') && (*type != 'w')) || type[1])
return (NULL);
if (!pids) {
@@ -83,13 +92,13 @@ ftpd_popen(program, type)
return (NULL);
/* break up string into pieces */
- for (argc = 0, cp = program;; cp = NULL)
+ for (argc = 0, cp = program; argc < MAXUSRARGS; cp = NULL)
if (!(argv[argc++] = strtok(cp, " \t\n")))
break;
/* glob each piece */
gargv[0] = argv[0];
- for (gargc = argc = 1; argv[argc]; argc++) {
+ for (gargc = argc = 1; argv[argc] && gargc < (MAXGLOBARGS-1); argc++) {
glob_t gl;
int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
@@ -97,14 +106,21 @@ ftpd_popen(program, type)
if (glob(argv[argc], flags, NULL, &gl))
gargv[gargc++] = strdup(argv[argc]);
else
- for (pop = gl.gl_pathv; *pop; pop++)
+ for (pop = gl.gl_pathv; *pop && gargc < (MAXGLOBARGS-1);
+ pop++)
gargv[gargc++] = strdup(*pop);
globfree(&gl);
}
gargv[gargc] = NULL;
iop = NULL;
- switch(pid = vfork()) {
+#ifdef INTERNAL_LS
+ fflush(NULL);
+ pid = (strcmp(gargv[0], _PATH_LS) == 0) ? fork() : vfork();
+#else
+ pid = vfork();
+#endif
+ switch(pid) {
case -1: /* error */
(void)close(pdes[0]);
(void)close(pdes[1]);
@@ -125,6 +141,14 @@ ftpd_popen(program, type)
}
(void)close(pdes[1]);
}
+#ifdef INTERNAL_LS
+ if (strcmp(gargv[0], _PATH_LS) == 0) {
+ extern int optreset;
+ /* Reset getopt for ls_main() */
+ optreset = optind = optopt = 1;
+ exit(ls_main(gargc, gargv));
+ }
+#endif
execv(gargv[0], gargv);
_exit(1);
}
diff --git a/libexec/ftpd/skey-stuff.c b/libexec/ftpd/skey-stuff.c
new file mode 100644
index 0000000..b6aba40
--- /dev/null
+++ b/libexec/ftpd/skey-stuff.c
@@ -0,0 +1,29 @@
+/* Author: Wietse Venema, Eindhoven University of Technology.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+
+#include <skey.h>
+
+/* skey_challenge - additional password prompt stuff */
+
+char *skey_challenge(name, pwd, pwok)
+char *name;
+struct passwd *pwd;
+int pwok;
+{
+ static char buf[128];
+ struct skey skey;
+
+ /* Display s/key challenge where appropriate. */
+
+ if (pwd == NULL || skeychallenge(&skey, pwd->pw_name, buf))
+ sprintf(buf, "Password required for %s.", name);
+ else if (!pwok)
+ strcat(buf, " (s/key required)");
+ return (buf);
+}
OpenPOWER on IntegriCloud