From 3140f4b557dd4ac41dda03a4b60cfcfa1a275508 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Thu, 30 May 2013 14:47:40 +0000 Subject: [PATCH] import netbsd ftpd Change-Id: Id7a3dbd40a6f37c55bcbb0d1456301f60626298f --- distrib/sets/lists/minix/mi | 5 + etc/rc.daemons.dist | 2 +- libexec/Makefile | 2 +- libexec/ftpd/Makefile | 58 + libexec/ftpd/cmds.c | 967 +++++++++ libexec/ftpd/conf.c | 953 +++++++++ libexec/ftpd/extern.h | 380 ++++ libexec/ftpd/ftpcmd.c | 2578 ++++++++++++++++++++++ libexec/ftpd/ftpcmd.y | 1875 ++++++++++++++++ libexec/ftpd/ftpd | Bin 0 -> 555934 bytes libexec/ftpd/ftpd.8 | 871 ++++++++ libexec/ftpd/ftpd.c | 4000 +++++++++++++++++++++++++++++++++++ libexec/ftpd/ftpd.conf.5 | 738 +++++++ libexec/ftpd/ftpusers.5 | 179 ++ libexec/ftpd/logutmp.c | 172 ++ libexec/ftpd/logwtmp.c | 145 ++ libexec/ftpd/pathnames.h | 48 + libexec/ftpd/popen.c | 245 +++ libexec/ftpd/version.h | 33 + releasetools/nbsd_ports | 1 + 20 files changed, 13250 insertions(+), 2 deletions(-) create mode 100644 libexec/ftpd/Makefile create mode 100644 libexec/ftpd/cmds.c create mode 100644 libexec/ftpd/conf.c create mode 100644 libexec/ftpd/extern.h create mode 100644 libexec/ftpd/ftpcmd.c create mode 100644 libexec/ftpd/ftpcmd.y create mode 100755 libexec/ftpd/ftpd create mode 100644 libexec/ftpd/ftpd.8 create mode 100644 libexec/ftpd/ftpd.c create mode 100644 libexec/ftpd/ftpd.conf.5 create mode 100644 libexec/ftpd/ftpusers.5 create mode 100644 libexec/ftpd/logutmp.c create mode 100644 libexec/ftpd/logwtmp.c create mode 100644 libexec/ftpd/pathnames.h create mode 100644 libexec/ftpd/popen.c create mode 100644 libexec/ftpd/version.h diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 3ab7c83ce..affcc01d4 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -128,6 +128,7 @@ ./lib minix-sys ./lib/cpp minix-sys ./libexec minix-sys +./usr/libexec/ftpd minix-sys ./libexec/ld.elf_so minix-sys ./libexec/ld-elf.so.1 minix-sys ./mnt minix-sys @@ -3667,6 +3668,10 @@ ./usr/man/man5/editrc.5 minix-sys ./usr/man/man5/ethers.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/hosts.5 minix-sys ./usr/man/man5/httpd.conf.5 minix-sys diff --git a/etc/rc.daemons.dist b/etc/rc.daemons.dist index ebd7db9b2..2bb8deaf3 100644 --- a/etc/rc.daemons.dist +++ b/etc/rc.daemons.dist @@ -1,4 +1,4 @@ daemonize talkd daemonize tcpd shell in.rshd daemonize tcpd telnet in.telnetd -daemonize tcpd ftp in.ftpd +daemonize tcpd ftp /usr/libexec/ftpd diff --git a/libexec/Makefile b/libexec/Makefile index f2a9cb621..fc47af434 100644 --- a/libexec/Makefile +++ b/libexec/Makefile @@ -4,7 +4,7 @@ .include SUBDIR= \ - \ + ftpd \ ld.elf_so .if defined(__MINIX) diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile new file mode 100644 index 000000000..615c9bd7f --- /dev/null +++ b/libexec/ftpd/Makefile @@ -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 + +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 diff --git a/libexec/ftpd/cmds.c b/libexec/ftpd/cmds.c new file mode 100644 index 000000000..e3d12ee65 --- /dev/null +++ b/libexec/ftpd/cmds.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: cmds.c,v 1.31 2012/06/19 06:06:34 dholland Exp $"); +#endif /* not lint */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef KERBEROS5 +#include +#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; +} + diff --git a/libexec/ftpd/conf.c b/libexec/ftpd/conf.c new file mode 100644 index 000000000..b5d7c0fb3 --- /dev/null +++ b/libexec/ftpd/conf.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: conf.c,v 1.63 2011/08/14 11:46:28 christos Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef KERBEROS5 +#include +#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 : ""); + 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 : ""); + 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- + * 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); +} diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h new file mode 100644 index 000000000..e5266d719 --- /dev/null +++ b/libexec/ftpd/extern.h @@ -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 + +#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" : \ + "" + +#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 diff --git a/libexec/ftpd/ftpcmd.c b/libexec/ftpd/ftpcmd.c new file mode 100644 index 000000000..582a8e55a --- /dev/null +++ b/libexec/ftpd/ftpcmd.c @@ -0,0 +1,2578 @@ +#ifndef lint +static const char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93"; +#endif + +#ifdef _LIBC +#include "namespace.h" +#endif +#include +#include + +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 + +#define YYEMPTY (-1) +#define yyclearin (yychar = YYEMPTY) +#define yyerrok (yyerrflag = 0) +#define YYRECOVERING() (yyerrflag != 0) + +#define YYPREFIX "yy" + +#define YYPURE 0 + +#line 69 "ftpcmd.y" +#include + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; +#else +__RCSID("$NetBSD: ftpcmd.y,v 1.93 2011/09/16 16:13:17 plunky Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef KERBEROS5 +#include +#endif + +#include "extern.h" +#include "version.h" + +static int cmd_type; +static int cmd_form; +static int cmd_bytesz; + +char cbuf[FTP_BUFLEN]; +char *cmdp; +char *fromname; + +extern int epsvall; +struct tab sitetab[]; + +static int check_write(const char *, int); +static void help(struct tab *, const char *); +static void port_check(const char *, int); + int yylex(void); + +#line 124 "ftpcmd.y" +#ifdef YYSTYPE +#undef YYSTYPE_IS_DECLARED +#define YYSTYPE_IS_DECLARED 1 +#endif +#ifndef YYSTYPE_IS_DECLARED +#define YYSTYPE_IS_DECLARED 1 +typedef union { + struct { + LLT ll; + int i; + } u; + char *s; + const char *cs; +} YYSTYPE; +#endif /* !YYSTYPE_IS_DECLARED */ +#line 94 "ftpcmd.c" + +/* compatibility with bison */ +#ifdef YYPARSE_PARAM +/* compatibility with FreeBSD */ +# ifdef YYPARSE_PARAM_TYPE +# define YYPARSE_DECL() yyparse(YYPARSE_PARAM_TYPE YYPARSE_PARAM) +# else +# define YYPARSE_DECL() yyparse(void *YYPARSE_PARAM) +# endif +#else +# define YYPARSE_DECL() yyparse(void) +#endif + +/* Parameters sent to lex. */ +#ifdef YYLEX_PARAM +# define YYLEX_DECL() yylex(void *YYLEX_PARAM) +# define YYLEX yylex(YYLEX_PARAM) +#else +# define YYLEX_DECL() yylex(void) +# define YYLEX yylex() +#endif + +/* Parameters sent to yyerror. */ +#define YYERROR_DECL() yyerror(const char *s) +#define YYERROR_CALL(msg) yyerror(msg) + +extern int YYPARSE_DECL(); + + +#define A 257 +#define B 258 +#define C 259 +#define E 260 +#define F 261 +#define I 262 +#define L 263 +#define N 264 +#define P 265 +#define R 266 +#define S 267 +#define T 268 +#define SP 269 +#define CRLF 270 +#define COMMA 271 +#define ALL 272 +#define USER 273 +#define PASS 274 +#define ACCT 275 +#define CWD 276 +#define CDUP 277 +#define SMNT 278 +#define QUIT 279 +#define REIN 280 +#define PORT 281 +#define PASV 282 +#define TYPE 283 +#define STRU 284 +#define MODE 285 +#define RETR 286 +#define STOR 287 +#define STOU 288 +#define APPE 289 +#define ALLO 290 +#define REST 291 +#define RNFR 292 +#define RNTO 293 +#define ABOR 294 +#define DELE 295 +#define RMD 296 +#define MKD 297 +#define PWD 298 +#define LIST 299 +#define NLST 300 +#define SITE 301 +#define SYST 302 +#define STAT 303 +#define HELP 304 +#define NOOP 305 +#define AUTH 306 +#define ADAT 307 +#define PROT 308 +#define PBSZ 309 +#define CCC 310 +#define MIC 311 +#define CONF 312 +#define ENC 313 +#define FEAT 314 +#define OPTS 315 +#define SIZE 316 +#define MDTM 317 +#define MLST 318 +#define MLSD 319 +#define LPRT 320 +#define LPSV 321 +#define EPRT 322 +#define EPSV 323 +#define MAIL 324 +#define MLFL 325 +#define MRCP 326 +#define MRSQ 327 +#define MSAM 328 +#define MSND 329 +#define MSOM 330 +#define CHMOD 331 +#define IDLE 332 +#define RATEGET 333 +#define RATEPUT 334 +#define UMASK 335 +#define LEXERR 336 +#define STRING 337 +#define NUMBER 338 +#define YYERRCODE 256 +static const short yylhs[] = { -1, + 0, 0, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 12, 11, 11, 3, 18, 19, 20, 7, + 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, + 4, 4, 4, 5, 5, 5, 10, 9, 2, 13, + 14, 15, 8, 1, +}; +static const short yylen[] = { 2, + 1, 1, 4, 4, 3, 5, 3, 2, 5, 5, + 5, 5, 3, 3, 5, 5, 3, 5, 5, 5, + 5, 4, 4, 4, 5, 9, 4, 3, 4, 4, + 4, 3, 3, 5, 3, 5, 4, 8, 6, 5, + 7, 5, 7, 5, 7, 5, 7, 2, 5, 2, + 2, 4, 2, 4, 4, 4, 4, 2, 4, 4, + 4, 2, 4, 5, 5, 5, 3, 5, 3, 2, + 5, 4, 1, 0, 1, 1, 11, 17, 41, 1, + 1, 1, 1, 3, 1, 3, 1, 1, 3, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, +}; +static const short yydefred[] = { 0, + 0, 0, 0, 104, 104, 0, 104, 104, 104, 104, + 104, 104, 0, 0, 0, 104, 104, 0, 0, 104, + 0, 0, 0, 104, 104, 104, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 104, 104, 104, 104, 104, 104, 104, 0, + 1, 2, 70, 0, 0, 0, 0, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, + 50, 0, 0, 51, 53, 0, 0, 0, 0, 58, + 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 73, 0, 75, 0, 0, 5, 7, + 0, 13, 0, 0, 0, 0, 98, 97, 0, 0, + 0, 0, 0, 0, 0, 28, 0, 0, 0, 32, + 0, 33, 0, 35, 0, 0, 104, 104, 104, 104, + 0, 0, 100, 0, 101, 0, 102, 0, 103, 0, + 0, 0, 0, 0, 0, 0, 0, 67, 0, 69, + 0, 14, 0, 0, 17, 3, 4, 0, 0, 0, + 0, 0, 87, 0, 0, 91, 93, 92, 0, 95, + 96, 94, 0, 0, 22, 23, 24, 0, 0, 72, + 27, 29, 30, 31, 0, 0, 0, 37, 0, 0, + 0, 0, 0, 0, 52, 54, 55, 56, 57, 59, + 60, 61, 63, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 0, 9, 0, 0, 0, 76, + 90, 18, 19, 20, 21, 0, 25, 71, 34, 36, + 0, 99, 0, 0, 40, 0, 42, 0, 44, 0, + 46, 49, 64, 65, 66, 68, 0, 10, 11, 12, + 16, 15, 0, 82, 80, 81, 84, 86, 89, 0, + 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 43, 45, 47, 0, 0, 0, 38, 0, 0, + 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 79, +}; +static const short yydgoto[] = { 50, + 56, 243, 231, 179, 183, 175, 267, 150, 118, 119, + 107, 105, 144, 146, 148, 51, 52, 170, 219, 220, +}; +static const short yysindex[] = { -179, + -260, -245, -239, 0, 0, -231, 0, 0, 0, 0, + 0, 0, -228, -225, -205, 0, 0, -203, -199, 0, + -195, -181, -177, 0, 0, 0, -173, -194, -171, -257, + -169, -124, -109, -108, -107, -106, -104, -103, -102, -101, + -99, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -174, -166, -242, -98, 0, -96, -95, + -93, -92, -91, -90, -163, -163, -163, -89, -88, -163, + -163, -87, -163, -163, -163, -86, -233, -191, -272, 0, + 0, -84, -155, 0, 0, -151, -150, -149, -170, 0, + -150, -150, -150, 0, -148, -79, -78, -189, -187, -77, + -76, -74, -185, 0, -73, 0, -72, -163, 0, 0, + -145, 0, -217, -193, -236, -163, 0, 0, -71, -70, + -69, -142, -136, -67, -65, 0, -63, -62, -61, 0, + -163, 0, -163, 0, -183, -59, 0, 0, 0, 0, + -163, -58, 0, -57, 0, -56, 0, -55, 0, -54, + -53, -52, -51, -50, -163, -163, -163, 0, -163, 0, + -134, 0, -126, -269, 0, 0, 0, -49, -48, -46, + -47, -43, 0, -267, -45, 0, 0, 0, -42, 0, + 0, 0, -41, -40, 0, 0, 0, -119, -39, 0, + 0, 0, 0, 0, -38, -37, -110, 0, -100, -117, + -115, -113, -111, -36, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -35, -34, -33, -31, -30, -28, -27, + -26, -25, -24, 0, -85, 0, -253, -253, -83, 0, + 0, 0, 0, 0, 0, -19, 0, 0, 0, 0, + -22, 0, -29, -82, 0, -80, 0, -75, 0, -100, + 0, 0, 0, 0, 0, 0, -68, 0, 0, 0, + 0, 0, -21, 0, 0, 0, 0, 0, 0, -20, + 0, -163, -18, -16, -12, -11, -10, -64, -60, -7, + 0, 0, 0, 0, -32, -6, -4, 0, -3, -23, + 0, -17, -2, 1, -15, -14, 2, 4, -13, -9, + 0, 5, -8, 6, -5, 8, -1, 10, 3, 11, + 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 0, +}; +static const short yyrindex[] = { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 35, -169, 0, 37, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, +}; +static const short yygindex[] = { 0, + 9, 36, 42, 0, 0, 0, 32, 0, 0, -66, + 0, 0, 0, -44, 0, 0, 0, 0, 0, 0, +}; +#define YYTABLESIZE 369 +static const short yytable[] = { 120, + 121, 229, 222, 124, 125, 264, 127, 128, 129, 53, + 265, 83, 84, 57, 266, 59, 60, 61, 62, 63, + 64, 180, 181, 54, 68, 69, 108, 109, 72, 55, + 182, 135, 76, 77, 78, 131, 132, 82, 58, 171, + 65, 168, 172, 66, 173, 174, 151, 152, 153, 184, + 96, 97, 98, 99, 100, 101, 102, 103, 136, 137, + 138, 139, 140, 67, 195, 70, 196, 176, 223, 71, + 230, 177, 178, 73, 204, 80, 1, 133, 134, 157, + 158, 159, 160, 164, 165, 197, 198, 74, 214, 215, + 216, 75, 217, 2, 3, 79, 4, 5, 81, 6, + 85, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 86, 200, 201, 202, 203, 236, + 237, 244, 245, 246, 247, 248, 249, 250, 251, 87, + 88, 89, 104, 90, 91, 92, 93, 149, 94, 95, + 106, 110, 111, 117, 112, 113, 114, 115, 116, 122, + 123, 142, 126, 130, 141, 143, 145, 147, 154, 155, + 156, 161, 169, 162, 163, 188, 166, 167, 185, 186, + 187, 189, 190, 218, 191, 280, 192, 193, 194, 199, + 221, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 224, 227, 225, 226, 232, 228, 241, 233, 234, 235, + 238, 239, 240, 252, 253, 254, 255, 242, 256, 272, + 257, 258, 259, 260, 261, 262, 270, 271, 279, 278, + 0, 281, 263, 282, 230, 273, 274, 283, 284, 268, + 285, 275, 288, 0, 290, 291, 0, 292, 295, 277, + 269, 296, 299, 286, 300, 303, 305, 287, 307, 0, + 309, 311, 313, 0, 315, 276, 317, 0, 319, 0, + 321, 0, 323, 0, 325, 0, 327, 0, 329, 0, + 331, 104, 0, 74, 83, 289, 88, 78, 0, 0, + 0, 0, 0, 0, 293, 0, 0, 0, 0, 0, + 294, 0, 297, 298, 301, 0, 0, 0, 302, 304, + 0, 0, 306, 0, 0, 0, 308, 0, 0, 0, + 310, 0, 0, 0, 312, 0, 0, 0, 0, 0, + 314, 0, 316, 0, 318, 0, 320, 0, 322, 0, + 324, 0, 326, 0, 328, 0, 330, 0, 332, +}; +static const short yycheck[] = { 66, + 67, 269, 272, 70, 71, 259, 73, 74, 75, 270, + 264, 269, 270, 5, 268, 7, 8, 9, 10, 11, + 12, 258, 259, 269, 16, 17, 269, 270, 20, 269, + 267, 304, 24, 25, 26, 269, 270, 29, 270, 257, + 269, 108, 260, 269, 262, 263, 91, 92, 93, 116, + 42, 43, 44, 45, 46, 47, 48, 49, 331, 332, + 333, 334, 335, 269, 131, 269, 133, 261, 338, 269, + 338, 265, 266, 269, 141, 270, 256, 269, 270, 269, + 270, 269, 270, 269, 270, 269, 270, 269, 155, 156, + 157, 269, 159, 273, 274, 269, 276, 277, 270, 279, + 270, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 269, 137, 138, 139, 140, 269, + 270, 269, 270, 269, 270, 269, 270, 269, 270, 269, + 269, 269, 337, 270, 269, 269, 269, 338, 270, 269, + 337, 270, 269, 337, 270, 269, 269, 269, 269, 269, + 269, 337, 270, 270, 269, 337, 337, 337, 337, 269, + 269, 269, 338, 270, 269, 338, 270, 270, 270, 270, + 270, 338, 270, 338, 270, 272, 270, 270, 270, 269, + 337, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 269, 271, 270, 270, 269, 337, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 338, 270, 269, + 271, 270, 270, 270, 270, 270, 266, 270, 269, 271, + -1, 270, 338, 270, 338, 338, 337, 270, 270, 228, + 271, 337, 270, -1, 271, 270, -1, 271, 271, 338, + 229, 271, 271, 338, 271, 271, 271, 338, 271, -1, + 271, 271, 271, -1, 271, 250, 271, -1, 271, -1, + 271, -1, 271, -1, 271, -1, 271, -1, 271, -1, + 271, 269, -1, 270, 270, 338, 270, 270, -1, -1, + -1, -1, -1, -1, 338, -1, -1, -1, -1, -1, + 338, -1, 338, 338, 338, -1, -1, -1, 338, 338, + -1, -1, 338, -1, -1, -1, 338, -1, -1, -1, + 338, -1, -1, -1, 338, -1, -1, -1, -1, -1, + 338, -1, 338, -1, 338, -1, 338, -1, 338, -1, + 338, -1, 338, -1, 338, -1, 338, -1, 338, +}; +#define YYFINAL 50 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 338 +#if YYDEBUG +static const char *yyname[] = { + +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"A","B","C","E","F","I","L","N", +"P","R","S","T","SP","CRLF","COMMA","ALL","USER","PASS","ACCT","CWD","CDUP", +"SMNT","QUIT","REIN","PORT","PASV","TYPE","STRU","MODE","RETR","STOR","STOU", +"APPE","ALLO","REST","RNFR","RNTO","ABOR","DELE","RMD","MKD","PWD","LIST", +"NLST","SITE","SYST","STAT","HELP","NOOP","AUTH","ADAT","PROT","PBSZ","CCC", +"MIC","CONF","ENC","FEAT","OPTS","SIZE","MDTM","MLST","MLSD","LPRT","LPSV", +"EPRT","EPSV","MAIL","MLFL","MRCP","MRSQ","MSAM","MSND","MSOM","CHMOD","IDLE", +"RATEGET","RATEPUT","UMASK","LEXERR","STRING","NUMBER", +}; +static const char *yyrule[] = { +"$accept : cmd_sel", +"cmd_sel : cmd", +"cmd_sel : rcmd", +"cmd : USER SP username CRLF", +"cmd : PASS SP password CRLF", +"cmd : CWD check_login CRLF", +"cmd : CWD check_login SP pathname CRLF", +"cmd : CDUP check_login CRLF", +"cmd : QUIT CRLF", +"cmd : PORT check_login SP host_port CRLF", +"cmd : LPRT check_login SP host_long_port4 CRLF", +"cmd : LPRT check_login SP host_long_port6 CRLF", +"cmd : EPRT check_login SP STRING CRLF", +"cmd : PASV check_login CRLF", +"cmd : LPSV check_login CRLF", +"cmd : EPSV check_login SP NUMBER CRLF", +"cmd : EPSV check_login SP ALL CRLF", +"cmd : EPSV check_login CRLF", +"cmd : TYPE check_login SP type_code CRLF", +"cmd : STRU check_login SP struct_code CRLF", +"cmd : MODE check_login SP mode_code CRLF", +"cmd : RETR check_login SP pathname CRLF", +"cmd : STOR SP pathname CRLF", +"cmd : STOU SP pathname CRLF", +"cmd : APPE SP pathname CRLF", +"cmd : ALLO check_login SP NUMBER CRLF", +"cmd : ALLO check_login SP NUMBER SP R SP NUMBER CRLF", +"cmd : RNTO SP pathname CRLF", +"cmd : ABOR check_login CRLF", +"cmd : DELE SP pathname CRLF", +"cmd : RMD SP pathname CRLF", +"cmd : MKD SP pathname CRLF", +"cmd : PWD check_login CRLF", +"cmd : LIST check_login CRLF", +"cmd : LIST check_login SP pathname CRLF", +"cmd : NLST check_login CRLF", +"cmd : NLST check_login SP pathname CRLF", +"cmd : SITE SP HELP CRLF", +"cmd : SITE SP CHMOD SP octal_number SP pathname CRLF", +"cmd : SITE SP HELP SP STRING CRLF", +"cmd : SITE SP IDLE check_login CRLF", +"cmd : SITE SP IDLE check_login SP NUMBER CRLF", +"cmd : SITE SP RATEGET check_login CRLF", +"cmd : SITE SP RATEGET check_login SP STRING CRLF", +"cmd : SITE SP RATEPUT check_login CRLF", +"cmd : SITE SP RATEPUT check_login SP STRING CRLF", +"cmd : SITE SP UMASK check_login CRLF", +"cmd : SITE SP UMASK check_login SP octal_number CRLF", +"cmd : SYST CRLF", +"cmd : STAT check_login SP pathname CRLF", +"cmd : STAT CRLF", +"cmd : HELP CRLF", +"cmd : HELP SP STRING CRLF", +"cmd : NOOP CRLF", +"cmd : AUTH SP mechanism_name CRLF", +"cmd : ADAT SP base64data CRLF", +"cmd : PROT SP prot_code CRLF", +"cmd : PBSZ SP decimal_integer CRLF", +"cmd : CCC CRLF", +"cmd : MIC SP base64data CRLF", +"cmd : CONF SP base64data CRLF", +"cmd : ENC SP base64data CRLF", +"cmd : FEAT CRLF", +"cmd : OPTS SP STRING CRLF", +"cmd : SIZE check_login SP pathname CRLF", +"cmd : MDTM check_login SP pathname CRLF", +"cmd : MLST check_login SP pathname CRLF", +"cmd : MLST check_login CRLF", +"cmd : MLSD check_login SP pathname CRLF", +"cmd : MLSD check_login CRLF", +"cmd : error CRLF", +"rcmd : REST check_login SP NUMBER CRLF", +"rcmd : RNFR SP pathname CRLF", +"username : STRING", +"password :", +"password : STRING", +"byte_size : NUMBER", +"host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER", +"host_long_port4 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER", +"host_long_port6 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER", +"form_code : N", +"form_code : T", +"form_code : C", +"type_code : A", +"type_code : A SP form_code", +"type_code : E", +"type_code : E SP form_code", +"type_code : I", +"type_code : L", +"type_code : L SP byte_size", +"type_code : L byte_size", +"struct_code : F", +"struct_code : R", +"struct_code : P", +"mode_code : S", +"mode_code : B", +"mode_code : C", +"pathname : pathstring", +"pathstring : STRING", +"octal_number : NUMBER", +"mechanism_name : STRING", +"base64data : STRING", +"prot_code : STRING", +"decimal_integer : NUMBER", +"check_login :", + +}; +#endif + +int yydebug; +int yynerrs; + +int yyerrflag; +int yychar; +YYSTYPE yyval; +YYSTYPE yylval; + +/* define the initial stack-sizes */ +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 +#endif +#endif + +#define YYINITSTACKSIZE 500 + +typedef struct { + unsigned stacksize; + short *s_base; + short *s_mark; + short *s_last; + YYSTYPE *l_base; + YYSTYPE *l_mark; +} YYSTACKDATA; +/* variables for the parser stack */ +static YYSTACKDATA yystack; +#line 1208 "ftpcmd.y" + +#define CMD 0 /* beginning of command */ +#define ARGS 1 /* expect miscellaneous arguments */ +#define STR1 2 /* expect SP followed by STRING */ +#define STR2 3 /* expect STRING */ +#define OSTR 4 /* optional SP then STRING */ +#define ZSTR1 5 /* SP then optional STRING */ +#define ZSTR2 6 /* optional STRING after SP */ +#define SITECMD 7 /* SITE command */ +#define NSTR 8 /* Number followed by a string */ +#define NOARGS 9 /* No arguments allowed */ +#define EOLN 10 /* End of line */ + +struct tab cmdtab[] = { + /* From RFC 959, in order defined (5.3.1) */ + { "USER", USER, STR1, 1, " username", 0, }, + { "PASS", PASS, ZSTR1, 1, " password", 0, }, + { "ACCT", ACCT, STR1, 0, "(specify account)", 0, }, + { "CWD", CWD, OSTR, 1, "[ directory-name ]", 0, }, + { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, + { "SMNT", SMNT, ARGS, 0, "(structure mount)", 0, }, + { "QUIT", QUIT, NOARGS, 1, "(terminate service)", 0, }, + { "REIN", REIN, NOARGS, 0, "(reinitialize server state)", 0, }, + { "PORT", PORT, ARGS, 1, " b0, b1, b2, b3, b4, b5", 0, }, + { "LPRT", LPRT, ARGS, 1, " af, hal, h1, h2, h3,..., pal, p1, p2...", 0, }, + { "EPRT", EPRT, STR1, 1, " |af|addr|port|", 0, }, + { "PASV", PASV, NOARGS, 1, "(set server in passive mode)", 0, }, + { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)", 0, }, + { "EPSV", EPSV, ARGS, 1, "[ af|ALL]", 0, }, + { "TYPE", TYPE, ARGS, 1, " [ A | E | I | L ]", 0, }, + { "STRU", STRU, ARGS, 1, "(specify file structure)", 0, }, + { "MODE", MODE, ARGS, 1, "(specify transfer mode)", 0, }, + { "RETR", RETR, STR1, 1, " file-name", 0, }, + { "STOR", STOR, STR1, 1, " file-name", 0, }, + { "STOU", STOU, STR1, 1, " file-name", 0, }, + { "APPE", APPE, STR1, 1, " file-name", 0, }, + { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)", 0, }, + { "REST", REST, ARGS, 1, " offset (restart command)", 0, }, + { "RNFR", RNFR, STR1, 1, " file-name", 0, }, + { "RNTO", RNTO, STR1, 1, " file-name", 0, }, + { "ABOR", ABOR, NOARGS, 4, "(abort operation)", 0, }, + { "DELE", DELE, STR1, 1, " file-name", 0, }, + { "RMD", RMD, STR1, 1, " path-name", 0, }, + { "MKD", MKD, STR1, 1, " path-name", 0, }, + { "PWD", PWD, NOARGS, 1, "(return current directory)", 0, }, + { "LIST", LIST, OSTR, 1, "[ path-name ]", 0, }, + { "NLST", NLST, OSTR, 1, "[ path-name ]", 0, }, + { "SITE", SITE, SITECMD, 1, "site-cmd [ arguments ]", 0, }, + { "SYST", SYST, NOARGS, 1, "(get type of operating system)", 0, }, + { "STAT", STAT, OSTR, 4, "[ path-name ]", 0, }, + { "HELP", HELP, OSTR, 1, "[ ]", 0, }, + { "NOOP", NOOP, NOARGS, 2, "", 0, }, + + /* From RFC 2228, in order defined */ + { "AUTH", AUTH, STR1, 1, " mechanism-name", 0, }, + { "ADAT", ADAT, STR1, 1, " base-64-data", 0, }, + { "PROT", PROT, STR1, 1, " prot-code", 0, }, + { "PBSZ", PBSZ, ARGS, 1, " decimal-integer", 0, }, + { "CCC", CCC, NOARGS, 1, "(Disable data protection)", 0, }, + { "MIC", MIC, STR1, 4, " base64data", 0, }, + { "CONF", CONF, STR1, 4, " base64data", 0, }, + { "ENC", ENC, STR1, 4, " base64data", 0, }, + + /* From RFC 2389, in order defined */ + { "FEAT", FEAT, NOARGS, 1, "(display extended features)", 0, }, + { "OPTS", OPTS, STR1, 1, " command [ options ]", 0, }, + + /* From RFC 3659, in order defined */ + { "MDTM", MDTM, OSTR, 1, " path-name", 0, }, + { "SIZE", SIZE, OSTR, 1, " path-name", 0, }, + { "MLST", MLST, OSTR, 2, "[ path-name ]", 0, }, + { "MLSD", MLSD, OSTR, 1, "[ directory-name ]", 0, }, + + /* obsolete commands */ + { "MAIL", MAIL, OSTR, 0, "(mail to user)", 0, }, + { "MLFL", MLFL, OSTR, 0, "(mail file)", 0, }, + { "MRCP", MRCP, STR1, 0, "(mail recipient)", 0, }, + { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)", 0, }, + { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)", 0, }, + { "MSND", MSND, OSTR, 0, "(mail send to terminal)", 0, }, + { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)", 0, }, + { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, + { "XCWD", CWD, OSTR, 1, "[ directory-name ]", 0, }, + { "XMKD", MKD, STR1, 1, " path-name", 0, }, + { "XPWD", PWD, NOARGS, 1, "(return current directory)", 0, }, + { "XRMD", RMD, STR1, 1, " path-name", 0, }, + + { NULL, 0, 0, 0, 0, 0, } +}; + +struct tab sitetab[] = { + { "CHMOD", CHMOD, NSTR, 1, " mode file-name", 0, }, + { "HELP", HELP, OSTR, 1, "[ ]", 0, }, + { "IDLE", IDLE, ARGS, 1, "[ maximum-idle-time ]", 0, }, + { "RATEGET", RATEGET,OSTR, 1, "[ get-throttle-rate ]", 0, }, + { "RATEPUT", RATEPUT,OSTR, 1, "[ put-throttle-rate ]", 0, }, + { "UMASK", UMASK, ARGS, 1, "[ umask ]", 0, }, + { NULL, 0, 0, 0, 0, 0, } +}; + +/* + * Check if a filename is allowed to be modified (isupload == 0) or + * uploaded (isupload == 1), and if necessary, check the filename is `sane'. + * If the filename is NULL, fail. + * If the filename is "", don't do the sane name check. + */ +static int +check_write(const char *file, int isupload) +{ + if (file == NULL) + return (0); + if (! logged_in) { + reply(530, "Please login with USER and PASS."); + return (0); + } + /* checking modify */ + if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) { + reply(502, "No permission to use this command."); + return (0); + } + /* checking upload */ + if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) { + reply(502, "No permission to use this command."); + return (0); + } + + /* checking sanenames */ + if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) { + const char *p; + + if (file[0] == '.') + goto insane_name; + for (p = file; *p; p++) { + if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' || + *p == ',' || *p == '.' || *p == '_') + continue; + insane_name: + reply(553, "File name `%s' not allowed.", file); + return (0); + } + } + return (1); +} + +struct tab * +lookup(struct tab *p, const char *cmd) +{ + + for (; p->name != NULL; p++) + if (strcasecmp(cmd, p->name) == 0) + return (p); + return (0); +} + +#include + +/* + * get_line - a hacked up version of fgets to ignore TELNET escape codes. + * `s' is the buffer to read into. + * `n' is the 1 less than the size of the buffer, to allow trailing NUL + * `iop' is the FILE to read from. + * Returns 0 on success, -1 on EOF, -2 if the command was too long. + */ +int +get_line(char *s, int n, FILE *iop) +{ + int c; + char *cs; + + cs = s; +/* tmpline may contain saved command from urgent mode interruption */ + for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { + *cs++ = tmpline[c]; + if (tmpline[c] == '\n') { + *cs++ = '\0'; + if (ftpd_debug) + syslog(LOG_DEBUG, "command: %s", s); + tmpline[0] = '\0'; + return(0); + } + if (c == 0) + tmpline[0] = '\0'; + } + while ((c = getc(iop)) != EOF) { + total_bytes++; + total_bytes_in++; + c &= 0377; + if (c == IAC) { + if ((c = getc(iop)) != EOF) { + total_bytes++; + total_bytes_in++; + c &= 0377; + switch (c) { + case WILL: + case WONT: + c = getc(iop); + total_bytes++; + total_bytes_in++; + cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c); + (void) fflush(stdout); + continue; + case DO: + case DONT: + c = getc(iop); + total_bytes++; + total_bytes_in++; + cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c); + (void) fflush(stdout); + continue; + case IAC: + break; + default: + continue; /* ignore command */ + } + } + } + *cs++ = c; + if (--n <= 0) { + /* + * If command doesn't fit into buffer, discard the + * rest of the command and indicate truncation. + * This prevents the command to be split up into + * multiple commands. + */ + if (ftpd_debug) + syslog(LOG_DEBUG, + "command too long, last char: %d", c); + while (c != '\n' && (c = getc(iop)) != EOF) + continue; + return (-2); + } + if (c == '\n') + break; + } + if (c == EOF && cs == s) + return (-1); + *cs++ = '\0'; + if (ftpd_debug) { + if ((curclass.type != CLASS_GUEST && + strncasecmp(s, "PASS ", 5) == 0) || + strncasecmp(s, "ACCT ", 5) == 0) { + /* Don't syslog passwords */ + syslog(LOG_DEBUG, "command: %.4s ???", s); + } else { + char *cp; + int len; + + /* Don't syslog trailing CR-LF */ + len = strlen(s); + cp = s + len - 1; + while (cp >= s && (*cp == '\n' || *cp == '\r')) { + --cp; + --len; + } + syslog(LOG_DEBUG, "command: %.*s", len, s); + } + } + return (0); +} + +void +ftp_handle_line(char *cp) +{ + + cmdp = cp; + yyparse(); +} + +void +ftp_loop(void) +{ + int ret; + + while (1) { + (void) alarm(curclass.timeout); + ret = get_line(cbuf, sizeof(cbuf)-1, stdin); + (void) alarm(0); + if (ret == -1) { + reply(221, "You could at least say goodbye."); + dologout(0); + } else if (ret == -2) { + reply(500, "Command too long."); + } else { + ftp_handle_line(cbuf); + } + } + /*NOTREACHED*/ +} + +int +yylex(void) +{ + static int cpos, state; + char *cp, *cp2; + struct tab *p; + int n; + char c; + + switch (state) { + + case CMD: + hasyyerrored = 0; + if ((cp = strchr(cmdp, '\r'))) { + *cp = '\0'; +#if defined(HAVE_SETPROCTITLE) + if (strncasecmp(cmdp, "PASS", 4) != 0 && + strncasecmp(cmdp, "ACCT", 4) != 0) + setproctitle("%s: %s", proctitle, cmdp); +#endif /* defined(HAVE_SETPROCTITLE) */ + *cp++ = '\n'; + *cp = '\0'; + } + if ((cp = strpbrk(cmdp, " \n"))) + cpos = cp - cmdp; + if (cpos == 0) + cpos = 4; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + p = lookup(cmdtab, cmdp); + cmdp[cpos] = c; + if (p != NULL) { + if (is_oob && ! CMD_OOB(p)) { + /* command will be handled in-band */ + return (0); + } else if (! CMD_IMPLEMENTED(p)) { + reply(502, "%s command not implemented.", + p->name); + hasyyerrored = 1; + break; + } + state = p->state; + yylval.cs = p->name; + return (p->token); + } + break; + + case SITECMD: + if (cmdp[cpos] == ' ') { + cpos++; + return (SP); + } + cp = &cmdp[cpos]; + if ((cp2 = strpbrk(cp, " \n"))) + cpos = cp2 - cmdp; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + p = lookup(sitetab, cp); + cmdp[cpos] = c; + if (p != NULL) { + if (!CMD_IMPLEMENTED(p)) { + reply(502, "SITE %s command not implemented.", + p->name); + hasyyerrored = 1; + break; + } + state = p->state; + yylval.cs = p->name; + return (p->token); + } + break; + + case OSTR: + if (cmdp[cpos] == '\n') { + state = EOLN; + return (CRLF); + } + /* FALLTHROUGH */ + + case STR1: + case ZSTR1: + dostr1: + if (cmdp[cpos] == ' ') { + cpos++; + state = state == OSTR ? STR2 : state+1; + return (SP); + } + break; + + case ZSTR2: + if (cmdp[cpos] == '\n') { + state = EOLN; + return (CRLF); + } + /* FALLTHROUGH */ + + case STR2: + cp = &cmdp[cpos]; + n = strlen(cp); + cpos += n - 1; + /* + * Make sure the string is nonempty and \n terminated. + */ + if (n > 1 && cmdp[cpos] == '\n') { + cmdp[cpos] = '\0'; + yylval.s = ftpd_strdup(cp); + cmdp[cpos] = '\n'; + state = ARGS; + return (STRING); + } + break; + + case NSTR: + if (cmdp[cpos] == ' ') { + cpos++; + return (SP); + } + if (isdigit((unsigned char)cmdp[cpos])) { + cp = &cmdp[cpos]; + while (isdigit((unsigned char)cmdp[++cpos])) + ; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + yylval.u.i = atoi(cp); + cmdp[cpos] = c; + state = STR1; + return (NUMBER); + } + state = STR1; + goto dostr1; + + case ARGS: + if (isdigit((unsigned char)cmdp[cpos])) { + cp = &cmdp[cpos]; + while (isdigit((unsigned char)cmdp[++cpos])) + ; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + yylval.u.i = atoi(cp); + yylval.u.ll = STRTOLL(cp, NULL, 10); + cmdp[cpos] = c; + return (NUMBER); + } + if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 + && !isalnum((unsigned char)cmdp[cpos + 3])) { + cpos += 3; + return (ALL); + } + switch (cmdp[cpos++]) { + + case '\n': + state = EOLN; + return (CRLF); + + case ' ': + return (SP); + + case ',': + return (COMMA); + + case 'A': + case 'a': + return (A); + + case 'B': + case 'b': + return (B); + + case 'C': + case 'c': + return (C); + + case 'E': + case 'e': + return (E); + + case 'F': + case 'f': + return (F); + + case 'I': + case 'i': + return (I); + + case 'L': + case 'l': + return (L); + + case 'N': + case 'n': + return (N); + + case 'P': + case 'p': + return (P); + + case 'R': + case 'r': + return (R); + + case 'S': + case 's': + return (S); + + case 'T': + case 't': + return (T); + + } + break; + + case NOARGS: + if (cmdp[cpos] == '\n') { + state = EOLN; + return (CRLF); + } + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + reply(501, "'%s' command does not take any arguments.", cmdp); + hasyyerrored = 1; + cmdp[cpos] = c; + break; + + case EOLN: + state = CMD; + return (0); + + default: + fatal("Unknown state in scanner."); + } + yyerror(NULL); + state = CMD; + return (0); +} + +/* ARGSUSED */ +void +yyerror(const char *s) +{ + char *cp; + + if (hasyyerrored || is_oob) + return; + if ((cp = strchr(cmdp,'\n')) != NULL) + *cp = '\0'; + reply(500, "'%s': command not understood.", cmdp); + hasyyerrored = 1; +} + +static void +help(struct tab *ctab, const char *s) +{ + struct tab *c; + int width, NCMDS; + const char *htype; + + if (ctab == sitetab) + htype = "SITE "; + else + htype = ""; + width = 0, NCMDS = 0; + for (c = ctab; c->name != NULL; c++) { + int len = strlen(c->name); + + if (len > width) + width = len; + NCMDS++; + } + width = (width + 8) &~ 7; + if (s == 0) { + int i, j, w; + int columns, lines; + + reply(-214, "%s", ""); + reply(0, "The following %scommands are recognized.", htype); + reply(0, "(`-' = not implemented, `+' = supports options)"); + columns = 76 / width; + if (columns == 0) + columns = 1; + lines = (NCMDS + columns - 1) / columns; + for (i = 0; i < lines; i++) { + cprintf(stdout, " "); + for (j = 0; j < columns; j++) { + c = ctab + j * lines + i; + cprintf(stdout, "%s", c->name); + w = strlen(c->name); + if (! CMD_IMPLEMENTED(c)) { + CPUTC('-', stdout); + w++; + } + if (CMD_HAS_OPTIONS(c)) { + CPUTC('+', stdout); + w++; + } + if (c + lines >= &ctab[NCMDS]) + break; + while (w < width) { + CPUTC(' ', stdout); + w++; + } + } + cprintf(stdout, "\r\n"); + } + (void) fflush(stdout); + reply(214, "Direct comments to ftp-bugs@%s.", hostname); + return; + } + c = lookup(ctab, s); + if (c == (struct tab *)0) { + reply(502, "Unknown command '%s'.", s); + return; + } + if (CMD_IMPLEMENTED(c)) + reply(214, "Syntax: %s%s %s", htype, c->name, c->help); + else + reply(504, "%s%-*s\t%s; not implemented.", htype, width, + c->name, c->help); +} + +/* + * Check that the structures used for a PORT, LPRT or EPRT command are + * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks. + * If family != -1 check that his_addr.su_family == family. + */ +static void +port_check(const char *cmd, int family) +{ + char h1[NI_MAXHOST], h2[NI_MAXHOST]; + char s1[NI_MAXHOST], s2[NI_MAXHOST]; +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; +#endif + + if (epsvall) { + reply(501, "%s disallowed after EPSV ALL", cmd); + return; + } + + if (family != -1 && his_addr.su_family != family) { + port_check_fail: + reply(500, "Illegal %s command rejected", cmd); + return; + } + + if (data_dest.su_family != his_addr.su_family) + goto port_check_fail; + + /* be paranoid, if told so */ + if (CURCLASS_FLAGS_ISSET(checkportcmd)) { +#ifdef INET6 + /* + * be paranoid, there are getnameinfo implementation that does + * not present scopeid portion + */ + if (data_dest.su_family == AF_INET6 && + data_dest.su_scope_id != his_addr.su_scope_id) + goto port_check_fail; +#endif + + if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, + h1, sizeof(h1), s1, sizeof(s1), niflags)) + goto port_check_fail; + if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + h2, sizeof(h2), s2, sizeof(s2), niflags)) + goto port_check_fail; + + if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) + goto port_check_fail; + } + + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "%s command successful.", cmd); +} +#line 1263 "ftpcmd.c" + +#if YYDEBUG +#include /* needed for printf */ +#endif + +#include /* needed for malloc, etc */ +#include /* needed for memset */ + +/* allocate initial stack or double stack size, up to YYMAXDEPTH */ +static int yygrowstack(YYSTACKDATA *data) +{ + int i; + unsigned newsize; + short *newss; + YYSTYPE *newvs; + + if ((newsize = data->stacksize) == 0) + newsize = YYINITSTACKSIZE; + else if (newsize >= YYMAXDEPTH) + return -1; + else if ((newsize *= 2) > YYMAXDEPTH) + newsize = YYMAXDEPTH; + + i = data->s_mark - data->s_base; + newss = (short *)realloc(data->s_base, newsize * sizeof(*newss)); + if (newss == 0) + return -1; + + data->s_base = newss; + data->s_mark = newss + i; + + newvs = (YYSTYPE *)realloc(data->l_base, newsize * sizeof(*newvs)); + if (newvs == 0) + return -1; + + data->l_base = newvs; + data->l_mark = newvs + i; + + data->stacksize = newsize; + data->s_last = data->s_base + newsize - 1; + return 0; +} + +#if YYPURE || defined(YY_NO_LEAKS) +static void yyfreestack(YYSTACKDATA *data) +{ + free(data->s_base); + free(data->l_base); + memset(data, 0, sizeof(*data)); +} +#else +#define yyfreestack(data) /* nothing */ +#endif + +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab + +int +YYPARSE_DECL() +{ + int yym, yyn, yystate; +#if YYDEBUG + const char *yys; + + if ((yys = getenv("YYDEBUG")) != 0) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = YYEMPTY; + yystate = 0; + +#if YYPURE + memset(&yystack, 0, sizeof(yystack)); +#endif + + if (yystack.s_base == NULL && yygrowstack(&yystack)) goto yyoverflow; + yystack.s_mark = yystack.s_base; + yystack.l_mark = yystack.l_base; + yystate = 0; + *yystack.s_mark = 0; + +yyloop: + if ((yyn = yydefred[yystate]) != 0) goto yyreduce; + if (yychar < 0) + { + if ((yychar = YYLEX) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); +#endif + if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack)) + { + goto yyoverflow; + } + yystate = yytable[yyn]; + *++yystack.s_mark = yytable[yyn]; + *++yystack.l_mark = yylval; + yychar = YYEMPTY; + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; + + yyerror("syntax error"); + + goto yyerrlab; + +yyerrlab: + ++yynerrs; + +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yystack.s_mark]) && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yystack.s_mark, yytable[yyn]); +#endif + if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack)) + { + goto yyoverflow; + } + yystate = yytable[yyn]; + *++yystack.s_mark = yytable[yyn]; + *++yystack.l_mark = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yystack.s_mark); +#endif + if (yystack.s_mark <= yystack.s_base) goto yyabort; + --yystack.s_mark; + --yystack.l_mark; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + yychar = YYEMPTY; + goto yyloop; + } + +yyreduce: +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + if (yym) + yyval = yystack.l_mark[1-yym]; + else + memset(&yyval, 0, sizeof yyval); + switch (yyn) + { +case 1: +#line 176 "ftpcmd.y" + { + REASSIGN(fromname, NULL); + restart_point = (off_t) 0; + } +break; +case 3: +#line 188 "ftpcmd.y" + { + user(yystack.l_mark[-1].s); + free(yystack.l_mark[-1].s); + } +break; +case 4: +#line 194 "ftpcmd.y" + { + pass(yystack.l_mark[-1].s); + memset(yystack.l_mark[-1].s, 0, strlen(yystack.l_mark[-1].s)); + free(yystack.l_mark[-1].s); + } +break; +case 5: +#line 201 "ftpcmd.y" + { + if (yystack.l_mark[-1].u.i) + cwd(homedir); + } +break; +case 6: +#line 207 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i && yystack.l_mark[-1].s != NULL) + cwd(yystack.l_mark[-1].s); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 7: +#line 215 "ftpcmd.y" + { + if (yystack.l_mark[-1].u.i) + cwd(".."); + } +break; +case 8: +#line 221 "ftpcmd.y" + { + if (logged_in) { + reply(-221, "%s", ""); + reply(0, + "Data traffic for this session was " LLF " byte%s in " LLF " file%s.", + (LLT)total_data, PLURAL(total_data), + (LLT)total_files, PLURAL(total_files)); + reply(0, + "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.", + (LLT)total_bytes, PLURAL(total_bytes), + (LLT)total_xfers, PLURAL(total_xfers)); + } + reply(221, + "Thank you for using the FTP service on %s.", + hostname); + if (logged_in && logging) { + syslog(LOG_INFO, + "Data traffic: " LLF " byte%s in " LLF " file%s", + (LLT)total_data, PLURAL(total_data), + (LLT)total_files, PLURAL(total_files)); + syslog(LOG_INFO, + "Total traffic: " LLF " byte%s in " LLF " transfer%s", + (LLT)total_bytes, PLURAL(total_bytes), + (LLT)total_xfers, PLURAL(total_xfers)); + } + + dologout(0); + } +break; +case 9: +#line 251 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) + port_check("PORT", AF_INET); + } +break; +case 10: +#line 257 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) + port_check("LPRT", AF_INET); + } +break; +case 11: +#line 263 "ftpcmd.y" + { +#ifdef INET6 + if (yystack.l_mark[-3].u.i) + port_check("LPRT", AF_INET6); +#else + reply(500, "IPv6 support not available."); +#endif + } +break; +case 12: +#line 273 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) { + if (extended_port(yystack.l_mark[-1].s) == 0) + port_check("EPRT", -1); + } + free(yystack.l_mark[-1].s); + } +break; +case 13: +#line 282 "ftpcmd.y" + { + if (yystack.l_mark[-1].u.i) { + if (CURCLASS_FLAGS_ISSET(passive)) + passive(); + else + reply(500, "PASV mode not available."); + } + } +break; +case 14: +#line 292 "ftpcmd.y" + { + if (yystack.l_mark[-1].u.i) { + if (CURCLASS_FLAGS_ISSET(passive)) { + if (epsvall) + reply(501, + "LPSV disallowed after EPSV ALL"); + else + long_passive("LPSV", PF_UNSPEC); + } else + reply(500, "LPSV mode not available."); + } + } +break; +case 15: +#line 306 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) { + if (CURCLASS_FLAGS_ISSET(passive)) + long_passive("EPSV", + epsvproto2af(yystack.l_mark[-1].u.i)); + else + reply(500, "EPSV mode not available."); + } + } +break; +case 16: +#line 317 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) { + if (CURCLASS_FLAGS_ISSET(passive)) { + reply(200, + "EPSV ALL command successful."); + epsvall++; + } else + reply(500, "EPSV mode not available."); + } + } +break; +case 17: +#line 329 "ftpcmd.y" + { + if (yystack.l_mark[-1].u.i) { + if (CURCLASS_FLAGS_ISSET(passive)) + long_passive("EPSV", PF_UNSPEC); + else + reply(500, "EPSV mode not available."); + } + } +break; +case 18: +#line 339 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) { + + switch (cmd_type) { + + case TYPE_A: + if (cmd_form == FORM_N) { + reply(200, "Type set to A."); + type = cmd_type; + form = cmd_form; + } else + reply(504, "Form must be N."); + break; + + case TYPE_E: + reply(504, "Type E not implemented."); + break; + + case TYPE_I: + reply(200, "Type set to I."); + type = cmd_type; + break; + + case TYPE_L: +#if NBBY == 8 + if (cmd_bytesz == 8) { + reply(200, + "Type set to L (byte size 8)."); + type = cmd_type; + } else + reply(504, "Byte size must be 8."); +#else /* NBBY == 8 */ + UNIMPLEMENTED for NBBY != 8 +#endif /* NBBY == 8 */ + } + + } + } +break; +case 19: +#line 379 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) { + switch (yystack.l_mark[-1].u.i) { + + case STRU_F: + reply(200, "STRU F ok."); + break; + + default: + reply(504, "Unimplemented STRU type."); + } + } + } +break; +case 20: +#line 394 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) { + switch (yystack.l_mark[-1].u.i) { + + case MODE_S: + reply(200, "MODE S ok."); + break; + + default: + reply(502, "Unimplemented MODE type."); + } + } + } +break; +case 21: +#line 409 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i && yystack.l_mark[-1].s != NULL) + retrieve(NULL, yystack.l_mark[-1].s); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 22: +#line 417 "ftpcmd.y" + { + if (check_write(yystack.l_mark[-1].s, 1)) + store(yystack.l_mark[-1].s, "w", 0); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 23: +#line 425 "ftpcmd.y" + { + if (check_write(yystack.l_mark[-1].s, 1)) + store(yystack.l_mark[-1].s, "w", 1); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 24: +#line 433 "ftpcmd.y" + { + if (check_write(yystack.l_mark[-1].s, 1)) + store(yystack.l_mark[-1].s, "a", 0); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 25: +#line 441 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) + reply(202, "ALLO command ignored."); + } +break; +case 26: +#line 447 "ftpcmd.y" + { + if (yystack.l_mark[-7].u.i) + reply(202, "ALLO command ignored."); + } +break; +case 27: +#line 453 "ftpcmd.y" + { + if (check_write(yystack.l_mark[-1].s, 0)) { + if (fromname) { + renamecmd(fromname, yystack.l_mark[-1].s); + REASSIGN(fromname, NULL); + } else { + reply(503, "Bad sequence of commands."); + } + } + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 28: +#line 467 "ftpcmd.y" + { + if (is_oob) + abor(); + else if (yystack.l_mark[-1].u.i) + reply(225, "ABOR command successful."); + } +break; +case 29: +#line 475 "ftpcmd.y" + { + if (check_write(yystack.l_mark[-1].s, 0)) + delete(yystack.l_mark[-1].s); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 30: +#line 483 "ftpcmd.y" + { + if (check_write(yystack.l_mark[-1].s, 0)) + removedir(yystack.l_mark[-1].s); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 31: +#line 491 "ftpcmd.y" + { + if (check_write(yystack.l_mark[-1].s, 0)) + makedir(yystack.l_mark[-1].s); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 32: +#line 499 "ftpcmd.y" + { + if (yystack.l_mark[-1].u.i) + pwd(); + } +break; +case 33: +#line 505 "ftpcmd.y" + { + const char *argv[] = { INTERNAL_LS, "-lgA", NULL }; + + if (CURCLASS_FLAGS_ISSET(hidesymlinks)) + argv[1] = "-LlgA"; + if (yystack.l_mark[-1].u.i) + retrieve(argv, ""); + } +break; +case 34: +#line 515 "ftpcmd.y" + { + const char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; + + if (CURCLASS_FLAGS_ISSET(hidesymlinks)) + argv[1] = "-LlgA"; + if (yystack.l_mark[-3].u.i && yystack.l_mark[-1].s != NULL) { + argv[2] = yystack.l_mark[-1].s; + retrieve(argv, yystack.l_mark[-1].s); + } + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 35: +#line 529 "ftpcmd.y" + { + if (yystack.l_mark[-1].u.i) + send_file_list("."); + } +break; +case 36: +#line 535 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) + send_file_list(yystack.l_mark[-1].s); + free(yystack.l_mark[-1].s); + } +break; +case 37: +#line 542 "ftpcmd.y" + { + help(sitetab, NULL); + } +break; +case 38: +#line 547 "ftpcmd.y" + { + if (check_write(yystack.l_mark[-1].s, 0)) { + if ((yystack.l_mark[-3].u.i == -1) || (yystack.l_mark[-3].u.i > 0777)) + reply(501, + "CHMOD: Mode value must be between 0 and 0777"); + else if (chmod(yystack.l_mark[-1].s, yystack.l_mark[-3].u.i) < 0) + perror_reply(550, yystack.l_mark[-1].s); + else + reply(200, "CHMOD command successful."); + } + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 39: +#line 562 "ftpcmd.y" + { + help(sitetab, yystack.l_mark[-1].s); + free(yystack.l_mark[-1].s); + } +break; +case 40: +#line 568 "ftpcmd.y" + { + if (yystack.l_mark[-1].u.i) { + reply(200, + "Current IDLE time limit is " LLF + " seconds; max " LLF, + (LLT)curclass.timeout, + (LLT)curclass.maxtimeout); + } + } +break; +case 41: +#line 579 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) { + if (yystack.l_mark[-1].u.i < 30 || yystack.l_mark[-1].u.i > curclass.maxtimeout) { + reply(501, + "IDLE time limit must be between 30 and " + LLF " seconds", + (LLT)curclass.maxtimeout); + } else { + curclass.timeout = yystack.l_mark[-1].u.i; + (void) alarm(curclass.timeout); + reply(200, + "IDLE time limit set to " + LLF " seconds", + (LLT)curclass.timeout); + } + } + } +break; +case 42: +#line 598 "ftpcmd.y" + { + if (yystack.l_mark[-1].u.i) { + reply(200, + "Current RATEGET is " LLF " bytes/sec", + (LLT)curclass.rateget); + } + } +break; +case 43: +#line 607 "ftpcmd.y" + { + char errbuf[100]; + char *p = yystack.l_mark[-1].s; + LLT rate; + + if (yystack.l_mark[-3].u.i) { + rate = strsuftollx("RATEGET", p, 0, + curclass.maxrateget + ? curclass.maxrateget + : LLTMAX, errbuf, sizeof(errbuf)); + if (errbuf[0]) + reply(501, "%s", errbuf); + else { + curclass.rateget = rate; + reply(200, + "RATEGET set to " LLF " bytes/sec", + (LLT)curclass.rateget); + } + } + free(yystack.l_mark[-1].s); + } +break; +case 44: +#line 630 "ftpcmd.y" + { + if (yystack.l_mark[-1].u.i) { + reply(200, + "Current RATEPUT is " LLF " bytes/sec", + (LLT)curclass.rateput); + } + } +break; +case 45: +#line 639 "ftpcmd.y" + { + char errbuf[100]; + char *p = yystack.l_mark[-1].s; + LLT rate; + + if (yystack.l_mark[-3].u.i) { + rate = strsuftollx("RATEPUT", p, 0, + curclass.maxrateput + ? curclass.maxrateput + : LLTMAX, errbuf, sizeof(errbuf)); + if (errbuf[0]) + reply(501, "%s", errbuf); + else { + curclass.rateput = rate; + reply(200, + "RATEPUT set to " LLF " bytes/sec", + (LLT)curclass.rateput); + } + } + free(yystack.l_mark[-1].s); + } +break; +case 46: +#line 662 "ftpcmd.y" + { + int oldmask; + + if (yystack.l_mark[-1].u.i) { + oldmask = umask(0); + (void) umask(oldmask); + reply(200, "Current UMASK is %03o", oldmask); + } + } +break; +case 47: +#line 673 "ftpcmd.y" + { + int oldmask; + + if (yystack.l_mark[-3].u.i && check_write("", 0)) { + if ((yystack.l_mark[-1].u.i == -1) || (yystack.l_mark[-1].u.i > 0777)) { + reply(501, "Bad UMASK value"); + } else { + oldmask = umask(yystack.l_mark[-1].u.i); + reply(200, + "UMASK set to %03o (was %03o)", + yystack.l_mark[-1].u.i, oldmask); + } + } + } +break; +case 48: +#line 689 "ftpcmd.y" + { + if (EMPTYSTR(version)) + reply(215, "UNIX Type: L%d", NBBY); + else + reply(215, "UNIX Type: L%d Version: %s", NBBY, + version); + } +break; +case 49: +#line 698 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i && yystack.l_mark[-1].s != NULL) + statfilecmd(yystack.l_mark[-1].s); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 50: +#line 706 "ftpcmd.y" + { + if (is_oob) + statxfer(); + else + statcmd(); + } +break; +case 51: +#line 714 "ftpcmd.y" + { + help(cmdtab, NULL); + } +break; +case 52: +#line 719 "ftpcmd.y" + { + char *cp = yystack.l_mark[-1].s; + + if (strncasecmp(cp, "SITE", 4) == 0) { + cp = yystack.l_mark[-1].s + 4; + if (*cp == ' ') + cp++; + if (*cp) + help(sitetab, cp); + else + help(sitetab, NULL); + } else + help(cmdtab, yystack.l_mark[-1].s); + free(yystack.l_mark[-1].s); + } +break; +case 53: +#line 736 "ftpcmd.y" + { + reply(200, "NOOP command successful."); + } +break; +case 54: +#line 742 "ftpcmd.y" + { + reply(502, "RFC 2228 authentication not implemented."); + free(yystack.l_mark[-1].s); + } +break; +case 55: +#line 748 "ftpcmd.y" + { + reply(503, + "Please set authentication state with AUTH."); + free(yystack.l_mark[-1].s); + } +break; +case 56: +#line 755 "ftpcmd.y" + { + reply(503, + "Please set protection buffer size with PBSZ."); + free(yystack.l_mark[-1].s); + } +break; +case 57: +#line 762 "ftpcmd.y" + { + reply(503, + "Please set authentication state with AUTH."); + } +break; +case 58: +#line 768 "ftpcmd.y" + { + reply(533, "No protection enabled."); + } +break; +case 59: +#line 773 "ftpcmd.y" + { + reply(502, "RFC 2228 authentication not implemented."); + free(yystack.l_mark[-1].s); + } +break; +case 60: +#line 779 "ftpcmd.y" + { + reply(502, "RFC 2228 authentication not implemented."); + free(yystack.l_mark[-1].s); + } +break; +case 61: +#line 785 "ftpcmd.y" + { + reply(502, "RFC 2228 authentication not implemented."); + free(yystack.l_mark[-1].s); + } +break; +case 62: +#line 792 "ftpcmd.y" + { + + feat(); + } +break; +case 63: +#line 798 "ftpcmd.y" + { + + opts(yystack.l_mark[-1].s); + free(yystack.l_mark[-1].s); + } +break; +case 64: +#line 812 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i && yystack.l_mark[-1].s != NULL) + sizecmd(yystack.l_mark[-1].s); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 65: +#line 826 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i && yystack.l_mark[-1].s != NULL) { + struct stat stbuf; + if (stat(yystack.l_mark[-1].s, &stbuf) < 0) + perror_reply(550, yystack.l_mark[-1].s); + else if (!S_ISREG(stbuf.st_mode)) { + reply(550, "%s: not a plain file.", yystack.l_mark[-1].s); + } else { + struct tm *t; + + t = gmtime(&stbuf.st_mtime); + reply(213, + "%04d%02d%02d%02d%02d%02d", + TM_YEAR_BASE + t->tm_year, + t->tm_mon+1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + } + } + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 66: +#line 849 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i && yystack.l_mark[-1].s != NULL) + mlst(yystack.l_mark[-1].s); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 67: +#line 857 "ftpcmd.y" + { + mlst(NULL); + } +break; +case 68: +#line 862 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i && yystack.l_mark[-1].s != NULL) + mlsd(yystack.l_mark[-1].s); + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 69: +#line 870 "ftpcmd.y" + { + mlsd(NULL); + } +break; +case 70: +#line 875 "ftpcmd.y" + { + yyerrok; + } +break; +case 71: +#line 882 "ftpcmd.y" + { + if (yystack.l_mark[-3].u.i) { + REASSIGN(fromname, NULL); + restart_point = (off_t)yystack.l_mark[-1].u.ll; + reply(350, + "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", + (LLT)restart_point); + } + } +break; +case 72: +#line 893 "ftpcmd.y" + { + restart_point = (off_t) 0; + if (check_write(yystack.l_mark[-1].s, 0)) { + REASSIGN(fromname, NULL); + fromname = renamefrom(yystack.l_mark[-1].s); + } + if (yystack.l_mark[-1].s != NULL) + free(yystack.l_mark[-1].s); + } +break; +case 74: +#line 910 "ftpcmd.y" + { + yyval.s = (char *)calloc(1, sizeof(char)); + } +break; +case 76: +#line 919 "ftpcmd.y" + { + yyval.u.i = yystack.l_mark[0].u.i; + } +break; +case 77: +#line 927 "ftpcmd.y" + { + char *a, *p; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_len = sizeof(struct sockaddr_in); + data_dest.su_family = AF_INET; + p = (char *)&data_dest.su_port; + p[0] = yystack.l_mark[-2].u.i; p[1] = yystack.l_mark[0].u.i; + a = (char *)&data_dest.su_addr; + a[0] = yystack.l_mark[-10].u.i; a[1] = yystack.l_mark[-8].u.i; a[2] = yystack.l_mark[-6].u.i; a[3] = yystack.l_mark[-4].u.i; + } +break; +case 78: +#line 944 "ftpcmd.y" + { + char *a, *p; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_len = sizeof(struct sockaddr_in); + data_dest.su_family = AF_INET; + p = (char *)&data_dest.su_port; + p[0] = yystack.l_mark[-2].u.i; p[1] = yystack.l_mark[0].u.i; + a = (char *)&data_dest.su_addr; + a[0] = yystack.l_mark[-12].u.i; a[1] = yystack.l_mark[-10].u.i; a[2] = yystack.l_mark[-8].u.i; a[3] = yystack.l_mark[-6].u.i; + + /* reject invalid LPRT command */ + if (yystack.l_mark[-16].u.i != 4 || yystack.l_mark[-14].u.i != 4 || yystack.l_mark[-4].u.i != 2) + memset(&data_dest, 0, sizeof(data_dest)); + } +break; +case 79: +#line 968 "ftpcmd.y" + { +#ifdef INET6 + unsigned char buf[16]; + + (void)memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_len = sizeof(struct sockaddr_in6); + data_dest.su_family = AF_INET6; + buf[0] = yystack.l_mark[-2].u.i; buf[1] = yystack.l_mark[0].u.i; + (void)memcpy(&data_dest.su_port, buf, + sizeof(data_dest.su_port)); + buf[0] = yystack.l_mark[-36].u.i; buf[1] = yystack.l_mark[-34].u.i; + buf[2] = yystack.l_mark[-32].u.i; buf[3] = yystack.l_mark[-30].u.i; + buf[4] = yystack.l_mark[-28].u.i; buf[5] = yystack.l_mark[-26].u.i; + buf[6] = yystack.l_mark[-24].u.i; buf[7] = yystack.l_mark[-22].u.i; + buf[8] = yystack.l_mark[-20].u.i; buf[9] = yystack.l_mark[-18].u.i; + buf[10] = yystack.l_mark[-16].u.i; buf[11] = yystack.l_mark[-14].u.i; + buf[12] = yystack.l_mark[-12].u.i; buf[13] = yystack.l_mark[-10].u.i; + buf[14] = yystack.l_mark[-8].u.i; buf[15] = yystack.l_mark[-6].u.i; + (void)memcpy(&data_dest.si_su.su_sin6.sin6_addr, + buf, sizeof(data_dest.si_su.su_sin6.sin6_addr)); + if (his_addr.su_family == AF_INET6) { + /* XXX: more sanity checks! */ + data_dest.su_scope_id = his_addr.su_scope_id; + } +#else + memset(&data_dest, 0, sizeof(data_dest)); +#endif /* INET6 */ + /* reject invalid LPRT command */ + if (yystack.l_mark[-40].u.i != 6 || yystack.l_mark[-38].u.i != 16 || yystack.l_mark[-4].u.i != 2) + memset(&data_dest, 0, sizeof(data_dest)); + } +break; +case 80: +#line 1003 "ftpcmd.y" + { + yyval.u.i = FORM_N; + } +break; +case 81: +#line 1008 "ftpcmd.y" + { + yyval.u.i = FORM_T; + } +break; +case 82: +#line 1013 "ftpcmd.y" + { + yyval.u.i = FORM_C; + } +break; +case 83: +#line 1020 "ftpcmd.y" + { + cmd_type = TYPE_A; + cmd_form = FORM_N; + } +break; +case 84: +#line 1026 "ftpcmd.y" + { + cmd_type = TYPE_A; + cmd_form = yystack.l_mark[0].u.i; + } +break; +case 85: +#line 1032 "ftpcmd.y" + { + cmd_type = TYPE_E; + cmd_form = FORM_N; + } +break; +case 86: +#line 1038 "ftpcmd.y" + { + cmd_type = TYPE_E; + cmd_form = yystack.l_mark[0].u.i; + } +break; +case 87: +#line 1044 "ftpcmd.y" + { + cmd_type = TYPE_I; + } +break; +case 88: +#line 1049 "ftpcmd.y" + { + cmd_type = TYPE_L; + cmd_bytesz = NBBY; + } +break; +case 89: +#line 1055 "ftpcmd.y" + { + cmd_type = TYPE_L; + cmd_bytesz = yystack.l_mark[0].u.i; + } +break; +case 90: +#line 1062 "ftpcmd.y" + { + cmd_type = TYPE_L; + cmd_bytesz = yystack.l_mark[0].u.i; + } +break; +case 91: +#line 1070 "ftpcmd.y" + { + yyval.u.i = STRU_F; + } +break; +case 92: +#line 1075 "ftpcmd.y" + { + yyval.u.i = STRU_R; + } +break; +case 93: +#line 1080 "ftpcmd.y" + { + yyval.u.i = STRU_P; + } +break; +case 94: +#line 1087 "ftpcmd.y" + { + yyval.u.i = MODE_S; + } +break; +case 95: +#line 1092 "ftpcmd.y" + { + yyval.u.i = MODE_B; + } +break; +case 96: +#line 1097 "ftpcmd.y" + { + yyval.u.i = MODE_C; + } +break; +case 97: +#line 1104 "ftpcmd.y" + { + /* + * Problem: this production is used for all pathname + * processing, but only gives a 550 error reply. + * This is a valid reply in some cases but not in + * others. + */ + if (logged_in && yystack.l_mark[0].s && *yystack.l_mark[0].s == '~') { + char *path, *home, *result; + size_t len; + + path = strchr(yystack.l_mark[0].s + 1, '/'); + if (path != NULL) + *path++ = '\0'; + if (yystack.l_mark[0].s[1] == '\0') + home = homedir; + else { + struct passwd *hpw; + + if ((hpw = getpwnam(yystack.l_mark[0].s + 1)) != NULL) + home = hpw->pw_dir; + else + home = yystack.l_mark[0].s; + } + len = strlen(home) + 1; + if (path != NULL) + len += strlen(path) + 1; + if ((result = malloc(len)) == NULL) + fatal("Local resource failure: malloc"); + strlcpy(result, home, len); + if (path != NULL) { + strlcat(result, "/", len); + strlcat(result, path, len); + } + yyval.s = result; + free(yystack.l_mark[0].s); + } else + yyval.s = yystack.l_mark[0].s; + } +break; +case 99: +#line 1151 "ftpcmd.y" + { + int ret, dec, multby, digit; + + /* + * Convert a number that was read as decimal number + * to what it would be if it had been read as octal. + */ + dec = yystack.l_mark[0].u.i; + multby = 1; + ret = 0; + while (dec) { + digit = dec%10; + if (digit > 7) { + ret = -1; + break; + } + ret += digit * multby; + multby *= 8; + dec /= 10; + } + yyval.u.i = ret; + } +break; +case 103: +#line 1189 "ftpcmd.y" + { + yyval.u.i = yystack.l_mark[0].u.i; + } +break; +case 104: +#line 1196 "ftpcmd.y" + { + if (logged_in) + yyval.u.i = 1; + else { + reply(530, "Please login with USER and PASS."); + yyval.u.i = 0; + hasyyerrored = 1; + } + } +break; +#line 2517 "ftpcmd.c" + } + yystack.s_mark -= yym; + yystate = *yystack.s_mark; + yystack.l_mark -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + yystate = YYFINAL; + *++yystack.s_mark = YYFINAL; + *++yystack.l_mark = yyval; + if (yychar < 0) + { + if ((yychar = YYLEX) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yystack.s_mark, yystate); +#endif + if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack)) + { + goto yyoverflow; + } + *++yystack.s_mark = (short) yystate; + *++yystack.l_mark = yyval; + goto yyloop; + +yyoverflow: + yyerror("yacc stack overflow"); + +yyabort: + yyfreestack(&yystack); + return (1); + +yyaccept: + yyfreestack(&yystack); + return (0); +} diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y new file mode 100644 index 000000000..be4256d41 --- /dev/null +++ b/libexec/ftpd/ftpcmd.y @@ -0,0 +1,1875 @@ +/* $NetBSD: ftpcmd.y,v 1.93 2011/09/16 16:13:17 plunky Exp $ */ + +/*- + * 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) 1985, 1988, 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. + * + * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 + */ + +/* + * Grammar for FTP commands. + * See RFC 959. + */ + +%{ +#include + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; +#else +__RCSID("$NetBSD: ftpcmd.y,v 1.93 2011/09/16 16:13:17 plunky Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef KERBEROS5 +#include +#endif + +#include "extern.h" +#include "version.h" + +static int cmd_type; +static int cmd_form; +static int cmd_bytesz; + +char cbuf[FTP_BUFLEN]; +char *cmdp; +char *fromname; + +extern int epsvall; +struct tab sitetab[]; + +static int check_write(const char *, int); +static void help(struct tab *, const char *); +static void port_check(const char *, int); + int yylex(void); + +%} + +%union { + struct { + LLT ll; + int i; + } u; + char *s; + const char *cs; +} + +%token + A B C E F I + L N P R S T + + SP CRLF COMMA ALL + + USER PASS ACCT CWD CDUP SMNT + QUIT REIN PORT PASV TYPE STRU + MODE RETR STOR STOU APPE ALLO + REST RNFR RNTO ABOR DELE RMD + MKD PWD LIST NLST SITE SYST + STAT HELP NOOP + + AUTH ADAT PROT PBSZ CCC MIC + CONF ENC + + FEAT OPTS + + SIZE MDTM MLST MLSD + + LPRT LPSV EPRT EPSV + + MAIL MLFL MRCP MRSQ MSAM MSND + MSOM + + CHMOD IDLE RATEGET RATEPUT UMASK + + LEXERR + +%token STRING +%token NUMBER + +%type check_login octal_number byte_size +%type struct_code mode_code type_code form_code decimal_integer +%type pathstring pathname password username +%type mechanism_name base64data prot_code + +%start cmd_sel + +%% + +cmd_sel + : cmd + { + REASSIGN(fromname, NULL); + restart_point = (off_t) 0; + } + + | rcmd + + ; + +cmd + /* RFC 959 */ + : USER SP username CRLF + { + user($3); + free($3); + } + + | PASS SP password CRLF + { + pass($3); + memset($3, 0, strlen($3)); + free($3); + } + + | CWD check_login CRLF + { + if ($2) + cwd(homedir); + } + + | CWD check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + cwd($4); + if ($4 != NULL) + free($4); + } + + | CDUP check_login CRLF + { + if ($2) + cwd(".."); + } + + | QUIT CRLF + { + if (logged_in) { + reply(-221, "%s", ""); + reply(0, + "Data traffic for this session was " LLF " byte%s in " LLF " file%s.", + (LLT)total_data, PLURAL(total_data), + (LLT)total_files, PLURAL(total_files)); + reply(0, + "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.", + (LLT)total_bytes, PLURAL(total_bytes), + (LLT)total_xfers, PLURAL(total_xfers)); + } + reply(221, + "Thank you for using the FTP service on %s.", + hostname); + if (logged_in && logging) { + syslog(LOG_INFO, + "Data traffic: " LLF " byte%s in " LLF " file%s", + (LLT)total_data, PLURAL(total_data), + (LLT)total_files, PLURAL(total_files)); + syslog(LOG_INFO, + "Total traffic: " LLF " byte%s in " LLF " transfer%s", + (LLT)total_bytes, PLURAL(total_bytes), + (LLT)total_xfers, PLURAL(total_xfers)); + } + + dologout(0); + } + + | PORT check_login SP host_port CRLF + { + if ($2) + port_check("PORT", AF_INET); + } + + | LPRT check_login SP host_long_port4 CRLF + { + if ($2) + port_check("LPRT", AF_INET); + } + + | LPRT check_login SP host_long_port6 CRLF + { +#ifdef INET6 + if ($2) + port_check("LPRT", AF_INET6); +#else + reply(500, "IPv6 support not available."); +#endif + } + + | EPRT check_login SP STRING CRLF + { + if ($2) { + if (extended_port($4) == 0) + port_check("EPRT", -1); + } + free($4); + } + + | PASV check_login CRLF + { + if ($2) { + if (CURCLASS_FLAGS_ISSET(passive)) + passive(); + else + reply(500, "PASV mode not available."); + } + } + + | LPSV check_login CRLF + { + if ($2) { + if (CURCLASS_FLAGS_ISSET(passive)) { + if (epsvall) + reply(501, + "LPSV disallowed after EPSV ALL"); + else + long_passive("LPSV", PF_UNSPEC); + } else + reply(500, "LPSV mode not available."); + } + } + + | EPSV check_login SP NUMBER CRLF + { + if ($2) { + if (CURCLASS_FLAGS_ISSET(passive)) + long_passive("EPSV", + epsvproto2af($4.i)); + else + reply(500, "EPSV mode not available."); + } + } + + | EPSV check_login SP ALL CRLF + { + if ($2) { + if (CURCLASS_FLAGS_ISSET(passive)) { + reply(200, + "EPSV ALL command successful."); + epsvall++; + } else + reply(500, "EPSV mode not available."); + } + } + + | EPSV check_login CRLF + { + if ($2) { + if (CURCLASS_FLAGS_ISSET(passive)) + long_passive("EPSV", PF_UNSPEC); + else + reply(500, "EPSV mode not available."); + } + } + + | TYPE check_login SP type_code CRLF + { + if ($2) { + + switch (cmd_type) { + + case TYPE_A: + if (cmd_form == FORM_N) { + reply(200, "Type set to A."); + type = cmd_type; + form = cmd_form; + } else + reply(504, "Form must be N."); + break; + + case TYPE_E: + reply(504, "Type E not implemented."); + break; + + case TYPE_I: + reply(200, "Type set to I."); + type = cmd_type; + break; + + case TYPE_L: +#if NBBY == 8 + if (cmd_bytesz == 8) { + reply(200, + "Type set to L (byte size 8)."); + type = cmd_type; + } else + reply(504, "Byte size must be 8."); +#else /* NBBY == 8 */ + UNIMPLEMENTED for NBBY != 8 +#endif /* NBBY == 8 */ + } + + } + } + + | STRU check_login SP struct_code CRLF + { + if ($2) { + switch ($4) { + + case STRU_F: + reply(200, "STRU F ok."); + break; + + default: + reply(504, "Unimplemented STRU type."); + } + } + } + + | MODE check_login SP mode_code CRLF + { + if ($2) { + switch ($4) { + + case MODE_S: + reply(200, "MODE S ok."); + break; + + default: + reply(502, "Unimplemented MODE type."); + } + } + } + + | RETR check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + retrieve(NULL, $4); + if ($4 != NULL) + free($4); + } + + | STOR SP pathname CRLF + { + if (check_write($3, 1)) + store($3, "w", 0); + if ($3 != NULL) + free($3); + } + + | STOU SP pathname CRLF + { + if (check_write($3, 1)) + store($3, "w", 1); + if ($3 != NULL) + free($3); + } + + | APPE SP pathname CRLF + { + if (check_write($3, 1)) + store($3, "a", 0); + if ($3 != NULL) + free($3); + } + + | ALLO check_login SP NUMBER CRLF + { + if ($2) + reply(202, "ALLO command ignored."); + } + + | ALLO check_login SP NUMBER SP R SP NUMBER CRLF + { + if ($2) + reply(202, "ALLO command ignored."); + } + + | RNTO SP pathname CRLF + { + if (check_write($3, 0)) { + if (fromname) { + renamecmd(fromname, $3); + REASSIGN(fromname, NULL); + } else { + reply(503, "Bad sequence of commands."); + } + } + if ($3 != NULL) + free($3); + } + + | ABOR check_login CRLF + { + if (is_oob) + abor(); + else if ($2) + reply(225, "ABOR command successful."); + } + + | DELE SP pathname CRLF + { + if (check_write($3, 0)) + delete($3); + if ($3 != NULL) + free($3); + } + + | RMD SP pathname CRLF + { + if (check_write($3, 0)) + removedir($3); + if ($3 != NULL) + free($3); + } + + | MKD SP pathname CRLF + { + if (check_write($3, 0)) + makedir($3); + if ($3 != NULL) + free($3); + } + + | PWD check_login CRLF + { + if ($2) + pwd(); + } + + | LIST check_login CRLF + { + const char *argv[] = { INTERNAL_LS, "-lgA", NULL }; + + if (CURCLASS_FLAGS_ISSET(hidesymlinks)) + argv[1] = "-LlgA"; + if ($2) + retrieve(argv, ""); + } + + | LIST check_login SP pathname CRLF + { + const char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; + + if (CURCLASS_FLAGS_ISSET(hidesymlinks)) + argv[1] = "-LlgA"; + if ($2 && $4 != NULL) { + argv[2] = $4; + retrieve(argv, $4); + } + if ($4 != NULL) + free($4); + } + + | NLST check_login CRLF + { + if ($2) + send_file_list("."); + } + + | NLST check_login SP pathname CRLF + { + if ($2) + send_file_list($4); + free($4); + } + + | SITE SP HELP CRLF + { + help(sitetab, NULL); + } + + | SITE SP CHMOD SP octal_number SP pathname CRLF + { + if (check_write($7, 0)) { + if (($5 == -1) || ($5 > 0777)) + reply(501, + "CHMOD: Mode value must be between 0 and 0777"); + else if (chmod($7, $5) < 0) + perror_reply(550, $7); + else + reply(200, "CHMOD command successful."); + } + if ($7 != NULL) + free($7); + } + + | SITE SP HELP SP STRING CRLF + { + help(sitetab, $5); + free($5); + } + + | SITE SP IDLE check_login CRLF + { + if ($4) { + reply(200, + "Current IDLE time limit is " LLF + " seconds; max " LLF, + (LLT)curclass.timeout, + (LLT)curclass.maxtimeout); + } + } + + | SITE SP IDLE check_login SP NUMBER CRLF + { + if ($4) { + if ($6.i < 30 || $6.i > curclass.maxtimeout) { + reply(501, + "IDLE time limit must be between 30 and " + LLF " seconds", + (LLT)curclass.maxtimeout); + } else { + curclass.timeout = $6.i; + (void) alarm(curclass.timeout); + reply(200, + "IDLE time limit set to " + LLF " seconds", + (LLT)curclass.timeout); + } + } + } + + | SITE SP RATEGET check_login CRLF + { + if ($4) { + reply(200, + "Current RATEGET is " LLF " bytes/sec", + (LLT)curclass.rateget); + } + } + + | SITE SP RATEGET check_login SP STRING CRLF + { + char errbuf[100]; + char *p = $6; + LLT rate; + + if ($4) { + rate = strsuftollx("RATEGET", p, 0, + curclass.maxrateget + ? curclass.maxrateget + : LLTMAX, errbuf, sizeof(errbuf)); + if (errbuf[0]) + reply(501, "%s", errbuf); + else { + curclass.rateget = rate; + reply(200, + "RATEGET set to " LLF " bytes/sec", + (LLT)curclass.rateget); + } + } + free($6); + } + + | SITE SP RATEPUT check_login CRLF + { + if ($4) { + reply(200, + "Current RATEPUT is " LLF " bytes/sec", + (LLT)curclass.rateput); + } + } + + | SITE SP RATEPUT check_login SP STRING CRLF + { + char errbuf[100]; + char *p = $6; + LLT rate; + + if ($4) { + rate = strsuftollx("RATEPUT", p, 0, + curclass.maxrateput + ? curclass.maxrateput + : LLTMAX, errbuf, sizeof(errbuf)); + if (errbuf[0]) + reply(501, "%s", errbuf); + else { + curclass.rateput = rate; + reply(200, + "RATEPUT set to " LLF " bytes/sec", + (LLT)curclass.rateput); + } + } + free($6); + } + + | SITE SP UMASK check_login CRLF + { + int oldmask; + + if ($4) { + oldmask = umask(0); + (void) umask(oldmask); + reply(200, "Current UMASK is %03o", oldmask); + } + } + + | SITE SP UMASK check_login SP octal_number CRLF + { + int oldmask; + + if ($4 && check_write("", 0)) { + if (($6 == -1) || ($6 > 0777)) { + reply(501, "Bad UMASK value"); + } else { + oldmask = umask($6); + reply(200, + "UMASK set to %03o (was %03o)", + $6, oldmask); + } + } + } + + | SYST CRLF + { + if (EMPTYSTR(version)) + reply(215, "UNIX Type: L%d", NBBY); + else + reply(215, "UNIX Type: L%d Version: %s", NBBY, + version); + } + + | STAT check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + statfilecmd($4); + if ($4 != NULL) + free($4); + } + + | STAT CRLF + { + if (is_oob) + statxfer(); + else + statcmd(); + } + + | HELP CRLF + { + help(cmdtab, NULL); + } + + | HELP SP STRING CRLF + { + char *cp = $3; + + if (strncasecmp(cp, "SITE", 4) == 0) { + cp = $3 + 4; + if (*cp == ' ') + cp++; + if (*cp) + help(sitetab, cp); + else + help(sitetab, NULL); + } else + help(cmdtab, $3); + free($3); + } + + | NOOP CRLF + { + reply(200, "NOOP command successful."); + } + + /* RFC 2228 */ + | AUTH SP mechanism_name CRLF + { + reply(502, "RFC 2228 authentication not implemented."); + free($3); + } + + | ADAT SP base64data CRLF + { + reply(503, + "Please set authentication state with AUTH."); + free($3); + } + + | PROT SP prot_code CRLF + { + reply(503, + "Please set protection buffer size with PBSZ."); + free($3); + } + + | PBSZ SP decimal_integer CRLF + { + reply(503, + "Please set authentication state with AUTH."); + } + + | CCC CRLF + { + reply(533, "No protection enabled."); + } + + | MIC SP base64data CRLF + { + reply(502, "RFC 2228 authentication not implemented."); + free($3); + } + + | CONF SP base64data CRLF + { + reply(502, "RFC 2228 authentication not implemented."); + free($3); + } + + | ENC SP base64data CRLF + { + reply(502, "RFC 2228 authentication not implemented."); + free($3); + } + + /* RFC 2389 */ + | FEAT CRLF + { + + feat(); + } + + | OPTS SP STRING CRLF + { + + opts($3); + free($3); + } + + + /* RFC 3659 */ + + /* + * Return size of file in a format suitable for + * using with RESTART (we just count bytes). + */ + | SIZE check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + sizecmd($4); + if ($4 != NULL) + free($4); + } + + /* + * Return modification time of file as an ISO 3307 + * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx + * where xxx is the fractional second (of any precision, + * not necessarily 3 digits) + */ + | MDTM check_login SP pathname CRLF + { + if ($2 && $4 != NULL) { + struct stat stbuf; + if (stat($4, &stbuf) < 0) + perror_reply(550, $4); + else if (!S_ISREG(stbuf.st_mode)) { + reply(550, "%s: not a plain file.", $4); + } else { + struct tm *t; + + t = gmtime(&stbuf.st_mtime); + reply(213, + "%04d%02d%02d%02d%02d%02d", + TM_YEAR_BASE + t->tm_year, + t->tm_mon+1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + } + } + if ($4 != NULL) + free($4); + } + + | MLST check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + mlst($4); + if ($4 != NULL) + free($4); + } + + | MLST check_login CRLF + { + mlst(NULL); + } + + | MLSD check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + mlsd($4); + if ($4 != NULL) + free($4); + } + + | MLSD check_login CRLF + { + mlsd(NULL); + } + + | error CRLF + { + yyerrok; + } + ; + +rcmd + : REST check_login SP NUMBER CRLF + { + if ($2) { + REASSIGN(fromname, NULL); + restart_point = (off_t)$4.ll; + reply(350, + "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", + (LLT)restart_point); + } + } + + | RNFR SP pathname CRLF + { + restart_point = (off_t) 0; + if (check_write($3, 0)) { + REASSIGN(fromname, NULL); + fromname = renamefrom($3); + } + if ($3 != NULL) + free($3); + } + ; + +username + : STRING + ; + +password + : /* empty */ + { + $$ = (char *)calloc(1, sizeof(char)); + } + + | STRING + ; + +byte_size + : NUMBER + { + $$ = $1.i; + } + ; + +host_port + : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER + { + char *a, *p; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_len = sizeof(struct sockaddr_in); + data_dest.su_family = AF_INET; + p = (char *)&data_dest.su_port; + p[0] = $9.i; p[1] = $11.i; + a = (char *)&data_dest.su_addr; + a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; + } + ; + +host_long_port4 + : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER + { + char *a, *p; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_len = sizeof(struct sockaddr_in); + data_dest.su_family = AF_INET; + p = (char *)&data_dest.su_port; + p[0] = $15.i; p[1] = $17.i; + a = (char *)&data_dest.su_addr; + a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; + + /* reject invalid LPRT command */ + if ($1.i != 4 || $3.i != 4 || $13.i != 2) + memset(&data_dest, 0, sizeof(data_dest)); + } + ; + +host_long_port6 + : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER + { +#ifdef INET6 + unsigned char buf[16]; + + (void)memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_len = sizeof(struct sockaddr_in6); + data_dest.su_family = AF_INET6; + buf[0] = $39.i; buf[1] = $41.i; + (void)memcpy(&data_dest.su_port, buf, + sizeof(data_dest.su_port)); + buf[0] = $5.i; buf[1] = $7.i; + buf[2] = $9.i; buf[3] = $11.i; + buf[4] = $13.i; buf[5] = $15.i; + buf[6] = $17.i; buf[7] = $19.i; + buf[8] = $21.i; buf[9] = $23.i; + buf[10] = $25.i; buf[11] = $27.i; + buf[12] = $29.i; buf[13] = $31.i; + buf[14] = $33.i; buf[15] = $35.i; + (void)memcpy(&data_dest.si_su.su_sin6.sin6_addr, + buf, sizeof(data_dest.si_su.su_sin6.sin6_addr)); + if (his_addr.su_family == AF_INET6) { + /* XXX: more sanity checks! */ + data_dest.su_scope_id = his_addr.su_scope_id; + } +#else + memset(&data_dest, 0, sizeof(data_dest)); +#endif /* INET6 */ + /* reject invalid LPRT command */ + if ($1.i != 6 || $3.i != 16 || $37.i != 2) + memset(&data_dest, 0, sizeof(data_dest)); + } + ; + +form_code + : N + { + $$ = FORM_N; + } + + | T + { + $$ = FORM_T; + } + + | C + { + $$ = FORM_C; + } + ; + +type_code + : A + { + cmd_type = TYPE_A; + cmd_form = FORM_N; + } + + | A SP form_code + { + cmd_type = TYPE_A; + cmd_form = $3; + } + + | E + { + cmd_type = TYPE_E; + cmd_form = FORM_N; + } + + | E SP form_code + { + cmd_type = TYPE_E; + cmd_form = $3; + } + + | I + { + cmd_type = TYPE_I; + } + + | L + { + cmd_type = TYPE_L; + cmd_bytesz = NBBY; + } + + | L SP byte_size + { + cmd_type = TYPE_L; + cmd_bytesz = $3; + } + + /* this is for a bug in the BBN ftp */ + | L byte_size + { + cmd_type = TYPE_L; + cmd_bytesz = $2; + } + ; + +struct_code + : F + { + $$ = STRU_F; + } + + | R + { + $$ = STRU_R; + } + + | P + { + $$ = STRU_P; + } + ; + +mode_code + : S + { + $$ = MODE_S; + } + + | B + { + $$ = MODE_B; + } + + | C + { + $$ = MODE_C; + } + ; + +pathname + : pathstring + { + /* + * Problem: this production is used for all pathname + * processing, but only gives a 550 error reply. + * This is a valid reply in some cases but not in + * others. + */ + if (logged_in && $1 && *$1 == '~') { + char *path, *home, *result; + size_t len; + + path = strchr($1 + 1, '/'); + if (path != NULL) + *path++ = '\0'; + if ($1[1] == '\0') + home = homedir; + else { + struct passwd *hpw; + + if ((hpw = getpwnam($1 + 1)) != NULL) + home = hpw->pw_dir; + else + home = $1; + } + len = strlen(home) + 1; + if (path != NULL) + len += strlen(path) + 1; + if ((result = malloc(len)) == NULL) + fatal("Local resource failure: malloc"); + strlcpy(result, home, len); + if (path != NULL) { + strlcat(result, "/", len); + strlcat(result, path, len); + } + $$ = result; + free($1); + } else + $$ = $1; + } + ; + +pathstring + : STRING + ; + +octal_number + : NUMBER + { + int ret, dec, multby, digit; + + /* + * Convert a number that was read as decimal number + * to what it would be if it had been read as octal. + */ + dec = $1.i; + multby = 1; + ret = 0; + while (dec) { + digit = dec%10; + if (digit > 7) { + ret = -1; + break; + } + ret += digit * multby; + multby *= 8; + dec /= 10; + } + $$ = ret; + } + ; + +mechanism_name + : STRING + ; + +base64data + : STRING + ; + +prot_code + : STRING + ; + +decimal_integer + : NUMBER + { + $$ = $1.i; + } + ; + +check_login + : /* empty */ + { + if (logged_in) + $$ = 1; + else { + reply(530, "Please login with USER and PASS."); + $$ = 0; + hasyyerrored = 1; + } + } + ; + +%% + +#define CMD 0 /* beginning of command */ +#define ARGS 1 /* expect miscellaneous arguments */ +#define STR1 2 /* expect SP followed by STRING */ +#define STR2 3 /* expect STRING */ +#define OSTR 4 /* optional SP then STRING */ +#define ZSTR1 5 /* SP then optional STRING */ +#define ZSTR2 6 /* optional STRING after SP */ +#define SITECMD 7 /* SITE command */ +#define NSTR 8 /* Number followed by a string */ +#define NOARGS 9 /* No arguments allowed */ +#define EOLN 10 /* End of line */ + +struct tab cmdtab[] = { + /* From RFC 959, in order defined (5.3.1) */ + { "USER", USER, STR1, 1, " username", 0, }, + { "PASS", PASS, ZSTR1, 1, " password", 0, }, + { "ACCT", ACCT, STR1, 0, "(specify account)", 0, }, + { "CWD", CWD, OSTR, 1, "[ directory-name ]", 0, }, + { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, + { "SMNT", SMNT, ARGS, 0, "(structure mount)", 0, }, + { "QUIT", QUIT, NOARGS, 1, "(terminate service)", 0, }, + { "REIN", REIN, NOARGS, 0, "(reinitialize server state)", 0, }, + { "PORT", PORT, ARGS, 1, " b0, b1, b2, b3, b4, b5", 0, }, + { "LPRT", LPRT, ARGS, 1, " af, hal, h1, h2, h3,..., pal, p1, p2...", 0, }, + { "EPRT", EPRT, STR1, 1, " |af|addr|port|", 0, }, + { "PASV", PASV, NOARGS, 1, "(set server in passive mode)", 0, }, + { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)", 0, }, + { "EPSV", EPSV, ARGS, 1, "[ af|ALL]", 0, }, + { "TYPE", TYPE, ARGS, 1, " [ A | E | I | L ]", 0, }, + { "STRU", STRU, ARGS, 1, "(specify file structure)", 0, }, + { "MODE", MODE, ARGS, 1, "(specify transfer mode)", 0, }, + { "RETR", RETR, STR1, 1, " file-name", 0, }, + { "STOR", STOR, STR1, 1, " file-name", 0, }, + { "STOU", STOU, STR1, 1, " file-name", 0, }, + { "APPE", APPE, STR1, 1, " file-name", 0, }, + { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)", 0, }, + { "REST", REST, ARGS, 1, " offset (restart command)", 0, }, + { "RNFR", RNFR, STR1, 1, " file-name", 0, }, + { "RNTO", RNTO, STR1, 1, " file-name", 0, }, + { "ABOR", ABOR, NOARGS, 4, "(abort operation)", 0, }, + { "DELE", DELE, STR1, 1, " file-name", 0, }, + { "RMD", RMD, STR1, 1, " path-name", 0, }, + { "MKD", MKD, STR1, 1, " path-name", 0, }, + { "PWD", PWD, NOARGS, 1, "(return current directory)", 0, }, + { "LIST", LIST, OSTR, 1, "[ path-name ]", 0, }, + { "NLST", NLST, OSTR, 1, "[ path-name ]", 0, }, + { "SITE", SITE, SITECMD, 1, "site-cmd [ arguments ]", 0, }, + { "SYST", SYST, NOARGS, 1, "(get type of operating system)", 0, }, + { "STAT", STAT, OSTR, 4, "[ path-name ]", 0, }, + { "HELP", HELP, OSTR, 1, "[ ]", 0, }, + { "NOOP", NOOP, NOARGS, 2, "", 0, }, + + /* From RFC 2228, in order defined */ + { "AUTH", AUTH, STR1, 1, " mechanism-name", 0, }, + { "ADAT", ADAT, STR1, 1, " base-64-data", 0, }, + { "PROT", PROT, STR1, 1, " prot-code", 0, }, + { "PBSZ", PBSZ, ARGS, 1, " decimal-integer", 0, }, + { "CCC", CCC, NOARGS, 1, "(Disable data protection)", 0, }, + { "MIC", MIC, STR1, 4, " base64data", 0, }, + { "CONF", CONF, STR1, 4, " base64data", 0, }, + { "ENC", ENC, STR1, 4, " base64data", 0, }, + + /* From RFC 2389, in order defined */ + { "FEAT", FEAT, NOARGS, 1, "(display extended features)", 0, }, + { "OPTS", OPTS, STR1, 1, " command [ options ]", 0, }, + + /* From RFC 3659, in order defined */ + { "MDTM", MDTM, OSTR, 1, " path-name", 0, }, + { "SIZE", SIZE, OSTR, 1, " path-name", 0, }, + { "MLST", MLST, OSTR, 2, "[ path-name ]", 0, }, + { "MLSD", MLSD, OSTR, 1, "[ directory-name ]", 0, }, + + /* obsolete commands */ + { "MAIL", MAIL, OSTR, 0, "(mail to user)", 0, }, + { "MLFL", MLFL, OSTR, 0, "(mail file)", 0, }, + { "MRCP", MRCP, STR1, 0, "(mail recipient)", 0, }, + { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)", 0, }, + { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)", 0, }, + { "MSND", MSND, OSTR, 0, "(mail send to terminal)", 0, }, + { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)", 0, }, + { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, + { "XCWD", CWD, OSTR, 1, "[ directory-name ]", 0, }, + { "XMKD", MKD, STR1, 1, " path-name", 0, }, + { "XPWD", PWD, NOARGS, 1, "(return current directory)", 0, }, + { "XRMD", RMD, STR1, 1, " path-name", 0, }, + + { NULL, 0, 0, 0, 0, 0, } +}; + +struct tab sitetab[] = { + { "CHMOD", CHMOD, NSTR, 1, " mode file-name", 0, }, + { "HELP", HELP, OSTR, 1, "[ ]", 0, }, + { "IDLE", IDLE, ARGS, 1, "[ maximum-idle-time ]", 0, }, + { "RATEGET", RATEGET,OSTR, 1, "[ get-throttle-rate ]", 0, }, + { "RATEPUT", RATEPUT,OSTR, 1, "[ put-throttle-rate ]", 0, }, + { "UMASK", UMASK, ARGS, 1, "[ umask ]", 0, }, + { NULL, 0, 0, 0, 0, 0, } +}; + +/* + * Check if a filename is allowed to be modified (isupload == 0) or + * uploaded (isupload == 1), and if necessary, check the filename is `sane'. + * If the filename is NULL, fail. + * If the filename is "", don't do the sane name check. + */ +static int +check_write(const char *file, int isupload) +{ + if (file == NULL) + return (0); + if (! logged_in) { + reply(530, "Please login with USER and PASS."); + return (0); + } + /* checking modify */ + if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) { + reply(502, "No permission to use this command."); + return (0); + } + /* checking upload */ + if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) { + reply(502, "No permission to use this command."); + return (0); + } + + /* checking sanenames */ + if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) { + const char *p; + + if (file[0] == '.') + goto insane_name; + for (p = file; *p; p++) { + if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' || + *p == ',' || *p == '.' || *p == '_') + continue; + insane_name: + reply(553, "File name `%s' not allowed.", file); + return (0); + } + } + return (1); +} + +struct tab * +lookup(struct tab *p, const char *cmd) +{ + + for (; p->name != NULL; p++) + if (strcasecmp(cmd, p->name) == 0) + return (p); + return (0); +} + +#include + +/* + * get_line - a hacked up version of fgets to ignore TELNET escape codes. + * `s' is the buffer to read into. + * `n' is the 1 less than the size of the buffer, to allow trailing NUL + * `iop' is the FILE to read from. + * Returns 0 on success, -1 on EOF, -2 if the command was too long. + */ +int +get_line(char *s, int n, FILE *iop) +{ + int c; + char *cs; + + cs = s; +/* tmpline may contain saved command from urgent mode interruption */ + for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { + *cs++ = tmpline[c]; + if (tmpline[c] == '\n') { + *cs++ = '\0'; + if (ftpd_debug) + syslog(LOG_DEBUG, "command: %s", s); + tmpline[0] = '\0'; + return(0); + } + if (c == 0) + tmpline[0] = '\0'; + } + while ((c = getc(iop)) != EOF) { + total_bytes++; + total_bytes_in++; + c &= 0377; + if (c == IAC) { + if ((c = getc(iop)) != EOF) { + total_bytes++; + total_bytes_in++; + c &= 0377; + switch (c) { + case WILL: + case WONT: + c = getc(iop); + total_bytes++; + total_bytes_in++; + cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c); + (void) fflush(stdout); + continue; + case DO: + case DONT: + c = getc(iop); + total_bytes++; + total_bytes_in++; + cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c); + (void) fflush(stdout); + continue; + case IAC: + break; + default: + continue; /* ignore command */ + } + } + } + *cs++ = c; + if (--n <= 0) { + /* + * If command doesn't fit into buffer, discard the + * rest of the command and indicate truncation. + * This prevents the command to be split up into + * multiple commands. + */ + if (ftpd_debug) + syslog(LOG_DEBUG, + "command too long, last char: %d", c); + while (c != '\n' && (c = getc(iop)) != EOF) + continue; + return (-2); + } + if (c == '\n') + break; + } + if (c == EOF && cs == s) + return (-1); + *cs++ = '\0'; + if (ftpd_debug) { + if ((curclass.type != CLASS_GUEST && + strncasecmp(s, "PASS ", 5) == 0) || + strncasecmp(s, "ACCT ", 5) == 0) { + /* Don't syslog passwords */ + syslog(LOG_DEBUG, "command: %.4s ???", s); + } else { + char *cp; + int len; + + /* Don't syslog trailing CR-LF */ + len = strlen(s); + cp = s + len - 1; + while (cp >= s && (*cp == '\n' || *cp == '\r')) { + --cp; + --len; + } + syslog(LOG_DEBUG, "command: %.*s", len, s); + } + } + return (0); +} + +void +ftp_handle_line(char *cp) +{ + + cmdp = cp; + yyparse(); +} + +void +ftp_loop(void) +{ + int ret; + + while (1) { + (void) alarm(curclass.timeout); + ret = get_line(cbuf, sizeof(cbuf)-1, stdin); + (void) alarm(0); + if (ret == -1) { + reply(221, "You could at least say goodbye."); + dologout(0); + } else if (ret == -2) { + reply(500, "Command too long."); + } else { + ftp_handle_line(cbuf); + } + } + /*NOTREACHED*/ +} + +int +yylex(void) +{ + static int cpos, state; + char *cp, *cp2; + struct tab *p; + int n; + char c; + + switch (state) { + + case CMD: + hasyyerrored = 0; + if ((cp = strchr(cmdp, '\r'))) { + *cp = '\0'; +#if defined(HAVE_SETPROCTITLE) + if (strncasecmp(cmdp, "PASS", 4) != 0 && + strncasecmp(cmdp, "ACCT", 4) != 0) + setproctitle("%s: %s", proctitle, cmdp); +#endif /* defined(HAVE_SETPROCTITLE) */ + *cp++ = '\n'; + *cp = '\0'; + } + if ((cp = strpbrk(cmdp, " \n"))) + cpos = cp - cmdp; + if (cpos == 0) + cpos = 4; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + p = lookup(cmdtab, cmdp); + cmdp[cpos] = c; + if (p != NULL) { + if (is_oob && ! CMD_OOB(p)) { + /* command will be handled in-band */ + return (0); + } else if (! CMD_IMPLEMENTED(p)) { + reply(502, "%s command not implemented.", + p->name); + hasyyerrored = 1; + break; + } + state = p->state; + yylval.cs = p->name; + return (p->token); + } + break; + + case SITECMD: + if (cmdp[cpos] == ' ') { + cpos++; + return (SP); + } + cp = &cmdp[cpos]; + if ((cp2 = strpbrk(cp, " \n"))) + cpos = cp2 - cmdp; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + p = lookup(sitetab, cp); + cmdp[cpos] = c; + if (p != NULL) { + if (!CMD_IMPLEMENTED(p)) { + reply(502, "SITE %s command not implemented.", + p->name); + hasyyerrored = 1; + break; + } + state = p->state; + yylval.cs = p->name; + return (p->token); + } + break; + + case OSTR: + if (cmdp[cpos] == '\n') { + state = EOLN; + return (CRLF); + } + /* FALLTHROUGH */ + + case STR1: + case ZSTR1: + dostr1: + if (cmdp[cpos] == ' ') { + cpos++; + state = state == OSTR ? STR2 : state+1; + return (SP); + } + break; + + case ZSTR2: + if (cmdp[cpos] == '\n') { + state = EOLN; + return (CRLF); + } + /* FALLTHROUGH */ + + case STR2: + cp = &cmdp[cpos]; + n = strlen(cp); + cpos += n - 1; + /* + * Make sure the string is nonempty and \n terminated. + */ + if (n > 1 && cmdp[cpos] == '\n') { + cmdp[cpos] = '\0'; + yylval.s = ftpd_strdup(cp); + cmdp[cpos] = '\n'; + state = ARGS; + return (STRING); + } + break; + + case NSTR: + if (cmdp[cpos] == ' ') { + cpos++; + return (SP); + } + if (isdigit((unsigned char)cmdp[cpos])) { + cp = &cmdp[cpos]; + while (isdigit((unsigned char)cmdp[++cpos])) + ; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + yylval.u.i = atoi(cp); + cmdp[cpos] = c; + state = STR1; + return (NUMBER); + } + state = STR1; + goto dostr1; + + case ARGS: + if (isdigit((unsigned char)cmdp[cpos])) { + cp = &cmdp[cpos]; + while (isdigit((unsigned char)cmdp[++cpos])) + ; + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + yylval.u.i = atoi(cp); + yylval.u.ll = STRTOLL(cp, NULL, 10); + cmdp[cpos] = c; + return (NUMBER); + } + if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 + && !isalnum((unsigned char)cmdp[cpos + 3])) { + cpos += 3; + return (ALL); + } + switch (cmdp[cpos++]) { + + case '\n': + state = EOLN; + return (CRLF); + + case ' ': + return (SP); + + case ',': + return (COMMA); + + case 'A': + case 'a': + return (A); + + case 'B': + case 'b': + return (B); + + case 'C': + case 'c': + return (C); + + case 'E': + case 'e': + return (E); + + case 'F': + case 'f': + return (F); + + case 'I': + case 'i': + return (I); + + case 'L': + case 'l': + return (L); + + case 'N': + case 'n': + return (N); + + case 'P': + case 'p': + return (P); + + case 'R': + case 'r': + return (R); + + case 'S': + case 's': + return (S); + + case 'T': + case 't': + return (T); + + } + break; + + case NOARGS: + if (cmdp[cpos] == '\n') { + state = EOLN; + return (CRLF); + } + c = cmdp[cpos]; + cmdp[cpos] = '\0'; + reply(501, "'%s' command does not take any arguments.", cmdp); + hasyyerrored = 1; + cmdp[cpos] = c; + break; + + case EOLN: + state = CMD; + return (0); + + default: + fatal("Unknown state in scanner."); + } + yyerror(NULL); + state = CMD; + return (0); +} + +/* ARGSUSED */ +void +yyerror(const char *s) +{ + char *cp; + + if (hasyyerrored || is_oob) + return; + if ((cp = strchr(cmdp,'\n')) != NULL) + *cp = '\0'; + reply(500, "'%s': command not understood.", cmdp); + hasyyerrored = 1; +} + +static void +help(struct tab *ctab, const char *s) +{ + struct tab *c; + int width, NCMDS; + const char *htype; + + if (ctab == sitetab) + htype = "SITE "; + else + htype = ""; + width = 0, NCMDS = 0; + for (c = ctab; c->name != NULL; c++) { + int len = strlen(c->name); + + if (len > width) + width = len; + NCMDS++; + } + width = (width + 8) &~ 7; + if (s == 0) { + int i, j, w; + int columns, lines; + + reply(-214, "%s", ""); + reply(0, "The following %scommands are recognized.", htype); + reply(0, "(`-' = not implemented, `+' = supports options)"); + columns = 76 / width; + if (columns == 0) + columns = 1; + lines = (NCMDS + columns - 1) / columns; + for (i = 0; i < lines; i++) { + cprintf(stdout, " "); + for (j = 0; j < columns; j++) { + c = ctab + j * lines + i; + cprintf(stdout, "%s", c->name); + w = strlen(c->name); + if (! CMD_IMPLEMENTED(c)) { + CPUTC('-', stdout); + w++; + } + if (CMD_HAS_OPTIONS(c)) { + CPUTC('+', stdout); + w++; + } + if (c + lines >= &ctab[NCMDS]) + break; + while (w < width) { + CPUTC(' ', stdout); + w++; + } + } + cprintf(stdout, "\r\n"); + } + (void) fflush(stdout); + reply(214, "Direct comments to ftp-bugs@%s.", hostname); + return; + } + c = lookup(ctab, s); + if (c == (struct tab *)0) { + reply(502, "Unknown command '%s'.", s); + return; + } + if (CMD_IMPLEMENTED(c)) + reply(214, "Syntax: %s%s %s", htype, c->name, c->help); + else + reply(504, "%s%-*s\t%s; not implemented.", htype, width, + c->name, c->help); +} + +/* + * Check that the structures used for a PORT, LPRT or EPRT command are + * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks. + * If family != -1 check that his_addr.su_family == family. + */ +static void +port_check(const char *cmd, int family) +{ + char h1[NI_MAXHOST], h2[NI_MAXHOST]; + char s1[NI_MAXHOST], s2[NI_MAXHOST]; +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; +#endif + + if (epsvall) { + reply(501, "%s disallowed after EPSV ALL", cmd); + return; + } + + if (family != -1 && his_addr.su_family != family) { + port_check_fail: + reply(500, "Illegal %s command rejected", cmd); + return; + } + + if (data_dest.su_family != his_addr.su_family) + goto port_check_fail; + + /* be paranoid, if told so */ + if (CURCLASS_FLAGS_ISSET(checkportcmd)) { +#ifdef INET6 + /* + * be paranoid, there are getnameinfo implementation that does + * not present scopeid portion + */ + if (data_dest.su_family == AF_INET6 && + data_dest.su_scope_id != his_addr.su_scope_id) + goto port_check_fail; +#endif + + if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, + h1, sizeof(h1), s1, sizeof(s1), niflags)) + goto port_check_fail; + if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + h2, sizeof(h2), s2, sizeof(s2), niflags)) + goto port_check_fail; + + if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) + goto port_check_fail; + } + + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "%s command successful.", cmd); +} diff --git a/libexec/ftpd/ftpd b/libexec/ftpd/ftpd new file mode 100755 index 0000000000000000000000000000000000000000..37871e6ff367670f77cb9d490f02d7ac55f7fd5b GIT binary patch literal 555934 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

