import netbsd ftpd
Change-Id: Id7a3dbd40a6f37c55bcbb0d1456301f60626298f
This commit is contained in:
parent
eb1e5bf042
commit
62da011387
19 changed files with 13250 additions and 2 deletions
|
@ -128,6 +128,7 @@
|
||||||
./lib minix-sys
|
./lib minix-sys
|
||||||
./lib/cpp minix-sys
|
./lib/cpp minix-sys
|
||||||
./libexec minix-sys
|
./libexec minix-sys
|
||||||
|
./usr/libexec/ftpd minix-sys
|
||||||
./libexec/ld.elf_so minix-sys
|
./libexec/ld.elf_so minix-sys
|
||||||
./libexec/ld-elf.so.1 minix-sys
|
./libexec/ld-elf.so.1 minix-sys
|
||||||
./mnt minix-sys
|
./mnt minix-sys
|
||||||
|
@ -3667,6 +3668,10 @@
|
||||||
./usr/man/man5/editrc.5 minix-sys
|
./usr/man/man5/editrc.5 minix-sys
|
||||||
./usr/man/man5/ethers.5 minix-sys
|
./usr/man/man5/ethers.5 minix-sys
|
||||||
./usr/man/man5/fstab.5 minix-sys
|
./usr/man/man5/fstab.5 minix-sys
|
||||||
|
./usr/man/man5/ftpchroot.5 minix-sys
|
||||||
|
./usr/man/man5/ftpd.conf.5 minix-sys
|
||||||
|
./usr/man/man5/ftpusers.5 minix-sys
|
||||||
|
./usr/man/man8/ftpd.8 minix-sys
|
||||||
./usr/man/man5/group.5 minix-sys
|
./usr/man/man5/group.5 minix-sys
|
||||||
./usr/man/man5/hosts.5 minix-sys
|
./usr/man/man5/hosts.5 minix-sys
|
||||||
./usr/man/man5/httpd.conf.5 minix-sys
|
./usr/man/man5/httpd.conf.5 minix-sys
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
daemonize talkd
|
daemonize talkd
|
||||||
daemonize tcpd shell in.rshd
|
daemonize tcpd shell in.rshd
|
||||||
daemonize tcpd telnet in.telnetd
|
daemonize tcpd telnet in.telnetd
|
||||||
daemonize tcpd ftp in.ftpd
|
daemonize tcpd ftp /usr/libexec/ftpd
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
.include <bsd.own.mk>
|
.include <bsd.own.mk>
|
||||||
|
|
||||||
SUBDIR= \
|
SUBDIR= \
|
||||||
\
|
ftpd \
|
||||||
ld.elf_so
|
ld.elf_so
|
||||||
|
|
||||||
.if defined(__MINIX)
|
.if defined(__MINIX)
|
||||||
|
|
58
libexec/ftpd/Makefile
Normal file
58
libexec/ftpd/Makefile
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# $NetBSD: Makefile,v 1.63 2011/08/14 11:46:28 christos Exp $
|
||||||
|
# @(#)Makefile 8.2 (Berkeley) 4/4/94
|
||||||
|
|
||||||
|
.include <bsd.own.mk>
|
||||||
|
|
||||||
|
PROG= ftpd
|
||||||
|
SRCS= cmds.c conf.c ftpd.c ftpcmd.y logutmp.c logwtmp.c popen.c
|
||||||
|
.ifdef __MINIX
|
||||||
|
CPPFLAGS+=-I${.CURDIR}
|
||||||
|
.else
|
||||||
|
CPPFLAGS+=-I${.CURDIR} -DSUPPORT_UTMP -DSUPPORT_UTMPX -DLOGIN_CAP
|
||||||
|
.endif
|
||||||
|
DPADD+= ${LIBCRYPT} ${LIBUTIL}
|
||||||
|
LDADD+= -lcrypt -lutil
|
||||||
|
MAN= ftpd.conf.5 ftpusers.5 ftpd.8
|
||||||
|
MLINKS= ftpusers.5 ftpchroot.5
|
||||||
|
|
||||||
|
.if defined(NO_INTERNAL_LS)
|
||||||
|
CPPFLAGS+=-DNO_INTERNAL_LS
|
||||||
|
.else
|
||||||
|
# for `internal' ls
|
||||||
|
SRCS+= ls.c cmp.c print.c util.c
|
||||||
|
.PATH: ${NETBSDSRCDIR}/bin/ls
|
||||||
|
.endif
|
||||||
|
|
||||||
|
.if (${USE_INET6} != "no")
|
||||||
|
CPPFLAGS+=-DINET6
|
||||||
|
.endif
|
||||||
|
|
||||||
|
.if (${USE_PAM} != "no")
|
||||||
|
CPPFLAGS+=-DUSE_PAM
|
||||||
|
DPADD+= ${LIBPAM} ${PAM_STATIC_DPADD}
|
||||||
|
LDADD+= -lpam ${PAM_STATIC_LDADD}
|
||||||
|
.else # USE_PAM == no
|
||||||
|
.if (${USE_SKEY} != "no")
|
||||||
|
CPPFLAGS+=-DSKEY
|
||||||
|
DPADD+= ${LIBSKEY}
|
||||||
|
LDADD+= -lskey
|
||||||
|
.endif
|
||||||
|
.endif # USE_PAM == no
|
||||||
|
|
||||||
|
ftpd.o ftpcmd.o: version.h
|
||||||
|
|
||||||
|
#.if (${USE_KERBEROS} != "no")
|
||||||
|
#
|
||||||
|
#.PATH: ${NETBSDSRCDIR}/usr.bin/login
|
||||||
|
#
|
||||||
|
#SRCS+= k5login.c
|
||||||
|
#CPPFLAGS+=-DKERBEROS5
|
||||||
|
#DPADD+= ${LIBKRB5} ${LIBASN1}
|
||||||
|
#LDADD+= -lkrb5 -lasn1
|
||||||
|
#
|
||||||
|
#DPADD+= ${LIBCRYPTO} ${{LIBCRYPT} ${LIBROKEN} ${LIBCOM_ERR}
|
||||||
|
#LDADD+= -lcrypto -lcrypt -lroken -lcom_err
|
||||||
|
#
|
||||||
|
#.endif
|
||||||
|
|
||||||
|
.include <bsd.prog.mk>
|
967
libexec/ftpd/cmds.c
Normal file
967
libexec/ftpd/cmds.c
Normal file
|
@ -0,0 +1,967 @@
|
||||||
|
/* $NetBSD: cmds.c,v 1.31 2012/06/19 06:06:34 dholland Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1999-2009 The NetBSD Foundation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
* by 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.
|
||||||
|
*
|
||||||
|
* 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 FOUNDATION 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 1997 and 1998 WIDE Project.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* 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. Neither the name of the project 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 PROJECT 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 PROJECT 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#ifndef lint
|
||||||
|
__RCSID("$NetBSD: cmds.c,v 1.31 2012/06/19 06:06:34 dholland Exp $");
|
||||||
|
#endif /* not lint */
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <arpa/ftp.h>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <tzfile.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#ifdef KERBEROS5
|
||||||
|
#include <krb5/krb5.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FE_MLSD = 1<<0, /* if op is MLSD (MLST otherwise ) */
|
||||||
|
FE_ISCURDIR = 1<<1, /* if name is the current directory */
|
||||||
|
} factflag_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *path; /* full pathname */
|
||||||
|
const char *display; /* name to display */
|
||||||
|
struct stat *stat; /* stat of path */
|
||||||
|
struct stat *pdirstat; /* stat of path's parent dir */
|
||||||
|
factflag_t flags; /* flags */
|
||||||
|
} factelem;
|
||||||
|
|
||||||
|
static void ack(const char *);
|
||||||
|
static void base64_encode(const char *, size_t, char *, int);
|
||||||
|
static void fact_type(const char *, FILE *, factelem *);
|
||||||
|
static void fact_size(const char *, FILE *, factelem *);
|
||||||
|
static void fact_modify(const char *, FILE *, factelem *);
|
||||||
|
static void fact_perm(const char *, FILE *, factelem *);
|
||||||
|
static void fact_unique(const char *, FILE *, factelem *);
|
||||||
|
static int matchgroup(gid_t);
|
||||||
|
static void mlsname(FILE *, factelem *);
|
||||||
|
static void replydirname(const char *, const char *);
|
||||||
|
|
||||||
|
struct ftpfact {
|
||||||
|
const char *name; /* name of fact */
|
||||||
|
int enabled; /* if fact is enabled */
|
||||||
|
void (*display)(const char *, FILE *, factelem *);
|
||||||
|
/* function to display fact */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ftpfact facttab[] = {
|
||||||
|
{ "Type", 1, fact_type },
|
||||||
|
#define FACT_TYPE 0
|
||||||
|
{ "Size", 1, fact_size },
|
||||||
|
{ "Modify", 1, fact_modify },
|
||||||
|
{ "Perm", 1, fact_perm },
|
||||||
|
{ "Unique", 1, fact_unique },
|
||||||
|
/* "Create" */
|
||||||
|
/* "Lang" */
|
||||||
|
/* "Media-Type" */
|
||||||
|
/* "CharSet" */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FACTTABSIZE (sizeof(facttab) / sizeof(struct ftpfact))
|
||||||
|
|
||||||
|
static char cached_path[MAXPATHLEN + 1] = "/";
|
||||||
|
static void discover_path(char *, const char *);
|
||||||
|
|
||||||
|
void
|
||||||
|
cwd(const char *path)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (chdir(path) < 0)
|
||||||
|
perror_reply(550, path);
|
||||||
|
else {
|
||||||
|
show_chdir_messages(250);
|
||||||
|
ack("CWD");
|
||||||
|
if (getcwd(cached_path, MAXPATHLEN) == NULL) {
|
||||||
|
discover_path(cached_path, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
delete(const char *name)
|
||||||
|
{
|
||||||
|
char *p = NULL;
|
||||||
|
|
||||||
|
if (remove(name) < 0) {
|
||||||
|
p = strerror(errno);
|
||||||
|
perror_reply(550, name);
|
||||||
|
} else
|
||||||
|
ack("DELE");
|
||||||
|
logxfer("delete", -1, name, NULL, NULL, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
feat(void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
reply(-211, "Features supported");
|
||||||
|
cprintf(stdout, " MDTM\r\n");
|
||||||
|
cprintf(stdout, " MLST ");
|
||||||
|
for (i = 0; i < FACTTABSIZE; i++)
|
||||||
|
cprintf(stdout, "%s%s;", facttab[i].name,
|
||||||
|
facttab[i].enabled ? "*" : "");
|
||||||
|
cprintf(stdout, "\r\n");
|
||||||
|
cprintf(stdout, " REST STREAM\r\n");
|
||||||
|
cprintf(stdout, " SIZE\r\n");
|
||||||
|
cprintf(stdout, " TVFS\r\n");
|
||||||
|
reply(211, "End");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
makedir(const char *name)
|
||||||
|
{
|
||||||
|
char *p = NULL;
|
||||||
|
|
||||||
|
if (mkdir(name, 0777) < 0) {
|
||||||
|
p = strerror(errno);
|
||||||
|
perror_reply(550, name);
|
||||||
|
} else
|
||||||
|
replydirname(name, "directory created.");
|
||||||
|
logxfer("mkdir", -1, name, NULL, NULL, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mlsd(const char *path)
|
||||||
|
{
|
||||||
|
struct dirent *dp;
|
||||||
|
struct stat sb, pdirstat;
|
||||||
|
factelem f;
|
||||||
|
FILE *dout;
|
||||||
|
DIR *dirp;
|
||||||
|
char name[MAXPATHLEN];
|
||||||
|
int hastypefact;
|
||||||
|
|
||||||
|
hastypefact = facttab[FACT_TYPE].enabled;
|
||||||
|
if (path == NULL)
|
||||||
|
path = ".";
|
||||||
|
if (stat(path, &pdirstat) == -1) {
|
||||||
|
mlsdperror:
|
||||||
|
perror_reply(550, path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (! S_ISDIR(pdirstat.st_mode)) {
|
||||||
|
errno = ENOTDIR;
|
||||||
|
perror_reply(501, path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((dirp = opendir(path)) == NULL)
|
||||||
|
goto mlsdperror;
|
||||||
|
|
||||||
|
dout = dataconn("MLSD", (off_t)-1, "w");
|
||||||
|
if (dout == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(&f, 0, sizeof(f));
|
||||||
|
f.stat = &sb;
|
||||||
|
f.flags |= FE_MLSD;
|
||||||
|
while ((dp = readdir(dirp)) != NULL) {
|
||||||
|
snprintf(name, sizeof(name), "%s/%s", path, dp->d_name);
|
||||||
|
if (ISDOTDIR(dp->d_name)) { /* special case curdir: */
|
||||||
|
if (! hastypefact)
|
||||||
|
continue;
|
||||||
|
f.pdirstat = NULL; /* require stat of parent */
|
||||||
|
f.display = path; /* set name to real name */
|
||||||
|
f.flags |= FE_ISCURDIR; /* flag name is curdir */
|
||||||
|
} else {
|
||||||
|
if (ISDOTDOTDIR(dp->d_name)) {
|
||||||
|
if (! hastypefact)
|
||||||
|
continue;
|
||||||
|
f.pdirstat = NULL;
|
||||||
|
} else
|
||||||
|
f.pdirstat = &pdirstat; /* cache parent stat */
|
||||||
|
f.display = dp->d_name;
|
||||||
|
f.flags &= ~FE_ISCURDIR;
|
||||||
|
}
|
||||||
|
if (stat(name, &sb) == -1)
|
||||||
|
continue;
|
||||||
|
f.path = name;
|
||||||
|
mlsname(dout, &f);
|
||||||
|
}
|
||||||
|
(void)closedir(dirp);
|
||||||
|
|
||||||
|
if (ferror(dout) != 0)
|
||||||
|
perror_reply(550, "Data connection");
|
||||||
|
else
|
||||||
|
reply(226, "MLSD complete.");
|
||||||
|
closedataconn(dout);
|
||||||
|
total_xfers_out++;
|
||||||
|
total_xfers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mlst(const char *path)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
factelem f;
|
||||||
|
|
||||||
|
if (path == NULL)
|
||||||
|
path = ".";
|
||||||
|
if (stat(path, &sb) == -1) {
|
||||||
|
perror_reply(550, path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reply(-250, "MLST %s", path);
|
||||||
|
memset(&f, 0, sizeof(f));
|
||||||
|
f.path = path;
|
||||||
|
f.display = path;
|
||||||
|
f.stat = &sb;
|
||||||
|
f.pdirstat = NULL;
|
||||||
|
CPUTC(' ', stdout);
|
||||||
|
mlsname(stdout, &f);
|
||||||
|
reply(250, "End");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
opts(const char *command)
|
||||||
|
{
|
||||||
|
struct tab *c;
|
||||||
|
char *ep;
|
||||||
|
|
||||||
|
if ((ep = strchr(command, ' ')) != NULL)
|
||||||
|
*ep++ = '\0';
|
||||||
|
c = lookup(cmdtab, command);
|
||||||
|
if (c == NULL) {
|
||||||
|
reply(502, "Unknown command '%s'.", command);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (! CMD_IMPLEMENTED(c)) {
|
||||||
|
reply(502, "%s command not implemented.", c->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (! CMD_HAS_OPTIONS(c)) {
|
||||||
|
reply(501, "%s command does not support persistent options.",
|
||||||
|
c->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* special case: MLST */
|
||||||
|
if (strcasecmp(command, "MLST") == 0) {
|
||||||
|
int enabled[FACTTABSIZE];
|
||||||
|
size_t i, onedone;
|
||||||
|
size_t len;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(enabled) / sizeof(int); i++)
|
||||||
|
enabled[i] = 0;
|
||||||
|
if (ep == NULL || *ep == '\0')
|
||||||
|
goto displaymlstopts;
|
||||||
|
|
||||||
|
/* don't like spaces, and need trailing ; */
|
||||||
|
len = strlen(ep);
|
||||||
|
if (strchr(ep, ' ') != NULL || ep[len - 1] != ';') {
|
||||||
|
badmlstopt:
|
||||||
|
reply(501, "Invalid MLST options");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ep[len - 1] = '\0';
|
||||||
|
while ((p = strsep(&ep, ";")) != NULL) {
|
||||||
|
if (*p == '\0')
|
||||||
|
goto badmlstopt;
|
||||||
|
for (i = 0; i < FACTTABSIZE; i++)
|
||||||
|
if (strcasecmp(p, facttab[i].name) == 0) {
|
||||||
|
enabled[i] = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displaymlstopts:
|
||||||
|
for (i = 0; i < FACTTABSIZE; i++)
|
||||||
|
facttab[i].enabled = enabled[i];
|
||||||
|
cprintf(stdout, "200 MLST OPTS");
|
||||||
|
for (i = onedone = 0; i < FACTTABSIZE; i++) {
|
||||||
|
if (facttab[i].enabled) {
|
||||||
|
cprintf(stdout, "%s%s;", onedone ? "" : " ",
|
||||||
|
facttab[i].name);
|
||||||
|
onedone++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cprintf(stdout, "\r\n");
|
||||||
|
fflush(stdout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* default cases */
|
||||||
|
if (ep != NULL && *ep != '\0')
|
||||||
|
REASSIGN(c->options, ftpd_strdup(ep));
|
||||||
|
if (c->options != NULL)
|
||||||
|
reply(200, "Options for %s are '%s'.", c->name,
|
||||||
|
c->options);
|
||||||
|
else
|
||||||
|
reply(200, "No options defined for %s.", c->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pwd(void)
|
||||||
|
{
|
||||||
|
char path[MAXPATHLEN];
|
||||||
|
|
||||||
|
if (getcwd(path, sizeof(path) - 1) == NULL) {
|
||||||
|
if (chdir(cached_path) < 0) {
|
||||||
|
reply(550, "Can't get the current directory: %s.",
|
||||||
|
strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(void)strlcpy(path, cached_path, MAXPATHLEN);
|
||||||
|
}
|
||||||
|
replydirname(path, "is the current directory.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
removedir(const char *name)
|
||||||
|
{
|
||||||
|
char *p = NULL;
|
||||||
|
|
||||||
|
if (rmdir(name) < 0) {
|
||||||
|
p = strerror(errno);
|
||||||
|
perror_reply(550, name);
|
||||||
|
} else
|
||||||
|
ack("RMD");
|
||||||
|
logxfer("rmdir", -1, name, NULL, NULL, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
renamefrom(const char *name)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (stat(name, &st) < 0) {
|
||||||
|
perror_reply(550, name);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
reply(350, "File exists, ready for destination name");
|
||||||
|
return (ftpd_strdup(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
renamecmd(const char *from, const char *to)
|
||||||
|
{
|
||||||
|
char *p = NULL;
|
||||||
|
|
||||||
|
if (rename(from, to) < 0) {
|
||||||
|
p = strerror(errno);
|
||||||
|
perror_reply(550, "rename");
|
||||||
|
} else
|
||||||
|
ack("RNTO");
|
||||||
|
logxfer("rename", -1, from, to, NULL, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sizecmd(const char *filename)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case TYPE_L:
|
||||||
|
case TYPE_I:
|
||||||
|
{
|
||||||
|
struct stat stbuf;
|
||||||
|
if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
|
||||||
|
reply(550, "%s: not a plain file.", filename);
|
||||||
|
else
|
||||||
|
reply(213, ULLF, (ULLT)stbuf.st_size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TYPE_A:
|
||||||
|
{
|
||||||
|
FILE *fin;
|
||||||
|
int c;
|
||||||
|
off_t count;
|
||||||
|
struct stat stbuf;
|
||||||
|
fin = fopen(filename, "r");
|
||||||
|
if (fin == NULL) {
|
||||||
|
perror_reply(550, filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
|
||||||
|
reply(550, "%s: not a plain file.", filename);
|
||||||
|
(void) fclose(fin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stbuf.st_size > 10240) {
|
||||||
|
reply(550, "%s: file too large for SIZE.", filename);
|
||||||
|
(void) fclose(fin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
while((c = getc(fin)) != EOF) {
|
||||||
|
if (c == '\n') /* will get expanded to \r\n */
|
||||||
|
count++;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
(void) fclose(fin);
|
||||||
|
|
||||||
|
reply(213, LLF, (LLT)count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
statfilecmd(const char *filename)
|
||||||
|
{
|
||||||
|
FILE *fin;
|
||||||
|
int c;
|
||||||
|
int atstart;
|
||||||
|
const char *argv[] = { INTERNAL_LS, "-lgA", "", NULL };
|
||||||
|
|
||||||
|
argv[2] = filename;
|
||||||
|
fin = ftpd_popen(argv, "r", STDOUT_FILENO);
|
||||||
|
reply(-211, "status of %s:", filename);
|
||||||
|
/* XXX: use fgetln() or fparseln() here? */
|
||||||
|
atstart = 1;
|
||||||
|
while ((c = getc(fin)) != EOF) {
|
||||||
|
if (c == '\n') {
|
||||||
|
if (ferror(stdout)){
|
||||||
|
perror_reply(421, "control connection");
|
||||||
|
(void) ftpd_pclose(fin);
|
||||||
|
dologout(1);
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
if (ferror(fin)) {
|
||||||
|
perror_reply(551, filename);
|
||||||
|
(void) ftpd_pclose(fin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CPUTC('\r', stdout);
|
||||||
|
}
|
||||||
|
if (atstart && isdigit(c))
|
||||||
|
CPUTC(' ', stdout);
|
||||||
|
CPUTC(c, stdout);
|
||||||
|
atstart = (c == '\n');
|
||||||
|
}
|
||||||
|
(void) ftpd_pclose(fin);
|
||||||
|
reply(211, "End of Status");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- */
|
||||||
|
|
||||||
|
static void
|
||||||
|
ack(const char *s)
|
||||||
|
{
|
||||||
|
|
||||||
|
reply(250, "%s command successful.", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode len bytes starting at clear using base64 encoding into encoded,
|
||||||
|
* which should be at least ((len + 2) * 4 / 3 + 1) in size.
|
||||||
|
* If nulterm is non-zero, terminate with \0 otherwise pad to 3 byte boundary
|
||||||
|
* with `='.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
base64_encode(const char *clear, size_t len, char *encoded, int nulterm)
|
||||||
|
{
|
||||||
|
static const char base64[] =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
const char *c;
|
||||||
|
char *e, termchar;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* determine whether to pad with '=' or NUL terminate */
|
||||||
|
termchar = nulterm ? '\0' : '=';
|
||||||
|
c = clear;
|
||||||
|
e = encoded;
|
||||||
|
/* convert all but last 2 bytes */
|
||||||
|
for (i = len; i > 2; i -= 3, c += 3) {
|
||||||
|
*e++ = base64[(c[0] >> 2) & 0x3f];
|
||||||
|
*e++ = base64[((c[0] << 4) & 0x30) | ((c[1] >> 4) & 0x0f)];
|
||||||
|
*e++ = base64[((c[1] << 2) & 0x3c) | ((c[2] >> 6) & 0x03)];
|
||||||
|
*e++ = base64[(c[2]) & 0x3f];
|
||||||
|
}
|
||||||
|
/* handle slop at end */
|
||||||
|
if (i > 0) {
|
||||||
|
*e++ = base64[(c[0] >> 2) & 0x3f];
|
||||||
|
*e++ = base64[((c[0] << 4) & 0x30) |
|
||||||
|
(i > 1 ? ((c[1] >> 4) & 0x0f) : 0)];
|
||||||
|
*e++ = (i > 1) ? base64[(c[1] << 2) & 0x3c] : termchar;
|
||||||
|
*e++ = termchar;
|
||||||
|
}
|
||||||
|
*e = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fact_modify(const char *fact, FILE *fd, factelem *fe)
|
||||||
|
{
|
||||||
|
struct tm *t;
|
||||||
|
|
||||||
|
t = gmtime(&(fe->stat->st_mtime));
|
||||||
|
cprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact,
|
||||||
|
TM_YEAR_BASE + t->tm_year,
|
||||||
|
t->tm_mon+1, t->tm_mday,
|
||||||
|
t->tm_hour, t->tm_min, t->tm_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fact_perm(const char *fact, FILE *fd, factelem *fe)
|
||||||
|
{
|
||||||
|
int rok, wok, xok, pdirwok;
|
||||||
|
struct stat *pdir;
|
||||||
|
|
||||||
|
if (fe->stat->st_uid == geteuid()) {
|
||||||
|
rok = ((fe->stat->st_mode & S_IRUSR) != 0);
|
||||||
|
wok = ((fe->stat->st_mode & S_IWUSR) != 0);
|
||||||
|
xok = ((fe->stat->st_mode & S_IXUSR) != 0);
|
||||||
|
} else if (matchgroup(fe->stat->st_gid)) {
|
||||||
|
rok = ((fe->stat->st_mode & S_IRGRP) != 0);
|
||||||
|
wok = ((fe->stat->st_mode & S_IWGRP) != 0);
|
||||||
|
xok = ((fe->stat->st_mode & S_IXGRP) != 0);
|
||||||
|
} else {
|
||||||
|
rok = ((fe->stat->st_mode & S_IROTH) != 0);
|
||||||
|
wok = ((fe->stat->st_mode & S_IWOTH) != 0);
|
||||||
|
xok = ((fe->stat->st_mode & S_IXOTH) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
cprintf(fd, "%s=", fact);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if parent info not provided, look it up, but
|
||||||
|
* only if the current class has modify rights,
|
||||||
|
* since we only need this info in such a case.
|
||||||
|
*/
|
||||||
|
pdir = fe->pdirstat;
|
||||||
|
if (pdir == NULL && CURCLASS_FLAGS_ISSET(modify)) {
|
||||||
|
size_t len;
|
||||||
|
char realdir[MAXPATHLEN], *p;
|
||||||
|
struct stat dir;
|
||||||
|
|
||||||
|
len = strlcpy(realdir, fe->path, sizeof(realdir));
|
||||||
|
if (len < sizeof(realdir) - 4) {
|
||||||
|
if (S_ISDIR(fe->stat->st_mode))
|
||||||
|
strlcat(realdir, "/..", sizeof(realdir));
|
||||||
|
else {
|
||||||
|
/* if has a /, move back to it */
|
||||||
|
/* otherwise use '..' */
|
||||||
|
if ((p = strrchr(realdir, '/')) != NULL) {
|
||||||
|
if (p == realdir)
|
||||||
|
p++;
|
||||||
|
*p = '\0';
|
||||||
|
} else
|
||||||
|
strlcpy(realdir, "..", sizeof(realdir));
|
||||||
|
}
|
||||||
|
if (stat(realdir, &dir) == 0)
|
||||||
|
pdir = &dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pdirwok = 0;
|
||||||
|
if (pdir != NULL) {
|
||||||
|
if (pdir->st_uid == geteuid())
|
||||||
|
pdirwok = ((pdir->st_mode & S_IWUSR) != 0);
|
||||||
|
else if (matchgroup(pdir->st_gid))
|
||||||
|
pdirwok = ((pdir->st_mode & S_IWGRP) != 0);
|
||||||
|
else
|
||||||
|
pdirwok = ((pdir->st_mode & S_IWOTH) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 'a': can APPE to file */
|
||||||
|
if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode))
|
||||||
|
CPUTC('a', fd);
|
||||||
|
|
||||||
|
/* 'c': can create or append to files in directory */
|
||||||
|
if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
|
||||||
|
CPUTC('c', fd);
|
||||||
|
|
||||||
|
/* 'd': can delete file or directory */
|
||||||
|
if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) {
|
||||||
|
int candel;
|
||||||
|
|
||||||
|
candel = 1;
|
||||||
|
if (S_ISDIR(fe->stat->st_mode)) {
|
||||||
|
DIR *dirp;
|
||||||
|
struct dirent *dp;
|
||||||
|
|
||||||
|
if ((dirp = opendir(fe->display)) == NULL)
|
||||||
|
candel = 0;
|
||||||
|
else {
|
||||||
|
while ((dp = readdir(dirp)) != NULL) {
|
||||||
|
if (ISDOTDIR(dp->d_name) ||
|
||||||
|
ISDOTDOTDIR(dp->d_name))
|
||||||
|
continue;
|
||||||
|
candel = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
closedir(dirp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (candel)
|
||||||
|
CPUTC('d', fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 'e': can enter directory */
|
||||||
|
if (xok && S_ISDIR(fe->stat->st_mode))
|
||||||
|
CPUTC('e', fd);
|
||||||
|
|
||||||
|
/* 'f': can rename file or directory */
|
||||||
|
if (pdirwok && CURCLASS_FLAGS_ISSET(modify))
|
||||||
|
CPUTC('f', fd);
|
||||||
|
|
||||||
|
/* 'l': can list directory */
|
||||||
|
if (rok && xok && S_ISDIR(fe->stat->st_mode))
|
||||||
|
CPUTC('l', fd);
|
||||||
|
|
||||||
|
/* 'm': can create directory */
|
||||||
|
if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
|
||||||
|
CPUTC('m', fd);
|
||||||
|
|
||||||
|
/* 'p': can remove files in directory */
|
||||||
|
if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
|
||||||
|
CPUTC('p', fd);
|
||||||
|
|
||||||
|
/* 'r': can RETR file */
|
||||||
|
if (rok && S_ISREG(fe->stat->st_mode))
|
||||||
|
CPUTC('r', fd);
|
||||||
|
|
||||||
|
/* 'w': can STOR file */
|
||||||
|
if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode))
|
||||||
|
CPUTC('w', fd);
|
||||||
|
|
||||||
|
CPUTC(';', fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fact_size(const char *fact, FILE *fd, factelem *fe)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (S_ISREG(fe->stat->st_mode))
|
||||||
|
cprintf(fd, "%s=" LLF ";", fact, (LLT)fe->stat->st_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fact_type(const char *fact, FILE *fd, factelem *fe)
|
||||||
|
{
|
||||||
|
|
||||||
|
cprintf(fd, "%s=", fact);
|
||||||
|
switch (fe->stat->st_mode & S_IFMT) {
|
||||||
|
case S_IFDIR:
|
||||||
|
if (fe->flags & FE_MLSD) {
|
||||||
|
if ((fe->flags & FE_ISCURDIR) || ISDOTDIR(fe->display))
|
||||||
|
cprintf(fd, "cdir");
|
||||||
|
else if (ISDOTDOTDIR(fe->display))
|
||||||
|
cprintf(fd, "pdir");
|
||||||
|
else
|
||||||
|
cprintf(fd, "dir");
|
||||||
|
} else {
|
||||||
|
cprintf(fd, "dir");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case S_IFREG:
|
||||||
|
cprintf(fd, "file");
|
||||||
|
break;
|
||||||
|
case S_IFIFO:
|
||||||
|
cprintf(fd, "OS.unix=fifo");
|
||||||
|
break;
|
||||||
|
case S_IFLNK: /* XXX: probably a NO-OP with stat() */
|
||||||
|
cprintf(fd, "OS.unix=slink");
|
||||||
|
break;
|
||||||
|
case S_IFSOCK:
|
||||||
|
cprintf(fd, "OS.unix=socket");
|
||||||
|
break;
|
||||||
|
case S_IFBLK:
|
||||||
|
case S_IFCHR:
|
||||||
|
cprintf(fd, "OS.unix=%s-" ULLF "/" ULLF,
|
||||||
|
S_ISBLK(fe->stat->st_mode) ? "blk" : "chr",
|
||||||
|
(ULLT)major(fe->stat->st_rdev),
|
||||||
|
(ULLT)minor(fe->stat->st_rdev));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CPUTC(';', fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fact_unique(const char *fact, FILE *fd, factelem *fe)
|
||||||
|
{
|
||||||
|
char obuf[(sizeof(dev_t) + sizeof(ino_t) + 2) * 4 / 3 + 2];
|
||||||
|
char tbuf[sizeof(dev_t) + sizeof(ino_t)];
|
||||||
|
|
||||||
|
memcpy(tbuf,
|
||||||
|
(char *)&(fe->stat->st_dev), sizeof(dev_t));
|
||||||
|
memcpy(tbuf + sizeof(dev_t),
|
||||||
|
(char *)&(fe->stat->st_ino), sizeof(ino_t));
|
||||||
|
base64_encode(tbuf, sizeof(dev_t) + sizeof(ino_t), obuf, 1);
|
||||||
|
cprintf(fd, "%s=%s;", fact, obuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
matchgroup(gid_t gid)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < gidcount; i++)
|
||||||
|
if (gid == gidlist[i])
|
||||||
|
return(1);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mlsname(FILE *fp, factelem *fe)
|
||||||
|
{
|
||||||
|
char realfile[MAXPATHLEN];
|
||||||
|
int userf = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < FACTTABSIZE; i++) {
|
||||||
|
if (facttab[i].enabled)
|
||||||
|
(facttab[i].display)(facttab[i].name, fp, fe);
|
||||||
|
}
|
||||||
|
if ((fe->flags & FE_MLSD) &&
|
||||||
|
!(fe->flags & FE_ISCURDIR) && !ISDOTDIR(fe->display)) {
|
||||||
|
/* if MLSD and not "." entry, display as-is */
|
||||||
|
userf = 0;
|
||||||
|
} else {
|
||||||
|
/* if MLST, or MLSD and "." entry, realpath(3) it */
|
||||||
|
if (realpath(fe->display, realfile) != NULL)
|
||||||
|
userf = 1;
|
||||||
|
}
|
||||||
|
cprintf(fp, " %s\r\n", userf ? realfile : fe->display);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
replydirname(const char *name, const char *message)
|
||||||
|
{
|
||||||
|
char *p, *ep;
|
||||||
|
char npath[MAXPATHLEN * 2];
|
||||||
|
|
||||||
|
p = npath;
|
||||||
|
ep = &npath[sizeof(npath) - 1];
|
||||||
|
while (*name) {
|
||||||
|
if (*name == '"') {
|
||||||
|
if (ep - p < 2)
|
||||||
|
break;
|
||||||
|
*p++ = *name++;
|
||||||
|
*p++ = '"';
|
||||||
|
} else {
|
||||||
|
if (ep - p < 1)
|
||||||
|
break;
|
||||||
|
*p++ = *name++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*p = '\0';
|
||||||
|
reply(257, "\"%s\" %s", npath, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
discover_path(char *last_path, const char *new_path)
|
||||||
|
{
|
||||||
|
char tp[MAXPATHLEN + 1] = "";
|
||||||
|
char tq[MAXPATHLEN + 1] = "";
|
||||||
|
char *cp;
|
||||||
|
char *cq;
|
||||||
|
int sz1, sz2;
|
||||||
|
int nomorelink;
|
||||||
|
struct stat st1, st2;
|
||||||
|
|
||||||
|
if (new_path[0] != '/') {
|
||||||
|
(void)strlcpy(tp, last_path, MAXPATHLEN);
|
||||||
|
(void)strlcat(tp, "/", MAXPATHLEN);
|
||||||
|
}
|
||||||
|
(void)strlcat(tp, new_path, MAXPATHLEN);
|
||||||
|
(void)strlcat(tp, "/", MAXPATHLEN);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* resolve symlinks. A symlink may introduce another symlink, so we
|
||||||
|
* loop trying to resolve symlinks until we don't find any of them.
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
/* Collapse any // into / */
|
||||||
|
while ((cp = strstr(tp, "//")) != NULL)
|
||||||
|
(void)memmove(cp, cp + 1, strlen(cp) - 1 + 1);
|
||||||
|
|
||||||
|
/* Collapse any /./ into / */
|
||||||
|
while ((cp = strstr(tp, "/./")) != NULL)
|
||||||
|
(void)memmove(cp, cp + 2, strlen(cp) - 2 + 1);
|
||||||
|
|
||||||
|
cp = tp;
|
||||||
|
nomorelink = 1;
|
||||||
|
|
||||||
|
while ((cp = strstr(++cp, "/")) != NULL) {
|
||||||
|
sz1 = (unsigned long)cp - (unsigned long)tp;
|
||||||
|
if (sz1 > MAXPATHLEN)
|
||||||
|
goto bad;
|
||||||
|
*cp = 0;
|
||||||
|
sz2 = readlink(tp, tq, MAXPATHLEN);
|
||||||
|
*cp = '/';
|
||||||
|
|
||||||
|
/* If this is not a symlink, move to next / */
|
||||||
|
if (sz2 <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We found a symlink, so we will have to
|
||||||
|
* do one more pass to check there is no
|
||||||
|
* more symlink in the path
|
||||||
|
*/
|
||||||
|
nomorelink = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Null terminate the string and remove trailing /
|
||||||
|
*/
|
||||||
|
tq[sz2] = 0;
|
||||||
|
sz2 = strlen(tq);
|
||||||
|
if (tq[sz2 - 1] == '/')
|
||||||
|
tq[--sz2] = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is this an absolute link or a relative link?
|
||||||
|
*/
|
||||||
|
if (tq[0] == '/') {
|
||||||
|
/* absolute link */
|
||||||
|
if (strlen(cp) + sz2 > MAXPATHLEN)
|
||||||
|
goto bad;
|
||||||
|
memmove(tp + sz2, cp, strlen(cp) + 1);
|
||||||
|
memcpy(tp, tq, sz2);
|
||||||
|
} else {
|
||||||
|
/* relative link */
|
||||||
|
for (cq = cp - 1; *cq != '/'; cq--);
|
||||||
|
if (strlen(tp) -
|
||||||
|
((unsigned long)cq - (unsigned long)cp)
|
||||||
|
+ 1 + sz2 > MAXPATHLEN)
|
||||||
|
goto bad;
|
||||||
|
(void)memmove(cq + 1 + sz2,
|
||||||
|
cp, strlen(cp) + 1);
|
||||||
|
(void)memcpy(cq + 1, tq, sz2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* start over, looking for new symlinks
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (nomorelink == 0);
|
||||||
|
|
||||||
|
/* Collapse any /foo/../ into /foo/ */
|
||||||
|
while ((cp = strstr(tp, "/../")) != NULL) {
|
||||||
|
/* ^/../foo/ becomes ^/foo/ */
|
||||||
|
if (cp == tp) {
|
||||||
|
(void)memmove(cp, cp + 3,
|
||||||
|
strlen(cp) - 3 + 1);
|
||||||
|
} else {
|
||||||
|
for (cq = cp - 1; *cq != '/'; cq--);
|
||||||
|
(void)memmove(cq, cp + 3,
|
||||||
|
strlen(cp) - 3 + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* strip strailing / */
|
||||||
|
if (strlen(tp) != 1)
|
||||||
|
tp[strlen(tp) - 1] = '\0';
|
||||||
|
|
||||||
|
/* check that the path is correct */
|
||||||
|
stat(tp, &st1);
|
||||||
|
stat(".", &st2);
|
||||||
|
if ((st1.st_dev != st2.st_dev) || (st1.st_ino != st2.st_ino))
|
||||||
|
goto bad;
|
||||||
|
|
||||||
|
(void)strlcpy(last_path, tp, MAXPATHLEN);
|
||||||
|
return;
|
||||||
|
|
||||||
|
bad:
|
||||||
|
(void)strlcat(last_path, "/", MAXPATHLEN);
|
||||||
|
(void)strlcat(last_path, new_path, MAXPATHLEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
953
libexec/ftpd/conf.c
Normal file
953
libexec/ftpd/conf.c
Normal file
|
@ -0,0 +1,953 @@
|
||||||
|
/* $NetBSD: conf.c,v 1.63 2011/08/14 11:46:28 christos Exp $ */
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1997-2009 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.
|
||||||
|
*
|
||||||
|
* 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 FOUNDATION 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#ifndef lint
|
||||||
|
__RCSID("$NetBSD: conf.c,v 1.63 2011/08/14 11:46:28 christos Exp $");
|
||||||
|
#endif /* not lint */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <glob.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stringlist.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <util.h>
|
||||||
|
|
||||||
|
#ifdef KERBEROS5
|
||||||
|
#include <krb5/krb5.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
|
#include "pathnames.h"
|
||||||
|
|
||||||
|
static char *strend(const char *, char *);
|
||||||
|
static int filetypematch(char *, int);
|
||||||
|
|
||||||
|
|
||||||
|
/* class defaults */
|
||||||
|
#define DEFAULT_LIMIT -1 /* unlimited connections */
|
||||||
|
#define DEFAULT_MAXFILESIZE -1 /* unlimited file size */
|
||||||
|
#define DEFAULT_MAXTIMEOUT 7200 /* 2 hours */
|
||||||
|
#define DEFAULT_TIMEOUT 900 /* 15 minutes */
|
||||||
|
#define DEFAULT_UMASK 027 /* rw-r----- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialise curclass to an `empty' state
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
init_curclass(void)
|
||||||
|
{
|
||||||
|
struct ftpconv *conv, *cnext;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset((char *)&curclass.advertise, 0, sizeof(curclass.advertise));
|
||||||
|
curclass.advertise.su_len = 0; /* `not used' */
|
||||||
|
REASSIGN(curclass.chroot, NULL);
|
||||||
|
REASSIGN(curclass.classname, NULL);
|
||||||
|
curclass.conversions = NULL;
|
||||||
|
REASSIGN(curclass.display, NULL);
|
||||||
|
REASSIGN(curclass.homedir, NULL);
|
||||||
|
curclass.limit = DEFAULT_LIMIT;
|
||||||
|
REASSIGN(curclass.limitfile, NULL);
|
||||||
|
curclass.maxfilesize = DEFAULT_MAXFILESIZE;
|
||||||
|
curclass.maxrateget = 0;
|
||||||
|
curclass.maxrateput = 0;
|
||||||
|
curclass.maxtimeout = DEFAULT_MAXTIMEOUT;
|
||||||
|
REASSIGN(curclass.motd, ftpd_strdup(_NAME_FTPLOGINMESG));
|
||||||
|
REASSIGN(curclass.notify, NULL);
|
||||||
|
curclass.portmin = 0;
|
||||||
|
curclass.portmax = 0;
|
||||||
|
curclass.rateget = 0;
|
||||||
|
curclass.rateput = 0;
|
||||||
|
curclass.timeout = DEFAULT_TIMEOUT;
|
||||||
|
/* curclass.type is set elsewhere */
|
||||||
|
curclass.umask = DEFAULT_UMASK;
|
||||||
|
curclass.mmapsize = 0;
|
||||||
|
curclass.readsize = 0;
|
||||||
|
curclass.writesize = 0;
|
||||||
|
curclass.sendbufsize = 0;
|
||||||
|
curclass.sendlowat = 0;
|
||||||
|
|
||||||
|
CURCLASS_FLAGS_SET(checkportcmd);
|
||||||
|
CURCLASS_FLAGS_CLR(denyquick);
|
||||||
|
CURCLASS_FLAGS_CLR(hidesymlinks);
|
||||||
|
CURCLASS_FLAGS_SET(modify);
|
||||||
|
CURCLASS_FLAGS_SET(passive);
|
||||||
|
CURCLASS_FLAGS_CLR(private);
|
||||||
|
CURCLASS_FLAGS_CLR(sanenames);
|
||||||
|
CURCLASS_FLAGS_SET(upload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the configuration file, looking for the named class, and
|
||||||
|
* define curclass to contain the appropriate settings.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
parse_conf(const char *findclass)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
char *buf, *p;
|
||||||
|
size_t len;
|
||||||
|
LLT llval;
|
||||||
|
int none, match;
|
||||||
|
char *endp, errbuf[100];
|
||||||
|
char *class, *word, *arg, *template;
|
||||||
|
const char *infile;
|
||||||
|
size_t line;
|
||||||
|
struct ftpconv *conv, *cnext;
|
||||||
|
|
||||||
|
init_curclass();
|
||||||
|
REASSIGN(curclass.classname, ftpd_strdup(findclass));
|
||||||
|
/* set more guest defaults */
|
||||||
|
if (strcasecmp(findclass, "guest") == 0) {
|
||||||
|
CURCLASS_FLAGS_CLR(modify);
|
||||||
|
curclass.umask = 0707;
|
||||||
|
}
|
||||||
|
|
||||||
|
infile = conffilename(_NAME_FTPDCONF);
|
||||||
|
if ((f = fopen(infile, "r")) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
line = 0;
|
||||||
|
template = NULL;
|
||||||
|
for (;
|
||||||
|
(buf = fparseln(f, &len, &line, NULL, FPARSELN_UNESCCOMM |
|
||||||
|
FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) != NULL;
|
||||||
|
free(buf)) {
|
||||||
|
none = match = 0;
|
||||||
|
p = buf;
|
||||||
|
if (len < 1)
|
||||||
|
continue;
|
||||||
|
if (p[len - 1] == '\n')
|
||||||
|
p[--len] = '\0';
|
||||||
|
if (EMPTYSTR(p))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
NEXTWORD(p, word);
|
||||||
|
NEXTWORD(p, class);
|
||||||
|
NEXTWORD(p, arg);
|
||||||
|
if (EMPTYSTR(word) || EMPTYSTR(class))
|
||||||
|
continue;
|
||||||
|
if (strcasecmp(class, "none") == 0)
|
||||||
|
none = 1;
|
||||||
|
if (! (strcasecmp(class, findclass) == 0 ||
|
||||||
|
(template != NULL && strcasecmp(class, template) == 0) ||
|
||||||
|
none ||
|
||||||
|
strcasecmp(class, "all") == 0) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
#define CONF_FLAG(Field) \
|
||||||
|
do { \
|
||||||
|
if (none || \
|
||||||
|
(!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) \
|
||||||
|
CURCLASS_FLAGS_CLR(Field); \
|
||||||
|
else \
|
||||||
|
CURCLASS_FLAGS_SET(Field); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CONF_STRING(Field) \
|
||||||
|
do { \
|
||||||
|
if (none || EMPTYSTR(arg)) \
|
||||||
|
arg = NULL; \
|
||||||
|
else \
|
||||||
|
arg = ftpd_strdup(arg); \
|
||||||
|
REASSIGN(curclass.Field, arg); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CONF_LL(Field,Arg,Min,Max) \
|
||||||
|
do { \
|
||||||
|
if (none || EMPTYSTR(Arg)) \
|
||||||
|
goto nextline; \
|
||||||
|
llval = strsuftollx(#Field, Arg, Min, Max, \
|
||||||
|
errbuf, sizeof(errbuf)); \
|
||||||
|
if (errbuf[0]) { \
|
||||||
|
syslog(LOG_WARNING, "%s line %d: %s", \
|
||||||
|
infile, (int)line, errbuf); \
|
||||||
|
goto nextline; \
|
||||||
|
} \
|
||||||
|
curclass.Field = llval; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
/* no-op */
|
||||||
|
|
||||||
|
} else if ((strcasecmp(word, "advertise") == 0)
|
||||||
|
|| (strcasecmp(word, "advertize") == 0)) {
|
||||||
|
struct addrinfo hints, *res;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
memset((char *)&curclass.advertise, 0,
|
||||||
|
sizeof(curclass.advertise));
|
||||||
|
curclass.advertise.su_len = 0;
|
||||||
|
if (none || EMPTYSTR(arg))
|
||||||
|
continue;
|
||||||
|
res = NULL;
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
/*
|
||||||
|
* only get addresses of the family
|
||||||
|
* that we're listening on
|
||||||
|
*/
|
||||||
|
hints.ai_family = ctrl_addr.su_family;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
error = getaddrinfo(arg, "0", &hints, &res);
|
||||||
|
if (error) {
|
||||||
|
syslog(LOG_WARNING, "%s line %d: %s",
|
||||||
|
infile, (int)line, gai_strerror(error));
|
||||||
|
advertiseparsefail:
|
||||||
|
if (res)
|
||||||
|
freeaddrinfo(res);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (res->ai_next) {
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"%s line %d: multiple addresses returned for `%s'; please be more specific",
|
||||||
|
infile, (int)line, arg);
|
||||||
|
goto advertiseparsefail;
|
||||||
|
}
|
||||||
|
if (sizeof(curclass.advertise) < res->ai_addrlen || (
|
||||||
|
#ifdef INET6
|
||||||
|
res->ai_family != AF_INET6 &&
|
||||||
|
#endif
|
||||||
|
res->ai_family != AF_INET)) {
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"%s line %d: unsupported protocol %d for `%s'",
|
||||||
|
infile, (int)line, res->ai_family, arg);
|
||||||
|
goto advertiseparsefail;
|
||||||
|
}
|
||||||
|
memcpy(&curclass.advertise, res->ai_addr,
|
||||||
|
res->ai_addrlen);
|
||||||
|
curclass.advertise.su_len = res->ai_addrlen;
|
||||||
|
freeaddrinfo(res);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "checkportcmd") == 0) {
|
||||||
|
CONF_FLAG(checkportcmd);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "chroot") == 0) {
|
||||||
|
CONF_STRING(chroot);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "classtype") == 0) {
|
||||||
|
if (!none && !EMPTYSTR(arg)) {
|
||||||
|
if (strcasecmp(arg, "GUEST") == 0)
|
||||||
|
curclass.type = CLASS_GUEST;
|
||||||
|
else if (strcasecmp(arg, "CHROOT") == 0)
|
||||||
|
curclass.type = CLASS_CHROOT;
|
||||||
|
else if (strcasecmp(arg, "REAL") == 0)
|
||||||
|
curclass.type = CLASS_REAL;
|
||||||
|
else {
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"%s line %d: unknown class type `%s'",
|
||||||
|
infile, (int)line, arg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "conversion") == 0) {
|
||||||
|
char *suffix, *types, *disable, *convcmd;
|
||||||
|
|
||||||
|
if (EMPTYSTR(arg)) {
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"%s line %d: %s requires a suffix",
|
||||||
|
infile, (int)line, word);
|
||||||
|
continue; /* need a suffix */
|
||||||
|
}
|
||||||
|
NEXTWORD(p, types);
|
||||||
|
NEXTWORD(p, disable);
|
||||||
|
convcmd = p;
|
||||||
|
if (convcmd)
|
||||||
|
convcmd += strspn(convcmd, " \t");
|
||||||
|
suffix = ftpd_strdup(arg);
|
||||||
|
if (none || EMPTYSTR(types) ||
|
||||||
|
EMPTYSTR(disable) || EMPTYSTR(convcmd)) {
|
||||||
|
types = NULL;
|
||||||
|
disable = NULL;
|
||||||
|
convcmd = NULL;
|
||||||
|
} else {
|
||||||
|
types = ftpd_strdup(types);
|
||||||
|
disable = ftpd_strdup(disable);
|
||||||
|
convcmd = ftpd_strdup(convcmd);
|
||||||
|
}
|
||||||
|
for (conv = curclass.conversions; conv != NULL;
|
||||||
|
conv = conv->next) {
|
||||||
|
if (strcmp(conv->suffix, suffix) == 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 = NULL;
|
||||||
|
for (cnext = curclass.conversions;
|
||||||
|
cnext != NULL; cnext = cnext->next)
|
||||||
|
if (cnext->next == NULL)
|
||||||
|
break;
|
||||||
|
if (cnext != NULL)
|
||||||
|
cnext->next = conv;
|
||||||
|
else
|
||||||
|
curclass.conversions = conv;
|
||||||
|
}
|
||||||
|
REASSIGN(conv->suffix, suffix);
|
||||||
|
REASSIGN(conv->types, types);
|
||||||
|
REASSIGN(conv->disable, disable);
|
||||||
|
REASSIGN(conv->command, convcmd);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "denyquick") == 0) {
|
||||||
|
CONF_FLAG(denyquick);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "display") == 0) {
|
||||||
|
CONF_STRING(display);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "hidesymlinks") == 0) {
|
||||||
|
CONF_FLAG(hidesymlinks);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "homedir") == 0) {
|
||||||
|
CONF_STRING(homedir);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "limit") == 0) {
|
||||||
|
curclass.limit = DEFAULT_LIMIT;
|
||||||
|
REASSIGN(curclass.limitfile, NULL);
|
||||||
|
CONF_LL(limit, arg, -1, LLTMAX);
|
||||||
|
REASSIGN(curclass.limitfile,
|
||||||
|
EMPTYSTR(p) ? NULL : ftpd_strdup(p));
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "maxfilesize") == 0) {
|
||||||
|
curclass.maxfilesize = DEFAULT_MAXFILESIZE;
|
||||||
|
CONF_LL(maxfilesize, arg, -1, LLTMAX);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "maxtimeout") == 0) {
|
||||||
|
curclass.maxtimeout = DEFAULT_MAXTIMEOUT;
|
||||||
|
CONF_LL(maxtimeout, arg,
|
||||||
|
MIN(30, curclass.timeout), LLTMAX);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "mmapsize") == 0) {
|
||||||
|
curclass.mmapsize = 0;
|
||||||
|
CONF_LL(mmapsize, arg, 0, SSIZE_MAX);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "readsize") == 0) {
|
||||||
|
curclass.readsize = 0;
|
||||||
|
CONF_LL(readsize, arg, 0, SSIZE_MAX);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "writesize") == 0) {
|
||||||
|
curclass.writesize = 0;
|
||||||
|
CONF_LL(writesize, arg, 0, SSIZE_MAX);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "recvbufsize") == 0) {
|
||||||
|
curclass.recvbufsize = 0;
|
||||||
|
CONF_LL(recvbufsize, arg, 0, INT_MAX);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "sendbufsize") == 0) {
|
||||||
|
curclass.sendbufsize = 0;
|
||||||
|
CONF_LL(sendbufsize, arg, 0, INT_MAX);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "sendlowat") == 0) {
|
||||||
|
curclass.sendlowat = 0;
|
||||||
|
CONF_LL(sendlowat, arg, 0, INT_MAX);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "modify") == 0) {
|
||||||
|
CONF_FLAG(modify);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "motd") == 0) {
|
||||||
|
CONF_STRING(motd);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "notify") == 0) {
|
||||||
|
CONF_STRING(notify);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "passive") == 0) {
|
||||||
|
CONF_FLAG(passive);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "portrange") == 0) {
|
||||||
|
long minport, maxport;
|
||||||
|
|
||||||
|
curclass.portmin = 0;
|
||||||
|
curclass.portmax = 0;
|
||||||
|
if (none || EMPTYSTR(arg))
|
||||||
|
continue;
|
||||||
|
if (EMPTYSTR(p)) {
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"%s line %d: missing maxport argument",
|
||||||
|
infile, (int)line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
minport = strsuftollx("minport", arg, IPPORT_RESERVED,
|
||||||
|
IPPORT_ANONMAX, errbuf, sizeof(errbuf));
|
||||||
|
if (errbuf[0]) {
|
||||||
|
syslog(LOG_WARNING, "%s line %d: %s",
|
||||||
|
infile, (int)line, errbuf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
maxport = strsuftollx("maxport", p, IPPORT_RESERVED,
|
||||||
|
IPPORT_ANONMAX, errbuf, sizeof(errbuf));
|
||||||
|
if (errbuf[0]) {
|
||||||
|
syslog(LOG_WARNING, "%s line %d: %s",
|
||||||
|
infile, (int)line, errbuf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (minport >= maxport) {
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"%s line %d: minport %ld >= maxport %ld",
|
||||||
|
infile, (int)line, minport, maxport);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
curclass.portmin = (int)minport;
|
||||||
|
curclass.portmax = (int)maxport;
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "private") == 0) {
|
||||||
|
CONF_FLAG(private);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "rateget") == 0) {
|
||||||
|
curclass.maxrateget = curclass.rateget = 0;
|
||||||
|
CONF_LL(rateget, arg, 0, LLTMAX);
|
||||||
|
curclass.maxrateget = curclass.rateget;
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "rateput") == 0) {
|
||||||
|
curclass.maxrateput = curclass.rateput = 0;
|
||||||
|
CONF_LL(rateput, arg, 0, LLTMAX);
|
||||||
|
curclass.maxrateput = curclass.rateput;
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "sanenames") == 0) {
|
||||||
|
CONF_FLAG(sanenames);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "timeout") == 0) {
|
||||||
|
curclass.timeout = DEFAULT_TIMEOUT;
|
||||||
|
CONF_LL(timeout, arg, 30, curclass.maxtimeout);
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "template") == 0) {
|
||||||
|
if (none)
|
||||||
|
continue;
|
||||||
|
REASSIGN(template, EMPTYSTR(arg) ? NULL : ftpd_strdup(arg));
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "umask") == 0) {
|
||||||
|
unsigned long fumask;
|
||||||
|
|
||||||
|
curclass.umask = DEFAULT_UMASK;
|
||||||
|
if (none || EMPTYSTR(arg))
|
||||||
|
continue;
|
||||||
|
errno = 0;
|
||||||
|
endp = NULL;
|
||||||
|
fumask = strtoul(arg, &endp, 8);
|
||||||
|
if (errno || *arg == '\0' || *endp != '\0' ||
|
||||||
|
fumask > 0777) {
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"%s line %d: invalid umask %s",
|
||||||
|
infile, (int)line, arg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
curclass.umask = (mode_t)fumask;
|
||||||
|
|
||||||
|
} else if (strcasecmp(word, "upload") == 0) {
|
||||||
|
CONF_FLAG(upload);
|
||||||
|
if (! CURCLASS_FLAGS_ISSET(upload))
|
||||||
|
CURCLASS_FLAGS_CLR(modify);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"%s line %d: unknown directive '%s'",
|
||||||
|
infile, (int)line, word);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nextline:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
REASSIGN(template, NULL);
|
||||||
|
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 prefix `code' + "-".
|
||||||
|
* If code == -1, flush the internal cache of directory names and return.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
show_chdir_messages(int code)
|
||||||
|
{
|
||||||
|
static StringList *slist = NULL;
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
struct tm *t;
|
||||||
|
glob_t gl;
|
||||||
|
time_t now, then;
|
||||||
|
int age;
|
||||||
|
char curwd[MAXPATHLEN];
|
||||||
|
char *cp, **rlist;
|
||||||
|
|
||||||
|
if (code == -1) {
|
||||||
|
if (slist != NULL)
|
||||||
|
sl_free(slist, 1);
|
||||||
|
slist = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quietmessages)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Setup list for directory cache */
|
||||||
|
if (slist == NULL)
|
||||||
|
slist = sl_init();
|
||||||
|
if (slist == NULL) {
|
||||||
|
syslog(LOG_WARNING, "can't allocate memory for stringlist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if this directory has already been visited */
|
||||||
|
if (getcwd(curwd, sizeof(curwd) - 1) == NULL) {
|
||||||
|
syslog(LOG_WARNING, "can't getcwd: %s", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sl_find(slist, curwd) != NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cp = ftpd_strdup(curwd);
|
||||||
|
if (sl_add(slist, cp) == -1)
|
||||||
|
syslog(LOG_WARNING, "can't add `%s' to stringlist", cp);
|
||||||
|
|
||||||
|
/* First check for a display file */
|
||||||
|
(void)display_file(curclass.display, code);
|
||||||
|
|
||||||
|
/* Now see if there are any notify files */
|
||||||
|
if (EMPTYSTR(curclass.notify))
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(&gl, 0, sizeof(gl));
|
||||||
|
if (glob(curclass.notify, GLOB_BRACE|GLOB_LIMIT, NULL, &gl) != 0
|
||||||
|
|| gl.gl_matchc == 0) {
|
||||||
|
globfree(&gl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
time(&now);
|
||||||
|
for (rlist = gl.gl_pathv; *rlist != NULL; rlist++) {
|
||||||
|
if (stat(*rlist, &st) != 0)
|
||||||
|
continue;
|
||||||
|
if (!S_ISREG(st.st_mode))
|
||||||
|
continue;
|
||||||
|
then = st.st_mtime;
|
||||||
|
if (code != 0) {
|
||||||
|
reply(-code, "%s", "");
|
||||||
|
code = 0;
|
||||||
|
}
|
||||||
|
reply(-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;
|
||||||
|
reply(-code, " it was last modified on %.24s - %d day%s ago",
|
||||||
|
ctime(&then), age, PLURAL(age));
|
||||||
|
}
|
||||||
|
globfree(&gl);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
display_file(const char *file, int code)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
char *buf, *p;
|
||||||
|
char curwd[MAXPATHLEN];
|
||||||
|
size_t len;
|
||||||
|
off_t lastnum;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
lastnum = 0;
|
||||||
|
if (quietmessages)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
if (EMPTYSTR(file))
|
||||||
|
return(0);
|
||||||
|
if ((f = fopen(file, "r")) == NULL)
|
||||||
|
return (0);
|
||||||
|
reply(-code, "%s", "");
|
||||||
|
|
||||||
|
for (;
|
||||||
|
(buf = fparseln(f, &len, NULL, "\0\0\0", 0)) != NULL; free(buf)) {
|
||||||
|
if (len > 0)
|
||||||
|
if (buf[len - 1] == '\n')
|
||||||
|
buf[--len] = '\0';
|
||||||
|
cprintf(stdout, " ");
|
||||||
|
|
||||||
|
for (p = buf; *p; p++) {
|
||||||
|
if (*p == '%') {
|
||||||
|
p++;
|
||||||
|
switch (*p) {
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
cprintf(stdout, "%s",
|
||||||
|
curclass.classname ?
|
||||||
|
curclass.classname : "<unknown>");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'C':
|
||||||
|
if (getcwd(curwd, sizeof(curwd)-1)
|
||||||
|
== NULL){
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"can't getcwd: %s",
|
||||||
|
strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cprintf(stdout, "%s", curwd);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'E':
|
||||||
|
if (! EMPTYSTR(emailaddr))
|
||||||
|
cprintf(stdout, "%s",
|
||||||
|
emailaddr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'L':
|
||||||
|
cprintf(stdout, "%s", hostname);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'M':
|
||||||
|
if (curclass.limit == -1) {
|
||||||
|
cprintf(stdout, "unlimited");
|
||||||
|
lastnum = 0;
|
||||||
|
} else {
|
||||||
|
cprintf(stdout, LLF,
|
||||||
|
(LLT)curclass.limit);
|
||||||
|
lastnum = curclass.limit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'N':
|
||||||
|
cprintf(stdout, "%d", connections);
|
||||||
|
lastnum = connections;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'R':
|
||||||
|
cprintf(stdout, "%s", remotehost);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
if (lastnum != 1)
|
||||||
|
cprintf(stdout, "s");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
if (lastnum != 1)
|
||||||
|
cprintf(stdout, "S");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'T':
|
||||||
|
now = time(NULL);
|
||||||
|
cprintf(stdout, "%.24s", ctime(&now));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'U':
|
||||||
|
cprintf(stdout, "%s",
|
||||||
|
pw ? pw->pw_name : "<unknown>");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '%':
|
||||||
|
CPUTC('%', stdout);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
CPUTC(*p, stdout);
|
||||||
|
}
|
||||||
|
cprintf(stdout, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)fflush(stdout);
|
||||||
|
(void)fclose(f);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse src, expanding '%' escapes, into dst (which must be at least
|
||||||
|
* MAXPATHLEN long).
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
format_path(char *dst, const char *src)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
dst[0] = '\0';
|
||||||
|
len = 0;
|
||||||
|
if (src == NULL)
|
||||||
|
return;
|
||||||
|
for (p = src; *p && len < MAXPATHLEN; p++) {
|
||||||
|
if (*p == '%') {
|
||||||
|
p++;
|
||||||
|
switch (*p) {
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
len += strlcpy(dst + len, curclass.classname,
|
||||||
|
MAXPATHLEN - len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
len += strlcpy(dst + len, pw->pw_dir,
|
||||||
|
MAXPATHLEN - len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'u':
|
||||||
|
len += strlcpy(dst + len, pw->pw_name,
|
||||||
|
MAXPATHLEN - len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '%':
|
||||||
|
dst[len++] = '%';
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
dst[len++] = *p;
|
||||||
|
}
|
||||||
|
if (len < MAXPATHLEN)
|
||||||
|
dst[len] = '\0';
|
||||||
|
dst[MAXPATHLEN - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find s2 at the end of s1. If found, return a string up to (but
|
||||||
|
* not including) s2, otherwise returns NULL.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
strend(const char *s1, char *s2)
|
||||||
|
{
|
||||||
|
static char buf[MAXPATHLEN];
|
||||||
|
|
||||||
|
char *start;
|
||||||
|
size_t l1, l2;
|
||||||
|
|
||||||
|
l1 = strlen(s1);
|
||||||
|
l2 = strlen(s2);
|
||||||
|
|
||||||
|
if (l2 >= l1 || l1 >= sizeof(buf))
|
||||||
|
return(NULL);
|
||||||
|
|
||||||
|
strlcpy(buf, s1, sizeof(buf));
|
||||||
|
start = buf + (l1 - l2);
|
||||||
|
|
||||||
|
if (strcmp(start, s2) == 0) {
|
||||||
|
*start = '\0';
|
||||||
|
return(buf);
|
||||||
|
} else
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
filetypematch(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...
|
||||||
|
*/
|
||||||
|
const char **
|
||||||
|
do_conversion(const char *fname)
|
||||||
|
{
|
||||||
|
struct ftpconv *cp;
|
||||||
|
struct stat st;
|
||||||
|
int o_errno;
|
||||||
|
char *base = NULL;
|
||||||
|
char *cmd, *p, *lp;
|
||||||
|
char **argv;
|
||||||
|
StringList *sl;
|
||||||
|
|
||||||
|
o_errno = errno;
|
||||||
|
sl = NULL;
|
||||||
|
cmd = NULL;
|
||||||
|
for (cp = curclass.conversions; cp != NULL; cp = cp->next) {
|
||||||
|
if (cp->suffix == NULL) {
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"cp->suffix==NULL in conv list; SHOULDN'T HAPPEN!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((base = strend(fname, cp->suffix)) == NULL)
|
||||||
|
continue;
|
||||||
|
if (cp->types == NULL || cp->disable == 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)
|
||||||
|
goto cleanup_do_conv;
|
||||||
|
|
||||||
|
/* Split up command into an argv */
|
||||||
|
if ((sl = sl_init()) == NULL)
|
||||||
|
goto cleanup_do_conv;
|
||||||
|
cmd = ftpd_strdup(cp->command);
|
||||||
|
p = cmd;
|
||||||
|
while (p) {
|
||||||
|
NEXTWORD(p, lp);
|
||||||
|
if (strcmp(lp, "%s") == 0)
|
||||||
|
lp = base;
|
||||||
|
if (sl_add(sl, ftpd_strdup(lp)) == -1)
|
||||||
|
goto cleanup_do_conv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sl_add(sl, NULL) == -1)
|
||||||
|
goto cleanup_do_conv;
|
||||||
|
argv = sl->sl_str;
|
||||||
|
free(cmd);
|
||||||
|
free(sl);
|
||||||
|
return (void *)(intptr_t)argv;
|
||||||
|
|
||||||
|
cleanup_do_conv:
|
||||||
|
if (sl)
|
||||||
|
sl_free(sl, 1);
|
||||||
|
free(cmd);
|
||||||
|
errno = o_errno;
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Count the number of current connections, reading from
|
||||||
|
* /var/run/ftpd.pids-<class>
|
||||||
|
* Does a kill -0 on each pid in that file, and only counts
|
||||||
|
* processes that exist (or frees the slot if it doesn't).
|
||||||
|
* Adds getpid() to the first free slot. Truncates the file
|
||||||
|
* if possible.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
count_users(void)
|
||||||
|
{
|
||||||
|
char fn[MAXPATHLEN];
|
||||||
|
int fd;
|
||||||
|
size_t i, last, count;
|
||||||
|
ssize_t scount;
|
||||||
|
pid_t *pids, mypid;
|
||||||
|
struct stat sb;
|
||||||
|
struct flock fl;
|
||||||
|
|
||||||
|
(void)strlcpy(fn, _PATH_CLASSPIDS, sizeof(fn));
|
||||||
|
(void)strlcat(fn, curclass.classname, sizeof(fn));
|
||||||
|
pids = NULL;
|
||||||
|
connections = 1;
|
||||||
|
fl.l_start = 0;
|
||||||
|
fl.l_len = 0;
|
||||||
|
fl.l_pid = 0;
|
||||||
|
fl.l_type = F_WRLCK;
|
||||||
|
fl.l_whence = SEEK_SET;
|
||||||
|
|
||||||
|
if ((fd = open(fn, O_RDWR | O_CREAT, 0600)) == -1)
|
||||||
|
return;
|
||||||
|
if (fcntl(fd, F_SETLK, &fl) == -1)
|
||||||
|
goto cleanup_count;
|
||||||
|
if (fstat(fd, &sb) == -1)
|
||||||
|
goto cleanup_count;
|
||||||
|
if ((pids = malloc(sb.st_size + sizeof(pid_t))) == NULL)
|
||||||
|
goto cleanup_count;
|
||||||
|
/* XXX: implement a better read loop */
|
||||||
|
scount = read(fd, pids, sb.st_size);
|
||||||
|
if (scount == -1 || scount != sb.st_size || scount < 0)
|
||||||
|
goto cleanup_count;
|
||||||
|
count = (size_t)scount / sizeof(pid_t);
|
||||||
|
mypid = getpid();
|
||||||
|
last = 0;
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (pids[i] == 0)
|
||||||
|
continue;
|
||||||
|
if (kill(pids[i], 0) == -1 && errno != EPERM) {
|
||||||
|
if (mypid != 0) {
|
||||||
|
pids[i] = mypid;
|
||||||
|
mypid = 0;
|
||||||
|
last = i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
connections++;
|
||||||
|
last = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mypid != 0) {
|
||||||
|
if (pids[last] != 0)
|
||||||
|
last++;
|
||||||
|
pids[last] = mypid;
|
||||||
|
}
|
||||||
|
count = (last + 1) * sizeof(pid_t);
|
||||||
|
if (lseek(fd, 0, SEEK_SET) == -1)
|
||||||
|
goto cleanup_count;
|
||||||
|
/* XXX: implement a better write loop */
|
||||||
|
scount = write(fd, pids, count);
|
||||||
|
if (scount == -1 || (size_t)scount != count)
|
||||||
|
goto cleanup_count;
|
||||||
|
(void)ftruncate(fd, count);
|
||||||
|
|
||||||
|
cleanup_count:
|
||||||
|
fl.l_type = F_UNLCK;
|
||||||
|
(void)fcntl(fd, F_SETLK, &fl);
|
||||||
|
close(fd);
|
||||||
|
REASSIGN(pids, NULL);
|
||||||
|
}
|
380
libexec/ftpd/extern.h
Normal file
380
libexec/ftpd/extern.h
Normal file
|
@ -0,0 +1,380 @@
|
||||||
|
/* $NetBSD: extern.h,v 1.62 2011/08/29 20:41:06 joerg Exp $ */
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1992, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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.
|
||||||
|
*
|
||||||
|
* @(#)extern.h 8.2 (Berkeley) 4/4/94
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
* by 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.
|
||||||
|
*
|
||||||
|
* 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 FOUNDATION 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 1997 and 1998 WIDE Project.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* 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. Neither the name of the project 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 PROJECT 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 PROJECT 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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)
|
||||||
|
# define LLTMIN LONG_MIN
|
||||||
|
# define LLTMAX LONG_MAX
|
||||||
|
#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)
|
||||||
|
# define LLTMIN LLONG_MIN
|
||||||
|
# define LLTMAX LLONG_MAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FTP_BUFLEN 512
|
||||||
|
|
||||||
|
void abor(void);
|
||||||
|
void closedataconn(FILE *);
|
||||||
|
char *conffilename(const char *);
|
||||||
|
void count_users(void);
|
||||||
|
void cprintf(FILE *, const char *, ...)
|
||||||
|
__attribute__((__format__(__printf__, 2, 3)));
|
||||||
|
void cwd(const char *);
|
||||||
|
FILE *dataconn(const char *, off_t, const char *);
|
||||||
|
void delete(const char *);
|
||||||
|
int display_file(const char *, int);
|
||||||
|
const char **do_conversion(const char *);
|
||||||
|
__dead void dologout(int);
|
||||||
|
__dead void fatal(const char *);
|
||||||
|
void feat(void);
|
||||||
|
void format_path(char *, const char *);
|
||||||
|
int ftpd_pclose(FILE *);
|
||||||
|
FILE *ftpd_popen(const char *[], const char *, int);
|
||||||
|
int get_line(char *, int, FILE *);
|
||||||
|
void init_curclass(void);
|
||||||
|
void logxfer(const char *, off_t, const char *, const char *,
|
||||||
|
const struct timeval *, const char *);
|
||||||
|
struct tab *lookup(struct tab *, const char *);
|
||||||
|
void makedir(const char *);
|
||||||
|
void mlsd(const char *);
|
||||||
|
void mlst(const char *);
|
||||||
|
void opts(const char *);
|
||||||
|
void parse_conf(const char *);
|
||||||
|
void pass(const char *);
|
||||||
|
void passive(void);
|
||||||
|
int lpsvproto2af(int);
|
||||||
|
int af2lpsvproto(int);
|
||||||
|
int epsvproto2af(int);
|
||||||
|
int af2epsvproto(int);
|
||||||
|
void long_passive(const char *, int);
|
||||||
|
int extended_port(const char *);
|
||||||
|
void epsv_protounsupp(const char *);
|
||||||
|
void perror_reply(int, const char *);
|
||||||
|
void pwd(void);
|
||||||
|
void removedir(const char *);
|
||||||
|
void renamecmd(const char *, const char *);
|
||||||
|
char *renamefrom(const char *);
|
||||||
|
void reply(int, const char *, ...)
|
||||||
|
__attribute__((__format__(__printf__, 2, 3)));
|
||||||
|
void retrieve(const char *[], const char *);
|
||||||
|
void send_file_list(const char *);
|
||||||
|
void show_chdir_messages(int);
|
||||||
|
void sizecmd(const char *);
|
||||||
|
void statcmd(void);
|
||||||
|
void statfilecmd(const char *);
|
||||||
|
void statxfer(void);
|
||||||
|
void store(const char *, const char *, int);
|
||||||
|
void user(const char *);
|
||||||
|
char *ftpd_strdup(const char *);
|
||||||
|
void yyerror(const char *);
|
||||||
|
|
||||||
|
#ifdef SUPPORT_UTMP
|
||||||
|
struct utmp;
|
||||||
|
|
||||||
|
void ftpd_initwtmp(void);
|
||||||
|
void ftpd_logwtmp(const char *, const char *, const char *);
|
||||||
|
void ftpd_login(const struct utmp *);
|
||||||
|
int ftpd_logout(const char *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SUPPORT_UTMPX
|
||||||
|
struct utmpx;
|
||||||
|
struct sockinet;
|
||||||
|
|
||||||
|
void ftpd_initwtmpx(void);
|
||||||
|
void ftpd_logwtmpx(const char *, const char *, const char *,
|
||||||
|
struct sockinet *, int, int);
|
||||||
|
void ftpd_loginx(const struct utmpx *);
|
||||||
|
int ftpd_logoutx(const char *, int, int);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#if defined(__NetBSD__)
|
||||||
|
# define HAVE_SETPROCTITLE 1
|
||||||
|
# define HAVE_STRUCT_SOCKADDR_SA_LEN 1
|
||||||
|
# define HAVE_SOCKADDR_SNPRINTF 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct sockinet {
|
||||||
|
union sockunion {
|
||||||
|
struct sockaddr_in su_sin;
|
||||||
|
#ifdef INET6
|
||||||
|
struct sockaddr_in6 su_sin6;
|
||||||
|
#endif
|
||||||
|
} si_su;
|
||||||
|
#if !defined(HAVE_STRUCT_SOCKADDR_SA_LEN)
|
||||||
|
int si_len;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(HAVE_STRUCT_SOCKADDR_SA_LEN)
|
||||||
|
# define su_len si_len
|
||||||
|
#else
|
||||||
|
# define su_len si_su.su_sin.sin_len
|
||||||
|
#endif
|
||||||
|
#define su_addr si_su.su_sin.sin_addr
|
||||||
|
#define su_family si_su.su_sin.sin_family
|
||||||
|
#define su_port si_su.su_sin.sin_port
|
||||||
|
#ifdef INET6
|
||||||
|
# define su_6addr si_su.su_sin6.sin6_addr
|
||||||
|
# define su_scope_id si_su.su_sin6.sin6_scope_id
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct tab {
|
||||||
|
const char *name;
|
||||||
|
short token;
|
||||||
|
short state;
|
||||||
|
short flags; /* 1 if command implemented, 2 if has options,
|
||||||
|
4 if can occur OOB */
|
||||||
|
const char *help;
|
||||||
|
char *options;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ftpconv {
|
||||||
|
struct ftpconv *next;
|
||||||
|
char *suffix; /* Suffix of requested name */
|
||||||
|
char *types; /* Valid file types */
|
||||||
|
char *disable; /* File to disable conversions */
|
||||||
|
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_denyquick = 1<<1, /* Check ftpusers(5) before PASS */
|
||||||
|
FLAG_hidesymlinks = 1<<2, /* For symbolic links, list the file
|
||||||
|
or directory the link references
|
||||||
|
rather than the link itself */
|
||||||
|
FLAG_modify = 1<<3, /* Allow CHMOD, DELE, MKD, RMD, RNFR,
|
||||||
|
UMASK */
|
||||||
|
FLAG_passive = 1<<4, /* Allow PASV mode */
|
||||||
|
FLAG_private = 1<<5, /* Don't publish class info in STAT */
|
||||||
|
FLAG_sanenames = 1<<6, /* Restrict names of uploaded files */
|
||||||
|
FLAG_upload = 1<<7, /* 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 sockinet advertise; /* PASV address to advertise as */
|
||||||
|
char *chroot; /* Directory to chroot(2) to at login */
|
||||||
|
char *classname; /* Current class */
|
||||||
|
struct ftpconv *conversions; /* List of conversions */
|
||||||
|
char *display; /* File to display upon chdir */
|
||||||
|
char *homedir; /* Directory to chdir(2) to at login */
|
||||||
|
classflag_t flags; /* Flags; see classflag_t above */
|
||||||
|
LLT limit; /* Max connections (-1 = unlimited) */
|
||||||
|
char *limitfile; /* File to display if limit reached */
|
||||||
|
LLT maxfilesize; /* Maximum file size of uploads */
|
||||||
|
LLT maxrateget; /* Maximum get transfer rate throttle */
|
||||||
|
LLT maxrateput; /* Maximum put transfer rate throttle */
|
||||||
|
LLT maxtimeout; /* Maximum permitted timeout */
|
||||||
|
char *motd; /* MotD file to display after login */
|
||||||
|
char *notify; /* Files to notify about upon chdir */
|
||||||
|
LLT portmin; /* Minumum port for passive mode */
|
||||||
|
LLT portmax; /* Maximum port for passive mode */
|
||||||
|
LLT rateget; /* Get (RETR) transfer rate throttle */
|
||||||
|
LLT rateput; /* Put (STOR) transfer rate throttle */
|
||||||
|
LLT timeout; /* Default timeout */
|
||||||
|
class_ft type; /* Class type */
|
||||||
|
mode_t umask; /* Umask to use */
|
||||||
|
LLT mmapsize; /* mmap window size */
|
||||||
|
LLT readsize; /* data read size */
|
||||||
|
LLT writesize; /* data write size */
|
||||||
|
LLT recvbufsize; /* SO_RCVBUF size */
|
||||||
|
LLT sendbufsize; /* SO_SNDBUF size */
|
||||||
|
LLT sendlowat; /* SO_SNDLOWAT size */
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void ftp_loop(void) __attribute__ ((noreturn));
|
||||||
|
extern void ftp_handle_line(char *);
|
||||||
|
|
||||||
|
#ifndef GLOBAL
|
||||||
|
#define GLOBAL extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
GLOBAL struct sockinet ctrl_addr;
|
||||||
|
GLOBAL struct sockinet data_dest;
|
||||||
|
GLOBAL struct sockinet data_source;
|
||||||
|
GLOBAL struct sockinet his_addr;
|
||||||
|
GLOBAL struct sockinet pasv_addr;
|
||||||
|
GLOBAL int connections;
|
||||||
|
GLOBAL struct ftpclass curclass;
|
||||||
|
GLOBAL int ftpd_debug;
|
||||||
|
GLOBAL char *emailaddr;
|
||||||
|
GLOBAL int form;
|
||||||
|
GLOBAL int gidcount; /* number of entries in gidlist[] */
|
||||||
|
GLOBAL gid_t *gidlist;
|
||||||
|
GLOBAL int hasyyerrored;
|
||||||
|
GLOBAL char hostname[MAXHOSTNAMELEN+1];
|
||||||
|
GLOBAL char homedir[MAXPATHLEN];
|
||||||
|
#ifdef KERBEROS5
|
||||||
|
GLOBAL krb5_context kcontext;
|
||||||
|
#endif
|
||||||
|
GLOBAL int logged_in;
|
||||||
|
GLOBAL int logging;
|
||||||
|
GLOBAL int pdata; /* for passive mode */
|
||||||
|
#if defined(HAVE_SETPROCTITLE)
|
||||||
|
GLOBAL char proctitle[BUFSIZ]; /* initial part of title */
|
||||||
|
#endif
|
||||||
|
GLOBAL struct passwd *pw;
|
||||||
|
GLOBAL int quietmessages;
|
||||||
|
GLOBAL char remotehost[MAXHOSTNAMELEN+1];
|
||||||
|
GLOBAL char remoteloghost[2 * MAXHOSTNAMELEN+1];
|
||||||
|
GLOBAL off_t restart_point;
|
||||||
|
GLOBAL char tmpline[FTP_BUFLEN];
|
||||||
|
GLOBAL int type;
|
||||||
|
GLOBAL int usedefault; /* for data transfers */
|
||||||
|
GLOBAL const char *version;
|
||||||
|
GLOBAL int is_oob;
|
||||||
|
|
||||||
|
/* total file data bytes */
|
||||||
|
GLOBAL off_t total_data_in, total_data_out, total_data;
|
||||||
|
/* total number of data files */
|
||||||
|
GLOBAL off_t total_files_in, total_files_out, total_files;
|
||||||
|
/* total bytes */
|
||||||
|
GLOBAL off_t total_bytes_in, total_bytes_out, total_bytes;
|
||||||
|
/* total number of xfers */
|
||||||
|
GLOBAL off_t total_xfers_in, total_xfers_out, total_xfers;
|
||||||
|
|
||||||
|
extern struct tab cmdtab[];
|
||||||
|
|
||||||
|
#define INTERNAL_LS "/bin/ls"
|
||||||
|
|
||||||
|
|
||||||
|
#define CMD_IMPLEMENTED(x) ((x)->flags != 0)
|
||||||
|
#define CMD_HAS_OPTIONS(x) ((x)->flags & 0x2)
|
||||||
|
#define CMD_OOB(x) ((x)->flags & 0x4)
|
||||||
|
|
||||||
|
#define CPUTC(c, f) do { \
|
||||||
|
putc(c, f); total_bytes++; total_bytes_out++; \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#define CURCLASSTYPE curclass.type == CLASS_GUEST ? "GUEST" : \
|
||||||
|
curclass.type == CLASS_CHROOT ? "CHROOT" : \
|
||||||
|
curclass.type == CLASS_REAL ? "REAL" : \
|
||||||
|
"<unknown>"
|
||||||
|
|
||||||
|
#define ISDOTDIR(x) (x[0] == '.' && x[1] == '\0')
|
||||||
|
#define ISDOTDOTDIR(x) (x[0] == '.' && x[1] == '.' && x[2] == '\0')
|
||||||
|
|
||||||
|
#define EMPTYSTR(p) ((p) == NULL || *(p) == '\0')
|
||||||
|
#define NEXTWORD(P, W) do { \
|
||||||
|
(W) = strsep(&(P), " \t"); \
|
||||||
|
} while ((W) != NULL && *(W) == '\0')
|
||||||
|
#define PLURAL(s) ((s) == 1 ? "" : "s")
|
||||||
|
#define REASSIGN(X,Y) do { if (X) free(X); (X)=(Y); } while (/*CONSTCOND*/0)
|
||||||
|
|
||||||
|
#ifndef IPPORT_ANONMAX
|
||||||
|
# define IPPORT_ANONMAX 65535
|
||||||
|
#endif
|
2578
libexec/ftpd/ftpcmd.c
Normal file
2578
libexec/ftpd/ftpcmd.c
Normal file
File diff suppressed because it is too large
Load diff
1875
libexec/ftpd/ftpcmd.y
Normal file
1875
libexec/ftpd/ftpcmd.y
Normal file
File diff suppressed because it is too large
Load diff
871
libexec/ftpd/ftpd.8
Normal file
871
libexec/ftpd/ftpd.8
Normal file
|
@ -0,0 +1,871 @@
|
||||||
|
.\" $NetBSD: ftpd.8,v 1.85 2009/05/01 10:53:27 wiz Exp $
|
||||||
|
.\"
|
||||||
|
.\" Copyright (c) 1997-2008 The NetBSD Foundation, Inc.
|
||||||
|
.\" All rights reserved.
|
||||||
|
.\"
|
||||||
|
.\" This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
.\" by 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.
|
||||||
|
.\"
|
||||||
|
.\" 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 FOUNDATION 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.
|
||||||
|
.\"
|
||||||
|
.\" Copyright (c) 1985, 1988, 1991, 1993
|
||||||
|
.\" The Regents of the University of California. All rights reserved.
|
||||||
|
.\"
|
||||||
|
.\" 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. Neither the name of the University nor the names of its contributors
|
||||||
|
.\" may be used to endorse or promote products derived from this software
|
||||||
|
.\" without specific prior written permission.
|
||||||
|
.\"
|
||||||
|
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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.
|
||||||
|
.\"
|
||||||
|
.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94
|
||||||
|
.\"
|
||||||
|
.Dd May 1, 2009
|
||||||
|
.Dt FTPD 8
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm ftpd
|
||||||
|
.Nd
|
||||||
|
Internet File Transfer Protocol server
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl 46DdHlnQqrsUuWwX
|
||||||
|
.Op Fl a Ar anondir
|
||||||
|
.Op Fl C Ar user Ns Op @ Ns Ar host
|
||||||
|
.Op Fl c Ar confdir
|
||||||
|
.Op Fl e Ar emailaddr
|
||||||
|
.Op Fl h Ar hostname
|
||||||
|
.Op Fl L Ar xferlogfile
|
||||||
|
.Op Fl P Ar dataport
|
||||||
|
.Op Fl V Ar version
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
is the Internet File Transfer Protocol server process.
|
||||||
|
The server uses the
|
||||||
|
.Tn TCP
|
||||||
|
protocol and listens at the port specified in the
|
||||||
|
.Dq ftp
|
||||||
|
service specification; see
|
||||||
|
.Xr services 5 .
|
||||||
|
.Pp
|
||||||
|
Available options:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl 4
|
||||||
|
When
|
||||||
|
.Fl D
|
||||||
|
is specified, bind to IPv4 addresses only.
|
||||||
|
.It Fl 6
|
||||||
|
When
|
||||||
|
.Fl D
|
||||||
|
is specified, bind to IPv6 addresses only.
|
||||||
|
.It Fl a Ar anondir
|
||||||
|
Define
|
||||||
|
.Ar anondir
|
||||||
|
as the directory to
|
||||||
|
.Xr chroot 2
|
||||||
|
into for anonymous logins.
|
||||||
|
Default is the home directory for the ftp user.
|
||||||
|
This can also be specified with the
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
.Sy chroot
|
||||||
|
directive.
|
||||||
|
.It Fl C Ar user Ns Op @ Ns Ar host
|
||||||
|
Check whether
|
||||||
|
.Ar user
|
||||||
|
.Po
|
||||||
|
as if connecting from
|
||||||
|
.Ar host ,
|
||||||
|
if provided
|
||||||
|
.Pc
|
||||||
|
would be granted access under
|
||||||
|
the restrictions given in
|
||||||
|
.Xr ftpusers 5 ,
|
||||||
|
and exit without attempting a connection.
|
||||||
|
.Nm
|
||||||
|
exits with an exit code of 0 if access would be granted, or 1 otherwise.
|
||||||
|
This can be useful for testing configurations.
|
||||||
|
.It Fl c Ar confdir
|
||||||
|
Change the root directory of the configuration files from
|
||||||
|
.Dq Pa /etc
|
||||||
|
to
|
||||||
|
.Ar confdir .
|
||||||
|
This changes the directory for the following files:
|
||||||
|
.Pa /etc/ftpchroot ,
|
||||||
|
.Pa /etc/ftpusers ,
|
||||||
|
.Pa /etc/ftpwelcome ,
|
||||||
|
.Pa /etc/motd ,
|
||||||
|
and the file specified by the
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
.Sy limit
|
||||||
|
directive.
|
||||||
|
.It Fl D
|
||||||
|
Run as daemon.
|
||||||
|
.Nm
|
||||||
|
will listen on the default FTP port for incoming connections
|
||||||
|
and fork a child for each connection.
|
||||||
|
This is lower overhead than starting
|
||||||
|
.Nm
|
||||||
|
from
|
||||||
|
.Xr inetd 8
|
||||||
|
and thus might be useful on busy servers to reduce load.
|
||||||
|
.It Fl d
|
||||||
|
Debugging information is written to the syslog using a facility of
|
||||||
|
.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
|
||||||
|
Equivalent to
|
||||||
|
.Do
|
||||||
|
-h
|
||||||
|
`hostname`
|
||||||
|
.Dc .
|
||||||
|
.It Fl h Ar hostname
|
||||||
|
Explicitly set the hostname to advertise as to
|
||||||
|
.Ar hostname .
|
||||||
|
The default is the hostname associated with the IP address that
|
||||||
|
.Nm
|
||||||
|
is listening on.
|
||||||
|
This ability (with or without
|
||||||
|
.Fl h ) ,
|
||||||
|
in conjunction with
|
||||||
|
.Fl c Ar confdir ,
|
||||||
|
is useful when configuring
|
||||||
|
.Sq virtual
|
||||||
|
.Tn FTP
|
||||||
|
servers, each listening on separate addresses as separate names.
|
||||||
|
Refer to
|
||||||
|
.Xr inetd.conf 5
|
||||||
|
for more information on starting services to listen on specific IP addresses.
|
||||||
|
.It Fl L Ar xferlogfile
|
||||||
|
Log
|
||||||
|
.Tn wu-ftpd
|
||||||
|
style
|
||||||
|
.Sq xferlog
|
||||||
|
entries to
|
||||||
|
.Ar xferlogfile .
|
||||||
|
.It Fl l
|
||||||
|
Each successful and failed
|
||||||
|
.Tn FTP
|
||||||
|
session is logged using syslog with a facility of
|
||||||
|
.Dv LOG_FTP .
|
||||||
|
If this option is specified more than once, the retrieve (get), store (put),
|
||||||
|
append, delete, make directory, remove directory and rename operations and
|
||||||
|
their file name arguments are also logged.
|
||||||
|
.It Fl n
|
||||||
|
Don't attempt translation of IP addresses to hostnames.
|
||||||
|
.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
|
||||||
|
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 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 r
|
||||||
|
Permanently drop root privileges once the user is logged in.
|
||||||
|
The use of this option may result in the server using a port other
|
||||||
|
than the (listening-port - 1) for
|
||||||
|
.Sy PORT
|
||||||
|
style commands, which is contrary to the
|
||||||
|
.Cm RFC 959
|
||||||
|
specification, but in practice very few clients rely upon this behaviour.
|
||||||
|
See
|
||||||
|
.Sx SECURITY CONSIDERATIONS
|
||||||
|
below for more details.
|
||||||
|
.It Fl s
|
||||||
|
Require a secure authentication mechanism like Kerberos or S/Key to be used.
|
||||||
|
.It Fl U
|
||||||
|
Don't log each concurrent
|
||||||
|
.Tn FTP
|
||||||
|
session to
|
||||||
|
.Pa /var/run/utmp .
|
||||||
|
This is the default.
|
||||||
|
.It Fl u
|
||||||
|
Log each concurrent
|
||||||
|
.Tn FTP
|
||||||
|
session to
|
||||||
|
.Pa /var/run/utmp ,
|
||||||
|
making them visible to commands such as
|
||||||
|
.Xr who 1 .
|
||||||
|
.It Fl V Ar version
|
||||||
|
Use
|
||||||
|
.Ar version
|
||||||
|
as the version to advertise in the login banner and in the output of
|
||||||
|
.Sy STAT
|
||||||
|
and
|
||||||
|
.Sy SYST
|
||||||
|
instead of the default version information.
|
||||||
|
If
|
||||||
|
.Ar version
|
||||||
|
is empty or
|
||||||
|
.Sq -
|
||||||
|
then don't display any version information.
|
||||||
|
.It Fl W
|
||||||
|
Don't log each
|
||||||
|
.Tn FTP
|
||||||
|
session to
|
||||||
|
.Pa /var/log/wtmp .
|
||||||
|
.It Fl w
|
||||||
|
Log each
|
||||||
|
.Tn FTP
|
||||||
|
session to
|
||||||
|
.Pa /var/log/wtmp ,
|
||||||
|
making them visible to commands such as
|
||||||
|
.Xr last 1 .
|
||||||
|
This is the default.
|
||||||
|
.It Fl X
|
||||||
|
Log
|
||||||
|
.Tn wu-ftpd
|
||||||
|
style
|
||||||
|
.Sq xferlog
|
||||||
|
entries to the syslog, prefixed with
|
||||||
|
.Dq "xferlog:\ " ,
|
||||||
|
using a facility of
|
||||||
|
.Dv LOG_FTP .
|
||||||
|
These syslog entries can be converted to a
|
||||||
|
.Tn wu-ftpd
|
||||||
|
style
|
||||||
|
.Pa xferlog
|
||||||
|
file suitable for input into a third-party log analysis tool with a command
|
||||||
|
similar to:
|
||||||
|
.Dl "sed -ne 's/^.*xferlog: //p' /var/log/xferlog \*[Gt] wuxferlog"
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The file
|
||||||
|
.Pa /etc/nologin
|
||||||
|
can be used to disable
|
||||||
|
.Tn FTP
|
||||||
|
access.
|
||||||
|
If the file exists,
|
||||||
|
.Nm
|
||||||
|
displays it and exits.
|
||||||
|
If the file
|
||||||
|
.Pa /etc/ftpwelcome
|
||||||
|
exists,
|
||||||
|
.Nm
|
||||||
|
prints it before issuing the
|
||||||
|
.Dq ready
|
||||||
|
message.
|
||||||
|
If the file
|
||||||
|
.Pa /etc/motd
|
||||||
|
exists (under the chroot directory if applicable),
|
||||||
|
.Nm
|
||||||
|
prints it after a successful login.
|
||||||
|
This may be changed with the
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
directive
|
||||||
|
.Sy motd .
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
server currently supports the following
|
||||||
|
.Tn FTP
|
||||||
|
requests.
|
||||||
|
The case of the requests is ignored.
|
||||||
|
.Bl -column "Request" "Description" -offset indent
|
||||||
|
.It Sy Request Ta Sy Description
|
||||||
|
.It ABOR Ta "abort previous command"
|
||||||
|
.It ACCT Ta "specify account (ignored)"
|
||||||
|
.It ALLO Ta "allocate storage (vacuously)"
|
||||||
|
.It APPE Ta "append to a file"
|
||||||
|
.It CDUP Ta "change to parent of current working directory"
|
||||||
|
.It CWD Ta "change working directory"
|
||||||
|
.It DELE Ta "delete a file"
|
||||||
|
.It EPSV Ta "prepare for server-to-server transfer"
|
||||||
|
.It EPRT Ta "specify data connection port"
|
||||||
|
.It FEAT Ta "list extra features that are not defined in" Cm "RFC 959"
|
||||||
|
.It HELP Ta "give help information"
|
||||||
|
.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lA"
|
||||||
|
.It LPSV Ta "prepare for server-to-server transfer"
|
||||||
|
.It LPRT Ta "specify data connection port"
|
||||||
|
.It MLSD Ta "list contents of directory in a machine-processable form"
|
||||||
|
.It MLST Ta "show a pathname in a machine-processable form"
|
||||||
|
.It MKD Ta "make a directory"
|
||||||
|
.It MDTM Ta "show last modification time of file"
|
||||||
|
.It MODE Ta "specify data transfer" Em mode
|
||||||
|
.It NLST Ta "give name list of files in directory"
|
||||||
|
.It NOOP Ta "do nothing"
|
||||||
|
.It OPTS Ta "define persistent options for a given command"
|
||||||
|
.It PASS Ta "specify password"
|
||||||
|
.It PASV Ta "prepare for server-to-server transfer"
|
||||||
|
.It PORT Ta "specify data connection port"
|
||||||
|
.It PWD Ta "print the current working directory"
|
||||||
|
.It QUIT Ta "terminate session"
|
||||||
|
.It REST Ta "restart incomplete transfer"
|
||||||
|
.It RETR Ta "retrieve a file"
|
||||||
|
.It RMD Ta "remove a directory"
|
||||||
|
.It RNFR Ta "specify rename-from file name"
|
||||||
|
.It RNTO Ta "specify rename-to file name"
|
||||||
|
.It SITE Ta "non-standard commands (see next section)"
|
||||||
|
.It SIZE Ta "return size of file"
|
||||||
|
.It STAT Ta "return status of server"
|
||||||
|
.It STOR Ta "store a file"
|
||||||
|
.It STOU Ta "store a file with a unique name"
|
||||||
|
.It STRU Ta "specify data transfer" Em structure
|
||||||
|
.It SYST Ta "show operating system type of server system"
|
||||||
|
.It TYPE Ta "specify data transfer" Em type
|
||||||
|
.It USER Ta "specify user name"
|
||||||
|
.It XCUP Ta "change to parent of current working directory (deprecated)"
|
||||||
|
.It XCWD Ta "change working directory (deprecated)"
|
||||||
|
.It XMKD Ta "make a directory (deprecated)"
|
||||||
|
.It XPWD Ta "print the current working directory (deprecated)"
|
||||||
|
.It XRMD Ta "remove a directory (deprecated)"
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The following non-standard or
|
||||||
|
.Ux
|
||||||
|
specific commands are supported by the SITE request.
|
||||||
|
.Pp
|
||||||
|
.Bl -column Request Description -offset indent
|
||||||
|
.It Sy Request Ta Sy Description
|
||||||
|
.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 RATEGET Ta "set maximum get rate throttle in bytes/second, e.g. ``SITE RATEGET 5k''"
|
||||||
|
.It RATEPUT Ta "set maximum put rate throttle in bytes/second, e.g. ``SITE RATEPUT 5k''"
|
||||||
|
.It UMASK Ta "change umask, e.g. ``SITE UMASK 002''"
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The following
|
||||||
|
.Tn FTP
|
||||||
|
requests (as specified in
|
||||||
|
.Cm RFC 959
|
||||||
|
and
|
||||||
|
.Cm RFC 2228 )
|
||||||
|
are recognized, but are not implemented:
|
||||||
|
.Sy ACCT ,
|
||||||
|
.Sy ADAT ,
|
||||||
|
.Sy AUTH ,
|
||||||
|
.Sy CCC ,
|
||||||
|
.Sy CONF ,
|
||||||
|
.Sy ENC ,
|
||||||
|
.Sy MIC ,
|
||||||
|
.Sy PBSZ ,
|
||||||
|
.Sy PROT ,
|
||||||
|
.Sy REIN ,
|
||||||
|
and
|
||||||
|
.Sy SMNT .
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
server will abort an active file transfer only when the
|
||||||
|
.Sy ABOR
|
||||||
|
command is preceded by a Telnet "Interrupt Process" (IP)
|
||||||
|
signal and a Telnet "Synch" signal in the command Telnet stream,
|
||||||
|
as described in Internet
|
||||||
|
.Cm RFC 959 .
|
||||||
|
If a
|
||||||
|
.Sy STAT
|
||||||
|
command is received during a data transfer, preceded by a Telnet IP
|
||||||
|
and Synch, transfer status will be returned.
|
||||||
|
.Pp
|
||||||
|
.Nm
|
||||||
|
interprets file names according to the
|
||||||
|
.Dq globbing
|
||||||
|
conventions used by
|
||||||
|
.Xr csh 1 .
|
||||||
|
This allows users to use the metacharacters
|
||||||
|
.Dq Li \&*?[]{}~ .
|
||||||
|
.Ss User authentication
|
||||||
|
.Nm
|
||||||
|
authenticates users according to five rules.
|
||||||
|
.Pp
|
||||||
|
.Bl -enum -offset indent
|
||||||
|
.It
|
||||||
|
The login name must be in the password data base,
|
||||||
|
.Xr passwd 5 ,
|
||||||
|
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
|
||||||
|
.Sy USER
|
||||||
|
command will include an S/Key challenge.
|
||||||
|
The client may choose to respond with a
|
||||||
|
.Sy 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 skey 1
|
||||||
|
for more information on S/Key authentication.
|
||||||
|
S/Key is a Trademark of Bellcore.
|
||||||
|
.It
|
||||||
|
The login name must be allowed based on the information in
|
||||||
|
.Xr ftpusers 5 .
|
||||||
|
.It
|
||||||
|
The user must have a standard shell returned by
|
||||||
|
.Xr getusershell 3 .
|
||||||
|
If the user's shell field in the password database is empty, the
|
||||||
|
shell is assumed to be
|
||||||
|
.Pa /bin/sh .
|
||||||
|
As per
|
||||||
|
.Xr shells 5 ,
|
||||||
|
the user's shell must be listed with full path in
|
||||||
|
.Pa /etc/shells .
|
||||||
|
.It
|
||||||
|
If directed by the file
|
||||||
|
.Xr ftpchroot 5
|
||||||
|
the session's root directory will be changed by
|
||||||
|
.Xr chroot 2
|
||||||
|
to the directory specified in the
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
.Sy chroot
|
||||||
|
directive (if set),
|
||||||
|
or to the home directory of the user.
|
||||||
|
This facility may also be triggered by enabling the boolean
|
||||||
|
.Sy ftp-chroot
|
||||||
|
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
|
||||||
|
.Dq ftp ,
|
||||||
|
an
|
||||||
|
anonymous
|
||||||
|
.Tn FTP
|
||||||
|
account must be present in the password
|
||||||
|
file (user
|
||||||
|
.Dq ftp ) .
|
||||||
|
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).
|
||||||
|
.Pp
|
||||||
|
The server performs a
|
||||||
|
.Xr chroot 2
|
||||||
|
to the directory specified in the
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
.Sy chroot
|
||||||
|
directive (if set),
|
||||||
|
the
|
||||||
|
.Fl a Ar anondir
|
||||||
|
directory (if set),
|
||||||
|
or to the home directory of the
|
||||||
|
.Dq ftp
|
||||||
|
user.
|
||||||
|
.Pp
|
||||||
|
The server then performs a
|
||||||
|
.Xr chdir 2
|
||||||
|
to the directory specified in the
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
.Sy homedir
|
||||||
|
directive (if set), otherwise to
|
||||||
|
.Pa / .
|
||||||
|
.Pp
|
||||||
|
If other restrictions are required (such as disabling of certain
|
||||||
|
commands and the setting of a specific umask), then appropriate
|
||||||
|
entries in
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
are required.
|
||||||
|
.Pp
|
||||||
|
If the first character of the password supplied by an anonymous user
|
||||||
|
is
|
||||||
|
.Dq - ,
|
||||||
|
then the verbose messages displayed at login and upon a
|
||||||
|
.Sy CWD
|
||||||
|
command are suppressed.
|
||||||
|
.El
|
||||||
|
.Ss Display file escape sequences
|
||||||
|
When
|
||||||
|
.Nm
|
||||||
|
displays various files back to the client (such as
|
||||||
|
.Pa /etc/ftpwelcome
|
||||||
|
and
|
||||||
|
.Pa /etc/motd ) ,
|
||||||
|
various escape strings are replaced with information pertinent
|
||||||
|
to the current connection.
|
||||||
|
.Pp
|
||||||
|
The supported escape strings are:
|
||||||
|
.Bl -tag -width "Escape" -offset indent -compact
|
||||||
|
.It Sy "Escape"
|
||||||
|
.Sy Description
|
||||||
|
.It "\&%c"
|
||||||
|
Class name.
|
||||||
|
.It "\&%C"
|
||||||
|
Current working directory.
|
||||||
|
.It "\&%E"
|
||||||
|
Email address given with
|
||||||
|
.Fl e .
|
||||||
|
.It "\&%L"
|
||||||
|
Local hostname.
|
||||||
|
.It "\&%M"
|
||||||
|
Maximum number of users for this class.
|
||||||
|
Displays
|
||||||
|
.Dq unlimited
|
||||||
|
if there's no limit.
|
||||||
|
.It "\&%N"
|
||||||
|
Current number of users for this class.
|
||||||
|
.It "\&%R"
|
||||||
|
Remote hostname.
|
||||||
|
.It "\&%s"
|
||||||
|
If the result of the most recent
|
||||||
|
.Dq "\&%M"
|
||||||
|
or
|
||||||
|
.Dq "\&%N"
|
||||||
|
was not
|
||||||
|
.Dq Li 1 ,
|
||||||
|
print an
|
||||||
|
.Dq s .
|
||||||
|
.It "\&%S"
|
||||||
|
If the result of the most recent
|
||||||
|
.Dq "\&%M"
|
||||||
|
or
|
||||||
|
.Dq "\&%N"
|
||||||
|
was not
|
||||||
|
.Dq Li 1 ,
|
||||||
|
print an
|
||||||
|
.Dq S .
|
||||||
|
.It "\&%T"
|
||||||
|
Current time.
|
||||||
|
.It "\&%U"
|
||||||
|
User name.
|
||||||
|
.It "\&%\&%"
|
||||||
|
A
|
||||||
|
.Dq \&%
|
||||||
|
character.
|
||||||
|
.El
|
||||||
|
.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
|
||||||
|
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
|
||||||
|
.Dq root
|
||||||
|
and unwritable by anyone.
|
||||||
|
.It Pa ~ftp/bin
|
||||||
|
Make this directory owned by
|
||||||
|
.Dq root
|
||||||
|
and unwritable by anyone (mode 555).
|
||||||
|
Generally any conversion commands should be installed
|
||||||
|
here (mode 111).
|
||||||
|
.It Pa ~ftp/etc
|
||||||
|
Make this directory owned by
|
||||||
|
.Dq root
|
||||||
|
and unwritable by anyone (mode 555).
|
||||||
|
The files
|
||||||
|
.Pa pwd.db
|
||||||
|
(see
|
||||||
|
.Xr passwd 5 )
|
||||||
|
and
|
||||||
|
.Pa group
|
||||||
|
(see
|
||||||
|
.Xr group 5 )
|
||||||
|
must be present for the
|
||||||
|
.Sy LIST
|
||||||
|
command to be able to display owner and group names instead of numbers.
|
||||||
|
The password field in
|
||||||
|
.Xr passwd 5
|
||||||
|
is not used, and should not contain real passwords.
|
||||||
|
The file
|
||||||
|
.Pa motd ,
|
||||||
|
if present, will be printed after a successful login.
|
||||||
|
These files should be mode 444.
|
||||||
|
.It Pa ~ftp/pub
|
||||||
|
This directory and the subdirectories beneath it should be owned
|
||||||
|
by the users and groups responsible for placing files in them,
|
||||||
|
and be writable only by them (mode 755 or 775).
|
||||||
|
They should
|
||||||
|
.Em not
|
||||||
|
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
|
||||||
|
.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
|
||||||
|
.Tn 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
|
||||||
|
The following
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
directives should be used:
|
||||||
|
.Dl "modify guest off"
|
||||||
|
.Dl "umask guest 0707"
|
||||||
|
.Dl "upload guest on"
|
||||||
|
.Pp
|
||||||
|
This will result in anonymous users being able to upload files to this
|
||||||
|
directory, but they will not be able to download them, delete them, or
|
||||||
|
overwrite them, due to the umask and disabling of the commands mentioned
|
||||||
|
above.
|
||||||
|
.It Pa ~ftp/tmp
|
||||||
|
This directory is used to create temporary files which contain
|
||||||
|
the error messages generated by a conversion or
|
||||||
|
.Sy LIST
|
||||||
|
command.
|
||||||
|
The owner should be the user
|
||||||
|
.Dq ftp .
|
||||||
|
The permissions should be 300.
|
||||||
|
.Pp
|
||||||
|
If you don't enable conversion commands, or don't want anonymous users
|
||||||
|
uploading files here (see
|
||||||
|
.Pa ~ftp/incoming
|
||||||
|
above), then don't create this directory.
|
||||||
|
However, error messages from conversion or
|
||||||
|
.Sy LIST
|
||||||
|
commands won't be returned to the user.
|
||||||
|
(This is the traditional behaviour.)
|
||||||
|
Note that the
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
directive
|
||||||
|
.Sy upload
|
||||||
|
can be used to prevent users uploading here.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
To set up "ftp-only" accounts that provide only
|
||||||
|
.Tn FTP ,
|
||||||
|
but no valid shell
|
||||||
|
login, you can copy/link
|
||||||
|
.Pa /sbin/nologin
|
||||||
|
to
|
||||||
|
.Pa /sbin/ftplogin ,
|
||||||
|
and enter
|
||||||
|
.Pa /sbin/ftplogin
|
||||||
|
to
|
||||||
|
.Pa /etc/shells
|
||||||
|
to allow logging-in via
|
||||||
|
.Tn FTP
|
||||||
|
into the accounts, which must have
|
||||||
|
.Pa /sbin/ftplogin
|
||||||
|
as login shell.
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width /etc/ftpwelcome -compact
|
||||||
|
.It Pa /etc/ftpchroot
|
||||||
|
List of normal users whose root directory should be changed via
|
||||||
|
.Xr chroot 2 .
|
||||||
|
.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 before login.
|
||||||
|
.It Pa /etc/motd
|
||||||
|
Welcome notice after login.
|
||||||
|
.It Pa /etc/nologin
|
||||||
|
If it exists, displayed and access is refused.
|
||||||
|
.It Pa /var/run/ftpd.pids-CLASS
|
||||||
|
State file of logged-in processes for the
|
||||||
|
.Nm
|
||||||
|
class
|
||||||
|
.Sq CLASS .
|
||||||
|
.It Pa /var/run/utmp
|
||||||
|
List of logged-in users on the system.
|
||||||
|
.It Pa /var/log/wtmp
|
||||||
|
Login history database.
|
||||||
|
.El
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr ftp 1 ,
|
||||||
|
.Xr skey 1 ,
|
||||||
|
.Xr who 1 ,
|
||||||
|
.Xr getusershell 3 ,
|
||||||
|
.Xr ftpchroot 5 ,
|
||||||
|
.Xr ftpd.conf 5 ,
|
||||||
|
.Xr ftpusers 5 ,
|
||||||
|
.Xr login.conf 5 ,
|
||||||
|
.Xr syslogd 8
|
||||||
|
.Sh STANDARDS
|
||||||
|
.Nm
|
||||||
|
recognizes all commands in
|
||||||
|
.Cm RFC 959 ,
|
||||||
|
follows the guidelines in
|
||||||
|
.Cm RFC 1123 ,
|
||||||
|
recognizes all commands in
|
||||||
|
.Cm RFC 2228
|
||||||
|
(although they are not supported yet),
|
||||||
|
and supports the extensions from
|
||||||
|
.Cm RFC 2389 ,
|
||||||
|
.Cm RFC 2428 ,
|
||||||
|
and
|
||||||
|
.Cm RFC 3659 .
|
||||||
|
.Sh HISTORY
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
command appeared in
|
||||||
|
.Bx 4.2 .
|
||||||
|
.Pp
|
||||||
|
Various features such as the
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
functionality,
|
||||||
|
.Cm RFC 2389 ,
|
||||||
|
and
|
||||||
|
.Cm RFC 3659
|
||||||
|
support was implemented in
|
||||||
|
.Nx 1.3
|
||||||
|
and later releases by Luke Mewburn.
|
||||||
|
.Sh BUGS
|
||||||
|
The server must run as the super-user to create sockets with
|
||||||
|
privileged port numbers (i.e, those less than
|
||||||
|
.Dv IPPORT_RESERVED ,
|
||||||
|
which is 1024).
|
||||||
|
If
|
||||||
|
.Nm
|
||||||
|
is listening on a privileged port
|
||||||
|
it maintains an effective user id of the logged in user, reverting
|
||||||
|
to the super-user only when binding addresses to privileged sockets.
|
||||||
|
The
|
||||||
|
.Fl r
|
||||||
|
option can be used to override this behaviour and force privileges to
|
||||||
|
be permanently revoked; see
|
||||||
|
.Sx SECURITY CONSIDERATIONS
|
||||||
|
below for more details.
|
||||||
|
.Pp
|
||||||
|
.Nm
|
||||||
|
may have trouble handling connections from scoped IPv6 addresses, or
|
||||||
|
IPv4 mapped addresses
|
||||||
|
.Po
|
||||||
|
IPv4 connection on
|
||||||
|
.Dv AF_INET6
|
||||||
|
socket
|
||||||
|
.Pc .
|
||||||
|
For the latter case, running two daemons,
|
||||||
|
one for IPv4 and one for IPv6, will avoid the problem.
|
||||||
|
.Sh SECURITY CONSIDERATIONS
|
||||||
|
.Cm RFC 959
|
||||||
|
provides no restrictions on the
|
||||||
|
.Sy PORT
|
||||||
|
command, and this can lead to security problems, as
|
||||||
|
.Nm
|
||||||
|
can be fooled into connecting to any service on any host.
|
||||||
|
With the
|
||||||
|
.Dq checkportcmd
|
||||||
|
feature of the
|
||||||
|
.Xr ftpd.conf 5 ,
|
||||||
|
.Sy PORT
|
||||||
|
commands with different host addresses, or TCP ports lower than
|
||||||
|
.Dv IPPORT_RESERVED
|
||||||
|
will be rejected.
|
||||||
|
This also prevents
|
||||||
|
.Sq third-party proxy ftp
|
||||||
|
from working.
|
||||||
|
Use of this option is
|
||||||
|
.Em strongly
|
||||||
|
recommended, and enabled by default.
|
||||||
|
.Pp
|
||||||
|
By default
|
||||||
|
.Nm
|
||||||
|
uses a port that is one less than the port it is listening on to
|
||||||
|
communicate back to the client for the
|
||||||
|
.Sy EPRT ,
|
||||||
|
.Sy LPRT ,
|
||||||
|
and
|
||||||
|
.Sy PORT
|
||||||
|
commands, unless overridden with
|
||||||
|
.Fl P Ar dataport .
|
||||||
|
As the default port for
|
||||||
|
.Nm
|
||||||
|
(21) is a privileged port below
|
||||||
|
.Dv IPPORT_RESERVED ,
|
||||||
|
.Nm
|
||||||
|
retains the ability to switch back to root privileges to bind these
|
||||||
|
ports.
|
||||||
|
In order to increase security by reducing the potential for a bug in
|
||||||
|
.Nm
|
||||||
|
providing a remote root compromise,
|
||||||
|
.Nm
|
||||||
|
will permanently drop root privileges if one of the following is true:
|
||||||
|
.Bl -enum -offset indent
|
||||||
|
.It
|
||||||
|
.Nm
|
||||||
|
is running on a port greater than
|
||||||
|
.Dv IPPORT_RESERVED
|
||||||
|
and the user has logged in as a
|
||||||
|
.Sq guest
|
||||||
|
or
|
||||||
|
.Sq chroot
|
||||||
|
user.
|
||||||
|
.It
|
||||||
|
.Nm
|
||||||
|
was invoked with
|
||||||
|
.Fl r .
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Don't create
|
||||||
|
.Pa ~ftp/tmp
|
||||||
|
if you don't want anonymous users to upload files there.
|
||||||
|
That directory is only necessary if you want to display the error
|
||||||
|
messages of conversion commands to the user.
|
||||||
|
Note that if uploads are disabled with the
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
directive
|
||||||
|
.Sy upload ,
|
||||||
|
then this directory cannot be abused by the user in this way, so it
|
||||||
|
should be safe to create.
|
||||||
|
.Pp
|
||||||
|
To avoid possible denial-of-service attacks,
|
||||||
|
.Sy SIZE
|
||||||
|
requests against files larger than 10240 bytes will be denied if
|
||||||
|
the current transfer
|
||||||
|
.Sy TYPE
|
||||||
|
is
|
||||||
|
.Sq Li A
|
||||||
|
(ASCII).
|
4000
libexec/ftpd/ftpd.c
Normal file
4000
libexec/ftpd/ftpd.c
Normal file
File diff suppressed because it is too large
Load diff
738
libexec/ftpd/ftpd.conf.5
Normal file
738
libexec/ftpd/ftpd.conf.5
Normal file
|
@ -0,0 +1,738 @@
|
||||||
|
.\" $NetBSD: ftpd.conf.5,v 1.37 2009/04/09 02:25:45 joerg Exp $
|
||||||
|
.\"
|
||||||
|
.\" Copyright (c) 1997-2008 The NetBSD Foundation, Inc.
|
||||||
|
.\" All rights reserved.
|
||||||
|
.\"
|
||||||
|
.\" This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
.\" by 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.
|
||||||
|
.\"
|
||||||
|
.\" 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 FOUNDATION 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.
|
||||||
|
.\"
|
||||||
|
.Dd April 13, 2007
|
||||||
|
.Dt FTPD.CONF 5
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm ftpd.conf
|
||||||
|
.Nd
|
||||||
|
.Xr ftpd 8
|
||||||
|
configuration file
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
file specifies various configuration options for
|
||||||
|
.Xr ftpd 8
|
||||||
|
that apply once a user has authenticated their connection.
|
||||||
|
.Pp
|
||||||
|
.Nm
|
||||||
|
consists of a series of lines, each of which may contain a
|
||||||
|
configuration directive, a comment, or a blank line.
|
||||||
|
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
|
||||||
|
A directive line has the format:
|
||||||
|
.Dl command class [arguments]
|
||||||
|
.Pp
|
||||||
|
A
|
||||||
|
.Dq \e
|
||||||
|
is the escape character; it can be used to escape the meaning of the
|
||||||
|
comment character, or if it is the last character on a line, extends
|
||||||
|
a configuration directive across multiple lines.
|
||||||
|
A
|
||||||
|
.Dq #
|
||||||
|
is the comment character, and all characters from it to the end of
|
||||||
|
line are ignored (unless it is escaped with the escape character).
|
||||||
|
.Pp
|
||||||
|
Each authenticated user is a member of a
|
||||||
|
.Em class ,
|
||||||
|
which is determined by
|
||||||
|
.Xr ftpusers 5 .
|
||||||
|
.Em class
|
||||||
|
is used to determine which
|
||||||
|
.Nm
|
||||||
|
entries apply to the user.
|
||||||
|
The following special classes exist when parsing entries in
|
||||||
|
.Nm :
|
||||||
|
.Bl -tag -width "chroot" -compact -offset indent
|
||||||
|
.It Sy all
|
||||||
|
Matches any class.
|
||||||
|
.It Sy none
|
||||||
|
Matches no class.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Each class has a type, which may be one of:
|
||||||
|
.Bl -tag -width "CHROOT" -offset indent
|
||||||
|
.It Sy GUEST
|
||||||
|
Guests (as per the
|
||||||
|
.Dq anonymous
|
||||||
|
and
|
||||||
|
.Dq ftp
|
||||||
|
logins).
|
||||||
|
A
|
||||||
|
.Xr chroot 2
|
||||||
|
is performed after login.
|
||||||
|
.It Sy CHROOT
|
||||||
|
.Xr chroot 2 Ns ed
|
||||||
|
users (as per
|
||||||
|
.Xr ftpchroot 5 ) .
|
||||||
|
A
|
||||||
|
.Xr chroot 2
|
||||||
|
is performed after login.
|
||||||
|
.It Sy REAL
|
||||||
|
Normal users.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Xr ftpd 8
|
||||||
|
.Sy STAT
|
||||||
|
command will return the class settings for the current user as defined by
|
||||||
|
.Nm ,
|
||||||
|
unless the
|
||||||
|
.Sy private
|
||||||
|
directive is set for the class.
|
||||||
|
.Pp
|
||||||
|
Each configuration line may be one of:
|
||||||
|
.Bl -tag -width 4n
|
||||||
|
.It Sy advertize Ar class Op Ar host
|
||||||
|
Set the address to advertise in the response to the
|
||||||
|
.Sy PASV
|
||||||
|
and
|
||||||
|
.Sy LPSV
|
||||||
|
commands to the address for
|
||||||
|
.Ar host
|
||||||
|
(which may be either a host name or IP address).
|
||||||
|
This may be useful in some firewall configurations, although many
|
||||||
|
ftp clients may not work if the address being advertised is different
|
||||||
|
to the address that they've connected to.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar host
|
||||||
|
not is specified, disable this.
|
||||||
|
.It Sy checkportcmd Ar class Op Sy off
|
||||||
|
Check the
|
||||||
|
.Sy PORT
|
||||||
|
command for validity.
|
||||||
|
The
|
||||||
|
.Sy PORT
|
||||||
|
command will fail if the IP address specified does not match the
|
||||||
|
.Tn FTP
|
||||||
|
command connection, or if the remote TCP port number is less than
|
||||||
|
.Dv IPPORT_RESERVED .
|
||||||
|
It is
|
||||||
|
.Em strongly
|
||||||
|
encouraged that this option be used, especially for sites concerned
|
||||||
|
with potential security problems with
|
||||||
|
.Tn FTP
|
||||||
|
bounce attacks.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Sy off
|
||||||
|
is specified, disable this feature, otherwise enable it.
|
||||||
|
.It Sy chroot Ar class Op Sy pathformat
|
||||||
|
If
|
||||||
|
.Ar pathformat
|
||||||
|
is not specified or
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none ,
|
||||||
|
use the default behavior (see below).
|
||||||
|
Otherwise,
|
||||||
|
.Ar pathformat
|
||||||
|
is parsed to create a directory to create as the root directory with
|
||||||
|
.Xr chroot 2
|
||||||
|
into upon login.
|
||||||
|
.Pp
|
||||||
|
.Ar pathformat
|
||||||
|
can contain the following escape strings:
|
||||||
|
.Bl -tag -width "Escape" -offset indent -compact
|
||||||
|
.It Sy "Escape"
|
||||||
|
.Sy Description
|
||||||
|
.It "\&%c"
|
||||||
|
Class name.
|
||||||
|
.It "\&%d"
|
||||||
|
Home directory of user.
|
||||||
|
.It "\&%u"
|
||||||
|
User name.
|
||||||
|
.It "\&%\&%"
|
||||||
|
A
|
||||||
|
.Dq \&%
|
||||||
|
character.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The default root directory is:
|
||||||
|
.Bl -tag -width "CHROOT" -offset indent -compact
|
||||||
|
.It Sy CHROOT
|
||||||
|
The user's home directory.
|
||||||
|
.It Sy GUEST
|
||||||
|
If
|
||||||
|
.Fl a Ar anondir
|
||||||
|
is specified, use
|
||||||
|
.Ar anondir ,
|
||||||
|
otherwise the home directory of the
|
||||||
|
.Sq ftp
|
||||||
|
user.
|
||||||
|
.It Sy REAL
|
||||||
|
By default no
|
||||||
|
.Xr chroot 2
|
||||||
|
is performed.
|
||||||
|
.El
|
||||||
|
.It Sy classtype Ar class Ar type
|
||||||
|
Set the class type of
|
||||||
|
.Ar class
|
||||||
|
to
|
||||||
|
.Ar type
|
||||||
|
(see above).
|
||||||
|
.It Sy conversion Ar class Ar suffix Op Ar "type disable command"
|
||||||
|
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 file types 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 file name of
|
||||||
|
.Dq Pa \&.
|
||||||
|
will prevent this disabling action
|
||||||
|
(i.e., the conversion is always permitted.)
|
||||||
|
.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.
|
||||||
|
All instances of the word
|
||||||
|
.Dq %s
|
||||||
|
in
|
||||||
|
.Ar command
|
||||||
|
are replaced with the requested file (sans
|
||||||
|
.Ar suffix ) .
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Conversion directives specified later in the file override earlier
|
||||||
|
conversions with the same suffix.
|
||||||
|
.It Sy denyquick Ar class Op Sy off
|
||||||
|
Enforce
|
||||||
|
.Xr ftpusers 5
|
||||||
|
rules after the
|
||||||
|
.Sy USER
|
||||||
|
command is received, rather than after the
|
||||||
|
.Sy PASS
|
||||||
|
command is received.
|
||||||
|
Whilst enabling this feature may allow information leakage about
|
||||||
|
available accounts (for example, if you allow some users of a
|
||||||
|
.Sy REAL
|
||||||
|
or
|
||||||
|
.Sy CHROOT
|
||||||
|
class but not others), it is useful in preventing a denied user
|
||||||
|
(such as
|
||||||
|
.Sq root )
|
||||||
|
from entering their password across an insecure connection.
|
||||||
|
This option is
|
||||||
|
.Em strongly
|
||||||
|
recommended for servers which run an anonymous-only service.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Sy off
|
||||||
|
is specified, disable this feature, otherwise enable it.
|
||||||
|
.It Sy display Ar class Op Ar file
|
||||||
|
If
|
||||||
|
.Ar file
|
||||||
|
is not specified 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.
|
||||||
|
Escape sequences are supported; refer to
|
||||||
|
.Sx Display file escape sequences
|
||||||
|
in
|
||||||
|
.Xr ftpd 8
|
||||||
|
for more information.
|
||||||
|
.It Sy hidesymlinks Ar class Op Sy off
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Sy off
|
||||||
|
is specified, disable this feature.
|
||||||
|
Otherwise, the
|
||||||
|
.Sy LIST
|
||||||
|
command lists symbolic links as the file or directory the link
|
||||||
|
references
|
||||||
|
.Pq Dq Li "ls -LlA" .
|
||||||
|
Servers which run an anonymous service may wish to enable this
|
||||||
|
feature for
|
||||||
|
.Sy GUEST
|
||||||
|
users, so that symbolic links do not leak names in
|
||||||
|
directories that are not searchable by
|
||||||
|
.Sy GUEST
|
||||||
|
users.
|
||||||
|
.It Sy homedir Ar class Op Sy pathformat
|
||||||
|
If
|
||||||
|
.Ar pathformat
|
||||||
|
is not specified or
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none ,
|
||||||
|
use the default behavior (see below).
|
||||||
|
Otherwise,
|
||||||
|
.Ar pathformat
|
||||||
|
is parsed to create a directory to change into upon login, and to use
|
||||||
|
as the
|
||||||
|
.Sq home
|
||||||
|
directory of the user for tilde expansion in pathnames, etc.
|
||||||
|
.Ar pathformat
|
||||||
|
is parsed as per the
|
||||||
|
.Sy chroot
|
||||||
|
directive.
|
||||||
|
.Pp
|
||||||
|
The default home directory is the home directory of the user for
|
||||||
|
.Sy REAL
|
||||||
|
users, and
|
||||||
|
.Pa /
|
||||||
|
for
|
||||||
|
.Sy GUEST
|
||||||
|
and
|
||||||
|
.Sy CHROOT
|
||||||
|
users.
|
||||||
|
.It Sy limit Ar class Op Ar count Op Ar file
|
||||||
|
Limit the maximum number of concurrent connections for
|
||||||
|
.Ar class
|
||||||
|
to
|
||||||
|
.Ar count ,
|
||||||
|
with
|
||||||
|
.Sq \-1
|
||||||
|
meaning unlimited connections.
|
||||||
|
If the limit is exceeded and
|
||||||
|
.Ar file
|
||||||
|
is specified, display its contents to the user.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar count
|
||||||
|
is not specified, disable this.
|
||||||
|
If
|
||||||
|
.Ar file
|
||||||
|
is a relative path, it will be searched for in
|
||||||
|
.Pa /etc
|
||||||
|
(which can be overridden with
|
||||||
|
.Fl c Ar confdir ) .
|
||||||
|
.It Sy maxfilesize Ar class Op Ar size
|
||||||
|
Set the maximum size of an uploaded file to
|
||||||
|
.Ar size ,
|
||||||
|
with
|
||||||
|
.Sq \-1
|
||||||
|
meaning unlimited connections.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar size
|
||||||
|
is not specified, disable this.
|
||||||
|
.It Sy maxtimeout Ar class Op Ar time
|
||||||
|
Set the maximum timeout period that a client may request,
|
||||||
|
defaulting to two hours.
|
||||||
|
This cannot be less than 30 seconds, or the value for
|
||||||
|
.Sy timeout .
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar time
|
||||||
|
is not specified, use the default.
|
||||||
|
.It Sy mmapsize Ar class Op Ar size
|
||||||
|
Set the size of the sliding window to map a file using
|
||||||
|
.Xr mmap 2 .
|
||||||
|
If zero,
|
||||||
|
.Xr ftpd 8
|
||||||
|
will use
|
||||||
|
.Xr read 2
|
||||||
|
instead.
|
||||||
|
The default is zero.
|
||||||
|
This option affects only binary transfers.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar size
|
||||||
|
is not specified, use the default.
|
||||||
|
.It Sy modify Ar class Op Sy off
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Sy off
|
||||||
|
is specified, disable the following commands:
|
||||||
|
.Sy CHMOD ,
|
||||||
|
.Sy DELE ,
|
||||||
|
.Sy MKD ,
|
||||||
|
.Sy RMD ,
|
||||||
|
.Sy RNFR ,
|
||||||
|
and
|
||||||
|
.Sy UMASK .
|
||||||
|
Otherwise, enable them.
|
||||||
|
.It Sy motd Ar class Op Ar file
|
||||||
|
If
|
||||||
|
.Ar file
|
||||||
|
is not specified or
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none ,
|
||||||
|
disable this.
|
||||||
|
Otherwise, use
|
||||||
|
.Ar file
|
||||||
|
as the message of the day file to display after login.
|
||||||
|
Escape sequences are supported; refer to
|
||||||
|
.Sx Display file escape sequences
|
||||||
|
in
|
||||||
|
.Xr ftpd 8
|
||||||
|
for more information.
|
||||||
|
If
|
||||||
|
.Ar file
|
||||||
|
is a relative path, it will be searched for in
|
||||||
|
.Pa /etc
|
||||||
|
(which can be overridden with
|
||||||
|
.Fl c Ar confdir ) .
|
||||||
|
.It Sy notify Ar class Op Ar fileglob
|
||||||
|
If
|
||||||
|
.Ar fileglob
|
||||||
|
is not specified 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 passive Ar class Op Sy off
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Sy off
|
||||||
|
is specified, prevent passive
|
||||||
|
.Sy ( PASV ,
|
||||||
|
.Sy LPSV ,
|
||||||
|
and
|
||||||
|
.Sy EPSV )
|
||||||
|
connections.
|
||||||
|
Otherwise, enable them.
|
||||||
|
.It Sy portrange Ar class Op Ar min Ar max
|
||||||
|
Set the range of port number which will be used for the passive data port.
|
||||||
|
.Ar max
|
||||||
|
must be greater than
|
||||||
|
.Ar min ,
|
||||||
|
and both numbers must be be between
|
||||||
|
.Dv IPPORT_RESERVED
|
||||||
|
(1024) and 65535.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or no arguments are specified, disable this.
|
||||||
|
.It Sy private Ar class Op Sy off
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Sy off
|
||||||
|
is specified, do not display class information in the output of the
|
||||||
|
.Sy STAT
|
||||||
|
command.
|
||||||
|
Otherwise, display the information.
|
||||||
|
.It Sy rateget Ar class Op Ar rate
|
||||||
|
Set the maximum get
|
||||||
|
.Pq Sy RETR
|
||||||
|
transfer rate throttle for
|
||||||
|
.Ar class
|
||||||
|
to
|
||||||
|
.Ar rate
|
||||||
|
bytes per second.
|
||||||
|
If
|
||||||
|
.Ar rate
|
||||||
|
is 0, the throttle is disabled.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar rate
|
||||||
|
is not specified, disable this.
|
||||||
|
.It Sy rateput Ar class Op Ar rate
|
||||||
|
Set the maximum put
|
||||||
|
.Pq Sy STOR
|
||||||
|
transfer rate throttle for
|
||||||
|
.Ar class
|
||||||
|
to
|
||||||
|
.Ar rate
|
||||||
|
bytes per second.
|
||||||
|
If
|
||||||
|
.Ar rate
|
||||||
|
is 0, the throttle is disabled.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar rate
|
||||||
|
is not specified, disable this.
|
||||||
|
.It Sy readsize Ar class Op Ar size
|
||||||
|
Set the size of the read buffer to
|
||||||
|
.Xr read 2
|
||||||
|
a file.
|
||||||
|
The default is the file system block size.
|
||||||
|
This option affects only binary transfers.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar size
|
||||||
|
is not specified, use the default.
|
||||||
|
.It Sy recvbufsize Ar class Op Ar size
|
||||||
|
Set the size of the socket receive buffer.
|
||||||
|
The default is zero and the system default value will be used.
|
||||||
|
This option affects only passive transfers.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar size
|
||||||
|
is not specified, use the default.
|
||||||
|
.It Sy sanenames Ar class Op Sy off
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Sy off
|
||||||
|
is specified, 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 sendbufsize Ar class Op Ar size
|
||||||
|
Set the size of the socket send buffer.
|
||||||
|
The default is zero and the system default value will be used.
|
||||||
|
This option affects only binary transfers.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar size
|
||||||
|
is not specified, use the default.
|
||||||
|
.It Sy sendlowat Ar class Op Ar size
|
||||||
|
Set the low water mark of socket send buffer.
|
||||||
|
The default is zero and system default value will be used.
|
||||||
|
This option affects only for binary transfer.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar size
|
||||||
|
is not specified, use the default.
|
||||||
|
.It Sy template Ar class Op Ar refclass
|
||||||
|
Define
|
||||||
|
.Ar refclass
|
||||||
|
as the
|
||||||
|
.Sq template
|
||||||
|
for
|
||||||
|
.Ar class ;
|
||||||
|
any reference to
|
||||||
|
.Ar refclass
|
||||||
|
in following directives will also apply to members of
|
||||||
|
.Ar class .
|
||||||
|
This is useful to define a template class so that other classes which are
|
||||||
|
to share common attributes can be easily defined without unnecessary
|
||||||
|
duplication.
|
||||||
|
There can be only one template defined at a time.
|
||||||
|
If
|
||||||
|
.Ar refclass
|
||||||
|
is not specified, disable the template for
|
||||||
|
.Ar class .
|
||||||
|
.It Sy timeout Ar class Op Ar time
|
||||||
|
Set the inactivity timeout period.
|
||||||
|
(the default is fifteen minutes).
|
||||||
|
This cannot be less than 30 seconds, or greater than the value for
|
||||||
|
.Sy maxtimeout .
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar time
|
||||||
|
is not specified, use the default.
|
||||||
|
.It Sy umask Ar class Op Ar umaskval
|
||||||
|
Set the umask to
|
||||||
|
.Ar umaskval .
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar umaskval
|
||||||
|
is not specified, set to the default of
|
||||||
|
.Li 027 .
|
||||||
|
.It Sy upload Ar class Op Sy off
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Sy off
|
||||||
|
is specified, disable the following commands:
|
||||||
|
.Sy APPE ,
|
||||||
|
.Sy STOR ,
|
||||||
|
and
|
||||||
|
.Sy STOU ,
|
||||||
|
as well as the modify commands:
|
||||||
|
.Sy CHMOD ,
|
||||||
|
.Sy DELE ,
|
||||||
|
.Sy MKD ,
|
||||||
|
.Sy RMD ,
|
||||||
|
.Sy RNFR ,
|
||||||
|
and
|
||||||
|
.Sy UMASK .
|
||||||
|
Otherwise, enable them.
|
||||||
|
.It Sy writesize Ar class Op Ar size
|
||||||
|
Limit the number of bytes to
|
||||||
|
.Xr write 2
|
||||||
|
at a time.
|
||||||
|
The default is zero, which means all the data available as a result of
|
||||||
|
.Xr mmap 2
|
||||||
|
or
|
||||||
|
.Xr read 2
|
||||||
|
will be written at a time.
|
||||||
|
This option affects only binary transfers.
|
||||||
|
If
|
||||||
|
.Ar class
|
||||||
|
is
|
||||||
|
.Dq none
|
||||||
|
or
|
||||||
|
.Ar size
|
||||||
|
is not specified, use the default.
|
||||||
|
.El
|
||||||
|
.Ss Numeric argument suffix parsing
|
||||||
|
Where command arguments are numeric, a decimal number is expected.
|
||||||
|
Two or more numbers may be separated by an
|
||||||
|
.Dq x
|
||||||
|
to indicate a product.
|
||||||
|
Each number may have one of the following optional suffixes:
|
||||||
|
.Bl -tag -width 3n -offset indent -compact
|
||||||
|
.It b
|
||||||
|
Block; multiply by 512
|
||||||
|
.It k
|
||||||
|
Kibi; multiply by 1024 (1 KiB)
|
||||||
|
.It m
|
||||||
|
Mebi; multiply by 1048576 (1 MiB)
|
||||||
|
.It g
|
||||||
|
Gibi; multiply by 1073741824 (1 GiB)
|
||||||
|
.It t
|
||||||
|
Tebi; multiply by 1099511627776 (1 TiB)
|
||||||
|
.It w
|
||||||
|
Word; multiply by the number of bytes in an integer
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
See
|
||||||
|
.Xr strsuftoll 3
|
||||||
|
for more information.
|
||||||
|
.Sh DEFAULTS
|
||||||
|
The following defaults are used:
|
||||||
|
.Pp
|
||||||
|
.Bd -literal -offset indent -compact
|
||||||
|
checkportcmd all
|
||||||
|
classtype chroot CHROOT
|
||||||
|
classtype guest GUEST
|
||||||
|
classtype real REAL
|
||||||
|
display none
|
||||||
|
limit all \-1 # unlimited connections
|
||||||
|
maxtimeout all 7200 # 2 hours
|
||||||
|
modify all
|
||||||
|
motd all motd
|
||||||
|
notify none
|
||||||
|
passive all
|
||||||
|
timeout all 900 # 15 minutes
|
||||||
|
umask all 027
|
||||||
|
upload all
|
||||||
|
modify guest off
|
||||||
|
umask guest 0707
|
||||||
|
.Ed
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width /usr/share/examples/ftpd/ftpd.conf -compact
|
||||||
|
.It Pa /etc/ftpd.conf
|
||||||
|
This file.
|
||||||
|
.It Pa /usr/share/examples/ftpd/ftpd.conf
|
||||||
|
A sample
|
||||||
|
.Nm
|
||||||
|
file.
|
||||||
|
.El
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr strsuftoll 3 ,
|
||||||
|
.Xr ftpchroot 5 ,
|
||||||
|
.Xr ftpusers 5 ,
|
||||||
|
.Xr ftpd 8
|
||||||
|
.Sh HISTORY
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
functionality was implemented in
|
||||||
|
.Nx 1.3
|
||||||
|
and later releases by Luke Mewburn, based on work by Simon Burge.
|
179
libexec/ftpd/ftpusers.5
Normal file
179
libexec/ftpd/ftpusers.5
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
.\" $NetBSD: ftpusers.5,v 1.17 2008/09/13 02:41:52 lukem Exp $
|
||||||
|
.\"
|
||||||
|
.\" Copyright (c) 1997-2008 The NetBSD Foundation, Inc.
|
||||||
|
.\" All rights reserved.
|
||||||
|
.\"
|
||||||
|
.\" This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
.\" by 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.
|
||||||
|
.\"
|
||||||
|
.\" 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 FOUNDATION 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.
|
||||||
|
.\"
|
||||||
|
.Dd July 17, 2000
|
||||||
|
.Dt FTPUSERS 5
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm ftpusers ,
|
||||||
|
.Nm ftpchroot
|
||||||
|
.Nd
|
||||||
|
.Xr ftpd 8
|
||||||
|
access control file
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
file provides user access control for
|
||||||
|
.Xr ftpd 8
|
||||||
|
by defining which users may login.
|
||||||
|
.Pp
|
||||||
|
If the
|
||||||
|
.Nm
|
||||||
|
file does not exist, all users are denied access.
|
||||||
|
.Pp
|
||||||
|
A
|
||||||
|
.Dq \e
|
||||||
|
is the escape character; it can be used to escape the meaning of the
|
||||||
|
comment character, or if it is the last character on a line, extends
|
||||||
|
a configuration directive across multiple lines.
|
||||||
|
A
|
||||||
|
.Dq #
|
||||||
|
is the comment character, and all characters from it to the end of
|
||||||
|
line are ignored (unless it is escaped with the escape character).
|
||||||
|
.Pp
|
||||||
|
The syntax of each line is:
|
||||||
|
.Dl userglob[:groupglob][@host] [directive [class]]
|
||||||
|
.Pp
|
||||||
|
These elements are:
|
||||||
|
.Bl -tag -width "groupglob" -offset indent
|
||||||
|
.It Sy userglob
|
||||||
|
matched against the user name, using
|
||||||
|
.Xr fnmatch 3
|
||||||
|
glob matching
|
||||||
|
(e.g,
|
||||||
|
.Sq f* ) .
|
||||||
|
.It Sy groupglob
|
||||||
|
matched against all the groups that the user is a member of, using
|
||||||
|
.Xr fnmatch 3
|
||||||
|
glob matching
|
||||||
|
(e.g,
|
||||||
|
.Sq *src ) .
|
||||||
|
.It Sy host
|
||||||
|
either a CIDR address (refer to
|
||||||
|
.Xr inet_net_pton 3 )
|
||||||
|
to match against the remote address
|
||||||
|
(e.g,
|
||||||
|
.Sq 1.2.3.4/24 ) ,
|
||||||
|
or an
|
||||||
|
.Xr fnmatch 3
|
||||||
|
glob to match against the remote hostname
|
||||||
|
(e.g,
|
||||||
|
.Sq *.NetBSD.org ) .
|
||||||
|
.It Sy directive
|
||||||
|
If
|
||||||
|
.Dq allow
|
||||||
|
or
|
||||||
|
.Dq yes
|
||||||
|
the user is allowed access.
|
||||||
|
If
|
||||||
|
.Dq deny
|
||||||
|
or
|
||||||
|
.Dq no ,
|
||||||
|
or
|
||||||
|
.Sy directive
|
||||||
|
is not given, the user is denied access.
|
||||||
|
.It Sy class
|
||||||
|
defines the class to use in
|
||||||
|
.Xr ftpd.conf 5 .
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
If
|
||||||
|
.Sy class
|
||||||
|
is not given, it defaults to one of the following:
|
||||||
|
.Bl -tag -width "chroot" -offset indent
|
||||||
|
.It Sy chroot
|
||||||
|
If there is a match in
|
||||||
|
.Sx /etc/ftpchroot
|
||||||
|
for the user.
|
||||||
|
.It Sy guest
|
||||||
|
If the user name is
|
||||||
|
.Dq anonymous
|
||||||
|
or
|
||||||
|
.Sq ftp .
|
||||||
|
.It Sy real
|
||||||
|
If neither of the above is true.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
No further comparisons are attempted after the first successful match.
|
||||||
|
If no match is found, the user is granted access.
|
||||||
|
This syntax is backward-compatible with the old syntax.
|
||||||
|
.Pp
|
||||||
|
If a user requests a guest login, the
|
||||||
|
.Xr ftpd 8
|
||||||
|
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/ftpchroot
|
||||||
|
The file
|
||||||
|
.Pa /etc/ftpchroot
|
||||||
|
is used to determine which users will have their session's root directory
|
||||||
|
changed (using
|
||||||
|
.Xr chroot 2 ) ,
|
||||||
|
either to the directory specified in the
|
||||||
|
.Xr ftpd.conf 5
|
||||||
|
.Sy chroot
|
||||||
|
directive (if set),
|
||||||
|
or to the home directory of the user.
|
||||||
|
If the file does not exist, the root directory change is not performed.
|
||||||
|
.Pp
|
||||||
|
The syntax is similar to
|
||||||
|
.Nm ,
|
||||||
|
except that the
|
||||||
|
.Sy class
|
||||||
|
argument is ignored.
|
||||||
|
If there's a positive match, the session's root directory is changed.
|
||||||
|
No further comparisons are attempted after the first successful match.
|
||||||
|
This syntax is backward-compatible with the old syntax.
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width /usr/share/examples/ftpd/ftpusers -compact
|
||||||
|
.It Pa /etc/ftpchroot
|
||||||
|
List of normal users who should have their ftp session's root directory
|
||||||
|
changed by using
|
||||||
|
.Xr chroot 2 .
|
||||||
|
.It Pa /etc/ftpusers
|
||||||
|
This file.
|
||||||
|
.It Pa /usr/share/examples/ftpd/ftpusers
|
||||||
|
A sample
|
||||||
|
.Nm
|
||||||
|
file.
|
||||||
|
.El
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr fnmatch 3 ,
|
||||||
|
.Xr inet_net_pton 3 ,
|
||||||
|
.Xr ftpd.conf 5 ,
|
||||||
|
.Xr ftpd 8
|
172
libexec/ftpd/logutmp.c
Normal file
172
libexec/ftpd/logutmp.c
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
/* $NetBSD: logutmp.c,v 1.12 2011/09/16 16:13:17 plunky Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Portions Copyright (c) 1988, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Portions Copyright (c) 1996, Jason Downs. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#ifndef lint
|
||||||
|
__RCSID("$NetBSD: logutmp.c,v 1.12 2011/09/16 16:13:17 plunky Exp $");
|
||||||
|
#endif /* not lint */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ttyent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <utmp.h>
|
||||||
|
#ifdef SUPPORT_UTMPX
|
||||||
|
#include <utmpx.h>
|
||||||
|
#endif
|
||||||
|
#include <util.h>
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
|
|
||||||
|
#ifdef SUPPORT_UTMP
|
||||||
|
typedef struct utmp UTMP;
|
||||||
|
|
||||||
|
static int fd = -1;
|
||||||
|
static int topslot = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Special versions of login()/logout() which hold the utmp file open,
|
||||||
|
* for use with ftpd.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
ftpd_login(const struct utmp *ut)
|
||||||
|
{
|
||||||
|
UTMP ubuf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, loop through /etc/ttys, if needed, to initialize the
|
||||||
|
* top of the tty slots, since ftpd has no tty.
|
||||||
|
*/
|
||||||
|
if (topslot < 0) {
|
||||||
|
topslot = 0;
|
||||||
|
while (getttyent() != NULL)
|
||||||
|
topslot++;
|
||||||
|
}
|
||||||
|
if ((topslot < 0) || ((fd < 0)
|
||||||
|
&& (fd = open(_PATH_UTMP, O_RDWR|O_CREAT, 0644)) < 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now find a slot that's not in use...
|
||||||
|
*/
|
||||||
|
(void)lseek(fd, (off_t)(topslot * sizeof(UTMP)), SEEK_SET);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (read(fd, &ubuf, sizeof(UTMP)) == sizeof(UTMP)) {
|
||||||
|
if (!ubuf.ut_name[0]) {
|
||||||
|
(void)lseek(fd, -(off_t)sizeof(UTMP), SEEK_CUR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
topslot++;
|
||||||
|
} else {
|
||||||
|
(void)lseek(fd, (off_t)(topslot * sizeof(UTMP)),
|
||||||
|
SEEK_SET);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)write(fd, ut, sizeof(UTMP));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ftpd_logout(const char *line)
|
||||||
|
{
|
||||||
|
UTMP ut;
|
||||||
|
int rval;
|
||||||
|
|
||||||
|
rval = 0;
|
||||||
|
if (fd < 0)
|
||||||
|
return(rval);
|
||||||
|
|
||||||
|
(void)lseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
|
while (read(fd, &ut, sizeof(UTMP)) == sizeof(UTMP)) {
|
||||||
|
if (!ut.ut_name[0]
|
||||||
|
|| strncmp(ut.ut_line, line, UT_LINESIZE))
|
||||||
|
continue;
|
||||||
|
memset(ut.ut_name, 0, UT_NAMESIZE);
|
||||||
|
memset(ut.ut_host, 0, UT_HOSTSIZE);
|
||||||
|
(void)time(&ut.ut_time);
|
||||||
|
(void)lseek(fd, -(off_t)sizeof(UTMP), SEEK_CUR);
|
||||||
|
(void)write(fd, &ut, sizeof(UTMP));
|
||||||
|
rval = 1;
|
||||||
|
}
|
||||||
|
return(rval);
|
||||||
|
}
|
||||||
|
#endif /* SUPPORT_UTMP */
|
||||||
|
|
||||||
|
#ifdef SUPPORT_UTMPX
|
||||||
|
/*
|
||||||
|
* special version of loginx which updates utmpx only.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ftpd_loginx(const struct utmpx *ut)
|
||||||
|
{
|
||||||
|
(void)pututxline(ut);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ftpd_logoutx(const char *line, int status, int mode)
|
||||||
|
{
|
||||||
|
return logoutx(line, status, mode);
|
||||||
|
}
|
||||||
|
#endif
|
145
libexec/ftpd/logwtmp.c
Normal file
145
libexec/ftpd/logwtmp.c
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
/* $NetBSD: logwtmp.c,v 1.25 2006/09/23 16:03:50 xtraeme Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1988, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#ifndef lint
|
||||||
|
#if 0
|
||||||
|
static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93";
|
||||||
|
#else
|
||||||
|
__RCSID("$NetBSD: logwtmp.c,v 1.25 2006/09/23 16:03:50 xtraeme Exp $");
|
||||||
|
#endif
|
||||||
|
#endif /* not lint */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#ifdef SUPPORT_UTMP
|
||||||
|
#include <utmp.h>
|
||||||
|
#endif
|
||||||
|
#ifdef SUPPORT_UTMPX
|
||||||
|
#include <utmpx.h>
|
||||||
|
#endif
|
||||||
|
#include <util.h>
|
||||||
|
|
||||||
|
#ifdef KERBEROS5
|
||||||
|
#include <krb5/krb5.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
|
|
||||||
|
#ifdef SUPPORT_UTMP
|
||||||
|
static int fd = -1;
|
||||||
|
|
||||||
|
void
|
||||||
|
ftpd_initwtmp(void)
|
||||||
|
{
|
||||||
|
const char *wf = _PATH_WTMP;
|
||||||
|
if ((fd = open(wf, O_WRONLY|O_APPEND, 0)) == -1)
|
||||||
|
syslog(LOG_ERR, "Cannot open `%s' (%m)", wf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Modified version of logwtmp that holds wtmp file open
|
||||||
|
* after first call, for use with ftp (which may chroot
|
||||||
|
* after login, but before logout).
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ftpd_logwtmp(const char *line, const char *name, const char *host)
|
||||||
|
{
|
||||||
|
struct utmp ut;
|
||||||
|
struct stat buf;
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
return;
|
||||||
|
if (fstat(fd, &buf) == 0) {
|
||||||
|
(void)strncpy(ut.ut_line, line, sizeof(ut.ut_line));
|
||||||
|
(void)strncpy(ut.ut_name, name, sizeof(ut.ut_name));
|
||||||
|
(void)strncpy(ut.ut_host, host, sizeof(ut.ut_host));
|
||||||
|
(void)time(&ut.ut_time);
|
||||||
|
if (write(fd, (char *)&ut, sizeof(struct utmp)) !=
|
||||||
|
sizeof(struct utmp))
|
||||||
|
(void)ftruncate(fd, buf.st_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SUPPORT_UTMPX
|
||||||
|
static int fdx = -1;
|
||||||
|
|
||||||
|
void
|
||||||
|
ftpd_initwtmpx(void)
|
||||||
|
{
|
||||||
|
const char *wf = _PATH_WTMPX;
|
||||||
|
if ((fdx = open(wf, O_WRONLY|O_APPEND, 0)) == -1)
|
||||||
|
syslog(LOG_ERR, "Cannot open `%s' (%m)", wf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ftpd_logwtmpx(const char *line, const char *name, const char *host,
|
||||||
|
struct sockinet *haddr, int status, int utx_type)
|
||||||
|
{
|
||||||
|
struct utmpx ut;
|
||||||
|
struct stat buf;
|
||||||
|
|
||||||
|
if (fdx < 0)
|
||||||
|
return;
|
||||||
|
if (fstat(fdx, &buf) == 0) {
|
||||||
|
(void)strncpy(ut.ut_line, line, sizeof(ut.ut_line));
|
||||||
|
(void)strncpy(ut.ut_name, name, sizeof(ut.ut_name));
|
||||||
|
(void)strncpy(ut.ut_host, host, sizeof(ut.ut_host));
|
||||||
|
if (haddr)
|
||||||
|
(void)memcpy(&ut.ut_ss, &haddr->si_su, haddr->su_len);
|
||||||
|
else
|
||||||
|
(void)memset(&ut.ut_ss, 0, sizeof(ut.ut_ss));
|
||||||
|
ut.ut_type = utx_type;
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
ut.ut_exit.e_exit = (uint16_t)WEXITSTATUS(status);
|
||||||
|
if (WIFSIGNALED(status))
|
||||||
|
ut.ut_exit.e_termination = (uint16_t)WTERMSIG(status);
|
||||||
|
(void)gettimeofday(&ut.ut_tv, NULL);
|
||||||
|
if(write(fdx, (char *)&ut, sizeof(struct utmpx)) !=
|
||||||
|
sizeof(struct utmpx))
|
||||||
|
(void)ftruncate(fdx, buf.st_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
48
libexec/ftpd/pathnames.h
Normal file
48
libexec/ftpd/pathnames.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/* $NetBSD: pathnames.h,v 1.12 2004/12/11 18:37:26 christos Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1989, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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.
|
||||||
|
*
|
||||||
|
* @(#)pathnames.h 8.1 (Berkeley) 6/4/93
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <paths.h>
|
||||||
|
|
||||||
|
#ifndef _DEFAULT_CONFDIR
|
||||||
|
#define _DEFAULT_CONFDIR "/etc"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _NAME_FTPCHROOT "ftpchroot"
|
||||||
|
#define _NAME_FTPDCONF "ftpd.conf"
|
||||||
|
#define _NAME_FTPLOGINMESG "motd"
|
||||||
|
#define _NAME_FTPUSERS "ftpusers"
|
||||||
|
#define _NAME_FTPWELCOME "ftpwelcome"
|
||||||
|
|
||||||
|
#define _PATH_CLASSPIDS "/var/run/ftpd.pids-"
|
||||||
|
|
||||||
|
#define TMPFILE "/tmp/ftpdXXXXXXX"
|
245
libexec/ftpd/popen.c
Normal file
245
libexec/ftpd/popen.c
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
/* $NetBSD: popen.c,v 1.37 2010/03/20 18:23:30 christos Exp $ */
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1999-2009 The NetBSD Foundation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
* by 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.
|
||||||
|
*
|
||||||
|
* 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 FOUNDATION 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1988, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software written by Ken Arnold and
|
||||||
|
* published in UNIX Review, Vol. 6, No. 8.
|
||||||
|
*
|
||||||
|
* 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. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#ifndef lint
|
||||||
|
#if 0
|
||||||
|
static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94";
|
||||||
|
#else
|
||||||
|
__RCSID("$NetBSD: popen.c,v 1.37 2010/03/20 18:23:30 christos Exp $");
|
||||||
|
#endif
|
||||||
|
#endif /* not lint */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <glob.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stringlist.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef KERBEROS5
|
||||||
|
#include <krb5/krb5.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
|
|
||||||
|
#define INCR 100
|
||||||
|
/*
|
||||||
|
* Special version of popen which avoids call to shell. This ensures no-one
|
||||||
|
* may create a pipe to a hidden program as a side effect of a list or dir
|
||||||
|
* command.
|
||||||
|
* If stderrfd != -1, then send stderr of a read command there,
|
||||||
|
* otherwise close stderr.
|
||||||
|
*/
|
||||||
|
static int *pids;
|
||||||
|
static int fds;
|
||||||
|
|
||||||
|
extern int ls_main(int, char *[]);
|
||||||
|
|
||||||
|
FILE *
|
||||||
|
ftpd_popen(const char *argv[], const char *ptype, int stderrfd)
|
||||||
|
{
|
||||||
|
FILE *iop;
|
||||||
|
int argc, pdes[2], pid;
|
||||||
|
volatile int isls;
|
||||||
|
char **pop;
|
||||||
|
StringList *sl;
|
||||||
|
|
||||||
|
iop = NULL;
|
||||||
|
isls = 0;
|
||||||
|
if ((*ptype != 'r' && *ptype != 'w') || ptype[1])
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
if (!pids) {
|
||||||
|
if ((fds = getdtablesize()) <= 0)
|
||||||
|
return (NULL);
|
||||||
|
if ((pids = (int *)malloc((unsigned int)(fds * sizeof(int)))) == NULL)
|
||||||
|
return (NULL);
|
||||||
|
memset(pids, 0, fds * sizeof(int));
|
||||||
|
}
|
||||||
|
if (pipe(pdes) < 0)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
if ((sl = sl_init()) == NULL)
|
||||||
|
goto pfree;
|
||||||
|
|
||||||
|
/* glob each piece */
|
||||||
|
if (sl_add(sl, ftpd_strdup(argv[0])) == -1)
|
||||||
|
goto pfree;
|
||||||
|
for (argc = 1; argv[argc]; argc++) {
|
||||||
|
glob_t gl;
|
||||||
|
int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE|GLOB_LIMIT;
|
||||||
|
|
||||||
|
memset(&gl, 0, sizeof(gl));
|
||||||
|
if (glob(argv[argc], flags, NULL, &gl)
|
||||||
|
|| gl.gl_pathv == NULL) {
|
||||||
|
if (sl_add(sl, ftpd_strdup(argv[argc])) == -1) {
|
||||||
|
globfree(&gl);
|
||||||
|
goto pfree;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (pop = gl.gl_pathv; *pop; pop++) {
|
||||||
|
if (sl_add(sl, ftpd_strdup(*pop)) == -1) {
|
||||||
|
globfree(&gl);
|
||||||
|
goto pfree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
globfree(&gl);
|
||||||
|
}
|
||||||
|
if (sl_add(sl, NULL) == -1)
|
||||||
|
goto pfree;
|
||||||
|
|
||||||
|
#ifndef NO_INTERNAL_LS
|
||||||
|
isls = (strcmp(sl->sl_str[0], INTERNAL_LS) == 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pid = isls ? fork() : vfork();
|
||||||
|
switch (pid) {
|
||||||
|
case -1: /* error */
|
||||||
|
(void)close(pdes[0]);
|
||||||
|
(void)close(pdes[1]);
|
||||||
|
goto pfree;
|
||||||
|
/* NOTREACHED */
|
||||||
|
case 0: /* child */
|
||||||
|
if (*ptype == 'r') {
|
||||||
|
if (pdes[1] != STDOUT_FILENO) {
|
||||||
|
dup2(pdes[1], STDOUT_FILENO);
|
||||||
|
(void)close(pdes[1]);
|
||||||
|
}
|
||||||
|
if (stderrfd == -1)
|
||||||
|
(void)close(STDERR_FILENO);
|
||||||
|
else
|
||||||
|
dup2(stderrfd, STDERR_FILENO);
|
||||||
|
(void)close(pdes[0]);
|
||||||
|
} else {
|
||||||
|
if (pdes[0] != STDIN_FILENO) {
|
||||||
|
dup2(pdes[0], STDIN_FILENO);
|
||||||
|
(void)close(pdes[0]);
|
||||||
|
}
|
||||||
|
(void)close(pdes[1]);
|
||||||
|
}
|
||||||
|
#ifndef NO_INTERNAL_LS
|
||||||
|
if (isls) { /* use internal ls */
|
||||||
|
optreset = optind = optopt = 1;
|
||||||
|
closelog();
|
||||||
|
exit(ls_main(sl->sl_cur - 1, sl->sl_str));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
execv(sl->sl_str[0], sl->sl_str);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
/* parent; assume fdopen can't fail... */
|
||||||
|
if (*ptype == 'r') {
|
||||||
|
iop = fdopen(pdes[0], ptype);
|
||||||
|
(void)close(pdes[1]);
|
||||||
|
} else {
|
||||||
|
iop = fdopen(pdes[1], ptype);
|
||||||
|
(void)close(pdes[0]);
|
||||||
|
}
|
||||||
|
pids[fileno(iop)] = pid;
|
||||||
|
|
||||||
|
pfree:
|
||||||
|
if (sl)
|
||||||
|
sl_free(sl, 1);
|
||||||
|
return (iop);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ftpd_pclose(FILE *iop)
|
||||||
|
{
|
||||||
|
int fdes, status;
|
||||||
|
pid_t pid;
|
||||||
|
sigset_t nsigset, osigset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pclose returns -1 if stream is not associated with a
|
||||||
|
* `popened' command, or, if already `pclosed'.
|
||||||
|
*/
|
||||||
|
if (pids == 0 || pids[fdes = fileno(iop)] == 0)
|
||||||
|
return (-1);
|
||||||
|
(void)fclose(iop);
|
||||||
|
sigemptyset(&nsigset);
|
||||||
|
sigaddset(&nsigset, SIGINT);
|
||||||
|
sigaddset(&nsigset, SIGQUIT);
|
||||||
|
sigaddset(&nsigset, SIGHUP);
|
||||||
|
sigprocmask(SIG_BLOCK, &nsigset, &osigset);
|
||||||
|
while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR)
|
||||||
|
continue;
|
||||||
|
sigprocmask(SIG_SETMASK, &osigset, NULL);
|
||||||
|
pids[fdes] = 0;
|
||||||
|
if (pid < 0)
|
||||||
|
return (pid);
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
return (WEXITSTATUS(status));
|
||||||
|
return (1);
|
||||||
|
}
|
33
libexec/ftpd/version.h
Normal file
33
libexec/ftpd/version.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/* $NetBSD: version.h,v 1.74 2010/03/21 20:27:26 lukem Exp $ */
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1999-2009 The NetBSD Foundation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
* by 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.
|
||||||
|
*
|
||||||
|
* 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 FOUNDATION 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 FTPD_VERSION
|
||||||
|
#define FTPD_VERSION "NetBSD-ftpd 20100320"
|
||||||
|
#endif
|
|
@ -59,6 +59,7 @@
|
||||||
2011/05/26 00:00:00,external/public-domain/xz
|
2011/05/26 00:00:00,external/public-domain/xz
|
||||||
2012/10/17 12:00:00,external/README
|
2012/10/17 12:00:00,external/README
|
||||||
2012/10/17 12:00:00,include
|
2012/10/17 12:00:00,include
|
||||||
|
2012/10/17 12:00:00,libexec/ftpd
|
||||||
2012/10/17 12:00:00,libexec/ld.elf_so
|
2012/10/17 12:00:00,libexec/ld.elf_so
|
||||||
2012/10/17 12:00:00,libexec/Makefile
|
2012/10/17 12:00:00,libexec/Makefile
|
||||||
2012/10/17 12:00:00,libexec/Makefile.inc
|
2012/10/17 12:00:00,libexec/Makefile.inc
|
||||||
|
|
Loading…
Reference in a new issue