;SXOio<=YmQo`O2gf`W08ExNAX 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%0DZE+(LqT|{HnY@IdRa=X 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 &RQpvy6(TS?XW5(&_Na4Y5 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 +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95"; +#else +__RCSID("$NetBSD: ftpd.c,v 1.198 2012/06/19 06:06:34 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * FTP server. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#ifndef __minix +#include +#include +#endif + +#define FTP_NAMES +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SUPPORT_UTMP +#include +#endif +#ifdef SUPPORT_UTMPX +#include +#endif +#ifdef SKEY +#include +#endif +#ifdef KERBEROS5 +#include +#include +#endif + +#ifdef LOGIN_CAP +#include +#endif + +#ifdef USE_PAM +#include +#endif + +#define GLOBAL +#include "extern.h" +#include "pathnames.h" +#include "version.h" + +static sig_atomic_t transflag; +static sig_atomic_t urgflag; + +int data; +int Dflag; +int sflag; +int stru; /* avoid C keyword */ +int mode; +int dataport; /* use specific data port */ +int dopidfile; /* maintain pid file */ +int doutmp; /* update utmp file */ +int dowtmp; /* update wtmp file */ +int doxferlog; /* syslog/write wu-ftpd style xferlog entries */ +int xferlogfd; /* fd to write wu-ftpd xferlog entries to */ +int getnameopts; /* flags for use with getname() */ +int dropprivs; /* if privileges should or have been dropped */ +int mapped; /* IPv4 connection on AF_INET6 socket */ +off_t file_size; +off_t byte_count; +static char ttyline[20]; + +#ifdef USE_PAM +static int auth_pam(void); +pam_handle_t *pamh = NULL; +#endif + +#ifdef SUPPORT_UTMP +static struct utmp utmp; /* for utmp */ +#endif +#ifdef SUPPORT_UTMPX +static struct utmpx utmpx; /* for utmpx */ +#endif + +static const char *anondir = NULL; +static const char *confdir = _DEFAULT_CONFDIR; + +static char *curname; /* current USER name */ +static size_t curname_len; /* length of curname (include NUL) */ + +#if defined(KERBEROS) || defined(KERBEROS5) +int has_ccache = 0; +int notickets = 1; +char *krbtkfile_env = NULL; +char *tty = ttyline; +int login_krb5_forwardable_tgt = 0; +#endif + +int epsvall = 0; + +/* + * Timeout intervals for retrying connections + * to hosts that don't accept PORT cmds. This + * is a kludge, but given the problems with TCP... + */ +#define SWAITMAX 90 /* wait at most 90 seconds */ +#define SWAITINT 5 /* interval between retries */ + +int swaitmax = SWAITMAX; +int swaitint = SWAITINT; + +enum send_status { + SS_SUCCESS, + SS_ABORTED, /* transfer aborted */ + SS_NO_TRANSFER, /* no transfer made yet */ + SS_FILE_ERROR, /* file read error */ + SS_DATA_ERROR /* data send error */ +}; + +static int bind_pasv_addr(void); +static int checkuser(const char *, const char *, int, int, char **); +static int checkaccess(const char *); +static int checkpassword(const struct passwd *, const char *); +static void do_pass(int, int, const char *); +static void end_login(void); +static FILE *getdatasock(const char *); +static char *gunique(const char *); +static void login_utmp(const char *, const char *, const char *, + struct sockinet *); +static void logremotehost(struct sockinet *); +__dead static void lostconn(int); +__dead static void toolong(int); +__dead static void sigquit(int); +static void sigurg(int); +static int handleoobcmd(void); +static int receive_data(FILE *, FILE *); +static int send_data(FILE *, FILE *, const struct stat *, int); +static struct passwd *sgetpwnam(const char *); +static int write_data(int, char *, size_t, off_t *, struct timeval *, + int); +static enum send_status + send_data_with_read(int, int, const struct stat *, int); +#ifndef __minix +static enum send_status + send_data_with_mmap(int, int, const struct stat *, int); +static void logrusage(const struct rusage *, const struct rusage *); +#endif +static void logout_utmp(void); + +int main(int, char *[]); + +#if defined(KERBEROS) +int klogin(struct passwd *, char *, char *, char *); +void kdestroy(void); +#endif +#if defined(KERBEROS5) +int k5login(struct passwd *, char *, char *, char *); +void k5destroy(void); +#endif + +int +main(int argc, char *argv[]) +{ + int ch, on = 1, tos, keepalive; + socklen_t addrlen; +#ifdef KERBEROS5 + krb5_error_code kerror; +#endif + char *p; + const char *xferlogname = NULL; + long l; + struct sigaction sa; + sa_family_t af = AF_UNSPEC; + + connections = 1; + ftpd_debug = 0; + logging = 0; + pdata = -1; + Dflag = 0; + sflag = 0; + dataport = 0; + dopidfile = 1; /* default: DO use a pid file to count users */ + doutmp = 0; /* default: Do NOT log to utmp */ + dowtmp = 1; /* default: DO log to wtmp */ + doxferlog = 0; /* default: Do NOT syslog xferlog */ + xferlogfd = -1; /* default: Do NOT write xferlog file */ + getnameopts = 0; /* default: xlate addrs to name */ + dropprivs = 0; + mapped = 0; + usedefault = 1; + emailaddr = NULL; + hostname[0] = '\0'; + homedir[0] = '\0'; + gidcount = 0; + is_oob = 0; + version = FTPD_VERSION; + + /* + * LOG_NDELAY sets up the logging connection immediately, + * necessary for anonymous ftp's that chroot and can't do it later. + */ + openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); + + while ((ch = getopt(argc, argv, + "46a:c:C:Dde:h:HlL:nP:qQrst:T:uUvV:wWX")) != -1) { + switch (ch) { + case '4': + af = AF_INET; + break; + + case '6': + af = AF_INET6; + break; + + case 'a': + anondir = optarg; + break; + + case 'c': + confdir = optarg; + break; + + case 'C': + if ((p = strchr(optarg, '@')) != NULL) { + *p++ = '\0'; + strlcpy(remotehost, p, MAXHOSTNAMELEN + 1); + if (inet_pton(AF_INET, p, + &his_addr.su_addr) == 1) { + his_addr.su_family = AF_INET; + his_addr.su_len = + sizeof(his_addr.si_su.su_sin); +#ifdef INET6 + } else if (inet_pton(AF_INET6, p, + &his_addr.su_6addr) == 1) { + his_addr.su_family = AF_INET6; + his_addr.su_len = + sizeof(his_addr.si_su.su_sin6); +#endif + } else + his_addr.su_family = AF_UNSPEC; + } + pw = sgetpwnam(optarg); + exit(checkaccess(optarg) ? 0 : 1); + /* NOTREACHED */ + + case 'D': + Dflag = 1; + break; + + case 'd': + case 'v': /* deprecated */ + ftpd_debug = 1; + break; + + case 'e': + emailaddr = optarg; + break; + + case 'h': + strlcpy(hostname, optarg, sizeof(hostname)); + break; + + case 'H': + if (gethostname(hostname, sizeof(hostname)) == -1) + hostname[0] = '\0'; + hostname[sizeof(hostname) - 1] = '\0'; + break; + + case 'l': + logging++; /* > 1 == extra logging */ + break; + + case 'L': + xferlogname = optarg; + break; + + case 'n': + getnameopts = NI_NUMERICHOST; + break; + + case 'P': + errno = 0; + p = NULL; + l = strtol(optarg, &p, 10); + if (errno || *optarg == '\0' || *p != '\0' || + l < IPPORT_RESERVED || + l > IPPORT_ANONMAX) { + syslog(LOG_WARNING, "Invalid dataport %s", + optarg); + dataport = 0; + } + dataport = (int)l; + break; + + case 'q': + dopidfile = 1; + break; + + case 'Q': + dopidfile = 0; + break; + + case 'r': + dropprivs = 1; + break; + + case 's': + sflag = 1; + break; + + case 't': + case 'T': + syslog(LOG_WARNING, + "-%c has been deprecated in favour of ftpd.conf", + ch); + break; + + case 'u': + doutmp = 1; + break; + + case 'U': + doutmp = 0; + break; + + case 'V': + if (EMPTYSTR(optarg) || strcmp(optarg, "-") == 0) + version = NULL; + else + version = ftpd_strdup(optarg); + break; + + case 'w': + dowtmp = 1; + break; + + case 'W': + dowtmp = 0; + break; + + case 'X': + doxferlog |= 1; + break; + + default: + if (optopt == 'a' || optopt == 'C') + exit(1); + syslog(LOG_WARNING, "unknown flag -%c ignored", optopt); + break; + } + } + if (EMPTYSTR(confdir)) + confdir = _DEFAULT_CONFDIR; + + if (dowtmp) { +#ifdef SUPPORT_UTMPX + ftpd_initwtmpx(); +#endif +#ifdef SUPPORT_UTMP + ftpd_initwtmp(); +#endif + } + errno = 0; +#ifndef __minix + l = sysconf(_SC_LOGIN_NAME_MAX); + if (l == -1 && errno != 0) { + syslog(LOG_ERR, "sysconf _SC_LOGIN_NAME_MAX: %m"); + exit(1); + } else if (l <= 0) { + syslog(LOG_WARNING, "using conservative LOGIN_NAME_MAX value"); + curname_len = _POSIX_LOGIN_NAME_MAX; + } else + curname_len = (size_t)l; +#else + curname_len = _POSIX_LOGIN_NAME_MAX; +#endif + curname = malloc(curname_len); + if (curname == NULL) { + syslog(LOG_ERR, "malloc: %m"); + exit(1); + } + curname[0] = '\0'; + + if (Dflag) { + int error, fd, i, n, *socks; + struct pollfd *fds; + struct addrinfo hints, *res, *res0; + + if (daemon(1, 0) == -1) { + syslog(LOG_ERR, "failed to daemonize: %m"); + exit(1); + } + (void)memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + sigemptyset(&sa.sa_mask); + (void)sigaction(SIGCHLD, &sa, NULL); + + (void)memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(NULL, "ftp", &hints, &res0); + if (error) { + syslog(LOG_ERR, "getaddrinfo: %s", gai_strerror(error)); + exit(1); + } + + for (n = 0, res = res0; res != NULL; res = res->ai_next) + n++; + if (n == 0) { + syslog(LOG_ERR, "no addresses available"); + exit(1); + } + socks = malloc(n * sizeof(int)); + fds = malloc(n * sizeof(struct pollfd)); + if (socks == NULL || fds == NULL) { + syslog(LOG_ERR, "malloc: %m"); + exit(1); + } + + for (n = 0, res = res0; res != NULL; res = res->ai_next) { + socks[n] = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (socks[n] == -1) + continue; + (void)setsockopt(socks[n], SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)); + if (bind(socks[n], res->ai_addr, res->ai_addrlen) + == -1) { + (void)close(socks[n]); + continue; + } + if (listen(socks[n], 12) == -1) { + (void)close(socks[n]); + continue; + } + + fds[n].fd = socks[n]; + fds[n].events = POLLIN; + n++; + } + if (n == 0) { + syslog(LOG_ERR, "%m"); + exit(1); + } + freeaddrinfo(res0); + + if (pidfile(NULL) == -1) + syslog(LOG_ERR, "failed to write a pid file: %m"); + + for (;;) { + if (poll(fds, n, INFTIM) == -1) { + if (errno == EINTR) + continue; + syslog(LOG_ERR, "poll: %m"); + exit(1); + } + for (i = 0; i < n; i++) { + if (fds[i].revents & POLLIN) { + fd = accept(fds[i].fd, NULL, NULL); + if (fd == -1) { + syslog(LOG_ERR, "accept: %m"); + continue; + } + switch (fork()) { + case -1: + syslog(LOG_ERR, "fork: %m"); + break; + case 0: + goto child; + /* NOTREACHED */ + } + (void)close(fd); + } + } + } + child: + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + for (i = 0; i < n; i++) + (void)close(socks[i]); + } + + memset((char *)&his_addr, 0, sizeof(his_addr)); + addrlen = sizeof(his_addr.si_su); + if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0) { + syslog((errno == ENOTCONN) ? LOG_NOTICE : LOG_ERR, + "getpeername (%s): %m",argv[0]); + exit(1); + } + his_addr.su_len = addrlen; + memset((char *)&ctrl_addr, 0, sizeof(ctrl_addr)); + addrlen = sizeof(ctrl_addr.si_su); + if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { + syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); + exit(1); + } + ctrl_addr.su_len = addrlen; +#ifdef INET6 + if (his_addr.su_family == AF_INET6 + && IN6_IS_ADDR_V4MAPPED(&his_addr.su_6addr)) { +#if 1 + /* + * IPv4 control connection arrived to AF_INET6 socket. + * I hate to do this, but this is the easiest solution. + * + * The assumption is untrue on SIIT environment. + */ + struct sockinet tmp_addr; + const int off = sizeof(struct in6_addr) - sizeof(struct in_addr); + + tmp_addr = his_addr; + memset(&his_addr, 0, sizeof(his_addr)); + his_addr.su_family = AF_INET; + his_addr.su_len = sizeof(his_addr.si_su.su_sin); + memcpy(&his_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off], + sizeof(his_addr.su_addr)); + his_addr.su_port = tmp_addr.su_port; + + tmp_addr = ctrl_addr; + memset(&ctrl_addr, 0, sizeof(ctrl_addr)); + ctrl_addr.su_family = AF_INET; + ctrl_addr.su_len = sizeof(ctrl_addr.si_su.su_sin); + memcpy(&ctrl_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off], + sizeof(ctrl_addr.su_addr)); + ctrl_addr.su_port = tmp_addr.su_port; +#else + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + reply(-530, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + reply(530, + "Connection from IPv4 mapped address is not supported."); + exit(0); +#endif + + mapped = 1; + } else +#endif /* INET6 */ + mapped = 0; +#ifdef IP_TOS + if (!mapped && his_addr.su_family == AF_INET) { + tos = IPTOS_LOWDELAY; + if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, + sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } +#endif + /* if the hostname hasn't been given, attempt to determine it */ + if (hostname[0] == '\0') { + if (getnameinfo((struct sockaddr *)&ctrl_addr.si_su, + ctrl_addr.su_len, hostname, sizeof(hostname), NULL, 0, + getnameopts) != 0) + (void)gethostname(hostname, sizeof(hostname)); + hostname[sizeof(hostname) - 1] = '\0'; + } + + /* set this here so klogin can use it... */ + (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); + + (void) freopen(_PATH_DEVNULL, "w", stderr); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sa.sa_flags = SA_RESTART; + sigemptyset(&sa.sa_mask); + (void) sigaction(SIGCHLD, &sa, NULL); + + sa.sa_handler = sigquit; + sa.sa_flags = SA_RESTART; + sigfillset(&sa.sa_mask); /* block all sigs in these handlers */ + (void) sigaction(SIGHUP, &sa, NULL); + (void) sigaction(SIGINT, &sa, NULL); + (void) sigaction(SIGQUIT, &sa, NULL); + (void) sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = lostconn; + (void) sigaction(SIGPIPE, &sa, NULL); + sa.sa_handler = toolong; + (void) sigaction(SIGALRM, &sa, NULL); + sa.sa_handler = sigurg; +#ifndef __minix + (void) sigaction(SIGURG, &sa, NULL); + + /* Try to handle urgent data inline */ +#ifdef SO_OOBINLINE + if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) + syslog(LOG_WARNING, "setsockopt: %m"); +#endif + /* Set keepalives on the socket to detect dropped connections. */ +#ifdef SO_KEEPALIVE + keepalive = 1; + if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, + sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); +#endif +#endif + +#ifdef F_SETOWN + if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) + syslog(LOG_WARNING, "fcntl F_SETOWN: %m"); +#endif + logremotehost(&his_addr); + /* + * Set up default state + */ + data = -1; + type = TYPE_A; + form = FORM_N; + stru = STRU_F; + mode = MODE_S; + tmpline[0] = '\0'; + hasyyerrored = 0; + +#ifdef KERBEROS5 + kerror = krb5_init_context(&kcontext); + if (kerror) { + syslog(LOG_ERR, "%s when initializing Kerberos context", + error_message(kerror)); + exit(0); + } +#endif /* KERBEROS5 */ + + init_curclass(); + curclass.timeout = 300; /* 5 minutes, as per login(1) */ + curclass.type = CLASS_REAL; + + /* If logins are disabled, print out the message. */ + if (display_file(_PATH_NOLOGIN, 530)) { + reply(530, "System not available."); + exit(0); + } + (void)display_file(conffilename(_NAME_FTPWELCOME), 220); + /* reply(220,) must follow */ + if (EMPTYSTR(version)) + reply(220, "%s FTP server ready.", hostname); + else + reply(220, "%s FTP server (%s) ready.", hostname, version); + + if (xferlogname != NULL) { + xferlogfd = open(xferlogname, O_WRONLY | O_APPEND | O_CREAT, + 0660); + if (xferlogfd == -1) + syslog(LOG_WARNING, "open xferlog `%s': %m", + xferlogname); + else + doxferlog |= 2; + } + + ftp_loop(); + /* NOTREACHED */ +} + +static void +lostconn(int signo __unused) +{ + + if (ftpd_debug) + syslog(LOG_DEBUG, "lost connection"); + dologout(1); +} + +static void +toolong(int signo __unused) +{ + + /* XXXSIGRACE */ + reply(421, + "Timeout (" LLF " seconds): closing control connection.", + (LLT)curclass.timeout); + if (logging) + syslog(LOG_INFO, "User %s timed out after " LLF " seconds", + (pw ? pw->pw_name : "unknown"), (LLT)curclass.timeout); + dologout(1); +} + +static void +sigquit(int signo) +{ + + if (ftpd_debug) + syslog(LOG_DEBUG, "got signal %d", signo); + dologout(1); +} + +static void +sigurg(int signo __unused) +{ + + urgflag = 1; +} + + +/* + * Save the result of a getpwnam. Used for USER command, since + * the data returned must not be clobbered by any other command + * (e.g., globbing). + */ +static struct passwd * +sgetpwnam(const char *name) +{ + static struct passwd save; + struct passwd *p; + + if ((p = getpwnam(name)) == NULL) + return (p); + if (save.pw_name) { + free((char *)save.pw_name); + memset(save.pw_passwd, 0, strlen(save.pw_passwd)); + free((char *)save.pw_passwd); + free((char *)save.pw_gecos); + free((char *)save.pw_dir); + free((char *)save.pw_shell); + } + save = *p; + save.pw_name = ftpd_strdup(p->pw_name); + save.pw_passwd = ftpd_strdup(p->pw_passwd); + save.pw_gecos = ftpd_strdup(p->pw_gecos); + save.pw_dir = ftpd_strdup(p->pw_dir); + save.pw_shell = ftpd_strdup(p->pw_shell); + return (&save); +} + +static int login_attempts; /* number of failed login attempts */ +static int askpasswd; /* had USER command, ask for PASSwd */ +static int permitted; /* USER permitted */ + +/* + * USER command. + * Sets global passwd pointer pw if named account exists and is acceptable; + * sets askpasswd if a PASS command is expected. If logged in previously, + * need to reset state. If name is "ftp" or "anonymous", the name is not in + * _NAME_FTPUSERS, and ftp account exists, set guest and pw, then just return. + * If account doesn't exist, ask for passwd anyway. Otherwise, check user + * requesting login privileges. Disallow anyone who does not have a standard + * shell as returned by getusershell(). Disallow anyone mentioned in the file + * _NAME_FTPUSERS to allow people such as root and uucp to be avoided. + */ +void +user(const char *name) +{ + char *class; +#ifdef LOGIN_CAP + login_cap_t *lc = NULL; +#endif +#ifdef USE_PAM + int e; +#endif + + class = NULL; + if (logged_in) { + switch (curclass.type) { + case CLASS_GUEST: + reply(530, "Can't change user from guest login."); + return; + case CLASS_CHROOT: + reply(530, "Can't change user from chroot user."); + return; + case CLASS_REAL: + if (dropprivs) { + reply(530, "Can't change user."); + return; + } + end_login(); + break; + default: + abort(); + } + } + +#if defined(KERBEROS) + kdestroy(); +#endif +#if defined(KERBEROS5) + k5destroy(); +#endif + + curclass.type = CLASS_REAL; + askpasswd = 0; + permitted = 0; + + if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { + /* need `pw' setup for checkaccess() and checkuser () */ + if ((pw = sgetpwnam("ftp")) == NULL) + reply(530, "User %s unknown.", name); + else if (! checkaccess("ftp") || ! checkaccess("anonymous")) + reply(530, "User %s access denied.", name); + else { + curclass.type = CLASS_GUEST; + askpasswd = 1; + reply(331, + "Guest login ok, type your name as password."); + } + if (!askpasswd) { + if (logging) + syslog(LOG_NOTICE, + "ANONYMOUS FTP LOGIN REFUSED FROM %s", + remoteloghost); + end_login(); + goto cleanup_user; + } + name = "ftp"; + } else + pw = sgetpwnam(name); + + strlcpy(curname, name, curname_len); + + /* check user in /etc/ftpusers, and setup class */ + permitted = checkuser(_NAME_FTPUSERS, curname, 1, 0, &class); + + /* check user in /etc/ftpchroot */ +#ifdef LOGIN_CAP + lc = login_getpwclass(pw); +#endif + if (checkuser(_NAME_FTPCHROOT, curname, 0, 0, NULL) +#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ + || login_getcapbool(lc, "ftp-chroot", 0) +#endif + ) { + if (curclass.type == CLASS_GUEST) { + syslog(LOG_NOTICE, + "Can't change guest user to chroot class; remove entry in %s", + _NAME_FTPCHROOT); + exit(1); + } + curclass.type = CLASS_CHROOT; + } + + /* determine default class */ + if (class == NULL) { + switch (curclass.type) { + case CLASS_GUEST: + class = ftpd_strdup("guest"); + break; + case CLASS_CHROOT: + class = ftpd_strdup("chroot"); + break; + case CLASS_REAL: + class = ftpd_strdup("real"); + break; + default: + syslog(LOG_ERR, "unknown curclass.type %d; aborting", + curclass.type); + abort(); + } + } + /* parse ftpd.conf, setting up various parameters */ + parse_conf(class); + /* if not guest user, check for valid shell */ + if (pw == NULL) + permitted = 0; + else { + const char *cp, *shell; + + if ((shell = pw->pw_shell) == NULL || *shell == 0) + shell = _PATH_BSHELL; + while ((cp = getusershell()) != NULL) + if (strcmp(cp, shell) == 0) + break; + endusershell(); + if (cp == NULL && curclass.type != CLASS_GUEST) + permitted = 0; + } + + /* deny quickly (after USER not PASS) if requested */ + if (CURCLASS_FLAGS_ISSET(denyquick) && !permitted) { + reply(530, "User %s may not use FTP.", curname); + if (logging) + syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s", + remoteloghost, curname); + end_login(); + goto cleanup_user; + } + + /* if haven't asked yet (i.e, not anon), ask now */ + if (!askpasswd) { + askpasswd = 1; +#ifdef USE_PAM + e = auth_pam(); /* this does reply(331, ...) */ + do_pass(1, e, ""); + goto cleanup_user; +#else /* !USE_PAM */ +#ifdef SKEY + if (skey_haskey(curname) == 0) { + const char *myskey; + + myskey = skey_keyinfo(curname); + reply(331, "Password [ %s ] required for %s.", + myskey ? myskey : "error getting challenge", + curname); + } else +#endif + reply(331, "Password required for %s.", curname); +#endif /* !USE_PAM */ + } + + cleanup_user: +#ifdef LOGIN_CAP + login_close(lc); +#endif + /* + * Delay before reading passwd after first failed + * attempt to slow down passwd-guessing programs. + */ + if (login_attempts) + sleep((unsigned) login_attempts); + + if (class) + free(class); +} + +/* + * Determine whether something is to happen (allow access, chroot) + * for a user. Each line is a shell-style glob followed by + * `yes' or `no'. + * + * For backward compatibility, `allow' and `deny' are synonymns + * for `yes' and `no', respectively. + * + * Each glob is matched against the username in turn, and the first + * match found is used. If no match is found, the result is the + * argument `def'. If a match is found but without and explicit + * `yes'/`no', the result is the opposite of def. + * + * If the file doesn't exist at all, the result is the argument + * `nofile' + * + * Any line starting with `#' is considered a comment and ignored. + * + * Returns 0 if the user is denied, or 1 if they are allowed. + * + * NOTE: needs struct passwd *pw setup before use. + */ +static int +checkuser(const char *fname, const char *name, int def, int nofile, + char **retclass) +{ + FILE *fd; + int retval; + char *word, *perm, *class, *buf, *p; + size_t len, line; + + retval = def; + if (retclass != NULL) + *retclass = NULL; + if ((fd = fopen(conffilename(fname), "r")) == NULL) + return nofile; + + line = 0; + for (; + (buf = fparseln(fd, &len, &line, NULL, FPARSELN_UNESCCOMM | + FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) != NULL; + free(buf), buf = NULL) { + word = perm = class = NULL; + p = buf; + if (len < 1) + continue; + if (p[len - 1] == '\n') + p[--len] = '\0'; + if (EMPTYSTR(p)) + continue; + + NEXTWORD(p, word); + NEXTWORD(p, perm); + NEXTWORD(p, class); + if (EMPTYSTR(word)) + continue; + if (!EMPTYSTR(class)) { + if (strcasecmp(class, "all") == 0 || + strcasecmp(class, "none") == 0) { + syslog(LOG_WARNING, + "%s line %d: illegal user-defined class `%s' - skipping entry", + fname, (int)line, class); + continue; + } + } + + /* have a host specifier */ + if ((p = strchr(word, '@')) != NULL) { + unsigned char net[16], mask[16], *addr; + int addrlen, bits, bytes, a; + + *p++ = '\0'; + /* check against network or CIDR */ + memset(net, 0x00, sizeof(net)); + if ((bits = inet_net_pton(his_addr.su_family, p, net, + sizeof(net))) != -1) { +#ifdef INET6 + if (his_addr.su_family == AF_INET) { +#endif + addrlen = 4; + addr = (unsigned char *)&his_addr.su_addr; +#ifdef INET6 + } else { + addrlen = 16; + addr = (unsigned char *)&his_addr.su_6addr; + } +#endif + bytes = bits / 8; + bits = bits % 8; + if (bytes > 0) + memset(mask, 0xFF, bytes); + if (bytes < addrlen) + mask[bytes] = 0xFF << (8 - bits); + if (bytes + 1 < addrlen) + memset(mask + bytes + 1, 0x00, + addrlen - bytes - 1); + for (a = 0; a < addrlen; a++) + if ((addr[a] & mask[a]) != net[a]) + break; + if (a < addrlen) + continue; + + /* check against hostname glob */ + } else if (fnmatch(p, remotehost, FNM_CASEFOLD) != 0) + continue; + } + + /* have a group specifier */ + if ((p = strchr(word, ':')) != NULL) { + gid_t *groups, *ng; + int gsize, i, found; + + if (pw == NULL) + continue; /* no match for unknown user */ + *p++ = '\0'; + groups = NULL; + gsize = 16; + do { + ng = realloc(groups, gsize * sizeof(gid_t)); + if (ng == NULL) + fatal( + "Local resource failure: realloc"); + groups = ng; + } while (getgrouplist(pw->pw_name, pw->pw_gid, + groups, &gsize) == -1); + found = 0; + for (i = 0; i < gsize; i++) { + struct group *g; + + if ((g = getgrgid(groups[i])) == NULL) + continue; + if (fnmatch(p, g->gr_name, 0) == 0) { + found = 1; + break; + } + } + free(groups); + if (!found) + continue; + } + + /* check against username glob */ + if (fnmatch(word, name, 0) != 0) + continue; + + if (perm != NULL && + ((strcasecmp(perm, "allow") == 0) || + (strcasecmp(perm, "yes") == 0))) + retval = 1; + else if (perm != NULL && + ((strcasecmp(perm, "deny") == 0) || + (strcasecmp(perm, "no") == 0))) + retval = 0; + else + retval = !def; + if (!EMPTYSTR(class) && retclass != NULL) + *retclass = ftpd_strdup(class); + free(buf); + break; + } + (void) fclose(fd); + return (retval); +} + +/* + * Check if user is allowed by /etc/ftpusers + * returns 1 for yes, 0 for no + * + * NOTE: needs struct passwd *pw setup (for checkuser()) + */ +static int +checkaccess(const char *name) +{ + + return (checkuser(_NAME_FTPUSERS, name, 1, 0, NULL)); +} + +static void +login_utmp(const char *line, const char *name, const char *host, + struct sockinet *haddr) +{ +#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) + struct timeval tv; + (void)gettimeofday(&tv, NULL); +#endif +#ifdef SUPPORT_UTMPX + if (doutmp) { + (void)memset(&utmpx, 0, sizeof(utmpx)); + utmpx.ut_tv = tv; + utmpx.ut_pid = getpid(); + utmpx.ut_id[0] = 'f'; + utmpx.ut_id[1] = 't'; + utmpx.ut_id[2] = 'p'; + utmpx.ut_id[3] = '*'; + utmpx.ut_type = USER_PROCESS; + (void)strncpy(utmpx.ut_name, name, sizeof(utmpx.ut_name)); + (void)strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line)); + (void)strncpy(utmpx.ut_host, host, sizeof(utmpx.ut_host)); + (void)memcpy(&utmpx.ut_ss, &haddr->si_su, haddr->su_len); + ftpd_loginx(&utmpx); + } + if (dowtmp) + ftpd_logwtmpx(line, name, host, haddr, 0, USER_PROCESS); +#endif +#ifdef SUPPORT_UTMP + if (doutmp) { + (void)memset(&utmp, 0, sizeof(utmp)); + (void)time(&utmp.ut_time); + (void)strncpy(utmp.ut_name, name, sizeof(utmp.ut_name)); + (void)strncpy(utmp.ut_line, line, sizeof(utmp.ut_line)); + (void)strncpy(utmp.ut_host, host, sizeof(utmp.ut_host)); + ftpd_login(&utmp); + } + if (dowtmp) + ftpd_logwtmp(line, name, host); +#endif +} + +static void +logout_utmp(void) +{ +#ifdef SUPPORT_UTMPX + int okwtmpx = dowtmp; +#endif +#ifdef SUPPORT_UTMP + int okwtmp = dowtmp; +#endif + if (logged_in) { +#ifdef SUPPORT_UTMPX + if (doutmp) + okwtmpx &= ftpd_logoutx(ttyline, 0, DEAD_PROCESS); + if (okwtmpx) + ftpd_logwtmpx(ttyline, "", "", NULL, 0, DEAD_PROCESS); +#endif +#ifdef SUPPORT_UTMP + if (doutmp) + okwtmp &= ftpd_logout(ttyline); + if (okwtmp) + ftpd_logwtmp(ttyline, "", ""); +#endif + } +} + +/* + * Terminate login as previous user (if any), resetting state; + * used when USER command is given or login fails. + */ +static void +end_login(void) +{ +#ifdef USE_PAM + int e; +#endif + logout_utmp(); + show_chdir_messages(-1); /* flush chdir cache */ + if (pw != NULL && pw->pw_passwd != NULL) + memset(pw->pw_passwd, 0, strlen(pw->pw_passwd)); + pw = NULL; + logged_in = 0; + askpasswd = 0; + permitted = 0; + quietmessages = 0; + gidcount = 0; + curclass.type = CLASS_REAL; + (void) seteuid((uid_t)0); +#ifdef LOGIN_CAP + setusercontext(NULL, getpwuid(0), 0, + LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK); +#endif +#ifdef USE_PAM + if (pamh) { + if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) + syslog(LOG_ERR, "pam_setcred: %s", + pam_strerror(pamh, e)); + if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) + syslog(LOG_ERR, "pam_close_session: %s", + pam_strerror(pamh, e)); + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); + pamh = NULL; + } +#endif +} + +void +pass(const char *passwd) +{ + do_pass(0, 0, passwd); +} + +/* + * Perform the passwd confirmation and login. + * + * If pass_checked is zero, confirm passwd is correct, & ignore pass_rval. + * This is the traditional PASS implementation. + * + * If pass_checked is non-zero, use pass_rval and ignore passwd. + * This is used by auth_pam() which has already parsed PASS. + * This only applies to curclass.type != CLASS_GUEST. + */ +static void +do_pass(int pass_checked, int pass_rval, const char *passwd) +{ + int rval; + char root[MAXPATHLEN]; +#ifdef LOGIN_CAP + login_cap_t *lc = NULL; +#endif +#ifdef USE_PAM + int e; +#endif + + rval = 1; + + if (logged_in || askpasswd == 0) { + reply(503, "Login with USER first."); + return; + } + askpasswd = 0; + if (curclass.type != CLASS_GUEST) { + /* "ftp" is the only account allowed with no password */ + if (pw == NULL) { + rval = 1; /* failure below */ + goto skip; + } + if (pass_checked) { /* password validated in user() */ + rval = pass_rval; + goto skip; + } +#ifdef USE_PAM + syslog(LOG_ERR, "do_pass: USE_PAM shouldn't get here"); + rval = 1; + goto skip; +#endif +#if defined(KERBEROS) + if (klogin(pw, "", hostname, (char *)passwd) == 0) { + rval = 0; + goto skip; + } +#endif +#if defined(KERBEROS5) + if (k5login(pw, "", hostname, (char *)passwd) == 0) { + rval = 0; + goto skip; + } +#endif +#ifdef SKEY + if (skey_haskey(pw->pw_name) == 0) { + char *p; + int r; + + p = ftpd_strdup(passwd); + r = skey_passcheck(pw->pw_name, p); + free(p); + if (r != -1) { + rval = 0; + goto skip; + } + } +#endif + if (!sflag) + rval = checkpassword(pw, passwd); + else + rval = 1; + + skip: + + /* + * If rval > 0, the user failed the authentication check + * above. If rval == 0, either Kerberos or local + * authentication succeeded. + */ + if (rval) { + reply(530, "%s", rval == 2 ? "Password expired." : + "Login incorrect."); + if (logging) { + syslog(LOG_NOTICE, + "FTP LOGIN FAILED FROM %s", remoteloghost); + syslog(LOG_AUTHPRIV | LOG_NOTICE, + "FTP LOGIN FAILED FROM %s, %s", + remoteloghost, curname); + } + pw = NULL; + if (login_attempts++ >= 5) { + syslog(LOG_NOTICE, + "repeated login failures from %s", + remoteloghost); + exit(0); + } + return; + } + } + + /* password ok; check if anything else prevents login */ + if (! permitted) { + reply(530, "User %s may not use FTP.", pw->pw_name); + if (logging) + syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s", + remoteloghost, pw->pw_name); + goto bad; + } + + login_attempts = 0; /* this time successful */ + if (setegid((gid_t)pw->pw_gid) < 0) { + reply(550, "Can't set gid."); + goto bad; + } +#ifdef LOGIN_CAP + if ((lc = login_getpwclass(pw)) != NULL) { +#ifdef notyet + char remote_ip[NI_MAXHOST]; + + if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + remote_ip, sizeof(remote_ip) - 1, NULL, 0, + NI_NUMERICHOST)) + *remote_ip = 0; + remote_ip[sizeof(remote_ip) - 1] = 0; + if (!auth_hostok(lc, remotehost, remote_ip)) { + syslog(LOG_INFO|LOG_AUTH, + "FTP LOGIN FAILED (HOST) as %s: permission denied.", + pw->pw_name); + reply(530, "Permission denied."); + pw = NULL; + return; + } + if (!auth_timeok(lc, time(NULL))) { + reply(530, "Login not available right now."); + pw = NULL; + return; + } +#endif + } + setsid(); + setusercontext(lc, pw, 0, + LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY| + LOGIN_SETRESOURCES|LOGIN_SETUMASK); +#else + (void) initgroups(pw->pw_name, pw->pw_gid); + /* cache groups for cmds.c::matchgroup() */ +#endif +#ifdef USE_PAM + if (pamh) { + if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_open_session: %s", + pam_strerror(pamh, e)); + } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) + != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_setcred: %s", + pam_strerror(pamh, e)); + } + } +#endif + gidcount = getgroups(0, NULL); + if (gidlist) + free(gidlist); + gidlist = malloc(gidcount * sizeof *gidlist); + gidcount = getgroups(gidcount, gidlist); + + /* open utmp/wtmp before chroot */ + login_utmp(ttyline, pw->pw_name, remotehost, &his_addr); + + logged_in = 1; + + connections = 1; + if (dopidfile) + count_users(); + if (curclass.limit != -1 && connections > curclass.limit) { + if (! EMPTYSTR(curclass.limitfile)) + (void)display_file(conffilename(curclass.limitfile), + 530); + reply(530, + "User %s access denied, connection limit of " LLF + " reached.", + pw->pw_name, (LLT)curclass.limit); + syslog(LOG_NOTICE, + "Maximum connection limit of " LLF + " for class %s reached, login refused for %s", + (LLT)curclass.limit, curclass.classname, pw->pw_name); + goto bad; + } + + homedir[0] = '/'; + switch (curclass.type) { + case CLASS_GUEST: + /* + * We MUST do a chdir() after the chroot. Otherwise + * the old current directory will be accessible as "." + * outside the new root! + */ + format_path(root, + curclass.chroot ? curclass.chroot : + anondir ? anondir : + pw->pw_dir); + format_path(homedir, + curclass.homedir ? curclass.homedir : + "/"); + if (EMPTYSTR(homedir)) + homedir[0] = '/'; + if (EMPTYSTR(root) || chroot(root) < 0) { + syslog(LOG_NOTICE, + "GUEST user %s: can't chroot to %s: %m", + pw->pw_name, root); + goto bad_guest; + } + if (chdir(homedir) < 0) { + syslog(LOG_NOTICE, + "GUEST user %s: can't chdir to %s: %m", + pw->pw_name, homedir); + bad_guest: + reply(550, "Can't set guest privileges."); + goto bad; + } + break; + case CLASS_CHROOT: + format_path(root, + curclass.chroot ? curclass.chroot : + pw->pw_dir); + format_path(homedir, + curclass.homedir ? curclass.homedir : + "/"); + if (EMPTYSTR(homedir)) + homedir[0] = '/'; + if (EMPTYSTR(root) || chroot(root) < 0) { + syslog(LOG_NOTICE, + "CHROOT user %s: can't chroot to %s: %m", + pw->pw_name, root); + goto bad_chroot; + } + if (chdir(homedir) < 0) { + syslog(LOG_NOTICE, + "CHROOT user %s: can't chdir to %s: %m", + pw->pw_name, homedir); + bad_chroot: + reply(550, "Can't change root."); + goto bad; + } + break; + case CLASS_REAL: + /* only chroot REAL if explicitly requested */ + if (! EMPTYSTR(curclass.chroot)) { + format_path(root, curclass.chroot); + if (EMPTYSTR(root) || chroot(root) < 0) { + syslog(LOG_NOTICE, + "REAL user %s: can't chroot to %s: %m", + pw->pw_name, root); + goto bad_chroot; + } + } + format_path(homedir, + curclass.homedir ? curclass.homedir : + pw->pw_dir); + if (EMPTYSTR(homedir) || chdir(homedir) < 0) { + if (chdir("/") < 0) { + syslog(LOG_NOTICE, + "REAL user %s: can't chdir to %s: %m", + pw->pw_name, + !EMPTYSTR(homedir) ? homedir : "/"); + reply(530, + "User %s: can't change directory to %s.", + pw->pw_name, + !EMPTYSTR(homedir) ? homedir : "/"); + goto bad; + } else { + reply(-230, + "No directory! Logging in with home=/"); + homedir[0] = '/'; + } + } + break; + } +#ifndef LOGIN_CAP + setsid(); +#ifndef __minix + setlogin(pw->pw_name); +#endif +#endif + if (dropprivs || + (curclass.type != CLASS_REAL && + ntohs(ctrl_addr.su_port) > IPPORT_RESERVED + 1)) { + dropprivs++; + if (setgid((gid_t)pw->pw_gid) < 0) { + reply(550, "Can't set gid."); + goto bad; + } + if (setuid((uid_t)pw->pw_uid) < 0) { + reply(550, "Can't set uid."); + goto bad; + } + } else { + if (seteuid((uid_t)pw->pw_uid) < 0) { + reply(550, "Can't set uid."); + goto bad; + } + } + setenv("HOME", homedir, 1); + + if (curclass.type == CLASS_GUEST && passwd[0] == '-') + quietmessages = 1; + + /* + * Display a login message, if it exists. + * N.B. reply(230,) must follow the message. + */ + if (! EMPTYSTR(curclass.motd)) + (void)display_file(conffilename(curclass.motd), 230); + show_chdir_messages(230); + if (curclass.type == CLASS_GUEST) { + char *p; + + reply(230, "Guest login ok, access restrictions apply."); +#if defined(HAVE_SETPROCTITLE) + snprintf(proctitle, sizeof(proctitle), + "%s: anonymous/%s", remotehost, passwd); + setproctitle("%s", proctitle); +#endif /* defined(HAVE_SETPROCTITLE) */ + if (logging) + syslog(LOG_INFO, + "ANONYMOUS FTP LOGIN FROM %s, %s (class: %s, type: %s)", + remoteloghost, passwd, + curclass.classname, CURCLASSTYPE); + /* store guest password reply into pw_passwd */ + REASSIGN(pw->pw_passwd, ftpd_strdup(passwd)); + for (p = pw->pw_passwd; *p; p++) + if (!isgraph((unsigned char)*p)) + *p = '_'; + } else { + reply(230, "User %s logged in.", pw->pw_name); +#if defined(HAVE_SETPROCTITLE) + snprintf(proctitle, sizeof(proctitle), + "%s: %s", remotehost, pw->pw_name); + setproctitle("%s", proctitle); +#endif /* defined(HAVE_SETPROCTITLE) */ + if (logging) + syslog(LOG_INFO, + "FTP LOGIN FROM %s as %s (class: %s, type: %s)", + remoteloghost, pw->pw_name, + curclass.classname, CURCLASSTYPE); + } + (void) umask(curclass.umask); +#ifdef LOGIN_CAP + login_close(lc); +#endif + return; + + bad: +#ifdef LOGIN_CAP + login_close(lc); +#endif + /* Forget all about it... */ + end_login(); +} + +void +retrieve(const char *argv[], const char *name) +{ + FILE *fin, *dout; + struct stat st; + int (*closefunc)(FILE *) = NULL; + int dolog, sendrv, closerv, stderrfd, isconversion, isdata, isls; + struct timeval start, finish, td, *tdp; +#ifndef __minix + struct rusage rusage_before, rusage_after; +#endif + const char *dispname; + const char *error; + + sendrv = closerv = stderrfd = -1; + isconversion = isdata = isls = dolog = 0; + tdp = NULL; + dispname = name; + fin = dout = NULL; + error = NULL; + if (argv == NULL) { /* if not running a command ... */ + dolog = 1; + isdata = 1; + fin = fopen(name, "r"); + closefunc = fclose; + if (fin == NULL) /* doesn't exist?; try a conversion */ + argv = do_conversion(name); + if (argv != NULL) { + isconversion++; + syslog(LOG_DEBUG, "get command: '%s' on '%s'", + argv[0], name); + } + } + if (argv != NULL) { + char temp[MAXPATHLEN]; + + if (strcmp(argv[0], INTERNAL_LS) == 0) { + isls = 1; + stderrfd = -1; + } else { + (void)snprintf(temp, sizeof(temp), "%s", TMPFILE); + stderrfd = mkstemp(temp); + if (stderrfd != -1) + (void)unlink(temp); + } + dispname = argv[0]; + fin = ftpd_popen(argv, "r", stderrfd); + closefunc = ftpd_pclose; + st.st_size = -1; + st.st_blksize = BUFSIZ; + } + if (fin == NULL) { + if (errno != 0) { + perror_reply(550, dispname); + if (dolog) + logxfer("get", -1, name, NULL, NULL, + strerror(errno)); + } + goto cleanupretrieve; + } + byte_count = -1; + if (argv == NULL + && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { + error = "Not a plain file"; + reply(550, "%s: %s.", dispname, error); + goto done; + } + if (restart_point) { + if (type == TYPE_A) { + off_t i; + int c; + + for (i = 0; i < restart_point; i++) { + if ((c=getc(fin)) == EOF) { + error = strerror(errno); + perror_reply(550, dispname); + goto done; + } + if (c == '\n') + i++; + } + } else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) { + error = strerror(errno); + perror_reply(550, dispname); + goto done; + } + } + dout = dataconn(dispname, st.st_size, "w"); + if (dout == NULL) + goto done; + +#ifndef __minix + (void)getrusage(RUSAGE_SELF, &rusage_before); +#endif + (void)gettimeofday(&start, NULL); + sendrv = send_data(fin, dout, &st, isdata); + (void)gettimeofday(&finish, NULL); +#ifndef __minix + (void)getrusage(RUSAGE_SELF, &rusage_after); +#endif + closedataconn(dout); /* close now to affect timing stats */ + timersub(&finish, &start, &td); + tdp = &td; + done: + if (dolog) { + logxfer("get", byte_count, name, NULL, tdp, error); +#ifndef __minix + if (tdp != NULL) + logrusage(&rusage_before, &rusage_after); +#endif + } + closerv = (*closefunc)(fin); + if (sendrv == 0) { + FILE *errf; + struct stat sb; + + if (!isls && argv != NULL && closerv != 0) { + reply(-226, + "Command returned an exit status of %d", + closerv); + if (isconversion) + syslog(LOG_WARNING, + "retrieve command: '%s' returned %d", + argv[0], closerv); + } + if (!isls && argv != NULL && stderrfd != -1 && + (fstat(stderrfd, &sb) == 0) && sb.st_size > 0 && + ((errf = fdopen(stderrfd, "r")) != NULL)) { + char *cp, line[LINE_MAX]; + + reply(-226, "Command error messages:"); + rewind(errf); + while (fgets(line, sizeof(line), errf) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + reply(0, " %s", line); + } + (void) fflush(stdout); + (void) fclose(errf); + /* a reply(226,) must follow */ + } + reply(226, "Transfer complete."); + } + cleanupretrieve: + if (stderrfd != -1) + (void)close(stderrfd); + if (isconversion) + free(argv); +} + +void +store(const char *name, const char *fmode, int unique) +{ + FILE *fout, *din; + struct stat st; + int (*closefunc)(FILE *); + struct timeval start, finish, td, *tdp; + const char *desc, *error; + + din = NULL; + desc = (*fmode == 'w') ? "put" : "append"; + error = NULL; + if (unique && stat(name, &st) == 0 && + (name = gunique(name)) == NULL) { + logxfer(desc, -1, name, NULL, NULL, + "cannot create unique file"); + goto cleanupstore; + } + + if (restart_point) + fmode = "r+"; + fout = fopen(name, fmode); + closefunc = fclose; + tdp = NULL; + if (fout == NULL) { + perror_reply(553, name); + logxfer(desc, -1, name, NULL, NULL, strerror(errno)); + goto cleanupstore; + } + byte_count = -1; + if (restart_point) { + if (type == TYPE_A) { + off_t i; + int c; + + for (i = 0; i < restart_point; i++) { + if ((c=getc(fout)) == EOF) { + error = strerror(errno); + perror_reply(550, name); + goto done; + } + if (c == '\n') + i++; + } + /* + * We must do this seek to "current" position + * because we are changing from reading to + * writing. + */ + if (fseek(fout, 0L, SEEK_CUR) < 0) { + error = strerror(errno); + perror_reply(550, name); + goto done; + } + } else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) { + error = strerror(errno); + perror_reply(550, name); + goto done; + } + } + din = dataconn(name, (off_t)-1, "r"); + if (din == NULL) + goto done; + (void)gettimeofday(&start, NULL); + if (receive_data(din, fout) == 0) { + if (unique) + reply(226, "Transfer complete (unique file name:%s).", + name); + else + reply(226, "Transfer complete."); + } + (void)gettimeofday(&finish, NULL); + closedataconn(din); /* close now to affect timing stats */ + timersub(&finish, &start, &td); + tdp = &td; + done: + logxfer(desc, byte_count, name, NULL, tdp, error); + (*closefunc)(fout); + cleanupstore: + ; +} + +static FILE * +getdatasock(const char *fmode) +{ + int on, s, t, tries; + in_port_t port; + + on = 1; + if (data >= 0) + return (fdopen(data, fmode)); + if (! dropprivs) + (void) seteuid((uid_t)0); + s = socket(ctrl_addr.su_family, SOCK_STREAM, 0); + if (s < 0) + goto bad; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (char *) &on, sizeof(on)) < 0) + goto bad; + if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof(on)) < 0) + goto bad; + /* anchor socket to avoid multi-homing problems */ + data_source = ctrl_addr; + /* + * By default source port for PORT connctions is + * ctrlport-1 (see RFC959 section 5.2). + * However, if privs have been dropped and that + * would be < IPPORT_RESERVED, use a random port + * instead. + */ + if (dataport) + port = dataport; + else + port = ntohs(ctrl_addr.su_port) - 1; + if (dropprivs && port < IPPORT_RESERVED) + port = 0; /* use random port */ + data_source.su_port = htons(port); + + for (tries = 1; ; tries++) { + if (bind(s, (struct sockaddr *)&data_source.si_su, + data_source.su_len) >= 0) + break; + if (errno != EADDRINUSE || tries > 10) + goto bad; + sleep(tries); + } + if (! dropprivs) + (void) seteuid((uid_t)pw->pw_uid); +#ifdef IP_TOS + if (!mapped && ctrl_addr.su_family == AF_INET) { + on = IPTOS_THROUGHPUT; + if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, + sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } +#endif + return (fdopen(s, fmode)); + bad: + /* Return the real value of errno (close may change it) */ + t = errno; + if (! dropprivs) + (void) seteuid((uid_t)pw->pw_uid); + (void) close(s); + errno = t; + return (NULL); +} + +FILE * +dataconn(const char *name, off_t size, const char *fmode) +{ + char sizebuf[32]; + FILE *file; + int retry, tos, keepalive, conerrno; + + file_size = size; + byte_count = 0; + if (size != (off_t) -1) + (void)snprintf(sizebuf, sizeof(sizebuf), " (" LLF " byte%s)", + (LLT)size, PLURAL(size)); + else + sizebuf[0] = '\0'; + if (pdata >= 0) { + struct sockinet from; + int s; + socklen_t fromlen = sizeof(from.su_len); + + (void) alarm(curclass.timeout); + s = accept(pdata, (struct sockaddr *)&from.si_su, &fromlen); + (void) alarm(0); + if (s < 0) { + reply(425, "Can't open data connection."); + (void) close(pdata); + pdata = -1; + return (NULL); + } + (void) close(pdata); + pdata = s; +#ifndef __minix + switch (from.su_family) { + case AF_INET: +#ifdef IP_TOS + if (!mapped) { + tos = IPTOS_THROUGHPUT; + (void) setsockopt(s, IPPROTO_IP, IP_TOS, + (char *)&tos, sizeof(int)); + } + break; +#endif + } +#endif + /* Set keepalives on the socket to detect dropped conns. */ +#ifdef SO_KEEPALIVE + keepalive = 1; + (void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, + (char *)&keepalive, sizeof(int)); +#endif + reply(150, "Opening %s mode data connection for '%s'%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + return (fdopen(pdata, fmode)); + } + if (data >= 0) { + reply(125, "Using existing data connection for '%s'%s.", + name, sizebuf); + usedefault = 1; + return (fdopen(data, fmode)); + } + if (usedefault) + data_dest = his_addr; + usedefault = 1; + retry = conerrno = 0; + do { + file = getdatasock(fmode); + if (file == NULL) { + char hbuf[NI_MAXHOST]; + char pbuf[NI_MAXSERV]; + + if (getnameinfo((struct sockaddr *)&data_source.si_su, + data_source.su_len, hbuf, sizeof(hbuf), pbuf, + sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV)) + strlcpy(hbuf, "?", sizeof(hbuf)); + reply(425, "Can't create data socket (%s,%s): %s.", + hbuf, pbuf, strerror(errno)); + return (NULL); + } + data = fileno(file); + conerrno = 0; + if (connect(data, (struct sockaddr *)&data_dest.si_su, + data_dest.su_len) == 0) + break; + conerrno = errno; + (void) fclose(file); + file = NULL; + data = -1; + if (conerrno == EADDRINUSE) { + sleep((unsigned) swaitint); + retry += swaitint; + } else { + break; + } + } while (retry <= swaitmax); + if (conerrno != 0) { + perror_reply(425, "Can't build data connection"); + return (NULL); + } + reply(150, "Opening %s mode data connection for '%s'%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + return (file); +} + +void +closedataconn(FILE *fd) +{ + + if (fd == NULL) + return; + (void)fclose(fd); + data = -1; + if (pdata >= 0) + (void)close(pdata); + pdata = -1; +} + +int +write_data(int fd, char *buf, size_t size, off_t *bufrem, + struct timeval *then, int isdata) +{ + struct timeval now, td; + ssize_t c; + + while (size > 0) { + c = size; + if (curclass.writesize) { + if (curclass.writesize < c) + c = curclass.writesize; + } + if (curclass.rateget) { + if (*bufrem < c) + c = *bufrem; + } + (void) alarm(curclass.timeout); + c = write(fd, buf, c); + if (c <= 0) + return (1); + buf += c; + size -= c; + byte_count += c; + if (isdata) { + total_data_out += c; + total_data += c; + } + total_bytes_out += c; + total_bytes += c; + if (curclass.rateget) { + *bufrem -= c; + if (*bufrem == 0) { + (void)gettimeofday(&now, NULL); + timersub(&now, then, &td); + if (td.tv_sec == 0) { + usleep(1000000 - td.tv_usec); + (void)gettimeofday(then, NULL); + } else + *then = now; + *bufrem = curclass.rateget; + } + } + } + return (0); +} + +static enum send_status +send_data_with_read(int filefd, int netfd, const struct stat *st, int isdata) +{ + struct timeval then; + off_t bufrem; + ssize_t readsize; + char *buf; + int c, error; + + if (curclass.readsize > 0) + readsize = curclass.readsize; + else + readsize = st->st_blksize; + if ((buf = malloc(readsize)) == NULL) { + perror_reply(451, "Local resource failure: malloc"); + return (SS_NO_TRANSFER); + } + + if (curclass.rateget) { + bufrem = curclass.rateget; + (void)gettimeofday(&then, NULL); + } else + bufrem = readsize; + while (1) { + (void) alarm(curclass.timeout); + c = read(filefd, buf, readsize); + if (c == 0) + error = SS_SUCCESS; + else if (c < 0) + error = SS_FILE_ERROR; + else if (write_data(netfd, buf, c, &bufrem, &then, isdata)) + error = SS_DATA_ERROR; + else if (urgflag && handleoobcmd()) + error = SS_ABORTED; + else + continue; + + free(buf); + return (error); + } +} + +static enum send_status +send_data_with_mmap(int filefd, int netfd, const struct stat *st, int isdata) +{ + struct timeval then; + off_t bufrem, filesize, off, origoff; + ssize_t mapsize, winsize; + int error, sendbufsize, sendlowat; + void *win; + + bufrem = 0; + if (curclass.sendbufsize) { + sendbufsize = curclass.sendbufsize; + if (setsockopt(netfd, SOL_SOCKET, SO_SNDBUF, + &sendbufsize, sizeof(int)) == -1) + syslog(LOG_WARNING, "setsockopt(SO_SNDBUF, %d): %m", + sendbufsize); + } + + if (curclass.sendlowat) { + sendlowat = curclass.sendlowat; + if (setsockopt(netfd, SOL_SOCKET, SO_SNDLOWAT, + &sendlowat, sizeof(int)) == -1) + syslog(LOG_WARNING, "setsockopt(SO_SNDLOWAT, %d): %m", + sendlowat); + } + + winsize = curclass.mmapsize; + filesize = st->st_size; + if (ftpd_debug) + syslog(LOG_INFO, "mmapsize = " LLF ", writesize = " LLF, + (LLT)winsize, (LLT)curclass.writesize); + if (winsize <= 0) + goto try_read; + + off = lseek(filefd, (off_t)0, SEEK_CUR); + if (off == -1) + goto try_read; + + origoff = off; + if (curclass.rateget) { + bufrem = curclass.rateget; + (void)gettimeofday(&then, NULL); + } else + bufrem = winsize; +#ifndef __minix + while (1) { + mapsize = MIN(filesize - off, winsize); + if (mapsize == 0) + break; + win = mmap(NULL, mapsize, PROT_READ, + MAP_FILE|MAP_SHARED, filefd, off); + if (win == MAP_FAILED) { + if (off == origoff) + goto try_read; + return (SS_FILE_ERROR); + } + (void) madvise(win, mapsize, MADV_SEQUENTIAL); + error = write_data(netfd, win, mapsize, &bufrem, &then, + isdata); + (void) madvise(win, mapsize, MADV_DONTNEED); + munmap(win, mapsize); + if (urgflag && handleoobcmd()) + return (SS_ABORTED); + if (error) + return (SS_DATA_ERROR); + off += mapsize; + } + return (SS_SUCCESS); + +#endif + try_read: + return (send_data_with_read(filefd, netfd, st, isdata)); +} + +/* + * Transfer the contents of "instr" to "outstr" peer using the appropriate + * encapsulation of the data subject to Mode, Structure, and Type. + * + * NB: Form isn't handled. + */ +static int +send_data(FILE *instr, FILE *outstr, const struct stat *st, int isdata) +{ + int c, filefd, netfd, rval; + + urgflag = 0; + transflag = 1; + rval = -1; + + switch (type) { + + case TYPE_A: + /* XXXLUKEM: rate limit ascii send (get) */ + (void) alarm(curclass.timeout); + while ((c = getc(instr)) != EOF) { + if (urgflag && handleoobcmd()) + goto cleanup_send_data; + byte_count++; + if (c == '\n') { + if (ferror(outstr)) + goto data_err; + (void) putc('\r', outstr); + if (isdata) { + total_data_out++; + total_data++; + } + total_bytes_out++; + total_bytes++; + } + (void) putc(c, outstr); + if (isdata) { + total_data_out++; + total_data++; + } + total_bytes_out++; + total_bytes++; + if ((byte_count % 4096) == 0) + (void) alarm(curclass.timeout); + } + (void) alarm(0); + fflush(outstr); + if (ferror(instr)) + goto file_err; + if (ferror(outstr)) + goto data_err; + rval = 0; + goto cleanup_send_data; + + case TYPE_I: + case TYPE_L: + filefd = fileno(instr); + netfd = fileno(outstr); + switch (send_data_with_mmap(filefd, netfd, st, isdata)) { + + case SS_SUCCESS: + break; + + case SS_ABORTED: + case SS_NO_TRANSFER: + goto cleanup_send_data; + + case SS_FILE_ERROR: + goto file_err; + + case SS_DATA_ERROR: + goto data_err; + } + rval = 0; + goto cleanup_send_data; + + default: + reply(550, "Unimplemented TYPE %d in send_data", type); + goto cleanup_send_data; + } + + data_err: + (void) alarm(0); + perror_reply(426, "Data connection"); + goto cleanup_send_data; + + file_err: + (void) alarm(0); + perror_reply(551, "Error on input file"); + goto cleanup_send_data; + + cleanup_send_data: + (void) alarm(0); + transflag = 0; + urgflag = 0; + if (isdata) { + total_files_out++; + total_files++; + } + total_xfers_out++; + total_xfers++; + return (rval); +} + +/* + * Transfer data from peer to "outstr" using the appropriate encapulation of + * the data subject to Mode, Structure, and Type. + * + * N.B.: Form isn't handled. + */ +static int +receive_data(FILE *instr, FILE *outstr) +{ + int c, netfd, filefd, rval; + int volatile bare_lfs; + off_t byteswritten; + char *buf; + ssize_t readsize; + struct sigaction sa, sa_saved; + struct stat st; + + memset(&sa, 0, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = lostconn; + (void) sigaction(SIGALRM, &sa, &sa_saved); + + bare_lfs = 0; + urgflag = 0; + transflag = 1; + rval = -1; + byteswritten = 0; + buf = NULL; + +#define FILESIZECHECK(x) \ + do { \ + if (curclass.maxfilesize != -1 && \ + (x) > curclass.maxfilesize) { \ + errno = EFBIG; \ + goto file_err; \ + } \ + } while (0) + + switch (type) { + + case TYPE_I: + case TYPE_L: + netfd = fileno(instr); + filefd = fileno(outstr); + (void) alarm(curclass.timeout); + if (curclass.readsize) + readsize = curclass.readsize; + else if (fstat(filefd, &st)) + readsize = (ssize_t)st.st_blksize; + else + readsize = BUFSIZ; + if ((buf = malloc(readsize)) == NULL) { + perror_reply(451, "Local resource failure: malloc"); + goto cleanup_recv_data; + } + if (curclass.rateput) { + while (1) { + int d; + struct timeval then, now, td; + off_t bufrem; + + (void)gettimeofday(&then, NULL); + errno = c = d = 0; + for (bufrem = curclass.rateput; bufrem > 0; ) { + if ((c = read(netfd, buf, + MIN(readsize, bufrem))) <= 0) + goto recvdone; + if (urgflag && handleoobcmd()) + goto cleanup_recv_data; + FILESIZECHECK(byte_count + c); + if ((d = write(filefd, buf, c)) != c) + goto file_err; + (void) alarm(curclass.timeout); + bufrem -= c; + byte_count += c; + total_data_in += c; + total_data += c; + total_bytes_in += c; + total_bytes += c; + } + (void)gettimeofday(&now, NULL); + timersub(&now, &then, &td); + if (td.tv_sec == 0) + usleep(1000000 - td.tv_usec); + } + } else { + while ((c = read(netfd, buf, readsize)) > 0) { + if (urgflag && handleoobcmd()) + goto cleanup_recv_data; + FILESIZECHECK(byte_count + c); + if (write(filefd, buf, c) != c) + goto file_err; + (void) alarm(curclass.timeout); + byte_count += c; + total_data_in += c; + total_data += c; + total_bytes_in += c; + total_bytes += c; + } + } + recvdone: + if (c < 0) + goto data_err; + rval = 0; + goto cleanup_recv_data; + + case TYPE_E: + reply(553, "TYPE E not implemented."); + goto cleanup_recv_data; + + case TYPE_A: + (void) alarm(curclass.timeout); + /* XXXLUKEM: rate limit ascii receive (put) */ + while ((c = getc(instr)) != EOF) { + if (urgflag && handleoobcmd()) + goto cleanup_recv_data; + byte_count++; + total_data_in++; + total_data++; + total_bytes_in++; + total_bytes++; + if ((byte_count % 4096) == 0) + (void) alarm(curclass.timeout); + if (c == '\n') + bare_lfs++; + while (c == '\r') { + if (ferror(outstr)) + goto data_err; + if ((c = getc(instr)) != '\n') { + byte_count++; + total_data_in++; + total_data++; + total_bytes_in++; + total_bytes++; + if ((byte_count % 4096) == 0) + (void) alarm(curclass.timeout); + byteswritten++; + FILESIZECHECK(byteswritten); + (void) putc ('\r', outstr); + if (c == '\0' || c == EOF) + goto contin2; + } + } + byteswritten++; + FILESIZECHECK(byteswritten); + (void) putc(c, outstr); + contin2: ; + } + (void) alarm(0); + fflush(outstr); + if (ferror(instr)) + goto data_err; + if (ferror(outstr)) + goto file_err; + if (bare_lfs) { + reply(-226, + "WARNING! %d bare linefeeds received in ASCII mode", + bare_lfs); + reply(0, "File may not have transferred correctly."); + } + rval = 0; + goto cleanup_recv_data; + + default: + reply(550, "Unimplemented TYPE %d in receive_data", type); + goto cleanup_recv_data; + } +#undef FILESIZECHECK + + data_err: + (void) alarm(0); + perror_reply(426, "Data Connection"); + goto cleanup_recv_data; + + file_err: + (void) alarm(0); + perror_reply(452, "Error writing file"); + goto cleanup_recv_data; + + cleanup_recv_data: + (void) alarm(0); + (void) sigaction(SIGALRM, &sa_saved, NULL); + if (buf) + free(buf); + transflag = 0; + urgflag = 0; + total_files_in++; + total_files++; + total_xfers_in++; + total_xfers++; + return (rval); +} + +void +statcmd(void) +{ + struct sockinet *su = NULL; + static char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + unsigned char *a, *p; + int ispassive, af; + off_t otbi, otbo, otb; + + a = p = NULL; + + reply(-211, "%s FTP server status:", hostname); + reply(0, "Version: %s", EMPTYSTR(version) ? "" : version); + hbuf[0] = '\0'; + if (!getnameinfo((struct sockaddr *)&his_addr.si_su, his_addr.su_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) + && strcmp(remotehost, hbuf) != 0) + reply(0, "Connected to %s (%s)", remotehost, hbuf); + else + reply(0, "Connected to %s", remotehost); + + if (logged_in) { + if (curclass.type == CLASS_GUEST) + reply(0, "Logged in anonymously"); + else + reply(0, "Logged in as %s%s", pw->pw_name, + curclass.type == CLASS_CHROOT ? " (chroot)" : ""); + } else if (askpasswd) + reply(0, "Waiting for password"); + else + reply(0, "Waiting for user name"); + cprintf(stdout, " TYPE: %s", typenames[type]); + if (type == TYPE_A || type == TYPE_E) + cprintf(stdout, ", FORM: %s", formnames[form]); + if (type == TYPE_L) { +#if NBBY == 8 + cprintf(stdout, " %d", NBBY); +#else + /* XXX: `bytesize' needs to be defined in this case */ + cprintf(stdout, " %d", bytesize); +#endif + } + cprintf(stdout, "; STRUcture: %s; transfer MODE: %s\r\n", + strunames[stru], modenames[mode]); + ispassive = 0; + if (data != -1) { + reply(0, "Data connection open"); + su = NULL; + } else if (pdata != -1) { + reply(0, "in Passive mode"); + if (curclass.advertise.su_len != 0) + su = &curclass.advertise; + else + su = &pasv_addr; + ispassive = 1; + goto printaddr; + } else if (usedefault == 0) { + su = (struct sockinet *)&data_dest; + + if (epsvall) { + reply(0, "EPSV only mode (EPSV ALL)"); + goto epsvonly; + } + printaddr: + /* PASV/PORT */ + if (su->su_family == AF_INET) { + a = (unsigned char *) &su->su_addr; + p = (unsigned char *) &su->su_port; +#define UC(b) (((int) b) & 0xff) + reply(0, "%s (%d,%d,%d,%d,%d,%d)", + ispassive ? "PASV" : "PORT" , + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(p[0]), UC(p[1])); + } + + /* LPSV/LPRT */ + { + int alen, i; + + alen = 0; + switch (su->su_family) { + case AF_INET: + a = (unsigned char *) &su->su_addr; + p = (unsigned char *) &su->su_port; + alen = sizeof(su->su_addr); + af = 4; + break; +#ifdef INET6 + case AF_INET6: + a = (unsigned char *) &su->su_6addr; + p = (unsigned char *) &su->su_port; + alen = sizeof(su->su_6addr); + af = 6; + break; +#endif + default: + af = 0; + break; + } + if (af) { + cprintf(stdout, " %s (%d,%d", + ispassive ? "LPSV" : "LPRT", af, alen); + for (i = 0; i < alen; i++) + cprintf(stdout, ",%d", UC(a[i])); + cprintf(stdout, ",%d,%d,%d)\r\n", + 2, UC(p[0]), UC(p[1])); +#undef UC + } + } + + /* EPRT/EPSV */ + epsvonly: + af = af2epsvproto(su->su_family); + hbuf[0] = '\0'; + if (af > 0) { + struct sockinet tmp; + + tmp = *su; +#ifdef INET6 + if (tmp.su_family == AF_INET6) + tmp.su_scope_id = 0; +#endif + if (getnameinfo((struct sockaddr *)&tmp.si_su, + tmp.su_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), + NI_NUMERICHOST | NI_NUMERICSERV) == 0) + reply(0, "%s (|%d|%s|%s|)", + ispassive ? "EPSV" : "EPRT", + af, hbuf, sbuf); + } + } else + reply(0, "No data connection"); + + if (logged_in) { + reply(0, + "Data sent: " LLF " byte%s in " LLF " file%s", + (LLT)total_data_out, PLURAL(total_data_out), + (LLT)total_files_out, PLURAL(total_files_out)); + reply(0, + "Data received: " LLF " byte%s in " LLF " file%s", + (LLT)total_data_in, PLURAL(total_data_in), + (LLT)total_files_in, PLURAL(total_files_in)); + reply(0, + "Total data: " LLF " byte%s in " LLF " file%s", + (LLT)total_data, PLURAL(total_data), + (LLT)total_files, PLURAL(total_files)); + } + otbi = total_bytes_in; + otbo = total_bytes_out; + otb = total_bytes; + reply(0, "Traffic sent: " LLF " byte%s in " LLF " transfer%s", + (LLT)otbo, PLURAL(otbo), + (LLT)total_xfers_out, PLURAL(total_xfers_out)); + reply(0, "Traffic received: " LLF " byte%s in " LLF " transfer%s", + (LLT)otbi, PLURAL(otbi), + (LLT)total_xfers_in, PLURAL(total_xfers_in)); + reply(0, "Total traffic: " LLF " byte%s in " LLF " transfer%s", + (LLT)otb, PLURAL(otb), + (LLT)total_xfers, PLURAL(total_xfers)); + + if (logged_in && !CURCLASS_FLAGS_ISSET(private)) { + struct ftpconv *cp; + + reply(0, "%s", ""); + reply(0, "Class: %s, type: %s", + curclass.classname, CURCLASSTYPE); + reply(0, "Check PORT/LPRT commands: %sabled", + CURCLASS_FLAGS_ISSET(checkportcmd) ? "en" : "dis"); + if (! EMPTYSTR(curclass.display)) + reply(0, "Display file: %s", curclass.display); + if (! EMPTYSTR(curclass.notify)) + reply(0, "Notify fileglob: %s", curclass.notify); + reply(0, "Idle timeout: " LLF ", maximum timeout: " LLF, + (LLT)curclass.timeout, (LLT)curclass.maxtimeout); + reply(0, "Current connections: %d", connections); + if (curclass.limit == -1) + reply(0, "Maximum connections: unlimited"); + else + reply(0, "Maximum connections: " LLF, + (LLT)curclass.limit); + if (curclass.limitfile) + reply(0, "Connection limit exceeded message file: %s", + conffilename(curclass.limitfile)); + if (! EMPTYSTR(curclass.chroot)) + reply(0, "Chroot format: %s", curclass.chroot); + reply(0, "Deny bad ftpusers(5) quickly: %sabled", + CURCLASS_FLAGS_ISSET(denyquick) ? "en" : "dis"); + if (! EMPTYSTR(curclass.homedir)) + reply(0, "Homedir format: %s", curclass.homedir); + if (curclass.maxfilesize == -1) + reply(0, "Maximum file size: unlimited"); + else + reply(0, "Maximum file size: " LLF, + (LLT)curclass.maxfilesize); + if (! EMPTYSTR(curclass.motd)) + reply(0, "MotD file: %s", conffilename(curclass.motd)); + reply(0, + "Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled", + CURCLASS_FLAGS_ISSET(modify) ? "en" : "dis"); + reply(0, "Upload commands (APPE, STOR, STOU): %sabled", + CURCLASS_FLAGS_ISSET(upload) ? "en" : "dis"); + reply(0, "Sanitize file names: %sabled", + CURCLASS_FLAGS_ISSET(sanenames) ? "en" : "dis"); + reply(0, "PASV/LPSV/EPSV connections: %sabled", + CURCLASS_FLAGS_ISSET(passive) ? "en" : "dis"); + if (curclass.advertise.su_len != 0) { + char buf[50]; /* big enough for IPv6 address */ + const char *bp; + + bp = inet_ntop(curclass.advertise.su_family, + (void *)&curclass.advertise.su_addr, + buf, sizeof(buf)); + if (bp != NULL) + reply(0, "PASV advertise address: %s", bp); + } + if (curclass.portmin && curclass.portmax) + reply(0, "PASV port range: " LLF " - " LLF, + (LLT)curclass.portmin, (LLT)curclass.portmax); + if (curclass.rateget) + reply(0, "Rate get limit: " LLF " bytes/sec", + (LLT)curclass.rateget); + else + reply(0, "Rate get limit: disabled"); + if (curclass.rateput) + reply(0, "Rate put limit: " LLF " bytes/sec", + (LLT)curclass.rateput); + else + reply(0, "Rate put limit: disabled"); + if (curclass.mmapsize) + reply(0, "Mmap size: " LLF, (LLT)curclass.mmapsize); + else + reply(0, "Mmap size: disabled"); + if (curclass.readsize) + reply(0, "Read size: " LLF, (LLT)curclass.readsize); + else + reply(0, "Read size: default"); + if (curclass.writesize) + reply(0, "Write size: " LLF, (LLT)curclass.writesize); + else + reply(0, "Write size: default"); + if (curclass.recvbufsize) + reply(0, "Receive buffer size: " LLF, + (LLT)curclass.recvbufsize); + else + reply(0, "Receive buffer size: default"); + if (curclass.sendbufsize) + reply(0, "Send buffer size: " LLF, + (LLT)curclass.sendbufsize); + else + reply(0, "Send buffer size: default"); + if (curclass.sendlowat) + reply(0, "Send low water mark: " LLF, + (LLT)curclass.sendlowat); + else + reply(0, "Send low water mark: default"); + reply(0, "Umask: %.04o", curclass.umask); + for (cp = curclass.conversions; cp != NULL; cp=cp->next) { + if (cp->suffix == NULL || cp->types == NULL || + cp->command == NULL) + continue; + reply(0, "Conversion: %s [%s] disable: %s, command: %s", + cp->suffix, cp->types, cp->disable, cp->command); + } + } + + reply(211, "End of status"); +} + +void +fatal(const char *s) +{ + + reply(451, "Error in server: %s\n", s); + reply(221, "Closing connection due to server error."); + dologout(0); + /* NOTREACHED */ +} + +/* + * reply() -- + * depending on the value of n, display fmt with a trailing CRLF and + * prefix of: + * n < -1 prefix the message with abs(n) + "-" (initial line) + * n == 0 prefix the message with 4 spaces (middle lines) + * n > 0 prefix the message with n + " " (final line) + */ +void +reply(int n, const char *fmt, ...) +{ + char msg[MAXPATHLEN * 2 + 100]; + size_t b; + va_list ap; + + b = 0; + if (n == 0) + b = snprintf(msg, sizeof(msg), " "); + else if (n < 0) + b = snprintf(msg, sizeof(msg), "%d-", -n); + else + b = snprintf(msg, sizeof(msg), "%d ", n); + va_start(ap, fmt); + vsnprintf(msg + b, sizeof(msg) - b, fmt, ap); + va_end(ap); + cprintf(stdout, "%s\r\n", msg); + (void)fflush(stdout); + if (ftpd_debug) + syslog(LOG_DEBUG, "<--- %s", msg); +} + +static void +logremotehost(struct sockinet *who) +{ + +#if defined(HAVE_SOCKADDR_SNPRINTF) + char abuf[BUFSIZ]; +#endif + + struct sockaddr *sa = (struct sockaddr *)&who->si_su; + if (getnameinfo(sa, who->su_len, remotehost, sizeof(remotehost), NULL, + 0, getnameopts)) + strlcpy(remotehost, "?", sizeof(remotehost)); +#if defined(HAVE_SOCKADDR_SNPRINTF) + sockaddr_snprintf(abuf, sizeof(abuf), "%a", sa); + snprintf(remoteloghost, sizeof(remoteloghost), "%s(%s)", remotehost, + abuf); +#else + strlcpy(remoteloghost, remotehost, sizeof(remoteloghost)); +#endif + +#if defined(HAVE_SETPROCTITLE) + snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); + setproctitle("%s", proctitle); +#endif /* defined(HAVE_SETPROCTITLE) */ + if (logging) + syslog(LOG_INFO, "connection from %s to %s", + remoteloghost, hostname); +} + +/* + * Record logout in wtmp file and exit with supplied status. + * NOTE: because this is called from signal handlers it cannot + * use stdio (or call other functions that use stdio). + */ +void +dologout(int status) +{ + /* + * Prevent reception of SIGURG from resulting in a resumption + * back to the main program loop. + */ + transflag = 0; + logout_utmp(); + if (logged_in) { +#ifdef KERBEROS + if (!notickets && krbtkfile_env) + unlink(krbtkfile_env); +#endif + } + /* beware of flushing buffers after a SIGPIPE */ + if (xferlogfd != -1) + close(xferlogfd); + _exit(status); +} + +void +abor(void) +{ + + if (!transflag) + return; + tmpline[0] = '\0'; + is_oob = 0; + reply(426, "Transfer aborted. Data connection closed."); + reply(226, "Abort successful"); + transflag = 0; /* flag that the transfer has aborted */ +} + +void +statxfer(void) +{ + + if (!transflag) + return; + tmpline[0] = '\0'; + is_oob = 0; + if (file_size != (off_t) -1) + reply(213, + "Status: " LLF " of " LLF " byte%s transferred", + (LLT)byte_count, (LLT)file_size, + PLURAL(byte_count)); + else + reply(213, "Status: " LLF " byte%s transferred", + (LLT)byte_count, PLURAL(byte_count)); +} + +/* + * Call when urgflag != 0 to handle Out Of Band commands. + * Returns non zero if the OOB command aborted the transfer + * by setting transflag to 0. (c.f., "ABOR"). + */ +static int +handleoobcmd(void) +{ + char *cp; + int ret; + + if (!urgflag) + return (0); + urgflag = 0; + /* only process if transfer occurring */ + if (!transflag) + return (0); + cp = tmpline; + ret = get_line(cp, sizeof(tmpline)-1, stdin); + if (ret == -1) { + reply(221, "You could at least say goodbye."); + dologout(0); + } else if (ret == -2) { + /* Ignore truncated command */ + /* XXX: abort xfer with "500 command too long", & return 1 ? */ + return 0; + } + /* + * Manually parse OOB commands, because we can't + * recursively call the yacc parser... + */ + if (strcasecmp(cp, "ABOR\r\n") == 0) { + abor(); + } else if (strcasecmp(cp, "STAT\r\n") == 0) { + statxfer(); + } else { + /* XXX: error with "500 unknown command" ? */ + } + return (transflag == 0); +} + +static int +bind_pasv_addr(void) +{ + static int passiveport; + int port, len; + + len = pasv_addr.su_len; + if (curclass.portmin == 0 && curclass.portmax == 0) { + pasv_addr.su_port = 0; + return (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len)); + } + + if (passiveport == 0) { + srand(getpid()); + passiveport = rand() % (curclass.portmax - curclass.portmin) + + curclass.portmin; + } + + port = passiveport; + while (1) { + port++; + if (port > curclass.portmax) + port = curclass.portmin; + else if (port == passiveport) { + errno = EAGAIN; + return (-1); + } + pasv_addr.su_port = htons(port); + if (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len) == 0) + break; + if (errno != EADDRINUSE) + return (-1); + } + passiveport = port; + return (0); +} + +/* + * Note: a response of 425 is not mentioned as a possible response to + * the PASV command in RFC959. However, it has been blessed as + * a legitimate response by Jon Postel in a telephone conversation + * with Rick Adams on 25 Jan 89. + */ +void +passive(void) +{ + socklen_t len; + int recvbufsize; + char *p, *a; + + if (pdata >= 0) + close(pdata); + pdata = socket(AF_INET, SOCK_STREAM, 0); + if (pdata < 0 || !logged_in) { + perror_reply(425, "Can't open passive connection"); + return; + } + pasv_addr = ctrl_addr; + + if (bind_pasv_addr() < 0) + goto pasv_error; + len = pasv_addr.su_len; + if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0) + goto pasv_error; + pasv_addr.su_len = len; + if (curclass.recvbufsize) { + recvbufsize = curclass.recvbufsize; + if (setsockopt(pdata, SOL_SOCKET, SO_RCVBUF, &recvbufsize, + sizeof(int)) == -1) + syslog(LOG_WARNING, "setsockopt(SO_RCVBUF, %d): %m", + recvbufsize); + } + if (listen(pdata, 1) < 0) + goto pasv_error; + if (curclass.advertise.su_len != 0) + a = (char *) &curclass.advertise.su_addr; + else + a = (char *) &pasv_addr.su_addr; + p = (char *) &pasv_addr.su_port; + +#define UC(b) (((int) b) & 0xff) + + reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), + UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); + return; + + pasv_error: + (void) close(pdata); + pdata = -1; + perror_reply(425, "Can't open passive connection"); + return; +} + +/* + * convert protocol identifier to/from AF + */ +int +lpsvproto2af(int proto) +{ + + switch (proto) { + case 4: + return AF_INET; +#ifdef INET6 + case 6: + return AF_INET6; +#endif + default: + return -1; + } +} + +int +af2lpsvproto(int af) +{ + + switch (af) { + case AF_INET: + return 4; +#ifdef INET6 + case AF_INET6: + return 6; +#endif + default: + return -1; + } +} + +int +epsvproto2af(int proto) +{ + + switch (proto) { + case 1: + return AF_INET; +#ifdef INET6 + case 2: + return AF_INET6; +#endif + default: + return -1; + } +} + +int +af2epsvproto(int af) +{ + + switch (af) { + case AF_INET: + return 1; +#ifdef INET6 + case AF_INET6: + return 2; +#endif + default: + return -1; + } +} + +/* + * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...) + * 229 Entering Extended Passive Mode (|||port|) + */ +void +long_passive(const char *cmd, int pf) +{ + socklen_t len; + char *p, *a; + + if (!logged_in) { + syslog(LOG_NOTICE, "long passive but not logged in"); + reply(503, "Login with USER first."); + return; + } + + if (pf != PF_UNSPEC && ctrl_addr.su_family != pf) { + /* + * XXX: only EPRT/EPSV ready clients will understand this + */ + if (strcmp(cmd, "EPSV") != 0) + reply(501, "Network protocol mismatch"); /*XXX*/ + else + epsv_protounsupp("Network protocol mismatch"); + + return; + } + + if (pdata >= 0) + close(pdata); + pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); + if (pdata < 0) { + perror_reply(425, "Can't open passive connection"); + return; + } + pasv_addr = ctrl_addr; + if (bind_pasv_addr() < 0) + goto pasv_error; + len = pasv_addr.su_len; + if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0) + goto pasv_error; + pasv_addr.su_len = len; + if (listen(pdata, 1) < 0) + goto pasv_error; + p = (char *) &pasv_addr.su_port; + +#define UC(b) (((int) b) & 0xff) + + if (strcmp(cmd, "LPSV") == 0) { + struct sockinet *advert; + + if (curclass.advertise.su_len != 0) + advert = &curclass.advertise; + else + advert = &pasv_addr; + switch (advert->su_family) { + case AF_INET: + a = (char *) &advert->su_addr; + reply(228, + "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", + 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + 2, UC(p[0]), UC(p[1])); + return; +#ifdef INET6 + case AF_INET6: + a = (char *) &advert->su_6addr; + reply(228, + "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", + 6, 16, + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), + UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), + UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), + 2, UC(p[0]), UC(p[1])); + return; +#endif + } +#undef UC + } else if (strcmp(cmd, "EPSV") == 0) { + switch (pasv_addr.su_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + reply(229, "Entering Extended Passive Mode (|||%d|)", + ntohs(pasv_addr.su_port)); + return; + } + } else { + /* more proper error code? */ + } + + pasv_error: + (void) close(pdata); + pdata = -1; + perror_reply(425, "Can't open passive connection"); + return; +} + +int +extended_port(const char *arg) +{ + char *tmp = NULL; + char *result[3]; + char *p, *q; + char delim; + struct addrinfo hints; + struct addrinfo *res = NULL; + int i; + unsigned long proto; + + tmp = ftpd_strdup(arg); + p = tmp; + delim = p[0]; + p++; + memset(result, 0, sizeof(result)); + for (i = 0; i < 3; i++) { + q = strchr(p, delim); + if (!q || *q != delim) + goto parsefail; + *q++ = '\0'; + result[i] = p; + p = q; + } + + /* some more sanity checks */ + errno = 0; + p = NULL; + (void)strtoul(result[2], &p, 10); + if (errno || !*result[2] || *p) + goto parsefail; + errno = 0; + p = NULL; + proto = strtoul(result[0], &p, 10); + if (errno || !*result[0] || *p) + goto protounsupp; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = epsvproto2af((int)proto); + if (hints.ai_family < 0) + goto protounsupp; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(result[1], result[2], &hints, &res)) + goto parsefail; + if (res->ai_next) + goto parsefail; + if (sizeof(data_dest) < res->ai_addrlen) + goto parsefail; + memcpy(&data_dest.si_su, res->ai_addr, res->ai_addrlen); + data_dest.su_len = res->ai_addrlen; +#ifdef INET6 + if (his_addr.su_family == AF_INET6 && + data_dest.su_family == AF_INET6) { + /* XXX: more sanity checks! */ + data_dest.su_scope_id = his_addr.su_scope_id; + } +#endif + + if (tmp != NULL) + free(tmp); + if (res) + freeaddrinfo(res); + return 0; + + parsefail: + reply(500, "Invalid argument, rejected."); + usedefault = 1; + if (tmp != NULL) + free(tmp); + if (res) + freeaddrinfo(res); + return -1; + + protounsupp: + epsv_protounsupp("Protocol not supported"); + usedefault = 1; + if (tmp != NULL) + free(tmp); + return -1; +} + +/* + * 522 Protocol not supported (proto,...) + * as we assume address family for control and data connections are the same, + * we do not return the list of address families we support - instead, we + * return the address family of the control connection. + */ +void +epsv_protounsupp(const char *message) +{ + int proto; + + proto = af2epsvproto(ctrl_addr.su_family); + if (proto < 0) + reply(501, "%s", message); /* XXX */ + else + reply(522, "%s, use (%d)", message, proto); +} + +/* + * Generate unique name for file with basename "local". + * The file named "local" is already known to exist. + * Generates failure reply on error. + * + * XXX: this function should under go changes similar to + * the mktemp(3)/mkstemp(3) changes. + */ +static char * +gunique(const char *local) +{ + static char new[MAXPATHLEN]; + struct stat st; + char *cp; + int count; + + cp = strrchr(local, '/'); + if (cp) + *cp = '\0'; + if (stat(cp ? local : ".", &st) < 0) { + perror_reply(553, cp ? local : "."); + return (NULL); + } + if (cp) + *cp = '/'; + for (count = 1; count < 100; count++) { + (void)snprintf(new, sizeof(new) - 1, "%s.%d", local, count); + if (stat(new, &st) < 0) + return (new); + } + reply(452, "Unique file name cannot be created."); + return (NULL); +} + +/* + * Format and send reply containing system error number. + */ +void +perror_reply(int code, const char *string) +{ + int save_errno; + + save_errno = errno; + reply(code, "%s: %s.", string, strerror(errno)); + errno = save_errno; +} + +static char *onefile[] = { + NULL, + 0 +}; + +void +send_file_list(const char *whichf) +{ + struct stat st; + DIR *dirp; + struct dirent *dir; + FILE *volatile dout; + char **volatile dirlist; + char *dirname, *p; + char *notglob; + int volatile simple; + int volatile freeglob; + glob_t gl; + + dirp = NULL; + dout = NULL; + notglob = NULL; + simple = 0; + freeglob = 0; + urgflag = 0; + + p = NULL; + if (strpbrk(whichf, "~{[*?") != NULL) { + int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE|GLOB_LIMIT; + + memset(&gl, 0, sizeof(gl)); + freeglob = 1; + if (glob(whichf, flags, 0, &gl)) { + reply(450, "Not found"); + goto cleanup_send_file_list; + } else if (gl.gl_pathc == 0) { + errno = ENOENT; + perror_reply(450, whichf); + goto cleanup_send_file_list; + } + dirlist = gl.gl_pathv; + } else { + notglob = ftpd_strdup(whichf); + onefile[0] = notglob; + dirlist = onefile; + simple = 1; + } + /* XXX: } for vi sm */ + + while ((dirname = *dirlist++) != NULL) { + int trailingslash = 0; + + if (stat(dirname, &st) < 0) { + /* + * If user typed "ls -l", etc, and the client + * used NLST, do what the user meant. + */ + /* XXX: nuke this support? */ + if (dirname[0] == '-' && *dirlist == NULL && + transflag == 0) { + const char *argv[] = { INTERNAL_LS, "", NULL }; + + argv[1] = dirname; + retrieve(argv, dirname); + goto cleanup_send_file_list; + } + perror_reply(450, whichf); + goto cleanup_send_file_list; + } + + if (S_ISREG(st.st_mode)) { + /* + * XXXRFC: + * should we follow RFC959 and not work + * for non directories? + */ + if (dout == NULL) { + dout = dataconn("file list", (off_t)-1, "w"); + if (dout == NULL) + goto cleanup_send_file_list; + transflag = 1; + } + cprintf(dout, "%s%s\n", dirname, + type == TYPE_A ? "\r" : ""); + continue; + } else if (!S_ISDIR(st.st_mode)) + continue; + + if (dirname[strlen(dirname) - 1] == '/') + trailingslash++; + + if ((dirp = opendir(dirname)) == NULL) + continue; + + while ((dir = readdir(dirp)) != NULL) { + char nbuf[MAXPATHLEN]; + + if (urgflag && handleoobcmd()) + goto cleanup_send_file_list; + + if (ISDOTDIR(dir->d_name) || ISDOTDOTDIR(dir->d_name)) + continue; + + (void)snprintf(nbuf, sizeof(nbuf), "%s%s%s", dirname, + trailingslash ? "" : "/", dir->d_name); + + /* + * We have to do a stat to ensure it's + * not a directory or special file. + */ + /* + * XXXRFC: + * should we follow RFC959 and filter out + * non files ? lukem - NO!, or not until + * our ftp client uses MLS{T,D} for completion. + */ + if (simple || (stat(nbuf, &st) == 0 && + S_ISREG(st.st_mode))) { + if (dout == NULL) { + dout = dataconn("file list", (off_t)-1, + "w"); + if (dout == NULL) + goto cleanup_send_file_list; + transflag = 1; + } + p = nbuf; + if (nbuf[0] == '.' && nbuf[1] == '/') + p = &nbuf[2]; + cprintf(dout, "%s%s\n", p, + type == TYPE_A ? "\r" : ""); + } + } + (void) closedir(dirp); + } + + if (dout == NULL) + reply(450, "No files found."); + else if (ferror(dout) != 0) + perror_reply(451, "Data connection"); + else + reply(226, "Transfer complete."); + + cleanup_send_file_list: + closedataconn(dout); + transflag = 0; + urgflag = 0; + total_xfers++; + total_xfers_out++; + if (notglob) + free(notglob); + if (freeglob) + globfree(&gl); +} + +char * +conffilename(const char *s) +{ + static char filename[MAXPATHLEN]; + + if (*s == '/') + strlcpy(filename, s, sizeof(filename)); + else + (void)snprintf(filename, sizeof(filename), "%s/%s", confdir ,s); + return (filename); +} + +/* + * logxfer -- + * if logging > 1, then based on the arguments, syslog a message: + * if bytes != -1 " = bytes" + * else if file2 != NULL " " + * else " " + * if elapsed != NULL, append "in xxx.yyy seconds" + * if error != NULL, append ": " + error + * + * if doxferlog != 0, bytes != -1, and command is "get", "put", + * or "append", syslog and/or write a wu-ftpd style xferlog entry + */ +void +logxfer(const char *command, off_t bytes, const char *file1, const char *file2, + const struct timeval *elapsed, const char *error) +{ + char buf[MAXPATHLEN * 2 + 100]; + char realfile1[MAXPATHLEN], realfile2[MAXPATHLEN]; + const char *r1, *r2; + char direction; + size_t len; + time_t now; + + if (logging <=1 && !doxferlog) + return; + + r1 = r2 = NULL; + if ((r1 = realpath(file1, realfile1)) == NULL) + r1 = file1; + if (file2 != NULL) + if ((r2 = realpath(file2, realfile2)) == NULL) + r2 = file2; + + /* + * syslog command + */ + if (logging > 1) { + len = snprintf(buf, sizeof(buf), "%s %s", command, r1); + if (bytes != (off_t)-1) + len += snprintf(buf + len, sizeof(buf) - len, + " = " LLF " byte%s", (LLT) bytes, PLURAL(bytes)); + else if (r2 != NULL) + len += snprintf(buf + len, sizeof(buf) - len, + " %s", r2); + if (elapsed != NULL) + len += snprintf(buf + len, sizeof(buf) - len, + " in " LLF ".%.03ld seconds", + (LLT)elapsed->tv_sec, + (long)(elapsed->tv_usec / 1000)); + if (error != NULL) + len += snprintf(buf + len, sizeof(buf) - len, + ": %s", error); + syslog(LOG_INFO, "%s", buf); + } + + /* + * syslog wu-ftpd style log entry, prefixed with "xferlog: " + */ + if (!doxferlog || bytes == -1) + return; + + if (strcmp(command, "get") == 0) + direction = 'o'; + else if (strcmp(command, "put") == 0 || strcmp(command, "append") == 0) + direction = 'i'; + else + return; + + time(&now); + len = snprintf(buf, sizeof(buf), + "%.24s " LLF " %s " LLF " %s %c %s %c %c %s FTP 0 * %c\n", + +/* + * XXX: wu-ftpd puts ' (send)' or ' (recv)' in the syslog message, and removes + * the full date. This may be problematic for accurate log parsing, + * given that syslog messages don't contain the full date. + */ + ctime(&now), + (LLT) + (elapsed == NULL ? 0 : elapsed->tv_sec + (elapsed->tv_usec > 0)), + remotehost, + (LLT) bytes, + r1, + type == TYPE_A ? 'a' : 'b', + "_", /* XXX: take conversions into account? */ + direction, + + curclass.type == CLASS_GUEST ? 'a' : + curclass.type == CLASS_CHROOT ? 'g' : + curclass.type == CLASS_REAL ? 'r' : '?', + + curclass.type == CLASS_GUEST ? pw->pw_passwd : pw->pw_name, + error != NULL ? 'i' : 'c' + ); + + if ((doxferlog & 2) && xferlogfd != -1) + write(xferlogfd, buf, len); + if ((doxferlog & 1)) { + buf[len-1] = '\n'; /* strip \n from syslog message */ + syslog(LOG_INFO, "xferlog: %s", buf); + } +} + +#ifndef __minix +/* + * Log the resource usage. + * + * XXX: more resource usage to logging? + */ +void +logrusage(const struct rusage *rusage_before, + const struct rusage *rusage_after) +{ + struct timeval usrtime, systime; + + if (logging <= 1) + return; + + timersub(&rusage_after->ru_utime, &rusage_before->ru_utime, &usrtime); + timersub(&rusage_after->ru_stime, &rusage_before->ru_stime, &systime); + syslog(LOG_INFO, LLF ".%.03ldu " LLF ".%.03lds %ld+%ldio %ldpf+%ldw", + (LLT)usrtime.tv_sec, (long)(usrtime.tv_usec / 1000), + (LLT)systime.tv_sec, (long)(systime.tv_usec / 1000), + rusage_after->ru_inblock - rusage_before->ru_inblock, + rusage_after->ru_oublock - rusage_before->ru_oublock, + rusage_after->ru_majflt - rusage_before->ru_majflt, + rusage_after->ru_nswap - rusage_before->ru_nswap); +} +#endif + +/* + * Determine if `password' is valid for user given in `pw'. + * Returns 2 if password expired, 1 if otherwise failed, 0 if ok + */ +int +checkpassword(const struct passwd *pwent, const char *password) +{ + const char *orig; + char *new; + time_t change, expire, now; + + change = expire = 0; + if (pwent == NULL) + return 1; + + time(&now); + orig = pwent->pw_passwd; /* save existing password */ + expire = pwent->pw_expire; + change = pwent->pw_change; + if (change == _PASSWORD_CHGNOW) + change = now; + + if (orig[0] == '\0') /* don't allow empty passwords */ + return 1; + + new = crypt(password, orig); /* encrypt given password */ + if (strcmp(new, orig) != 0) /* compare */ + return 1; + + if ((expire && now >= expire) || (change && now >= change)) + return 2; /* check if expired */ + + return 0; /* OK! */ +} + +char * +ftpd_strdup(const char *s) +{ + char *new = strdup(s); + + if (new == NULL) + fatal("Local resource failure: malloc"); + /* NOTREACHED */ + return (new); +} + +/* + * As per fprintf(), but increment total_bytes and total_bytes_out, + * by the appropriate amount. + */ +void +cprintf(FILE *fd, const char *fmt, ...) +{ + off_t b; + va_list ap; + + va_start(ap, fmt); + b = vfprintf(fd, fmt, ap); + va_end(ap); + total_bytes += b; + total_bytes_out += b; +} + +#ifdef USE_PAM +/* + * the following code is stolen from imap-uw PAM authentication module and + * login.c + */ +typedef struct { + const char *uname; /* user name */ + int triedonce; /* if non-zero, tried before */ +} ftpd_cred_t; + +static int +auth_conv(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata) +{ + int i, ret; + size_t n; + ftpd_cred_t *cred = (ftpd_cred_t *) appdata; + struct pam_response *myreply; + char pbuf[FTP_BUFLEN]; + + if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG) + return (PAM_CONV_ERR); + myreply = calloc(num_msg, sizeof *myreply); + if (myreply == NULL) + return PAM_BUF_ERR; + + for (i = 0; i < num_msg; i++) { + myreply[i].resp_retcode = 0; + myreply[i].resp = NULL; + switch (msg[i]->msg_style) { + case PAM_PROMPT_ECHO_ON: /* user */ + myreply[i].resp = ftpd_strdup(cred->uname); + /* PAM frees resp. */ + break; + case PAM_PROMPT_ECHO_OFF: /* authtok (password) */ + /* + * Only send a single 331 reply and + * then expect a PASS. + */ + if (cred->triedonce) { + syslog(LOG_ERR, + "auth_conv: already performed PAM_PROMPT_ECHO_OFF"); + goto fail; + } + cred->triedonce++; + if (msg[i]->msg[0] == '\0') { + (void)strlcpy(pbuf, "password", sizeof(pbuf)); + } else { + /* Uncapitalize msg */ + (void)strlcpy(pbuf, msg[i]->msg, sizeof(pbuf)); + if (isupper((unsigned char)pbuf[0])) + pbuf[0] = tolower( + (unsigned char)pbuf[0]); + /* Remove trailing ':' and whitespace */ + n = strlen(pbuf); + while (n-- > 0) { + if (isspace((unsigned char)pbuf[n]) || + pbuf[n] == ':') + pbuf[n] = '\0'; + else + break; + } + } + /* Send reply, wait for a response. */ + reply(331, "User %s accepted, provide %s.", + cred->uname, pbuf); + (void) alarm(curclass.timeout); + ret = get_line(pbuf, sizeof(pbuf)-1, stdin); + (void) alarm(0); + if (ret == -1) { + reply(221, "You could at least say goodbye."); + dologout(0); + } else if (ret == -2) { + /* XXX: should we do this reply(-530, ..) ? */ + reply(-530, "Command too long."); + goto fail; + } + /* Ensure it is PASS */ + if (strncasecmp(pbuf, "PASS ", 5) != 0) { + syslog(LOG_ERR, + "auth_conv: unexpected reply '%.4s'", pbuf); + /* XXX: should we do this reply(-530, ..) ? */ + reply(-530, "Unexpected reply '%.4s'.", pbuf); + goto fail; + } + /* Strip CRLF from "PASS" reply */ + n = strlen(pbuf); + while (--n >= 5 && + (pbuf[n] == '\r' || pbuf[n] == '\n')) + pbuf[n] = '\0'; + /* Copy password into reply */ + myreply[i].resp = ftpd_strdup(pbuf+5); + /* PAM frees resp. */ + break; + case PAM_TEXT_INFO: + case PAM_ERROR_MSG: + break; + default: /* unknown message style */ + goto fail; + } + } + + *resp = myreply; + return PAM_SUCCESS; + + fail: + free(myreply); + *resp = NULL; + return PAM_CONV_ERR; +} + +/* + * Attempt to authenticate the user using PAM. Returns 0 if the user is + * authenticated, or 1 if not authenticated. If some sort of PAM system + * error occurs (e.g., the "/etc/pam.conf" file is missing) then this + * function returns -1. This can be used as an indication that we should + * fall back to a different authentication mechanism. + * pw maybe be updated to a new user if PAM_USER changes from curname. + */ +static int +auth_pam(void) +{ + const char *tmpl_user; + const void *item; + int rval; + int e; + ftpd_cred_t auth_cred = { curname, 0 }; + struct pam_conv conv = { &auth_conv, &auth_cred }; + + e = pam_start("ftpd", curname, &conv, &pamh); + if (e != PAM_SUCCESS) { + /* + * In OpenPAM, it's OK to pass NULL to pam_strerror() + * if context creation has failed in the first place. + */ + syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e)); + return -1; + } + + e = pam_set_item(pamh, PAM_RHOST, remotehost); + if (e != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", + pam_strerror(pamh, e)); + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); + } + pamh = NULL; + return -1; + } + + e = pam_set_item(pamh, PAM_SOCKADDR, &his_addr); + if (e != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_set_item(PAM_SOCKADDR): %s", + pam_strerror(pamh, e)); + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); + } + pamh = NULL; + return -1; + } + + e = pam_authenticate(pamh, 0); + if (ftpd_debug) + syslog(LOG_DEBUG, "pam_authenticate: user '%s' returned %d", + curname, e); + switch (e) { + case PAM_SUCCESS: + /* + * With PAM we support the concept of a "template" + * user. The user enters a login name which is + * authenticated by PAM, usually via a remote service + * such as RADIUS or TACACS+. If authentication + * succeeds, a different but related "template" name + * is used for setting the credentials, shell, and + * home directory. The name the user enters need only + * exist on the remote authentication server, but the + * template name must be present in the local password + * database. + * + * This is supported by two various mechanisms in the + * individual modules. However, from the application's + * point of view, the template user is always passed + * back as a changed value of the PAM_USER item. + */ + if ((e = pam_get_item(pamh, PAM_USER, &item)) == + PAM_SUCCESS) { + tmpl_user = (const char *) item; + if (pw == NULL + || strcmp(pw->pw_name, tmpl_user) != 0) { + pw = sgetpwnam(tmpl_user); + if (ftpd_debug) + syslog(LOG_DEBUG, + "auth_pam: PAM changed " + "user from '%s' to '%s'", + curname, pw->pw_name); + (void)strlcpy(curname, pw->pw_name, + curname_len); + } + } else + syslog(LOG_ERR, "Couldn't get PAM_USER: %s", + pam_strerror(pamh, e)); + rval = 0; + break; + + case PAM_AUTH_ERR: + case PAM_USER_UNKNOWN: + case PAM_MAXTRIES: + rval = 1; + break; + + default: + syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); + rval = -1; + break; + } + + if (rval == 0) { + e = pam_acct_mgmt(pamh, 0); + if (e != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_acct_mgmt: %s", + pam_strerror(pamh, e)); + rval = 1; + } + } + + if (rval != 0) { + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); + } + pamh = NULL; + } + return rval; +} + +#endif /* USE_PAM */ diff --git a/libexec/ftpd/ftpd.conf.5 b/libexec/ftpd/ftpd.conf.5 new file mode 100644 index 000000000..1a8da295f --- /dev/null +++ b/libexec/ftpd/ftpd.conf.5 @@ -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. diff --git a/libexec/ftpd/ftpusers.5 b/libexec/ftpd/ftpusers.5 new file mode 100644 index 000000000..285f385f3 --- /dev/null +++ b/libexec/ftpd/ftpusers.5 @@ -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 diff --git a/libexec/ftpd/logutmp.c b/libexec/ftpd/logutmp.c new file mode 100644 index 000000000..eacd9eb8e --- /dev/null +++ b/libexec/ftpd/logutmp.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: logutmp.c,v 1.12 2011/09/16 16:13:17 plunky Exp $"); +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef SUPPORT_UTMPX +#include +#endif +#include + +#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 diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c new file mode 100644 index 000000000..50135514a --- /dev/null +++ b/libexec/ftpd/logwtmp.c @@ -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 +#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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef SUPPORT_UTMP +#include +#endif +#ifdef SUPPORT_UTMPX +#include +#endif +#include + +#ifdef KERBEROS5 +#include +#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 diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h new file mode 100644 index 000000000..9e127bb9f --- /dev/null +++ b/libexec/ftpd/pathnames.h @@ -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 + +#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" diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c new file mode 100644 index 000000000..ed52dc094 --- /dev/null +++ b/libexec/ftpd/popen.c @@ -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 +#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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef KERBEROS5 +#include +#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); +} diff --git a/libexec/ftpd/version.h b/libexec/ftpd/version.h new file mode 100644 index 000000000..ff250ff82 --- /dev/null +++ b/libexec/ftpd/version.h @@ -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 diff --git a/releasetools/nbsd_ports b/releasetools/nbsd_ports index 5344b8717..fc01df8ac 100644 --- a/releasetools/nbsd_ports +++ b/releasetools/nbsd_ports @@ -59,6 +59,7 @@ 2011/05/26 00:00:00,external/public-domain/xz 2012/10/17 12:00:00,external/README 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/Makefile 2012/10/17 12:00:00,libexec/Makefile.inc